@firtoz/collection-sync 5.0.0 → 6.0.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.
Files changed (128) hide show
  1. package/README.md +46 -0
  2. package/dist/cache-manager.d.ts +52 -0
  3. package/dist/cache-manager.js +5 -0
  4. package/dist/cache-manager.js.map +1 -0
  5. package/dist/chunk-3EHHMLSV.js +57 -0
  6. package/dist/chunk-3EHHMLSV.js.map +1 -0
  7. package/dist/chunk-43KYAIKY.js +46 -0
  8. package/dist/chunk-43KYAIKY.js.map +1 -0
  9. package/dist/chunk-4BEXLBCH.js +64 -0
  10. package/dist/chunk-4BEXLBCH.js.map +1 -0
  11. package/dist/chunk-5V6BSQAB.js +148 -0
  12. package/dist/chunk-5V6BSQAB.js.map +1 -0
  13. package/dist/chunk-5VMFQT5Z.js +112 -0
  14. package/dist/chunk-5VMFQT5Z.js.map +1 -0
  15. package/dist/chunk-6EHROJFY.js +111 -0
  16. package/dist/chunk-6EHROJFY.js.map +1 -0
  17. package/dist/chunk-6X3434GJ.js +21 -0
  18. package/dist/chunk-6X3434GJ.js.map +1 -0
  19. package/dist/chunk-BGJH6PH2.js +175 -0
  20. package/dist/chunk-BGJH6PH2.js.map +1 -0
  21. package/dist/chunk-BJJEAKXL.js +252 -0
  22. package/dist/chunk-BJJEAKXL.js.map +1 -0
  23. package/dist/chunk-GWIOC5CP.js +51 -0
  24. package/dist/chunk-GWIOC5CP.js.map +1 -0
  25. package/dist/chunk-HMLY7DHA.js +12 -0
  26. package/dist/chunk-HMLY7DHA.js.map +1 -0
  27. package/dist/chunk-I6RJWBGF.js +112 -0
  28. package/dist/chunk-I6RJWBGF.js.map +1 -0
  29. package/dist/chunk-M5MJHS6A.js +10 -0
  30. package/dist/chunk-M5MJHS6A.js.map +1 -0
  31. package/dist/chunk-O3KBDCEI.js +615 -0
  32. package/dist/chunk-O3KBDCEI.js.map +1 -0
  33. package/dist/chunk-OP53UBPN.js +19 -0
  34. package/dist/chunk-OP53UBPN.js.map +1 -0
  35. package/dist/chunk-P3JOTUAB.js +802 -0
  36. package/dist/chunk-P3JOTUAB.js.map +1 -0
  37. package/dist/chunk-QJP4GSJH.js +373 -0
  38. package/dist/chunk-QJP4GSJH.js.map +1 -0
  39. package/dist/chunk-RDDS7JQW.js +623 -0
  40. package/dist/chunk-RDDS7JQW.js.map +1 -0
  41. package/dist/chunk-TEH7V76G.js +209 -0
  42. package/dist/chunk-TEH7V76G.js.map +1 -0
  43. package/dist/chunk-UJ24XW52.js +20 -0
  44. package/dist/chunk-UJ24XW52.js.map +1 -0
  45. package/dist/chunk-UVZJL6QV.js +18 -0
  46. package/dist/chunk-UVZJL6QV.js.map +1 -0
  47. package/dist/chunk-XC4QNFSQ.js +238 -0
  48. package/dist/chunk-XC4QNFSQ.js.map +1 -0
  49. package/dist/chunk-YD5LVGWX.js +125 -0
  50. package/dist/chunk-YD5LVGWX.js.map +1 -0
  51. package/dist/chunk-YYGPIHHJ.js +166 -0
  52. package/dist/chunk-YYGPIHHJ.js.map +1 -0
  53. package/dist/connect-partial-sync.d.ts +41 -0
  54. package/dist/connect-partial-sync.js +6 -0
  55. package/dist/connect-partial-sync.js.map +1 -0
  56. package/dist/connect-sync.d.ts +26 -0
  57. package/dist/connect-sync.js +5 -0
  58. package/dist/connect-sync.js.map +1 -0
  59. package/dist/create-partial-synced-collection.d.ts +24 -0
  60. package/dist/create-partial-synced-collection.js +8 -0
  61. package/dist/create-partial-synced-collection.js.map +1 -0
  62. package/dist/create-synced-collection.d.ts +26 -0
  63. package/dist/create-synced-collection.js +8 -0
  64. package/dist/create-synced-collection.js.map +1 -0
  65. package/dist/index.d.ts +18 -0
  66. package/dist/index.js +18 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/partial-sync-client-bridge.d.ts +157 -0
  69. package/dist/partial-sync-client-bridge.js +6 -0
  70. package/dist/partial-sync-client-bridge.js.map +1 -0
  71. package/dist/partial-sync-interest.d.ts +48 -0
  72. package/dist/partial-sync-interest.js +6 -0
  73. package/dist/partial-sync-interest.js.map +1 -0
  74. package/dist/partial-sync-mutation-handler.d.ts +31 -0
  75. package/dist/partial-sync-mutation-handler.js +6 -0
  76. package/dist/partial-sync-mutation-handler.js.map +1 -0
  77. package/dist/partial-sync-predicate-match.d.ts +8 -0
  78. package/dist/partial-sync-predicate-match.js +4 -0
  79. package/dist/partial-sync-predicate-match.js.map +1 -0
  80. package/dist/partial-sync-row-key.d.ts +41 -0
  81. package/dist/partial-sync-row-key.js +4 -0
  82. package/dist/partial-sync-row-key.js.map +1 -0
  83. package/dist/partial-sync-server-bridge.d.ts +102 -0
  84. package/dist/partial-sync-server-bridge.js +8 -0
  85. package/dist/partial-sync-server-bridge.js.map +1 -0
  86. package/dist/react/constants.d.ts +12 -0
  87. package/dist/react/constants.js +4 -0
  88. package/dist/react/constants.js.map +1 -0
  89. package/dist/react/index.d.ts +19 -0
  90. package/dist/react/index.js +17 -0
  91. package/dist/react/index.js.map +1 -0
  92. package/dist/react/partial-sync-adapter.d.ts +40 -0
  93. package/dist/react/partial-sync-adapter.js +4 -0
  94. package/dist/react/partial-sync-adapter.js.map +1 -0
  95. package/dist/react/partial-sync-utils.d.ts +42 -0
  96. package/dist/react/partial-sync-utils.js +5 -0
  97. package/dist/react/partial-sync-utils.js.map +1 -0
  98. package/dist/react/range-conditions-expression.d.ts +49 -0
  99. package/dist/react/range-conditions-expression.js +4 -0
  100. package/dist/react/range-conditions-expression.js.map +1 -0
  101. package/dist/react/types.d.ts +196 -0
  102. package/dist/react/types.js +3 -0
  103. package/dist/react/types.js.map +1 -0
  104. package/dist/react/usePartialSyncCollection.d.ts +20 -0
  105. package/dist/react/usePartialSyncCollection.js +10 -0
  106. package/dist/react/usePartialSyncCollection.js.map +1 -0
  107. package/dist/react/usePartialSyncViewport.d.ts +20 -0
  108. package/dist/react/usePartialSyncViewport.js +7 -0
  109. package/dist/react/usePartialSyncViewport.js.map +1 -0
  110. package/dist/react/usePartialSyncWindow.d.ts +17 -0
  111. package/dist/react/usePartialSyncWindow.js +12 -0
  112. package/dist/react/usePartialSyncWindow.js.map +1 -0
  113. package/dist/react/usePredicateFilteredRows.d.ts +20 -0
  114. package/dist/react/usePredicateFilteredRows.js +6 -0
  115. package/dist/react/usePredicateFilteredRows.js.map +1 -0
  116. package/dist/sync-client-bridge.d.ts +48 -0
  117. package/dist/sync-client-bridge.js +6 -0
  118. package/dist/sync-client-bridge.js.map +1 -0
  119. package/dist/sync-protocol.d.ts +378 -0
  120. package/dist/sync-protocol.js +4 -0
  121. package/dist/sync-protocol.js.map +1 -0
  122. package/dist/sync-server-bridge.d.ts +35 -0
  123. package/dist/sync-server-bridge.js +6 -0
  124. package/dist/sync-server-bridge.js.map +1 -0
  125. package/dist/with-sync.d.ts +107 -0
  126. package/dist/with-sync.js +7 -0
  127. package/dist/with-sync.js.map +1 -0
  128. package/package.json +23 -17
@@ -0,0 +1,111 @@
1
+ import { matchesPredicate } from './chunk-3EHHMLSV.js';
2
+ import { partialSyncRowKey } from './chunk-UJ24XW52.js';
3
+ import { exhaustiveGuard } from '@firtoz/maybe-error';
4
+
5
+ function compareInterestValues(left, right) {
6
+ const leftValue = left instanceof Date ? left.getTime() : left;
7
+ const rightValue = right instanceof Date ? right.getTime() : right;
8
+ if (typeof leftValue === "number" && typeof rightValue === "number") {
9
+ return leftValue === rightValue ? 0 : leftValue < rightValue ? -1 : 1;
10
+ }
11
+ const leftString = String(leftValue);
12
+ const rightString = String(rightValue);
13
+ return leftString.localeCompare(rightString);
14
+ }
15
+ function sortValueWithinDeliveredRange(value, range) {
16
+ if (value === void 0 || value === null) return false;
17
+ const compareFrom = compareInterestValues(value, range.fromValue);
18
+ const compareTo = compareInterestValues(value, range.toValue);
19
+ if (range.sortDirection === "asc") {
20
+ return compareFrom >= 0 && compareTo <= 0;
21
+ }
22
+ return compareFrom <= 0 && compareTo >= 0;
23
+ }
24
+ function rowMatchesDeliveredSortRanges(ranges, row, getSortValue) {
25
+ if (ranges.length === 0) return false;
26
+ return ranges.some((range) => {
27
+ const sortValue = getSortValue(row, range.sortColumn);
28
+ return sortValueWithinDeliveredRange(sortValue, range);
29
+ });
30
+ }
31
+ function rowMatchesPredicateGroups(predicateGroups, row, getColumnValue) {
32
+ if (predicateGroups.length === 0) return false;
33
+ return predicateGroups.some(
34
+ (group) => matchesPredicate(row, group, getColumnValue)
35
+ );
36
+ }
37
+ function rowMatchesClientInterest(sortRanges, predicateGroups, row, getSortValue, getColumnValue) {
38
+ if (predicateGroups.length > 0) {
39
+ return rowMatchesPredicateGroups(predicateGroups, row, getColumnValue);
40
+ }
41
+ if (sortRanges.length > 0) {
42
+ return rowMatchesDeliveredSortRanges(sortRanges, row, getSortValue);
43
+ }
44
+ return false;
45
+ }
46
+ function classifyPartialSyncRangePatch(sortRanges, predicateGroups, change, getSortValue, getColumnValue, options) {
47
+ const deliveredIds = options?.deliveredRowIds;
48
+ if (change.type === "delete" && deliveredIds !== void 0) {
49
+ return deliveredIds.has(String(partialSyncRowKey(change.key))) ? { change } : null;
50
+ }
51
+ const hasInterest = sortRanges.length > 0 || predicateGroups.length > 0;
52
+ if (!hasInterest) return null;
53
+ if (change.type === "truncate") return { change };
54
+ if (change.type === "delete") return { change };
55
+ if (change.type === "insert") {
56
+ return rowMatchesClientInterest(
57
+ sortRanges,
58
+ predicateGroups,
59
+ change.value,
60
+ getSortValue,
61
+ getColumnValue
62
+ ) ? { change } : null;
63
+ }
64
+ if (change.type === "update") {
65
+ const newIn = rowMatchesClientInterest(
66
+ sortRanges,
67
+ predicateGroups,
68
+ change.value,
69
+ getSortValue,
70
+ getColumnValue
71
+ );
72
+ const oldIn = change.previousValue !== void 0 ? rowMatchesClientInterest(
73
+ sortRanges,
74
+ predicateGroups,
75
+ change.previousValue,
76
+ getSortValue,
77
+ getColumnValue
78
+ ) : false;
79
+ if (newIn && oldIn) return { change };
80
+ if (newIn && !oldIn) return { change, viewTransition: "enterView" };
81
+ if (!newIn && oldIn) return { change, viewTransition: "exitView" };
82
+ return null;
83
+ }
84
+ exhaustiveGuard(change);
85
+ }
86
+ function filterSyncMessagesForPredicateRange(conditions, changes, getSortValue, getColumnValue) {
87
+ const predicateGroups = [conditions];
88
+ const sortRanges = [];
89
+ const out = [];
90
+ for (const change of changes) {
91
+ if (change.type === "delete") {
92
+ out.push(change);
93
+ continue;
94
+ }
95
+ const patch = classifyPartialSyncRangePatch(
96
+ sortRanges,
97
+ predicateGroups,
98
+ change,
99
+ getSortValue,
100
+ getColumnValue
101
+ );
102
+ if (patch !== null) {
103
+ out.push(patch.change);
104
+ }
105
+ }
106
+ return out;
107
+ }
108
+
109
+ export { classifyPartialSyncRangePatch, compareInterestValues, filterSyncMessagesForPredicateRange, rowMatchesClientInterest, rowMatchesDeliveredSortRanges, rowMatchesPredicateGroups, sortValueWithinDeliveredRange };
110
+ //# sourceMappingURL=chunk-6EHROJFY.js.map
111
+ //# sourceMappingURL=chunk-6EHROJFY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/partial-sync-interest.ts"],"names":[],"mappings":"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","file":"chunk-6EHROJFY.js","sourcesContent":["import type { SyncMessage } from \"@firtoz/db-helpers\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport type { RangeCondition } from \"./sync-protocol\";\nimport type { PartialSyncRowShape } from \"./partial-sync-row-key\";\nimport { partialSyncRowKey } from \"./partial-sync-row-key\";\nimport { matchesPredicate } from \"./partial-sync-predicate-match\";\n\n/** Metadata on `rangePatch` when an update crosses client interest boundaries. */\nexport type PartialSyncViewTransition = \"enterView\" | \"exitView\";\n\nexport type PartialSyncPatchResult<TItem extends PartialSyncRowShape> = {\n\tchange: SyncMessage<TItem>;\n\tviewTransition?: PartialSyncViewTransition;\n};\n\n/** Tracked 1D sort interval delivered to a client (index / cursor queries). */\nexport type DeliveredRange = {\n\tsortColumn: string;\n\tsortDirection: \"asc\" | \"desc\";\n\tfromValue: unknown;\n\ttoValue: unknown;\n};\n\nexport function compareInterestValues(left: unknown, right: unknown): number {\n\tconst leftValue = left instanceof Date ? left.getTime() : left;\n\tconst rightValue = right instanceof Date ? right.getTime() : right;\n\tif (typeof leftValue === \"number\" && typeof rightValue === \"number\") {\n\t\treturn leftValue === rightValue ? 0 : leftValue < rightValue ? -1 : 1;\n\t}\n\tconst leftString = String(leftValue);\n\tconst rightString = String(rightValue);\n\treturn leftString.localeCompare(rightString);\n}\n\nexport function sortValueWithinDeliveredRange(\n\tvalue: unknown,\n\trange: DeliveredRange,\n): boolean {\n\tif (value === undefined || value === null) return false;\n\tconst compareFrom = compareInterestValues(value, range.fromValue);\n\tconst compareTo = compareInterestValues(value, range.toValue);\n\tif (range.sortDirection === \"asc\") {\n\t\treturn compareFrom >= 0 && compareTo <= 0;\n\t}\n\treturn compareFrom <= 0 && compareTo >= 0;\n}\n\nexport function rowMatchesDeliveredSortRanges<TItem>(\n\tranges: DeliveredRange[],\n\trow: TItem,\n\tgetSortValue: (row: TItem, column: string) => unknown,\n): boolean {\n\tif (ranges.length === 0) return false;\n\treturn ranges.some((range) => {\n\t\tconst sortValue = getSortValue(row, range.sortColumn);\n\t\treturn sortValueWithinDeliveredRange(sortValue, range);\n\t});\n}\n\nexport function rowMatchesPredicateGroups<TItem>(\n\tpredicateGroups: RangeCondition[][],\n\trow: TItem,\n\tgetColumnValue: (row: TItem, column: string) => unknown,\n): boolean {\n\tif (predicateGroups.length === 0) return false;\n\treturn predicateGroups.some((group) =>\n\t\tmatchesPredicate(row, group, getColumnValue),\n\t);\n}\n\nexport function rowMatchesClientInterest<TItem>(\n\tsortRanges: DeliveredRange[],\n\tpredicateGroups: RangeCondition[][],\n\trow: TItem,\n\tgetSortValue: (row: TItem, column: string) => unknown,\n\tgetColumnValue: (row: TItem, column: string) => unknown,\n): boolean {\n\t/** Predicate viewport wins over 1D sort windows so chunk sort keys do not widen visibility. */\n\tif (predicateGroups.length > 0) {\n\t\treturn rowMatchesPredicateGroups(predicateGroups, row, getColumnValue);\n\t}\n\tif (sortRanges.length > 0) {\n\t\treturn rowMatchesDeliveredSortRanges(sortRanges, row, getSortValue);\n\t}\n\treturn false;\n}\n\nexport type ClassifyPartialSyncRangePatchOptions = {\n\t/**\n\t * When set, `delete` is only forwarded if the key was previously delivered to this client\n\t * (viewport-scoped deletes). When omitted, `delete` is always forwarded (legacy behavior).\n\t */\n\tdeliveredRowIds?: ReadonlySet<string> | undefined;\n};\n\n/**\n * Maps a server-side change to a `rangePatch` payload, or `null` if this client should not\n * receive a patch. View enter/exit keeps the real `update` on the wire so clients can cache\n * rows and filter locally instead of fake delete/insert.\n */\nexport function classifyPartialSyncRangePatch<\n\tTItem extends PartialSyncRowShape,\n>(\n\tsortRanges: DeliveredRange[],\n\tpredicateGroups: RangeCondition[][],\n\tchange: SyncMessage<TItem>,\n\tgetSortValue: (row: TItem, column: string) => unknown,\n\tgetColumnValue: (row: TItem, column: string) => unknown,\n\toptions?: ClassifyPartialSyncRangePatchOptions,\n): PartialSyncPatchResult<TItem> | null {\n\tconst deliveredIds = options?.deliveredRowIds;\n\tif (change.type === \"delete\" && deliveredIds !== undefined) {\n\t\treturn deliveredIds.has(String(partialSyncRowKey(change.key)))\n\t\t\t? { change }\n\t\t\t: null;\n\t}\n\n\tconst hasInterest = sortRanges.length > 0 || predicateGroups.length > 0;\n\tif (!hasInterest) return null;\n\n\tif (change.type === \"truncate\") return { change };\n\tif (change.type === \"delete\") return { change };\n\n\tif (change.type === \"insert\") {\n\t\treturn rowMatchesClientInterest(\n\t\t\tsortRanges,\n\t\t\tpredicateGroups,\n\t\t\tchange.value,\n\t\t\tgetSortValue,\n\t\t\tgetColumnValue,\n\t\t)\n\t\t\t? { change }\n\t\t\t: null;\n\t}\n\n\tif (change.type === \"update\") {\n\t\tconst newIn = rowMatchesClientInterest(\n\t\t\tsortRanges,\n\t\t\tpredicateGroups,\n\t\t\tchange.value,\n\t\t\tgetSortValue,\n\t\t\tgetColumnValue,\n\t\t);\n\t\tconst oldIn =\n\t\t\tchange.previousValue !== undefined\n\t\t\t\t? rowMatchesClientInterest(\n\t\t\t\t\t\tsortRanges,\n\t\t\t\t\t\tpredicateGroups,\n\t\t\t\t\t\tchange.previousValue,\n\t\t\t\t\t\tgetSortValue,\n\t\t\t\t\t\tgetColumnValue,\n\t\t\t\t\t)\n\t\t\t\t: false;\n\t\tif (newIn && oldIn) return { change };\n\t\tif (newIn && !oldIn) return { change, viewTransition: \"enterView\" };\n\t\tif (!newIn && oldIn) return { change, viewTransition: \"exitView\" };\n\t\treturn null;\n\t}\n\n\texhaustiveGuard(change);\n}\n\n/**\n * Filters sync messages to those relevant to a single predicate range (viewport). Used for\n * `rangeDelta` so clients never receive changelog rows outside their requested conditions.\n *\n * - `insert` / `update` / `truncate`: uses {@link classifyPartialSyncRangePatch} with only this\n * predicate group (no sort ranges).\n * - `delete`: passed through unchanged — callers should filter deletes in the store when the\n * deleted row snapshot is available (e.g. changelog payload).\n */\nexport function filterSyncMessagesForPredicateRange<\n\tTItem extends PartialSyncRowShape,\n>(\n\tconditions: RangeCondition[],\n\tchanges: SyncMessage<TItem>[],\n\tgetSortValue: (row: TItem, column: string) => unknown,\n\tgetColumnValue: (row: TItem, column: string) => unknown,\n): SyncMessage<TItem>[] {\n\tconst predicateGroups: RangeCondition[][] = [conditions];\n\tconst sortRanges: DeliveredRange[] = [];\n\tconst out: SyncMessage<TItem>[] = [];\n\tfor (const change of changes) {\n\t\tif (change.type === \"delete\") {\n\t\t\tout.push(change);\n\t\t\tcontinue;\n\t\t}\n\t\tconst patch = classifyPartialSyncRangePatch(\n\t\t\tsortRanges,\n\t\t\tpredicateGroups,\n\t\t\tchange,\n\t\t\tgetSortValue,\n\t\t\tgetColumnValue,\n\t\t);\n\t\tif (patch !== null) {\n\t\t\tout.push(patch.change);\n\t\t}\n\t}\n\treturn out;\n}\n"]}
@@ -0,0 +1,21 @@
1
+ // src/react/partial-sync-adapter.ts
2
+ function createPartialSyncAdapter(config) {
3
+ return {
4
+ toConditions: config.toConditions,
5
+ expandViewport: config.expandViewport ?? ((v) => v),
6
+ sort: config.sort,
7
+ getSortValue: config.getSortValue
8
+ };
9
+ }
10
+ function betweenConditionsForNumericAxes(viewport, axes) {
11
+ return axes.map((axis) => ({
12
+ column: axis.column,
13
+ op: "between",
14
+ value: axis.min(viewport),
15
+ valueTo: axis.max(viewport)
16
+ }));
17
+ }
18
+
19
+ export { betweenConditionsForNumericAxes, createPartialSyncAdapter };
20
+ //# sourceMappingURL=chunk-6X3434GJ.js.map
21
+ //# sourceMappingURL=chunk-6X3434GJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/partial-sync-adapter.ts"],"names":[],"mappings":";AAsCO,SAAS,yBAKf,MAAA,EAC4D;AAC5D,EAAA,OAAO;AAAA,IACN,cAAc,MAAA,CAAO,YAAA;AAAA,IACrB,cAAA,EAAgB,MAAA,CAAO,cAAA,KAAmB,CAAC,CAAA,KAAM,CAAA,CAAA;AAAA,IACjD,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,cAAc,MAAA,CAAO;AAAA,GACtB;AACD;AAWO,SAAS,+BAAA,CACf,UACA,IAAA,EACmB;AACnB,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,IAC1B,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,EAAA,EAAI,SAAA;AAAA,IACJ,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA;AAAA,IACxB,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,QAAQ;AAAA,GAC3B,CAAE,CAAA;AACH","file":"chunk-6X3434GJ.js","sourcesContent":["import type { PartialSyncRowShape } from \"../partial-sync-row-key\";\nimport type { RangeCondition, SyncRangeSort } from \"../sync-protocol\";\n\n/** Row shape compatible with partial-sync viewport hooks (matches {@link PartialSyncItem}). */\nexport type PartialSyncViewportItem = PartialSyncRowShape;\n\nexport type PredicateSortSpec<TSortColumn extends string> = {\n\tcolumn: TSortColumn;\n\tdirection: SyncRangeSort[\"direction\"];\n};\n\n/**\n * Bundles predicate ↔ viewport mapping, optional prefetch expansion, and sort accessors for\n * {@link usePartialSyncViewport} + {@link usePredicateFilteredRows}.\n */\nexport type PartialSyncViewportAdapter<\n\tTItem extends PartialSyncViewportItem,\n\tTViewport,\n\tTSortColumn extends keyof TItem & string,\n> = {\n\ttoConditions: (viewport: TViewport) => RangeCondition[];\n\texpandViewport: (viewport: TViewport, pad: number) => TViewport;\n\tsort: PredicateSortSpec<TSortColumn>;\n\tgetSortValue: (row: TItem, column: TSortColumn) => unknown;\n};\n\nexport type CreatePartialSyncAdapterConfig<\n\tTItem extends PartialSyncViewportItem,\n\tTViewport,\n\tTSortColumn extends keyof TItem & string,\n> = {\n\ttoConditions: (viewport: TViewport) => RangeCondition[];\n\t/** Widen viewport before server `rangeQuery`. Default: identity (no prefetch). */\n\texpandViewport?: (viewport: TViewport, pad: number) => TViewport;\n\tsort: PredicateSortSpec<TSortColumn>;\n\tgetSortValue: (row: TItem, column: TSortColumn) => unknown;\n};\n\nexport function createPartialSyncAdapter<\n\tTItem extends PartialSyncViewportItem,\n\tTViewport,\n\tTSortColumn extends keyof TItem & string,\n>(\n\tconfig: CreatePartialSyncAdapterConfig<TItem, TViewport, TSortColumn>,\n): PartialSyncViewportAdapter<TItem, TViewport, TSortColumn> {\n\treturn {\n\t\ttoConditions: config.toConditions,\n\t\texpandViewport: config.expandViewport ?? ((v) => v),\n\t\tsort: config.sort,\n\t\tgetSortValue: config.getSortValue,\n\t};\n}\n\nexport type NumericAxisSpec<TViewport> = {\n\treadonly column: string;\n\treadonly min: (viewport: TViewport) => number;\n\treadonly max: (viewport: TViewport) => number;\n};\n\n/**\n * Builds `between` {@link RangeCondition}s from numeric axis accessors (N-D box / interval per column).\n */\nexport function betweenConditionsForNumericAxes<TViewport>(\n\tviewport: TViewport,\n\taxes: readonly NumericAxisSpec<TViewport>[],\n): RangeCondition[] {\n\treturn axes.map((axis) => ({\n\t\tcolumn: axis.column,\n\t\top: \"between\" as const,\n\t\tvalue: axis.min(viewport),\n\t\tvalueTo: axis.max(viewport),\n\t}));\n}\n"]}
@@ -0,0 +1,175 @@
1
+ import { usePredicateFilteredRows } from './chunk-YD5LVGWX.js';
2
+ import { DEFAULT_VIEWPORT_RANGE_MAX_WAIT_MS, DEFAULT_VIEWPORT_RANGE_QUIET_MS } from './chunk-M5MJHS6A.js';
3
+ import { useMemo, useSyncExternalStore, useRef, useEffectEvent, useLayoutEffect } from 'react';
4
+
5
+ function usePartialSyncViewport({
6
+ bridge,
7
+ bridgeState,
8
+ collection,
9
+ adapter,
10
+ viewport,
11
+ predicateLimit,
12
+ prefetchPad = 0,
13
+ quietMs = DEFAULT_VIEWPORT_RANGE_QUIET_MS,
14
+ maxWaitMs = DEFAULT_VIEWPORT_RANGE_MAX_WAIT_MS,
15
+ totalCountFallback = 0,
16
+ getColumnValue,
17
+ cacheDisplayMode = "immediate",
18
+ alwaysIncludeRowIds,
19
+ onRangeReconcile
20
+ }) {
21
+ const conditions = useMemo(
22
+ () => adapter.toConditions(viewport),
23
+ [adapter, viewport]
24
+ );
25
+ const confirmedKeysRevision = useSyncExternalStore(
26
+ (onStoreChange) => bridge.subscribeConfirmedKeysRevision(onStoreChange),
27
+ () => bridge.serverConfirmedKeysRevision,
28
+ () => 0
29
+ );
30
+ const viewportRows = usePredicateFilteredRows({
31
+ collection,
32
+ conditions,
33
+ sort: adapter.sort,
34
+ getSortValue: adapter.getSortValue,
35
+ ...getColumnValue !== void 0 ? { getColumnValue } : {},
36
+ limit: predicateLimit,
37
+ cacheDisplayMode,
38
+ ...alwaysIncludeRowIds !== void 0 && alwaysIncludeRowIds.length > 0 ? { alwaysIncludeRowIds } : {},
39
+ ...cacheDisplayMode === "confirmed" ? {
40
+ confirmedRowKeys: bridge.serverConfirmedKeys,
41
+ confirmedKeysRevision
42
+ } : {}
43
+ });
44
+ const fetchViewport = useMemo(
45
+ () => adapter.expandViewport(viewport, prefetchPad),
46
+ [adapter, prefetchPad, viewport]
47
+ );
48
+ const bridgeRef = useRef(bridge);
49
+ bridgeRef.current = bridge;
50
+ const adapterRef = useRef(adapter);
51
+ adapterRef.current = adapter;
52
+ const fetchViewportRef = useRef(fetchViewport);
53
+ fetchViewportRef.current = fetchViewport;
54
+ const predicateLimitRef = useRef(predicateLimit);
55
+ predicateLimitRef.current = predicateLimit;
56
+ const quietTimerRef = useRef(null);
57
+ const maxWaitTimerRef = useRef(null);
58
+ const lastRangeFetchAtRef = useRef(0);
59
+ const onRangeReconcileEvent = useEffectEvent(
60
+ (rec) => {
61
+ onRangeReconcile?.(rec);
62
+ }
63
+ );
64
+ const canReconcileWithManifest = useEffectEvent(() => {
65
+ const col = collection;
66
+ if (typeof col.get !== "function") return false;
67
+ const keys = bridge.serverConfirmedKeys;
68
+ if (keys.size === 0) return false;
69
+ for (const k of keys) {
70
+ let row = col.get(k);
71
+ if (row === void 0 && typeof k === "number") {
72
+ row = col.get(String(k));
73
+ } else if (row === void 0 && typeof k === "string") {
74
+ const n = Number(k);
75
+ if (!Number.isNaN(n)) row = col.get(n);
76
+ }
77
+ if (row !== void 0) return true;
78
+ }
79
+ return false;
80
+ });
81
+ useLayoutEffect(() => {
82
+ const clearQuietTimer = () => {
83
+ if (quietTimerRef.current !== null) {
84
+ clearTimeout(quietTimerRef.current);
85
+ quietTimerRef.current = null;
86
+ }
87
+ };
88
+ const clearMaxWaitTimer = () => {
89
+ if (maxWaitTimerRef.current !== null) {
90
+ clearTimeout(maxWaitTimerRef.current);
91
+ maxWaitTimerRef.current = null;
92
+ }
93
+ };
94
+ const runRangeQuery = () => {
95
+ clearQuietTimer();
96
+ clearMaxWaitTimer();
97
+ lastRangeFetchAtRef.current = performance.now();
98
+ const v = fetchViewportRef.current;
99
+ const ad = adapterRef.current;
100
+ const range = {
101
+ kind: "predicate",
102
+ conditions: ad.toConditions(v),
103
+ sort: ad.sort,
104
+ limit: predicateLimitRef.current
105
+ };
106
+ if (canReconcileWithManifest()) {
107
+ void bridgeRef.current.requestRangeReconcile(range).then((rec) => {
108
+ onRangeReconcileEvent(rec);
109
+ }).catch((err) => {
110
+ console.error("partial sync viewport rangeReconcile failed", err);
111
+ });
112
+ return;
113
+ }
114
+ void bridgeRef.current.requestRangeQuery(range).catch((err) => {
115
+ console.error("partial sync viewport rangeQuery failed", err);
116
+ });
117
+ };
118
+ clearQuietTimer();
119
+ clearMaxWaitTimer();
120
+ const now = performance.now();
121
+ const sinceLastFetch = now - lastRangeFetchAtRef.current;
122
+ const fetchImmediately = lastRangeFetchAtRef.current === 0 || sinceLastFetch >= maxWaitMs;
123
+ if (fetchImmediately) {
124
+ runRangeQuery();
125
+ return () => {
126
+ clearQuietTimer();
127
+ clearMaxWaitTimer();
128
+ };
129
+ }
130
+ quietTimerRef.current = setTimeout(runRangeQuery, quietMs);
131
+ maxWaitTimerRef.current = setTimeout(
132
+ runRangeQuery,
133
+ Math.max(0, maxWaitMs - sinceLastFetch)
134
+ );
135
+ return () => {
136
+ clearQuietTimer();
137
+ clearMaxWaitTimer();
138
+ };
139
+ }, [conditions, prefetchPad, maxWaitMs, quietMs]);
140
+ useLayoutEffect(() => {
141
+ if (typeof document === "undefined") return;
142
+ const onVisibleRefresh = () => {
143
+ if (document.visibilityState !== "visible") return;
144
+ const v = fetchViewportRef.current;
145
+ const ad = adapterRef.current;
146
+ const range = {
147
+ kind: "predicate",
148
+ conditions: ad.toConditions(v),
149
+ sort: ad.sort,
150
+ limit: predicateLimitRef.current
151
+ };
152
+ if (canReconcileWithManifest()) {
153
+ void bridgeRef.current.requestRangeReconcile(range).then((rec) => {
154
+ onRangeReconcileEvent(rec);
155
+ }).catch((err) => {
156
+ console.error("partial sync viewport rangeReconcile failed", err);
157
+ });
158
+ return;
159
+ }
160
+ void bridgeRef.current.requestRangeQuery(range).catch((err) => {
161
+ console.error("partial sync viewport rangeQuery failed", err);
162
+ });
163
+ };
164
+ document.addEventListener("visibilitychange", onVisibleRefresh);
165
+ return () => {
166
+ document.removeEventListener("visibilitychange", onVisibleRefresh);
167
+ };
168
+ }, []);
169
+ const totalCount = bridgeState.status === "partial" || bridgeState.status === "realtime" ? bridgeState.totalCount : totalCountFallback;
170
+ return { viewportRows, totalCount };
171
+ }
172
+
173
+ export { usePartialSyncViewport };
174
+ //# sourceMappingURL=chunk-BGJH6PH2.js.map
175
+ //# sourceMappingURL=chunk-BGJH6PH2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/usePartialSyncViewport.ts"],"names":[],"mappings":"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","file":"chunk-BGJH6PH2.js","sourcesContent":["import {\n\tuseEffectEvent,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n\tuseSyncExternalStore,\n} from \"react\";\nimport type { PartialSyncReconcileResult } from \"../partial-sync-client-bridge\";\nimport type { SyncRange } from \"../sync-protocol\";\nimport {\n\tDEFAULT_VIEWPORT_RANGE_MAX_WAIT_MS,\n\tDEFAULT_VIEWPORT_RANGE_QUIET_MS,\n} from \"./constants\";\nimport { usePredicateFilteredRows } from \"./usePredicateFilteredRows\";\nimport type {\n\tPartialSyncItem,\n\tUsePartialSyncViewportOptions,\n\tUsePartialSyncViewportResult,\n} from \"./types\";\n\n/**\n * Predicate viewport sync: debounced server `rangeQuery` for a moving logical viewport plus\n * {@link usePredicateFilteredRows} for the visible rows already in the local collection.\n */\nexport function usePartialSyncViewport<\n\tTItem extends PartialSyncItem,\n\tTViewport,\n\tTSortColumn extends keyof TItem & string,\n>({\n\tbridge,\n\tbridgeState,\n\tcollection,\n\tadapter,\n\tviewport,\n\tpredicateLimit,\n\tprefetchPad = 0,\n\tquietMs = DEFAULT_VIEWPORT_RANGE_QUIET_MS,\n\tmaxWaitMs = DEFAULT_VIEWPORT_RANGE_MAX_WAIT_MS,\n\ttotalCountFallback = 0,\n\tgetColumnValue,\n\tcacheDisplayMode = \"immediate\",\n\talwaysIncludeRowIds,\n\tonRangeReconcile,\n}: UsePartialSyncViewportOptions<\n\tTItem,\n\tTViewport,\n\tTSortColumn\n>): UsePartialSyncViewportResult<TItem> {\n\tconst conditions = useMemo(\n\t\t() => adapter.toConditions(viewport),\n\t\t[adapter, viewport],\n\t);\n\n\tconst confirmedKeysRevision = useSyncExternalStore(\n\t\t(onStoreChange) => bridge.subscribeConfirmedKeysRevision(onStoreChange),\n\t\t() => bridge.serverConfirmedKeysRevision,\n\t\t() => 0,\n\t);\n\n\tconst viewportRows = usePredicateFilteredRows({\n\t\tcollection,\n\t\tconditions,\n\t\tsort: adapter.sort,\n\t\tgetSortValue: adapter.getSortValue,\n\t\t...(getColumnValue !== undefined ? { getColumnValue } : {}),\n\t\tlimit: predicateLimit,\n\t\tcacheDisplayMode,\n\t\t...(alwaysIncludeRowIds !== undefined && alwaysIncludeRowIds.length > 0\n\t\t\t? { alwaysIncludeRowIds }\n\t\t\t: {}),\n\t\t...(cacheDisplayMode === \"confirmed\"\n\t\t\t? {\n\t\t\t\t\tconfirmedRowKeys: bridge.serverConfirmedKeys,\n\t\t\t\t\tconfirmedKeysRevision,\n\t\t\t\t}\n\t\t\t: {}),\n\t});\n\n\tconst fetchViewport = useMemo(\n\t\t() => adapter.expandViewport(viewport, prefetchPad),\n\t\t[adapter, prefetchPad, viewport],\n\t);\n\n\tconst bridgeRef = useRef(bridge);\n\tbridgeRef.current = bridge;\n\tconst adapterRef = useRef(adapter);\n\tadapterRef.current = adapter;\n\tconst fetchViewportRef = useRef(fetchViewport);\n\tfetchViewportRef.current = fetchViewport;\n\tconst predicateLimitRef = useRef(predicateLimit);\n\tpredicateLimitRef.current = predicateLimit;\n\n\tconst quietTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst maxWaitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst lastRangeFetchAtRef = useRef(0);\n\n\tconst onRangeReconcileEvent = useEffectEvent(\n\t\t(rec: PartialSyncReconcileResult<TItem>) => {\n\t\t\tonRangeReconcile?.(rec);\n\t\t},\n\t);\n\n\tconst canReconcileWithManifest = useEffectEvent((): boolean => {\n\t\tconst col = collection;\n\t\tif (typeof col.get !== \"function\") return false;\n\t\tconst keys = bridge.serverConfirmedKeys;\n\t\tif (keys.size === 0) return false;\n\t\tfor (const k of keys) {\n\t\t\tlet row = col.get(k);\n\t\t\tif (row === undefined && typeof k === \"number\") {\n\t\t\t\trow = col.get(String(k));\n\t\t\t} else if (row === undefined && typeof k === \"string\") {\n\t\t\t\tconst n = Number(k);\n\t\t\t\tif (!Number.isNaN(n)) row = col.get(n);\n\t\t\t}\n\t\t\tif (row !== undefined) return true;\n\t\t}\n\t\treturn false;\n\t});\n\n\tuseLayoutEffect(() => {\n\t\tconst clearQuietTimer = () => {\n\t\t\tif (quietTimerRef.current !== null) {\n\t\t\t\tclearTimeout(quietTimerRef.current);\n\t\t\t\tquietTimerRef.current = null;\n\t\t\t}\n\t\t};\n\t\tconst clearMaxWaitTimer = () => {\n\t\t\tif (maxWaitTimerRef.current !== null) {\n\t\t\t\tclearTimeout(maxWaitTimerRef.current);\n\t\t\t\tmaxWaitTimerRef.current = null;\n\t\t\t}\n\t\t};\n\n\t\tconst runRangeQuery = () => {\n\t\t\tclearQuietTimer();\n\t\t\tclearMaxWaitTimer();\n\t\t\tlastRangeFetchAtRef.current = performance.now();\n\t\t\tconst v = fetchViewportRef.current;\n\t\t\tconst ad = adapterRef.current;\n\t\t\tconst range: SyncRange = {\n\t\t\t\tkind: \"predicate\",\n\t\t\t\tconditions: ad.toConditions(v),\n\t\t\t\tsort: ad.sort,\n\t\t\t\tlimit: predicateLimitRef.current,\n\t\t\t};\n\t\t\tif (canReconcileWithManifest()) {\n\t\t\t\tvoid bridgeRef.current\n\t\t\t\t\t.requestRangeReconcile(range)\n\t\t\t\t\t.then((rec) => {\n\t\t\t\t\t\tonRangeReconcileEvent(rec);\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\t\tconsole.error(\"partial sync viewport rangeReconcile failed\", err);\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvoid bridgeRef.current.requestRangeQuery(range).catch((err: unknown) => {\n\t\t\t\tconsole.error(\"partial sync viewport rangeQuery failed\", err);\n\t\t\t});\n\t\t};\n\n\t\tclearQuietTimer();\n\t\tclearMaxWaitTimer();\n\n\t\tconst now = performance.now();\n\t\tconst sinceLastFetch = now - lastRangeFetchAtRef.current;\n\t\tconst fetchImmediately =\n\t\t\tlastRangeFetchAtRef.current === 0 || sinceLastFetch >= maxWaitMs;\n\n\t\tif (fetchImmediately) {\n\t\t\trunRangeQuery();\n\t\t\treturn () => {\n\t\t\t\tclearQuietTimer();\n\t\t\t\tclearMaxWaitTimer();\n\t\t\t};\n\t\t}\n\n\t\tquietTimerRef.current = setTimeout(runRangeQuery, quietMs);\n\t\tmaxWaitTimerRef.current = setTimeout(\n\t\t\trunRangeQuery,\n\t\t\tMath.max(0, maxWaitMs - sinceLastFetch),\n\t\t);\n\n\t\treturn () => {\n\t\t\tclearQuietTimer();\n\t\t\tclearMaxWaitTimer();\n\t\t};\n\t}, [conditions, prefetchPad, maxWaitMs, quietMs]);\n\n\tuseLayoutEffect(() => {\n\t\tif (typeof document === \"undefined\") return;\n\t\tconst onVisibleRefresh = (): void => {\n\t\t\tif (document.visibilityState !== \"visible\") return;\n\t\t\tconst v = fetchViewportRef.current;\n\t\t\tconst ad = adapterRef.current;\n\t\t\tconst range: SyncRange = {\n\t\t\t\tkind: \"predicate\",\n\t\t\t\tconditions: ad.toConditions(v),\n\t\t\t\tsort: ad.sort,\n\t\t\t\tlimit: predicateLimitRef.current,\n\t\t\t};\n\t\t\tif (canReconcileWithManifest()) {\n\t\t\t\tvoid bridgeRef.current\n\t\t\t\t\t.requestRangeReconcile(range)\n\t\t\t\t\t.then((rec) => {\n\t\t\t\t\t\tonRangeReconcileEvent(rec);\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\t\tconsole.error(\"partial sync viewport rangeReconcile failed\", err);\n\t\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvoid bridgeRef.current.requestRangeQuery(range).catch((err: unknown) => {\n\t\t\t\tconsole.error(\"partial sync viewport rangeQuery failed\", err);\n\t\t\t});\n\t\t};\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibleRefresh);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibleRefresh);\n\t\t};\n\t}, []);\n\n\tconst totalCount =\n\t\tbridgeState.status === \"partial\" || bridgeState.status === \"realtime\"\n\t\t\t? bridgeState.totalCount\n\t\t\t: totalCountFallback;\n\n\treturn { viewportRows, totalCount };\n}\n"]}
@@ -0,0 +1,252 @@
1
+ import { exhaustiveGuard } from '@firtoz/maybe-error';
2
+ import { z } from 'zod';
3
+
4
+ // src/sync-protocol.ts
5
+ var DEFAULT_SYNC_COLLECTION_ID = "default";
6
+ var collectionIdSchema = z.string().min(1).default(DEFAULT_SYNC_COLLECTION_ID);
7
+ var mutationTypeSchema = z.enum(["insert", "update", "delete", "truncate"]);
8
+ var mutationIntentSchema = z.object({
9
+ clientMutationId: z.string().min(1),
10
+ type: mutationTypeSchema,
11
+ value: z.record(z.string(), z.unknown()).optional(),
12
+ previousValue: z.record(z.string(), z.unknown()).optional(),
13
+ key: z.union([z.string(), z.number()]).optional()
14
+ });
15
+ var syncRangeSortSchema = z.object({
16
+ column: z.string().min(1),
17
+ direction: z.enum(["asc", "desc"])
18
+ });
19
+ var rangeConditionOpSchema = z.enum([
20
+ "gt",
21
+ "gte",
22
+ "lt",
23
+ "lte",
24
+ "eq",
25
+ "neq",
26
+ "between"
27
+ ]);
28
+ var rangeConditionSchema = z.object({
29
+ column: z.string().min(1),
30
+ op: rangeConditionOpSchema,
31
+ value: z.unknown(),
32
+ valueTo: z.unknown().optional()
33
+ });
34
+ var indexRangeCursorSchema = z.object({
35
+ kind: z.literal("index"),
36
+ mode: z.literal("cursor"),
37
+ sort: syncRangeSortSchema,
38
+ limit: z.number().int().positive(),
39
+ afterCursor: z.unknown().nullable()
40
+ });
41
+ var indexRangeOffsetSchema = z.object({
42
+ kind: z.literal("index"),
43
+ mode: z.literal("offset"),
44
+ sort: syncRangeSortSchema,
45
+ limit: z.number().int().positive(),
46
+ offset: z.number().int().nonnegative()
47
+ });
48
+ var predicateRangeSchema = z.object({
49
+ kind: z.literal("predicate"),
50
+ conditions: z.array(rangeConditionSchema).min(1),
51
+ sort: syncRangeSortSchema.optional(),
52
+ limit: z.number().int().positive().optional()
53
+ });
54
+ var syncRangeSchema = z.union([
55
+ indexRangeCursorSchema,
56
+ indexRangeOffsetSchema,
57
+ predicateRangeSchema
58
+ ]);
59
+ var rangeFingerprintSchema = z.object({
60
+ version: z.number().int().nonnegative(),
61
+ count: z.number().int().nonnegative()
62
+ });
63
+ function createClientMessageSchema() {
64
+ return z.discriminatedUnion("type", [
65
+ z.object({
66
+ type: z.literal("mutateBatch"),
67
+ collectionId: collectionIdSchema,
68
+ clientId: z.string().min(1),
69
+ mutations: z.array(mutationIntentSchema).min(1)
70
+ }),
71
+ z.object({
72
+ type: z.literal("syncHello"),
73
+ collectionId: collectionIdSchema,
74
+ clientId: z.string().min(1),
75
+ lastAckedServerVersion: z.number().int().nonnegative()
76
+ }),
77
+ z.object({
78
+ type: z.literal("ping"),
79
+ collectionId: collectionIdSchema,
80
+ clientId: z.string().min(1),
81
+ timestamp: z.number()
82
+ }),
83
+ z.object({
84
+ type: z.literal("queryRange"),
85
+ collectionId: collectionIdSchema,
86
+ clientId: z.string().min(1),
87
+ requestId: z.string().min(1),
88
+ sort: z.object({
89
+ column: z.string().min(1),
90
+ direction: z.enum(["asc", "desc"])
91
+ }),
92
+ limit: z.number().int().positive(),
93
+ afterCursor: z.unknown().nullable()
94
+ }),
95
+ z.object({
96
+ type: z.literal("queryByOffset"),
97
+ collectionId: collectionIdSchema,
98
+ clientId: z.string().min(1),
99
+ requestId: z.string().min(1),
100
+ sort: z.object({
101
+ column: z.string().min(1),
102
+ direction: z.enum(["asc", "desc"])
103
+ }),
104
+ limit: z.number().int().positive(),
105
+ offset: z.number().int().nonnegative()
106
+ }),
107
+ z.object({
108
+ type: z.literal("rangeQuery"),
109
+ collectionId: collectionIdSchema,
110
+ clientId: z.string().min(1),
111
+ requestId: z.string().min(1),
112
+ range: syncRangeSchema,
113
+ fingerprint: rangeFingerprintSchema.optional()
114
+ }),
115
+ z.object({
116
+ type: z.literal("rangeReconcile"),
117
+ collectionId: collectionIdSchema,
118
+ clientId: z.string().min(1),
119
+ requestId: z.string().min(1),
120
+ range: syncRangeSchema,
121
+ manifest: z.array(
122
+ z.object({
123
+ id: z.union([z.string(), z.number()]),
124
+ version: z.number().int().nonnegative()
125
+ })
126
+ )
127
+ })
128
+ ]);
129
+ }
130
+ var clientMessageSchema = createClientMessageSchema();
131
+ function createServerMessageSchema() {
132
+ const syncMessageSchema = z.custom();
133
+ return z.discriminatedUnion("type", [
134
+ z.object({
135
+ type: z.literal("ack"),
136
+ collectionId: collectionIdSchema,
137
+ clientId: z.string().min(1),
138
+ clientMutationIds: z.array(z.string().min(1)),
139
+ serverVersion: z.number().int().nonnegative(),
140
+ changes: z.array(syncMessageSchema)
141
+ }),
142
+ z.object({
143
+ type: z.literal("syncBatch"),
144
+ collectionId: collectionIdSchema,
145
+ serverVersion: z.number().int().nonnegative(),
146
+ changes: z.array(syncMessageSchema)
147
+ }),
148
+ z.object({
149
+ type: z.literal("syncBackfill"),
150
+ collectionId: collectionIdSchema,
151
+ mode: z.enum(["snapshot", "delta"]),
152
+ serverVersion: z.number().int().nonnegative(),
153
+ changes: z.array(syncMessageSchema),
154
+ chunkIndex: z.number().int().nonnegative().optional(),
155
+ totalChunks: z.number().int().positive().optional()
156
+ }),
157
+ z.object({
158
+ type: z.literal("reject"),
159
+ collectionId: collectionIdSchema,
160
+ clientId: z.string().min(1),
161
+ clientMutationId: z.string().min(1),
162
+ reason: z.string().min(1),
163
+ correctiveChanges: z.array(syncMessageSchema).default([])
164
+ }),
165
+ z.object({
166
+ type: z.literal("pong"),
167
+ collectionId: collectionIdSchema,
168
+ timestamp: z.number()
169
+ }),
170
+ z.object({
171
+ type: z.literal("queryRangeChunk"),
172
+ collectionId: collectionIdSchema,
173
+ requestId: z.string().min(1),
174
+ rows: z.array(z.custom()),
175
+ totalCount: z.number().int().nonnegative(),
176
+ lastCursor: z.unknown().nullable(),
177
+ hasMore: z.boolean(),
178
+ chunkIndex: z.number().int().nonnegative(),
179
+ done: z.boolean()
180
+ }),
181
+ z.object({
182
+ type: z.literal("rangePatch"),
183
+ collectionId: collectionIdSchema,
184
+ change: syncMessageSchema,
185
+ viewTransition: z.enum(["enterView", "exitView"]).optional()
186
+ }),
187
+ z.object({
188
+ type: z.literal("rangeUpToDate"),
189
+ collectionId: collectionIdSchema,
190
+ requestId: z.string().min(1),
191
+ totalCount: z.number().int().nonnegative()
192
+ }),
193
+ z.object({
194
+ type: z.literal("rangeDelta"),
195
+ collectionId: collectionIdSchema,
196
+ requestId: z.string().min(1),
197
+ totalCount: z.number().int().nonnegative(),
198
+ changes: z.array(syncMessageSchema),
199
+ lastCursor: z.unknown().optional()
200
+ }),
201
+ z.object({
202
+ type: z.literal("rangeReconcileResult"),
203
+ collectionId: collectionIdSchema,
204
+ requestId: z.string().min(1),
205
+ added: z.array(syncMessageSchema),
206
+ updated: z.array(syncMessageSchema),
207
+ stale: z.array(z.union([z.string(), z.number()])),
208
+ movedHints: z.array(
209
+ z.object({
210
+ id: z.union([z.string(), z.number()]),
211
+ hint: z.record(z.string(), z.unknown())
212
+ })
213
+ ),
214
+ totalCount: z.number().int().nonnegative()
215
+ })
216
+ ]);
217
+ }
218
+ function withServerCollectionId(collectionId, message) {
219
+ return { ...message, collectionId };
220
+ }
221
+ var serverMessageSchema = createServerMessageSchema();
222
+ function toSyncMessage(intent) {
223
+ switch (intent.type) {
224
+ case "insert":
225
+ if (!intent.value) throw new Error("Insert intent requires value");
226
+ return { type: "insert", value: intent.value };
227
+ case "update":
228
+ if (!intent.value || !intent.previousValue) {
229
+ throw new Error("Update intent requires value and previousValue");
230
+ }
231
+ return {
232
+ type: "update",
233
+ value: intent.value,
234
+ previousValue: intent.previousValue
235
+ };
236
+ case "delete":
237
+ if (intent.key === void 0)
238
+ throw new Error("Delete intent requires key");
239
+ return { type: "delete", key: intent.key };
240
+ case "truncate":
241
+ return { type: "truncate" };
242
+ default:
243
+ exhaustiveGuard(intent.type);
244
+ }
245
+ }
246
+ function createClientMutationId(prefix = "m") {
247
+ return `${prefix}_${crypto.randomUUID()}`;
248
+ }
249
+
250
+ export { DEFAULT_SYNC_COLLECTION_ID, clientMessageSchema, createClientMessageSchema, createClientMutationId, createServerMessageSchema, mutationIntentSchema, serverMessageSchema, syncRangeSchema, toSyncMessage, withServerCollectionId };
251
+ //# sourceMappingURL=chunk-BJJEAKXL.js.map
252
+ //# sourceMappingURL=chunk-BJJEAKXL.js.map