cbor-x 1.4.1 → 1.5.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/encode.js CHANGED
@@ -4,13 +4,14 @@ try {
4
4
  textEncoder = new TextEncoder()
5
5
  } catch (error) {}
6
6
  let extensions, extensionClasses
7
+ const Buffer = globalThis.Buffer
7
8
  const hasNodeBuffer = typeof Buffer !== 'undefined'
8
9
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
9
10
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
10
- const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
11
11
  const MAX_STRUCTURES = 0x100
12
12
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
13
13
  let serializationId = 1
14
+ let throwOnIterable
14
15
  let target
15
16
  let targetView
16
17
  let position = 0
@@ -175,7 +176,10 @@ export class Encoder extends Decoder {
175
176
  }
176
177
  }
177
178
  }
179
+ throwOnIterable = encodeOptions & THROW_ON_ITERABLE;
178
180
  try {
181
+ if (throwOnIterable)
182
+ return;
179
183
  encode(value)
180
184
  if (bundledStrings) {
181
185
  writeBundles(start, encode)
@@ -536,6 +540,11 @@ export class Encoder extends Decoder {
536
540
  }
537
541
  }
538
542
  if (value[Symbol.iterator]) {
543
+ if (throwOnIterable) {
544
+ let error = new Error('Iterable should be serialized as iterator')
545
+ error.iteratorNotHandled = true;
546
+ throw error;
547
+ }
539
548
  target[position++] = 0x9f // indefinite length array
540
549
  for (let entry of value) {
541
550
  encode(entry)
@@ -543,6 +552,11 @@ export class Encoder extends Decoder {
543
552
  target[position++] = 0xff // stop-code
544
553
  return
545
554
  }
555
+ if (value[Symbol.asyncIterator] || isBlob(value)) {
556
+ let error = new Error('Iterable/blob should be serialized as iterator')
557
+ error.iteratorNotHandled = true;
558
+ throw error;
559
+ }
546
560
  // no extension found, write as object
547
561
  writeObject(value, !value.hasOwnProperty) // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
548
562
  }
@@ -707,7 +721,10 @@ export class Encoder extends Decoder {
707
721
  writeArrayHeader(length + 2)
708
722
  encode(0xe000 + recordId)
709
723
  encode(keys)
710
- for (let v of Object.values(object)) encode(v)
724
+ if (safePrototype === null) return; // special exit for iterator
725
+ for (let key in object)
726
+ if (safePrototype || object.hasOwnProperty(key))
727
+ encode(object[key])
711
728
  return
712
729
  }
713
730
  }
@@ -716,6 +733,7 @@ export class Encoder extends Decoder {
716
733
  } else {
717
734
  writeArrayHeader(length)
718
735
  }
736
+ if (safePrototype === null) return; // special exit for iterator
719
737
  for (let key in object)
720
738
  if (safePrototype || object.hasOwnProperty(key))
721
739
  encode(object[key])
@@ -741,6 +759,131 @@ export class Encoder extends Decoder {
741
759
  safeEnd = newBuffer.length - 10
742
760
  return target = newBuffer
743
761
  }
762
+ let chunkThreshold = 100;
763
+ let continuedChunkThreshold = 1000;
764
+ this.encodeAsIterable = function(value, options) {
765
+ return startEncoding(value, options, encodeObjectAsIterable);
766
+ }
767
+ this.encodeAsAsyncIterable = function(value, options) {
768
+ return startEncoding(value, options, encodeObjectAsAsyncIterable);
769
+ }
770
+
771
+ function* encodeObjectAsIterable(object, iterateProperties, finalIterable) {
772
+ let constructor = object.constructor;
773
+ if (constructor === Object) {
774
+ let useRecords = encoder.useRecords !== false;
775
+ if (useRecords)
776
+ writeObject(object, null); // write the record identifier
777
+ else
778
+ writeEntityLength(Object.keys(object).length, 0xa0);
779
+ for (let key in object) {
780
+ let value = object[key];
781
+ if (!useRecords) encode(key);
782
+ if (value && typeof value === 'object') {
783
+ if (iterateProperties[key])
784
+ yield* encodeObjectAsIterable(value, iterateProperties[key]);
785
+ else
786
+ yield* tryEncode(value, iterateProperties, key);
787
+ } else encode(value);
788
+ }
789
+ } else if (constructor === Array) {
790
+ let length = object.length;
791
+ writeArrayHeader(length);
792
+ for (let i = 0; i < length; i++) {
793
+ let value = object[i];
794
+ if (value && (typeof value === 'object' || position - start > chunkThreshold)) {
795
+ if (iterateProperties.element)
796
+ yield* encodeObjectAsIterable(value, iterateProperties.element);
797
+ else
798
+ yield* tryEncode(value, iterateProperties, 'element');
799
+ } else encode(value);
800
+ }
801
+ } else if (object[Symbol.iterator]) {
802
+ target[position++] = 0x9f; // start indefinite array
803
+ for (let value of object) {
804
+ if (value && (typeof value === 'object' || position - start > chunkThreshold)) {
805
+ if (iterateProperties.element)
806
+ yield* encodeObjectAsIterable(value, iterateProperties.element);
807
+ else
808
+ yield* tryEncode(value, iterateProperties, 'element');
809
+ } else encode(value);
810
+ }
811
+ target[position++] = 0xff; // stop byte
812
+ } else if (isBlob(object)){
813
+ writeEntityLength(object.size, 0x40); // encode as binary data
814
+ yield target.subarray(start, position);
815
+ yield object; // directly return blobs, they have to be encoded asynchronously
816
+ restartEncoding();
817
+ } else if (object[Symbol.asyncIterator]) {
818
+ target[position++] = 0x9f; // start indefinite array
819
+ yield target.subarray(start, position);
820
+ yield object; // directly return async iterators, they have to be encoded asynchronously
821
+ restartEncoding();
822
+ target[position++] = 0xff; // stop byte
823
+ } else {
824
+ encode(object);
825
+ }
826
+ if (finalIterable && position > start) yield target.subarray(start, position);
827
+ else if (position - start > chunkThreshold) {
828
+ yield target.subarray(start, position);
829
+ restartEncoding();
830
+ }
831
+ }
832
+ function* tryEncode(value, iterateProperties, key) {
833
+ let restart = position - start;
834
+ try {
835
+ encode(value);
836
+ if (position - start > chunkThreshold) {
837
+ yield target.subarray(start, position);
838
+ restartEncoding();
839
+ }
840
+ } catch (error) {
841
+ if (error.iteratorNotHandled) {
842
+ iterateProperties[key] = {};
843
+ position = start + restart; // restart our position so we don't have partial data from last encode
844
+ yield* encodeObjectAsIterable.call(this, value, iterateProperties[key]);
845
+ } else throw error;
846
+ }
847
+ }
848
+ function restartEncoding() {
849
+ chunkThreshold = continuedChunkThreshold;
850
+ encoder.encode(null, THROW_ON_ITERABLE); // restart encoding
851
+ }
852
+ function startEncoding(value, options, encodeIterable) {
853
+ if (options && options.chunkThreshold) // explicitly specified chunk sizes
854
+ chunkThreshold = continuedChunkThreshold = options.chunkThreshold;
855
+ else // we start with a smaller threshold to get initial bytes sent quickly
856
+ chunkThreshold = 100;
857
+ if (value && typeof value === 'object') {
858
+ encoder.encode(null, THROW_ON_ITERABLE); // start encoding
859
+ return encodeIterable(value, encoder.iterateProperties || (encoder.iterateProperties = {}), true);
860
+ }
861
+ return [encoder.encode(value)];
862
+ }
863
+
864
+ async function* encodeObjectAsAsyncIterable(value, iterateProperties) {
865
+ for (let encodedValue of encodeObjectAsIterable(value, iterateProperties, true)) {
866
+ let constructor = encodedValue.constructor;
867
+ if (constructor === ByteArray || constructor === Uint8Array)
868
+ yield encodedValue;
869
+ else if (isBlob(encodedValue)) {
870
+ let reader = encodedValue.stream().getReader();
871
+ let next;
872
+ while (!(next = await reader.read()).done) {
873
+ yield next.value;
874
+ }
875
+ } else if (encodedValue[Symbol.asyncIterator]) {
876
+ for await (let asyncValue of encodedValue) {
877
+ restartEncoding();
878
+ if (asyncValue)
879
+ yield* encodeObjectAsAsyncIterable(asyncValue, iterateProperties.async || (iterateProperties.async = {}));
880
+ else yield encoder.encode(asyncValue);
881
+ }
882
+ } else {
883
+ yield encodedValue;
884
+ }
885
+ }
886
+ }
744
887
  }
745
888
  useBuffer(buffer) {
746
889
  // this means we are finished using our own buffer and we can write over it safely
@@ -776,6 +919,23 @@ export class Encoder extends Decoder {
776
919
  return saveResults
777
920
  }
778
921
  }
922
+ function writeEntityLength(length, majorValue) {
923
+ if (length < 0x18)
924
+ target[position++] = majorValue | length
925
+ else if (length < 0x100) {
926
+ target[position++] = majorValue | 0x18
927
+ target[position++] = length
928
+ } else if (length < 0x10000) {
929
+ target[position++] = majorValue | 0x19
930
+ target[position++] = length >> 8
931
+ target[position++] = length & 0xff
932
+ } else {
933
+ target[position++] = majorValue | 0x1a
934
+ targetView.setUint32(position, length)
935
+ position += 4
936
+ }
937
+
938
+ }
779
939
  class SharedData {
780
940
  constructor(structures, values, version) {
781
941
  this.structures = structures
@@ -801,6 +961,13 @@ function writeArrayHeader(length) {
801
961
  }
802
962
  }
803
963
 
964
+ const BlobConstructor = typeof Blob === 'undefined' ? function(){} : Blob;
965
+ function isBlob(object) {
966
+ if (object instanceof BlobConstructor)
967
+ return true;
968
+ let tag = object[Symbol.toStringTag];
969
+ return tag === 'Blob' || tag === 'File';
970
+ }
804
971
  function findRepetitiveStrings(value, packedValues) {
805
972
  switch(typeof value) {
806
973
  case 'string':
@@ -1033,9 +1200,13 @@ export function addExtension(extension) {
1033
1200
  }
1034
1201
  let defaultEncoder = new Encoder({ useRecords: false })
1035
1202
  export const encode = defaultEncoder.encode
1203
+ export const encodeAsIterable = defaultEncoder.encodeAsIterable
1204
+ export const encodeAsAsyncIterable = defaultEncoder.encodeAsAsyncIterable
1036
1205
  export { FLOAT32_OPTIONS } from './decode.js'
1037
1206
  import { FLOAT32_OPTIONS } from './decode.js'
1038
1207
  export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
1039
1208
  export const REUSE_BUFFER_MODE = 512
1040
1209
  export const RESET_BUFFER_MODE = 1024
1210
+ export const THROW_ON_ITERABLE = 2048
1211
+
1041
1212
 
package/index.d.ts CHANGED
@@ -26,12 +26,14 @@ export interface Options {
26
26
  saveStructures?(structures: {}[]): boolean | void
27
27
  onInvalidDate?: () => any
28
28
  tagUint8Array?: boolean
29
+ pack?: boolean
29
30
  }
30
- interface Extension {
31
- Class: Function
31
+ type ClassOf<T> = new (...args: any[]) => T;
32
+ interface Extension<T, R> {
33
+ Class: ClassOf<T>
32
34
  tag: number
33
- encode(value: any): Buffer | Uint8Array
34
- decode(messagePack: Buffer | Uint8Array): any
35
+ encode(value: T, encodeFn: (data: R) => Uint8Array): Buffer | Uint8Array
36
+ decode(item: R): T
35
37
  }
36
38
  export class Decoder {
37
39
  constructor(options?: Options)
@@ -40,7 +42,7 @@ export class Decoder {
40
42
  }
41
43
  export function decode(messagePack: Buffer | Uint8Array): any
42
44
  export function decodeMultiple(messagePack: Buffer | Uint8Array, forEach?: (value: any) => any): [] | void
43
- export function addExtension(extension: Extension): void
45
+ export function addExtension<T, R>(extension: Extension<T, R>): void
44
46
  export function clearSource(): void
45
47
  export function roundFloat32(float32Number: number): number
46
48
  export let isNativeAccelerationEnabled: boolean
@@ -49,6 +51,8 @@ export class Encoder extends Decoder {
49
51
  encode(value: any): Buffer
50
52
  }
51
53
  export function encode(value: any): Buffer
54
+ export function encodeAsIterable(value: any): Iterable<Buffer | Blob | AsyncIterable<Buffer>>
55
+ export function encodeAsAsyncIterable(value: any): AsyncIterable<Buffer>
52
56
 
53
57
  import { Transform, Readable } from 'stream'
54
58
 
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './encode.js'
1
+ export { Encoder, addExtension, encode, encodeAsIterable, encodeAsAsyncIterable, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './encode.js'
2
2
  export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './decode.js'
3
3
  export { decodeIter, encodeIter } from './iterators.js'
package/node-index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './encode.js'
1
+ export { Encoder, addExtension, encode, encodeAsIterable, encodeAsAsyncIterable, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './encode.js'
2
2
  export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './decode.js'
3
3
  export { EncoderStream, DecoderStream } from './stream.js'
4
4
  export { decodeIter, encodeIter } from './iterators.js'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cbor-x",
3
3
  "author": "Kris Zyp",
4
- "version": "1.4.1",
4
+ "version": "1.5.1",
5
5
  "description": "Ultra-fast and conformant CBOR (RFC 8949) implementation with support for numerous tag extensions including records and structured cloning",
6
6
  "license": "MIT",
7
7
  "types": "./index.d.ts",
@@ -63,11 +63,16 @@
63
63
  "/*.js",
64
64
  "/*.ts"
65
65
  ],
66
+ "browser": {
67
+ "node:buffer": false
68
+ },
66
69
  "optionalDependencies": {
67
- "cbor-extract": "^2.0.2"
70
+ "cbor-extract": "^2.1.1"
68
71
  },
69
72
  "devDependencies": {
70
- "@rollup/plugin-json": "^4.1.0",
73
+ "@rollup/plugin-json": "^5.0.1",
74
+ "@rollup/plugin-replace": "^5.0.1",
75
+ "@rollup/plugin-terser": "^0.1.0",
71
76
  "@types/node": "latest",
72
77
  "async": "^3",
73
78
  "cbor": "^5",
@@ -75,8 +80,7 @@
75
80
  "chai": "^4.3.4",
76
81
  "cpy-cli": "^4.1.0",
77
82
  "esm": "^3.2.25",
78
- "mocha": "^8.1.3",
79
- "rollup": "^1.20.3",
80
- "rollup-plugin-babel-minify": "^9.0.0"
83
+ "mocha": "^10.1.0",
84
+ "rollup": "^3.2.5"
81
85
  }
82
86
  }
package/rollup.config.js CHANGED
@@ -1,5 +1,6 @@
1
- import minify from "rollup-plugin-babel-minify";
2
- import json from "@rollup/plugin-json";
1
+ import terser from '@rollup/plugin-terser';
2
+ import json from "@rollup/plugin-json";
3
+ import replace from "@rollup/plugin-replace";
3
4
 
4
5
  export default [
5
6
  {
@@ -7,7 +8,8 @@ export default [
7
8
  output: [
8
9
  {
9
10
  file: "dist/node.cjs",
10
- format: "cjs"
11
+ format: "cjs",
12
+ sourcemap: true
11
13
  }
12
14
  ]
13
15
  },
@@ -16,17 +18,57 @@ export default [
16
18
  output: {
17
19
  file: "dist/index.js",
18
20
  format: "umd",
19
- name: "CBOR"
21
+ name: "CBOR",
22
+ sourcemap: true
20
23
  }
21
- },
24
+ },
25
+ {
26
+ input: "index.js",
27
+ plugins: [
28
+ replace({ Function: 'BlockedFunction '})
29
+ ],
30
+ output: {
31
+ file: "dist/index-no-eval.cjs",
32
+ format: "umd",
33
+ name: "CBOR",
34
+ sourcemap: true
35
+ },
36
+ },
37
+ {
38
+ input: "decode.js",
39
+ plugins: [
40
+ replace({ Function: 'BlockedFunction '})
41
+ ],
42
+ output: {
43
+ file: "dist/decode-no-eval.cjs",
44
+ format: "umd",
45
+ name: "CBOR",
46
+ sourcemap: true
47
+ },
48
+ },
22
49
  {
23
50
  input: "index.js",
24
- plugins: [minify({
25
- })],
51
+ plugins: [
52
+ terser({})
53
+ ],
26
54
  output: {
27
55
  file: "dist/index.min.js",
28
56
  format: "umd",
29
- name: "CBOR"
57
+ name: "CBOR",
58
+ sourcemap: true
59
+ }
60
+ },
61
+ {
62
+ input: "index.js",
63
+ plugins: [
64
+ replace({ Function: 'BlockedFunction '}),
65
+ terser({})
66
+ ],
67
+ output: {
68
+ file: "dist/index-no-eval.min.js",
69
+ format: "umd",
70
+ name: "CBOR",
71
+ sourcemap: true
30
72
  }
31
73
  },
32
74
  {
@@ -36,6 +78,7 @@ export default [
36
78
  output: {
37
79
  file: "dist/test.js",
38
80
  format: "iife",
81
+ sourcemap: true,
39
82
  globals: {
40
83
  chai: 'chai',
41
84
  './index.js': 'CBOR',
package/stream.js CHANGED
@@ -12,8 +12,10 @@ export class EncoderStream extends Transform {
12
12
  options.sequential = true
13
13
  this.encoder = options.encoder || new Encoder(options)
14
14
  }
15
- _transform(value, encoding, callback) {
16
- this.push(this.encoder.encode(value))
15
+ async _transform(value, encoding, callback) {
16
+ for await (let chunk of this.encoder.encodeAsAsyncIterable(value)) {
17
+ this.push(chunk)
18
+ }
17
19
  callback()
18
20
  }
19
21
  }