@absolutejs/sync 0.4.0 → 0.6.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 +86 -24
- package/dist/engine/index.d.ts +8 -0
- package/dist/engine/index.js +361 -66
- package/dist/engine/index.js.map +8 -4
- package/dist/engine/schedule.d.ts +39 -0
- package/dist/engine/search.d.ts +61 -0
- package/dist/engine/syncEngine.d.ts +22 -0
- package/dist/engine/textIndex.d.ts +33 -0
- package/dist/engine/vectorIndex.d.ts +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +28 -1
- package/dist/index.js.map +4 -3
- package/dist/scheduled.d.ts +47 -0
- package/package.json +6 -1
package/dist/engine/index.js
CHANGED
|
@@ -885,6 +885,158 @@ var query = (source) => makeQuery(source, []);
|
|
|
885
885
|
var defineGraphCollection = (definition) => ({ ...definition, kind: "graph" });
|
|
886
886
|
// src/engine/permissions.ts
|
|
887
887
|
var definePermissions = (permissions) => permissions;
|
|
888
|
+
// src/engine/search.ts
|
|
889
|
+
var SEARCH_SCORE_FIELD = "_score";
|
|
890
|
+
var defineSearchCollection = (definition) => ({
|
|
891
|
+
...definition,
|
|
892
|
+
kind: "search"
|
|
893
|
+
});
|
|
894
|
+
// src/engine/textIndex.ts
|
|
895
|
+
var defaultTokenize = (text) => text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
896
|
+
var createTextIndex = (options) => {
|
|
897
|
+
const { key, fields } = options;
|
|
898
|
+
const tokenize = options.tokenize ?? defaultTokenize;
|
|
899
|
+
const stopwords = new Set(options.stopwords ?? []);
|
|
900
|
+
const k1 = options.k1 ?? 1.5;
|
|
901
|
+
const b = options.b ?? 0.75;
|
|
902
|
+
const docs = new Map;
|
|
903
|
+
const postings = new Map;
|
|
904
|
+
let totalLen = 0;
|
|
905
|
+
const termsOf = (row) => {
|
|
906
|
+
const text = fields.map((field) => {
|
|
907
|
+
const value = row[field];
|
|
908
|
+
return value === undefined || value === null ? "" : String(value);
|
|
909
|
+
}).join(" ");
|
|
910
|
+
return tokenize(text).filter((term) => !stopwords.has(term));
|
|
911
|
+
};
|
|
912
|
+
const remove = (rowKey) => {
|
|
913
|
+
const doc = docs.get(rowKey);
|
|
914
|
+
if (doc === undefined) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
for (const term of doc.tf.keys()) {
|
|
918
|
+
const set = postings.get(term);
|
|
919
|
+
if (set !== undefined) {
|
|
920
|
+
set.delete(rowKey);
|
|
921
|
+
if (set.size === 0) {
|
|
922
|
+
postings.delete(term);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
totalLen -= doc.len;
|
|
927
|
+
docs.delete(rowKey);
|
|
928
|
+
};
|
|
929
|
+
const add = (row) => {
|
|
930
|
+
const rowKey = key(row);
|
|
931
|
+
remove(rowKey);
|
|
932
|
+
const terms = termsOf(row);
|
|
933
|
+
const tf = new Map;
|
|
934
|
+
for (const term of terms) {
|
|
935
|
+
tf.set(term, (tf.get(term) ?? 0) + 1);
|
|
936
|
+
}
|
|
937
|
+
for (const term of tf.keys()) {
|
|
938
|
+
let set = postings.get(term);
|
|
939
|
+
if (set === undefined) {
|
|
940
|
+
set = new Set;
|
|
941
|
+
postings.set(term, set);
|
|
942
|
+
}
|
|
943
|
+
set.add(rowKey);
|
|
944
|
+
}
|
|
945
|
+
docs.set(rowKey, { row, len: terms.length, tf });
|
|
946
|
+
totalLen += terms.length;
|
|
947
|
+
};
|
|
948
|
+
const search = (query2, limit) => {
|
|
949
|
+
const total = docs.size;
|
|
950
|
+
if (total === 0) {
|
|
951
|
+
return [];
|
|
952
|
+
}
|
|
953
|
+
const avgdl = totalLen / total;
|
|
954
|
+
const queryTerms = new Set(tokenize(query2).filter((term) => !stopwords.has(term)));
|
|
955
|
+
const scores = new Map;
|
|
956
|
+
for (const term of queryTerms) {
|
|
957
|
+
const set = postings.get(term);
|
|
958
|
+
if (set === undefined) {
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
const df = set.size;
|
|
962
|
+
const idf = Math.log(1 + (total - df + 0.5) / (df + 0.5));
|
|
963
|
+
for (const rowKey of set) {
|
|
964
|
+
const doc = docs.get(rowKey);
|
|
965
|
+
const freq = doc.tf.get(term) ?? 0;
|
|
966
|
+
const norm = freq * (k1 + 1) / (freq + k1 * (1 - b + b * doc.len / avgdl));
|
|
967
|
+
scores.set(rowKey, (scores.get(rowKey) ?? 0) + idf * norm);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return [...scores.entries()].map(([rowKey, score]) => ({ row: docs.get(rowKey).row, score })).sort((first, second) => second.score - first.score).slice(0, limit);
|
|
971
|
+
};
|
|
972
|
+
return {
|
|
973
|
+
add,
|
|
974
|
+
remove,
|
|
975
|
+
search,
|
|
976
|
+
size: () => docs.size,
|
|
977
|
+
clear: () => {
|
|
978
|
+
docs.clear();
|
|
979
|
+
postings.clear();
|
|
980
|
+
totalLen = 0;
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
};
|
|
984
|
+
// src/engine/vectorIndex.ts
|
|
985
|
+
var dot = (first, second) => {
|
|
986
|
+
const length = Math.min(first.length, second.length);
|
|
987
|
+
let sum = 0;
|
|
988
|
+
for (let index = 0;index < length; index += 1) {
|
|
989
|
+
sum += first[index] * second[index];
|
|
990
|
+
}
|
|
991
|
+
return sum;
|
|
992
|
+
};
|
|
993
|
+
var normOf = (vec) => Math.sqrt(dot(vec, vec));
|
|
994
|
+
var euclidean = (first, second) => {
|
|
995
|
+
const length = Math.max(first.length, second.length);
|
|
996
|
+
let sum = 0;
|
|
997
|
+
for (let index = 0;index < length; index += 1) {
|
|
998
|
+
const delta = (first[index] ?? 0) - (second[index] ?? 0);
|
|
999
|
+
sum += delta * delta;
|
|
1000
|
+
}
|
|
1001
|
+
return Math.sqrt(sum);
|
|
1002
|
+
};
|
|
1003
|
+
var createVectorIndex = (options) => {
|
|
1004
|
+
const { key, embedding } = options;
|
|
1005
|
+
const metric = options.metric ?? "cosine";
|
|
1006
|
+
const entries = new Map;
|
|
1007
|
+
const score = (query2, queryNorm, entry) => {
|
|
1008
|
+
if (metric === "dot") {
|
|
1009
|
+
return dot(query2, entry.vec);
|
|
1010
|
+
}
|
|
1011
|
+
if (metric === "euclidean") {
|
|
1012
|
+
return -euclidean(query2, entry.vec);
|
|
1013
|
+
}
|
|
1014
|
+
const denominator = queryNorm * entry.norm;
|
|
1015
|
+
return denominator === 0 ? 0 : dot(query2, entry.vec) / denominator;
|
|
1016
|
+
};
|
|
1017
|
+
return {
|
|
1018
|
+
add: (row) => {
|
|
1019
|
+
const vec = embedding(row);
|
|
1020
|
+
entries.set(key(row), { row, vec, norm: normOf(vec) });
|
|
1021
|
+
},
|
|
1022
|
+
remove: (rowKey) => {
|
|
1023
|
+
entries.delete(rowKey);
|
|
1024
|
+
},
|
|
1025
|
+
search: (query2, limit) => {
|
|
1026
|
+
const queryNorm = normOf(query2);
|
|
1027
|
+
return [...entries.values()].map((entry) => ({
|
|
1028
|
+
row: entry.row,
|
|
1029
|
+
score: score(query2, queryNorm, entry)
|
|
1030
|
+
})).sort((first, second) => second.score - first.score).slice(0, limit);
|
|
1031
|
+
},
|
|
1032
|
+
size: () => entries.size,
|
|
1033
|
+
clear: () => {
|
|
1034
|
+
entries.clear();
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
};
|
|
1038
|
+
// src/engine/schedule.ts
|
|
1039
|
+
var defineSchedule = (definition) => definition;
|
|
888
1040
|
// src/engine/mutation.ts
|
|
889
1041
|
var defineMutation = (definition) => definition;
|
|
890
1042
|
// src/engine/syncEngine.ts
|
|
@@ -906,11 +1058,21 @@ var shallowEqual4 = (a, b) => {
|
|
|
906
1058
|
const bKeys = Object.keys(b);
|
|
907
1059
|
return aKeys.length === bKeys.length && aKeys.every((k) => a[k] === b[k]);
|
|
908
1060
|
};
|
|
1061
|
+
var equalsIgnoringScore = (a, b) => {
|
|
1062
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
|
|
1063
|
+
return a === b;
|
|
1064
|
+
}
|
|
1065
|
+
const strip = (value) => Object.keys(value).filter((k) => k !== SEARCH_SCORE_FIELD);
|
|
1066
|
+
const aKeys = strip(a);
|
|
1067
|
+
const bKeys = strip(b);
|
|
1068
|
+
return aKeys.length === bKeys.length && aKeys.every((k) => a[k] === b[k]);
|
|
1069
|
+
};
|
|
909
1070
|
var createSyncEngine = (options = {}) => {
|
|
910
1071
|
const registry = new Map;
|
|
911
1072
|
const mutations = new Map;
|
|
912
1073
|
const writers = new Map;
|
|
913
1074
|
const readers = new Map;
|
|
1075
|
+
const schedules = new Map;
|
|
914
1076
|
const permissions = new Map;
|
|
915
1077
|
for (const [table, rules] of Object.entries(options.permissions ?? {})) {
|
|
916
1078
|
permissions.set(table, rules);
|
|
@@ -921,6 +1083,8 @@ var createSyncEngine = (options = {}) => {
|
|
|
921
1083
|
return rules?.[op] ?? rules?.write;
|
|
922
1084
|
};
|
|
923
1085
|
const reactiveSubs = new Set;
|
|
1086
|
+
const searchSubs = new Set;
|
|
1087
|
+
const searchIndexes = new Map;
|
|
924
1088
|
const active = new Map;
|
|
925
1089
|
const tableIndex = new Map;
|
|
926
1090
|
const changeLogSize = options.changeLogSize ?? 1024;
|
|
@@ -973,6 +1137,9 @@ var createSyncEngine = (options = {}) => {
|
|
|
973
1137
|
if (subscription.kind === "reactive") {
|
|
974
1138
|
return EMPTY_DIFF;
|
|
975
1139
|
}
|
|
1140
|
+
if (subscription.kind === "search") {
|
|
1141
|
+
return EMPTY_DIFF;
|
|
1142
|
+
}
|
|
976
1143
|
if (subscription.incremental) {
|
|
977
1144
|
try {
|
|
978
1145
|
return subscription.view.apply(change);
|
|
@@ -1037,7 +1204,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
1037
1204
|
};
|
|
1038
1205
|
const depKey = (table, key) => `${table} ${key}`;
|
|
1039
1206
|
const changedKeyFor = (table, change) => readers.get(table)?.key?.(change.row);
|
|
1040
|
-
const makeReadHandle = (ctx, readTables, readKeys, rangeDeps) => {
|
|
1207
|
+
const makeReadHandle = (ctx, readTables, readKeys, rangeDeps, applyRules = true) => {
|
|
1041
1208
|
const readerFor = (table) => {
|
|
1042
1209
|
const reader = readers.get(table);
|
|
1043
1210
|
if (reader === undefined) {
|
|
@@ -1045,11 +1212,12 @@ var createSyncEngine = (options = {}) => {
|
|
|
1045
1212
|
}
|
|
1046
1213
|
return reader;
|
|
1047
1214
|
};
|
|
1215
|
+
const ruleFor = (table) => applyRules ? readRuleFor(table) : undefined;
|
|
1048
1216
|
return {
|
|
1049
1217
|
all: async (table) => {
|
|
1050
1218
|
readTables.add(table);
|
|
1051
1219
|
const rows = [...await readerFor(table).all(ctx)];
|
|
1052
|
-
const rule =
|
|
1220
|
+
const rule = ruleFor(table);
|
|
1053
1221
|
return rule ? rows.filter((row) => rule(ctx, row)) : rows;
|
|
1054
1222
|
},
|
|
1055
1223
|
get: async (table, key) => {
|
|
@@ -1063,12 +1231,12 @@ var createSyncEngine = (options = {}) => {
|
|
|
1063
1231
|
readTables.add(table);
|
|
1064
1232
|
}
|
|
1065
1233
|
const row = await reader.get(key, ctx);
|
|
1066
|
-
const rule =
|
|
1234
|
+
const rule = ruleFor(table);
|
|
1067
1235
|
return rule && row !== undefined && !rule(ctx, row) ? undefined : row;
|
|
1068
1236
|
},
|
|
1069
1237
|
where: async (table, predicate) => {
|
|
1070
1238
|
const reader = readerFor(table);
|
|
1071
|
-
const rule =
|
|
1239
|
+
const rule = ruleFor(table);
|
|
1072
1240
|
const effective = rule ? (row) => predicate(row) && rule(ctx, row) : predicate;
|
|
1073
1241
|
const matched = [...await reader.all(ctx)].filter(effective);
|
|
1074
1242
|
if (reader.key !== undefined) {
|
|
@@ -1085,7 +1253,72 @@ var createSyncEngine = (options = {}) => {
|
|
|
1085
1253
|
}
|
|
1086
1254
|
};
|
|
1087
1255
|
};
|
|
1088
|
-
const
|
|
1256
|
+
const writerFor = (table) => {
|
|
1257
|
+
const writer = writers.get(table);
|
|
1258
|
+
if (writer === undefined) {
|
|
1259
|
+
throw new Error(`No writer registered for table "${table}" \u2014 register one with engine.registerWriter, or use actions.change`);
|
|
1260
|
+
}
|
|
1261
|
+
return writer;
|
|
1262
|
+
};
|
|
1263
|
+
const authorizeWrite = async (table, op, value, ctx) => {
|
|
1264
|
+
const rule = writeRuleFor(table, op);
|
|
1265
|
+
if (rule === undefined) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
let subject = value;
|
|
1269
|
+
if (op !== "insert") {
|
|
1270
|
+
const reader = readers.get(table);
|
|
1271
|
+
if (reader?.get !== undefined) {
|
|
1272
|
+
const id = reader.key ? reader.key(value) : value.id;
|
|
1273
|
+
if (id !== undefined) {
|
|
1274
|
+
const existing = await reader.get(id, ctx);
|
|
1275
|
+
if (existing !== undefined) {
|
|
1276
|
+
subject = existing;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
if (!rule(ctx, subject)) {
|
|
1282
|
+
throw new UnauthorizedError(`${op} on table "${table}"`);
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
const makeActions = (tx, ctx, enforce) => {
|
|
1286
|
+
const buffered = [];
|
|
1287
|
+
const actions = {
|
|
1288
|
+
change: (collection, change) => {
|
|
1289
|
+
buffered.push({
|
|
1290
|
+
table: collection,
|
|
1291
|
+
change
|
|
1292
|
+
});
|
|
1293
|
+
return Promise.resolve();
|
|
1294
|
+
},
|
|
1295
|
+
insert: async (table, data) => {
|
|
1296
|
+
if (enforce) {
|
|
1297
|
+
await authorizeWrite(table, "insert", data, ctx);
|
|
1298
|
+
}
|
|
1299
|
+
const row = await writerFor(table).insert(data, ctx, tx);
|
|
1300
|
+
buffered.push({ table, change: { op: "insert", row } });
|
|
1301
|
+
return row;
|
|
1302
|
+
},
|
|
1303
|
+
update: async (table, data) => {
|
|
1304
|
+
if (enforce) {
|
|
1305
|
+
await authorizeWrite(table, "update", data, ctx);
|
|
1306
|
+
}
|
|
1307
|
+
const row = await writerFor(table).update(data, ctx, tx);
|
|
1308
|
+
buffered.push({ table, change: { op: "update", row } });
|
|
1309
|
+
return row;
|
|
1310
|
+
},
|
|
1311
|
+
delete: async (table, row) => {
|
|
1312
|
+
if (enforce) {
|
|
1313
|
+
await authorizeWrite(table, "delete", row, ctx);
|
|
1314
|
+
}
|
|
1315
|
+
await writerFor(table).delete(row, ctx, tx);
|
|
1316
|
+
buffered.push({ table, change: { op: "delete", row } });
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
return { actions, buffered };
|
|
1320
|
+
};
|
|
1321
|
+
const diffRerun = (sub, rows, equals = shallowEqual4) => {
|
|
1089
1322
|
const next = new Map;
|
|
1090
1323
|
for (const row of rows) {
|
|
1091
1324
|
next.set(sub.key(row), row);
|
|
@@ -1097,7 +1330,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
1097
1330
|
const previous = sub.current.get(rowKey);
|
|
1098
1331
|
if (previous === undefined) {
|
|
1099
1332
|
added.push(row);
|
|
1100
|
-
} else if (!
|
|
1333
|
+
} else if (!equals(previous, row)) {
|
|
1101
1334
|
changed.push(row);
|
|
1102
1335
|
}
|
|
1103
1336
|
}
|
|
@@ -1128,6 +1361,47 @@ var createSyncEngine = (options = {}) => {
|
|
|
1128
1361
|
}
|
|
1129
1362
|
return pairs;
|
|
1130
1363
|
};
|
|
1364
|
+
const ensureSearchIndex = async (definition) => {
|
|
1365
|
+
let entry = searchIndexes.get(definition.name);
|
|
1366
|
+
if (entry === undefined) {
|
|
1367
|
+
entry = { index: definition.index(), definition, hydrated: false };
|
|
1368
|
+
searchIndexes.set(definition.name, entry);
|
|
1369
|
+
}
|
|
1370
|
+
if (!entry.hydrated) {
|
|
1371
|
+
for (const row of await definition.source()) {
|
|
1372
|
+
entry.index.add(row);
|
|
1373
|
+
}
|
|
1374
|
+
entry.hydrated = true;
|
|
1375
|
+
}
|
|
1376
|
+
return entry;
|
|
1377
|
+
};
|
|
1378
|
+
const searchPairs = (changes) => {
|
|
1379
|
+
const touched = new Set;
|
|
1380
|
+
for (const { table, change } of changes) {
|
|
1381
|
+
for (const entry of searchIndexes.values()) {
|
|
1382
|
+
if (!entry.hydrated || entry.definition.table !== table) {
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (change.op === "delete") {
|
|
1386
|
+
entry.index.remove(entry.definition.key(change.row));
|
|
1387
|
+
} else {
|
|
1388
|
+
entry.index.add(change.row);
|
|
1389
|
+
}
|
|
1390
|
+
touched.add(entry.definition.name);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
const pairs = [];
|
|
1394
|
+
for (const sub of searchSubs) {
|
|
1395
|
+
if (!touched.has(sub.collection)) {
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1398
|
+
const diff = diffRerun(sub, sub.rerun(), equalsIgnoringScore);
|
|
1399
|
+
if (!isEmptyViewDiff(diff)) {
|
|
1400
|
+
pairs.push([sub, diff]);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
return pairs;
|
|
1404
|
+
};
|
|
1131
1405
|
const logChange = (changeVersion, entry) => {
|
|
1132
1406
|
changeLog.push(entry);
|
|
1133
1407
|
if (changeLog.length > changeLogSize) {
|
|
@@ -1148,6 +1422,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
1148
1422
|
emissions.push(...await reactivePairs([
|
|
1149
1423
|
{ table, key: changedKeyFor(table, change), row: change.row }
|
|
1150
1424
|
]));
|
|
1425
|
+
emissions.push(...searchPairs([{ table, change }]));
|
|
1151
1426
|
for (const [subscription, diff] of emissions) {
|
|
1152
1427
|
subscription.onDiff(diff, changeVersion);
|
|
1153
1428
|
}
|
|
@@ -1188,6 +1463,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
1188
1463
|
}
|
|
1189
1464
|
}
|
|
1190
1465
|
emissions.push(...await reactivePairs(reactiveChanges));
|
|
1466
|
+
emissions.push(...searchPairs(changes));
|
|
1191
1467
|
for (const [subscription, diff] of emissions) {
|
|
1192
1468
|
subscription.onDiff(diff, batchVersion);
|
|
1193
1469
|
}
|
|
@@ -1330,6 +1606,50 @@ var createSyncEngine = (options = {}) => {
|
|
|
1330
1606
|
}
|
|
1331
1607
|
};
|
|
1332
1608
|
};
|
|
1609
|
+
const subscribeSearch = async (collection, definition, params, ctx, onDiff, set) => {
|
|
1610
|
+
const query2 = params;
|
|
1611
|
+
if (definition.authorize !== undefined) {
|
|
1612
|
+
const allowed = await definition.authorize(query2, ctx);
|
|
1613
|
+
if (!allowed) {
|
|
1614
|
+
throw new UnauthorizedError(`subscribe to collection "${collection}"`);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
const entry = await ensureSearchIndex(definition);
|
|
1618
|
+
const limit = definition.limit ?? 20;
|
|
1619
|
+
const readRule = readRuleFor(definition.table);
|
|
1620
|
+
const rerun = () => {
|
|
1621
|
+
const candidates = entry.index.search(query2, readRule ? limit * 5 : limit);
|
|
1622
|
+
const visible = readRule ? candidates.filter((hit) => readRule(ctx, hit.row)) : candidates;
|
|
1623
|
+
return visible.slice(0, limit).map((hit) => ({
|
|
1624
|
+
...hit.row,
|
|
1625
|
+
[SEARCH_SCORE_FIELD]: hit.score
|
|
1626
|
+
}));
|
|
1627
|
+
};
|
|
1628
|
+
const initial = rerun();
|
|
1629
|
+
const current = new Map;
|
|
1630
|
+
for (const row of initial) {
|
|
1631
|
+
current.set(definition.key(row), row);
|
|
1632
|
+
}
|
|
1633
|
+
const atVersion = version;
|
|
1634
|
+
const subscription = {
|
|
1635
|
+
kind: "search",
|
|
1636
|
+
collection,
|
|
1637
|
+
key: definition.key,
|
|
1638
|
+
rerun,
|
|
1639
|
+
current,
|
|
1640
|
+
onDiff
|
|
1641
|
+
};
|
|
1642
|
+
set.add(subscription);
|
|
1643
|
+
searchSubs.add(subscription);
|
|
1644
|
+
return {
|
|
1645
|
+
initial,
|
|
1646
|
+
version: atVersion,
|
|
1647
|
+
unsubscribe: () => {
|
|
1648
|
+
set.delete(subscription);
|
|
1649
|
+
searchSubs.delete(subscription);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
};
|
|
1333
1653
|
return {
|
|
1334
1654
|
register: (collection) => {
|
|
1335
1655
|
registry.set(collection.name, collection);
|
|
@@ -1348,6 +1668,9 @@ var createSyncEngine = (options = {}) => {
|
|
|
1348
1668
|
addTableIndex(table, collection.name);
|
|
1349
1669
|
}
|
|
1350
1670
|
},
|
|
1671
|
+
registerSearch: (collection) => {
|
|
1672
|
+
registry.set(collection.name, collection);
|
|
1673
|
+
},
|
|
1351
1674
|
subscribe: async ({ collection, params, ctx, onDiff, since }) => {
|
|
1352
1675
|
const registered = registry.get(collection);
|
|
1353
1676
|
if (registered === undefined) {
|
|
@@ -1368,6 +1691,10 @@ var createSyncEngine = (options = {}) => {
|
|
|
1368
1691
|
const reactived = await subscribeReactive(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1369
1692
|
return reactived;
|
|
1370
1693
|
}
|
|
1694
|
+
if (registeredKind === "search") {
|
|
1695
|
+
const searched = await subscribeSearch(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1696
|
+
return searched;
|
|
1697
|
+
}
|
|
1371
1698
|
const definition = registered;
|
|
1372
1699
|
if (definition.authorize !== undefined) {
|
|
1373
1700
|
const allowed = await definition.authorize(params, ctx);
|
|
@@ -1488,69 +1815,32 @@ var createSyncEngine = (options = {}) => {
|
|
|
1488
1815
|
throw new UnauthorizedError(`run mutation "${name}"`);
|
|
1489
1816
|
}
|
|
1490
1817
|
}
|
|
1491
|
-
const writerFor = (table) => {
|
|
1492
|
-
const writer = writers.get(table);
|
|
1493
|
-
if (writer === undefined) {
|
|
1494
|
-
throw new Error(`No writer registered for table "${table}" \u2014 register one with engine.registerWriter, or use actions.change`);
|
|
1495
|
-
}
|
|
1496
|
-
return writer;
|
|
1497
|
-
};
|
|
1498
|
-
const authorizeWrite = async (table, op, value) => {
|
|
1499
|
-
const rule = writeRuleFor(table, op);
|
|
1500
|
-
if (rule === undefined) {
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
let subject = value;
|
|
1504
|
-
if (op !== "insert") {
|
|
1505
|
-
const reader = readers.get(table);
|
|
1506
|
-
if (reader?.get !== undefined) {
|
|
1507
|
-
const id = reader.key ? reader.key(value) : value.id;
|
|
1508
|
-
if (id !== undefined) {
|
|
1509
|
-
const existing = await reader.get(id, ctx);
|
|
1510
|
-
if (existing !== undefined) {
|
|
1511
|
-
subject = existing;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
if (!rule(ctx, subject)) {
|
|
1517
|
-
throw new UnauthorizedError(`${op} on table "${table}"`);
|
|
1518
|
-
}
|
|
1519
|
-
};
|
|
1520
1818
|
const runHandler = async (tx) => {
|
|
1521
|
-
const buffered2 =
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
buffered2.push({
|
|
1525
|
-
table: collection,
|
|
1526
|
-
change
|
|
1527
|
-
});
|
|
1528
|
-
return Promise.resolve();
|
|
1529
|
-
},
|
|
1530
|
-
insert: async (table, data) => {
|
|
1531
|
-
await authorizeWrite(table, "insert", data);
|
|
1532
|
-
const row = await writerFor(table).insert(data, ctx, tx);
|
|
1533
|
-
buffered2.push({ table, change: { op: "insert", row } });
|
|
1534
|
-
return row;
|
|
1535
|
-
},
|
|
1536
|
-
update: async (table, data) => {
|
|
1537
|
-
await authorizeWrite(table, "update", data);
|
|
1538
|
-
const row = await writerFor(table).update(data, ctx, tx);
|
|
1539
|
-
buffered2.push({ table, change: { op: "update", row } });
|
|
1540
|
-
return row;
|
|
1541
|
-
},
|
|
1542
|
-
delete: async (table, row) => {
|
|
1543
|
-
await authorizeWrite(table, "delete", row);
|
|
1544
|
-
await writerFor(table).delete(row, ctx, tx);
|
|
1545
|
-
buffered2.push({ table, change: { op: "delete", row } });
|
|
1546
|
-
}
|
|
1547
|
-
};
|
|
1548
|
-
const handlerResult = await mutation.handler(args, ctx, actions);
|
|
1549
|
-
return { buffered: buffered2, result: handlerResult };
|
|
1819
|
+
const { actions, buffered: buffered2 } = makeActions(tx, ctx, true);
|
|
1820
|
+
const result2 = await mutation.handler(args, ctx, actions);
|
|
1821
|
+
return { buffered: buffered2, result: result2 };
|
|
1550
1822
|
};
|
|
1551
1823
|
const { buffered, result } = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
|
|
1552
1824
|
await applyChangeBatch(buffered);
|
|
1553
1825
|
return result;
|
|
1826
|
+
},
|
|
1827
|
+
registerSchedule: (schedule) => {
|
|
1828
|
+
schedules.set(schedule.name, schedule);
|
|
1829
|
+
},
|
|
1830
|
+
listSchedules: () => [...schedules.values()],
|
|
1831
|
+
runSchedule: async (name) => {
|
|
1832
|
+
const schedule = schedules.get(name);
|
|
1833
|
+
if (schedule === undefined) {
|
|
1834
|
+
throw new Error(`Unknown schedule "${name}"`);
|
|
1835
|
+
}
|
|
1836
|
+
const runHandler = async (tx) => {
|
|
1837
|
+
const { actions, buffered: buffered2 } = makeActions(tx, {}, false);
|
|
1838
|
+
const db = makeReadHandle({}, new Set, new Set, [], false);
|
|
1839
|
+
await schedule.run({ actions, db });
|
|
1840
|
+
return buffered2;
|
|
1841
|
+
};
|
|
1842
|
+
const buffered = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
|
|
1843
|
+
await applyChangeBatch(buffered);
|
|
1554
1844
|
}
|
|
1555
1845
|
};
|
|
1556
1846
|
};
|
|
@@ -1801,12 +2091,16 @@ export {
|
|
|
1801
2091
|
hydrateRoute,
|
|
1802
2092
|
fromRowChange,
|
|
1803
2093
|
filterOp,
|
|
2094
|
+
defineSearchCollection,
|
|
2095
|
+
defineSchedule,
|
|
1804
2096
|
defineReactiveQuery,
|
|
1805
2097
|
definePermissions,
|
|
1806
2098
|
defineMutation,
|
|
1807
2099
|
defineJoinCollection,
|
|
1808
2100
|
defineGraphCollection,
|
|
1809
2101
|
defineCollection,
|
|
2102
|
+
createVectorIndex,
|
|
2103
|
+
createTextIndex,
|
|
1810
2104
|
createSyncEngine,
|
|
1811
2105
|
createSyncConnection,
|
|
1812
2106
|
createPresenceHub,
|
|
@@ -1817,8 +2111,9 @@ export {
|
|
|
1817
2111
|
createAggregate,
|
|
1818
2112
|
chain,
|
|
1819
2113
|
aggregateOp,
|
|
1820
|
-
UnauthorizedError
|
|
2114
|
+
UnauthorizedError,
|
|
2115
|
+
SEARCH_SCORE_FIELD
|
|
1821
2116
|
};
|
|
1822
2117
|
|
|
1823
|
-
//# debugId=
|
|
2118
|
+
//# debugId=54AD7964887E323764756E2164756E21
|
|
1824
2119
|
//# sourceMappingURL=index.js.map
|