@workglow/storage 0.0.77 → 0.0.79

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 CHANGED
@@ -327,9 +327,15 @@ const userCount = await userRepo.size();
327
327
  const engineeringUsers = await userRepo.search({ department: "Engineering" });
328
328
  const adultUsers = await userRepo.search({ age: 25 }); // Exact match
329
329
 
330
- // Delete by search criteria
331
- await userRepo.deleteSearch("department", "Sales", "=");
332
- await userRepo.deleteSearch("age", 65, ">="); // Delete users 65 and older
330
+ // Delete by search criteria (supports multiple columns)
331
+ await userRepo.deleteSearch({ department: "Sales" }); // Equality
332
+ await userRepo.deleteSearch({ age: { value: 65, operator: ">=" } }); // Delete users 65 and older
333
+
334
+ // Multiple criteria (AND logic)
335
+ await userRepo.deleteSearch({
336
+ department: "Sales",
337
+ age: { value: 30, operator: "<" },
338
+ }); // Delete young Sales employees
333
339
  ```
334
340
 
335
341
  #### Environment-Specific Tabular Storage
@@ -629,11 +635,7 @@ interface ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>
629
635
 
630
636
  // Search operations
631
637
  search(criteria: Partial<Entity>): Promise<Entity[] | undefined>;
632
- deleteSearch(
633
- column: keyof Entity,
634
- value: any,
635
- operator: "=" | "<" | "<=" | ">" | ">="
636
- ): Promise<void>;
638
+ deleteSearch(criteria: DeleteSearchCriteria<Entity>): Promise<void>;
637
639
 
638
640
  // Event handling
639
641
  on(event: "put" | "get" | "search" | "delete" | "clearall", callback: Function): void;
@@ -644,6 +646,37 @@ interface ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>
644
646
  }
645
647
  ```
646
648
 
649
+ #### DeleteSearchCriteria<Entity>
650
+
651
+ The `deleteSearch` method accepts a criteria object that supports multiple columns with optional comparison operators:
652
+
653
+ ```typescript
654
+ // Type definitions
655
+ type SearchOperator = "=" | "<" | "<=" | ">" | ">=";
656
+
657
+ interface SearchCondition<T> {
658
+ readonly value: T;
659
+ readonly operator: SearchOperator;
660
+ }
661
+
662
+ type DeleteSearchCriteria<Entity> = {
663
+ readonly [K in keyof Entity]?: Entity[K] | SearchCondition<Entity[K]>;
664
+ };
665
+
666
+ // Usage examples
667
+ // Equality match (direct value)
668
+ await repo.deleteSearch({ category: "electronics" });
669
+
670
+ // With comparison operator
671
+ await repo.deleteSearch({ createdAt: { value: date, operator: "<" } });
672
+
673
+ // Multiple criteria (AND logic)
674
+ await repo.deleteSearch({
675
+ category: "electronics",
676
+ value: { value: 100, operator: ">=" },
677
+ });
678
+ ```
679
+
647
680
  ### IQueueStorage<Input, Output>
648
681
 
649
682
  Core interface for job queue storage:
package/dist/browser.js CHANGED
@@ -7,6 +7,11 @@ import {
7
7
  makeFingerprint as makeFingerprint2
8
8
  } from "@workglow/util";
9
9
 
10
+ // src/tabular/ITabularRepository.ts
11
+ function isSearchCondition(value) {
12
+ return typeof value === "object" && value !== null && "value" in value && "operator" in value && typeof value.operator === "string";
13
+ }
14
+
10
15
  // src/tabular/TabularRepository.ts
11
16
  import {
12
17
  createServiceToken,
@@ -258,24 +263,50 @@ class InMemoryTabularRepository extends TabularRepository {
258
263
  async size() {
259
264
  return this.values.size;
260
265
  }
261
- async deleteSearch(column, value, operator = "=") {
262
- const entries = this.values.entries();
266
+ async deleteSearch(criteria) {
267
+ const criteriaKeys = Object.keys(criteria);
268
+ if (criteriaKeys.length === 0) {
269
+ return;
270
+ }
271
+ const entries = Array.from(this.values.entries());
263
272
  const entriesToDelete = entries.filter(([_, entity]) => {
264
- const columnValue = entity[column];
265
- switch (operator) {
266
- case "=":
267
- return columnValue === value;
268
- case "<":
269
- return columnValue !== null && columnValue !== undefined && columnValue < value;
270
- case "<=":
271
- return columnValue !== null && columnValue !== undefined && columnValue <= value;
272
- case ">":
273
- return columnValue !== null && columnValue !== undefined && columnValue > value;
274
- case ">=":
275
- return columnValue !== null && columnValue !== undefined && columnValue >= value;
276
- default:
277
- return false;
273
+ for (const column of criteriaKeys) {
274
+ const criterion = criteria[column];
275
+ const columnValue = entity[column];
276
+ if (isSearchCondition(criterion)) {
277
+ const { value, operator } = criterion;
278
+ const v = value;
279
+ const cv = columnValue;
280
+ switch (operator) {
281
+ case "=":
282
+ if (cv !== v)
283
+ return false;
284
+ break;
285
+ case "<":
286
+ if (cv === null || cv === undefined || !(cv < v))
287
+ return false;
288
+ break;
289
+ case "<=":
290
+ if (cv === null || cv === undefined || !(cv <= v))
291
+ return false;
292
+ break;
293
+ case ">":
294
+ if (cv === null || cv === undefined || !(cv > v))
295
+ return false;
296
+ break;
297
+ case ">=":
298
+ if (cv === null || cv === undefined || !(cv >= v))
299
+ return false;
300
+ break;
301
+ default:
302
+ return false;
303
+ }
304
+ } else {
305
+ if (columnValue !== criterion)
306
+ return false;
307
+ }
278
308
  }
309
+ return true;
279
310
  });
280
311
  for (const [id, entity] of entriesToDelete) {
281
312
  this.values.delete(id);
@@ -417,10 +448,10 @@ class CachedTabularRepository extends TabularRepository {
417
448
  await this.initializeCache();
418
449
  return await this.durable.size();
419
450
  }
420
- async deleteSearch(column, value, operator = "=") {
451
+ async deleteSearch(criteria) {
421
452
  await this.initializeCache();
422
- await this.durable.deleteSearch(column, value, operator);
423
- await this.cache.deleteSearch(column, value, operator);
453
+ await this.durable.deleteSearch(criteria);
454
+ await this.cache.deleteSearch(criteria);
424
455
  }
425
456
  async invalidateCache() {
426
457
  await this.cache.deleteAll();
@@ -1731,32 +1762,76 @@ class IndexedDbTabularRepository extends TabularRepository {
1731
1762
  request.onsuccess = () => resolve(request.result);
1732
1763
  });
1733
1764
  }
1734
- async deleteSearch(column, value, operator = "=") {
1765
+ matchesCriteria(record, criteria) {
1766
+ for (const column of Object.keys(criteria)) {
1767
+ const criterion = criteria[column];
1768
+ const recordValue = record[column];
1769
+ let operator = "=";
1770
+ let value;
1771
+ if (isSearchCondition(criterion)) {
1772
+ operator = criterion.operator;
1773
+ value = criterion.value;
1774
+ } else {
1775
+ value = criterion;
1776
+ }
1777
+ if (operator !== "=" && (recordValue === null || recordValue === undefined)) {
1778
+ return false;
1779
+ }
1780
+ switch (operator) {
1781
+ case "=":
1782
+ if (recordValue !== value)
1783
+ return false;
1784
+ break;
1785
+ case "<":
1786
+ if (!(recordValue < value))
1787
+ return false;
1788
+ break;
1789
+ case "<=":
1790
+ if (!(recordValue <= value))
1791
+ return false;
1792
+ break;
1793
+ case ">":
1794
+ if (!(recordValue > value))
1795
+ return false;
1796
+ break;
1797
+ case ">=":
1798
+ if (!(recordValue >= value))
1799
+ return false;
1800
+ break;
1801
+ default:
1802
+ return false;
1803
+ }
1804
+ }
1805
+ return true;
1806
+ }
1807
+ async deleteSearch(criteria) {
1808
+ const criteriaKeys = Object.keys(criteria);
1809
+ if (criteriaKeys.length === 0) {
1810
+ return;
1811
+ }
1735
1812
  const db = await this.getDb();
1736
1813
  return new Promise(async (resolve, reject) => {
1737
1814
  try {
1738
- if (operator === "=") {
1739
- const searchKey = { [column]: value };
1740
- const recordsToDelete = await this.search(searchKey);
1741
- if (!recordsToDelete || recordsToDelete.length === 0) {
1742
- this.events.emit("delete", column);
1743
- this.hybridManager?.notifyLocalChange();
1744
- resolve();
1815
+ const transaction = db.transaction(this.table, "readwrite");
1816
+ const store = transaction.objectStore(this.table);
1817
+ transaction.oncomplete = () => {
1818
+ this.events.emit("delete", criteriaKeys[0]);
1819
+ this.hybridManager?.notifyLocalChange();
1820
+ resolve();
1821
+ };
1822
+ transaction.onerror = () => {
1823
+ reject(transaction.error);
1824
+ };
1825
+ const getAllRequest = store.getAll();
1826
+ getAllRequest.onsuccess = () => {
1827
+ const allRecords = getAllRequest.result;
1828
+ const recordsToDelete = allRecords.filter((record) => this.matchesCriteria(record, criteria));
1829
+ if (recordsToDelete.length === 0) {
1745
1830
  return;
1746
1831
  }
1747
- const transaction = db.transaction(this.table, "readwrite");
1748
- const store = transaction.objectStore(this.table);
1749
- transaction.oncomplete = () => {
1750
- this.events.emit("delete", column);
1751
- this.hybridManager?.notifyLocalChange();
1752
- resolve();
1753
- };
1754
- transaction.onerror = () => {
1755
- reject(transaction.error);
1756
- };
1757
1832
  for (const record of recordsToDelete) {
1758
- const primaryKey = this.primaryKeyColumns().reduce((key, column2) => {
1759
- key[column2] = record[column2];
1833
+ const primaryKey = this.primaryKeyColumns().reduce((key, col) => {
1834
+ key[col] = record[col];
1760
1835
  return key;
1761
1836
  }, {});
1762
1837
  const request = store.delete(this.getIndexedKey(primaryKey));
@@ -1764,56 +1839,10 @@ class IndexedDbTabularRepository extends TabularRepository {
1764
1839
  console.error("Error deleting record:", request.error);
1765
1840
  };
1766
1841
  }
1767
- } else {
1768
- const transaction = db.transaction(this.table, "readwrite");
1769
- const store = transaction.objectStore(this.table);
1770
- transaction.oncomplete = () => {
1771
- this.events.emit("delete", column);
1772
- this.hybridManager?.notifyLocalChange();
1773
- resolve();
1774
- };
1775
- transaction.onerror = () => {
1776
- reject(transaction.error);
1777
- };
1778
- const getAllRequest = store.getAll();
1779
- getAllRequest.onsuccess = () => {
1780
- const allRecords = getAllRequest.result;
1781
- const recordsToDelete = allRecords.filter((record) => {
1782
- const recordValue = record[column];
1783
- if (recordValue === null || recordValue === undefined || value === null || value === undefined) {
1784
- return false;
1785
- }
1786
- switch (operator) {
1787
- case "<":
1788
- return recordValue < value;
1789
- case "<=":
1790
- return recordValue <= value;
1791
- case ">":
1792
- return recordValue > value;
1793
- case ">=":
1794
- return recordValue >= value;
1795
- default:
1796
- return false;
1797
- }
1798
- });
1799
- if (recordsToDelete.length === 0) {
1800
- return;
1801
- }
1802
- for (const record of recordsToDelete) {
1803
- const primaryKey = this.primaryKeyColumns().reduce((key, column2) => {
1804
- key[column2] = record[column2];
1805
- return key;
1806
- }, {});
1807
- const request = store.delete(this.getIndexedKey(primaryKey));
1808
- request.onerror = () => {
1809
- console.error("Error deleting record:", request.error);
1810
- };
1811
- }
1812
- };
1813
- getAllRequest.onerror = () => {
1814
- reject(getAllRequest.error);
1815
- };
1816
- }
1842
+ };
1843
+ getAllRequest.onerror = () => {
1844
+ reject(getAllRequest.error);
1845
+ };
1817
1846
  } catch (error) {
1818
1847
  reject(error);
1819
1848
  }
@@ -1941,7 +1970,7 @@ class SharedInMemoryTabularRepository extends TabularRepository {
1941
1970
  await this.inMemoryRepo.deleteAll();
1942
1971
  break;
1943
1972
  case "DELETE_SEARCH":
1944
- await this.inMemoryRepo.deleteSearch(message.column, message.value, message.operator);
1973
+ await this.inMemoryRepo.deleteSearch(message.criteria);
1945
1974
  break;
1946
1975
  }
1947
1976
  }
@@ -2002,13 +2031,11 @@ class SharedInMemoryTabularRepository extends TabularRepository {
2002
2031
  async size() {
2003
2032
  return await this.inMemoryRepo.size();
2004
2033
  }
2005
- async deleteSearch(column, value, operator = "=") {
2006
- await this.inMemoryRepo.deleteSearch(column, value, operator);
2034
+ async deleteSearch(criteria) {
2035
+ await this.inMemoryRepo.deleteSearch(criteria);
2007
2036
  this.broadcast({
2008
2037
  type: "DELETE_SEARCH",
2009
- column: String(column),
2010
- value,
2011
- operator
2038
+ criteria
2012
2039
  });
2013
2040
  }
2014
2041
  subscribeToChanges(callback, options) {
@@ -2541,35 +2568,47 @@ class SupabaseTabularRepository extends BaseSqlTabularRepository {
2541
2568
  throw error;
2542
2569
  return count ?? 0;
2543
2570
  }
2544
- generateWhereClause(column, operator = "=") {
2545
- if (!(column in this.schema.properties)) {
2546
- throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);
2571
+ async deleteSearch(criteria) {
2572
+ const criteriaKeys = Object.keys(criteria);
2573
+ if (criteriaKeys.length === 0) {
2574
+ return;
2547
2575
  }
2548
- return `${String(column)} ${operator} $1`;
2549
- }
2550
- async deleteSearch(column, value, operator = "=") {
2551
2576
  let query = this.client.from(this.table).delete();
2552
- switch (operator) {
2553
- case "=":
2554
- query = query.eq(String(column), value);
2555
- break;
2556
- case "<":
2557
- query = query.lt(String(column), value);
2558
- break;
2559
- case "<=":
2560
- query = query.lte(String(column), value);
2561
- break;
2562
- case ">":
2563
- query = query.gt(String(column), value);
2564
- break;
2565
- case ">=":
2566
- query = query.gte(String(column), value);
2567
- break;
2577
+ for (const column of criteriaKeys) {
2578
+ if (!(column in this.schema.properties)) {
2579
+ throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);
2580
+ }
2581
+ const criterion = criteria[column];
2582
+ let operator = "=";
2583
+ let value;
2584
+ if (isSearchCondition(criterion)) {
2585
+ operator = criterion.operator;
2586
+ value = criterion.value;
2587
+ } else {
2588
+ value = criterion;
2589
+ }
2590
+ switch (operator) {
2591
+ case "=":
2592
+ query = query.eq(String(column), value);
2593
+ break;
2594
+ case "<":
2595
+ query = query.lt(String(column), value);
2596
+ break;
2597
+ case "<=":
2598
+ query = query.lte(String(column), value);
2599
+ break;
2600
+ case ">":
2601
+ query = query.gt(String(column), value);
2602
+ break;
2603
+ case ">=":
2604
+ query = query.gte(String(column), value);
2605
+ break;
2606
+ }
2568
2607
  }
2569
2608
  const { error } = await query;
2570
2609
  if (error)
2571
2610
  throw error;
2572
- this.events.emit("delete", column);
2611
+ this.events.emit("delete", criteriaKeys[0]);
2573
2612
  }
2574
2613
  convertRealtimeRow(row) {
2575
2614
  const entity = { ...row };
@@ -4078,6 +4117,7 @@ class SupabaseRateLimiterStorage {
4078
4117
  }
4079
4118
  }
4080
4119
  export {
4120
+ isSearchCondition,
4081
4121
  ensureIndexedDbTable,
4082
4122
  dropIndexedDbTable,
4083
4123
  TabularRepository,
@@ -4122,4 +4162,4 @@ export {
4122
4162
  CACHED_TABULAR_REPOSITORY
4123
4163
  };
4124
4164
 
4125
- //# debugId=8E3873F6AB0E1B4764756E2164756E21
4165
+ //# debugId=DD99166332ED2CB564756E2164756E21