@tanstack/electric-db-collection 0.2.43 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"tag-index.cjs","sources":["../../src/tag-index.ts"],"sourcesContent":["// Import Row and Message types for the isEventMessage function\nimport type { Message, Row } from '@electric-sql/client'\n\nexport type RowId = string | number\nexport type MoveTag = string\nexport type ParsedMoveTag = Array<string>\nexport type Position = number\nexport type Value = string\nexport type MoveOutPattern = {\n pos: Position\n value: Value\n}\n\nconst TAG_WILDCARD = `_`\n\n/**\n * Event message type for move-out events\n */\nexport interface EventMessage {\n headers: {\n event: `move-out`\n patterns: Array<MoveOutPattern>\n }\n}\n\n/**\n * Tag index structure: array indexed by position, maps value to set of row IDs.\n * For example:\n * ```example\n * const tag1 = [a, b, c]\n * const tag2 = [a, b, d]\n * const tag3 = [a, d, e]\n *\n * // Index is:\n * [\n * new Map([a -> <rows with a on index 0>])\n * new Map([b -> <rows with b on index 1>, d -> <rows with d on index 1>])\n * new Map([c -> <rows with c on index 2>, d -> <rows with d on index 2>, e -> <rows with e on index 2>])\n * ]\n * ```\n */\nexport type TagIndex = Array<Map<Value, Set<RowId>>>\n\n/**\n * Abstraction to get the value at a specific position in a tag\n */\nexport function getValue(tag: ParsedMoveTag, position: Position): Value {\n if (position >= tag.length) {\n throw new Error(`Position out of bounds`)\n }\n return tag[position]!\n}\n\n/**\n * Abstraction to extract position and value from a pattern.\n */\nfunction getPositionalValue(pattern: MoveOutPattern): {\n pos: number\n value: string\n} {\n return pattern\n}\n\n/**\n * Abstraction to get the length of a tag\n */\nexport function getTagLength(tag: ParsedMoveTag): number {\n return tag.length\n}\n\n/**\n * Check if a tag matches a pattern.\n * A tag matches if the value at the pattern's position equals the pattern's value,\n * or if the value at that position is \"_\" (wildcard).\n */\nexport function tagMatchesPattern(\n tag: ParsedMoveTag,\n pattern: MoveOutPattern,\n): boolean {\n const { pos, value } = getPositionalValue(pattern)\n const tagValue = getValue(tag, pos)\n return tagValue === value || tagValue === TAG_WILDCARD\n}\n\n/**\n * Add a tag to the index for efficient pattern matching\n */\nexport function addTagToIndex(\n tag: ParsedMoveTag,\n rowId: RowId,\n index: TagIndex,\n tagLength: number,\n): void {\n for (let i = 0; i < tagLength; i++) {\n const value = getValue(tag, i)\n\n // Only index non-wildcard values\n if (value !== TAG_WILDCARD) {\n const positionIndex = index[i]!\n if (!positionIndex.has(value)) {\n positionIndex.set(value, new Set())\n }\n\n const tags = positionIndex.get(value)!\n tags.add(rowId)\n }\n }\n}\n\n/**\n * Remove a tag from the index\n */\nexport function removeTagFromIndex(\n tag: ParsedMoveTag,\n rowId: RowId,\n index: TagIndex,\n tagLength: number,\n): void {\n for (let i = 0; i < tagLength; i++) {\n const value = getValue(tag, i)\n\n // Only remove non-wildcard values\n if (value !== TAG_WILDCARD) {\n const positionIndex = index[i]\n if (positionIndex) {\n const rowSet = positionIndex.get(value)\n if (rowSet) {\n rowSet.delete(rowId)\n\n // Clean up empty sets\n if (rowSet.size === 0) {\n positionIndex.delete(value)\n }\n }\n }\n }\n }\n}\n\n/**\n * Find all rows that match a given pattern\n */\nexport function findRowsMatchingPattern(\n pattern: MoveOutPattern,\n index: TagIndex,\n): Set<RowId> {\n const { pos, value } = getPositionalValue(pattern)\n const positionIndex = index[pos]\n const rowSet = positionIndex?.get(value)\n return rowSet ?? new Set()\n}\n\n/**\n * Check if a message is an event message with move-out event\n */\nexport function isMoveOutMessage<T extends Row<unknown>>(\n message: Message<T>,\n): message is Message<T> & EventMessage {\n return message.headers.event === `move-out`\n}\n"],"names":[],"mappings":";;AAaA,MAAM,eAAe;AAiCd,SAAS,SAAS,KAAoB,UAA2B;AACtE,MAAI,YAAY,IAAI,QAAQ;AAC1B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO,IAAI,QAAQ;AACrB;AAKA,SAAS,mBAAmB,SAG1B;AACA,SAAO;AACT;AAKO,SAAS,aAAa,KAA4B;AACvD,SAAO,IAAI;AACb;AAOO,SAAS,kBACd,KACA,SACS;AACT,QAAM,EAAE,KAAK,UAAU,mBAAmB,OAAO;AACjD,QAAM,WAAW,SAAS,KAAK,GAAG;AAClC,SAAO,aAAa,SAAS,aAAa;AAC5C;AAKO,SAAS,cACd,KACA,OACA,OACA,WACM;AACN,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,UAAM,QAAQ,SAAS,KAAK,CAAC;AAG7B,QAAI,UAAU,cAAc;AAC1B,YAAM,gBAAgB,MAAM,CAAC;AAC7B,UAAI,CAAC,cAAc,IAAI,KAAK,GAAG;AAC7B,sBAAc,IAAI,OAAO,oBAAI,IAAA,CAAK;AAAA,MACpC;AAEA,YAAM,OAAO,cAAc,IAAI,KAAK;AACpC,WAAK,IAAI,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAKO,SAAS,mBACd,KACA,OACA,OACA,WACM;AACN,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,UAAM,QAAQ,SAAS,KAAK,CAAC;AAG7B,QAAI,UAAU,cAAc;AAC1B,YAAM,gBAAgB,MAAM,CAAC;AAC7B,UAAI,eAAe;AACjB,cAAM,SAAS,cAAc,IAAI,KAAK;AACtC,YAAI,QAAQ;AACV,iBAAO,OAAO,KAAK;AAGnB,cAAI,OAAO,SAAS,GAAG;AACrB,0BAAc,OAAO,KAAK;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,wBACd,SACA,OACY;AACZ,QAAM,EAAE,KAAK,UAAU,mBAAmB,OAAO;AACjD,QAAM,gBAAgB,MAAM,GAAG;AAC/B,QAAM,SAAS,eAAe,IAAI,KAAK;AACvC,SAAO,8BAAc,IAAA;AACvB;AAKO,SAAS,iBACd,SACsC;AACtC,SAAO,QAAQ,QAAQ,UAAU;AACnC;;;;;;;;"}
1
+ {"version":3,"file":"tag-index.cjs","sources":["../../src/tag-index.ts"],"sourcesContent":["// Import Row and Message types for the isEventMessage function\nimport type { Message, Row } from '@electric-sql/client'\n\nexport type RowId = string | number\nexport type MoveTag = string\nexport type ParsedMoveTag = Array<string | NonParticipating>\nexport type Position = number\nexport type Value = string\nexport type MovePattern = {\n pos: Position\n value: Value\n}\n\n/**\n * Sentinel value for tag positions where the disjunct does not participate\n * in that condition. These positions are not indexed and won't match any\n * move pattern.\n */\nexport const NON_PARTICIPATING = null\nexport type NonParticipating = typeof NON_PARTICIPATING\n\nexport type ActiveConditions = Array<boolean>\nexport type DisjunctPositions = Array<Array<number>>\n\n/**\n * Event message type for move-out and move-in events\n */\nexport interface EventMessage {\n headers: {\n event: `move-out` | `move-in`\n patterns: Array<MovePattern>\n }\n}\n\n/**\n * Tag index structure: array indexed by position, maps value to set of row IDs.\n * For example:\n * ```example\n * const tag1 = [a, b, c]\n * const tag2 = [a, b, d]\n * const tag3 = [a, d, e]\n *\n * // Index is:\n * [\n * new Map([a -> <rows with a on index 0>])\n * new Map([b -> <rows with b on index 1>, d -> <rows with d on index 1>])\n * new Map([c -> <rows with c on index 2>, d -> <rows with d on index 2>, e -> <rows with e on index 2>])\n * ]\n * ```\n */\nexport type TagIndex = Array<Map<Value, Set<RowId>>>\n\n/**\n * Parse a tag string into a ParsedMoveTag.\n * Splits on `/` delimiter and maps empty strings to {@link NON_PARTICIPATING}.\n */\nexport function parseTag(tag: MoveTag): ParsedMoveTag {\n return tag.split(`/`).map((s) => (s === `` ? NON_PARTICIPATING : s))\n}\n\n/**\n * Abstraction to get the value at a specific position in a tag\n */\nexport function getValue(\n tag: ParsedMoveTag,\n position: Position,\n): string | NonParticipating {\n if (position >= tag.length) {\n throw new Error(`Position out of bounds`)\n }\n return tag[position]!\n}\n\n/**\n * Abstraction to extract position and value from a pattern.\n */\nfunction getPositionalValue(pattern: MovePattern): {\n pos: number\n value: string\n} {\n return pattern\n}\n\n/**\n * Abstraction to get the length of a tag\n */\nexport function getTagLength(tag: ParsedMoveTag): number {\n return tag.length\n}\n\n/**\n * Check if a tag matches a pattern.\n * A tag matches if the value at the pattern's position equals the pattern's value.\n * {@link NON_PARTICIPATING} positions naturally don't match any string value.\n */\nexport function tagMatchesPattern(\n tag: ParsedMoveTag,\n pattern: MovePattern,\n): boolean {\n const { pos, value } = getPositionalValue(pattern)\n const tagValue = getValue(tag, pos)\n return tagValue === value\n}\n\n/**\n * Add a tag to the index for efficient pattern matching\n */\nexport function addTagToIndex(\n tag: ParsedMoveTag,\n rowId: RowId,\n index: TagIndex,\n tagLength: number,\n): void {\n for (let i = 0; i < tagLength; i++) {\n const value = getValue(tag, i)\n\n if (value !== NON_PARTICIPATING) {\n const positionIndex = index[i]!\n if (!positionIndex.has(value)) {\n positionIndex.set(value, new Set())\n }\n\n const tags = positionIndex.get(value)!\n tags.add(rowId)\n }\n }\n}\n\n/**\n * Remove a tag from the index\n */\nexport function removeTagFromIndex(\n tag: ParsedMoveTag,\n rowId: RowId,\n index: TagIndex,\n tagLength: number,\n): void {\n for (let i = 0; i < tagLength; i++) {\n const value = getValue(tag, i)\n\n if (value !== NON_PARTICIPATING) {\n const positionIndex = index[i]\n if (positionIndex) {\n const rowSet = positionIndex.get(value)\n if (rowSet) {\n rowSet.delete(rowId)\n\n // Clean up empty sets\n if (rowSet.size === 0) {\n positionIndex.delete(value)\n }\n }\n }\n }\n }\n}\n\n/**\n * Find all rows that match a given pattern\n */\nexport function findRowsMatchingPattern(\n pattern: MovePattern,\n index: TagIndex,\n): Set<RowId> {\n const { pos, value } = getPositionalValue(pattern)\n const positionIndex = index[pos]\n const rowSet = positionIndex?.get(value)\n return rowSet ?? new Set()\n}\n\n/**\n * Derive disjunct positions from parsed tags.\n * For each tag (= disjunct), collect the indices of participating positions.\n * E.g., [\"hash_a\", NON_PARTICIPATING, \"hash_b\"] → [0, 2]\n */\nexport function deriveDisjunctPositions(\n tags: Array<ParsedMoveTag>,\n): DisjunctPositions {\n return tags.map((tag) => {\n const positions: Array<number> = []\n for (let i = 0; i < tag.length; i++) {\n if (tag[i] !== NON_PARTICIPATING) {\n positions.push(i)\n }\n }\n return positions\n })\n}\n\n/**\n * Evaluate whether a row is visible given active conditions and disjunct positions.\n * Returns true if ANY disjunct has ALL its positions as true in activeConditions.\n */\nexport function rowVisible(\n activeConditions: ActiveConditions,\n disjunctPositions: DisjunctPositions,\n): boolean {\n return disjunctPositions.some((positions) =>\n positions.every((pos) => activeConditions[pos]),\n )\n}\n\n/**\n * Check if a message is an event message with move-out event\n */\nexport function isMoveOutMessage<T extends Row<unknown>>(\n message: Message<T>,\n): message is Message<T> & EventMessage {\n return message.headers.event === `move-out`\n}\n\n/**\n * Check if a message is an event message with move-in event\n */\nexport function isMoveInMessage<T extends Row<unknown>>(\n message: Message<T>,\n): message is Message<T> & EventMessage {\n return message.headers.event === `move-in`\n}\n"],"names":[],"mappings":";;AAkBO,MAAM,oBAAoB;AAsC1B,SAAS,SAAS,KAA6B;AACpD,SAAO,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAO,MAAM,KAAK,oBAAoB,CAAE;AACrE;AAKO,SAAS,SACd,KACA,UAC2B;AAC3B,MAAI,YAAY,IAAI,QAAQ;AAC1B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO,IAAI,QAAQ;AACrB;AAKA,SAAS,mBAAmB,SAG1B;AACA,SAAO;AACT;AAKO,SAAS,aAAa,KAA4B;AACvD,SAAO,IAAI;AACb;AAOO,SAAS,kBACd,KACA,SACS;AACT,QAAM,EAAE,KAAK,UAAU,mBAAmB,OAAO;AACjD,QAAM,WAAW,SAAS,KAAK,GAAG;AAClC,SAAO,aAAa;AACtB;AAKO,SAAS,cACd,KACA,OACA,OACA,WACM;AACN,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,UAAM,QAAQ,SAAS,KAAK,CAAC;AAE7B,QAAI,UAAU,mBAAmB;AAC/B,YAAM,gBAAgB,MAAM,CAAC;AAC7B,UAAI,CAAC,cAAc,IAAI,KAAK,GAAG;AAC7B,sBAAc,IAAI,OAAO,oBAAI,IAAA,CAAK;AAAA,MACpC;AAEA,YAAM,OAAO,cAAc,IAAI,KAAK;AACpC,WAAK,IAAI,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAKO,SAAS,mBACd,KACA,OACA,OACA,WACM;AACN,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,UAAM,QAAQ,SAAS,KAAK,CAAC;AAE7B,QAAI,UAAU,mBAAmB;AAC/B,YAAM,gBAAgB,MAAM,CAAC;AAC7B,UAAI,eAAe;AACjB,cAAM,SAAS,cAAc,IAAI,KAAK;AACtC,YAAI,QAAQ;AACV,iBAAO,OAAO,KAAK;AAGnB,cAAI,OAAO,SAAS,GAAG;AACrB,0BAAc,OAAO,KAAK;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,wBACd,SACA,OACY;AACZ,QAAM,EAAE,KAAK,UAAU,mBAAmB,OAAO;AACjD,QAAM,gBAAgB,MAAM,GAAG;AAC/B,QAAM,SAAS,eAAe,IAAI,KAAK;AACvC,SAAO,8BAAc,IAAA;AACvB;AAOO,SAAS,wBACd,MACmB;AACnB,SAAO,KAAK,IAAI,CAAC,QAAQ;AACvB,UAAM,YAA2B,CAAA;AACjC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,IAAI,CAAC,MAAM,mBAAmB;AAChC,kBAAU,KAAK,CAAC;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAMO,SAAS,WACd,kBACA,mBACS;AACT,SAAO,kBAAkB;AAAA,IAAK,CAAC,cAC7B,UAAU,MAAM,CAAC,QAAQ,iBAAiB,GAAG,CAAC;AAAA,EAAA;AAElD;AAKO,SAAS,iBACd,SACsC;AACtC,SAAO,QAAQ,QAAQ,UAAU;AACnC;AAKO,SAAS,gBACd,SACsC;AACtC,SAAO,QAAQ,QAAQ,UAAU;AACnC;;;;;;;;;;;;;"}
@@ -1,20 +1,29 @@
1
1
  import { Message, Row } from '@electric-sql/client';
2
2
  export type RowId = string | number;
3
3
  export type MoveTag = string;
4
- export type ParsedMoveTag = Array<string>;
4
+ export type ParsedMoveTag = Array<string | NonParticipating>;
5
5
  export type Position = number;
6
6
  export type Value = string;
7
- export type MoveOutPattern = {
7
+ export type MovePattern = {
8
8
  pos: Position;
9
9
  value: Value;
10
10
  };
11
11
  /**
12
- * Event message type for move-out events
12
+ * Sentinel value for tag positions where the disjunct does not participate
13
+ * in that condition. These positions are not indexed and won't match any
14
+ * move pattern.
15
+ */
16
+ export declare const NON_PARTICIPATING: null;
17
+ export type NonParticipating = typeof NON_PARTICIPATING;
18
+ export type ActiveConditions = Array<boolean>;
19
+ export type DisjunctPositions = Array<Array<number>>;
20
+ /**
21
+ * Event message type for move-out and move-in events
13
22
  */
14
23
  export interface EventMessage {
15
24
  headers: {
16
- event: `move-out`;
17
- patterns: Array<MoveOutPattern>;
25
+ event: `move-out` | `move-in`;
26
+ patterns: Array<MovePattern>;
18
27
  };
19
28
  }
20
29
  /**
@@ -34,20 +43,25 @@ export interface EventMessage {
34
43
  * ```
35
44
  */
36
45
  export type TagIndex = Array<Map<Value, Set<RowId>>>;
46
+ /**
47
+ * Parse a tag string into a ParsedMoveTag.
48
+ * Splits on `/` delimiter and maps empty strings to {@link NON_PARTICIPATING}.
49
+ */
50
+ export declare function parseTag(tag: MoveTag): ParsedMoveTag;
37
51
  /**
38
52
  * Abstraction to get the value at a specific position in a tag
39
53
  */
40
- export declare function getValue(tag: ParsedMoveTag, position: Position): Value;
54
+ export declare function getValue(tag: ParsedMoveTag, position: Position): string | NonParticipating;
41
55
  /**
42
56
  * Abstraction to get the length of a tag
43
57
  */
44
58
  export declare function getTagLength(tag: ParsedMoveTag): number;
45
59
  /**
46
60
  * Check if a tag matches a pattern.
47
- * A tag matches if the value at the pattern's position equals the pattern's value,
48
- * or if the value at that position is "_" (wildcard).
61
+ * A tag matches if the value at the pattern's position equals the pattern's value.
62
+ * {@link NON_PARTICIPATING} positions naturally don't match any string value.
49
63
  */
50
- export declare function tagMatchesPattern(tag: ParsedMoveTag, pattern: MoveOutPattern): boolean;
64
+ export declare function tagMatchesPattern(tag: ParsedMoveTag, pattern: MovePattern): boolean;
51
65
  /**
52
66
  * Add a tag to the index for efficient pattern matching
53
67
  */
@@ -59,8 +73,23 @@ export declare function removeTagFromIndex(tag: ParsedMoveTag, rowId: RowId, ind
59
73
  /**
60
74
  * Find all rows that match a given pattern
61
75
  */
62
- export declare function findRowsMatchingPattern(pattern: MoveOutPattern, index: TagIndex): Set<RowId>;
76
+ export declare function findRowsMatchingPattern(pattern: MovePattern, index: TagIndex): Set<RowId>;
77
+ /**
78
+ * Derive disjunct positions from parsed tags.
79
+ * For each tag (= disjunct), collect the indices of participating positions.
80
+ * E.g., ["hash_a", NON_PARTICIPATING, "hash_b"] → [0, 2]
81
+ */
82
+ export declare function deriveDisjunctPositions(tags: Array<ParsedMoveTag>): DisjunctPositions;
83
+ /**
84
+ * Evaluate whether a row is visible given active conditions and disjunct positions.
85
+ * Returns true if ANY disjunct has ALL its positions as true in activeConditions.
86
+ */
87
+ export declare function rowVisible(activeConditions: ActiveConditions, disjunctPositions: DisjunctPositions): boolean;
63
88
  /**
64
89
  * Check if a message is an event message with move-out event
65
90
  */
66
91
  export declare function isMoveOutMessage<T extends Row<unknown>>(message: Message<T>): message is Message<T> & EventMessage;
92
+ /**
93
+ * Check if a message is an event message with move-in event
94
+ */
95
+ export declare function isMoveInMessage<T extends Row<unknown>>(message: Message<T>): message is Message<T> & EventMessage;
@@ -5,7 +5,7 @@ import DebugModule from "debug";
5
5
  import { DeduplicatedLoadSubset, and } from "@tanstack/db";
6
6
  import { StreamAbortedError, ExpectedNumberInAwaitTxIdError, TimeoutWaitingForTxIdError, TimeoutWaitingForMatchError } from "./errors.js";
7
7
  import { compileSQL } from "./sql-compiler.js";
8
- import { isMoveOutMessage, getTagLength, addTagToIndex, removeTagFromIndex, findRowsMatchingPattern, tagMatchesPattern } from "./tag-index.js";
8
+ import { isMoveOutMessage, isMoveInMessage, deriveDisjunctPositions, getTagLength, addTagToIndex, parseTag, removeTagFromIndex, findRowsMatchingPattern, rowVisible, tagMatchesPattern } from "./tag-index.js";
9
9
  const debug = DebugModule.debug(`ts/db:electric`);
10
10
  const ELECTRIC_TEST_HOOKS = /* @__PURE__ */ Symbol(`electricTestHooks`);
11
11
  function isUpToDateMessage(message) {
@@ -399,18 +399,20 @@ function createElectricSync(shapeOptions, options) {
399
399
  const MAX_BATCH_MESSAGES = 1e3;
400
400
  const relationSchema = new Store(void 0);
401
401
  const tagCache = /* @__PURE__ */ new Map();
402
- const parseTag = (tag) => {
402
+ const parseTag$1 = (tag) => {
403
403
  const cachedTag = tagCache.get(tag);
404
404
  if (cachedTag) {
405
405
  return cachedTag;
406
406
  }
407
- const parsedTag = tag.split(`|`);
407
+ const parsedTag = parseTag(tag);
408
408
  tagCache.set(tag, parsedTag);
409
409
  return parsedTag;
410
410
  };
411
411
  const rowTagSets = /* @__PURE__ */ new Map();
412
412
  const tagIndex = [];
413
413
  let tagLength = void 0;
414
+ const rowActiveConditions = /* @__PURE__ */ new Map();
415
+ let disjunctPositions = void 0;
414
416
  const initializeTagIndex = (length) => {
415
417
  if (tagIndex.length < length) {
416
418
  for (let i = tagIndex.length; i < length; i++) {
@@ -420,7 +422,7 @@ function createElectricSync(shapeOptions, options) {
420
422
  };
421
423
  const addTagsToRow = (tags, rowId, rowTagSet) => {
422
424
  for (const tag of tags) {
423
- const parsedTag = parseTag(tag);
425
+ const parsedTag = parseTag$1(tag);
424
426
  if (tagLength === void 0) {
425
427
  tagLength = getTagLength(parsedTag);
426
428
  initializeTagIndex(tagLength);
@@ -441,29 +443,38 @@ function createElectricSync(shapeOptions, options) {
441
443
  return;
442
444
  }
443
445
  for (const tag of removedTags) {
444
- const parsedTag = parseTag(tag);
446
+ const parsedTag = parseTag$1(tag);
445
447
  rowTagSet.delete(tag);
446
448
  removeTagFromIndex(parsedTag, rowId, tagIndex, tagLength);
447
449
  tagCache.delete(tag);
448
450
  }
449
451
  };
450
- const processTagsForChangeMessage = (tags, removedTags, rowId) => {
452
+ const processTagsForChangeMessage = (tags, removedTags, rowId, activeConditions) => {
451
453
  if (!rowTagSets.has(rowId)) {
452
454
  rowTagSets.set(rowId, /* @__PURE__ */ new Set());
453
455
  }
454
456
  const rowTagSet = rowTagSets.get(rowId);
455
457
  if (tags) {
456
458
  addTagsToRow(tags, rowId, rowTagSet);
459
+ if (disjunctPositions === void 0) {
460
+ const parsedTags = tags.map(parseTag$1);
461
+ disjunctPositions = deriveDisjunctPositions(parsedTags);
462
+ }
457
463
  }
458
464
  if (removedTags) {
459
465
  removeTagsFromRow(removedTags, rowId, rowTagSet);
460
466
  }
467
+ if (activeConditions && activeConditions.length > 0) {
468
+ rowActiveConditions.set(rowId, [...activeConditions]);
469
+ }
461
470
  return rowTagSet;
462
471
  };
463
472
  const clearTagTrackingState = () => {
464
473
  rowTagSets.clear();
465
474
  tagIndex.length = 0;
466
475
  tagLength = void 0;
476
+ rowActiveConditions.clear();
477
+ disjunctPositions = void 0;
467
478
  };
468
479
  const clearTagsForRow = (rowId) => {
469
480
  if (tagLength === void 0) {
@@ -474,7 +485,7 @@ function createElectricSync(shapeOptions, options) {
474
485
  return;
475
486
  }
476
487
  for (const tag of rowTagSet) {
477
- const parsedTag = parseTag(tag);
488
+ const parsedTag = parseTag$1(tag);
478
489
  const currentTagLength = getTagLength(parsedTag);
479
490
  if (currentTagLength === tagLength) {
480
491
  removeTagFromIndex(parsedTag, rowId, tagIndex, tagLength);
@@ -482,14 +493,30 @@ function createElectricSync(shapeOptions, options) {
482
493
  tagCache.delete(tag);
483
494
  }
484
495
  rowTagSets.delete(rowId);
496
+ rowActiveConditions.delete(rowId);
485
497
  };
486
498
  const removeMatchingTagsFromRow = (rowId, pattern) => {
487
499
  const rowTagSet = rowTagSets.get(rowId);
488
500
  if (!rowTagSet) {
489
501
  return false;
490
502
  }
503
+ const activeConditions = rowActiveConditions.get(rowId);
504
+ if (activeConditions && disjunctPositions) {
505
+ activeConditions[pattern.pos] = false;
506
+ if (!rowVisible(activeConditions, disjunctPositions)) {
507
+ for (const tag of rowTagSet) {
508
+ const parsedTag = parseTag$1(tag);
509
+ removeTagFromIndex(parsedTag, rowId, tagIndex, tagLength);
510
+ tagCache.delete(tag);
511
+ }
512
+ rowTagSets.delete(rowId);
513
+ rowActiveConditions.delete(rowId);
514
+ return true;
515
+ }
516
+ return false;
517
+ }
491
518
  for (const tag of rowTagSet) {
492
- const parsedTag = parseTag(tag);
519
+ const parsedTag = parseTag$1(tag);
493
520
  if (tagMatchesPattern(parsedTag, pattern)) {
494
521
  rowTagSet.delete(tag);
495
522
  removeTagFromIndex(parsedTag, rowId, tagIndex, tagLength);
@@ -526,6 +553,23 @@ function createElectricSync(shapeOptions, options) {
526
553
  }
527
554
  return txStarted;
528
555
  };
556
+ const processMoveInEvent = (patterns) => {
557
+ if (tagLength === void 0) {
558
+ debug(
559
+ `${collectionId ? `[${collectionId}] ` : ``}Received move-in message but no tag length set yet, ignoring`
560
+ );
561
+ return;
562
+ }
563
+ for (const pattern of patterns) {
564
+ const affectedRowIds = findRowsMatchingPattern(pattern, tagIndex);
565
+ for (const rowId of affectedRowIds) {
566
+ const activeConditions = rowActiveConditions.get(rowId);
567
+ if (activeConditions) {
568
+ activeConditions[pattern.pos] = true;
569
+ }
570
+ }
571
+ }
572
+ };
529
573
  const getSyncMetadata = () => {
530
574
  const schema = relationSchema.state || `public`;
531
575
  return {
@@ -678,6 +722,7 @@ You can provide an 'onError' handler on the shapeOptions to handle this error, a
678
722
  const tags = changeMessage.headers.tags;
679
723
  const removedTags = changeMessage.headers.removed_tags;
680
724
  const hasTags = tags || removedTags;
725
+ const activeConditions = changeMessage.headers.active_conditions;
681
726
  const rowId = collection.getKeyFromItem(changeMessage.value);
682
727
  const operation = changeMessage.headers.operation;
683
728
  const isDelete = operation === `delete`;
@@ -690,7 +735,12 @@ You can provide an 'onError' handler on the shapeOptions to handle this error, a
690
735
  if (isDelete) {
691
736
  clearTagsForRow(rowId);
692
737
  } else if (hasTags) {
693
- processTagsForChangeMessage(tags, removedTags, rowId);
738
+ processTagsForChangeMessage(
739
+ tags,
740
+ removedTags,
741
+ rowId,
742
+ activeConditions
743
+ );
694
744
  }
695
745
  write({
696
746
  type: isDuplicateInsert ? `update` : operation,
@@ -719,7 +769,7 @@ You can provide an 'onError' handler on the shapeOptions to handle this error, a
719
769
  let commitPoint = null;
720
770
  batchCommitted.setState(() => false);
721
771
  for (const message of messages) {
722
- if (isChangeMessage(message) || isMoveOutMessage(message)) {
772
+ if (isChangeMessage(message) || isMoveOutMessage(message) || isMoveInMessage(message)) {
723
773
  currentBatchMessages.setState((currentBuffer) => {
724
774
  const newBuffer = [...currentBuffer, message];
725
775
  if (newBuffer.length > MAX_BATCH_MESSAGES) {
@@ -782,6 +832,12 @@ You can provide an 'onError' handler on the shapeOptions to handle this error, a
782
832
  transactionStarted
783
833
  );
784
834
  }
835
+ } else if (isMoveInMessage(message)) {
836
+ if (isBufferingInitialSync() && !transactionStarted) {
837
+ bufferedMessages.push(message);
838
+ } else {
839
+ processMoveInEvent(message.headers.patterns);
840
+ }
785
841
  } else if (isMustRefetchMessage(message)) {
786
842
  debug(
787
843
  `${collectionId ? `[${collectionId}] ` : ``}Received must-refetch message, starting transaction with truncate`
@@ -826,6 +882,8 @@ You can provide an 'onError' handler on the shapeOptions to handle this error, a
826
882
  write,
827
883
  transactionStarted
828
884
  );
885
+ } else if (isMoveInMessage(bufferedMsg)) {
886
+ processMoveInEvent(bufferedMsg.headers.patterns);
829
887
  }
830
888
  }
831
889
  stageResumeMetadata();