@absolutejs/sync 1.17.0 → 1.18.1

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,63 @@ 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);
2026
2070
  }
2027
2071
  };
2072
+ const normalizeSince = (since) => {
2073
+ if (typeof since === "number") {
2074
+ return { [instanceId]: since };
2075
+ }
2076
+ return decodeCursor(since);
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
+ for (const [origin, lastSeen] of Object.entries(sinceVec)) {
2094
+ if (origin === instanceId) {
2095
+ if (lastSeen >= version)
2096
+ continue;
2097
+ const oldestLocal = oldestPerOrigin.get(instanceId);
2098
+ if (oldestLocal === undefined || oldestLocal > lastSeen + 1)
2099
+ return false;
2100
+ } else {
2101
+ const oldestPeer = oldestPerOrigin.get(origin);
2102
+ if (oldestPeer === undefined) {
2103
+ if (lastSeen > 0)
2104
+ return false;
2105
+ } else if (oldestPeer > lastSeen + 1) {
2106
+ return false;
2107
+ }
2108
+ }
2034
2109
  }
2035
- const oldest = changeLog[0];
2036
- return oldest !== undefined && oldest.version <= since + 1;
2110
+ return true;
2037
2111
  };
2038
2112
  const buildCatchup = (since, tables, key, match) => {
2113
+ const sinceVec = normalizeSince(since) ?? {};
2039
2114
  const latest = new Map;
2040
2115
  for (const entry of changeLog) {
2041
- if (entry.version <= since || !tables.includes(entry.table)) {
2116
+ if (!tables.includes(entry.table))
2117
+ continue;
2118
+ const lastSeen = sinceVec[entry.origin];
2119
+ if (lastSeen !== undefined && entry.originVersion <= lastSeen)
2042
2120
  continue;
2043
- }
2044
2121
  const row = entry.change.row;
2045
2122
  const present = entry.change.op !== "delete" && match(row) ? "upsert" : "remove";
2046
2123
  latest.set(key(row), { op: present, row });
@@ -2085,6 +2162,7 @@ var createSyncEngine = (options = {}) => {
2085
2162
  set.add(subscription);
2086
2163
  return {
2087
2164
  initial: op.rows(),
2165
+ cursor: currentCursor(),
2088
2166
  version: atVersion,
2089
2167
  unsubscribe: () => {
2090
2168
  set.delete(subscription);
@@ -2111,6 +2189,7 @@ var createSyncEngine = (options = {}) => {
2111
2189
  set.add(subscription);
2112
2190
  return {
2113
2191
  initial,
2192
+ cursor: currentCursor(),
2114
2193
  version: atVersion,
2115
2194
  unsubscribe: () => {
2116
2195
  set.delete(subscription);
@@ -2172,6 +2251,7 @@ var createSyncEngine = (options = {}) => {
2172
2251
  reactiveSubs.add(subscription);
2173
2252
  return {
2174
2253
  initial: first.rows,
2254
+ cursor: currentCursor(),
2175
2255
  version: atVersion,
2176
2256
  unsubscribe: () => {
2177
2257
  set.delete(subscription);
@@ -2216,6 +2296,7 @@ var createSyncEngine = (options = {}) => {
2216
2296
  searchSubs.add(subscription);
2217
2297
  return {
2218
2298
  initial,
2299
+ cursor: currentCursor(),
2219
2300
  version: atVersion,
2220
2301
  unsubscribe: () => {
2221
2302
  set.delete(subscription);
@@ -2317,12 +2398,14 @@ var createSyncEngine = (options = {}) => {
2317
2398
  return wrapReturn({
2318
2399
  initial: [],
2319
2400
  catchup: buildCatchup(since, tables, key, boundMatch),
2401
+ cursor: currentCursor(),
2320
2402
  version: atVersion,
2321
2403
  unsubscribe
2322
2404
  });
2323
2405
  }
2324
2406
  return wrapReturn({
2325
2407
  initial: view.rows(),
2408
+ cursor: currentCursor(),
2326
2409
  version: atVersion,
2327
2410
  unsubscribe
2328
2411
  });
@@ -2361,7 +2444,10 @@ var createSyncEngine = (options = {}) => {
2361
2444
  if (message.origin === instanceId) {
2362
2445
  return;
2363
2446
  }
2364
- applyChangeBatch(message.changes, false);
2447
+ applyChangeBatch(message.changes, false, {
2448
+ origin: message.origin,
2449
+ originVersion: message.originVersion ?? 0
2450
+ });
2365
2451
  });
2366
2452
  clusterBus = bus;
2367
2453
  return async () => {
@@ -3032,12 +3118,13 @@ var parseFrame = (raw, serializer) => {
3032
3118
  }
3033
3119
  const frame = value;
3034
3120
  if (frame.type === "subscribe") {
3121
+ const since = typeof frame.since === "number" || typeof frame.since === "string" ? frame.since : undefined;
3035
3122
  return typeof frame.id === "string" && typeof frame.collection === "string" ? {
3036
3123
  type: "subscribe",
3037
3124
  id: frame.id,
3038
3125
  collection: frame.collection,
3039
3126
  params: frame.params,
3040
- since: typeof frame.since === "number" ? frame.since : undefined
3127
+ since
3041
3128
  } : undefined;
3042
3129
  }
3043
3130
  if (frame.type === "unsubscribe") {
@@ -3089,6 +3176,7 @@ var createSyncConnection = ({
3089
3176
  };
3090
3177
  let pending = [];
3091
3178
  let pendingVersion;
3179
+ let pendingCursor;
3092
3180
  let flushScheduled = false;
3093
3181
  const flush = () => {
3094
3182
  if (pending.length === 0) {
@@ -3096,8 +3184,10 @@ var createSyncConnection = ({
3096
3184
  }
3097
3185
  const diffs = pending;
3098
3186
  const version = pendingVersion;
3187
+ const cursor = pendingCursor;
3099
3188
  pending = [];
3100
3189
  pendingVersion = undefined;
3190
+ pendingCursor = undefined;
3101
3191
  if (diffs.length === 1) {
3102
3192
  const only = diffs[0];
3103
3193
  send({
@@ -3106,10 +3196,11 @@ var createSyncConnection = ({
3106
3196
  added: only.added,
3107
3197
  removed: only.removed,
3108
3198
  changed: only.changed,
3109
- version
3199
+ version,
3200
+ cursor
3110
3201
  });
3111
3202
  } else {
3112
- send({ type: "frame", diffs, version });
3203
+ send({ type: "frame", diffs, version, cursor });
3113
3204
  }
3114
3205
  };
3115
3206
  const scheduleFlush = () => {
@@ -3122,12 +3213,14 @@ var createSyncConnection = ({
3122
3213
  flush();
3123
3214
  });
3124
3215
  };
3125
- const bufferDiff = (diff, diffVersion) => {
3216
+ const bufferDiff = (diff, diffVersion, cursor) => {
3126
3217
  if (pending.length > 0 && pendingVersion !== diffVersion) {
3127
3218
  flush();
3128
3219
  }
3129
3220
  pending.push(diff);
3130
3221
  pendingVersion = diffVersion;
3222
+ if (cursor !== undefined)
3223
+ pendingCursor = cursor;
3131
3224
  scheduleFlush();
3132
3225
  };
3133
3226
  const handle = async (raw) => {
@@ -3203,13 +3296,13 @@ var createSyncConnection = ({
3203
3296
  params: frame.params,
3204
3297
  ctx,
3205
3298
  since: frame.since,
3206
- onDiff: (diff, diffVersion) => {
3299
+ onDiff: (diff, diffVersion, cursor) => {
3207
3300
  bufferDiff({
3208
3301
  id: frame.id,
3209
3302
  added: diff.added,
3210
3303
  removed: diff.removed,
3211
3304
  changed: diff.changed
3212
- }, diffVersion);
3305
+ }, diffVersion, cursor);
3213
3306
  }
3214
3307
  });
3215
3308
  subscriptions.set(frame.id, subscription);
@@ -3220,14 +3313,16 @@ var createSyncConnection = ({
3220
3313
  added: subscription.catchup.added,
3221
3314
  removed: subscription.catchup.removed,
3222
3315
  changed: subscription.catchup.changed,
3223
- version: subscription.version
3316
+ version: subscription.version,
3317
+ cursor: subscription.cursor
3224
3318
  });
3225
3319
  } else {
3226
3320
  send({
3227
3321
  type: "snapshot",
3228
3322
  id: frame.id,
3229
3323
  rows: subscription.initial,
3230
- version: subscription.version
3324
+ version: subscription.version,
3325
+ cursor: subscription.cursor
3231
3326
  });
3232
3327
  }
3233
3328
  } catch (error) {
@@ -3304,5 +3399,5 @@ export {
3304
3399
  CdcConsumerSlowError
3305
3400
  };
3306
3401
 
3307
- //# debugId=2B8B686DC5E953C364756E2164756E21
3402
+ //# debugId=2B4AA96446522F9164756E2164756E21
3308
3403
  //# sourceMappingURL=index.js.map