hbsig 0.0.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/bin_to_str.js +44 -0
- package/cjs/collect-body-keys.js +470 -0
- package/cjs/encode-array-item.js +110 -0
- package/cjs/encode-utils.js +236 -0
- package/cjs/encode.js +1318 -0
- package/cjs/erl_json.js +317 -0
- package/cjs/erl_str.js +1037 -0
- package/cjs/flat.js +222 -0
- package/cjs/http-message-signatures/httpbis.js +489 -0
- package/cjs/http-message-signatures/index.js +25 -0
- package/cjs/http-message-signatures/structured-header.js +129 -0
- package/cjs/httpsig.js +716 -0
- package/cjs/httpsig2.js +1160 -0
- package/cjs/id.js +470 -0
- package/cjs/index.js +63 -0
- package/cjs/send.js +194 -0
- package/cjs/signer-utils.js +617 -0
- package/cjs/signer.js +606 -0
- package/cjs/structured.js +296 -0
- package/cjs/test.js +27 -0
- package/cjs/utils.js +42 -0
- package/esm/bin_to_str.js +46 -0
- package/esm/collect-body-keys.js +436 -0
- package/esm/encode-array-item.js +112 -0
- package/esm/encode-utils.js +185 -0
- package/esm/encode.js +1219 -0
- package/esm/erl_json.js +289 -0
- package/esm/erl_str.js +1139 -0
- package/esm/flat.js +196 -0
- package/esm/http-message-signatures/httpbis.js +438 -0
- package/esm/http-message-signatures/index.js +4 -0
- package/esm/http-message-signatures/structured-header.js +105 -0
- package/esm/httpsig.js +658 -0
- package/esm/httpsig2.js +1097 -0
- package/esm/id.js +459 -0
- package/esm/index.js +4 -0
- package/esm/package.json +3 -0
- package/esm/send.js +124 -0
- package/esm/signer-utils.js +494 -0
- package/esm/signer.js +452 -0
- package/esm/structured.js +269 -0
- package/esm/test.js +6 -0
- package/esm/utils.js +28 -0
- package/package.json +28 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hasNonAscii,
|
|
3
|
+
sha256,
|
|
4
|
+
hasNewline,
|
|
5
|
+
isBytes,
|
|
6
|
+
isPojo,
|
|
7
|
+
} from "./encode-utils.js"
|
|
8
|
+
|
|
9
|
+
// Helper functions
|
|
10
|
+
const isEmpty = value => {
|
|
11
|
+
if (typeof value === "string") return value === ""
|
|
12
|
+
if (Array.isArray(value)) return value.length === 0
|
|
13
|
+
if (isPojo(value)) return Object.keys(value).length === 0
|
|
14
|
+
if (isBytes(value)) return value.length === 0 || value.byteLength === 0
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const hasOnlyEmptyValues = obj => {
|
|
19
|
+
if (!isPojo(obj)) return false
|
|
20
|
+
return Object.values(obj).every(
|
|
21
|
+
v =>
|
|
22
|
+
(typeof v === "string" && v === "") ||
|
|
23
|
+
(Array.isArray(v) && v.length === 0) ||
|
|
24
|
+
(isPojo(v) && Object.keys(v).length === 0)
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const isSimpleValue = value => {
|
|
29
|
+
return (
|
|
30
|
+
typeof value === "string" ||
|
|
31
|
+
typeof value === "number" ||
|
|
32
|
+
typeof value === "boolean" ||
|
|
33
|
+
value === null ||
|
|
34
|
+
value === undefined ||
|
|
35
|
+
typeof value === "symbol"
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const getValueByPath = (obj, path) => {
|
|
40
|
+
const parts = path.split("/")
|
|
41
|
+
let value = obj
|
|
42
|
+
for (const part of parts) {
|
|
43
|
+
if (/^\d+$/.test(part)) {
|
|
44
|
+
value = value[parseInt(part) - 1]
|
|
45
|
+
} else {
|
|
46
|
+
value = value[part]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return value
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const canArrayBeInHeader = array => {
|
|
53
|
+
// Empty arrays can be in headers
|
|
54
|
+
if (array.length === 0) return true
|
|
55
|
+
|
|
56
|
+
// Arrays with objects must go to body
|
|
57
|
+
if (array.some(item => isPojo(item))) return false
|
|
58
|
+
|
|
59
|
+
// Arrays with binary data must go to body
|
|
60
|
+
if (array.some(item => isBytes(item) && item.length > 0)) return false
|
|
61
|
+
|
|
62
|
+
// Arrays with non-ASCII strings must go to body
|
|
63
|
+
if (array.some(item => typeof item === "string" && hasNonAscii(item)))
|
|
64
|
+
return false
|
|
65
|
+
|
|
66
|
+
// Arrays with nested arrays must go to body (to match original behavior)
|
|
67
|
+
if (array.some(item => Array.isArray(item))) return false
|
|
68
|
+
|
|
69
|
+
// Arrays with nested arrays that have objects must go to body
|
|
70
|
+
if (
|
|
71
|
+
array.some(
|
|
72
|
+
item => Array.isArray(item) && item.some(subItem => isPojo(subItem))
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
return false
|
|
76
|
+
|
|
77
|
+
// Simple arrays of primitives can stay in headers
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Array analysis helper
|
|
82
|
+
const analyzeArray = array => {
|
|
83
|
+
const analysis = {
|
|
84
|
+
hasObjects: false,
|
|
85
|
+
hasArrays: false,
|
|
86
|
+
hasNonObjects: false,
|
|
87
|
+
hasArraysOfObjects: false,
|
|
88
|
+
hasEmptyStrings: false,
|
|
89
|
+
hasEmptyObjects: false,
|
|
90
|
+
hasNonEmptyObjects: false,
|
|
91
|
+
hasObjectsWithOnlyEmptyValues: false,
|
|
92
|
+
hasOnlyEmptyElements: true,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const item of array) {
|
|
96
|
+
if (isPojo(item)) {
|
|
97
|
+
analysis.hasObjects = true
|
|
98
|
+
if (Object.keys(item).length === 0) {
|
|
99
|
+
analysis.hasEmptyObjects = true
|
|
100
|
+
} else {
|
|
101
|
+
analysis.hasNonEmptyObjects = true
|
|
102
|
+
if (hasOnlyEmptyValues(item)) {
|
|
103
|
+
analysis.hasObjectsWithOnlyEmptyValues = true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} else if (Array.isArray(item)) {
|
|
107
|
+
analysis.hasArrays = true
|
|
108
|
+
if (item.some(subItem => isPojo(subItem))) {
|
|
109
|
+
analysis.hasArraysOfObjects = true
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
analysis.hasNonObjects = true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof item === "string" && item === "") {
|
|
116
|
+
analysis.hasEmptyStrings = true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!isEmpty(item)) {
|
|
120
|
+
analysis.hasOnlyEmptyElements = false
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return analysis
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Body key collector class
|
|
128
|
+
class BodyKeyCollector {
|
|
129
|
+
constructor(obj) {
|
|
130
|
+
this.obj = obj
|
|
131
|
+
this.keys = []
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
collect() {
|
|
135
|
+
this.processRootObject()
|
|
136
|
+
return this.deduplicateAndFilter()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Process top-level object
|
|
140
|
+
processRootObject() {
|
|
141
|
+
const objKeys = Object.keys(this.obj)
|
|
142
|
+
|
|
143
|
+
for (const [key, value] of Object.entries(this.obj)) {
|
|
144
|
+
if (this.isSpecialDataBodyField(key, value, objKeys)) {
|
|
145
|
+
this.keys.push(key)
|
|
146
|
+
} else if (Array.isArray(value) && value.length > 0) {
|
|
147
|
+
// Check if array can stay in header
|
|
148
|
+
if (!canArrayBeInHeader(value)) {
|
|
149
|
+
this.processRootArray(key, value)
|
|
150
|
+
}
|
|
151
|
+
} else if (isPojo(value)) {
|
|
152
|
+
this.processRootNestedObject(key, value)
|
|
153
|
+
} else if (this.needsBodyKey(key, value)) {
|
|
154
|
+
this.keys.push(key)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check if field is special data/body field
|
|
160
|
+
isSpecialDataBodyField(key, value, objKeys) {
|
|
161
|
+
if (
|
|
162
|
+
(key === "data" || key === "body") &&
|
|
163
|
+
isSimpleValue(value) &&
|
|
164
|
+
objKeys.length > 1
|
|
165
|
+
) {
|
|
166
|
+
const otherKey = key === "data" ? "body" : "data"
|
|
167
|
+
const otherValue = this.obj[otherKey]
|
|
168
|
+
|
|
169
|
+
if (
|
|
170
|
+
otherValue &&
|
|
171
|
+
isPojo(otherValue) &&
|
|
172
|
+
Object.keys(otherValue).length > 0
|
|
173
|
+
) {
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
return true
|
|
177
|
+
}
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check if value needs a body key
|
|
182
|
+
needsBodyKey(key, value) {
|
|
183
|
+
return (
|
|
184
|
+
(isBytes(value) && value.length > 0) ||
|
|
185
|
+
(typeof value === "string" && value.includes("\n")) ||
|
|
186
|
+
(typeof value === "string" && hasNonAscii(value))
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Process root-level arrays
|
|
191
|
+
processRootArray(key, array) {
|
|
192
|
+
const analysis = analyzeArray(array)
|
|
193
|
+
let bodyPartCounter = 1
|
|
194
|
+
|
|
195
|
+
if (analysis.hasArraysOfObjects) {
|
|
196
|
+
// Handle arrays of arrays containing objects
|
|
197
|
+
array.forEach((item, index) => {
|
|
198
|
+
if (Array.isArray(item)) {
|
|
199
|
+
item.forEach((subItem, subIndex) => {
|
|
200
|
+
if (isPojo(subItem)) {
|
|
201
|
+
this.keys.push(`${key}/${index + 1}/${subIndex + 1}`)
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
this.keys.push(key)
|
|
207
|
+
} else if (
|
|
208
|
+
analysis.hasObjects &&
|
|
209
|
+
(analysis.hasEmptyStrings || analysis.hasEmptyObjects) &&
|
|
210
|
+
!analysis.hasObjectsWithOnlyEmptyValues
|
|
211
|
+
) {
|
|
212
|
+
// Mixed array: only non-empty objects get parts
|
|
213
|
+
array.forEach(item => {
|
|
214
|
+
if (isPojo(item) && Object.keys(item).length > 0) {
|
|
215
|
+
const path = `${key}/${bodyPartCounter}`
|
|
216
|
+
this.keys.push(path)
|
|
217
|
+
this.addNestedObjectPaths(item, path)
|
|
218
|
+
}
|
|
219
|
+
bodyPartCounter++
|
|
220
|
+
})
|
|
221
|
+
this.keys.push(key)
|
|
222
|
+
} else if (analysis.hasObjects) {
|
|
223
|
+
// Regular array with objects
|
|
224
|
+
const skipEmptyObjects = analysis.hasOnlyEmptyElements
|
|
225
|
+
|
|
226
|
+
array.forEach(item => {
|
|
227
|
+
if (isPojo(item)) {
|
|
228
|
+
if (!(skipEmptyObjects && Object.keys(item).length === 0)) {
|
|
229
|
+
const path = `${key}/${bodyPartCounter}`
|
|
230
|
+
this.keys.push(path)
|
|
231
|
+
if (Object.keys(item).length > 0) {
|
|
232
|
+
this.addNestedObjectPaths(item, path)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else if (typeof item === "string" && item === "") {
|
|
236
|
+
this.keys.push(`${key}/${bodyPartCounter}`)
|
|
237
|
+
}
|
|
238
|
+
bodyPartCounter++
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// Add main array key conditionally
|
|
242
|
+
if (!analysis.hasObjectsWithOnlyEmptyValues || analysis.hasNonObjects) {
|
|
243
|
+
if (!analysis.hasOnlyEmptyElements) {
|
|
244
|
+
this.keys.push(key)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
// Simple array without objects
|
|
249
|
+
const hasOnlyEmptyArraysOrObjects = array.every(
|
|
250
|
+
item =>
|
|
251
|
+
isEmpty(item) &&
|
|
252
|
+
(Array.isArray(item) || isPojo(item) || typeof item === "string")
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if (hasOnlyEmptyArraysOrObjects || !array.every(isEmpty)) {
|
|
256
|
+
this.keys.push(key)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Add paths for nested objects within an object
|
|
262
|
+
addNestedObjectPaths(obj, basePath) {
|
|
263
|
+
for (const [nestedKey, nestedValue] of Object.entries(obj)) {
|
|
264
|
+
if (isPojo(nestedValue) && Object.keys(nestedValue).length > 0) {
|
|
265
|
+
this.keys.push(`${basePath}/${nestedKey}`)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Process root-level nested object
|
|
271
|
+
processRootNestedObject(key, obj) {
|
|
272
|
+
// Check for arrays with only empty elements
|
|
273
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
274
|
+
if (Array.isArray(v) && v.length > 0 && v.every(isEmpty)) {
|
|
275
|
+
this.keys.push(`${key}/${k}`)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Traverse the object
|
|
280
|
+
this.traverse(obj, key)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Traverse nested structures
|
|
284
|
+
traverse(current, path) {
|
|
285
|
+
let hasSimpleFields = false
|
|
286
|
+
const nestedPaths = []
|
|
287
|
+
let hasArraysWithObjects = false
|
|
288
|
+
|
|
289
|
+
// First pass: analyze structure
|
|
290
|
+
for (const [key, value] of Object.entries(current)) {
|
|
291
|
+
const fullPath = path ? `${path}/${key}` : key
|
|
292
|
+
const result = this.analyzeFieldInTraversal(value, fullPath, nestedPaths)
|
|
293
|
+
|
|
294
|
+
hasSimpleFields = hasSimpleFields || result.hasSimpleFields
|
|
295
|
+
hasArraysWithObjects = hasArraysWithObjects || result.hasArraysWithObjects
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Add current path if needed
|
|
299
|
+
if (hasSimpleFields || (hasArraysWithObjects && path)) {
|
|
300
|
+
this.keys.push(path)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Second pass: check for arrays with only empty elements
|
|
304
|
+
for (const [key, value] of Object.entries(current)) {
|
|
305
|
+
const fullPath = path ? `${path}/${key}` : key
|
|
306
|
+
if (Array.isArray(value) && value.length > 0 && value.every(isEmpty)) {
|
|
307
|
+
this.keys.push(fullPath)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Traverse nested objects
|
|
312
|
+
for (const nestedPath of nestedPaths) {
|
|
313
|
+
const nestedObj = getValueByPath(this.obj, nestedPath)
|
|
314
|
+
if (isPojo(nestedObj)) {
|
|
315
|
+
this.traverse(nestedObj, nestedPath)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Analyze a field during traversal
|
|
321
|
+
analyzeFieldInTraversal(value, fullPath, nestedPaths) {
|
|
322
|
+
let hasSimpleFields = false
|
|
323
|
+
let hasArraysWithObjects = false
|
|
324
|
+
|
|
325
|
+
if (Array.isArray(value)) {
|
|
326
|
+
if (value.length === 0) {
|
|
327
|
+
hasSimpleFields = true
|
|
328
|
+
} else {
|
|
329
|
+
const analysis = analyzeArray(value)
|
|
330
|
+
|
|
331
|
+
if (analysis.hasObjects) {
|
|
332
|
+
hasArraysWithObjects = true
|
|
333
|
+
this.processArrayInTraversal(value, fullPath, nestedPaths, analysis)
|
|
334
|
+
|
|
335
|
+
if (analysis.hasNonObjects) {
|
|
336
|
+
hasSimpleFields = true
|
|
337
|
+
// Only add to keys if array can't be in header
|
|
338
|
+
if (!canArrayBeInHeader(value)) {
|
|
339
|
+
this.keys.push(fullPath)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
hasSimpleFields = true
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} else if (isPojo(value)) {
|
|
347
|
+
if (Object.keys(value).length === 0) {
|
|
348
|
+
hasSimpleFields = true
|
|
349
|
+
} else if (hasOnlyEmptyValues(value)) {
|
|
350
|
+
this.keys.push(fullPath)
|
|
351
|
+
} else {
|
|
352
|
+
const hasArraysWithOnlyEmptyElements = Object.entries(value).some(
|
|
353
|
+
([k, v]) => Array.isArray(v) && v.length > 0 && v.every(isEmpty)
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
if (hasArraysWithOnlyEmptyElements) {
|
|
357
|
+
this.keys.push(fullPath)
|
|
358
|
+
}
|
|
359
|
+
nestedPaths.push(fullPath)
|
|
360
|
+
}
|
|
361
|
+
} else if ((isBytes(value) && value.length > 0) || isSimpleValue(value)) {
|
|
362
|
+
hasSimpleFields = true
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return { hasSimpleFields, hasArraysWithObjects }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Process arrays found during traversal
|
|
369
|
+
processArrayInTraversal(array, fullPath, nestedPaths, analysis) {
|
|
370
|
+
if (
|
|
371
|
+
(analysis.hasEmptyStrings || analysis.hasEmptyObjects) &&
|
|
372
|
+
analysis.hasNonEmptyObjects
|
|
373
|
+
) {
|
|
374
|
+
// Special case: only non-empty objects get parts
|
|
375
|
+
array.forEach((item, index) => {
|
|
376
|
+
if (isPojo(item) && Object.keys(item).length > 0) {
|
|
377
|
+
const itemPath = `${fullPath}/${index + 1}`
|
|
378
|
+
this.keys.push(itemPath)
|
|
379
|
+
nestedPaths.push(itemPath)
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
} else if (
|
|
383
|
+
analysis.hasObjectsWithOnlyEmptyValues &&
|
|
384
|
+
!analysis.hasNonObjects
|
|
385
|
+
) {
|
|
386
|
+
// Objects with only empty values
|
|
387
|
+
array.forEach((item, index) => {
|
|
388
|
+
if (isPojo(item)) {
|
|
389
|
+
const itemPath = `${fullPath}/${index + 1}`
|
|
390
|
+
this.keys.push(itemPath)
|
|
391
|
+
if (Object.keys(item).length > 0) {
|
|
392
|
+
nestedPaths.push(itemPath)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
})
|
|
396
|
+
} else {
|
|
397
|
+
// Normal case
|
|
398
|
+
array.forEach((item, index) => {
|
|
399
|
+
if (isPojo(item)) {
|
|
400
|
+
const itemPath = `${fullPath}/${index + 1}`
|
|
401
|
+
this.keys.push(itemPath)
|
|
402
|
+
if (Object.keys(item).length > 0) {
|
|
403
|
+
nestedPaths.push(itemPath)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Deduplicate and filter results
|
|
411
|
+
deduplicateAndFilter() {
|
|
412
|
+
const uniqueKeys = [...new Set(this.keys)]
|
|
413
|
+
|
|
414
|
+
return uniqueKeys.filter(k => {
|
|
415
|
+
if (!k) return false
|
|
416
|
+
|
|
417
|
+
// Check if this is a path to an element inside an array with only empty elements
|
|
418
|
+
const parts = k.split("/")
|
|
419
|
+
if (parts.length >= 2 && /^\d+$/.test(parts[parts.length - 1])) {
|
|
420
|
+
const arrayPath = parts.slice(0, -1).join("/")
|
|
421
|
+
const arrayValue = getValueByPath(this.obj, arrayPath)
|
|
422
|
+
|
|
423
|
+
if (Array.isArray(arrayValue) && arrayValue.every(isEmpty)) {
|
|
424
|
+
return false
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return true
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export default function collectBodyKeys(obj, prefix = "") {
|
|
434
|
+
const collector = new BodyKeyCollector(obj)
|
|
435
|
+
return collector.collect()
|
|
436
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { toBuffer, formatFloat, isBytes, isPojo } from "./encode-utils.js"
|
|
2
|
+
|
|
3
|
+
// Helper to generate the correct number of backslashes for a given nesting level
|
|
4
|
+
function getBackslashes(level) {
|
|
5
|
+
return "\\".repeat(Math.pow(2, level) - 1)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Helper to encode primitive values at a specific nesting level
|
|
9
|
+
function encodePrimitiveAtLevel(value, level) {
|
|
10
|
+
const bs = getBackslashes(level)
|
|
11
|
+
|
|
12
|
+
if (typeof value === "number") {
|
|
13
|
+
if (Number.isInteger(value)) {
|
|
14
|
+
return `${bs}"(ao-type-integer) ${value}${bs}"`
|
|
15
|
+
} else {
|
|
16
|
+
return `${bs}"(ao-type-float) ${formatFloat(value)}${bs}"`
|
|
17
|
+
}
|
|
18
|
+
} else if (typeof value === "string") {
|
|
19
|
+
return `${bs}"${value}${bs}"`
|
|
20
|
+
} else if (value === null) {
|
|
21
|
+
const innerBs = getBackslashes(level + 1)
|
|
22
|
+
return `${bs}"(ao-type-atom) ${innerBs}"null${innerBs}"${bs}"`
|
|
23
|
+
} else if (value === undefined) {
|
|
24
|
+
const innerBs = getBackslashes(level + 1)
|
|
25
|
+
return `${bs}"(ao-type-atom) ${innerBs}"undefined${innerBs}"${bs}"`
|
|
26
|
+
} else if (typeof value === "symbol") {
|
|
27
|
+
const desc = value.description || "Symbol.for()"
|
|
28
|
+
const innerBs = getBackslashes(level + 1)
|
|
29
|
+
return `${bs}"(ao-type-atom) ${innerBs}"${desc}${innerBs}"${bs}"`
|
|
30
|
+
} else if (typeof value === "boolean") {
|
|
31
|
+
const innerBs = getBackslashes(level + 1)
|
|
32
|
+
return `${bs}"(ao-type-atom) ${innerBs}"${value}${innerBs}"${bs}"`
|
|
33
|
+
} else {
|
|
34
|
+
return `${bs}"${String(value)}${bs}"`
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Recursive function to handle arrays at any nesting level
|
|
39
|
+
function encodeArrayAtLevel(items, level) {
|
|
40
|
+
// The original code only goes 3 levels deep for arrays
|
|
41
|
+
if (level >= 3) {
|
|
42
|
+
const bs = getBackslashes(level)
|
|
43
|
+
const stringItems = items
|
|
44
|
+
.map(item => {
|
|
45
|
+
if (typeof item === "number") {
|
|
46
|
+
if (Number.isInteger(item)) {
|
|
47
|
+
return `${bs}"(ao-type-integer) ${item}${bs}"`
|
|
48
|
+
} else {
|
|
49
|
+
return `${bs}"(ao-type-float) ${formatFloat(item)}${bs}"`
|
|
50
|
+
}
|
|
51
|
+
} else if (typeof item === "string") {
|
|
52
|
+
return `${bs}"${item}${bs}"`
|
|
53
|
+
} else {
|
|
54
|
+
return `${bs}"${String(item)}${bs}"`
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
.join(", ")
|
|
58
|
+
return `${getBackslashes(level - 1)}"(ao-type-list) ${stringItems}${getBackslashes(level - 1)}"`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const encodedItems = items
|
|
62
|
+
.map(item => {
|
|
63
|
+
if (Array.isArray(item)) {
|
|
64
|
+
return encodeArrayAtLevel(item, level + 1)
|
|
65
|
+
} else {
|
|
66
|
+
return encodePrimitiveAtLevel(item, level)
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.join(", ")
|
|
70
|
+
|
|
71
|
+
if (level === 0) {
|
|
72
|
+
return `"(ao-type-list) ${encodedItems}"`
|
|
73
|
+
} else {
|
|
74
|
+
const bs = getBackslashes(level - 1)
|
|
75
|
+
return `${bs}"(ao-type-list) ${encodedItems}${bs}"`
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default function encodeArrayItem(item) {
|
|
80
|
+
if (typeof item === "number") {
|
|
81
|
+
if (Number.isInteger(item)) {
|
|
82
|
+
return `"(ao-type-integer) ${item}"`
|
|
83
|
+
} else {
|
|
84
|
+
return `"(ao-type-float) ${formatFloat(item)}"`
|
|
85
|
+
}
|
|
86
|
+
} else if (typeof item === "string") {
|
|
87
|
+
return `"${item}"`
|
|
88
|
+
} else if (item === null) {
|
|
89
|
+
return `"(ao-type-atom) \\"null\\""`
|
|
90
|
+
} else if (item === undefined) {
|
|
91
|
+
return `"(ao-type-atom) \\"undefined\\""`
|
|
92
|
+
} else if (typeof item === "symbol") {
|
|
93
|
+
const desc = item.description || "Symbol.for()"
|
|
94
|
+
return `"(ao-type-atom) \\"${desc}\\""`
|
|
95
|
+
} else if (typeof item === "boolean") {
|
|
96
|
+
return `"(ao-type-atom) \\"${item}\\""`
|
|
97
|
+
} else if (Array.isArray(item)) {
|
|
98
|
+
return encodeArrayAtLevel(item, 1)
|
|
99
|
+
} else if (isBytes(item)) {
|
|
100
|
+
const buffer = toBuffer(item)
|
|
101
|
+
if (buffer.length === 0 || buffer.byteLength === 0) {
|
|
102
|
+
return `""`
|
|
103
|
+
}
|
|
104
|
+
return `"(ao-type-binary)"`
|
|
105
|
+
} else if (isPojo(item)) {
|
|
106
|
+
const json = JSON.stringify(item)
|
|
107
|
+
const escaped = json.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
|
108
|
+
return `"(ao-type-map) ${escaped}"`
|
|
109
|
+
} else {
|
|
110
|
+
return `"${String(item)}"`
|
|
111
|
+
}
|
|
112
|
+
}
|