@firtoz/collection-sync 4.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.
- package/README.md +46 -0
- package/dist/cache-manager.d.ts +52 -0
- package/dist/cache-manager.js +5 -0
- package/dist/cache-manager.js.map +1 -0
- package/dist/chunk-3EHHMLSV.js +57 -0
- package/dist/chunk-3EHHMLSV.js.map +1 -0
- package/dist/chunk-43KYAIKY.js +46 -0
- package/dist/chunk-43KYAIKY.js.map +1 -0
- package/dist/chunk-4BEXLBCH.js +64 -0
- package/dist/chunk-4BEXLBCH.js.map +1 -0
- package/dist/chunk-5V6BSQAB.js +148 -0
- package/dist/chunk-5V6BSQAB.js.map +1 -0
- package/dist/chunk-5VMFQT5Z.js +112 -0
- package/dist/chunk-5VMFQT5Z.js.map +1 -0
- package/dist/chunk-6EHROJFY.js +111 -0
- package/dist/chunk-6EHROJFY.js.map +1 -0
- package/dist/chunk-6X3434GJ.js +21 -0
- package/dist/chunk-6X3434GJ.js.map +1 -0
- package/dist/chunk-BGJH6PH2.js +175 -0
- package/dist/chunk-BGJH6PH2.js.map +1 -0
- package/dist/chunk-BJJEAKXL.js +252 -0
- package/dist/chunk-BJJEAKXL.js.map +1 -0
- package/dist/chunk-GWIOC5CP.js +51 -0
- package/dist/chunk-GWIOC5CP.js.map +1 -0
- package/dist/chunk-HMLY7DHA.js +12 -0
- package/dist/chunk-HMLY7DHA.js.map +1 -0
- package/dist/chunk-I6RJWBGF.js +112 -0
- package/dist/chunk-I6RJWBGF.js.map +1 -0
- package/dist/chunk-M5MJHS6A.js +10 -0
- package/dist/chunk-M5MJHS6A.js.map +1 -0
- package/dist/chunk-O3KBDCEI.js +615 -0
- package/dist/chunk-O3KBDCEI.js.map +1 -0
- package/dist/chunk-OP53UBPN.js +19 -0
- package/dist/chunk-OP53UBPN.js.map +1 -0
- package/dist/chunk-P3JOTUAB.js +802 -0
- package/dist/chunk-P3JOTUAB.js.map +1 -0
- package/dist/chunk-QJP4GSJH.js +373 -0
- package/dist/chunk-QJP4GSJH.js.map +1 -0
- package/dist/chunk-RDDS7JQW.js +623 -0
- package/dist/chunk-RDDS7JQW.js.map +1 -0
- package/dist/chunk-TEH7V76G.js +209 -0
- package/dist/chunk-TEH7V76G.js.map +1 -0
- package/dist/chunk-UJ24XW52.js +20 -0
- package/dist/chunk-UJ24XW52.js.map +1 -0
- package/dist/chunk-UVZJL6QV.js +18 -0
- package/dist/chunk-UVZJL6QV.js.map +1 -0
- package/dist/chunk-XC4QNFSQ.js +238 -0
- package/dist/chunk-XC4QNFSQ.js.map +1 -0
- package/dist/chunk-YD5LVGWX.js +125 -0
- package/dist/chunk-YD5LVGWX.js.map +1 -0
- package/dist/chunk-YYGPIHHJ.js +166 -0
- package/dist/chunk-YYGPIHHJ.js.map +1 -0
- package/dist/connect-partial-sync.d.ts +41 -0
- package/dist/connect-partial-sync.js +6 -0
- package/dist/connect-partial-sync.js.map +1 -0
- package/dist/connect-sync.d.ts +26 -0
- package/dist/connect-sync.js +5 -0
- package/dist/connect-sync.js.map +1 -0
- package/dist/create-partial-synced-collection.d.ts +24 -0
- package/dist/create-partial-synced-collection.js +8 -0
- package/dist/create-partial-synced-collection.js.map +1 -0
- package/dist/create-synced-collection.d.ts +26 -0
- package/dist/create-synced-collection.js +8 -0
- package/dist/create-synced-collection.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/partial-sync-client-bridge.d.ts +157 -0
- package/dist/partial-sync-client-bridge.js +6 -0
- package/dist/partial-sync-client-bridge.js.map +1 -0
- package/dist/partial-sync-interest.d.ts +48 -0
- package/dist/partial-sync-interest.js +6 -0
- package/dist/partial-sync-interest.js.map +1 -0
- package/dist/partial-sync-mutation-handler.d.ts +31 -0
- package/dist/partial-sync-mutation-handler.js +6 -0
- package/dist/partial-sync-mutation-handler.js.map +1 -0
- package/dist/partial-sync-predicate-match.d.ts +8 -0
- package/dist/partial-sync-predicate-match.js +4 -0
- package/dist/partial-sync-predicate-match.js.map +1 -0
- package/dist/partial-sync-row-key.d.ts +41 -0
- package/dist/partial-sync-row-key.js +4 -0
- package/dist/partial-sync-row-key.js.map +1 -0
- package/dist/partial-sync-server-bridge.d.ts +102 -0
- package/dist/partial-sync-server-bridge.js +8 -0
- package/dist/partial-sync-server-bridge.js.map +1 -0
- package/dist/react/constants.d.ts +12 -0
- package/dist/react/constants.js +4 -0
- package/dist/react/constants.js.map +1 -0
- package/dist/react/index.d.ts +19 -0
- package/dist/react/index.js +17 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/partial-sync-adapter.d.ts +40 -0
- package/dist/react/partial-sync-adapter.js +4 -0
- package/dist/react/partial-sync-adapter.js.map +1 -0
- package/dist/react/partial-sync-utils.d.ts +42 -0
- package/dist/react/partial-sync-utils.js +5 -0
- package/dist/react/partial-sync-utils.js.map +1 -0
- package/dist/react/range-conditions-expression.d.ts +49 -0
- package/dist/react/range-conditions-expression.js +4 -0
- package/dist/react/range-conditions-expression.js.map +1 -0
- package/dist/react/types.d.ts +196 -0
- package/dist/react/types.js +3 -0
- package/dist/react/types.js.map +1 -0
- package/dist/react/usePartialSyncCollection.d.ts +20 -0
- package/dist/react/usePartialSyncCollection.js +10 -0
- package/dist/react/usePartialSyncCollection.js.map +1 -0
- package/dist/react/usePartialSyncViewport.d.ts +20 -0
- package/dist/react/usePartialSyncViewport.js +7 -0
- package/dist/react/usePartialSyncViewport.js.map +1 -0
- package/dist/react/usePartialSyncWindow.d.ts +17 -0
- package/dist/react/usePartialSyncWindow.js +12 -0
- package/dist/react/usePartialSyncWindow.js.map +1 -0
- package/dist/react/usePredicateFilteredRows.d.ts +20 -0
- package/dist/react/usePredicateFilteredRows.js +6 -0
- package/dist/react/usePredicateFilteredRows.js.map +1 -0
- package/dist/sync-client-bridge.d.ts +48 -0
- package/dist/sync-client-bridge.js +6 -0
- package/dist/sync-client-bridge.js.map +1 -0
- package/dist/sync-protocol.d.ts +378 -0
- package/dist/sync-protocol.js +4 -0
- package/dist/sync-protocol.js.map +1 -0
- package/dist/sync-server-bridge.d.ts +35 -0
- package/dist/sync-server-bridge.js +6 -0
- package/dist/sync-server-bridge.js.map +1 -0
- package/dist/with-sync.d.ts +107 -0
- package/dist/with-sync.js +7 -0
- package/dist/with-sync.js.map +1 -0
- package/package.json +27 -21
- package/src/connect-partial-sync.ts +16 -12
- package/src/connect-sync.ts +12 -10
|
@@ -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
|