@absolutejs/sync 1.18.0 → 1.18.2

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.
@@ -1565,11 +1565,11 @@ var createSyncEngine = (options = {}) => {
1565
1565
  };
1566
1566
  const streamSubscribers = new Set;
1567
1567
  const runInTransaction = options.transaction;
1568
- const instanceId = globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
1568
+ const instanceId = options.instanceId ?? globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
1569
1569
  let clusterBus;
1570
- const broadcast = (changes) => {
1570
+ const broadcast = (changes, originVersion) => {
1571
1571
  if (clusterBus !== undefined && changes.length > 0) {
1572
- clusterBus.publish({ changes, origin: instanceId });
1572
+ clusterBus.publish({ changes, origin: instanceId, originVersion });
1573
1573
  }
1574
1574
  };
1575
1575
  const subsFor = (collection) => {
@@ -1946,11 +1946,44 @@ var createSyncEngine = (options = {}) => {
1946
1946
  subscriber(entry);
1947
1947
  }
1948
1948
  };
1949
+ const encodeCursor = (versions) => JSON.stringify(versions);
1950
+ const decodeCursor = (cursor) => {
1951
+ try {
1952
+ const parsed = JSON.parse(cursor);
1953
+ if (typeof parsed !== "object" || parsed === null)
1954
+ return null;
1955
+ const out = {};
1956
+ for (const [k, v] of Object.entries(parsed)) {
1957
+ if (typeof v === "number")
1958
+ out[k] = v;
1959
+ }
1960
+ return out;
1961
+ } catch {
1962
+ return null;
1963
+ }
1964
+ };
1965
+ const currentCursor = () => {
1966
+ const versions = { [instanceId]: version };
1967
+ for (let i = changeLog.length - 1;i >= 0; i--) {
1968
+ const entry = changeLog[i];
1969
+ if (versions[entry.origin] === undefined) {
1970
+ versions[entry.origin] = entry.originVersion;
1971
+ }
1972
+ }
1973
+ return encodeCursor(versions);
1974
+ };
1949
1975
  const applyChange = async (table, change, shouldBroadcast = true) => {
1950
1976
  version += 1;
1951
1977
  const changeVersion = version;
1952
1978
  const at = Date.now();
1953
- logChange(changeVersion, { version: changeVersion, table, change, at });
1979
+ logChange(changeVersion, {
1980
+ version: changeVersion,
1981
+ table,
1982
+ change,
1983
+ at,
1984
+ origin: instanceId,
1985
+ originVersion: changeVersion
1986
+ });
1954
1987
  emitActivity({
1955
1988
  type: "change",
1956
1989
  at,
@@ -1969,14 +2002,15 @@ var createSyncEngine = (options = {}) => {
1969
2002
  { table, key: changedKeyFor(table, change), row: change.row }
1970
2003
  ]));
1971
2004
  emissions.push(...searchPairs([{ table, change }]));
2005
+ const cursorForBatch = currentCursor();
1972
2006
  for (const [subscription, diff] of emissions) {
1973
- subscription.onDiff(diff, changeVersion);
2007
+ subscription.onDiff(diff, changeVersion, cursorForBatch);
1974
2008
  }
1975
2009
  if (shouldBroadcast) {
1976
- broadcast([{ table, change }]);
2010
+ broadcast([{ table, change }], changeVersion);
1977
2011
  }
1978
2012
  };
1979
- const applyChangeBatch = async (changes, shouldBroadcast = true) => {
2013
+ const applyChangeBatch = async (changes, shouldBroadcast = true, peerOrigin) => {
1980
2014
  if (changes.length === 0) {
1981
2015
  return;
1982
2016
  }
@@ -1985,8 +2019,17 @@ var createSyncEngine = (options = {}) => {
1985
2019
  const perSubscription = new Map;
1986
2020
  const reactiveChanges = [];
1987
2021
  const batchAt = Date.now();
2022
+ const batchOrigin = peerOrigin?.origin ?? instanceId;
2023
+ const batchOriginVersion = peerOrigin?.originVersion ?? batchVersion;
1988
2024
  for (const { table, change } of changes) {
1989
- logChange(batchVersion, { version: batchVersion, table, change, at: batchAt });
2025
+ logChange(batchVersion, {
2026
+ version: batchVersion,
2027
+ table,
2028
+ change,
2029
+ at: batchAt,
2030
+ origin: batchOrigin,
2031
+ originVersion: batchOriginVersion
2032
+ });
1990
2033
  emitActivity({
1991
2034
  type: "change",
1992
2035
  at: batchAt,
@@ -2018,29 +2061,70 @@ var createSyncEngine = (options = {}) => {
2018
2061
  }
2019
2062
  emissions.push(...await reactivePairs(reactiveChanges));
2020
2063
  emissions.push(...searchPairs(changes));
2064
+ const cursorForBatch = currentCursor();
2021
2065
  for (const [subscription, diff] of emissions) {
2022
- subscription.onDiff(diff, batchVersion);
2066
+ subscription.onDiff(diff, batchVersion, cursorForBatch);
2023
2067
  }
2024
2068
  if (shouldBroadcast) {
2025
- broadcast(changes);
2069
+ broadcast(changes, batchVersion);
2070
+ }
2071
+ };
2072
+ const normalizeSince = (since) => {
2073
+ if (typeof since === "number") {
2074
+ return { [instanceId]: since };
2026
2075
  }
2076
+ return decodeCursor(since);
2027
2077
  };
2028
2078
  const canResume = (since, incremental) => {
2029
2079
  if (!incremental) {
2030
2080
  return false;
2031
2081
  }
2032
- if (since >= version) {
2033
- return true;
2082
+ const sinceVec = normalizeSince(since);
2083
+ if (sinceVec === null) {
2084
+ return false;
2085
+ }
2086
+ const oldestPerOrigin = new Map;
2087
+ for (const entry of changeLog) {
2088
+ const current = oldestPerOrigin.get(entry.origin);
2089
+ if (current === undefined || entry.originVersion < current) {
2090
+ oldestPerOrigin.set(entry.origin, entry.originVersion);
2091
+ }
2092
+ }
2093
+ const oldestLogVersion = changeLog[0]?.version;
2094
+ for (const [origin, lastSeen] of Object.entries(sinceVec)) {
2095
+ if (origin === instanceId) {
2096
+ if (lastSeen >= version)
2097
+ continue;
2098
+ const oldestLocal = oldestPerOrigin.get(instanceId);
2099
+ if (oldestLocal !== undefined) {
2100
+ if (oldestLocal > lastSeen + 1)
2101
+ return false;
2102
+ continue;
2103
+ }
2104
+ if (oldestLogVersion !== undefined && oldestLogVersion > lastSeen + 1) {
2105
+ return false;
2106
+ }
2107
+ } else {
2108
+ const oldestPeer = oldestPerOrigin.get(origin);
2109
+ if (oldestPeer === undefined) {
2110
+ if (lastSeen > 0)
2111
+ return false;
2112
+ } else if (oldestPeer > lastSeen + 1) {
2113
+ return false;
2114
+ }
2115
+ }
2034
2116
  }
2035
- const oldest = changeLog[0];
2036
- return oldest !== undefined && oldest.version <= since + 1;
2117
+ return true;
2037
2118
  };
2038
2119
  const buildCatchup = (since, tables, key, match) => {
2120
+ const sinceVec = normalizeSince(since) ?? {};
2039
2121
  const latest = new Map;
2040
2122
  for (const entry of changeLog) {
2041
- if (entry.version <= since || !tables.includes(entry.table)) {
2123
+ if (!tables.includes(entry.table))
2124
+ continue;
2125
+ const lastSeen = sinceVec[entry.origin];
2126
+ if (lastSeen !== undefined && entry.originVersion <= lastSeen)
2042
2127
  continue;
2043
- }
2044
2128
  const row = entry.change.row;
2045
2129
  const present = entry.change.op !== "delete" && match(row) ? "upsert" : "remove";
2046
2130
  latest.set(key(row), { op: present, row });
@@ -2085,6 +2169,7 @@ var createSyncEngine = (options = {}) => {
2085
2169
  set.add(subscription);
2086
2170
  return {
2087
2171
  initial: op.rows(),
2172
+ cursor: currentCursor(),
2088
2173
  version: atVersion,
2089
2174
  unsubscribe: () => {
2090
2175
  set.delete(subscription);
@@ -2111,6 +2196,7 @@ var createSyncEngine = (options = {}) => {
2111
2196
  set.add(subscription);
2112
2197
  return {
2113
2198
  initial,
2199
+ cursor: currentCursor(),
2114
2200
  version: atVersion,
2115
2201
  unsubscribe: () => {
2116
2202
  set.delete(subscription);
@@ -2172,6 +2258,7 @@ var createSyncEngine = (options = {}) => {
2172
2258
  reactiveSubs.add(subscription);
2173
2259
  return {
2174
2260
  initial: first.rows,
2261
+ cursor: currentCursor(),
2175
2262
  version: atVersion,
2176
2263
  unsubscribe: () => {
2177
2264
  set.delete(subscription);
@@ -2216,6 +2303,7 @@ var createSyncEngine = (options = {}) => {
2216
2303
  searchSubs.add(subscription);
2217
2304
  return {
2218
2305
  initial,
2306
+ cursor: currentCursor(),
2219
2307
  version: atVersion,
2220
2308
  unsubscribe: () => {
2221
2309
  set.delete(subscription);
@@ -2317,12 +2405,14 @@ var createSyncEngine = (options = {}) => {
2317
2405
  return wrapReturn({
2318
2406
  initial: [],
2319
2407
  catchup: buildCatchup(since, tables, key, boundMatch),
2408
+ cursor: currentCursor(),
2320
2409
  version: atVersion,
2321
2410
  unsubscribe
2322
2411
  });
2323
2412
  }
2324
2413
  return wrapReturn({
2325
2414
  initial: view.rows(),
2415
+ cursor: currentCursor(),
2326
2416
  version: atVersion,
2327
2417
  unsubscribe
2328
2418
  });
@@ -2361,7 +2451,10 @@ var createSyncEngine = (options = {}) => {
2361
2451
  if (message.origin === instanceId) {
2362
2452
  return;
2363
2453
  }
2364
- applyChangeBatch(message.changes, false);
2454
+ applyChangeBatch(message.changes, false, {
2455
+ origin: message.origin,
2456
+ originVersion: message.originVersion ?? 0
2457
+ });
2365
2458
  });
2366
2459
  clusterBus = bus;
2367
2460
  return async () => {
@@ -3032,12 +3125,13 @@ var parseFrame = (raw, serializer) => {
3032
3125
  }
3033
3126
  const frame = value;
3034
3127
  if (frame.type === "subscribe") {
3128
+ const since = typeof frame.since === "number" || typeof frame.since === "string" ? frame.since : undefined;
3035
3129
  return typeof frame.id === "string" && typeof frame.collection === "string" ? {
3036
3130
  type: "subscribe",
3037
3131
  id: frame.id,
3038
3132
  collection: frame.collection,
3039
3133
  params: frame.params,
3040
- since: typeof frame.since === "number" ? frame.since : undefined
3134
+ since
3041
3135
  } : undefined;
3042
3136
  }
3043
3137
  if (frame.type === "unsubscribe") {
@@ -3089,6 +3183,7 @@ var createSyncConnection = ({
3089
3183
  };
3090
3184
  let pending = [];
3091
3185
  let pendingVersion;
3186
+ let pendingCursor;
3092
3187
  let flushScheduled = false;
3093
3188
  const flush = () => {
3094
3189
  if (pending.length === 0) {
@@ -3096,8 +3191,10 @@ var createSyncConnection = ({
3096
3191
  }
3097
3192
  const diffs = pending;
3098
3193
  const version = pendingVersion;
3194
+ const cursor = pendingCursor;
3099
3195
  pending = [];
3100
3196
  pendingVersion = undefined;
3197
+ pendingCursor = undefined;
3101
3198
  if (diffs.length === 1) {
3102
3199
  const only = diffs[0];
3103
3200
  send({
@@ -3106,10 +3203,11 @@ var createSyncConnection = ({
3106
3203
  added: only.added,
3107
3204
  removed: only.removed,
3108
3205
  changed: only.changed,
3109
- version
3206
+ version,
3207
+ cursor
3110
3208
  });
3111
3209
  } else {
3112
- send({ type: "frame", diffs, version });
3210
+ send({ type: "frame", diffs, version, cursor });
3113
3211
  }
3114
3212
  };
3115
3213
  const scheduleFlush = () => {
@@ -3122,12 +3220,14 @@ var createSyncConnection = ({
3122
3220
  flush();
3123
3221
  });
3124
3222
  };
3125
- const bufferDiff = (diff, diffVersion) => {
3223
+ const bufferDiff = (diff, diffVersion, cursor) => {
3126
3224
  if (pending.length > 0 && pendingVersion !== diffVersion) {
3127
3225
  flush();
3128
3226
  }
3129
3227
  pending.push(diff);
3130
3228
  pendingVersion = diffVersion;
3229
+ if (cursor !== undefined)
3230
+ pendingCursor = cursor;
3131
3231
  scheduleFlush();
3132
3232
  };
3133
3233
  const handle = async (raw) => {
@@ -3203,13 +3303,13 @@ var createSyncConnection = ({
3203
3303
  params: frame.params,
3204
3304
  ctx,
3205
3305
  since: frame.since,
3206
- onDiff: (diff, diffVersion) => {
3306
+ onDiff: (diff, diffVersion, cursor) => {
3207
3307
  bufferDiff({
3208
3308
  id: frame.id,
3209
3309
  added: diff.added,
3210
3310
  removed: diff.removed,
3211
3311
  changed: diff.changed
3212
- }, diffVersion);
3312
+ }, diffVersion, cursor);
3213
3313
  }
3214
3314
  });
3215
3315
  subscriptions.set(frame.id, subscription);
@@ -3220,14 +3320,16 @@ var createSyncConnection = ({
3220
3320
  added: subscription.catchup.added,
3221
3321
  removed: subscription.catchup.removed,
3222
3322
  changed: subscription.catchup.changed,
3223
- version: subscription.version
3323
+ version: subscription.version,
3324
+ cursor: subscription.cursor
3224
3325
  });
3225
3326
  } else {
3226
3327
  send({
3227
3328
  type: "snapshot",
3228
3329
  id: frame.id,
3229
3330
  rows: subscription.initial,
3230
- version: subscription.version
3331
+ version: subscription.version,
3332
+ cursor: subscription.cursor
3231
3333
  });
3232
3334
  }
3233
3335
  } catch (error) {
@@ -3304,5 +3406,5 @@ export {
3304
3406
  CdcConsumerSlowError
3305
3407
  };
3306
3408
 
3307
- //# debugId=2B8B686DC5E953C364756E2164756E21
3409
+ //# debugId=83816548811B9BFD64756E2164756E21
3308
3410
  //# sourceMappingURL=index.js.map