hbsig 0.3.1 → 0.3.3

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.
Files changed (94) hide show
  1. package/.babelrc-cjs +5 -0
  2. package/.babelrc-esm +5 -0
  3. package/README.md +1 -0
  4. package/dist/package.json +39 -0
  5. package/make.js +36 -0
  6. package/package.json +16 -16
  7. package/src/bin_to_str.js +46 -0
  8. package/src/collect-body-keys.js +436 -0
  9. package/src/commit.js +219 -0
  10. package/src/encode-array-item.js +112 -0
  11. package/src/encode-utils.js +191 -0
  12. package/src/encode.js +1256 -0
  13. package/src/erl_json.js +292 -0
  14. package/src/erl_str.js +1144 -0
  15. package/src/flat.js +250 -0
  16. package/src/http-message-signatures/httpbis.js +438 -0
  17. package/src/http-message-signatures/index.js +4 -0
  18. package/src/http-message-signatures/structured-header.js +105 -0
  19. package/src/httpsig.js +866 -0
  20. package/src/id.js +459 -0
  21. package/src/index.js +13 -0
  22. package/src/nocrypto.js +4 -0
  23. package/src/parser.js +171 -0
  24. package/src/send-utils.js +1132 -0
  25. package/src/send.js +142 -0
  26. package/src/signer-utils.js +375 -0
  27. package/src/signer.js +312 -0
  28. package/src/structured.js +496 -0
  29. package/src/test.js +2 -0
  30. package/src/utils.js +29 -0
  31. package/test/commit.test.js +41 -0
  32. package/test/erl_json.test.js +8 -0
  33. package/test/flat.test.js +27 -0
  34. package/test/httpsig.test.js +31 -0
  35. package/test/id.test.js +114 -0
  36. package/test/lib/all_cases.js +408 -0
  37. package/test/lib/cases.js +408 -0
  38. package/test/lib/erl_json_cases.js +161 -0
  39. package/test/lib/flat_cases.js +189 -0
  40. package/test/lib/gen.js +528 -0
  41. package/test/lib/httpsig_cases.js +313 -0
  42. package/test/lib/structured_cases.js +222 -0
  43. package/test/lib/test-utils.js +399 -0
  44. package/test/signer.test.js +48 -0
  45. package/test/structured.test.js +35 -0
  46. /package/{cjs → dist/cjs}/bin_to_str.js +0 -0
  47. /package/{cjs → dist/cjs}/collect-body-keys.js +0 -0
  48. /package/{cjs → dist/cjs}/commit.js +0 -0
  49. /package/{cjs → dist/cjs}/encode-array-item.js +0 -0
  50. /package/{cjs → dist/cjs}/encode-utils.js +0 -0
  51. /package/{cjs → dist/cjs}/encode.js +0 -0
  52. /package/{cjs → dist/cjs}/erl_json.js +0 -0
  53. /package/{cjs → dist/cjs}/erl_str.js +0 -0
  54. /package/{cjs → dist/cjs}/flat.js +0 -0
  55. /package/{cjs → dist/cjs}/http-message-signatures/httpbis.js +0 -0
  56. /package/{cjs → dist/cjs}/http-message-signatures/index.js +0 -0
  57. /package/{cjs → dist/cjs}/http-message-signatures/structured-header.js +0 -0
  58. /package/{cjs → dist/cjs}/httpsig.js +0 -0
  59. /package/{cjs → dist/cjs}/id.js +0 -0
  60. /package/{cjs → dist/cjs}/index.js +0 -0
  61. /package/{cjs → dist/cjs}/nocrypto.js +0 -0
  62. /package/{cjs → dist/cjs}/parser.js +0 -0
  63. /package/{cjs → dist/cjs}/send-utils.js +0 -0
  64. /package/{cjs → dist/cjs}/send.js +0 -0
  65. /package/{cjs → dist/cjs}/signer-utils.js +0 -0
  66. /package/{cjs → dist/cjs}/signer.js +0 -0
  67. /package/{cjs → dist/cjs}/structured.js +0 -0
  68. /package/{cjs → dist/cjs}/test.js +0 -0
  69. /package/{cjs → dist/cjs}/utils.js +0 -0
  70. /package/{esm → dist/esm}/bin_to_str.js +0 -0
  71. /package/{esm → dist/esm}/collect-body-keys.js +0 -0
  72. /package/{esm → dist/esm}/commit.js +0 -0
  73. /package/{esm → dist/esm}/encode-array-item.js +0 -0
  74. /package/{esm → dist/esm}/encode-utils.js +0 -0
  75. /package/{esm → dist/esm}/encode.js +0 -0
  76. /package/{esm → dist/esm}/erl_json.js +0 -0
  77. /package/{esm → dist/esm}/erl_str.js +0 -0
  78. /package/{esm → dist/esm}/flat.js +0 -0
  79. /package/{esm → dist/esm}/http-message-signatures/httpbis.js +0 -0
  80. /package/{esm → dist/esm}/http-message-signatures/index.js +0 -0
  81. /package/{esm → dist/esm}/http-message-signatures/structured-header.js +0 -0
  82. /package/{esm → dist/esm}/httpsig.js +0 -0
  83. /package/{esm → dist/esm}/id.js +0 -0
  84. /package/{esm → dist/esm}/index.js +0 -0
  85. /package/{esm → dist/esm}/nocrypto.js +0 -0
  86. /package/{esm → dist/esm}/package.json +0 -0
  87. /package/{esm → dist/esm}/parser.js +0 -0
  88. /package/{esm → dist/esm}/send-utils.js +0 -0
  89. /package/{esm → dist/esm}/send.js +0 -0
  90. /package/{esm → dist/esm}/signer-utils.js +0 -0
  91. /package/{esm → dist/esm}/signer.js +0 -0
  92. /package/{esm → dist/esm}/structured.js +0 -0
  93. /package/{esm → dist/esm}/test.js +0 -0
  94. /package/{esm → dist/esm}/utils.js +0 -0
@@ -0,0 +1,496 @@
1
+ /**
2
+ * Structured field codec for JavaScript-Erlang interoperability
3
+ * Implements the same behavior as dev_codec_structured.erl
4
+ */
5
+
6
+ import { erl_str_from } from "./erl_str.js"
7
+
8
+ /**
9
+ * Convert from structured format (rich message to TABM)
10
+ * Mirrors Erlang's from/1 function
11
+ * @param {*} obj - Rich message object
12
+ * @returns {*} - TABM object
13
+ */
14
+ export function structured_from(obj) {
15
+ // Handle binary input
16
+ if (
17
+ typeof obj === "string" ||
18
+ obj instanceof Buffer ||
19
+ obj instanceof Uint8Array
20
+ ) {
21
+ return obj
22
+ }
23
+
24
+ // Handle non-object input
25
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
26
+ return obj
27
+ }
28
+
29
+ // Convert rich message to TABM
30
+ return from(obj)
31
+ }
32
+
33
+ /**
34
+ * Convert to structured format (TABM to rich message)
35
+ * Mirrors Erlang's to/1 function
36
+ * @param {string|object} input - Erlang term string or TABM object
37
+ * @returns {object} - Rich message object
38
+ */
39
+ export function structured_to(input) {
40
+ // If input is a string (Erlang response), parse it
41
+ if (typeof input === "string") {
42
+ return erl_str_from(input, false)
43
+ }
44
+
45
+ // Otherwise convert TABM to rich message
46
+ return to(input)
47
+ }
48
+
49
+ /**
50
+ * Convert a TABM into a rich message (mirrors Erlang's to/1)
51
+ * @param {*} tabm - Type-Annotated-Binary-Message
52
+ * @returns {*} - Rich message
53
+ */
54
+ function to(tabm) {
55
+ // Handle binary input
56
+ if (
57
+ typeof tabm === "string" ||
58
+ tabm instanceof Buffer ||
59
+ tabm instanceof Uint8Array
60
+ ) {
61
+ return tabm
62
+ }
63
+
64
+ // Handle non-object input
65
+ if (typeof tabm !== "object" || tabm === null || Array.isArray(tabm)) {
66
+ return tabm
67
+ }
68
+
69
+ // Parse ao-types if present
70
+ const aoTypesStr = tabm["ao-types"] || ""
71
+ const types = parseAoTypes(aoTypesStr)
72
+
73
+ // Build result with empty values first
74
+ const result = {}
75
+
76
+ // Add empty values based on their types (these may be overwritten if data exists)
77
+ for (const [key, type] of Object.entries(types)) {
78
+ if (type === "empty-binary") {
79
+ result[key] = ""
80
+ } else if (type === "empty-list") {
81
+ result[key] = []
82
+ } else if (type === "empty-message") {
83
+ result[key] = {}
84
+ }
85
+ }
86
+
87
+ // Process all other key-value pairs
88
+ for (const [rawKey, value] of Object.entries(tabm)) {
89
+ // Skip ao-types field
90
+ if (rawKey === "ao-types") {
91
+ continue
92
+ }
93
+
94
+ const normalizedKey = rawKey.toLowerCase()
95
+
96
+ if (
97
+ typeof value === "string" ||
98
+ value instanceof Buffer ||
99
+ value instanceof Uint8Array
100
+ ) {
101
+ const type = types[normalizedKey]
102
+ if (type) {
103
+ // Decode according to type
104
+ result[rawKey] = decodeValue(type, value)
105
+ } else {
106
+ // No type info, keep as binary/string
107
+ result[rawKey] = value
108
+ }
109
+ } else if (
110
+ typeof value === "object" &&
111
+ value !== null &&
112
+ !Array.isArray(value)
113
+ ) {
114
+ // Check if the child object itself indicates it's a list via .="list" in its ao-types
115
+ const childAoTypes = value["ao-types"] || ""
116
+ const childTypes = parseAoTypes(childAoTypes)
117
+ const isChildList = childTypes["."] === "list"
118
+
119
+ // Recursively decode child TABM
120
+ const childDecoded = to(value)
121
+
122
+ // Only convert numbered map to array if the child object itself
123
+ // declares it's a list via .="list" in its ao-types.
124
+ // This preserves original keys when the parent declares the type
125
+ // but the child doesn't have the list marker.
126
+ if (isChildList) {
127
+ // Convert numbered map back to ordered list
128
+ result[rawKey] = messageToOrderedList(childDecoded)
129
+ } else {
130
+ result[rawKey] = childDecoded
131
+ }
132
+ } else {
133
+ // Value already has converted type
134
+ result[rawKey] = value
135
+ }
136
+ }
137
+
138
+ return filterDefaultKeys(result)
139
+ }
140
+
141
+ /**
142
+ * Parse ao-types field and return map of keys to types
143
+ * @param {string} aoTypesStr - ao-types field content
144
+ * @returns {object} - Map of normalized keys to types
145
+ */
146
+ function parseAoTypes(aoTypesStr) {
147
+ if (!aoTypesStr) {
148
+ return {}
149
+ }
150
+
151
+ const types = {}
152
+
153
+ // Simple parser for "key1=\"type1\", key2=\"type2\"" format
154
+ const pairs = aoTypesStr.split(", ")
155
+
156
+ for (const pair of pairs) {
157
+ const match = pair.match(/^(.+?)="(.+?)"$/)
158
+ if (match) {
159
+ const [, key, type] = match
160
+ // Decode escaped key and normalize
161
+ const decodedKey = decodeEscapedKey(key)
162
+ types[decodedKey.toLowerCase()] = type
163
+ }
164
+ }
165
+
166
+ return types
167
+ }
168
+
169
+ /**
170
+ * Decode escaped key (simplified version)
171
+ * @param {string} key - Escaped key
172
+ * @returns {string} - Decoded key
173
+ */
174
+ function decodeEscapedKey(key) {
175
+ // This is a simplified decoder - in practice you'd want more robust escaping
176
+ return key.replace(/\\"/g, '"').replace(/\\\\/g, "\\")
177
+ }
178
+
179
+ /**
180
+ * Convert numbered map back to ordered list
181
+ * @param {object} numberedMap - Map with numeric string keys
182
+ * @returns {Array} - Ordered array
183
+ */
184
+ function messageToOrderedList(numberedMap) {
185
+ const keys = Object.keys(numberedMap)
186
+ .filter(key => /^\d+$/.test(key))
187
+ .map(key => parseInt(key, 10))
188
+ .sort((a, b) => a - b)
189
+
190
+ return keys.map(key => numberedMap[key.toString()])
191
+ }
192
+
193
+ /**
194
+ * Filter default keys (simplified - removes common defaults)
195
+ * @param {object} message - Message object
196
+ * @returns {object} - Filtered message
197
+ */
198
+ function filterDefaultKeys(message) {
199
+ // This is a placeholder - implement based on your needs
200
+ return message
201
+ }
202
+
203
+ /**
204
+ * Decode a value based on its type
205
+ * @param {string} type - Type identifier
206
+ * @param {*} value - Encoded value
207
+ * @returns {*} - Decoded value
208
+ */
209
+ function decodeValue(type, value) {
210
+ switch (type.toLowerCase()) {
211
+ case "integer":
212
+ return parseInt(parseStructuredItem(value), 10)
213
+
214
+ case "float":
215
+ return parseFloat(value)
216
+
217
+ case "boolean":
218
+ // SF boolean format: ?1 = true, ?0 = false
219
+ // Convert to native boolean, will be encoded as "atom" type
220
+ if (value === "?1") return true
221
+ if (value === "?0") return false
222
+ // Fallback for other formats
223
+ return value === "true" || value === "1"
224
+
225
+ case "atom":
226
+ const atomItem = parseStructuredItem(value)
227
+ const atomName = atomItem.replace(/^"|"$/g, "") // Remove quotes
228
+ // Convert to Symbol to preserve atom type through round-trip
229
+ // Special cases for common atoms that JS has native types for
230
+ if (atomName === "true") return true
231
+ if (atomName === "false") return false
232
+ if (atomName === "null") return null
233
+ return Symbol.for(atomName)
234
+
235
+ case "list":
236
+ return parseStructuredList(value).map(item => {
237
+ if (typeof item === "string" && item.startsWith("(ao-type-")) {
238
+ const match = item.match(/^\(ao-type-(.+?)\) (.+)$/)
239
+ if (match) {
240
+ const [, itemType, itemValue] = match
241
+ return decodeValue(itemType, itemValue)
242
+ }
243
+ }
244
+ return item
245
+ })
246
+
247
+ case "binary":
248
+ return value
249
+
250
+ default:
251
+ return value
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Parse structured field item (simplified)
257
+ * @param {string} value - Structured field value
258
+ * @returns {*} - Parsed value
259
+ */
260
+ function parseStructuredItem(value) {
261
+ // Handle non-string values (e.g., numbers from HyperBEAM responses)
262
+ if (typeof value !== "string") return String(value)
263
+ if (value.startsWith('"') && value.endsWith('"')) {
264
+ return value.slice(1, -1) // Remove quotes
265
+ }
266
+ return value
267
+ }
268
+
269
+ /**
270
+ * Parse structured field list (simplified)
271
+ * @param {string} value - Structured field list
272
+ * @returns {Array} - Parsed list
273
+ */
274
+ function parseStructuredList(value) {
275
+ if (typeof value !== "string") return [value]
276
+ return value.split(", ").map(item => {
277
+ if (item.startsWith('"') && item.endsWith('"')) {
278
+ // Remove quotes and unescape SF string escapes
279
+ // In SF strings: \" = " and \\ = \
280
+ return item.slice(1, -1)
281
+ .replace(/\\"/g, '"') // \" -> "
282
+ .replace(/\\\\/g, '\\') // \\ -> \
283
+ }
284
+ return item
285
+ })
286
+ }
287
+
288
+ /**
289
+ * Convert rich message to TABM (mirrors Erlang's from/1)
290
+ * @param {object} msg - Rich message
291
+ * @returns {object} - TABM
292
+ */
293
+ function from(msg) {
294
+ // Handle binary input - passthrough
295
+ if (msg instanceof Buffer || msg instanceof Uint8Array) {
296
+ return msg
297
+ }
298
+
299
+ // Handle non-object values - passthrough
300
+ if (typeof msg !== "object" || msg === null) {
301
+ return msg
302
+ }
303
+
304
+ // Handle arrays - convert to numbered map with .="list" in ao-types
305
+ // Mirrors Erlang: from(List, Req, Opts) when is_list(List)
306
+ if (Array.isArray(msg)) {
307
+ // Convert to numbered map (1-based indexing like Erlang)
308
+ const numberedMap = {}
309
+ msg.forEach((item, idx) => {
310
+ numberedMap[(idx + 1).toString()] = item
311
+ })
312
+
313
+ // Recursively process the numbered map
314
+ const result = from(numberedMap)
315
+
316
+ // Add .="list" to ao-types to indicate this message is a list
317
+ const existingAoTypes = result["ao-types"] || ""
318
+ if (existingAoTypes) {
319
+ result["ao-types"] = '.="list", ' + existingAoTypes
320
+ } else {
321
+ result["ao-types"] = '.="list"'
322
+ }
323
+
324
+ return result
325
+ }
326
+
327
+ // Process keys - preserve original case to match Erlang behavior
328
+ // HTTP headers are case-insensitive but JSON/map keys preserve case
329
+ const keysMap = {}
330
+ for (const [key, value] of Object.entries(msg)) {
331
+ keysMap[key] = value
332
+ }
333
+
334
+ // Get sorted keys (preserving case)
335
+ const sortedKeys = Object.keys(keysMap).sort()
336
+
337
+ const types = []
338
+ const values = []
339
+
340
+ // Process each key in sorted order
341
+ for (const key of sortedKeys) {
342
+ const value = keysMap[key]
343
+
344
+ // Handle empty binaries/strings - just include as-is, no type annotation
345
+ // (Erlang doesn't add empty-binary type, it just keeps the empty binary)
346
+ if (value === "" || (value instanceof Buffer && value.length === 0)) {
347
+ values.push([key, value])
348
+ continue
349
+ }
350
+
351
+ // Empty arrays - convert to numbered map with .="list" in ao-types
352
+ // (Erlang doesn't add empty-list type, just the list marker)
353
+ if (Array.isArray(value) && value.length === 0) {
354
+ values.push([key, from(value)])
355
+ continue
356
+ }
357
+
358
+ // Empty objects - just include as-is, no type annotation
359
+ // (Erlang doesn't add empty-message type, it just keeps the empty map)
360
+ if (
361
+ typeof value === "object" &&
362
+ value !== null &&
363
+ !Array.isArray(value) &&
364
+ !(value instanceof Buffer) &&
365
+ Object.keys(value).length === 0
366
+ ) {
367
+ values.push([key, value])
368
+ continue
369
+ }
370
+
371
+ // Handle binary/string values
372
+ if (value instanceof Buffer || value instanceof Uint8Array) {
373
+ values.push([key, value])
374
+ continue
375
+ }
376
+
377
+ if (typeof value === "string") {
378
+ values.push([key, value])
379
+ continue
380
+ }
381
+
382
+ // Handle nested maps
383
+ if (typeof value === "object" && !Array.isArray(value) && value !== null) {
384
+ values.push([key, from(value)])
385
+ continue
386
+ }
387
+
388
+ // Handle arrays - from() converts to numbered map with .="list" in ao-types
389
+ if (Array.isArray(value) && value.length > 0) {
390
+ values.push([key, from(value)])
391
+ continue
392
+ }
393
+
394
+ // Handle typed values (need encoding)
395
+ if (
396
+ typeof value === "symbol" ||
397
+ typeof value === "number" ||
398
+ typeof value === "boolean" ||
399
+ value === null
400
+ ) {
401
+ const [type, encoded] = encodeValue(value)
402
+ types.push([key, type])
403
+ values.push([key, encoded])
404
+ continue
405
+ }
406
+ }
407
+
408
+ // Build result
409
+ const result = {}
410
+
411
+ // Add ao-types if present
412
+ if (types.length > 0) {
413
+ result["ao-types"] = types.map(([k, t]) => `${k}="${t}"`).join(", ")
414
+ }
415
+
416
+ // Add values (but NOT empty values)
417
+ for (const [k, v] of values) {
418
+ result[k] = v
419
+ }
420
+
421
+ return result
422
+ }
423
+
424
+ /**
425
+ * Encode a value with its type
426
+ */
427
+ function encodeValue(value) {
428
+ // Null (as atom) - use token format (unquoted)
429
+ if (value === null) {
430
+ return ["atom", "null"]
431
+ }
432
+
433
+ // Integer
434
+ if (typeof value === "number" && Number.isInteger(value)) {
435
+ return ["integer", value.toString()]
436
+ }
437
+
438
+ // Float
439
+ if (typeof value === "number") {
440
+ // Format like Erlang's float_to_binary - scientific notation with full precision
441
+ // Erlang's float_to_binary/1 uses ~20 decimal digits and keeps trailing zeros
442
+ let str = value.toExponential(20)
443
+ // Ensure 2-digit exponent with sign
444
+ str = str.replace(/e([+-])(\d)$/, "e$10$2")
445
+ return ["float", str]
446
+ }
447
+
448
+ // Boolean (as atom) - use token format (unquoted)
449
+ if (typeof value === "boolean") {
450
+ return ["atom", value.toString()]
451
+ }
452
+
453
+ // Symbol (as atom) - use token format (unquoted)
454
+ if (typeof value === "symbol") {
455
+ const name = Symbol.keyFor(value) || value.description || ""
456
+ return ["atom", name]
457
+ }
458
+
459
+ // List
460
+ if (Array.isArray(value)) {
461
+ const parts = []
462
+
463
+ for (const item of value) {
464
+ if (item instanceof Buffer) {
465
+ // Empty buffer => empty string
466
+ parts.push(item.length === 0 ? '""' : `"${item.toString()}"`)
467
+ } else if (typeof item === "string") {
468
+ parts.push(`"${item}"`)
469
+ } else {
470
+ const [itemType, itemEncoded] = encodeValue(item)
471
+
472
+ if (itemType === "list") {
473
+ // Escape nested list quotes
474
+ const escaped = itemEncoded
475
+ .replace(/\\/g, "\\\\")
476
+ .replace(/"/g, '\\"')
477
+ parts.push(`"(ao-type-list) ${escaped}"`)
478
+ } else if (itemType === "atom") {
479
+ // Escape atom quotes
480
+ const escaped = itemEncoded.replace(/"/g, '\\"')
481
+ parts.push(`"(ao-type-atom) ${escaped}"`)
482
+ } else {
483
+ parts.push(`"(ao-type-${itemType}) ${itemEncoded}"`)
484
+ }
485
+ }
486
+ }
487
+
488
+ return ["list", parts.join(", ")]
489
+ }
490
+
491
+ if (value instanceof Buffer) {
492
+ return ["binary", value]
493
+ }
494
+
495
+ return ["unknown", String(value)]
496
+ }
package/src/test.js ADDED
@@ -0,0 +1,2 @@
1
+ import { toAddr } from "./utils.js"
2
+ export { toAddr }
package/src/utils.js ADDED
@@ -0,0 +1,29 @@
1
+ import sha256 from "fast-sha256"
2
+
3
+ function base64urlDecode(str) {
4
+ str = str.replace(/-/g, "+").replace(/_/g, "/")
5
+ const pad = str.length % 4
6
+ if (pad === 2) str += "=="
7
+ else if (pad === 3) str += "="
8
+ else if (pad !== 0) throw new Error("Invalid base64url string")
9
+ const bin = atob(str)
10
+ const bytes = new Uint8Array(bin.length)
11
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)
12
+ return bytes
13
+ }
14
+
15
+ function base64urlEncode(bytes) {
16
+ let bin = ""
17
+ for (const b of bytes) bin += String.fromCharCode(b)
18
+ let b64 = btoa(bin)
19
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "")
20
+ }
21
+
22
+ function toAddr(n) {
23
+ if (typeof n === "object" && typeof n.n === "string") n = n.n
24
+ const pubBytes = base64urlDecode(n)
25
+ const hash = sha256(pubBytes)
26
+ return base64urlEncode(hash)
27
+ }
28
+
29
+ export { toAddr }
@@ -0,0 +1,41 @@
1
+ import assert from "assert"
2
+ import { after, describe, it, before, beforeEach } from "node:test"
3
+ import { mod } from "./lib/test-utils.js"
4
+ import { HyperBEAM } from "../../src/test.js"
5
+ import cases, { errors } from "./lib/cases.js"
6
+ import { normalize } from "../src/erl_json.js"
7
+ import { createSigner } from "../src/signer.js"
8
+ import { send } from "../src/send.js"
9
+ import { commit } from "../src/commit.js"
10
+
11
+ describe("Hyperbeam commit", function () {
12
+ let hbeam, sign, hb
13
+ before(async () => {
14
+ hbeam = await new HyperBEAM({ reset: true, linkify_mode: false }).ready()
15
+ sign = createSigner(hbeam.jwk, hbeam.url)
16
+ hb = hbeam.hb
17
+ })
18
+ after(async () => hbeam.kill())
19
+
20
+ it("should test commit", async () => {
21
+ const msg = await commit(
22
+ { key: "value", data: Buffer.from([1, 2, 3]) },
23
+ { signer: sign }
24
+ )
25
+ for (const k in msg.commitments) {
26
+ if (msg.commitments[k].committer) {
27
+ assert.equal(hbeam.addr, msg.commitments[k].committer)
28
+ }
29
+ }
30
+ })
31
+
32
+ it("should schedule a nested message", async () => {
33
+ const { pid } = await hb.spawn()
34
+ const { slot } = await hb.schedule({
35
+ pid,
36
+ tags: { str: "value", num: 123 },
37
+ data: "abc",
38
+ })
39
+ assert.equal(1, slot)
40
+ })
41
+ })
@@ -0,0 +1,8 @@
1
+ import { cases_from } from "./lib/erl_json_cases.js"
2
+ import all_cases from "./lib/all_cases.js"
3
+ import { gen } from "./lib/gen.js"
4
+ import { genTest } from "./lib/test-utils.js"
5
+
6
+ genTest({
7
+ its: [{ cases: cases_from }, { cases: all_cases }, { cases: gen(100) }],
8
+ })
@@ -0,0 +1,27 @@
1
+ import { flat_from, flat_to } from "../src/flat.js"
2
+ import { cases_from, cases_to } from "./lib/flat_cases.js"
3
+ import { normalize } from "../src/erl_json.js"
4
+ import { genTest } from "./lib/test-utils.js"
5
+
6
+ genTest({
7
+ its: [
8
+ {
9
+ it: "should test flat_from",
10
+ cases: cases_from,
11
+ path: "/~hbsig@1.0/flat_from",
12
+ mod: v => flat_from(normalize(v)),
13
+ // Skip ao-types processing and remove ao-types field for flat codec
14
+ skipAoTypes: true,
15
+ removeAoTypes: true,
16
+ },
17
+ {
18
+ it: "should test flat_to",
19
+ cases: cases_to,
20
+ path: "/~hbsig@1.0/flat_to",
21
+ mod: v => flat_to(normalize(v)),
22
+ // Skip ao-types processing and remove ao-types field for flat codec
23
+ skipAoTypes: true,
24
+ removeAoTypes: true,
25
+ },
26
+ ],
27
+ })
@@ -0,0 +1,31 @@
1
+ import { structured_from, structured_to } from "../src/structured.js"
2
+ import { cases_from, cases_to } from "./lib/structured_cases.js"
3
+ import { normalize } from "../src/erl_json.js"
4
+ import { genTest } from "./lib/test-utils.js"
5
+ import { ok } from "./lib/cases.js"
6
+ import { httpsig_from, httpsig_to } from "../src/httpsig.js"
7
+
8
+ genTest({
9
+ its: [
10
+ {
11
+ it: "should test httpsig_to (cases_from)",
12
+ cases: cases_from,
13
+ path: "/~hbsig@1.0/httpsig_to",
14
+ pmod: v => structured_from(normalize(v)),
15
+ mod: v => httpsig_to(normalize(v)),
16
+ // Skip ao-types conversions since httpsig codec produces TABM strings
17
+ skipAoTypes: true,
18
+ removeAoTypes: true,
19
+ },
20
+ {
21
+ it: "should test httpsig_to (ok cases)",
22
+ cases: ok,
23
+ path: "/~hbsig@1.0/httpsig_to",
24
+ pmod: v => structured_from(normalize(v)),
25
+ mod: v => httpsig_to(normalize(v)),
26
+ // Skip ao-types conversions since httpsig codec produces TABM strings
27
+ skipAoTypes: true,
28
+ removeAoTypes: true,
29
+ },
30
+ ],
31
+ })