@lavalogic/scoria 0.38.4 → 0.38.6

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.
@@ -37,6 +37,31 @@ const LAYOUT_PERSIST_DEBOUNCE_MS = 250;
37
37
  * auto-persist effects behave consistently.
38
38
  */
39
39
  const FILTER_PERSIST_DEBOUNCE_MS = 250;
40
+ /**
41
+ * `JSON.stringify` with object keys sorted recursively, so two
42
+ * structurally-equal values produce an identical string regardless of
43
+ * key insertion order. Array element order is preserved (it is
44
+ * significant for `columnOrder` and pinning entries).
45
+ *
46
+ * Used for the layout / filter dirty comparison: a baseline parsed from a
47
+ * backend JSON column has arbitrary key order, while `_captureLayout` /
48
+ * `_captureFilter` build their objects in a fixed order - a plain
49
+ * `JSON.stringify` compare would then report a false "dirty" the moment a
50
+ * saved view is applied.
51
+ */
52
+ function stableStringify(value) {
53
+ if (value === null || typeof value !== 'object') {
54
+ return JSON.stringify(value) ?? 'null';
55
+ }
56
+ if (Array.isArray(value)) {
57
+ return `[${value.map(stableStringify).join(',')}]`;
58
+ }
59
+ const record = value;
60
+ return `{${Object.keys(record)
61
+ .sort()
62
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
63
+ .join(',')}}`;
64
+ }
40
65
  /**
41
66
  * The complete set of filter-mode operator strings scoria recognises -
42
67
  * the union of every `StringFilterMode` and `NumberDateFilterMode`
@@ -412,13 +437,14 @@ export class TableContext {
412
437
  this._layoutPersistTimer = undefined;
413
438
  this._preferences.persistLayout(snapshot);
414
439
  // Dirty tracking: the layout is dirty when it diverges
415
- // from the active view's baseline. `_captureLayout`
416
- // builds its object deterministically, so a stable
417
- // `JSON.stringify` comparison is sufficient. When no
418
- // baseline has been captured yet (should not happen
419
- // past boot) treat the layout as clean.
440
+ // from the active view's baseline. The baseline can be a
441
+ // layout parsed from a backend JSON column (arbitrary key
442
+ // order), so the comparison must be key-order-insensitive
443
+ // - a plain `JSON.stringify` compare flagged every applied
444
+ // saved view as dirty. When no baseline has been captured
445
+ // yet (should not happen past boot) treat it as clean.
420
446
  const differs = this._viewBaseline !== undefined &&
421
- JSON.stringify(snapshot) !== JSON.stringify(this._viewBaseline);
447
+ stableStringify(snapshot) !== stableStringify(this._viewBaseline);
422
448
  this._viewState.markDirty('layout', differs);
423
449
  this._persistActiveView();
424
450
  }, LAYOUT_PERSIST_DEBOUNCE_MS);
@@ -449,13 +475,13 @@ export class TableContext {
449
475
  this._filterPersistTimer = undefined;
450
476
  this._preferences.persistFilter(snapshot);
451
477
  // Dirty tracking: the filter is dirty when it diverges from
452
- // the active filter view's baseline. `_captureFilter` builds
453
- // its object deterministically (sorted entries / sorted mode
454
- // keys), so a stable `JSON.stringify` comparison is
455
- // sufficient. When no baseline has been captured yet (should
456
- // not happen past boot) treat the filter as clean.
478
+ // the active filter view's baseline. The baseline can be a
479
+ // filter parsed from a backend JSON column (arbitrary key
480
+ // order), so the comparison is key-order-insensitive via
481
+ // `stableStringify`. When no baseline has been captured yet
482
+ // (should not happen past boot) treat the filter as clean.
457
483
  const differs = this._filterBaseline !== undefined &&
458
- JSON.stringify(snapshot) !== JSON.stringify(this._filterBaseline);
484
+ stableStringify(snapshot) !== stableStringify(this._filterBaseline);
459
485
  this._viewState.markDirty('filter', differs);
460
486
  this._persistActiveFilterView();
461
487
  }, FILTER_PERSIST_DEBOUNCE_MS);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.38.4",
4
+ "version": "0.38.6",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },