@tchain/tcabci-read-js-client 2.7.12 → 2.7.13
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/README.md +62 -46
- package/block.d.ts +19 -10
- package/block.js +39 -41
- package/breaker.d.ts +5 -1
- package/breaker.js +47 -44
- package/bytea.d.ts +1 -0
- package/bytea.js +16 -0
- package/callback.d.ts +12 -7
- package/client.d.ts +170 -119
- package/client.js +465 -315
- package/errors.d.ts +16 -2
- package/errors.js +5 -1
- package/http.d.ts +31 -6
- package/http.js +286 -51
- package/message.d.ts +48 -45
- package/message.js +77 -53
- package/package.json +22 -17
- package/transaction.d.ts +178 -92
- package/transaction.js +96 -69
- package/types.d.ts +60 -0
- package/util.d.ts +4 -0
- package/util.js +20 -6
- package/validation.d.ts +28 -0
- package/validation.js +155 -0
- package/websocket.d.ts +35 -15
- package/websocket.js +129 -43
- package/websocketOptions.d.ts +48 -10
- package/websocketOptions.js +53 -23
package/transaction.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { INVALID_ARGUMENT_WITH_CS } from './errors.js'
|
|
2
|
-
import {
|
|
2
|
+
import { cloneAndFreeze, rfc3339Timestamp } from './validation.js'
|
|
3
3
|
|
|
4
4
|
export const TX_TYPE_MASTER = 'initial_storage',
|
|
5
5
|
TX_TYPE_ADDRESS = 'interim_storage',
|
|
@@ -58,7 +58,8 @@ export const TX_TYPE_MASTER = 'initial_storage',
|
|
|
58
58
|
TX_TYPE_COLLECTION_POLICY = 'coll_policy',
|
|
59
59
|
TX_TYPE_DATAV2_COLLECTION = 'datav2_coll',
|
|
60
60
|
TX_TYPE_DATAV2F = 'datav2F',
|
|
61
|
-
|
|
61
|
+
/** @type {ReadonlyArray<import('./transaction.js').TXType>} */
|
|
62
|
+
TX_TYPE_LIST = Object.freeze([
|
|
62
63
|
TX_TYPE_MASTER,
|
|
63
64
|
TX_TYPE_ADDRESS,
|
|
64
65
|
TX_TYPE_ADDRESSES,
|
|
@@ -116,7 +117,7 @@ export const TX_TYPE_MASTER = 'initial_storage',
|
|
|
116
117
|
TX_TYPE_COLLECTION_POLICY,
|
|
117
118
|
TX_TYPE_DATAV2_COLLECTION,
|
|
118
119
|
TX_TYPE_DATAV2F
|
|
119
|
-
]
|
|
120
|
+
])
|
|
120
121
|
|
|
121
122
|
export class Transaction {
|
|
122
123
|
_order
|
|
@@ -148,7 +149,7 @@ export class Transaction {
|
|
|
148
149
|
* @param {string} sign
|
|
149
150
|
* @param {number} fee
|
|
150
151
|
* @param {string} hash
|
|
151
|
-
* @param {Date} inserted_at
|
|
152
|
+
* @param {string|number|Date} [inserted_at] Date text/RFC3339Nano, Unix milliseconds or Date.
|
|
152
153
|
* @param {?string} additionalData
|
|
153
154
|
* @param {?string} cipherData
|
|
154
155
|
* @param {?string} chainName
|
|
@@ -182,10 +183,17 @@ export class Transaction {
|
|
|
182
183
|
this._sign = sign
|
|
183
184
|
this._fee = fee
|
|
184
185
|
this._hash = hash
|
|
185
|
-
this._inserted_at =
|
|
186
|
+
this._inserted_at =
|
|
187
|
+
typeof inserted_at === 'undefined'
|
|
188
|
+
? undefined
|
|
189
|
+
: rfc3339Timestamp(inserted_at)
|
|
190
|
+
|
|
186
191
|
if (additionalData) this._additionalData = additionalData
|
|
192
|
+
|
|
187
193
|
if (cipherData) this._cipherData = cipherData
|
|
194
|
+
|
|
188
195
|
if (chainName) this._chainName = chainName
|
|
196
|
+
|
|
189
197
|
if (chainVersion) this._chainVersion = chainVersion
|
|
190
198
|
}
|
|
191
199
|
|
|
@@ -217,14 +225,17 @@ export class Transaction {
|
|
|
217
225
|
return this._recipient_addr
|
|
218
226
|
}
|
|
219
227
|
|
|
228
|
+
/** @returns {import('./types.js').JSONValue} Factory data is a deeply frozen snapshot. */
|
|
220
229
|
get Data() {
|
|
221
230
|
return this._data
|
|
222
231
|
}
|
|
223
232
|
|
|
233
|
+
/** @returns {import('./types.js').JSONValue|undefined} Factory data is a deeply frozen snapshot. */
|
|
224
234
|
get AdditionalData() {
|
|
225
235
|
return this._additionalData
|
|
226
236
|
}
|
|
227
237
|
|
|
238
|
+
/** @returns {import('./types.js').JSONValue|undefined} Factory data is a deeply frozen snapshot. */
|
|
228
239
|
get CipherData() {
|
|
229
240
|
return this._cipherData
|
|
230
241
|
}
|
|
@@ -241,8 +252,11 @@ export class Transaction {
|
|
|
241
252
|
return this._hash
|
|
242
253
|
}
|
|
243
254
|
|
|
255
|
+
/** @returns {Date|undefined} Detached Date converted from date text or Unix milliseconds. */
|
|
244
256
|
get InsertedAt() {
|
|
245
|
-
return this._inserted_at
|
|
257
|
+
return typeof this._inserted_at !== 'undefined'
|
|
258
|
+
? new Date(this._inserted_at)
|
|
259
|
+
: undefined
|
|
246
260
|
}
|
|
247
261
|
|
|
248
262
|
get ChainName() {
|
|
@@ -253,50 +267,19 @@ export class Transaction {
|
|
|
253
267
|
return this._chainVersion
|
|
254
268
|
}
|
|
255
269
|
|
|
270
|
+
/** @param {string} value @returns {import('./types.js').ParseResult<'transaction', Readonly<Transaction>>} */
|
|
256
271
|
static FromJSON(value) {
|
|
257
272
|
if (typeof value !== 'string')
|
|
258
|
-
return {
|
|
259
|
-
transaction: null,
|
|
260
|
-
error: new Error('invalid json payload')
|
|
261
|
-
}
|
|
273
|
+
return { transaction: null, error: new Error('Invalid transaction JSON') }
|
|
262
274
|
|
|
263
275
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (typeof parsed.order !== 'undefined') tx._order = parsed.order
|
|
268
|
-
tx._id = parsed.id
|
|
269
|
-
tx._typ = parsed.typ
|
|
270
|
-
if (typeof parsed.identifier !== 'undefined')
|
|
271
|
-
tx._identifier = parsed.identifier
|
|
272
|
-
tx._height = parsed.height
|
|
273
|
-
tx._version = parsed.version
|
|
274
|
-
tx._sender_addr = parsed.sender_addr
|
|
275
|
-
tx._recipient_addr = parsed.recipient_addr
|
|
276
|
-
tx._data = parsed.data
|
|
277
|
-
if (typeof parsed.additional_data !== 'undefined')
|
|
278
|
-
tx._additionalData = parsed.additional_data
|
|
279
|
-
if (typeof parsed.cipher_data !== 'undefined')
|
|
280
|
-
tx._cipherData = parsed.cipher_data
|
|
281
|
-
tx._sign = parsed.sign
|
|
282
|
-
tx._fee = parsed.fee
|
|
283
|
-
tx._hash = parsed.hash
|
|
284
|
-
if (typeof parsed.inserted_at !== 'undefined')
|
|
285
|
-
tx._inserted_at = new Date(parsed.inserted_at)
|
|
286
|
-
if (typeof parsed.chain_name !== 'undefined')
|
|
287
|
-
this._chainName = parsed.chain_name
|
|
288
|
-
if (typeof parsed.chain_version !== 'undefined')
|
|
289
|
-
this._chainVersion = parsed.chain_version
|
|
290
|
-
|
|
291
|
-
tx.Validate()
|
|
292
|
-
|
|
293
|
-
return { transaction: tx, error: null }
|
|
294
|
-
} catch (e) {
|
|
295
|
-
Debug.error(e)
|
|
296
|
-
return { transaction: null, error: e }
|
|
276
|
+
return Transaction.FromObject(JSON.parse(value))
|
|
277
|
+
} catch {
|
|
278
|
+
return { transaction: null, error: new Error('Invalid transaction JSON') }
|
|
297
279
|
}
|
|
298
280
|
}
|
|
299
281
|
|
|
282
|
+
/** @param {Record<string, unknown>} obj @returns {import('./types.js').ParseResult<'transaction', Readonly<Transaction>>} */
|
|
300
283
|
static FromObject(obj) {
|
|
301
284
|
if (!obj)
|
|
302
285
|
return {
|
|
@@ -308,33 +291,45 @@ export class Transaction {
|
|
|
308
291
|
const tx = new Transaction()
|
|
309
292
|
|
|
310
293
|
if (typeof obj.order !== 'undefined') tx._order = obj.order
|
|
294
|
+
|
|
311
295
|
tx._id = obj.id
|
|
312
296
|
tx._typ = obj.typ
|
|
313
|
-
|
|
297
|
+
|
|
298
|
+
if (typeof obj.identifier !== 'undefined')
|
|
299
|
+
tx._identifier = cloneAndFreeze(obj.identifier)
|
|
300
|
+
|
|
314
301
|
tx._height = obj.height
|
|
315
302
|
tx._version = obj.version
|
|
316
303
|
tx._sender_addr = obj.sender_addr
|
|
317
304
|
tx._recipient_addr = obj.recipient_addr
|
|
318
|
-
tx._data = obj.data
|
|
305
|
+
tx._data = cloneAndFreeze(obj.data)
|
|
306
|
+
|
|
319
307
|
if (typeof obj.additional_data !== 'undefined')
|
|
320
|
-
tx._additionalData = obj.additional_data
|
|
308
|
+
tx._additionalData = cloneAndFreeze(obj.additional_data)
|
|
309
|
+
|
|
321
310
|
if (typeof obj.cipher_data !== 'undefined')
|
|
322
|
-
tx._cipherData = obj.cipher_data
|
|
311
|
+
tx._cipherData = cloneAndFreeze(obj.cipher_data)
|
|
312
|
+
|
|
323
313
|
tx._sign = obj.sign
|
|
324
314
|
tx._fee = obj.fee
|
|
325
315
|
tx._hash = obj.hash
|
|
316
|
+
|
|
326
317
|
if (typeof obj.inserted_at !== 'undefined')
|
|
327
|
-
tx._inserted_at =
|
|
318
|
+
tx._inserted_at = rfc3339Timestamp(obj.inserted_at)
|
|
319
|
+
|
|
328
320
|
if (typeof obj.chain_name !== 'undefined') tx._chainName = obj.chain_name
|
|
321
|
+
|
|
329
322
|
if (typeof obj.chain_version !== 'undefined')
|
|
330
323
|
tx._chainVersion = obj.chain_version
|
|
331
324
|
|
|
332
325
|
tx.Validate()
|
|
333
326
|
|
|
334
|
-
return { transaction: tx, error: null }
|
|
335
|
-
} catch
|
|
336
|
-
|
|
337
|
-
|
|
327
|
+
return { transaction: Object.freeze(tx), error: null }
|
|
328
|
+
} catch {
|
|
329
|
+
return {
|
|
330
|
+
transaction: null,
|
|
331
|
+
error: new Error('Invalid transaction payload')
|
|
332
|
+
}
|
|
338
333
|
}
|
|
339
334
|
}
|
|
340
335
|
|
|
@@ -355,9 +350,9 @@ export class Transaction {
|
|
|
355
350
|
data: this._data,
|
|
356
351
|
sign: this._sign,
|
|
357
352
|
fee: this._fee,
|
|
358
|
-
hash: this._hash,
|
|
359
|
-
...(this._inserted_at
|
|
360
|
-
? { inserted_at: this._inserted_at.toISOString() }
|
|
353
|
+
...(typeof this._hash !== 'undefined' ? { hash: this._hash } : {}),
|
|
354
|
+
...(typeof this._inserted_at !== 'undefined'
|
|
355
|
+
? { inserted_at: new Date(this._inserted_at).toISOString() }
|
|
361
356
|
: {}),
|
|
362
357
|
...(this._additionalData
|
|
363
358
|
? { additional_data: this._additionalData }
|
|
@@ -369,13 +364,13 @@ export class Transaction {
|
|
|
369
364
|
}
|
|
370
365
|
|
|
371
366
|
/**
|
|
372
|
-
* @return {
|
|
367
|
+
* @return {Readonly<Record<string, import('./types.js').JSONValue>>} Deeply frozen JSON snapshot.
|
|
373
368
|
* @throws Error
|
|
374
369
|
*/
|
|
375
370
|
ToObject() {
|
|
376
371
|
this.Validate()
|
|
377
372
|
|
|
378
|
-
return {
|
|
373
|
+
return cloneAndFreeze({
|
|
379
374
|
id: this._id,
|
|
380
375
|
height: this._height,
|
|
381
376
|
version: this._version,
|
|
@@ -385,9 +380,9 @@ export class Transaction {
|
|
|
385
380
|
data: this._data,
|
|
386
381
|
sign: this._sign,
|
|
387
382
|
fee: this._fee,
|
|
388
|
-
hash: this._hash,
|
|
389
|
-
...(this._inserted_at
|
|
390
|
-
? { inserted_at: this._inserted_at.toISOString() }
|
|
383
|
+
...(typeof this._hash !== 'undefined' ? { hash: this._hash } : {}),
|
|
384
|
+
...(typeof this._inserted_at !== 'undefined'
|
|
385
|
+
? { inserted_at: new Date(this._inserted_at).toISOString() }
|
|
391
386
|
: {}),
|
|
392
387
|
...(this._additionalData
|
|
393
388
|
? { additional_data: this._additionalData }
|
|
@@ -395,37 +390,69 @@ export class Transaction {
|
|
|
395
390
|
...(this._cipherData ? { cipher_data: this._cipherData } : {}),
|
|
396
391
|
...(this._chainName ? { chain_name: this._chainName } : {}),
|
|
397
392
|
...(this._chainVersion ? { chain_version: this._chainVersion } : {})
|
|
398
|
-
}
|
|
393
|
+
})
|
|
399
394
|
}
|
|
400
395
|
|
|
401
396
|
Validate() {
|
|
402
|
-
if (
|
|
397
|
+
if (
|
|
398
|
+
typeof this._order !== 'undefined' &&
|
|
399
|
+
(!Number.isSafeInteger(this._order) || this._order < 0)
|
|
400
|
+
)
|
|
401
|
+
throw new Error(INVALID_ARGUMENT_WITH_CS('order'))
|
|
402
|
+
|
|
403
|
+
if (typeof this._hash !== 'undefined' && typeof this._hash !== 'string')
|
|
404
|
+
throw new Error(INVALID_ARGUMENT_WITH_CS('hash'))
|
|
405
|
+
|
|
406
|
+
if (typeof this._id !== 'string' || !this._id.length)
|
|
403
407
|
throw new Error(INVALID_ARGUMENT_WITH_CS('id'))
|
|
404
|
-
|
|
408
|
+
|
|
409
|
+
if (!Number.isSafeInteger(this._height) || this._height < 0)
|
|
405
410
|
throw new Error(INVALID_ARGUMENT_WITH_CS('height'))
|
|
406
|
-
|
|
411
|
+
|
|
412
|
+
if (!Number.isSafeInteger(this._version) || this._version < 0)
|
|
407
413
|
throw new Error(INVALID_ARGUMENT_WITH_CS('version'))
|
|
414
|
+
|
|
408
415
|
if (typeof this._typ !== 'string')
|
|
409
416
|
throw new Error(INVALID_ARGUMENT_WITH_CS('typ'))
|
|
417
|
+
|
|
410
418
|
if (!TX_TYPE_LIST.includes(this._typ))
|
|
411
419
|
throw new Error(INVALID_ARGUMENT_WITH_CS('typ'))
|
|
412
|
-
|
|
420
|
+
|
|
421
|
+
if (
|
|
422
|
+
typeof this._sender_addr !== 'string' ||
|
|
423
|
+
!this._sender_addr.length ||
|
|
424
|
+
this._sender_addr.length > 2048
|
|
425
|
+
)
|
|
413
426
|
throw new Error(INVALID_ARGUMENT_WITH_CS('senderAddr'))
|
|
414
|
-
|
|
427
|
+
|
|
428
|
+
if (
|
|
429
|
+
typeof this._recipient_addr !== 'string' ||
|
|
430
|
+
!this._recipient_addr.length ||
|
|
431
|
+
this._recipient_addr.length > 2048
|
|
432
|
+
)
|
|
415
433
|
throw new Error(INVALID_ARGUMENT_WITH_CS('recipientAddr'))
|
|
434
|
+
|
|
416
435
|
if (typeof this._data === 'undefined')
|
|
417
436
|
throw new Error(INVALID_ARGUMENT_WITH_CS('data'))
|
|
418
|
-
|
|
437
|
+
|
|
438
|
+
if (typeof this._sign !== 'string' || !this._sign.length)
|
|
419
439
|
throw new Error(INVALID_ARGUMENT_WITH_CS('sign'))
|
|
420
|
-
|
|
440
|
+
|
|
441
|
+
if (!Number.isFinite(this._fee) || this._fee < 0)
|
|
421
442
|
throw new Error(INVALID_ARGUMENT_WITH_CS('fee'))
|
|
422
|
-
|
|
443
|
+
|
|
444
|
+
if (
|
|
445
|
+
typeof this._inserted_at !== 'undefined' &&
|
|
446
|
+
!Number.isFinite(this._inserted_at)
|
|
447
|
+
)
|
|
423
448
|
throw new Error(INVALID_ARGUMENT_WITH_CS('insertedAt'))
|
|
449
|
+
|
|
424
450
|
if (
|
|
425
451
|
typeof this._chainName !== 'undefined' &&
|
|
426
452
|
typeof this._chainName !== 'string'
|
|
427
453
|
)
|
|
428
454
|
throw new Error(INVALID_ARGUMENT_WITH_CS('chainName'))
|
|
455
|
+
|
|
429
456
|
if (
|
|
430
457
|
typeof this._chainVersion !== 'undefined' &&
|
|
431
458
|
typeof this._chainVersion !== 'string'
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Immutable JSON snapshot; byte buffers are not JSON values. */
|
|
2
|
+
export type JSONValue =
|
|
3
|
+
| null
|
|
4
|
+
| boolean
|
|
5
|
+
| number
|
|
6
|
+
| string
|
|
7
|
+
| readonly JSONValue[]
|
|
8
|
+
| { readonly [key: string]: JSONValue }
|
|
9
|
+
|
|
10
|
+
export type ParseResult<K extends string, T> =
|
|
11
|
+
| ({ [P in K]: T } & { error: null })
|
|
12
|
+
| ({ [P in K]: null } & { error: Error })
|
|
13
|
+
|
|
14
|
+
/** Total request deadline, including retries and response consumption. */
|
|
15
|
+
export interface RequestOptions {
|
|
16
|
+
signal?: AbortSignal
|
|
17
|
+
timeoutMs?: number
|
|
18
|
+
maxResponseBytes?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ClientOptions {
|
|
22
|
+
allowInsecure?: boolean
|
|
23
|
+
timeoutMs?: number
|
|
24
|
+
maxResponseBytes?: number
|
|
25
|
+
maxRequestBytes?: number
|
|
26
|
+
maxConcurrentRequests?: number
|
|
27
|
+
maxMessageBytes?: number
|
|
28
|
+
maxEnqueuedMessages?: number
|
|
29
|
+
maxPendingCallbacks?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Browser WebSocket and ws constructors can both be injected. */
|
|
33
|
+
export interface WebSocketLike {
|
|
34
|
+
readonly readyState: number
|
|
35
|
+
readonly bufferedAmount: number
|
|
36
|
+
binaryType: string
|
|
37
|
+
|
|
38
|
+
close(code?: number, reason?: string): void
|
|
39
|
+
|
|
40
|
+
send(data: any): void
|
|
41
|
+
|
|
42
|
+
addEventListener(type: string, listener: any): void
|
|
43
|
+
|
|
44
|
+
removeEventListener(type: string, listener: any): void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WebSocketConstructor {
|
|
48
|
+
new (url: string | URL, protocols?: string | string[]): WebSocketLike
|
|
49
|
+
|
|
50
|
+
readonly CONNECTING: number
|
|
51
|
+
readonly OPEN: number
|
|
52
|
+
readonly CLOSING: number
|
|
53
|
+
readonly CLOSED: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface SocketCloseEvent {
|
|
57
|
+
readonly code: number
|
|
58
|
+
readonly reason: string
|
|
59
|
+
readonly wasClean: boolean
|
|
60
|
+
}
|
package/util.d.ts
CHANGED
package/util.js
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Synchronously throw the supplied value without wrapping or retaining it.
|
|
3
|
+
* Catch/finally behavior and Error identity are preserved.
|
|
4
|
+
* @param {unknown} error The value to throw.
|
|
5
|
+
* @returns {never}
|
|
6
|
+
* @throws {unknown} The exact supplied value.
|
|
7
|
+
*/
|
|
8
|
+
export function SendThrow(error) {
|
|
9
|
+
throw error
|
|
10
|
+
}
|
|
2
11
|
|
|
12
|
+
/** Parse JSON without retaining untrusted input in parse errors. */
|
|
3
13
|
export function fromJSON(data) {
|
|
4
|
-
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(data)
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Error('Invalid JSON payload')
|
|
18
|
+
}
|
|
5
19
|
}
|
|
6
20
|
|
|
21
|
+
/** @param {string} str @returns {boolean} */
|
|
7
22
|
export function isJSON(str) {
|
|
8
23
|
try {
|
|
9
24
|
JSON.parse(str)
|
|
10
|
-
|
|
11
|
-
|
|
25
|
+
|
|
26
|
+
return true
|
|
27
|
+
} catch {
|
|
12
28
|
return false
|
|
13
29
|
}
|
|
14
|
-
|
|
15
|
-
return true
|
|
16
30
|
}
|
package/validation.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { JSONValue } from './types.js'
|
|
2
|
+
|
|
3
|
+
export declare const CLIENT_VERSION: string, MAX_ADDRESSES: number
|
|
4
|
+
|
|
5
|
+
export declare function positiveInteger(value: number, name: string): number
|
|
6
|
+
|
|
7
|
+
export declare function endpoint(
|
|
8
|
+
value: string,
|
|
9
|
+
protocols: readonly string[],
|
|
10
|
+
allowInsecure?: boolean
|
|
11
|
+
): URL
|
|
12
|
+
|
|
13
|
+
export declare function addresses(value: readonly string[]): string[]
|
|
14
|
+
|
|
15
|
+
export declare function signedData(
|
|
16
|
+
value: Readonly<Record<string, string>>
|
|
17
|
+
): Record<string, string>
|
|
18
|
+
|
|
19
|
+
export declare function byteLength(
|
|
20
|
+
value: string | ArrayBuffer | ArrayBufferView | Blob
|
|
21
|
+
): number
|
|
22
|
+
|
|
23
|
+
export declare function cloneAndFreeze(value: unknown): JSONValue
|
|
24
|
+
|
|
25
|
+
export declare function closeCode(value?: number | null): number
|
|
26
|
+
|
|
27
|
+
/** Convert date text/RFC3339Nano, Unix milliseconds or Date at millisecond precision. */
|
|
28
|
+
export declare function rfc3339Timestamp(value: string | number | Date): number
|
package/validation.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
export const CLIENT_VERSION = '2.7.13',
|
|
2
|
+
MAX_ADDRESSES = 251
|
|
3
|
+
|
|
4
|
+
export function positiveInteger(value, name) {
|
|
5
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
6
|
+
throw new TypeError(`Invalid ${name}`)
|
|
7
|
+
|
|
8
|
+
return value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function endpoint(value, protocols, allowInsecure = false) {
|
|
12
|
+
let url
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
url = new URL(value)
|
|
16
|
+
} catch {
|
|
17
|
+
throw new TypeError('Invalid endpoint')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
!protocols.includes(url.protocol) ||
|
|
22
|
+
url.username ||
|
|
23
|
+
url.password ||
|
|
24
|
+
url.hash
|
|
25
|
+
)
|
|
26
|
+
throw new TypeError('Invalid endpoint')
|
|
27
|
+
|
|
28
|
+
const local = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)
|
|
29
|
+
|
|
30
|
+
if (!allowInsecure && !local && ['http:', 'ws:'].includes(url.protocol))
|
|
31
|
+
throw new TypeError('Insecure endpoint requires allowInsecure')
|
|
32
|
+
|
|
33
|
+
return url
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function addresses(value) {
|
|
37
|
+
if (
|
|
38
|
+
!Array.isArray(value) ||
|
|
39
|
+
!value.length ||
|
|
40
|
+
value.length > MAX_ADDRESSES ||
|
|
41
|
+
value.some((v) => typeof v !== 'string' || !v.length || v.length > 2048)
|
|
42
|
+
)
|
|
43
|
+
throw new TypeError('Invalid addresses')
|
|
44
|
+
|
|
45
|
+
return [...new Set(value)]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function signedData(value) {
|
|
49
|
+
if (
|
|
50
|
+
!value ||
|
|
51
|
+
typeof value !== 'object' ||
|
|
52
|
+
Array.isArray(value) ||
|
|
53
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)) ||
|
|
54
|
+
Object.keys(value).length > MAX_ADDRESSES ||
|
|
55
|
+
Object.keys(value).some((key) => !key.length || key.length > 2048) ||
|
|
56
|
+
Object.values(value).some((v) => typeof v !== 'string' || v.length > 16384)
|
|
57
|
+
)
|
|
58
|
+
throw new TypeError('Invalid signed data')
|
|
59
|
+
|
|
60
|
+
return { ...value }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Counts UTF-8 bytes without producing another copy of message contents.
|
|
64
|
+
export function byteLength(value) {
|
|
65
|
+
if (typeof value === 'string') {
|
|
66
|
+
let size = 0
|
|
67
|
+
|
|
68
|
+
for (const point of value) {
|
|
69
|
+
const code = point.codePointAt(0)
|
|
70
|
+
|
|
71
|
+
size += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return size
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer)
|
|
78
|
+
return value.byteLength
|
|
79
|
+
|
|
80
|
+
if (typeof Blob !== 'undefined' && value instanceof Blob) return value.size
|
|
81
|
+
|
|
82
|
+
throw new TypeError('Invalid message payload')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Own and deeply freeze JSON snapshots without freezing caller objects.
|
|
87
|
+
* @param {unknown} value
|
|
88
|
+
* @param {number} [depth=0]
|
|
89
|
+
* @param {{count: number}} [budget={count: 0}]
|
|
90
|
+
* @returns {import('./types.js').JSONValue} Recursively readonly JSON value.
|
|
91
|
+
*/
|
|
92
|
+
export function cloneAndFreeze(value, depth = 0, budget = { count: 0 }) {
|
|
93
|
+
if (++budget.count > 100000 || depth > 32)
|
|
94
|
+
throw new TypeError('Data exceeds structural limit')
|
|
95
|
+
|
|
96
|
+
if (value === null || ['string', 'boolean'].includes(typeof value))
|
|
97
|
+
return value
|
|
98
|
+
|
|
99
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value
|
|
100
|
+
|
|
101
|
+
if (!value || typeof value !== 'object')
|
|
102
|
+
throw new TypeError('Invalid JSON data')
|
|
103
|
+
|
|
104
|
+
if (Array.isArray(value))
|
|
105
|
+
return Object.freeze(
|
|
106
|
+
value.map((item) => cloneAndFreeze(item, depth + 1, budget))
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if (![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
110
|
+
throw new TypeError('Invalid JSON object')
|
|
111
|
+
|
|
112
|
+
return Object.freeze(
|
|
113
|
+
Object.fromEntries(
|
|
114
|
+
Object.entries(value).map(([key, item]) => [
|
|
115
|
+
key,
|
|
116
|
+
cloneAndFreeze(item, depth + 1, budget)
|
|
117
|
+
])
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Browser-compatible explicit close code. */
|
|
123
|
+
export function closeCode(value = 1000) {
|
|
124
|
+
if (value === null) return 1000
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
value !== 1000 &&
|
|
128
|
+
(!Number.isInteger(value) || value < 3000 || value > 4999)
|
|
129
|
+
)
|
|
130
|
+
throw new TypeError('Invalid close code')
|
|
131
|
+
|
|
132
|
+
return value
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Convert date text (including RFC3339Nano), Unix milliseconds or a Date.
|
|
137
|
+
* Date precision is milliseconds; extra fractional digits are truncated.
|
|
138
|
+
* @param {string|number|Date} value Date-compatible input; numbers are milliseconds.
|
|
139
|
+
* @returns {number} Timestamp suitable for constructing a detached Date.
|
|
140
|
+
* @throws {TypeError} Invalid date; input is not disclosed.
|
|
141
|
+
*/
|
|
142
|
+
export function rfc3339Timestamp(value) {
|
|
143
|
+
if (!['string', 'number'].includes(typeof value) && !(value instanceof Date))
|
|
144
|
+
throw new TypeError('Invalid timestamp')
|
|
145
|
+
|
|
146
|
+
const input =
|
|
147
|
+
typeof value === 'string'
|
|
148
|
+
? value.replace(/(\.\d{3})\d+(?=[Zz]|[+-]\d{2}:\d{2}$)/, '$1')
|
|
149
|
+
: value,
|
|
150
|
+
timestamp = new Date(input).valueOf()
|
|
151
|
+
|
|
152
|
+
if (!Number.isFinite(timestamp)) throw new TypeError('Invalid timestamp')
|
|
153
|
+
|
|
154
|
+
return timestamp
|
|
155
|
+
}
|
package/websocket.d.ts
CHANGED
|
@@ -1,24 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SocketCloseEvent } from './types.js'
|
|
2
|
+
import { Options } from './websocketOptions.js'
|
|
2
3
|
|
|
3
4
|
export declare const MaxListenerSize: number
|
|
4
5
|
|
|
5
6
|
export declare class TWebSocket {
|
|
6
7
|
constructor(options: Options)
|
|
8
|
+
|
|
9
|
+
get active(): boolean
|
|
10
|
+
|
|
7
11
|
get ready(): boolean
|
|
8
|
-
|
|
9
|
-
get
|
|
10
|
-
|
|
11
|
-
get
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
|
|
13
|
+
get openListeners(): readonly ((event: Event) => void)[]
|
|
14
|
+
|
|
15
|
+
get messageListeners(): readonly ((event: MessageEvent) => void)[]
|
|
16
|
+
|
|
17
|
+
get errorListeners(): readonly ((event: Error | Event) => void)[]
|
|
18
|
+
|
|
19
|
+
get closeListeners(): readonly ((event: SocketCloseEvent) => void)[]
|
|
20
|
+
|
|
21
|
+
connect(force?: boolean): Promise<TWebSocket>
|
|
22
|
+
|
|
23
|
+
reconnect(code?: number): Promise<TWebSocket>
|
|
24
|
+
|
|
25
|
+
disconnect(code?: number): Promise<void>
|
|
26
|
+
|
|
27
|
+
send(message: string | ArrayBuffer | Blob | ArrayBufferView): void
|
|
28
|
+
|
|
16
29
|
addOpenListener(callback: (event: Event) => void): TWebSocket
|
|
30
|
+
|
|
17
31
|
removeOpenListener(callback: (event: Event) => void): TWebSocket
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
|
|
33
|
+
addMessageListener(callback: (event: MessageEvent) => void): TWebSocket
|
|
34
|
+
|
|
35
|
+
removeMessageListener(callback: (event: MessageEvent) => void): TWebSocket
|
|
36
|
+
|
|
37
|
+
addErrorListener(callback: (event: Error | Event) => void): TWebSocket
|
|
38
|
+
|
|
39
|
+
removeErrorListener(callback: (event: Error | Event) => void): TWebSocket
|
|
40
|
+
|
|
41
|
+
addCloseListener(callback: (event: SocketCloseEvent) => void): TWebSocket
|
|
42
|
+
|
|
43
|
+
removeCloseListener(callback: (event: SocketCloseEvent) => void): TWebSocket
|
|
24
44
|
}
|