@tanstack/db 0.0.6 → 0.0.8

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.
Files changed (43) hide show
  1. package/dist/cjs/collection.cjs +452 -286
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +115 -26
  4. package/dist/cjs/index.cjs +1 -1
  5. package/dist/cjs/index.d.cts +1 -1
  6. package/dist/cjs/proxy.cjs +2 -2
  7. package/dist/cjs/proxy.cjs.map +1 -1
  8. package/dist/cjs/query/compiled-query.cjs +24 -38
  9. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  10. package/dist/cjs/query/compiled-query.d.cts +2 -2
  11. package/dist/cjs/query/order-by.cjs +41 -38
  12. package/dist/cjs/query/order-by.cjs.map +1 -1
  13. package/dist/cjs/query/schema.d.cts +3 -3
  14. package/dist/cjs/transactions.cjs +7 -6
  15. package/dist/cjs/transactions.cjs.map +1 -1
  16. package/dist/cjs/transactions.d.cts +9 -9
  17. package/dist/cjs/types.d.cts +36 -22
  18. package/dist/esm/collection.d.ts +115 -26
  19. package/dist/esm/collection.js +453 -287
  20. package/dist/esm/collection.js.map +1 -1
  21. package/dist/esm/index.d.ts +1 -1
  22. package/dist/esm/index.js +2 -2
  23. package/dist/esm/proxy.js +2 -2
  24. package/dist/esm/proxy.js.map +1 -1
  25. package/dist/esm/query/compiled-query.d.ts +2 -2
  26. package/dist/esm/query/compiled-query.js +25 -39
  27. package/dist/esm/query/compiled-query.js.map +1 -1
  28. package/dist/esm/query/order-by.js +41 -38
  29. package/dist/esm/query/order-by.js.map +1 -1
  30. package/dist/esm/query/schema.d.ts +3 -3
  31. package/dist/esm/transactions.d.ts +9 -9
  32. package/dist/esm/transactions.js +7 -6
  33. package/dist/esm/transactions.js.map +1 -1
  34. package/dist/esm/types.d.ts +36 -22
  35. package/package.json +2 -2
  36. package/src/collection.ts +652 -368
  37. package/src/index.ts +1 -1
  38. package/src/proxy.ts +2 -2
  39. package/src/query/compiled-query.ts +29 -39
  40. package/src/query/order-by.ts +69 -67
  41. package/src/query/schema.ts +3 -3
  42. package/src/transactions.ts +24 -22
  43. package/src/types.ts +54 -22
@@ -1 +1 @@
1
- {"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n PendingMutation,\n TransactionConfig,\n TransactionState,\n TransactionWithMutations,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction> = []\nlet transactionStack: Array<Transaction> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\nexport function getActiveTransaction(): Transaction | undefined {\n if (transactionStack.length > 0) {\n return transactionStack.slice(-1)[0]\n } else {\n return undefined\n }\n}\n\nfunction registerTransaction(tx: Transaction) {\n transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction) {\n transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n}\n\nfunction removeFromPendingList(tx: Transaction) {\n const index = transactions.findIndex((t) => t.id === tx.id)\n if (index !== -1) {\n transactions.splice(index, 1)\n }\n}\n\nexport class Transaction {\n public id: string\n public state: TransactionState\n public mutationFn\n public mutations: Array<PendingMutation<any>>\n public isPersisted: Deferred<Transaction>\n public autoCommit: boolean\n public createdAt: Date\n public metadata: Record<string, unknown>\n public error?: {\n message: string\n error: Error\n }\n\n constructor(config: TransactionConfig) {\n this.id = config.id!\n this.mutationFn = config.mutationFn\n this.state = `pending`\n this.mutations = []\n this.isPersisted = createDeferred()\n this.autoCommit = config.autoCommit ?? true\n this.createdAt = new Date()\n this.metadata = config.metadata ?? {}\n }\n\n setState(newState: TransactionState) {\n this.state = newState\n\n if (newState === `completed` || newState === `failed`) {\n removeFromPendingList(this)\n }\n }\n\n mutate(callback: () => void): Transaction {\n if (this.state !== `pending`) {\n throw `You can no longer call .mutate() as the transaction is no longer pending`\n }\n\n registerTransaction(this)\n try {\n callback()\n } finally {\n unregisterTransaction(this)\n }\n\n if (this.autoCommit) {\n this.commit()\n }\n\n return this\n }\n\n applyMutations(mutations: Array<PendingMutation<any>>): void {\n for (const newMutation of mutations) {\n const existingIndex = this.mutations.findIndex(\n (m) => m.key === newMutation.key\n )\n\n if (existingIndex >= 0) {\n // Replace existing mutation\n this.mutations[existingIndex] = newMutation\n } else {\n // Insert new mutation\n this.mutations.push(newMutation)\n }\n }\n }\n\n rollback(config?: { isSecondaryRollback?: boolean }): Transaction {\n const isSecondaryRollback = config?.isSecondaryRollback ?? false\n if (this.state === `completed`) {\n throw `You can no longer call .rollback() as the transaction is already completed`\n }\n\n this.setState(`failed`)\n\n // See if there's any other transactions w/ mutations on the same ids\n // and roll them back as well.\n if (!isSecondaryRollback) {\n const mutationIds = new Set()\n this.mutations.forEach((m) => mutationIds.add(m.key))\n for (const t of transactions) {\n t.state === `pending` &&\n t.mutations.some((m) => mutationIds.has(m.key)) &&\n t.rollback({ isSecondaryRollback: true })\n }\n }\n\n // Reject the promise\n this.isPersisted.reject(this.error?.error)\n this.touchCollection()\n\n return this\n }\n\n // Tell collection that something has changed with the transaction\n touchCollection(): void {\n const hasCalled = new Set()\n for (const mutation of this.mutations) {\n if (!hasCalled.has(mutation.collection.id)) {\n mutation.collection.transactions.setState((state) => state)\n mutation.collection.commitPendingTransactions()\n hasCalled.add(mutation.collection.id)\n }\n }\n }\n\n async commit(): Promise<Transaction> {\n if (this.state !== `pending`) {\n throw `You can no longer call .commit() as the transaction is no longer pending`\n }\n\n this.setState(`persisting`)\n\n if (this.mutations.length === 0) {\n this.setState(`completed`)\n\n return this\n }\n\n // Run mutationFn\n try {\n // At this point we know there's at least one mutation\n // Use type assertion to tell TypeScript about this guarantee\n const transactionWithMutations =\n this as unknown as TransactionWithMutations\n await this.mutationFn({ transaction: transactionWithMutations })\n\n this.setState(`completed`)\n this.touchCollection()\n\n this.isPersisted.resolve(this)\n } catch (error) {\n // Update transaction with error information\n this.error = {\n message: error instanceof Error ? error.message : String(error),\n error: error instanceof Error ? error : new Error(String(error)),\n }\n\n // rollback the transaction\n return this.rollback()\n }\n\n return this\n }\n}\n"],"names":["createDeferred"],"mappings":"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"}
1
+ {"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n MutationFn,\n PendingMutation,\n TransactionConfig,\n TransactionState,\n TransactionWithMutations,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction<any>> = []\nlet transactionStack: Array<Transaction<any>> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\nexport function getActiveTransaction(): Transaction | undefined {\n if (transactionStack.length > 0) {\n return transactionStack.slice(-1)[0]\n } else {\n return undefined\n }\n}\n\nfunction registerTransaction(tx: Transaction<any>) {\n transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction<any>) {\n transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n}\n\nfunction removeFromPendingList(tx: Transaction<any>) {\n const index = transactions.findIndex((t) => t.id === tx.id)\n if (index !== -1) {\n transactions.splice(index, 1)\n }\n}\n\nexport class Transaction<T extends object = Record<string, unknown>> {\n public id: string\n public state: TransactionState\n public mutationFn: MutationFn<T>\n public mutations: Array<PendingMutation<T>>\n public isPersisted: Deferred<Transaction<T>>\n public autoCommit: boolean\n public createdAt: Date\n public metadata: Record<string, unknown>\n public error?: {\n message: string\n error: Error\n }\n\n constructor(config: TransactionConfig<T>) {\n this.id = config.id!\n this.mutationFn = config.mutationFn\n this.state = `pending`\n this.mutations = []\n this.isPersisted = createDeferred<Transaction<T>>()\n this.autoCommit = config.autoCommit ?? true\n this.createdAt = new Date()\n this.metadata = config.metadata ?? {}\n }\n\n setState(newState: TransactionState) {\n this.state = newState\n\n if (newState === `completed` || newState === `failed`) {\n removeFromPendingList(this)\n }\n }\n\n mutate(callback: () => void): Transaction<T> {\n if (this.state !== `pending`) {\n throw `You can no longer call .mutate() as the transaction is no longer pending`\n }\n\n registerTransaction(this)\n try {\n callback()\n } finally {\n unregisterTransaction(this)\n }\n\n if (this.autoCommit) {\n this.commit()\n }\n\n return this\n }\n\n applyMutations(mutations: Array<PendingMutation<any>>): void {\n for (const newMutation of mutations) {\n const existingIndex = this.mutations.findIndex(\n (m) => m.globalKey === newMutation.globalKey\n )\n\n if (existingIndex >= 0) {\n // Replace existing mutation\n this.mutations[existingIndex] = newMutation\n } else {\n // Insert new mutation\n this.mutations.push(newMutation)\n }\n }\n }\n\n rollback(config?: { isSecondaryRollback?: boolean }): Transaction<T> {\n const isSecondaryRollback = config?.isSecondaryRollback ?? false\n if (this.state === `completed`) {\n throw `You can no longer call .rollback() as the transaction is already completed`\n }\n\n this.setState(`failed`)\n\n // See if there's any other transactions w/ mutations on the same ids\n // and roll them back as well.\n if (!isSecondaryRollback) {\n const mutationIds = new Set()\n this.mutations.forEach((m) => mutationIds.add(m.globalKey))\n for (const t of transactions) {\n t.state === `pending` &&\n t.mutations.some((m) => mutationIds.has(m.globalKey)) &&\n t.rollback({ isSecondaryRollback: true })\n }\n }\n\n // Reject the promise\n this.isPersisted.reject(this.error?.error)\n this.touchCollection()\n\n return this\n }\n\n // Tell collection that something has changed with the transaction\n touchCollection(): void {\n const hasCalled = new Set()\n for (const mutation of this.mutations) {\n if (!hasCalled.has(mutation.collection.id)) {\n mutation.collection.onTransactionStateChange()\n mutation.collection.commitPendingTransactions()\n hasCalled.add(mutation.collection.id)\n }\n }\n }\n\n async commit(): Promise<Transaction<T>> {\n if (this.state !== `pending`) {\n throw `You can no longer call .commit() as the transaction is no longer pending`\n }\n\n this.setState(`persisting`)\n\n if (this.mutations.length === 0) {\n this.setState(`completed`)\n\n return this\n }\n\n // Run mutationFn\n try {\n // At this point we know there's at least one mutation\n // We've already verified mutations is non-empty, so this cast is safe\n // Use a direct type assertion instead of object spreading to preserve the original type\n await this.mutationFn({\n transaction: this as unknown as TransactionWithMutations<T>,\n })\n\n this.setState(`completed`)\n this.touchCollection()\n\n this.isPersisted.resolve(this)\n } catch (error) {\n // Update transaction with error information\n this.error = {\n message: error instanceof Error ? error.message : String(error),\n error: error instanceof Error ? error : new Error(String(error)),\n }\n\n // rollback the transaction\n return this.rollback()\n }\n\n return this\n }\n}\n"],"names":["createDeferred"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import { Deferred } from './deferred.cjs';
2
- import { PendingMutation, TransactionConfig, TransactionState } from './types.cjs';
2
+ import { MutationFn, PendingMutation, TransactionConfig, TransactionState } from './types.cjs';
3
3
  export declare function createTransaction(config: TransactionConfig): Transaction;
4
4
  export declare function getActiveTransaction(): Transaction | undefined;
5
- export declare class Transaction {
5
+ export declare class Transaction<T extends object = Record<string, unknown>> {
6
6
  id: string;
7
7
  state: TransactionState;
8
- mutationFn: import('./types.cjs').MutationFn;
9
- mutations: Array<PendingMutation<any>>;
10
- isPersisted: Deferred<Transaction>;
8
+ mutationFn: MutationFn<T>;
9
+ mutations: Array<PendingMutation<T>>;
10
+ isPersisted: Deferred<Transaction<T>>;
11
11
  autoCommit: boolean;
12
12
  createdAt: Date;
13
13
  metadata: Record<string, unknown>;
@@ -15,13 +15,13 @@ export declare class Transaction {
15
15
  message: string;
16
16
  error: Error;
17
17
  };
18
- constructor(config: TransactionConfig);
18
+ constructor(config: TransactionConfig<T>);
19
19
  setState(newState: TransactionState): void;
20
- mutate(callback: () => void): Transaction;
20
+ mutate(callback: () => void): Transaction<T>;
21
21
  applyMutations(mutations: Array<PendingMutation<any>>): void;
22
22
  rollback(config?: {
23
23
  isSecondaryRollback?: boolean;
24
- }): Transaction;
24
+ }): Transaction<T>;
25
25
  touchCollection(): void;
26
- commit(): Promise<Transaction>;
26
+ commit(): Promise<Transaction<T>>;
27
27
  }
@@ -3,42 +3,55 @@ import { Collection } from './collection.cjs';
3
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import { Transaction } from './transactions.cjs';
5
5
  export type TransactionState = `pending` | `persisting` | `completed` | `failed`;
6
+ /**
7
+ * Represents a utility function that can be attached to a collection
8
+ */
9
+ export type Fn = (...args: Array<any>) => any;
10
+ /**
11
+ * A record of utility functions that can be attached to a collection
12
+ */
13
+ export type UtilsRecord = Record<string, Fn>;
6
14
  /**
7
15
  * Represents a pending mutation within a transaction
8
16
  * Contains information about the original and modified data, as well as metadata
9
17
  */
10
18
  export interface PendingMutation<T extends object = Record<string, unknown>> {
11
19
  mutationId: string;
12
- original: Record<string, unknown>;
13
- modified: Record<string, unknown>;
14
- changes: Record<string, unknown>;
20
+ original: Partial<T>;
21
+ modified: T;
22
+ changes: Partial<T>;
23
+ globalKey: string;
15
24
  key: any;
16
25
  type: OperationType;
17
26
  metadata: unknown;
18
27
  syncMetadata: Record<string, unknown>;
19
28
  createdAt: Date;
20
29
  updatedAt: Date;
21
- collection: Collection<T>;
30
+ collection: Collection<T, any>;
22
31
  }
23
32
  /**
24
33
  * Configuration options for creating a new transaction
25
34
  */
26
- export type MutationFnParams = {
27
- transaction: Transaction;
35
+ export type MutationFnParams<T extends object = Record<string, unknown>> = {
36
+ transaction: TransactionWithMutations<T>;
28
37
  };
29
- export type MutationFn = (params: MutationFnParams) => Promise<any>;
38
+ export type MutationFn<T extends object = Record<string, unknown>> = (params: MutationFnParams<T>) => Promise<any>;
39
+ /**
40
+ * Represents a non-empty array (at least one element)
41
+ */
42
+ export type NonEmptyArray<T> = [T, ...Array<T>];
30
43
  /**
31
44
  * Utility type for a Transaction with at least one mutation
32
45
  * This is used internally by the Transaction.commit method
33
46
  */
34
- export type TransactionWithMutations<T extends object = Record<string, unknown>> = Transaction & {
35
- mutations: [PendingMutation<T>, ...Array<PendingMutation<T>>];
47
+ export type TransactionWithMutations<T extends object = Record<string, unknown>> = Transaction<T> & {
48
+ mutations: NonEmptyArray<PendingMutation<T>>;
36
49
  };
37
- export interface TransactionConfig {
50
+ export interface TransactionConfig<T extends object = Record<string, unknown>> {
38
51
  /** Unique identifier for the transaction */
39
52
  id?: string;
40
53
  autoCommit?: boolean;
41
- mutationFn: MutationFn;
54
+ mutationFn: MutationFn<T>;
42
55
  /** Custom metadata to associate with the transaction */
43
56
  metadata?: Record<string, unknown>;
44
57
  }
@@ -48,9 +61,9 @@ type Value<TExtensions = never> = string | number | boolean | bigint | null | TE
48
61
  };
49
62
  export type Row<TExtensions = never> = Record<string, Value<TExtensions>>;
50
63
  export type OperationType = `insert` | `update` | `delete`;
51
- export interface SyncConfig<T extends object = Record<string, unknown>> {
64
+ export interface SyncConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
52
65
  sync: (params: {
53
- collection: Collection<T>;
66
+ collection: Collection<T, TKey>;
54
67
  begin: () => void;
55
68
  write: (message: Omit<ChangeMessage<T>, `key`>) => void;
56
69
  commit: () => void;
@@ -61,8 +74,8 @@ export interface SyncConfig<T extends object = Record<string, unknown>> {
61
74
  */
62
75
  getSyncMetadata?: () => Record<string, unknown>;
63
76
  }
64
- export interface ChangeMessage<T extends object = Record<string, unknown>> {
65
- key: any;
77
+ export interface ChangeMessage<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
78
+ key: TKey;
66
79
  value: T;
67
80
  previousValue?: T;
68
81
  type: OperationType;
@@ -93,9 +106,9 @@ export interface OperationConfig {
93
106
  export interface InsertConfig {
94
107
  metadata?: Record<string, unknown>;
95
108
  }
96
- export interface CollectionConfig<T extends object = Record<string, unknown>> {
109
+ export interface CollectionConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
97
110
  id?: string;
98
- sync: SyncConfig<T>;
111
+ sync: SyncConfig<T, TKey>;
99
112
  schema?: StandardSchema<T>;
100
113
  /**
101
114
  * Function to extract the ID from an object
@@ -104,27 +117,27 @@ export interface CollectionConfig<T extends object = Record<string, unknown>> {
104
117
  * @returns The ID string for the item
105
118
  * @example
106
119
  * // For a collection with a 'uuid' field as the primary key
107
- * getId: (item) => item.uuid
120
+ * getKey: (item) => item.uuid
108
121
  */
109
- getId: (item: T) => any;
122
+ getKey: (item: T) => TKey;
110
123
  /**
111
124
  * Optional asynchronous handler function called before an insert operation
112
125
  * @param params Object containing transaction and mutation information
113
126
  * @returns Promise resolving to any value
114
127
  */
115
- onInsert?: MutationFn;
128
+ onInsert?: MutationFn<T>;
116
129
  /**
117
130
  * Optional asynchronous handler function called before an update operation
118
131
  * @param params Object containing transaction and mutation information
119
132
  * @returns Promise resolving to any value
120
133
  */
121
- onUpdate?: MutationFn;
134
+ onUpdate?: MutationFn<T>;
122
135
  /**
123
136
  * Optional asynchronous handler function called before a delete operation
124
137
  * @param params Object containing transaction and mutation information
125
138
  * @returns Promise resolving to any value
126
139
  */
127
- onDelete?: MutationFn;
140
+ onDelete?: MutationFn<T>;
128
141
  }
129
142
  export type ChangesPayload<T extends object = Record<string, unknown>> = Array<ChangeMessage<T>>;
130
143
  /**
@@ -151,3 +164,4 @@ export type KeyedNamespacedRow = [unknown, NamespacedRow];
151
164
  * a `select` clause.
152
165
  */
153
166
  export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>;
167
+ export type ChangeListener<T extends object = Record<string, unknown>, TKey extends string | number = string | number> = (changes: Array<ChangeMessage<T, TKey>>) => void;
@@ -1,16 +1,28 @@
1
- import { Derived, Store } from '@tanstack/store';
1
+ import { Store } from '@tanstack/store';
2
2
  import { Transaction } from './transactions.js';
3
3
  import { SortedMap } from './SortedMap.js';
4
- import { ChangeMessage, CollectionConfig, InsertConfig, OperationConfig, OptimisticChangeMessage, Transaction as TransactionType } from './types.js';
5
- export declare const collectionsStore: Store<Map<string, Collection<any>>, (cb: Map<string, Collection<any>>) => Map<string, Collection<any>>>;
4
+ import { ChangeListener, ChangeMessage, CollectionConfig, Fn, InsertConfig, OperationConfig, Transaction as TransactionType, UtilsRecord } from './types.js';
5
+ export declare const collectionsStore: Map<string, CollectionImpl<any, any>>;
6
+ /**
7
+ * Enhanced Collection interface that includes both data type T and utilities TUtils
8
+ * @template T - The type of items in the collection
9
+ * @template TUtils - The utilities record type
10
+ */
11
+ export interface Collection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}> extends CollectionImpl<T, TKey> {
12
+ readonly utils: TUtils;
13
+ }
6
14
  /**
7
15
  * Creates a new Collection instance with the given configuration
8
16
  *
9
17
  * @template T - The type of items in the collection
10
- * @param config - Configuration for the collection, including id and sync
11
- * @returns A new Collection instance
18
+ * @template TKey - The type of the key for the collection
19
+ * @template TUtils - The utilities record type
20
+ * @param options - Collection options with optional utilities
21
+ * @returns A new Collection with utilities exposed both at top level and under .utils
12
22
  */
13
- export declare function createCollection<T extends object = Record<string, unknown>>(config: CollectionConfig<T>): Collection<T>;
23
+ export declare function createCollection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<T, TKey> & {
24
+ utils?: TUtils;
25
+ }): Collection<T, TKey, TUtils>;
14
26
  /**
15
27
  * Preloads a collection with the given configuration
16
28
  * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)
@@ -37,7 +49,7 @@ export declare function createCollection<T extends object = Record<string, unkno
37
49
  * @param config - Configuration for the collection, including id and sync
38
50
  * @returns Promise that resolves when the initial sync is finished
39
51
  */
40
- export declare function preloadCollection<T extends object = Record<string, unknown>>(config: CollectionConfig<T>): Promise<Collection<T>>;
52
+ export declare function preloadCollection<T extends object = Record<string, unknown>, TKey extends string | number = string | number>(config: CollectionConfig<T, TKey>): Promise<CollectionImpl<T, TKey>>;
41
53
  /**
42
54
  * Custom error class for schema validation errors
43
55
  */
@@ -52,17 +64,19 @@ export declare class SchemaValidationError extends Error {
52
64
  path?: ReadonlyArray<string | number | symbol>;
53
65
  }>, message?: string);
54
66
  }
55
- export declare class Collection<T extends object = Record<string, unknown>> {
56
- transactions: Store<SortedMap<string, TransactionType>>;
57
- optimisticOperations: Derived<Array<OptimisticChangeMessage<T>>>;
58
- derivedState: Derived<Map<string, T>>;
59
- derivedArray: Derived<Array<T>>;
60
- derivedChanges: Derived<Array<ChangeMessage<T>>>;
61
- syncedData: Store<Map<string, T>, (cb: Map<string, T>) => Map<string, T>>;
62
- syncedMetadata: Store<Map<string, unknown>, (cb: Map<string, unknown>) => Map<string, unknown>>;
67
+ export declare class CollectionImpl<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
68
+ transactions: SortedMap<string, Transaction<any>>;
69
+ syncedData: Map<TKey, T>;
70
+ syncedMetadata: Map<TKey, unknown>;
71
+ derivedUpserts: Map<TKey, T>;
72
+ derivedDeletes: Set<TKey>;
73
+ private _size;
74
+ private changeListeners;
75
+ private changeKeyListeners;
76
+ utils: Record<string, Fn>;
63
77
  private pendingSyncedTransactions;
64
78
  private syncedKeys;
65
- config: CollectionConfig<T>;
79
+ config: CollectionConfig<T, TKey>;
66
80
  private hasReceivedFirstCommit;
67
81
  private onFirstCommitCallbacks;
68
82
  /**
@@ -78,15 +92,59 @@ export declare class Collection<T extends object = Record<string, unknown>> {
78
92
  * @param config - Configuration object for the collection
79
93
  * @throws Error if sync config is missing
80
94
  */
81
- constructor(config: CollectionConfig<T>);
95
+ constructor(config: CollectionConfig<T, TKey>);
96
+ /**
97
+ * Recompute optimistic state from active transactions
98
+ */
99
+ private recomputeOptimisticState;
100
+ /**
101
+ * Calculate the current size based on synced data and optimistic changes
102
+ */
103
+ private calculateSize;
104
+ /**
105
+ * Collect events for optimistic changes
106
+ */
107
+ private collectOptimisticChanges;
108
+ /**
109
+ * Get the previous value for a key given previous optimistic state
110
+ */
111
+ private getPreviousValue;
112
+ /**
113
+ * Emit multiple events at once to all listeners
114
+ */
115
+ private emitEvents;
116
+ /**
117
+ * Get the current value for a key (virtual derived state)
118
+ */
119
+ get(key: TKey): T | undefined;
120
+ /**
121
+ * Check if a key exists in the collection (virtual derived state)
122
+ */
123
+ has(key: TKey): boolean;
124
+ /**
125
+ * Get the current size of the collection (cached)
126
+ */
127
+ get size(): number;
128
+ /**
129
+ * Get all keys (virtual derived state)
130
+ */
131
+ keys(): IterableIterator<TKey>;
132
+ /**
133
+ * Get all values (virtual derived state)
134
+ */
135
+ values(): IterableIterator<T>;
136
+ /**
137
+ * Get all entries (virtual derived state)
138
+ */
139
+ entries(): IterableIterator<[TKey, T]>;
82
140
  /**
83
141
  * Attempts to commit pending synced transactions if there are no active transactions
84
142
  * This method processes operations from pending transactions and applies them to the synced data
85
143
  */
86
144
  commitPendingTransactions: () => void;
87
145
  private ensureStandardSchema;
88
- private getKeyFromId;
89
- generateObjectKey(id: any, item: any): string;
146
+ getKeyFromItem(item: T): TKey;
147
+ generateGlobalKey(key: any, item: any): string;
90
148
  private validateData;
91
149
  /**
92
150
  * Inserts one or more items into the collection
@@ -107,7 +165,7 @@ export declare class Collection<T extends object = Record<string, unknown>> {
107
165
  * // Insert with custom key
108
166
  * insert({ text: "Buy groceries" }, { key: "grocery-task" })
109
167
  */
110
- insert: (data: T | Array<T>, config?: InsertConfig) => Transaction;
168
+ insert: (data: T | Array<T>, config?: InsertConfig) => Transaction<Record<string, unknown>>;
111
169
  /**
112
170
  * Updates one or more items in the collection using a callback function
113
171
  * @param items - Single item/key or array of items/keys to update
@@ -146,8 +204,10 @@ export declare class Collection<T extends object = Record<string, unknown>> {
146
204
  * // Update with metadata
147
205
  * update("todo-1", { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
148
206
  */
149
- update<TItem extends object = T>(id: unknown, configOrCallback: ((draft: TItem) => void) | OperationConfig, maybeCallback?: (draft: TItem) => void): TransactionType;
150
- update<TItem extends object = T>(ids: Array<unknown>, configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig, maybeCallback?: (draft: Array<TItem>) => void): TransactionType;
207
+ update<TItem extends object = T>(key: Array<TKey | unknown>, callback: (drafts: Array<TItem>) => void): TransactionType;
208
+ update<TItem extends object = T>(keys: Array<TKey | unknown>, config: OperationConfig, callback: (drafts: Array<TItem>) => void): TransactionType;
209
+ update<TItem extends object = T>(id: TKey | unknown, callback: (draft: TItem) => void): TransactionType;
210
+ update<TItem extends object = T>(id: TKey | unknown, config: OperationConfig, callback: (draft: TItem) => void): TransactionType;
151
211
  /**
152
212
  * Deletes one or more items from the collection
153
213
  * @param ids - Single ID or array of IDs to delete
@@ -163,20 +223,20 @@ export declare class Collection<T extends object = Record<string, unknown>> {
163
223
  * // Delete with metadata
164
224
  * delete("todo-1", { metadata: { reason: "completed" } })
165
225
  */
166
- delete: (ids: Array<string> | string, config?: OperationConfig) => TransactionType;
226
+ delete: (keys: Array<TKey> | TKey, config?: OperationConfig) => TransactionType<any>;
167
227
  /**
168
228
  * Gets the current state of the collection as a Map
169
229
  *
170
230
  * @returns A Map containing all items in the collection, with keys as identifiers
171
231
  */
172
- get state(): Map<string, T>;
232
+ get state(): Map<TKey, T>;
173
233
  /**
174
234
  * Gets the current state of the collection as a Map, but only resolves when data is available
175
235
  * Waits for the first sync commit to complete before resolving
176
236
  *
177
237
  * @returns Promise that resolves to a Map containing all items in the collection
178
238
  */
179
- stateWhenReady(): Promise<Map<string, T>>;
239
+ stateWhenReady(): Promise<Map<TKey, T>>;
180
240
  /**
181
241
  * Gets the current state of the collection as an Array
182
242
  *
@@ -200,5 +260,34 @@ export declare class Collection<T extends object = Record<string, unknown>> {
200
260
  * @param callback - A function that will be called with the changes in the collection
201
261
  * @returns A function that can be called to unsubscribe from the changes
202
262
  */
203
- subscribeChanges(callback: (changes: Array<ChangeMessage<T>>) => void): () => void;
263
+ subscribeChanges(callback: (changes: Array<ChangeMessage<T>>) => void, { includeInitialState }?: {
264
+ includeInitialState?: boolean;
265
+ }): () => void;
266
+ /**
267
+ * Subscribe to changes for a specific key
268
+ */
269
+ subscribeChangesKey(key: TKey, listener: ChangeListener<T, TKey>, { includeInitialState }?: {
270
+ includeInitialState?: boolean;
271
+ }): () => void;
272
+ /**
273
+ * Trigger a recomputation when transactions change
274
+ * This method should be called by the Transaction class when state changes
275
+ */
276
+ onTransactionStateChange(): void;
277
+ private _storeMap;
278
+ /**
279
+ * Returns a Tanstack Store Map that is updated when the collection changes
280
+ * This is a temporary solution to enable the existing framework hooks to work
281
+ * with the new internals of Collection until they are rewritten.
282
+ * TODO: Remove this once the framework hooks are rewritten.
283
+ */
284
+ asStoreMap(): Store<Map<TKey, T>>;
285
+ private _storeArray;
286
+ /**
287
+ * Returns a Tanstack Store Array that is updated when the collection changes
288
+ * This is a temporary solution to enable the existing framework hooks to work
289
+ * with the new internals of Collection until they are rewritten.
290
+ * TODO: Remove this once the framework hooks are rewritten.
291
+ */
292
+ asStoreArray(): Store<Array<T>>;
204
293
  }