@syncular/client 0.25.0 → 0.25.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.
@@ -136,6 +136,17 @@ function valuesEqual(left, right) {
136
136
  typeof right !== 'object') {
137
137
  return false;
138
138
  }
139
+ if (left instanceof Date || right instanceof Date) {
140
+ return (left instanceof Date &&
141
+ right instanceof Date &&
142
+ Object.is(left.getTime(), right.getTime()));
143
+ }
144
+ // Errors and class instances keep state outside their enumerable keys.
145
+ for (const value of [left, right]) {
146
+ const prototype = Object.getPrototypeOf(value);
147
+ if (prototype !== Object.prototype && prototype !== null)
148
+ return false;
149
+ }
139
150
  const leftRecord = left;
140
151
  const rightRecord = right;
141
152
  const keys = Object.keys(leftRecord);
@@ -146,7 +157,7 @@ function valuesEqual(left, right) {
146
157
  }
147
158
  function reconcileRows(previous, fresh, rowKey) {
148
159
  if (previous.length === 0)
149
- return fresh;
160
+ return fresh.length === 0 ? previous : fresh;
150
161
  if (rowKey === undefined) {
151
162
  let changed = previous.length !== fresh.length;
152
163
  const next = fresh.map((row, index) => {
@@ -539,7 +550,7 @@ class ValueEntry {
539
550
  }
540
551
  set(next) {
541
552
  this.invalidate();
542
- if (next === this.value)
553
+ if (valuesEqual(next, this.value))
543
554
  return;
544
555
  this.value = next;
545
556
  for (const listener of this.#listeners)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/client",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Syncular TypeScript client core — offline-first sync over SQLite (WASM/OPFS, Bun, Node)",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -89,10 +89,10 @@
89
89
  },
90
90
  "dependencies": {
91
91
  "@sqlite.org/sqlite-wasm": "^3.53.0-build1",
92
- "@syncular/core": "0.25.0"
92
+ "@syncular/core": "0.25.1"
93
93
  },
94
94
  "devDependencies": {
95
- "@syncular/server": "0.25.0",
95
+ "@syncular/server": "0.25.1",
96
96
  "playwright": "1.63.0"
97
97
  }
98
98
  }
@@ -249,6 +249,18 @@ function valuesEqual(left: unknown, right: unknown): boolean {
249
249
  ) {
250
250
  return false;
251
251
  }
252
+ if (left instanceof Date || right instanceof Date) {
253
+ return (
254
+ left instanceof Date &&
255
+ right instanceof Date &&
256
+ Object.is(left.getTime(), right.getTime())
257
+ );
258
+ }
259
+ // Errors and class instances keep state outside their enumerable keys.
260
+ for (const value of [left, right]) {
261
+ const prototype = Object.getPrototypeOf(value);
262
+ if (prototype !== Object.prototype && prototype !== null) return false;
263
+ }
252
264
  const leftRecord = left as Record<string, unknown>;
253
265
  const rightRecord = right as Record<string, unknown>;
254
266
  const keys = Object.keys(leftRecord);
@@ -265,7 +277,7 @@ function reconcileRows<Row>(
265
277
  fresh: readonly Row[],
266
278
  rowKey: ((row: Row) => readonly SqlValue[]) | undefined,
267
279
  ): readonly Row[] {
268
- if (previous.length === 0) return fresh;
280
+ if (previous.length === 0) return fresh.length === 0 ? previous : fresh;
269
281
  if (rowKey === undefined) {
270
282
  let changed = previous.length !== fresh.length;
271
283
  const next = fresh.map((row, index) => {
@@ -668,7 +680,7 @@ class ValueEntry<T> implements ExternalStoreEntry<T> {
668
680
  }
669
681
  set(next: T): void {
670
682
  this.invalidate();
671
- if (next === this.value) return;
683
+ if (valuesEqual(next, this.value)) return;
672
684
  this.value = next;
673
685
  for (const listener of this.#listeners) listener();
674
686
  }