cbor-x 1.4.0 → 1.5.0

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
@@ -7,10 +7,11 @@ let extensions, extensionClasses
7
7
  const hasNodeBuffer = typeof Buffer !== 'undefined'
8
8
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
9
9
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
10
- const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
10
+ const BlobConstructor = typeof Blob === 'undefined' ? {} : Blob;
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] || constructor === BlobConstructor) {
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,130 @@ 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
+ writeArrayHeader(object.length);
791
+ for (let i = 0; i < length; i++) {
792
+ let value = object[i];
793
+ if (value && (typeof value === 'object' || position - start > chunkThreshold)) {
794
+ if (iterateProperties.element)
795
+ yield* encodeObjectAsIterable(value, iterateProperties.element);
796
+ else
797
+ yield* tryEncode(value, iterateProperties, 'element');
798
+ } else encode(value);
799
+ }
800
+ } else if (object[Symbol.iterator]) {
801
+ target[position++] = 0x9f; // start indefinite array
802
+ for (let value of object) {
803
+ if (value && (typeof value === 'object' || position - start > chunkThreshold)) {
804
+ if (iterateProperties.element)
805
+ yield* encodeObjectAsIterable(value, iterateProperties.element);
806
+ else
807
+ yield* tryEncode(value, iterateProperties, 'element');
808
+ } else encode(value);
809
+ }
810
+ target[position++] = 0xff; // stop byte
811
+ } else if (constructor === BlobConstructor){
812
+ writeEntityLength(object.size, 0x40); // encode as binary data
813
+ yield target.subarray(start, position);
814
+ yield object; // directly return blobs, they have to be encoded asynchronously
815
+ restartEncoding();
816
+ } else if (object[Symbol.asyncIterator]) {
817
+ target[position++] = 0x9f; // start indefinite array
818
+ yield target.subarray(start, position);
819
+ yield object; // directly return async iterators, they have to be encoded asynchronously
820
+ restartEncoding();
821
+ target[position++] = 0xff; // stop byte
822
+ } else {
823
+ encode(object);
824
+ }
825
+ if (finalIterable && position > start) yield target.subarray(start, position);
826
+ else if (position - start > chunkThreshold) {
827
+ yield target.subarray(start, position);
828
+ restartEncoding();
829
+ }
830
+ }
831
+ function* tryEncode(value, iterateProperties, key) {
832
+ let restart = position - start;
833
+ try {
834
+ encode(value);
835
+ if (position - start > chunkThreshold) {
836
+ yield target.subarray(start, position);
837
+ restartEncoding();
838
+ }
839
+ } catch (error) {
840
+ if (error.iteratorNotHandled) {
841
+ iterateProperties[key] = {};
842
+ position = start + restart; // restart our position so we don't have partial data from last encode
843
+ yield* encodeObjectAsIterable.call(this, value, iterateProperties[key]);
844
+ } else throw error;
845
+ }
846
+ }
847
+ function restartEncoding() {
848
+ chunkThreshold = continuedChunkThreshold;
849
+ encoder.encode(null, THROW_ON_ITERABLE); // restart encoding
850
+ }
851
+ function startEncoding(value, options, encodeIterable) {
852
+ if (options && options.chunkThreshold) // explicitly specified chunk sizes
853
+ chunkThreshold = continuedChunkThreshold = options.chunkThreshold;
854
+ else // we start with a smaller threshold to get initial bytes sent quickly
855
+ chunkThreshold = 100;
856
+ if (value && typeof value === 'object') {
857
+ encoder.encode(null, THROW_ON_ITERABLE); // start encoding
858
+ return encodeIterable(value, encoder.iterateProperties || (encoder.iterateProperties = {}), true);
859
+ }
860
+ return [encoder.encode(value)];
861
+ }
862
+
863
+ async function* encodeObjectAsAsyncIterable(value, iterateProperties) {
864
+ for (let encodedValue of encodeObjectAsIterable(value, iterateProperties, true)) {
865
+ let constructor = encodedValue.constructor;
866
+ if (constructor === ByteArray || constructor === Uint8Array)
867
+ yield encodedValue;
868
+ else if (constructor === BlobConstructor) {
869
+ let reader = encodedValue.stream().getReader();
870
+ let next;
871
+ while (!(next = await reader.read()).done) {
872
+ yield next.value;
873
+ }
874
+ } else if (encodedValue[Symbol.asyncIterator]) {
875
+ for await (let asyncValue of encodedValue) {
876
+ restartEncoding();
877
+ if (asyncValue)
878
+ yield* encodeObjectAsAsyncIterable(asyncValue, iterateProperties.async || (iterateProperties.async = {}));
879
+ else yield encoder.encode(asyncValue);
880
+ }
881
+ } else {
882
+ yield encodedValue;
883
+ }
884
+ }
885
+ }
744
886
  }
745
887
  useBuffer(buffer) {
746
888
  // this means we are finished using our own buffer and we can write over it safely
@@ -776,6 +918,23 @@ export class Encoder extends Decoder {
776
918
  return saveResults
777
919
  }
778
920
  }
921
+ function writeEntityLength(length, majorValue) {
922
+ if (length < 0x18)
923
+ target[position++] = majorValue | length
924
+ else if (length < 0x100) {
925
+ target[position++] = majorValue | 0x18
926
+ target[position++] = length
927
+ } else if (length < 0x10000) {
928
+ target[position++] = majorValue | 0x19
929
+ target[position++] = length >> 8
930
+ target[position++] = length & 0xff
931
+ } else {
932
+ target[position++] = majorValue | 0x1a
933
+ targetView.setUint32(position, length)
934
+ position += 4
935
+ }
936
+
937
+ }
779
938
  class SharedData {
780
939
  constructor(structures, values, version) {
781
940
  this.structures = structures
@@ -1033,9 +1192,13 @@ export function addExtension(extension) {
1033
1192
  }
1034
1193
  let defaultEncoder = new Encoder({ useRecords: false })
1035
1194
  export const encode = defaultEncoder.encode
1195
+ export const encodeAsIterable = defaultEncoder.encodeAsIterable
1196
+ export const encodeAsAsyncIterable = defaultEncoder.encodeAsAsyncIterable
1036
1197
  export { FLOAT32_OPTIONS } from './decode.js'
1037
1198
  import { FLOAT32_OPTIONS } from './decode.js'
1038
1199
  export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
1039
1200
  export const REUSE_BUFFER_MODE = 512
1040
1201
  export const RESET_BUFFER_MODE = 1024
1202
+ export const THROW_ON_ITERABLE = 2048
1203
+
1041
1204
 
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.0",
4
+ "version": "1.5.0",
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",
@@ -64,10 +64,12 @@
64
64
  "/*.ts"
65
65
  ],
66
66
  "optionalDependencies": {
67
- "cbor-extract": "^2.0.2"
67
+ "cbor-extract": "^2.1.1"
68
68
  },
69
69
  "devDependencies": {
70
- "@rollup/plugin-json": "^4.1.0",
70
+ "@rollup/plugin-json": "^5.0.1",
71
+ "@rollup/plugin-replace": "^5.0.1",
72
+ "@rollup/plugin-terser": "^0.1.0",
71
73
  "@types/node": "latest",
72
74
  "async": "^3",
73
75
  "cbor": "^5",
@@ -75,8 +77,7 @@
75
77
  "chai": "^4.3.4",
76
78
  "cpy-cli": "^4.1.0",
77
79
  "esm": "^3.2.25",
78
- "mocha": "^8.1.3",
79
- "rollup": "^1.20.3",
80
- "rollup-plugin-babel-minify": "^9.0.0"
80
+ "mocha": "^10.1.0",
81
+ "rollup": "^3.2.5"
81
82
  }
82
83
  }
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
  }