@evolu/common 8.6.2 → 8.7.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.
@@ -170,14 +170,11 @@
170
170
  * initiator/non-initiator terminology instead, and consolidate into a single
171
171
  * `applyProtocolMessage` function with conditional arguments to reduce code
172
172
  * duplication.
173
- * - Replace try-catch with Result + new Error (to preserve stacktraces). Measure
174
- * Result overhead, it should be super small.
175
173
  * - Allow clients to broadcast messages that are not persisted by relays. This
176
174
  * would enable real-time ephemeral data (like cursor positions, typing
177
175
  * indicators) to be forwarded by relays without storage overhead.
178
176
  */
179
177
 
180
- import { Packr } from "msgpackr";
181
178
  import {
182
179
  createMutableArray,
183
180
  isNonEmptyArray,
@@ -190,6 +187,8 @@ import {
190
187
  bytesToHex,
191
188
  bytesToUtf8,
192
189
  createBuffer,
190
+ decodeJsonValue,
191
+ encodeJsonValue,
193
192
  hexToBytes,
194
193
  utf8ToBytes,
195
194
  } from "../Buffer.ts";
@@ -271,13 +270,7 @@ import {
271
270
  timestampToTimestampBytes,
272
271
  } from "./Timestamp.ts";
273
272
 
274
- /**
275
- * Evolu uses MessagePack for numbers and JSONs.
276
- *
277
- * - `variableMapSize: true` - More compact maps, ~5-10% slower encoding
278
- * - `useRecords: false` - Standard MessagePack without extensions
279
- */
280
- const packr = new Packr({ variableMapSize: true, useRecords: false });
273
+ const jsonBuffer = createBuffer();
281
274
 
282
275
  const minProtocolMessageMaxSize = 1_000_000;
283
276
  const maxProtocolMessageMaxSize = 100_000_000;
@@ -1779,29 +1772,12 @@ const decodeId = (buffer: Buffer): Id => {
1779
1772
  * For NonNegativeInt, Evolu provides more efficient encoding.
1780
1773
  */
1781
1774
  export const encodeNumber = (buffer: Buffer, number: FiniteNumber): void => {
1782
- buffer.extend(packr.pack(number));
1775
+ encodeJsonValue(buffer, number);
1783
1776
  };
1784
1777
 
1785
1778
  export const decodeNumber = (buffer: Buffer): FiniteNumber => {
1786
- let number: unknown;
1787
- let end: unknown;
1788
-
1789
- packr.unpackMultiple(
1790
- buffer.unwrap(),
1791
- (n: unknown, _: unknown, e: unknown) => {
1792
- number = n;
1793
- end = e;
1794
- return false;
1795
- },
1796
- );
1797
-
1798
- const endResult = NonNegativeInt.fromUnknown(end);
1799
- if (!endResult.ok) throw new ProtocolDecodeError(endResult.error.type);
1800
-
1801
- const numberResult = FiniteNumber.fromUnknown(number);
1779
+ const numberResult = FiniteNumber.fromUnknown(decodeJsonValue(buffer));
1802
1780
  if (!numberResult.ok) throw new ProtocolDecodeError(numberResult.error.type);
1803
-
1804
- buffer.shiftN(endResult.value);
1805
1781
  return numberResult.value;
1806
1782
  };
1807
1783
 
@@ -2157,10 +2133,16 @@ export const encodeSqliteValue = (buffer: Buffer, value: SqliteValue): void => {
2157
2133
  // Some valid JSON strings like "-0E0" get normalized to "0" during parsing,
2158
2134
  // which would cause data corruption if we don't verify round-trip safety.
2159
2135
  if (json.ok && JSON.stringify(jsonToJsonValue(json.value)) === value) {
2160
- const jsonBytes = packr.pack(jsonToJsonValue(json.value));
2161
- encodeNonNegativeInt(buffer, ProtocolValueType.Json);
2162
- encodeLength(buffer, jsonBytes);
2163
- buffer.extend(jsonBytes);
2136
+ jsonBuffer.reset();
2137
+ try {
2138
+ encodeJsonValue(jsonBuffer, jsonToJsonValue(json.value));
2139
+ const jsonBytes = jsonBuffer.unwrap();
2140
+ encodeNonNegativeInt(buffer, ProtocolValueType.Json);
2141
+ encodeLength(buffer, jsonBytes);
2142
+ buffer.extend(jsonBytes);
2143
+ } finally {
2144
+ jsonBuffer.reset();
2145
+ }
2164
2146
  return;
2165
2147
  }
2166
2148
 
@@ -2179,7 +2161,9 @@ export const encodeSqliteValue = (buffer: Buffer, value: SqliteValue): void => {
2179
2161
  }
2180
2162
 
2181
2163
  case "number": {
2182
- if (NonNegativeInt.is(value)) {
2164
+ // Negative zero is a non-negative integer in JavaScript, but integer
2165
+ // encoding would lose its observable sign.
2166
+ if (!globalThis.Object.is(value, -0) && NonNegativeInt.is(value)) {
2183
2167
  if (isSmallInt(value)) {
2184
2168
  encodeNonNegativeInt(buffer, value);
2185
2169
  return;
@@ -2229,8 +2213,12 @@ export const decodeSqliteValue = (buffer: Buffer): SqliteValue => {
2229
2213
 
2230
2214
  case ProtocolValueType.Json: {
2231
2215
  const length = decodeLength(buffer);
2232
- const bytes = buffer.shiftN(length);
2233
- return JSON.stringify(packr.unpack(bytes));
2216
+ const lengthBeforeDecoding = buffer.getLength();
2217
+ const value = decodeJsonValue(buffer);
2218
+ if (lengthBeforeDecoding - buffer.getLength() !== length) {
2219
+ throw new ProtocolDecodeError("Invalid JSON MessagePack length");
2220
+ }
2221
+ return JSON.stringify(value);
2234
2222
  }
2235
2223
 
2236
2224
  case ProtocolValueType.DateIsoWithNonNegativeTime: