mapshaper 0.6.68 → 0.6.69

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/mapshaper.js CHANGED
@@ -7415,6 +7415,8 @@
7415
7415
  return this ? this.unpack(source, options) : Unpackr.prototype.unpack.call(defaultOptions, source, options)
7416
7416
  })
7417
7417
  }
7418
+ if (!source.buffer && source.constructor === ArrayBuffer)
7419
+ source = typeof Buffer !== 'undefined' ? Buffer.from(source) : new Uint8Array(source);
7418
7420
  if (typeof options === 'object') {
7419
7421
  srcEnd = options.end || source.length;
7420
7422
  position$1 = options.start || 0;
@@ -7460,10 +7462,10 @@
7460
7462
  let size = source.length;
7461
7463
  let value = this ? this.unpack(source, size) : defaultUnpackr.unpack(source, size);
7462
7464
  if (forEach) {
7463
- if (forEach(value) === false) return;
7465
+ if (forEach(value, lastPosition, position$1) === false) return;
7464
7466
  while(position$1 < size) {
7465
7467
  lastPosition = position$1;
7466
- if (forEach(checkedRead()) === false) {
7468
+ if (forEach(checkedRead(), lastPosition, position$1) === false) {
7467
7469
  return
7468
7470
  }
7469
7471
  }
@@ -7511,8 +7513,8 @@
7511
7513
  }
7512
7514
  return this.structures = loadedStructures
7513
7515
  }
7514
- decode(source, end) {
7515
- return this.unpack(source, end)
7516
+ decode(source, options) {
7517
+ return this.unpack(source, options)
7516
7518
  }
7517
7519
  }
7518
7520
  function checkedRead(options) {
@@ -7535,6 +7537,10 @@
7535
7537
  position$1 = bundledStrings$1.postBundlePosition;
7536
7538
  bundledStrings$1 = null;
7537
7539
  }
7540
+ if (sequentialMode)
7541
+ // we only need to restore the structures if there was an error, but if we completed a read,
7542
+ // we can clear this out and keep the structures we read
7543
+ currentStructures.restoreStructures = null;
7538
7544
 
7539
7545
  if (position$1 == srcEnd) {
7540
7546
  // finished reading this source, cleanup references
@@ -7548,7 +7554,13 @@
7548
7554
  // over read
7549
7555
  throw new Error('Unexpected end of MessagePack data')
7550
7556
  } else if (!sequentialMode) {
7551
- throw new Error('Data read, but end of buffer not reached ' + JSON.stringify(result).slice(0, 100))
7557
+ let jsonView;
7558
+ try {
7559
+ jsonView = JSON.stringify(result, (_, value) => typeof value === "bigint" ? `${value}n` : value).slice(0, 100);
7560
+ } catch(error) {
7561
+ jsonView = '(JSON view not available ' + error + ')';
7562
+ }
7563
+ throw new Error('Data read, but end of buffer not reached ' + jsonView)
7552
7564
  }
7553
7565
  // else more to read, but we are reading sequentially, so don't clear source yet
7554
7566
  return result
@@ -7704,6 +7716,9 @@
7704
7716
  value += dataView.getUint32(position$1 + 4);
7705
7717
  } else if (currentUnpackr.int64AsType === 'string') {
7706
7718
  value = dataView.getBigUint64(position$1).toString();
7719
+ } else if (currentUnpackr.int64AsType === 'auto') {
7720
+ value = dataView.getBigUint64(position$1);
7721
+ if (value<=BigInt(2)<<BigInt(52)) value=Number(value);
7707
7722
  } else
7708
7723
  value = dataView.getBigUint64(position$1);
7709
7724
  position$1 += 8;
@@ -7726,6 +7741,9 @@
7726
7741
  value += dataView.getUint32(position$1 + 4);
7727
7742
  } else if (currentUnpackr.int64AsType === 'string') {
7728
7743
  value = dataView.getBigInt64(position$1).toString();
7744
+ } else if (currentUnpackr.int64AsType === 'auto') {
7745
+ value = dataView.getBigInt64(position$1);
7746
+ if (value>=BigInt(-2)<<BigInt(52)&&value<=BigInt(2)<<BigInt(52)) value=Number(value);
7729
7747
  } else
7730
7748
  value = dataView.getBigInt64(position$1);
7731
7749
  position$1 += 8;
@@ -8183,7 +8201,7 @@
8183
8201
  return readFixedString(length)
8184
8202
  } else { // not cacheable, go back and do a standard read
8185
8203
  position$1--;
8186
- return read().toString()
8204
+ return asSafeString(read())
8187
8205
  }
8188
8206
  let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position$1) : length > 0 ? src[position$1] : 0)) & 0xfff;
8189
8207
  let entry = keyCache[key];
@@ -8235,16 +8253,25 @@
8235
8253
  return entry.string = readFixedString(length)
8236
8254
  }
8237
8255
 
8256
+ function asSafeString(property) {
8257
+ if (typeof property === 'string') return property;
8258
+ if (typeof property === 'number') return property.toString();
8259
+ throw new Error('Invalid property type for record', typeof property);
8260
+ }
8238
8261
  // the registration of the record definition extension (as "r")
8239
8262
  const recordDefinition = (id, highByte) => {
8240
- let structure = read().map(property => property.toString()); // ensure that all keys are strings and that the array is mutable
8263
+ let structure = read().map(asSafeString); // ensure that all keys are strings and
8264
+ // that the array is mutable
8241
8265
  let firstByte = id;
8242
8266
  if (highByte !== undefined) {
8243
8267
  id = id < 32 ? -((highByte << 5) + id) : ((highByte << 5) + id);
8244
8268
  structure.highByte = highByte;
8245
8269
  }
8246
8270
  let existingStructure = currentStructures[id];
8247
- if (existingStructure && existingStructure.isShared) {
8271
+ // If it is a shared structure, we need to restore any changes after reading.
8272
+ // Also in sequential mode, we may get incomplete reads and thus errors, and we need to restore
8273
+ // to the state prior to an incomplete read in order to properly resume.
8274
+ if (existingStructure && (existingStructure.isShared || sequentialMode)) {
8248
8275
  (currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
8249
8276
  }
8250
8277
  currentStructures[id] = structure;
@@ -8254,13 +8281,26 @@
8254
8281
  currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
8255
8282
  currentExtensions[0].noBuffer = true;
8256
8283
 
8284
+ currentExtensions[0x42] = (data) => {
8285
+ // decode bigint
8286
+ let length = data.length;
8287
+ let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
8288
+ for (let i = 1; i < length; i++) {
8289
+ value <<= 8n;
8290
+ value += BigInt(data[i]);
8291
+ }
8292
+ return value;
8293
+ };
8294
+
8295
+ let errors = { Error, TypeError, ReferenceError };
8257
8296
  currentExtensions[0x65] = () => {
8258
8297
  let data = read();
8259
- return (globalThis[data[0]] || Error)(data[1])
8298
+ return (errors[data[0]] || Error)(data[1])
8260
8299
  };
8261
8300
 
8262
8301
  currentExtensions[0x69] = (data) => {
8263
8302
  // id extension (for structured clones)
8303
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
8264
8304
  let id = dataView.getUint32(position$1 - 4);
8265
8305
  if (!referenceMap)
8266
8306
  referenceMap = new Map();
@@ -8284,6 +8324,7 @@
8284
8324
 
8285
8325
  currentExtensions[0x70] = (data) => {
8286
8326
  // pointer extension (for structured clones)
8327
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
8287
8328
  let id = dataView.getUint32(position$1 - 4);
8288
8329
  let refEntry = referenceMap.get(id);
8289
8330
  refEntry.used = true;
@@ -8294,13 +8335,14 @@
8294
8335
 
8295
8336
  const typedArrays = ['Int8','Uint8','Uint8Clamped','Int16','Uint16','Int32','Uint32','Float32','Float64','BigInt64','BigUint64'].map(type => type + 'Array');
8296
8337
 
8338
+ let glbl = typeof globalThis === 'object' ? globalThis : window;
8297
8339
  currentExtensions[0x74] = (data) => {
8298
8340
  let typeCode = data[0];
8299
8341
  let typedArrayName = typedArrays[typeCode];
8300
8342
  if (!typedArrayName)
8301
8343
  throw new Error('Could not find typed array for code ' + typeCode)
8302
8344
  // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
8303
- return new globalThis[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
8345
+ return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
8304
8346
  };
8305
8347
  currentExtensions[0x78] = () => {
8306
8348
  let data = read();
@@ -8465,6 +8507,7 @@
8465
8507
  } else
8466
8508
  position = (position + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
8467
8509
  start = position;
8510
+ if (encodeOptions & RESERVE_START_SPACE) position += (encodeOptions & 0xff);
8468
8511
  referenceMap = packr.structuredClone ? new Map() : null;
8469
8512
  if (packr.bundleStrings && typeof value !== 'string') {
8470
8513
  bundledStrings = [];
@@ -8506,8 +8549,9 @@
8506
8549
  }
8507
8550
  if (hasSharedUpdate)
8508
8551
  hasSharedUpdate = false;
8552
+ let encodingError;
8509
8553
  try {
8510
- if (packr.randomAccessStructure && value.constructor && value.constructor === Object)
8554
+ if (packr.randomAccessStructure && value && value.constructor && value.constructor === Object)
8511
8555
  writeStruct(value);
8512
8556
  else
8513
8557
  pack(value);
@@ -8556,42 +8600,51 @@
8556
8600
  return target
8557
8601
  }
8558
8602
  return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
8603
+ } catch(error) {
8604
+ encodingError = error;
8605
+ throw error;
8559
8606
  } finally {
8560
8607
  if (structures) {
8561
- if (serializationsSinceTransitionRebuild < 10)
8562
- serializationsSinceTransitionRebuild++;
8563
- let sharedLength = structures.sharedLength || 0;
8564
- if (structures.length > sharedLength)
8565
- structures.length = sharedLength;
8566
- if (transitionsCount > 10000) {
8567
- // force a rebuild occasionally after a lot of transitions so it can get cleaned up
8568
- structures.transitions = null;
8569
- serializationsSinceTransitionRebuild = 0;
8570
- transitionsCount = 0;
8571
- if (recordIdsToRemove.length > 0)
8572
- recordIdsToRemove = [];
8573
- } else if (recordIdsToRemove.length > 0 && !isSequential) {
8574
- for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
8575
- recordIdsToRemove[i][RECORD_SYMBOL] = 0;
8576
- }
8577
- recordIdsToRemove = [];
8578
- }
8608
+ resetStructures();
8579
8609
  if (hasSharedUpdate && packr.saveStructures) {
8610
+ let sharedLength = structures.sharedLength || 0;
8580
8611
  // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
8581
8612
  let returnBuffer = target.subarray(start, position);
8582
8613
  let newSharedData = prepareStructures(structures, packr);
8583
- if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
8584
- // get updated structures and try again if the update failed
8585
- return packr.pack(value)
8614
+ if (!encodingError) { // TODO: If there is an encoding error, should make the structures as uninitialized so they get rebuilt next time
8615
+ if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
8616
+ // get updated structures and try again if the update failed
8617
+ return packr.pack(value, encodeOptions)
8618
+ }
8619
+ packr.lastNamedStructuresLength = sharedLength;
8620
+ return returnBuffer
8586
8621
  }
8587
- packr.lastNamedStructuresLength = sharedLength;
8588
- return returnBuffer
8589
8622
  }
8590
8623
  }
8591
8624
  if (encodeOptions & RESET_BUFFER_MODE)
8592
8625
  position = start;
8593
8626
  }
8594
8627
  };
8628
+ const resetStructures = () => {
8629
+ if (serializationsSinceTransitionRebuild < 10)
8630
+ serializationsSinceTransitionRebuild++;
8631
+ let sharedLength = structures.sharedLength || 0;
8632
+ if (structures.length > sharedLength && !isSequential)
8633
+ structures.length = sharedLength;
8634
+ if (transitionsCount > 10000) {
8635
+ // force a rebuild occasionally after a lot of transitions so it can get cleaned up
8636
+ structures.transitions = null;
8637
+ serializationsSinceTransitionRebuild = 0;
8638
+ transitionsCount = 0;
8639
+ if (recordIdsToRemove.length > 0)
8640
+ recordIdsToRemove = [];
8641
+ } else if (recordIdsToRemove.length > 0 && !isSequential) {
8642
+ for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
8643
+ recordIdsToRemove[i][RECORD_SYMBOL] = 0;
8644
+ }
8645
+ recordIdsToRemove = [];
8646
+ }
8647
+ };
8595
8648
  const packArray = (value) => {
8596
8649
  var length = value.length;
8597
8650
  if (length < 0x10) {
@@ -8769,7 +8822,7 @@
8769
8822
  targetView.setFloat64(position, value);
8770
8823
  position += 8;
8771
8824
  }
8772
- } else if (type === 'object') {
8825
+ } else if (type === 'object' || type === 'function') {
8773
8826
  if (!value)
8774
8827
  target[position++] = 0xc0;
8775
8828
  else {
@@ -8794,21 +8847,24 @@
8794
8847
  } else if (constructor === Array) {
8795
8848
  packArray(value);
8796
8849
  } else if (constructor === Map) {
8797
- length = value.size;
8798
- if (length < 0x10) {
8799
- target[position++] = 0x80 | length;
8800
- } else if (length < 0x10000) {
8801
- target[position++] = 0xde;
8802
- target[position++] = length >> 8;
8803
- target[position++] = length & 0xff;
8804
- } else {
8805
- target[position++] = 0xdf;
8806
- targetView.setUint32(position, length);
8807
- position += 4;
8808
- }
8809
- for (let [ key, entryValue ] of value) {
8810
- pack(key);
8811
- pack(entryValue);
8850
+ if (this.mapAsEmptyObject) target[position++] = 0x80;
8851
+ else {
8852
+ length = value.size;
8853
+ if (length < 0x10) {
8854
+ target[position++] = 0x80 | length;
8855
+ } else if (length < 0x10000) {
8856
+ target[position++] = 0xde;
8857
+ target[position++] = length >> 8;
8858
+ target[position++] = length & 0xff;
8859
+ } else {
8860
+ target[position++] = 0xdf;
8861
+ targetView.setUint32(position, length);
8862
+ position += 4;
8863
+ }
8864
+ for (let [key, entryValue] of value) {
8865
+ pack(key);
8866
+ pack(entryValue);
8867
+ }
8812
8868
  }
8813
8869
  } else {
8814
8870
  for (let i = 0, l = extensions.length; i < l; i++) {
@@ -8871,6 +8927,18 @@
8871
8927
  if (Array.isArray(value)) {
8872
8928
  packArray(value);
8873
8929
  } else {
8930
+ // use this as an alternate mechanism for expressing how to serialize
8931
+ if (value.toJSON) {
8932
+ const json = value.toJSON();
8933
+ // if for some reason value.toJSON returns itself it'll loop forever
8934
+ if (json !== value)
8935
+ return pack(json)
8936
+ }
8937
+
8938
+ // if there is a writeFunction, use it, otherwise just encode as undefined
8939
+ if (type === 'function')
8940
+ return pack(this.writeFunction && this.writeFunction(value));
8941
+
8874
8942
  // no extension found, write as object
8875
8943
  writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
8876
8944
  }
@@ -8892,8 +8960,26 @@
8892
8960
  if (this.largeBigIntToFloat) {
8893
8961
  target[position++] = 0xcb;
8894
8962
  targetView.setFloat64(position, Number(value));
8963
+ } else if (this.useBigIntExtension && value < 2n**(1023n) && value > -(2n**(1023n))) {
8964
+ target[position++] = 0xc7;
8965
+ position++;
8966
+ target[position++] = 0x42; // "B" for BigInt
8967
+ let bytes = [];
8968
+ let alignedSign;
8969
+ do {
8970
+ let byte = value & 0xffn;
8971
+ alignedSign = (byte & 0x80n) === (value < 0n ? 0x80n : 0n);
8972
+ bytes.push(byte);
8973
+ value >>= 8n;
8974
+ } while (!((value === 0n || value === -1n) && alignedSign));
8975
+ target[position-2] = bytes.length;
8976
+ for (let i = bytes.length; i > 0;) {
8977
+ target[position++] = Number(bytes[--i]);
8978
+ }
8979
+ return
8895
8980
  } else {
8896
- throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, set largeBigIntToFloat to convert to float-64')
8981
+ throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
8982
+ ' useBigIntExtension or set largeBigIntToFloat to convert to float-64')
8897
8983
  }
8898
8984
  }
8899
8985
  position += 8;
@@ -8905,14 +8991,12 @@
8905
8991
  target[position++] = 0;
8906
8992
  target[position++] = 0;
8907
8993
  }
8908
- } else if (type === 'function') {
8909
- pack(this.writeFunction && this.writeFunction()); // if there is a writeFunction, use it, otherwise just encode as undefined
8910
8994
  } else {
8911
8995
  throw new Error('Unknown type: ' + type)
8912
8996
  }
8913
8997
  };
8914
8998
 
8915
- const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
8999
+ const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber) ? (object) => {
8916
9000
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
8917
9001
  let keys = Object.keys(object);
8918
9002
  let length = keys.length;
@@ -8928,9 +9012,19 @@
8928
9012
  position += 4;
8929
9013
  }
8930
9014
  let key;
8931
- for (let i = 0; i < length; i++) {
8932
- pack(key = keys[i]);
8933
- pack(object[key]);
9015
+ if (this.coercibleKeyAsNumber) {
9016
+ for (let i = 0; i < length; i++) {
9017
+ key = keys[i];
9018
+ let num = Number(key);
9019
+ pack(isNaN(num) ? key : num);
9020
+ pack(object[key]);
9021
+ }
9022
+
9023
+ } else {
9024
+ for (let i = 0; i < length; i++) {
9025
+ pack(key = keys[i]);
9026
+ pack(object[key]);
9027
+ }
8934
9028
  }
8935
9029
  } :
8936
9030
  (object, safePrototype) => {
@@ -8947,7 +9041,9 @@
8947
9041
  }
8948
9042
  target[objectOffset++ + start] = size >> 8;
8949
9043
  target[objectOffset + start] = size & 0xff;
8950
- } :
9044
+ };
9045
+
9046
+ const writeRecord = this.useRecords === false ? writePlainObject :
8951
9047
  (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
8952
9048
  (object, safePrototype) => {
8953
9049
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
@@ -9016,9 +9112,18 @@
9016
9112
  }
9017
9113
  // now write the values
9018
9114
  for (let key in object)
9019
- if (safePrototype || object.hasOwnProperty(key))
9115
+ if (safePrototype || object.hasOwnProperty(key)) {
9020
9116
  pack(object[key]);
9117
+ }
9021
9118
  };
9119
+
9120
+ // craete reference to useRecords if useRecords is a function
9121
+ const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
9122
+
9123
+ const writeObject = checkUseRecords ? (object, safePrototype) => {
9124
+ checkUseRecords(object) ? writeRecord(object,safePrototype) : writePlainObject(object,safePrototype);
9125
+ } : writeRecord;
9126
+
9022
9127
  const makeRoom = (end) => {
9023
9128
  let newSize;
9024
9129
  if (end > 0x1000000) {
@@ -9122,12 +9227,13 @@
9122
9227
  }
9123
9228
  };
9124
9229
  const writeStruct = (object, safePrototype) => {
9125
- let newPosition = writeStructSlots(object, target, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
9230
+ let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
9126
9231
  if (notifySharedUpdate)
9127
9232
  return hasSharedUpdate = true;
9128
9233
  position = newPosition;
9129
9234
  let startTarget = target;
9130
9235
  pack(value);
9236
+ resetStructures();
9131
9237
  if (startTarget !== target) {
9132
9238
  return { position, targetView, target }; // indicate the buffer was re-allocated
9133
9239
  }
@@ -9191,6 +9297,10 @@
9191
9297
  }
9192
9298
  }, {
9193
9299
  pack(set, allocateForWrite, pack) {
9300
+ if (this.setAsEmptyObject) {
9301
+ allocateForWrite(0);
9302
+ return pack({})
9303
+ }
9194
9304
  let array = Array.from(set);
9195
9305
  let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0);
9196
9306
  if (this.moreTypes) {
@@ -9371,6 +9481,7 @@
9371
9481
  defaultPackr.pack;
9372
9482
  const REUSE_BUFFER_MODE = 512;
9373
9483
  const RESET_BUFFER_MODE = 1024;
9484
+ const RESERVE_START_SPACE = 2048;
9374
9485
 
9375
9486
  // DEFLATE is a complex format; to read this code, you should probably check the RFC first:
9376
9487
  // https://tools.ietf.org/html/rfc1951
@@ -45304,7 +45415,7 @@ ${svg}
45304
45415
  });
45305
45416
  }
45306
45417
 
45307
- var version = "0.6.68";
45418
+ var version = "0.6.69";
45308
45419
 
45309
45420
  // Parse command line args into commands and run them
45310
45421
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.68",
3
+ "version": "0.6.69",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -57,7 +57,7 @@
57
57
  "idb-keyval": "^6.2.0",
58
58
  "kdbush": "^3.0.0",
59
59
  "mproj": "0.0.37",
60
- "msgpackr": "^1.8.5",
60
+ "msgpackr": "^1.10.1",
61
61
  "opn": "^5.3.0",
62
62
  "rw": "~1.3.3",
63
63
  "sync-request": "5.0.0",
@@ -2417,7 +2417,7 @@
2417
2417
  // Show a popup error message, then throw an error
2418
2418
  var msg = GUI.formatMessageArgs(arguments);
2419
2419
  gui.alert(msg);
2420
- throw new Error(msg);
2420
+ throw new internal.UserError(msg);
2421
2421
  }
2422
2422
 
2423
2423
  function error() {
@@ -3882,7 +3882,11 @@
3882
3882
  } else {
3883
3883
  // stack seems to change if Error is logged directly
3884
3884
  console.error(err.stack);
3885
- gui.alert('Export failed for an unknown reason');
3885
+ var msg = 'Export failed for an unknown reason';
3886
+ if (err.name == 'UserError') {
3887
+ msg = err.message;
3888
+ }
3889
+ gui.alert(msg, 'Export failed');
3886
3890
  }
3887
3891
  }).finally(function() {
3888
3892
  gui.clearProgressMessage();
@@ -8827,11 +8831,11 @@
8827
8831
  return textNode.childNodes.length > 1;
8828
8832
  }
8829
8833
 
8830
- function toggleTextAlign(textNode, rec) {
8831
- var curr = rec['text-anchor'] || 'middle';
8832
- var value = curr == 'middle' && 'start' || curr == 'start' && 'end' || 'middle';
8833
- updateTextAnchor(value, textNode, rec);
8834
- }
8834
+ // export function toggleTextAlign(textNode, rec) {
8835
+ // var curr = rec['text-anchor'] || 'middle';
8836
+ // var value = curr == 'middle' && 'start' || curr == 'start' && 'end' || 'middle';
8837
+ // updateTextAnchor(value, textNode, rec);
8838
+ // }
8835
8839
 
8836
8840
  // Set an attribute on a <text> node and any child <tspan> elements
8837
8841
  // (mapshaper's svg labels require tspans to have the same x and dx values
@@ -8863,6 +8867,7 @@
8863
8867
  var rect = textNode.getBoundingClientRect();
8864
8868
  var labelCenterX = rect.left - svg.getBoundingClientRect().left + rect.width / 2;
8865
8869
  var xpct = (labelCenterX - p[0]) / rect.width; // offset of label center from anchor center
8870
+
8866
8871
  var value = xpct < -0.25 && 'end' || xpct > 0.25 && 'start' || 'middle';
8867
8872
  updateTextAnchor(value, textNode, rec);
8868
8873
  }
@@ -8874,7 +8879,6 @@
8874
8879
  var curr = rec['text-anchor'] || 'middle';
8875
8880
  var xshift = 0;
8876
8881
 
8877
- // console.log("anchor() curr:", curr, "xpct:", xpct, "left:", rect.left, "anchorX:", anchorX, "targ:", targ, "dx:", xshift)
8878
8882
  if (curr == 'middle' && value == 'end' || curr == 'start' && value == 'middle') {
8879
8883
  xshift = width / 2;
8880
8884
  } else if (curr == 'middle' && value == 'start' || curr == 'end' && value == 'middle') {
@@ -8886,16 +8890,29 @@
8886
8890
  }
8887
8891
  if (xshift) {
8888
8892
  rec['text-anchor'] = value;
8889
- applyDelta(rec, 'dx', Math.round(xshift));
8893
+ applyDelta(rec, 'dx', xshift / getScaleAttribute(textNode));
8890
8894
  }
8891
8895
  }
8892
8896
 
8893
- // handle either numeric strings or numbers in fields
8897
+ function getScaleAttribute(node) {
8898
+ // this is fragile, consider passing in the value of <MapExtent>.getSymbolScale()
8899
+ var transform = node.getAttribute('transform') ||
8900
+ node.parentNode.getAttribute('transform'); // compound label puts it here
8901
+ var match = /scale\(([^)]+)\)/.exec(transform || '');
8902
+ return match ? parseFloat(match[1]) : 1;
8903
+ }
8904
+
8905
+ // handle either numeric strings or numbers in record
8894
8906
  function applyDelta(rec, key, delta) {
8895
8907
  var currVal = rec[key];
8896
- var isString = utils$1.isString(currVal);
8897
8908
  var newVal = (+currVal + delta) || 0;
8898
- rec[key] = isString ? String(newVal) : newVal;
8909
+ updateNumber(rec, key, newVal);
8910
+ }
8911
+
8912
+ // handle either numeric strings or numbers in record
8913
+ function updateNumber(rec, key, num) {
8914
+ var isString = utils$1.isString(rec[key]);
8915
+ rec[key] = isString ? String(num) : num;
8899
8916
  }
8900
8917
 
8901
8918
  function initLabelDragging(gui, ext, hit) {
@@ -8909,9 +8926,9 @@
8909
8926
 
8910
8927
  hit.on('dragstart', function(e) {
8911
8928
  if (!active(e)) return;
8912
- var textNode = getTextTarget3(e);
8929
+ var symNode = getSymbolTarget(e);
8913
8930
  var table = hit.getTargetDataTable();
8914
- if (!textNode || !table) {
8931
+ if (!symNode || !table) {
8915
8932
  activeId = -1;
8916
8933
  return false;
8917
8934
  }
@@ -8927,18 +8944,22 @@
8927
8944
  error$1("Mismatched hit ids:", e.id, activeId);
8928
8945
  }
8929
8946
  var scale = ext.getSymbolScale() || 1;
8930
- var textNode;
8947
+ var symNode, textNode;
8931
8948
  applyDelta(activeRecord, 'dx', e.dx / scale);
8932
8949
  applyDelta(activeRecord, 'dy', e.dy / scale);
8933
- textNode = getTextTarget3(e);
8934
- if (!isMultilineLabel(textNode)) {
8935
- // update anchor position of single-line labels based on label position
8936
- // relative to anchor point, for better placement when eventual display font is
8937
- // different from mapshaper's font.
8938
- autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget().layer, ext));
8939
- }
8940
- // updateSymbol(targetTextNode, activeRecord);
8941
- updateSymbol2(textNode, activeRecord, activeId);
8950
+ symNode = getSymbolTarget(e);
8951
+ textNode = getTextNode(symNode);
8952
+ // update anchor position of labels based on label position relative
8953
+ // to anchor point, for better placement when eventual display font is
8954
+ // different from mapshaper's font.
8955
+ // if (!isMultilineLabel(textNode)) {
8956
+ autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget().layer, ext));
8957
+ // }
8958
+ updateNumber(activeRecord, 'dx', internal.roundToDigits(+activeRecord.dx, 3));
8959
+ updateNumber(activeRecord, 'dy', internal.roundToDigits(+activeRecord.dy, 3));
8960
+ updateTextNode(textNode, activeRecord);
8961
+ // updateSymbolNode(symNode, activeRecord, activeId);
8962
+ gui.dispatchEvent('popup-needs-refresh');
8942
8963
  });
8943
8964
 
8944
8965
  hit.on('dragend', function(e) {
@@ -8962,32 +8983,37 @@
8962
8983
  return coords[0];
8963
8984
  }
8964
8985
 
8965
- function getTextTarget3(e) {
8986
+ function getSymbolTarget(e) {
8966
8987
  if (e.id > -1 === false || !e.container) return null;
8967
8988
  return getSymbolNodeById(e.id, e.container);
8968
8989
  }
8969
8990
 
8991
+ function getTextNode(symNode) {
8992
+ if (symNode.tagName == 'text') return symNode;
8993
+ return symNode.querySelector('text');
8994
+ }
8995
+
8970
8996
  function getSymbolNodeById(id, parent) {
8971
8997
  // TODO: optimize selector
8972
8998
  var sel = '[data-id="' + id + '"]';
8973
8999
  return parent.querySelector(sel);
8974
9000
  }
8975
9001
 
8976
- function getTextTarget2(e) {
8977
- var el = e && e.targetSymbol || null;
8978
- if (el && el.tagName == 'tspan') {
8979
- el = el.parentNode;
8980
- }
8981
- return el && el.tagName == 'text' ? el : null;
8982
- }
9002
+ // function getTextTarget2(e) {
9003
+ // var el = e && e.targetSymbol || null;
9004
+ // if (el && el.tagName == 'tspan') {
9005
+ // el = el.parentNode;
9006
+ // }
9007
+ // return el && el.tagName == 'text' ? el : null;
9008
+ // }
8983
9009
 
8984
- function getTextTarget(e) {
8985
- var el = e.target;
8986
- if (el.tagName == 'tspan') {
8987
- el = el.parentNode;
8988
- }
8989
- return el.tagName == 'text' ? el : null;
8990
- }
9010
+ // function getTextTarget(e) {
9011
+ // var el = e.target;
9012
+ // if (el.tagName == 'tspan') {
9013
+ // el = el.parentNode;
9014
+ // }
9015
+ // return el.tagName == 'text' ? el : null;
9016
+ // }
8991
9017
 
8992
9018
  function getLabelRecordById(id) {
8993
9019
  var table = hit.getTargetDataTable();
@@ -9006,7 +9032,7 @@
9006
9032
  }
9007
9033
 
9008
9034
  // update symbol by setting attributes
9009
- function updateSymbol(node, d) {
9035
+ function updateTextNode(node, d) {
9010
9036
  var a = d['text-anchor'];
9011
9037
  if (a) node.setAttribute('text-anchor', a);
9012
9038
  setMultilineAttribute(node, 'dx', d.dx || 0);
@@ -9014,7 +9040,8 @@
9014
9040
  }
9015
9041
 
9016
9042
  // update symbol by re-rendering it
9017
- function updateSymbol2(node, d, id) {
9043
+ // fails when symbol includes a dot (<g><circle/><text/></g> structure)
9044
+ function updateSymbolNode(node, d, id) {
9018
9045
  var o = internal.svg.renderStyledLabel(d); // TODO: symbol support
9019
9046
  var activeLayer = hit.getHitTarget().layer;
9020
9047
  var xy = activeLayer.shapes[id][0];
@@ -9027,8 +9054,6 @@
9027
9054
  g.innerHTML = internal.svg.stringify(o);
9028
9055
  node2 = g.firstChild;
9029
9056
  node.parentNode.replaceChild(node2, node);
9030
- gui.dispatchEvent('popup-needs-refresh');
9031
- return node2;
9032
9057
  }
9033
9058
  }
9034
9059
 
@@ -9427,15 +9452,16 @@
9427
9452
  var styler = function(style, i) {
9428
9453
  var defaultStyle = i === topIdx ? topStyle : outlineStyle;
9429
9454
  if (baseStyle.styler) {
9430
- Object.assign(style, baseStyle);
9431
- baseStyle.styler(style, i);
9455
+ // TODO: render default stroke widths without scaling
9456
+ // (will need to pass symbol scale to the styler function)
9457
+ style.strokeWidth = defaultStyle.strokeWidth;
9458
+ baseStyle.styler(style, i); // get styled stroke width (if set)
9432
9459
  style.strokeColor = defaultStyle.strokeColor;
9433
9460
  style.fillColor = defaultStyle.fillColor;
9434
9461
  } else {
9435
9462
  Object.assign(style, defaultStyle);
9436
9463
  }
9437
9464
  };
9438
- // var baseStyle = getDefaultStyle(baseLyr, selectionStyles[geomType]);
9439
9465
  var baseStyle = getActiveLayerStyle(baseLyr, opts);
9440
9466
  var outlineStyle = getDefaultStyle(baseLyr, selectionStyles[geomType]);
9441
9467
  var topStyle;
@@ -10308,7 +10334,9 @@
10308
10334
  for (var i=0; i<shapes.length; i++) {
10309
10335
  shp = shapes[i];
10310
10336
  if (!shp || filter && !filter(shp)) continue;
10311
- if (styler) styler(style, i);
10337
+ if (styler) {
10338
+ styler(style, i);
10339
+ }
10312
10340
  if (style.overlay || style.opacity < 1 || style.fillOpacity < 1 || style.strokeOpacity < 1 || style.fillEffect) {
10313
10341
  // don't batch shapes with opacity, in case they overlap
10314
10342
  drawPaths([shp], startPath, draw, style);
@@ -11738,6 +11766,7 @@
11738
11766
  }
11739
11767
 
11740
11768
  _activeLyr = getDisplayLayer(e.layer, e.dataset, getDisplayOptions());
11769
+ _activeLyr.style = getActiveLayerStyle(_activeLyr.layer, getGlobalStyleOptions());
11741
11770
  _activeLyr.active = true;
11742
11771
 
11743
11772
  if (popupCanStayOpen(e.flags)) {
@@ -11841,7 +11870,12 @@
11841
11870
  }
11842
11871
 
11843
11872
  function calcFullBounds() {
11844
- var b = getContentLayerBounds();
11873
+ var b;
11874
+ if (isPreviewView()) {
11875
+ b = new Bounds(getFrameData().bbox);
11876
+ } else {
11877
+ b = getContentLayerBounds();
11878
+ }
11845
11879
 
11846
11880
  // add margin
11847
11881
  // use larger margin for small sizes
@@ -11868,7 +11902,7 @@
11868
11902
  }
11869
11903
 
11870
11904
  function isTableView() {
11871
- return !isPreviewView() && !!_activeLyr.tabular;
11905
+ return !!_activeLyr.tabular;
11872
11906
  }
11873
11907
 
11874
11908
  function findFrameLayer() {
@@ -11879,8 +11913,7 @@
11879
11913
 
11880
11914
  // Preview view: symbols are scaled based on display size of frame layer
11881
11915
  function isPreviewView() {
11882
- var data = getFrameData();
11883
- return !!data;
11916
+ return !isTableView() && !!getFrameData();
11884
11917
  }
11885
11918
 
11886
11919
  function getFrameData() {
@@ -11919,7 +11952,9 @@
11919
11952
 
11920
11953
  function getContentLayers() {
11921
11954
  var layers = getVisibleMapLayers();
11922
- if (isTableView()) return findActiveLayer(layers);
11955
+ if (isTableView()) {
11956
+ return findActiveLayer(layers);
11957
+ }
11923
11958
  return layers.filter(function(o) {
11924
11959
  return !!o.geographic;
11925
11960
  });
package/www/mapshaper.js CHANGED
@@ -7415,6 +7415,8 @@
7415
7415
  return this ? this.unpack(source, options) : Unpackr.prototype.unpack.call(defaultOptions, source, options)
7416
7416
  })
7417
7417
  }
7418
+ if (!source.buffer && source.constructor === ArrayBuffer)
7419
+ source = typeof Buffer !== 'undefined' ? Buffer.from(source) : new Uint8Array(source);
7418
7420
  if (typeof options === 'object') {
7419
7421
  srcEnd = options.end || source.length;
7420
7422
  position$1 = options.start || 0;
@@ -7460,10 +7462,10 @@
7460
7462
  let size = source.length;
7461
7463
  let value = this ? this.unpack(source, size) : defaultUnpackr.unpack(source, size);
7462
7464
  if (forEach) {
7463
- if (forEach(value) === false) return;
7465
+ if (forEach(value, lastPosition, position$1) === false) return;
7464
7466
  while(position$1 < size) {
7465
7467
  lastPosition = position$1;
7466
- if (forEach(checkedRead()) === false) {
7468
+ if (forEach(checkedRead(), lastPosition, position$1) === false) {
7467
7469
  return
7468
7470
  }
7469
7471
  }
@@ -7511,8 +7513,8 @@
7511
7513
  }
7512
7514
  return this.structures = loadedStructures
7513
7515
  }
7514
- decode(source, end) {
7515
- return this.unpack(source, end)
7516
+ decode(source, options) {
7517
+ return this.unpack(source, options)
7516
7518
  }
7517
7519
  }
7518
7520
  function checkedRead(options) {
@@ -7535,6 +7537,10 @@
7535
7537
  position$1 = bundledStrings$1.postBundlePosition;
7536
7538
  bundledStrings$1 = null;
7537
7539
  }
7540
+ if (sequentialMode)
7541
+ // we only need to restore the structures if there was an error, but if we completed a read,
7542
+ // we can clear this out and keep the structures we read
7543
+ currentStructures.restoreStructures = null;
7538
7544
 
7539
7545
  if (position$1 == srcEnd) {
7540
7546
  // finished reading this source, cleanup references
@@ -7548,7 +7554,13 @@
7548
7554
  // over read
7549
7555
  throw new Error('Unexpected end of MessagePack data')
7550
7556
  } else if (!sequentialMode) {
7551
- throw new Error('Data read, but end of buffer not reached ' + JSON.stringify(result).slice(0, 100))
7557
+ let jsonView;
7558
+ try {
7559
+ jsonView = JSON.stringify(result, (_, value) => typeof value === "bigint" ? `${value}n` : value).slice(0, 100);
7560
+ } catch(error) {
7561
+ jsonView = '(JSON view not available ' + error + ')';
7562
+ }
7563
+ throw new Error('Data read, but end of buffer not reached ' + jsonView)
7552
7564
  }
7553
7565
  // else more to read, but we are reading sequentially, so don't clear source yet
7554
7566
  return result
@@ -7704,6 +7716,9 @@
7704
7716
  value += dataView.getUint32(position$1 + 4);
7705
7717
  } else if (currentUnpackr.int64AsType === 'string') {
7706
7718
  value = dataView.getBigUint64(position$1).toString();
7719
+ } else if (currentUnpackr.int64AsType === 'auto') {
7720
+ value = dataView.getBigUint64(position$1);
7721
+ if (value<=BigInt(2)<<BigInt(52)) value=Number(value);
7707
7722
  } else
7708
7723
  value = dataView.getBigUint64(position$1);
7709
7724
  position$1 += 8;
@@ -7726,6 +7741,9 @@
7726
7741
  value += dataView.getUint32(position$1 + 4);
7727
7742
  } else if (currentUnpackr.int64AsType === 'string') {
7728
7743
  value = dataView.getBigInt64(position$1).toString();
7744
+ } else if (currentUnpackr.int64AsType === 'auto') {
7745
+ value = dataView.getBigInt64(position$1);
7746
+ if (value>=BigInt(-2)<<BigInt(52)&&value<=BigInt(2)<<BigInt(52)) value=Number(value);
7729
7747
  } else
7730
7748
  value = dataView.getBigInt64(position$1);
7731
7749
  position$1 += 8;
@@ -8183,7 +8201,7 @@
8183
8201
  return readFixedString(length)
8184
8202
  } else { // not cacheable, go back and do a standard read
8185
8203
  position$1--;
8186
- return read().toString()
8204
+ return asSafeString(read())
8187
8205
  }
8188
8206
  let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position$1) : length > 0 ? src[position$1] : 0)) & 0xfff;
8189
8207
  let entry = keyCache[key];
@@ -8235,16 +8253,25 @@
8235
8253
  return entry.string = readFixedString(length)
8236
8254
  }
8237
8255
 
8256
+ function asSafeString(property) {
8257
+ if (typeof property === 'string') return property;
8258
+ if (typeof property === 'number') return property.toString();
8259
+ throw new Error('Invalid property type for record', typeof property);
8260
+ }
8238
8261
  // the registration of the record definition extension (as "r")
8239
8262
  const recordDefinition = (id, highByte) => {
8240
- let structure = read().map(property => property.toString()); // ensure that all keys are strings and that the array is mutable
8263
+ let structure = read().map(asSafeString); // ensure that all keys are strings and
8264
+ // that the array is mutable
8241
8265
  let firstByte = id;
8242
8266
  if (highByte !== undefined) {
8243
8267
  id = id < 32 ? -((highByte << 5) + id) : ((highByte << 5) + id);
8244
8268
  structure.highByte = highByte;
8245
8269
  }
8246
8270
  let existingStructure = currentStructures[id];
8247
- if (existingStructure && existingStructure.isShared) {
8271
+ // If it is a shared structure, we need to restore any changes after reading.
8272
+ // Also in sequential mode, we may get incomplete reads and thus errors, and we need to restore
8273
+ // to the state prior to an incomplete read in order to properly resume.
8274
+ if (existingStructure && (existingStructure.isShared || sequentialMode)) {
8248
8275
  (currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
8249
8276
  }
8250
8277
  currentStructures[id] = structure;
@@ -8254,13 +8281,26 @@
8254
8281
  currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
8255
8282
  currentExtensions[0].noBuffer = true;
8256
8283
 
8284
+ currentExtensions[0x42] = (data) => {
8285
+ // decode bigint
8286
+ let length = data.length;
8287
+ let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
8288
+ for (let i = 1; i < length; i++) {
8289
+ value <<= 8n;
8290
+ value += BigInt(data[i]);
8291
+ }
8292
+ return value;
8293
+ };
8294
+
8295
+ let errors = { Error, TypeError, ReferenceError };
8257
8296
  currentExtensions[0x65] = () => {
8258
8297
  let data = read();
8259
- return (globalThis[data[0]] || Error)(data[1])
8298
+ return (errors[data[0]] || Error)(data[1])
8260
8299
  };
8261
8300
 
8262
8301
  currentExtensions[0x69] = (data) => {
8263
8302
  // id extension (for structured clones)
8303
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
8264
8304
  let id = dataView.getUint32(position$1 - 4);
8265
8305
  if (!referenceMap)
8266
8306
  referenceMap = new Map();
@@ -8284,6 +8324,7 @@
8284
8324
 
8285
8325
  currentExtensions[0x70] = (data) => {
8286
8326
  // pointer extension (for structured clones)
8327
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
8287
8328
  let id = dataView.getUint32(position$1 - 4);
8288
8329
  let refEntry = referenceMap.get(id);
8289
8330
  refEntry.used = true;
@@ -8294,13 +8335,14 @@
8294
8335
 
8295
8336
  const typedArrays = ['Int8','Uint8','Uint8Clamped','Int16','Uint16','Int32','Uint32','Float32','Float64','BigInt64','BigUint64'].map(type => type + 'Array');
8296
8337
 
8338
+ let glbl = typeof globalThis === 'object' ? globalThis : window;
8297
8339
  currentExtensions[0x74] = (data) => {
8298
8340
  let typeCode = data[0];
8299
8341
  let typedArrayName = typedArrays[typeCode];
8300
8342
  if (!typedArrayName)
8301
8343
  throw new Error('Could not find typed array for code ' + typeCode)
8302
8344
  // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
8303
- return new globalThis[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
8345
+ return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
8304
8346
  };
8305
8347
  currentExtensions[0x78] = () => {
8306
8348
  let data = read();
@@ -8465,6 +8507,7 @@
8465
8507
  } else
8466
8508
  position = (position + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
8467
8509
  start = position;
8510
+ if (encodeOptions & RESERVE_START_SPACE) position += (encodeOptions & 0xff);
8468
8511
  referenceMap = packr.structuredClone ? new Map() : null;
8469
8512
  if (packr.bundleStrings && typeof value !== 'string') {
8470
8513
  bundledStrings = [];
@@ -8506,8 +8549,9 @@
8506
8549
  }
8507
8550
  if (hasSharedUpdate)
8508
8551
  hasSharedUpdate = false;
8552
+ let encodingError;
8509
8553
  try {
8510
- if (packr.randomAccessStructure && value.constructor && value.constructor === Object)
8554
+ if (packr.randomAccessStructure && value && value.constructor && value.constructor === Object)
8511
8555
  writeStruct(value);
8512
8556
  else
8513
8557
  pack(value);
@@ -8556,42 +8600,51 @@
8556
8600
  return target
8557
8601
  }
8558
8602
  return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
8603
+ } catch(error) {
8604
+ encodingError = error;
8605
+ throw error;
8559
8606
  } finally {
8560
8607
  if (structures) {
8561
- if (serializationsSinceTransitionRebuild < 10)
8562
- serializationsSinceTransitionRebuild++;
8563
- let sharedLength = structures.sharedLength || 0;
8564
- if (structures.length > sharedLength)
8565
- structures.length = sharedLength;
8566
- if (transitionsCount > 10000) {
8567
- // force a rebuild occasionally after a lot of transitions so it can get cleaned up
8568
- structures.transitions = null;
8569
- serializationsSinceTransitionRebuild = 0;
8570
- transitionsCount = 0;
8571
- if (recordIdsToRemove.length > 0)
8572
- recordIdsToRemove = [];
8573
- } else if (recordIdsToRemove.length > 0 && !isSequential) {
8574
- for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
8575
- recordIdsToRemove[i][RECORD_SYMBOL] = 0;
8576
- }
8577
- recordIdsToRemove = [];
8578
- }
8608
+ resetStructures();
8579
8609
  if (hasSharedUpdate && packr.saveStructures) {
8610
+ let sharedLength = structures.sharedLength || 0;
8580
8611
  // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
8581
8612
  let returnBuffer = target.subarray(start, position);
8582
8613
  let newSharedData = prepareStructures(structures, packr);
8583
- if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
8584
- // get updated structures and try again if the update failed
8585
- return packr.pack(value)
8614
+ if (!encodingError) { // TODO: If there is an encoding error, should make the structures as uninitialized so they get rebuilt next time
8615
+ if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
8616
+ // get updated structures and try again if the update failed
8617
+ return packr.pack(value, encodeOptions)
8618
+ }
8619
+ packr.lastNamedStructuresLength = sharedLength;
8620
+ return returnBuffer
8586
8621
  }
8587
- packr.lastNamedStructuresLength = sharedLength;
8588
- return returnBuffer
8589
8622
  }
8590
8623
  }
8591
8624
  if (encodeOptions & RESET_BUFFER_MODE)
8592
8625
  position = start;
8593
8626
  }
8594
8627
  };
8628
+ const resetStructures = () => {
8629
+ if (serializationsSinceTransitionRebuild < 10)
8630
+ serializationsSinceTransitionRebuild++;
8631
+ let sharedLength = structures.sharedLength || 0;
8632
+ if (structures.length > sharedLength && !isSequential)
8633
+ structures.length = sharedLength;
8634
+ if (transitionsCount > 10000) {
8635
+ // force a rebuild occasionally after a lot of transitions so it can get cleaned up
8636
+ structures.transitions = null;
8637
+ serializationsSinceTransitionRebuild = 0;
8638
+ transitionsCount = 0;
8639
+ if (recordIdsToRemove.length > 0)
8640
+ recordIdsToRemove = [];
8641
+ } else if (recordIdsToRemove.length > 0 && !isSequential) {
8642
+ for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
8643
+ recordIdsToRemove[i][RECORD_SYMBOL] = 0;
8644
+ }
8645
+ recordIdsToRemove = [];
8646
+ }
8647
+ };
8595
8648
  const packArray = (value) => {
8596
8649
  var length = value.length;
8597
8650
  if (length < 0x10) {
@@ -8769,7 +8822,7 @@
8769
8822
  targetView.setFloat64(position, value);
8770
8823
  position += 8;
8771
8824
  }
8772
- } else if (type === 'object') {
8825
+ } else if (type === 'object' || type === 'function') {
8773
8826
  if (!value)
8774
8827
  target[position++] = 0xc0;
8775
8828
  else {
@@ -8794,21 +8847,24 @@
8794
8847
  } else if (constructor === Array) {
8795
8848
  packArray(value);
8796
8849
  } else if (constructor === Map) {
8797
- length = value.size;
8798
- if (length < 0x10) {
8799
- target[position++] = 0x80 | length;
8800
- } else if (length < 0x10000) {
8801
- target[position++] = 0xde;
8802
- target[position++] = length >> 8;
8803
- target[position++] = length & 0xff;
8804
- } else {
8805
- target[position++] = 0xdf;
8806
- targetView.setUint32(position, length);
8807
- position += 4;
8808
- }
8809
- for (let [ key, entryValue ] of value) {
8810
- pack(key);
8811
- pack(entryValue);
8850
+ if (this.mapAsEmptyObject) target[position++] = 0x80;
8851
+ else {
8852
+ length = value.size;
8853
+ if (length < 0x10) {
8854
+ target[position++] = 0x80 | length;
8855
+ } else if (length < 0x10000) {
8856
+ target[position++] = 0xde;
8857
+ target[position++] = length >> 8;
8858
+ target[position++] = length & 0xff;
8859
+ } else {
8860
+ target[position++] = 0xdf;
8861
+ targetView.setUint32(position, length);
8862
+ position += 4;
8863
+ }
8864
+ for (let [key, entryValue] of value) {
8865
+ pack(key);
8866
+ pack(entryValue);
8867
+ }
8812
8868
  }
8813
8869
  } else {
8814
8870
  for (let i = 0, l = extensions.length; i < l; i++) {
@@ -8871,6 +8927,18 @@
8871
8927
  if (Array.isArray(value)) {
8872
8928
  packArray(value);
8873
8929
  } else {
8930
+ // use this as an alternate mechanism for expressing how to serialize
8931
+ if (value.toJSON) {
8932
+ const json = value.toJSON();
8933
+ // if for some reason value.toJSON returns itself it'll loop forever
8934
+ if (json !== value)
8935
+ return pack(json)
8936
+ }
8937
+
8938
+ // if there is a writeFunction, use it, otherwise just encode as undefined
8939
+ if (type === 'function')
8940
+ return pack(this.writeFunction && this.writeFunction(value));
8941
+
8874
8942
  // no extension found, write as object
8875
8943
  writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
8876
8944
  }
@@ -8892,8 +8960,26 @@
8892
8960
  if (this.largeBigIntToFloat) {
8893
8961
  target[position++] = 0xcb;
8894
8962
  targetView.setFloat64(position, Number(value));
8963
+ } else if (this.useBigIntExtension && value < 2n**(1023n) && value > -(2n**(1023n))) {
8964
+ target[position++] = 0xc7;
8965
+ position++;
8966
+ target[position++] = 0x42; // "B" for BigInt
8967
+ let bytes = [];
8968
+ let alignedSign;
8969
+ do {
8970
+ let byte = value & 0xffn;
8971
+ alignedSign = (byte & 0x80n) === (value < 0n ? 0x80n : 0n);
8972
+ bytes.push(byte);
8973
+ value >>= 8n;
8974
+ } while (!((value === 0n || value === -1n) && alignedSign));
8975
+ target[position-2] = bytes.length;
8976
+ for (let i = bytes.length; i > 0;) {
8977
+ target[position++] = Number(bytes[--i]);
8978
+ }
8979
+ return
8895
8980
  } else {
8896
- throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, set largeBigIntToFloat to convert to float-64')
8981
+ throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
8982
+ ' useBigIntExtension or set largeBigIntToFloat to convert to float-64')
8897
8983
  }
8898
8984
  }
8899
8985
  position += 8;
@@ -8905,14 +8991,12 @@
8905
8991
  target[position++] = 0;
8906
8992
  target[position++] = 0;
8907
8993
  }
8908
- } else if (type === 'function') {
8909
- pack(this.writeFunction && this.writeFunction()); // if there is a writeFunction, use it, otherwise just encode as undefined
8910
8994
  } else {
8911
8995
  throw new Error('Unknown type: ' + type)
8912
8996
  }
8913
8997
  };
8914
8998
 
8915
- const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
8999
+ const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber) ? (object) => {
8916
9000
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
8917
9001
  let keys = Object.keys(object);
8918
9002
  let length = keys.length;
@@ -8928,9 +9012,19 @@
8928
9012
  position += 4;
8929
9013
  }
8930
9014
  let key;
8931
- for (let i = 0; i < length; i++) {
8932
- pack(key = keys[i]);
8933
- pack(object[key]);
9015
+ if (this.coercibleKeyAsNumber) {
9016
+ for (let i = 0; i < length; i++) {
9017
+ key = keys[i];
9018
+ let num = Number(key);
9019
+ pack(isNaN(num) ? key : num);
9020
+ pack(object[key]);
9021
+ }
9022
+
9023
+ } else {
9024
+ for (let i = 0; i < length; i++) {
9025
+ pack(key = keys[i]);
9026
+ pack(object[key]);
9027
+ }
8934
9028
  }
8935
9029
  } :
8936
9030
  (object, safePrototype) => {
@@ -8947,7 +9041,9 @@
8947
9041
  }
8948
9042
  target[objectOffset++ + start] = size >> 8;
8949
9043
  target[objectOffset + start] = size & 0xff;
8950
- } :
9044
+ };
9045
+
9046
+ const writeRecord = this.useRecords === false ? writePlainObject :
8951
9047
  (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
8952
9048
  (object, safePrototype) => {
8953
9049
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
@@ -9016,9 +9112,18 @@
9016
9112
  }
9017
9113
  // now write the values
9018
9114
  for (let key in object)
9019
- if (safePrototype || object.hasOwnProperty(key))
9115
+ if (safePrototype || object.hasOwnProperty(key)) {
9020
9116
  pack(object[key]);
9117
+ }
9021
9118
  };
9119
+
9120
+ // craete reference to useRecords if useRecords is a function
9121
+ const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
9122
+
9123
+ const writeObject = checkUseRecords ? (object, safePrototype) => {
9124
+ checkUseRecords(object) ? writeRecord(object,safePrototype) : writePlainObject(object,safePrototype);
9125
+ } : writeRecord;
9126
+
9022
9127
  const makeRoom = (end) => {
9023
9128
  let newSize;
9024
9129
  if (end > 0x1000000) {
@@ -9122,12 +9227,13 @@
9122
9227
  }
9123
9228
  };
9124
9229
  const writeStruct = (object, safePrototype) => {
9125
- let newPosition = writeStructSlots(object, target, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
9230
+ let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
9126
9231
  if (notifySharedUpdate)
9127
9232
  return hasSharedUpdate = true;
9128
9233
  position = newPosition;
9129
9234
  let startTarget = target;
9130
9235
  pack(value);
9236
+ resetStructures();
9131
9237
  if (startTarget !== target) {
9132
9238
  return { position, targetView, target }; // indicate the buffer was re-allocated
9133
9239
  }
@@ -9191,6 +9297,10 @@
9191
9297
  }
9192
9298
  }, {
9193
9299
  pack(set, allocateForWrite, pack) {
9300
+ if (this.setAsEmptyObject) {
9301
+ allocateForWrite(0);
9302
+ return pack({})
9303
+ }
9194
9304
  let array = Array.from(set);
9195
9305
  let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0);
9196
9306
  if (this.moreTypes) {
@@ -9371,6 +9481,7 @@
9371
9481
  defaultPackr.pack;
9372
9482
  const REUSE_BUFFER_MODE = 512;
9373
9483
  const RESET_BUFFER_MODE = 1024;
9484
+ const RESERVE_START_SPACE = 2048;
9374
9485
 
9375
9486
  // DEFLATE is a complex format; to read this code, you should probably check the RFC first:
9376
9487
  // https://tools.ietf.org/html/rfc1951
@@ -45304,7 +45415,7 @@ ${svg}
45304
45415
  });
45305
45416
  }
45306
45417
 
45307
- var version = "0.6.68";
45418
+ var version = "0.6.69";
45308
45419
 
45309
45420
  // Parse command line args into commands and run them
45310
45421
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.