@absolutejs/sync 0.1.0 → 0.2.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 +51 -34
- package/dist/adapters/drizzle/collection.d.ts +27 -0
- package/dist/adapters/drizzle/index.d.ts +3 -0
- package/dist/adapters/drizzle/index.js +139 -2
- package/dist/adapters/drizzle/index.js.map +5 -3
- package/dist/adapters/drizzle/predicate.d.ts +20 -0
- package/dist/angular/index.js +2 -2
- package/dist/angular/index.js.map +3 -3
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +357 -2
- package/dist/client/index.js.map +6 -4
- package/dist/client/presence.d.ts +37 -0
- package/dist/client/syncClient.d.ts +53 -0
- package/dist/engine/cluster.d.ts +41 -0
- package/dist/engine/connection.d.ts +33 -1
- package/dist/engine/index.d.ts +8 -2
- package/dist/engine/index.js +542 -37
- package/dist/engine/index.js.map +9 -6
- package/dist/engine/mutation.d.ts +39 -3
- package/dist/engine/presence.d.ts +46 -0
- package/dist/engine/reactive.d.ts +67 -0
- package/dist/engine/socket.d.ts +4 -1
- package/dist/engine/syncEngine.d.ts +33 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +175 -9
- package/dist/index.js.map +6 -5
- package/dist/react/index.js +2 -2
- package/dist/react/index.js.map +3 -3
- package/dist/svelte/index.js +2 -2
- package/dist/svelte/index.js.map +3 -3
- package/dist/vue/index.js +2 -2
- package/dist/vue/index.js.map +3 -3
- package/package.json +3 -1
package/dist/engine/index.js
CHANGED
|
@@ -614,12 +614,98 @@ var createPollingChangeSource = (options) => {
|
|
|
614
614
|
}
|
|
615
615
|
};
|
|
616
616
|
};
|
|
617
|
+
// src/engine/cluster.ts
|
|
618
|
+
var createInMemoryClusterBus = () => {
|
|
619
|
+
const listeners = new Set;
|
|
620
|
+
return {
|
|
621
|
+
publish: (message) => {
|
|
622
|
+
for (const listener of listeners) {
|
|
623
|
+
listener(message);
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
subscribe: (onMessage) => {
|
|
627
|
+
listeners.add(onMessage);
|
|
628
|
+
return () => {
|
|
629
|
+
listeners.delete(onMessage);
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
// src/engine/presence.ts
|
|
635
|
+
var createPresenceHub = () => {
|
|
636
|
+
const rooms = new Map;
|
|
637
|
+
const roomMembers = (room) => {
|
|
638
|
+
const members = rooms.get(room);
|
|
639
|
+
if (members === undefined) {
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
return [...members].map(([id, member]) => ({
|
|
643
|
+
id,
|
|
644
|
+
state: member.state
|
|
645
|
+
}));
|
|
646
|
+
};
|
|
647
|
+
const notify = (room, diff, exceptId) => {
|
|
648
|
+
const members = rooms.get(room);
|
|
649
|
+
if (members === undefined) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
for (const [id, member] of members) {
|
|
653
|
+
if (id !== exceptId) {
|
|
654
|
+
member.onDiff(diff);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
return {
|
|
659
|
+
join: (room, memberId, state, onDiff) => {
|
|
660
|
+
let members = rooms.get(room);
|
|
661
|
+
if (members === undefined) {
|
|
662
|
+
members = new Map;
|
|
663
|
+
rooms.set(room, members);
|
|
664
|
+
}
|
|
665
|
+
members.set(memberId, {
|
|
666
|
+
state,
|
|
667
|
+
onDiff
|
|
668
|
+
});
|
|
669
|
+
notify(room, { joined: [{ id: memberId, state }], updated: [], left: [] }, memberId);
|
|
670
|
+
const snapshot = roomMembers(room);
|
|
671
|
+
return {
|
|
672
|
+
members: snapshot,
|
|
673
|
+
set: (next) => {
|
|
674
|
+
const current = rooms.get(room)?.get(memberId);
|
|
675
|
+
if (current === undefined) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
current.state = next;
|
|
679
|
+
notify(room, {
|
|
680
|
+
joined: [],
|
|
681
|
+
updated: [{ id: memberId, state: next }],
|
|
682
|
+
left: []
|
|
683
|
+
}, memberId);
|
|
684
|
+
},
|
|
685
|
+
leave: () => {
|
|
686
|
+
const roomNow = rooms.get(room);
|
|
687
|
+
if (roomNow?.delete(memberId) !== true) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
notify(room, { joined: [], updated: [], left: [memberId] }, memberId);
|
|
691
|
+
if (roomNow.size === 0) {
|
|
692
|
+
rooms.delete(room);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
},
|
|
697
|
+
members: (room) => roomMembers(room),
|
|
698
|
+
count: (room) => rooms.get(room)?.size ?? 0
|
|
699
|
+
};
|
|
700
|
+
};
|
|
617
701
|
// src/engine/collection.ts
|
|
618
702
|
var defineCollection = (definition) => definition;
|
|
619
703
|
var defineJoinCollection = (definition) => ({
|
|
620
704
|
...definition,
|
|
621
705
|
kind: "join"
|
|
622
706
|
});
|
|
707
|
+
// src/engine/reactive.ts
|
|
708
|
+
var defineReactiveQuery = (definition) => ({ ...definition, kind: "reactive" });
|
|
623
709
|
// src/engine/graph.ts
|
|
624
710
|
var PLANS = new WeakMap;
|
|
625
711
|
var planTables = (plan) => {
|
|
@@ -807,14 +893,36 @@ class UnauthorizedError extends Error {
|
|
|
807
893
|
}
|
|
808
894
|
}
|
|
809
895
|
var defaultKey = (row) => row.id;
|
|
896
|
+
var shallowEqual4 = (a, b) => {
|
|
897
|
+
if (a === b) {
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
900
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
const aKeys = Object.keys(a);
|
|
904
|
+
const bKeys = Object.keys(b);
|
|
905
|
+
return aKeys.length === bKeys.length && aKeys.every((k) => a[k] === b[k]);
|
|
906
|
+
};
|
|
810
907
|
var createSyncEngine = (options = {}) => {
|
|
811
908
|
const registry = new Map;
|
|
812
909
|
const mutations = new Map;
|
|
910
|
+
const writers = new Map;
|
|
911
|
+
const readers = new Map;
|
|
912
|
+
const reactiveSubs = new Set;
|
|
813
913
|
const active = new Map;
|
|
814
914
|
const tableIndex = new Map;
|
|
815
915
|
const changeLogSize = options.changeLogSize ?? 1024;
|
|
816
916
|
const changeLog = [];
|
|
817
917
|
let version = 0;
|
|
918
|
+
const runInTransaction = options.transaction;
|
|
919
|
+
const instanceId = globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
|
|
920
|
+
let clusterBus;
|
|
921
|
+
const broadcast = (changes) => {
|
|
922
|
+
if (clusterBus !== undefined && changes.length > 0) {
|
|
923
|
+
clusterBus.publish({ changes, origin: instanceId });
|
|
924
|
+
}
|
|
925
|
+
};
|
|
818
926
|
const subsFor = (collection) => {
|
|
819
927
|
let set = active.get(collection);
|
|
820
928
|
if (set === undefined) {
|
|
@@ -832,52 +940,243 @@ var createSyncEngine = (options = {}) => {
|
|
|
832
940
|
set.add(name);
|
|
833
941
|
};
|
|
834
942
|
const sideChange = (change, match) => change.op !== "delete" && match !== undefined && !match(change.row) ? { op: "delete", row: change.row } : change;
|
|
835
|
-
const
|
|
836
|
-
|
|
943
|
+
const EMPTY_DIFF = {
|
|
944
|
+
added: [],
|
|
945
|
+
removed: [],
|
|
946
|
+
changed: []
|
|
947
|
+
};
|
|
948
|
+
const subscriptionDiff = async (subscription, table, change) => {
|
|
837
949
|
if (subscription.kind === "graph") {
|
|
838
|
-
|
|
839
|
-
}
|
|
950
|
+
return subscription.instance.applyChange(table, change);
|
|
951
|
+
}
|
|
952
|
+
if (subscription.kind === "join") {
|
|
840
953
|
const js = subscription.join;
|
|
841
954
|
if (table === js.leftTable) {
|
|
842
|
-
|
|
843
|
-
} else if (table === js.rightTable) {
|
|
844
|
-
diff = js.op.applyRight(sideChange(change, js.rightMatch));
|
|
845
|
-
} else {
|
|
846
|
-
return;
|
|
955
|
+
return js.op.applyLeft(sideChange(change, js.leftMatch));
|
|
847
956
|
}
|
|
848
|
-
|
|
957
|
+
if (table === js.rightTable) {
|
|
958
|
+
return js.op.applyRight(sideChange(change, js.rightMatch));
|
|
959
|
+
}
|
|
960
|
+
return EMPTY_DIFF;
|
|
961
|
+
}
|
|
962
|
+
if (subscription.kind === "reactive") {
|
|
963
|
+
return EMPTY_DIFF;
|
|
964
|
+
}
|
|
965
|
+
if (subscription.incremental) {
|
|
849
966
|
try {
|
|
850
|
-
|
|
967
|
+
return subscription.view.apply(change);
|
|
851
968
|
} catch {
|
|
852
|
-
|
|
969
|
+
return subscription.view.reset(await subscription.rehydrate());
|
|
853
970
|
}
|
|
854
|
-
} else {
|
|
855
|
-
diff = subscription.view.reset(await subscription.rehydrate());
|
|
856
971
|
}
|
|
857
|
-
|
|
858
|
-
|
|
972
|
+
return subscription.view.reset(await subscription.rehydrate());
|
|
973
|
+
};
|
|
974
|
+
const subscriptionsForTable = function* (table) {
|
|
975
|
+
const names = tableIndex.get(table);
|
|
976
|
+
if (names === undefined) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
for (const name of names) {
|
|
980
|
+
const set = active.get(name);
|
|
981
|
+
if (set === undefined) {
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
yield* set;
|
|
859
985
|
}
|
|
860
986
|
};
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
987
|
+
const mergeViewDiffs = (diffs, key) => {
|
|
988
|
+
const net = new Map;
|
|
989
|
+
for (const diff of diffs) {
|
|
990
|
+
for (const row of diff.removed) {
|
|
991
|
+
const previous = net.get(key(row));
|
|
992
|
+
if (previous?.state === "added") {
|
|
993
|
+
net.delete(key(row));
|
|
994
|
+
} else {
|
|
995
|
+
net.set(key(row), { state: "removed", row });
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
for (const row of diff.added) {
|
|
999
|
+
const previous = net.get(key(row));
|
|
1000
|
+
net.set(key(row), {
|
|
1001
|
+
state: previous?.state === "removed" ? "changed" : "added",
|
|
1002
|
+
row
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
for (const row of diff.changed) {
|
|
1006
|
+
const previous = net.get(key(row));
|
|
1007
|
+
net.set(key(row), {
|
|
1008
|
+
state: previous?.state === "added" ? "added" : "changed",
|
|
1009
|
+
row
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const added = [];
|
|
1014
|
+
const changed = [];
|
|
1015
|
+
const removed = [];
|
|
1016
|
+
for (const { state, row } of net.values()) {
|
|
1017
|
+
if (state === "added") {
|
|
1018
|
+
added.push(row);
|
|
1019
|
+
} else if (state === "changed") {
|
|
1020
|
+
changed.push(row);
|
|
1021
|
+
} else {
|
|
1022
|
+
removed.push(row);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return { added, changed, removed };
|
|
1026
|
+
};
|
|
1027
|
+
const depKey = (table, key) => `${table} ${key}`;
|
|
1028
|
+
const changedKeyFor = (table, change) => readers.get(table)?.key?.(change.row);
|
|
1029
|
+
const makeReadHandle = (ctx, readTables, readKeys, rangeDeps) => {
|
|
1030
|
+
const readerFor = (table) => {
|
|
1031
|
+
const reader = readers.get(table);
|
|
1032
|
+
if (reader === undefined) {
|
|
1033
|
+
throw new Error(`No reader registered for table "${table}" \u2014 register one with engine.registerReader`);
|
|
1034
|
+
}
|
|
1035
|
+
return reader;
|
|
1036
|
+
};
|
|
1037
|
+
return {
|
|
1038
|
+
all: async (table) => {
|
|
1039
|
+
readTables.add(table);
|
|
1040
|
+
return [...await readerFor(table).all(ctx)];
|
|
1041
|
+
},
|
|
1042
|
+
get: async (table, key) => {
|
|
1043
|
+
const reader = readerFor(table);
|
|
1044
|
+
if (reader.get === undefined) {
|
|
1045
|
+
throw new Error(`Reader for table "${table}" has no get(); use db.all() or add get`);
|
|
1046
|
+
}
|
|
1047
|
+
if (reader.key !== undefined) {
|
|
1048
|
+
readKeys.add(depKey(table, key));
|
|
1049
|
+
} else {
|
|
1050
|
+
readTables.add(table);
|
|
1051
|
+
}
|
|
1052
|
+
return await reader.get(key, ctx);
|
|
1053
|
+
},
|
|
1054
|
+
where: async (table, predicate) => {
|
|
1055
|
+
const reader = readerFor(table);
|
|
1056
|
+
const matched = [...await reader.all(ctx)].filter(predicate);
|
|
1057
|
+
if (reader.key !== undefined) {
|
|
1058
|
+
const key = reader.key;
|
|
1059
|
+
rangeDeps.push({
|
|
1060
|
+
table,
|
|
1061
|
+
predicate,
|
|
1062
|
+
keys: new Set(matched.map(key))
|
|
1063
|
+
});
|
|
1064
|
+
} else {
|
|
1065
|
+
readTables.add(table);
|
|
1066
|
+
}
|
|
1067
|
+
return matched;
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
};
|
|
1071
|
+
const diffRerun = (sub, rows) => {
|
|
1072
|
+
const next = new Map;
|
|
1073
|
+
for (const row of rows) {
|
|
1074
|
+
next.set(sub.key(row), row);
|
|
1075
|
+
}
|
|
1076
|
+
const added = [];
|
|
1077
|
+
const removed = [];
|
|
1078
|
+
const changed = [];
|
|
1079
|
+
for (const [rowKey, row] of next) {
|
|
1080
|
+
const previous = sub.current.get(rowKey);
|
|
1081
|
+
if (previous === undefined) {
|
|
1082
|
+
added.push(row);
|
|
1083
|
+
} else if (!shallowEqual4(previous, row)) {
|
|
1084
|
+
changed.push(row);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
for (const [rowKey, row] of sub.current) {
|
|
1088
|
+
if (!next.has(rowKey)) {
|
|
1089
|
+
removed.push(row);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
sub.current = next;
|
|
1093
|
+
return { added, removed, changed };
|
|
1094
|
+
};
|
|
1095
|
+
const inRange = (dep, change) => dep.table === change.table && (change.key !== undefined && dep.keys.has(change.key) || dep.predicate(change.row));
|
|
1096
|
+
const isReactiveAffected = (sub, changes) => changes.some((change) => sub.readTables.has(change.table) || change.key !== undefined && sub.readKeys.has(depKey(change.table, change.key)) || sub.rangeDeps.some((dep) => inRange(dep, change)));
|
|
1097
|
+
const reactivePairs = async (changes) => {
|
|
1098
|
+
const pairs = [];
|
|
1099
|
+
for (const sub of reactiveSubs) {
|
|
1100
|
+
if (!isReactiveAffected(sub, changes)) {
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
const { rows, readTables, readKeys, rangeDeps } = await sub.rerun();
|
|
1104
|
+
sub.readTables = readTables;
|
|
1105
|
+
sub.readKeys = readKeys;
|
|
1106
|
+
sub.rangeDeps = rangeDeps;
|
|
1107
|
+
const diff = diffRerun(sub, rows);
|
|
1108
|
+
if (!isEmptyViewDiff(diff)) {
|
|
1109
|
+
pairs.push([sub, diff]);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return pairs;
|
|
1113
|
+
};
|
|
1114
|
+
const logChange = (changeVersion, entry) => {
|
|
1115
|
+
changeLog.push(entry);
|
|
864
1116
|
if (changeLog.length > changeLogSize) {
|
|
865
1117
|
changeLog.shift();
|
|
866
1118
|
}
|
|
1119
|
+
};
|
|
1120
|
+
const applyChange = async (table, change, shouldBroadcast = true) => {
|
|
1121
|
+
version += 1;
|
|
867
1122
|
const changeVersion = version;
|
|
868
|
-
|
|
869
|
-
|
|
1123
|
+
logChange(changeVersion, { version: changeVersion, table, change });
|
|
1124
|
+
const emissions = [];
|
|
1125
|
+
for (const subscription of subscriptionsForTable(table)) {
|
|
1126
|
+
const diff = await subscriptionDiff(subscription, table, change);
|
|
1127
|
+
if (!isEmptyViewDiff(diff)) {
|
|
1128
|
+
emissions.push([subscription, diff]);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
emissions.push(...await reactivePairs([
|
|
1132
|
+
{ table, key: changedKeyFor(table, change), row: change.row }
|
|
1133
|
+
]));
|
|
1134
|
+
for (const [subscription, diff] of emissions) {
|
|
1135
|
+
subscription.onDiff(diff, changeVersion);
|
|
1136
|
+
}
|
|
1137
|
+
if (shouldBroadcast) {
|
|
1138
|
+
broadcast([{ table, change }]);
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
const applyChangeBatch = async (changes, shouldBroadcast = true) => {
|
|
1142
|
+
if (changes.length === 0) {
|
|
870
1143
|
return;
|
|
871
1144
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1145
|
+
version += 1;
|
|
1146
|
+
const batchVersion = version;
|
|
1147
|
+
const perSubscription = new Map;
|
|
1148
|
+
const reactiveChanges = [];
|
|
1149
|
+
for (const { table, change } of changes) {
|
|
1150
|
+
logChange(batchVersion, { version: batchVersion, table, change });
|
|
1151
|
+
reactiveChanges.push({
|
|
1152
|
+
table,
|
|
1153
|
+
key: changedKeyFor(table, change),
|
|
1154
|
+
row: change.row
|
|
1155
|
+
});
|
|
1156
|
+
for (const subscription of subscriptionsForTable(table)) {
|
|
1157
|
+
const diff = await subscriptionDiff(subscription, table, change);
|
|
1158
|
+
const list = perSubscription.get(subscription);
|
|
1159
|
+
if (list === undefined) {
|
|
1160
|
+
perSubscription.set(subscription, [diff]);
|
|
1161
|
+
} else {
|
|
1162
|
+
list.push(diff);
|
|
1163
|
+
}
|
|
876
1164
|
}
|
|
877
|
-
|
|
878
|
-
|
|
1165
|
+
}
|
|
1166
|
+
const emissions = [];
|
|
1167
|
+
for (const [subscription, diffs] of perSubscription) {
|
|
1168
|
+
const merged = diffs.length === 1 ? diffs[0] : mergeViewDiffs(diffs, subscription.key);
|
|
1169
|
+
if (!isEmptyViewDiff(merged)) {
|
|
1170
|
+
emissions.push([subscription, merged]);
|
|
879
1171
|
}
|
|
880
1172
|
}
|
|
1173
|
+
emissions.push(...await reactivePairs(reactiveChanges));
|
|
1174
|
+
for (const [subscription, diff] of emissions) {
|
|
1175
|
+
subscription.onDiff(diff, batchVersion);
|
|
1176
|
+
}
|
|
1177
|
+
if (shouldBroadcast) {
|
|
1178
|
+
broadcast(changes);
|
|
1179
|
+
}
|
|
881
1180
|
};
|
|
882
1181
|
const canResume = (since, incremental) => {
|
|
883
1182
|
if (!incremental) {
|
|
@@ -933,6 +1232,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
933
1232
|
leftMatch: left.match ? (row) => left.match(row, params, ctx) : undefined,
|
|
934
1233
|
rightMatch: right.match ? (row) => right.match(row, params, ctx) : undefined
|
|
935
1234
|
},
|
|
1235
|
+
key: definition.key,
|
|
936
1236
|
onDiff
|
|
937
1237
|
};
|
|
938
1238
|
set.add(subscription);
|
|
@@ -958,6 +1258,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
958
1258
|
kind: "graph",
|
|
959
1259
|
collection,
|
|
960
1260
|
instance,
|
|
1261
|
+
key: definition.key,
|
|
961
1262
|
onDiff
|
|
962
1263
|
};
|
|
963
1264
|
set.add(subscription);
|
|
@@ -969,6 +1270,49 @@ var createSyncEngine = (options = {}) => {
|
|
|
969
1270
|
}
|
|
970
1271
|
};
|
|
971
1272
|
};
|
|
1273
|
+
const subscribeReactive = async (collection, definition, params, ctx, onDiff, set) => {
|
|
1274
|
+
if (definition.authorize !== undefined) {
|
|
1275
|
+
const allowed = await definition.authorize(params, ctx);
|
|
1276
|
+
if (!allowed) {
|
|
1277
|
+
throw new UnauthorizedError(`subscribe to collection "${collection}"`);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
const rerun = async () => {
|
|
1281
|
+
const readTables = new Set;
|
|
1282
|
+
const readKeys = new Set;
|
|
1283
|
+
const rangeDeps = [];
|
|
1284
|
+
const db = makeReadHandle(ctx, readTables, readKeys, rangeDeps);
|
|
1285
|
+
const rows = [...await definition.run({ ctx, db, params })];
|
|
1286
|
+
return { rangeDeps, readKeys, readTables, rows };
|
|
1287
|
+
};
|
|
1288
|
+
const first = await rerun();
|
|
1289
|
+
const current = new Map;
|
|
1290
|
+
for (const row of first.rows) {
|
|
1291
|
+
current.set(definition.key(row), row);
|
|
1292
|
+
}
|
|
1293
|
+
const atVersion = version;
|
|
1294
|
+
const subscription = {
|
|
1295
|
+
kind: "reactive",
|
|
1296
|
+
collection,
|
|
1297
|
+
key: definition.key,
|
|
1298
|
+
rerun,
|
|
1299
|
+
current,
|
|
1300
|
+
readTables: first.readTables,
|
|
1301
|
+
readKeys: first.readKeys,
|
|
1302
|
+
rangeDeps: first.rangeDeps,
|
|
1303
|
+
onDiff
|
|
1304
|
+
};
|
|
1305
|
+
set.add(subscription);
|
|
1306
|
+
reactiveSubs.add(subscription);
|
|
1307
|
+
return {
|
|
1308
|
+
initial: first.rows,
|
|
1309
|
+
version: atVersion,
|
|
1310
|
+
unsubscribe: () => {
|
|
1311
|
+
set.delete(subscription);
|
|
1312
|
+
reactiveSubs.delete(subscription);
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
};
|
|
972
1316
|
return {
|
|
973
1317
|
register: (collection) => {
|
|
974
1318
|
registry.set(collection.name, collection);
|
|
@@ -1003,6 +1347,10 @@ var createSyncEngine = (options = {}) => {
|
|
|
1003
1347
|
const graphed = await subscribeGraph(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1004
1348
|
return graphed;
|
|
1005
1349
|
}
|
|
1350
|
+
if (registeredKind === "reactive") {
|
|
1351
|
+
const reactived = await subscribeReactive(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1352
|
+
return reactived;
|
|
1353
|
+
}
|
|
1006
1354
|
const definition = registered;
|
|
1007
1355
|
if (definition.authorize !== undefined) {
|
|
1008
1356
|
const allowed = await definition.authorize(params, ctx);
|
|
@@ -1029,6 +1377,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
1029
1377
|
view,
|
|
1030
1378
|
incremental,
|
|
1031
1379
|
rehydrate,
|
|
1380
|
+
key,
|
|
1032
1381
|
onDiff: typedOnDiff
|
|
1033
1382
|
};
|
|
1034
1383
|
subscribeSet.add(subscription);
|
|
@@ -1069,6 +1418,19 @@ var createSyncEngine = (options = {}) => {
|
|
|
1069
1418
|
await source.stop();
|
|
1070
1419
|
};
|
|
1071
1420
|
},
|
|
1421
|
+
connectCluster: async (bus) => {
|
|
1422
|
+
const unsubscribe = await bus.subscribe((message) => {
|
|
1423
|
+
if (message.origin === instanceId) {
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
applyChangeBatch(message.changes, false);
|
|
1427
|
+
});
|
|
1428
|
+
clusterBus = bus;
|
|
1429
|
+
return async () => {
|
|
1430
|
+
clusterBus = undefined;
|
|
1431
|
+
await unsubscribe();
|
|
1432
|
+
};
|
|
1433
|
+
},
|
|
1072
1434
|
subscriptionCount: (collection) => {
|
|
1073
1435
|
if (collection !== undefined) {
|
|
1074
1436
|
return active.get(collection)?.size ?? 0;
|
|
@@ -1082,6 +1444,15 @@ var createSyncEngine = (options = {}) => {
|
|
|
1082
1444
|
registerMutation: (mutation) => {
|
|
1083
1445
|
mutations.set(mutation.name, mutation);
|
|
1084
1446
|
},
|
|
1447
|
+
registerWriter: (table, writer) => {
|
|
1448
|
+
writers.set(table, writer);
|
|
1449
|
+
},
|
|
1450
|
+
registerReactive: (query2) => {
|
|
1451
|
+
registry.set(query2.name, query2);
|
|
1452
|
+
},
|
|
1453
|
+
registerReader: (table, reader) => {
|
|
1454
|
+
readers.set(table, reader);
|
|
1455
|
+
},
|
|
1085
1456
|
runMutation: async (name, args, ctx) => {
|
|
1086
1457
|
const mutation = mutations.get(name);
|
|
1087
1458
|
if (mutation === undefined) {
|
|
@@ -1093,9 +1464,44 @@ var createSyncEngine = (options = {}) => {
|
|
|
1093
1464
|
throw new UnauthorizedError(`run mutation "${name}"`);
|
|
1094
1465
|
}
|
|
1095
1466
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1467
|
+
const writerFor = (table) => {
|
|
1468
|
+
const writer = writers.get(table);
|
|
1469
|
+
if (writer === undefined) {
|
|
1470
|
+
throw new Error(`No writer registered for table "${table}" \u2014 register one with engine.registerWriter, or use actions.change`);
|
|
1471
|
+
}
|
|
1472
|
+
return writer;
|
|
1473
|
+
};
|
|
1474
|
+
const runHandler = async (tx) => {
|
|
1475
|
+
const buffered2 = [];
|
|
1476
|
+
const actions = {
|
|
1477
|
+
change: (collection, change) => {
|
|
1478
|
+
buffered2.push({
|
|
1479
|
+
table: collection,
|
|
1480
|
+
change
|
|
1481
|
+
});
|
|
1482
|
+
return Promise.resolve();
|
|
1483
|
+
},
|
|
1484
|
+
insert: async (table, data) => {
|
|
1485
|
+
const row = await writerFor(table).insert(data, ctx, tx);
|
|
1486
|
+
buffered2.push({ table, change: { op: "insert", row } });
|
|
1487
|
+
return row;
|
|
1488
|
+
},
|
|
1489
|
+
update: async (table, data) => {
|
|
1490
|
+
const row = await writerFor(table).update(data, ctx, tx);
|
|
1491
|
+
buffered2.push({ table, change: { op: "update", row } });
|
|
1492
|
+
return row;
|
|
1493
|
+
},
|
|
1494
|
+
delete: async (table, row) => {
|
|
1495
|
+
await writerFor(table).delete(row, ctx, tx);
|
|
1496
|
+
buffered2.push({ table, change: { op: "delete", row } });
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
const handlerResult = await mutation.handler(args, ctx, actions);
|
|
1500
|
+
return { buffered: buffered2, result: handlerResult };
|
|
1501
|
+
};
|
|
1502
|
+
const { buffered, result } = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
|
|
1503
|
+
await applyChangeBatch(buffered);
|
|
1504
|
+
return result;
|
|
1099
1505
|
}
|
|
1100
1506
|
};
|
|
1101
1507
|
};
|
|
@@ -1147,14 +1553,73 @@ var parseFrame = (raw) => {
|
|
|
1147
1553
|
args: frame.args
|
|
1148
1554
|
} : undefined;
|
|
1149
1555
|
}
|
|
1556
|
+
if (frame.type === "presence-join") {
|
|
1557
|
+
return typeof frame.room === "string" && typeof frame.memberId === "string" ? {
|
|
1558
|
+
type: "presence-join",
|
|
1559
|
+
room: frame.room,
|
|
1560
|
+
memberId: frame.memberId,
|
|
1561
|
+
state: frame.state
|
|
1562
|
+
} : undefined;
|
|
1563
|
+
}
|
|
1564
|
+
if (frame.type === "presence-set") {
|
|
1565
|
+
return typeof frame.room === "string" ? { type: "presence-set", room: frame.room, state: frame.state } : undefined;
|
|
1566
|
+
}
|
|
1567
|
+
if (frame.type === "presence-leave") {
|
|
1568
|
+
return typeof frame.room === "string" ? { type: "presence-leave", room: frame.room } : undefined;
|
|
1569
|
+
}
|
|
1150
1570
|
return;
|
|
1151
1571
|
};
|
|
1152
1572
|
var createSyncConnection = ({
|
|
1153
1573
|
engine,
|
|
1154
1574
|
ctx,
|
|
1155
|
-
send
|
|
1575
|
+
send,
|
|
1576
|
+
presence
|
|
1156
1577
|
}) => {
|
|
1157
1578
|
const subscriptions = new Map;
|
|
1579
|
+
const presenceRooms = new Map;
|
|
1580
|
+
let pending = [];
|
|
1581
|
+
let pendingVersion;
|
|
1582
|
+
let flushScheduled = false;
|
|
1583
|
+
const flush = () => {
|
|
1584
|
+
if (pending.length === 0) {
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
const diffs = pending;
|
|
1588
|
+
const version = pendingVersion;
|
|
1589
|
+
pending = [];
|
|
1590
|
+
pendingVersion = undefined;
|
|
1591
|
+
if (diffs.length === 1) {
|
|
1592
|
+
const only = diffs[0];
|
|
1593
|
+
send({
|
|
1594
|
+
type: "diff",
|
|
1595
|
+
id: only.id,
|
|
1596
|
+
added: only.added,
|
|
1597
|
+
removed: only.removed,
|
|
1598
|
+
changed: only.changed,
|
|
1599
|
+
version
|
|
1600
|
+
});
|
|
1601
|
+
} else {
|
|
1602
|
+
send({ type: "frame", diffs, version });
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
const scheduleFlush = () => {
|
|
1606
|
+
if (flushScheduled) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
flushScheduled = true;
|
|
1610
|
+
queueMicrotask(() => {
|
|
1611
|
+
flushScheduled = false;
|
|
1612
|
+
flush();
|
|
1613
|
+
});
|
|
1614
|
+
};
|
|
1615
|
+
const bufferDiff = (diff, diffVersion) => {
|
|
1616
|
+
if (pending.length > 0 && pendingVersion !== diffVersion) {
|
|
1617
|
+
flush();
|
|
1618
|
+
}
|
|
1619
|
+
pending.push(diff);
|
|
1620
|
+
pendingVersion = diffVersion;
|
|
1621
|
+
scheduleFlush();
|
|
1622
|
+
};
|
|
1158
1623
|
const handle = async (raw) => {
|
|
1159
1624
|
const frame = parseFrame(raw);
|
|
1160
1625
|
if (frame === undefined) {
|
|
@@ -1164,6 +1629,7 @@ var createSyncConnection = ({
|
|
|
1164
1629
|
if (frame.type === "mutate") {
|
|
1165
1630
|
try {
|
|
1166
1631
|
const result = await engine.runMutation(frame.name, frame.args, ctx);
|
|
1632
|
+
flush();
|
|
1167
1633
|
send({ type: "ack", mutationId: frame.mutationId, result });
|
|
1168
1634
|
} catch (error) {
|
|
1169
1635
|
send({
|
|
@@ -1179,6 +1645,40 @@ var createSyncConnection = ({
|
|
|
1179
1645
|
subscriptions.delete(frame.id);
|
|
1180
1646
|
return;
|
|
1181
1647
|
}
|
|
1648
|
+
if (frame.type === "presence-join") {
|
|
1649
|
+
if (presence === undefined) {
|
|
1650
|
+
send({ type: "error", message: "Presence is not enabled" });
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
presenceRooms.get(frame.room)?.leave();
|
|
1654
|
+
const handle2 = presence.join(frame.room, frame.memberId, frame.state, (diff) => {
|
|
1655
|
+
send({
|
|
1656
|
+
type: "presence",
|
|
1657
|
+
room: frame.room,
|
|
1658
|
+
joined: diff.joined,
|
|
1659
|
+
updated: diff.updated,
|
|
1660
|
+
left: diff.left
|
|
1661
|
+
});
|
|
1662
|
+
});
|
|
1663
|
+
presenceRooms.set(frame.room, handle2);
|
|
1664
|
+
send({
|
|
1665
|
+
type: "presence",
|
|
1666
|
+
room: frame.room,
|
|
1667
|
+
joined: handle2.members,
|
|
1668
|
+
updated: [],
|
|
1669
|
+
left: []
|
|
1670
|
+
});
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
if (frame.type === "presence-set") {
|
|
1674
|
+
presenceRooms.get(frame.room)?.set(frame.state);
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
if (frame.type === "presence-leave") {
|
|
1678
|
+
presenceRooms.get(frame.room)?.leave();
|
|
1679
|
+
presenceRooms.delete(frame.room);
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1182
1682
|
if (subscriptions.has(frame.id)) {
|
|
1183
1683
|
send({
|
|
1184
1684
|
type: "error",
|
|
@@ -1194,14 +1694,12 @@ var createSyncConnection = ({
|
|
|
1194
1694
|
ctx,
|
|
1195
1695
|
since: frame.since,
|
|
1196
1696
|
onDiff: (diff, diffVersion) => {
|
|
1197
|
-
|
|
1198
|
-
type: "diff",
|
|
1697
|
+
bufferDiff({
|
|
1199
1698
|
id: frame.id,
|
|
1200
1699
|
added: diff.added,
|
|
1201
1700
|
removed: diff.removed,
|
|
1202
|
-
changed: diff.changed
|
|
1203
|
-
|
|
1204
|
-
});
|
|
1701
|
+
changed: diff.changed
|
|
1702
|
+
}, diffVersion);
|
|
1205
1703
|
}
|
|
1206
1704
|
});
|
|
1207
1705
|
subscriptions.set(frame.id, subscription);
|
|
@@ -1235,6 +1733,10 @@ var createSyncConnection = ({
|
|
|
1235
1733
|
subscription.unsubscribe();
|
|
1236
1734
|
}
|
|
1237
1735
|
subscriptions.clear();
|
|
1736
|
+
for (const handle2 of presenceRooms.values()) {
|
|
1737
|
+
handle2.leave();
|
|
1738
|
+
}
|
|
1739
|
+
presenceRooms.clear();
|
|
1238
1740
|
};
|
|
1239
1741
|
return { handle, close };
|
|
1240
1742
|
};
|
|
@@ -1250,14 +1752,17 @@ export {
|
|
|
1250
1752
|
hydrateRoute,
|
|
1251
1753
|
fromRowChange,
|
|
1252
1754
|
filterOp,
|
|
1755
|
+
defineReactiveQuery,
|
|
1253
1756
|
defineMutation,
|
|
1254
1757
|
defineJoinCollection,
|
|
1255
1758
|
defineGraphCollection,
|
|
1256
1759
|
defineCollection,
|
|
1257
1760
|
createSyncEngine,
|
|
1258
1761
|
createSyncConnection,
|
|
1762
|
+
createPresenceHub,
|
|
1259
1763
|
createPollingChangeSource,
|
|
1260
1764
|
createMaterializedView,
|
|
1765
|
+
createInMemoryClusterBus,
|
|
1261
1766
|
createEquiJoin,
|
|
1262
1767
|
createAggregate,
|
|
1263
1768
|
chain,
|
|
@@ -1265,5 +1770,5 @@ export {
|
|
|
1265
1770
|
UnauthorizedError
|
|
1266
1771
|
};
|
|
1267
1772
|
|
|
1268
|
-
//# debugId=
|
|
1773
|
+
//# debugId=4ADB152722C87EA464756E2164756E21
|
|
1269
1774
|
//# sourceMappingURL=index.js.map
|