@tanstack/db 0.0.10 → 0.0.12

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 (59) hide show
  1. package/dist/cjs/collection.cjs +9 -49
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +29 -30
  4. package/dist/cjs/index.cjs +0 -1
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/query/compiled-query.cjs +55 -62
  7. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  8. package/dist/cjs/query/compiled-query.d.cts +0 -4
  9. package/dist/cjs/query/group-by.cjs +3 -3
  10. package/dist/cjs/query/group-by.cjs.map +1 -1
  11. package/dist/cjs/query/group-by.d.cts +1 -1
  12. package/dist/cjs/query/joins.cjs +16 -16
  13. package/dist/cjs/query/joins.cjs.map +1 -1
  14. package/dist/cjs/query/joins.d.cts +1 -1
  15. package/dist/cjs/query/order-by.cjs +6 -6
  16. package/dist/cjs/query/order-by.cjs.map +1 -1
  17. package/dist/cjs/query/pipeline-compiler.cjs +5 -5
  18. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
  19. package/dist/cjs/query/pipeline-compiler.d.cts +1 -1
  20. package/dist/cjs/query/select.cjs +2 -2
  21. package/dist/cjs/query/select.cjs.map +1 -1
  22. package/dist/cjs/transactions.cjs +5 -12
  23. package/dist/cjs/transactions.cjs.map +1 -1
  24. package/dist/cjs/transactions.d.cts +1 -1
  25. package/dist/cjs/types.d.cts +40 -11
  26. package/dist/esm/collection.d.ts +29 -30
  27. package/dist/esm/collection.js +10 -50
  28. package/dist/esm/collection.js.map +1 -1
  29. package/dist/esm/index.js +1 -2
  30. package/dist/esm/query/compiled-query.d.ts +0 -4
  31. package/dist/esm/query/compiled-query.js +55 -62
  32. package/dist/esm/query/compiled-query.js.map +1 -1
  33. package/dist/esm/query/group-by.d.ts +1 -1
  34. package/dist/esm/query/group-by.js +1 -1
  35. package/dist/esm/query/group-by.js.map +1 -1
  36. package/dist/esm/query/joins.d.ts +1 -1
  37. package/dist/esm/query/joins.js +1 -1
  38. package/dist/esm/query/joins.js.map +1 -1
  39. package/dist/esm/query/order-by.js +1 -1
  40. package/dist/esm/query/order-by.js.map +1 -1
  41. package/dist/esm/query/pipeline-compiler.d.ts +1 -1
  42. package/dist/esm/query/pipeline-compiler.js +1 -1
  43. package/dist/esm/query/pipeline-compiler.js.map +1 -1
  44. package/dist/esm/query/select.js +1 -1
  45. package/dist/esm/query/select.js.map +1 -1
  46. package/dist/esm/transactions.d.ts +1 -1
  47. package/dist/esm/transactions.js +5 -12
  48. package/dist/esm/transactions.js.map +1 -1
  49. package/dist/esm/types.d.ts +40 -11
  50. package/package.json +2 -2
  51. package/src/collection.ts +66 -121
  52. package/src/query/compiled-query.ts +85 -71
  53. package/src/query/group-by.ts +1 -1
  54. package/src/query/joins.ts +2 -2
  55. package/src/query/order-by.ts +1 -1
  56. package/src/query/pipeline-compiler.ts +2 -2
  57. package/src/query/select.ts +1 -1
  58. package/src/transactions.ts +8 -20
  59. package/src/types.ts +78 -9
package/src/collection.ts CHANGED
@@ -11,23 +11,16 @@ import type {
11
11
  OperationConfig,
12
12
  OptimisticChangeMessage,
13
13
  PendingMutation,
14
+ ResolveType,
14
15
  StandardSchema,
15
16
  Transaction as TransactionType,
16
17
  UtilsRecord,
17
18
  } from "./types"
19
+ import type { StandardSchemaV1 } from "@standard-schema/spec"
18
20
 
19
21
  // Store collections in memory
20
22
  export const collectionsStore = new Map<string, CollectionImpl<any, any>>()
21
23
 
22
- // Map to track loading collections
23
- const loadingCollectionResolvers = new Map<
24
- string,
25
- {
26
- promise: Promise<CollectionImpl<any, any>>
27
- resolve: (value: CollectionImpl<any, any>) => void
28
- }
29
- >()
30
-
31
24
  interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
32
25
  committed: boolean
33
26
  operations: Array<OptimisticChangeMessage<T>>
@@ -36,6 +29,7 @@ interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
36
29
  /**
37
30
  * Enhanced Collection interface that includes both data type T and utilities TUtils
38
31
  * @template T - The type of items in the collection
32
+ * @template TKey - The type of the key for the collection
39
33
  * @template TUtils - The utilities record type
40
34
  */
41
35
  export interface Collection<
@@ -49,20 +43,53 @@ export interface Collection<
49
43
  /**
50
44
  * Creates a new Collection instance with the given configuration
51
45
  *
52
- * @template T - The type of items in the collection
46
+ * @template TExplicit - The explicit type of items in the collection (highest priority)
53
47
  * @template TKey - The type of the key for the collection
54
48
  * @template TUtils - The utilities record type
49
+ * @template TSchema - The schema type for validation and type inference (second priority)
50
+ * @template TFallback - The fallback type if no explicit or schema type is provided
55
51
  * @param options - Collection options with optional utilities
56
52
  * @returns A new Collection with utilities exposed both at top level and under .utils
53
+ *
54
+ * @example
55
+ * // Using explicit type
56
+ * const todos = createCollection<Todo>({
57
+ * getKey: (todo) => todo.id,
58
+ * sync: { sync: () => {} }
59
+ * })
60
+ *
61
+ * // Using schema for type inference (preferred as it also gives you client side validation)
62
+ * const todoSchema = z.object({
63
+ * id: z.string(),
64
+ * title: z.string(),
65
+ * completed: z.boolean()
66
+ * })
67
+ *
68
+ * const todos = createCollection({
69
+ * schema: todoSchema,
70
+ * getKey: (todo) => todo.id,
71
+ * sync: { sync: () => {} }
72
+ * })
73
+ *
74
+ * // Note: You must provide either an explicit type or a schema, but not both
57
75
  */
58
76
  export function createCollection<
59
- T extends object = Record<string, unknown>,
77
+ TExplicit = unknown,
60
78
  TKey extends string | number = string | number,
61
79
  TUtils extends UtilsRecord = {},
80
+ TSchema extends StandardSchemaV1 = StandardSchemaV1,
81
+ TFallback extends object = Record<string, unknown>,
62
82
  >(
63
- options: CollectionConfig<T, TKey> & { utils?: TUtils }
64
- ): Collection<T, TKey, TUtils> {
65
- const collection = new CollectionImpl<T, TKey>(options)
83
+ options: CollectionConfig<
84
+ ResolveType<TExplicit, TSchema, TFallback>,
85
+ TKey,
86
+ TSchema
87
+ > & { utils?: TUtils }
88
+ ): Collection<ResolveType<TExplicit, TSchema, TFallback>, TKey, TUtils> {
89
+ const collection = new CollectionImpl<
90
+ ResolveType<TExplicit, TSchema, TFallback>,
91
+ TKey
92
+ >(options)
66
93
 
67
94
  // Copy utils to both top level and .utils namespace
68
95
  if (options.utils) {
@@ -71,98 +98,11 @@ export function createCollection<
71
98
  collection.utils = {} as TUtils
72
99
  }
73
100
 
74
- return collection as Collection<T, TKey, TUtils>
75
- }
76
-
77
- /**
78
- * Preloads a collection with the given configuration
79
- * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)
80
- * If the collection has already loaded, it resolves immediately
81
- *
82
- * This function is useful in route loaders or similar pre-rendering scenarios where you want
83
- * to ensure data is available before a route transition completes. It uses the same shared collection
84
- * instance that will be used by useCollection, ensuring data consistency.
85
- *
86
- * @example
87
- * ```typescript
88
- * // In a route loader
89
- * async function loader({ params }) {
90
- * await preloadCollection({
91
- * id: `users-${params.userId}`,
92
- * sync: { ... },
93
- * });
94
- *
95
- * return null;
96
- * }
97
- * ```
98
- *
99
- * @template T - The type of items in the collection
100
- * @param config - Configuration for the collection, including id and sync
101
- * @returns Promise that resolves when the initial sync is finished
102
- */
103
- export function preloadCollection<
104
- T extends object = Record<string, unknown>,
105
- TKey extends string | number = string | number,
106
- >(config: CollectionConfig<T, TKey>): Promise<CollectionImpl<T, TKey>> {
107
- if (!config.id) {
108
- throw new Error(`The id property is required for preloadCollection`)
109
- }
110
-
111
- // If the collection is already fully loaded, return a resolved promise
112
- if (
113
- collectionsStore.has(config.id) &&
114
- !loadingCollectionResolvers.has(config.id)
115
- ) {
116
- return Promise.resolve(
117
- collectionsStore.get(config.id)! as CollectionImpl<T, TKey>
118
- )
119
- }
120
-
121
- // If the collection is in the process of loading, return its promise
122
- if (loadingCollectionResolvers.has(config.id)) {
123
- return loadingCollectionResolvers.get(config.id)!.promise
124
- }
125
-
126
- // Create a new collection instance if it doesn't exist
127
- if (!collectionsStore.has(config.id)) {
128
- collectionsStore.set(
129
- config.id,
130
- createCollection<T, TKey>({
131
- id: config.id,
132
- getKey: config.getKey,
133
- sync: config.sync,
134
- schema: config.schema,
135
- })
136
- )
137
- }
138
-
139
- const collection = collectionsStore.get(config.id)! as CollectionImpl<T, TKey>
140
-
141
- // Create a promise that will resolve after the first commit
142
- let resolveFirstCommit: (value: CollectionImpl<T, TKey>) => void
143
- const firstCommitPromise = new Promise<CollectionImpl<T, TKey>>((resolve) => {
144
- resolveFirstCommit = resolve
145
- })
146
-
147
- // Store the loading promise first
148
- loadingCollectionResolvers.set(config.id, {
149
- promise: firstCommitPromise,
150
- resolve: resolveFirstCommit!,
151
- })
152
-
153
- // Register a one-time listener for the first commit
154
- collection.onFirstCommit(() => {
155
- if (!config.id) {
156
- throw new Error(`The id property is required for preloadCollection`)
157
- }
158
- if (loadingCollectionResolvers.has(config.id)) {
159
- const resolver = loadingCollectionResolvers.get(config.id)!
160
- loadingCollectionResolvers.delete(config.id)
161
- resolver.resolve(collection)
162
- }
163
- })
164
-
165
- return firstCommitPromise
101
+ return collection as Collection<
102
+ ResolveType<TExplicit, TSchema, TFallback>,
103
+ TKey,
104
+ TUtils
105
+ >
166
106
  }
167
107
 
168
108
  /**
@@ -184,8 +124,8 @@ export class SchemaValidationError extends Error {
184
124
  message?: string
185
125
  ) {
186
126
  const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues
187
- .map((issue) => issue.message)
188
- .join(`, `)}`
127
+ .map((issue) => `\n- ${issue.message} - path: ${issue.path}`)
128
+ .join(``)}`
189
129
 
190
130
  super(message || defaultMessage)
191
131
  this.name = `SchemaValidationError`
@@ -221,7 +161,7 @@ export class CollectionImpl<
221
161
 
222
162
  private pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []
223
163
  private syncedKeys = new Set<TKey>()
224
- public config: CollectionConfig<T, TKey>
164
+ public config: CollectionConfig<T, TKey, any>
225
165
  private hasReceivedFirstCommit = false
226
166
 
227
167
  // Array to store one-time commit listeners
@@ -244,7 +184,7 @@ export class CollectionImpl<
244
184
  * @param config - Configuration object for the collection
245
185
  * @throws Error if sync config is missing
246
186
  */
247
- constructor(config: CollectionConfig<T, TKey>) {
187
+ constructor(config: CollectionConfig<T, TKey, any>) {
248
188
  // eslint-disable-next-line
249
189
  if (!config) {
250
190
  throw new Error(`Collection requires a config`)
@@ -803,7 +743,7 @@ export class CollectionImpl<
803
743
  }
804
744
 
805
745
  const items = Array.isArray(data) ? data : [data]
806
- const mutations: Array<PendingMutation<T>> = []
746
+ const mutations: Array<PendingMutation<T, `insert`>> = []
807
747
 
808
748
  // Create mutations for each item
809
749
  items.forEach((item) => {
@@ -817,7 +757,7 @@ export class CollectionImpl<
817
757
  }
818
758
  const globalKey = this.generateGlobalKey(key, item)
819
759
 
820
- const mutation: PendingMutation<T> = {
760
+ const mutation: PendingMutation<T, `insert`> = {
821
761
  mutationId: crypto.randomUUID(),
822
762
  original: {},
823
763
  modified: validatedData,
@@ -987,7 +927,7 @@ export class CollectionImpl<
987
927
  }
988
928
 
989
929
  // Create mutations for each object that has changes
990
- const mutations: Array<PendingMutation<T>> = keysArray
930
+ const mutations: Array<PendingMutation<T, `update`>> = keysArray
991
931
  .map((key, index) => {
992
932
  const itemChanges = changesArray[index] // User-provided changes for this specific item
993
933
 
@@ -1025,9 +965,9 @@ export class CollectionImpl<
1025
965
 
1026
966
  return {
1027
967
  mutationId: crypto.randomUUID(),
1028
- original: originalItem as Record<string, unknown>,
1029
- modified: modifiedItem as Record<string, unknown>,
1030
- changes: validatedUpdatePayload as Record<string, unknown>,
968
+ original: originalItem,
969
+ modified: modifiedItem,
970
+ changes: validatedUpdatePayload as Partial<T>,
1031
971
  globalKey,
1032
972
  key,
1033
973
  metadata: config.metadata as unknown,
@@ -1041,7 +981,7 @@ export class CollectionImpl<
1041
981
  collection: this,
1042
982
  }
1043
983
  })
1044
- .filter(Boolean) as Array<PendingMutation<T>>
984
+ .filter(Boolean) as Array<PendingMutation<T, `update`>>
1045
985
 
1046
986
  // If no changes were made, return an empty transaction early
1047
987
  if (mutations.length === 0) {
@@ -1117,15 +1057,20 @@ export class CollectionImpl<
1117
1057
  }
1118
1058
 
1119
1059
  const keysArray = Array.isArray(keys) ? keys : [keys]
1120
- const mutations: Array<PendingMutation<T>> = []
1060
+ const mutations: Array<PendingMutation<T, `delete`>> = []
1121
1061
 
1122
1062
  for (const key of keysArray) {
1063
+ if (!this.has(key)) {
1064
+ throw new Error(
1065
+ `Collection.delete was called with key '${key}' but there is no item in the collection with this key`
1066
+ )
1067
+ }
1123
1068
  const globalKey = this.generateGlobalKey(key, this.get(key)!)
1124
- const mutation: PendingMutation<T> = {
1069
+ const mutation: PendingMutation<T, `delete`> = {
1125
1070
  mutationId: crypto.randomUUID(),
1126
- original: this.get(key) || {},
1071
+ original: this.get(key)!,
1127
1072
  modified: this.get(key)!,
1128
- changes: this.get(key) || {},
1073
+ changes: this.get(key)!,
1129
1074
  globalKey,
1130
1075
  key,
1131
1076
  metadata: config?.metadata as unknown,
@@ -1,13 +1,13 @@
1
- import { D2, MessageType, MultiSet, output } from "@electric-sql/d2ts"
1
+ import { D2, MultiSet, output } from "@electric-sql/d2mini"
2
2
  import { createCollection } from "../collection.js"
3
3
  import { compileQueryPipeline } from "./pipeline-compiler.js"
4
4
  import type { Collection } from "../collection.js"
5
- import type { ChangeMessage, SyncConfig } from "../types.js"
5
+ import type { ChangeMessage, ResolveType, SyncConfig } from "../types.js"
6
6
  import type {
7
7
  IStreamBuilder,
8
8
  MultiSetArray,
9
9
  RootStreamBuilder,
10
- } from "@electric-sql/d2ts"
10
+ } from "@electric-sql/d2mini"
11
11
  import type { QueryBuilder, ResultsFromContext } from "./query-builder.js"
12
12
  import type { Context, Schema } from "./types.js"
13
13
 
@@ -25,7 +25,6 @@ export class CompiledQuery<TResults extends object = Record<string, unknown>> {
25
25
  private inputCollections: Record<string, Collection<any>>
26
26
  private resultCollection: Collection<TResults>
27
27
  public state: `compiled` | `running` | `stopped` = `compiled`
28
- private version = 0
29
28
  private unsubscribeCallbacks: Array<() => void> = []
30
29
 
31
30
  constructor(queryBuilder: QueryBuilder<Context<Schema>>) {
@@ -38,58 +37,76 @@ export class CompiledQuery<TResults extends object = Record<string, unknown>> {
38
37
 
39
38
  this.inputCollections = collections
40
39
 
41
- const graph = new D2({ initialFrontier: this.version })
40
+ const graph = new D2()
42
41
  const inputs = Object.fromEntries(
43
42
  Object.entries(collections).map(([key]) => [key, graph.newInput<any>()])
44
43
  )
45
44
 
46
- const sync: SyncConfig<TResults>[`sync`] = ({ begin, write, commit }) => {
45
+ // Use TResults directly to ensure type compatibility
46
+ const sync: SyncConfig<TResults>[`sync`] = ({
47
+ begin,
48
+ write,
49
+ commit,
50
+ collection,
51
+ }) => {
47
52
  compileQueryPipeline<IStreamBuilder<[unknown, TResults]>>(
48
53
  query,
49
54
  inputs
50
55
  ).pipe(
51
- output(({ type, data }) => {
52
- if (type === MessageType.DATA) {
53
- begin()
54
- data.collection
55
- .getInner()
56
- .reduce((acc, [[key, value], multiplicity]) => {
57
- const changes = acc.get(key) || {
58
- deletes: 0,
59
- inserts: 0,
60
- value,
61
- }
62
- if (multiplicity < 0) {
63
- changes.deletes += Math.abs(multiplicity)
64
- } else if (multiplicity > 0) {
65
- changes.inserts += multiplicity
66
- changes.value = value
67
- }
68
- acc.set(key, changes)
69
- return acc
70
- }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())
71
- .forEach((changes, rawKey) => {
72
- const { deletes, inserts, value } = changes
73
- const valueWithKey = { ...value, _key: rawKey }
74
- if (inserts && !deletes) {
75
- write({
76
- value: valueWithKey,
77
- type: `insert`,
78
- })
79
- } else if (inserts >= deletes) {
80
- write({
81
- value: valueWithKey,
82
- type: `update`,
83
- })
84
- } else if (deletes > 0) {
85
- write({
86
- value: valueWithKey,
87
- type: `delete`,
88
- })
89
- }
90
- })
91
- commit()
92
- }
56
+ output((data) => {
57
+ begin()
58
+ data
59
+ .getInner()
60
+ .reduce((acc, [[key, value], multiplicity]) => {
61
+ const changes = acc.get(key) || {
62
+ deletes: 0,
63
+ inserts: 0,
64
+ value,
65
+ }
66
+ if (multiplicity < 0) {
67
+ changes.deletes += Math.abs(multiplicity)
68
+ } else if (multiplicity > 0) {
69
+ changes.inserts += multiplicity
70
+ changes.value = value
71
+ }
72
+ acc.set(key, changes)
73
+ return acc
74
+ }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())
75
+ .forEach((changes, rawKey) => {
76
+ const { deletes, inserts, value } = changes
77
+ const valueWithKey = { ...value, _key: rawKey }
78
+
79
+ // Simple singular insert.
80
+ if (inserts && deletes === 0) {
81
+ write({
82
+ value: valueWithKey,
83
+ type: `insert`,
84
+ })
85
+ } else if (
86
+ // Insert & update(s) (updates are a delete & insert)
87
+ inserts > deletes ||
88
+ // Just update(s) but the item is already in the collection (so
89
+ // was inserted previously).
90
+ (inserts === deletes &&
91
+ collection.has(valueWithKey._key as string | number))
92
+ ) {
93
+ write({
94
+ value: valueWithKey,
95
+ type: `update`,
96
+ })
97
+ // Only delete is left as an option
98
+ } else if (deletes > 0) {
99
+ write({
100
+ value: valueWithKey,
101
+ type: `delete`,
102
+ })
103
+ } else {
104
+ throw new Error(
105
+ `This should never happen ${JSON.stringify(changes)}`
106
+ )
107
+ }
108
+ })
109
+ commit()
93
110
  })
94
111
  )
95
112
  graph.finalize()
@@ -98,14 +115,30 @@ export class CompiledQuery<TResults extends object = Record<string, unknown>> {
98
115
  this.graph = graph
99
116
  this.inputs = inputs
100
117
  this.resultCollection = createCollection<TResults>({
101
- id: crypto.randomUUID(), // TODO: remove when we don't require any more
102
118
  getKey: (val: unknown) => {
103
119
  return (val as any)._key
104
120
  },
105
121
  sync: {
106
- sync,
122
+ sync: sync as unknown as (params: {
123
+ collection: Collection<
124
+ ResolveType<TResults, never, Record<string, unknown>>,
125
+ string | number,
126
+ {}
127
+ >
128
+ begin: () => void
129
+ write: (
130
+ message: Omit<
131
+ ChangeMessage<
132
+ ResolveType<TResults, never, Record<string, unknown>>,
133
+ string | number
134
+ >,
135
+ `key`
136
+ >
137
+ ) => void
138
+ commit: () => void
139
+ }) => void,
107
140
  },
108
- })
141
+ }) as unknown as Collection<TResults, string | number, {}>
109
142
  }
110
143
 
111
144
  get results() {
@@ -131,22 +164,7 @@ export class CompiledQuery<TResults extends object = Record<string, unknown>> {
131
164
  multiSetArray.push([[key, change.value], -1])
132
165
  }
133
166
  }
134
- input.sendData(this.version, new MultiSet(multiSetArray))
135
- }
136
-
137
- private sendFrontierToInput(inputKey: string) {
138
- const input = this.inputs[inputKey]!
139
- input.sendFrontier(this.version)
140
- }
141
-
142
- private sendFrontierToAllInputs() {
143
- Object.entries(this.inputs).forEach(([key]) => {
144
- this.sendFrontierToInput(key)
145
- })
146
- }
147
-
148
- private incrementVersion() {
149
- this.version++
167
+ input.sendData(new MultiSet(multiSetArray))
150
168
  }
151
169
 
152
170
  private runGraph() {
@@ -168,16 +186,12 @@ export class CompiledQuery<TResults extends object = Record<string, unknown>> {
168
186
  collection.config.getKey
169
187
  )
170
188
  })
171
- this.incrementVersion()
172
- this.sendFrontierToAllInputs()
173
189
  this.runGraph()
174
190
 
175
191
  // Subscribe to changes
176
192
  Object.entries(this.inputCollections).forEach(([key, collection]) => {
177
193
  const unsubscribe = collection.subscribeChanges((changes) => {
178
194
  this.sendChangesToInput(key, changes, collection.config.getKey)
179
- this.incrementVersion()
180
- this.sendFrontierToAllInputs()
181
195
  this.runGraph()
182
196
  })
183
197
 
@@ -1,4 +1,4 @@
1
- import { groupBy, groupByOperators } from "@electric-sql/d2ts"
1
+ import { groupBy, groupByOperators } from "@electric-sql/d2mini"
2
2
  import {
3
3
  evaluateOperandOnNamespacedRow,
4
4
  extractValueFromNamespacedRow,
@@ -3,11 +3,11 @@ import {
3
3
  filter,
4
4
  join as joinOperator,
5
5
  map,
6
- } from "@electric-sql/d2ts"
6
+ } from "@electric-sql/d2mini"
7
7
  import { evaluateConditionOnNamespacedRow } from "./evaluators.js"
8
8
  import { extractJoinKey } from "./extractors.js"
9
9
  import type { Query } from "./index.js"
10
- import type { IStreamBuilder, JoinType } from "@electric-sql/d2ts"
10
+ import type { IStreamBuilder, JoinType } from "@electric-sql/d2mini"
11
11
  import type {
12
12
  KeyedStream,
13
13
  NamespacedAndKeyedStream,
@@ -3,7 +3,7 @@ import {
3
3
  orderBy,
4
4
  orderByWithFractionalIndex,
5
5
  orderByWithIndex,
6
- } from "@electric-sql/d2ts"
6
+ } from "@electric-sql/d2mini"
7
7
  import { evaluateOperandOnNamespacedRow } from "./extractors"
8
8
  import { isOrderIndexFunctionCall } from "./utils"
9
9
  import type { ConditionOperand, Query } from "./schema"
@@ -1,11 +1,11 @@
1
- import { filter, map } from "@electric-sql/d2ts"
1
+ import { filter, map } from "@electric-sql/d2mini"
2
2
  import { evaluateWhereOnNamespacedRow } from "./evaluators.js"
3
3
  import { processJoinClause } from "./joins.js"
4
4
  import { processGroupBy } from "./group-by.js"
5
5
  import { processOrderBy } from "./order-by.js"
6
6
  import { processSelect } from "./select.js"
7
7
  import type { Query } from "./schema.js"
8
- import type { IStreamBuilder } from "@electric-sql/d2ts"
8
+ import type { IStreamBuilder } from "@electric-sql/d2mini"
9
9
  import type {
10
10
  InputRow,
11
11
  KeyedStream,
@@ -1,4 +1,4 @@
1
- import { map } from "@electric-sql/d2ts"
1
+ import { map } from "@electric-sql/d2mini"
2
2
  import {
3
3
  evaluateOperandOnNamespacedRow,
4
4
  extractValueFromNamespacedRow,
@@ -8,36 +8,24 @@ import type {
8
8
  TransactionWithMutations,
9
9
  } from "./types"
10
10
 
11
- function generateUUID() {
12
- // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)
13
- if (
14
- typeof crypto !== `undefined` &&
15
- typeof crypto.randomUUID === `function`
16
- ) {
17
- return crypto.randomUUID()
18
- }
19
-
20
- // Fallback implementation for older environments
21
- return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {
22
- const r = (Math.random() * 16) | 0
23
- const v = c === `x` ? r : (r & 0x3) | 0x8
24
- return v.toString(16)
25
- })
26
- }
27
-
28
11
  const transactions: Array<Transaction<any>> = []
29
12
  let transactionStack: Array<Transaction<any>> = []
30
13
 
31
- export function createTransaction(config: TransactionConfig): Transaction {
14
+ export function createTransaction<
15
+ TData extends object = Record<string, unknown>,
16
+ >(config: TransactionConfig<TData>): Transaction<TData> {
32
17
  if (typeof config.mutationFn === `undefined`) {
33
18
  throw `mutationFn is required when creating a transaction`
34
19
  }
35
20
 
36
21
  let transactionId = config.id
37
22
  if (!transactionId) {
38
- transactionId = generateUUID()
23
+ transactionId = crypto.randomUUID()
39
24
  }
40
- const newTransaction = new Transaction({ ...config, id: transactionId })
25
+ const newTransaction = new Transaction<TData>({
26
+ ...config,
27
+ id: transactionId,
28
+ })
41
29
 
42
30
  transactions.push(newTransaction)
43
31