@tanstack/db 0.0.27 → 0.0.30
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/dist/cjs/change-events.cjs +141 -0
- package/dist/cjs/change-events.cjs.map +1 -0
- package/dist/cjs/change-events.d.cts +49 -0
- package/dist/cjs/collection.cjs +234 -86
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +95 -20
- package/dist/cjs/errors.cjs +509 -1
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +225 -1
- package/dist/cjs/index.cjs +82 -3
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +5 -1
- package/dist/cjs/indexes/auto-index.cjs +64 -0
- package/dist/cjs/indexes/auto-index.cjs.map +1 -0
- package/dist/cjs/indexes/auto-index.d.cts +9 -0
- package/dist/cjs/indexes/base-index.cjs +46 -0
- package/dist/cjs/indexes/base-index.cjs.map +1 -0
- package/dist/cjs/indexes/base-index.d.cts +54 -0
- package/dist/cjs/indexes/btree-index.cjs +191 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -0
- package/dist/cjs/indexes/btree-index.d.cts +74 -0
- package/dist/cjs/indexes/index-options.d.cts +13 -0
- package/dist/cjs/indexes/lazy-index.cjs +193 -0
- package/dist/cjs/indexes/lazy-index.cjs.map +1 -0
- package/dist/cjs/indexes/lazy-index.d.cts +96 -0
- package/dist/cjs/local-storage.cjs +9 -15
- package/dist/cjs/local-storage.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +11 -0
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +4 -0
- package/dist/cjs/query/builder/index.cjs +6 -7
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.cjs +37 -0
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +12 -0
- package/dist/cjs/query/compiler/evaluators.cjs +83 -58
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/evaluators.d.cts +8 -0
- package/dist/cjs/query/compiler/expressions.cjs +61 -0
- package/dist/cjs/query/compiler/expressions.cjs.map +1 -0
- package/dist/cjs/query/compiler/expressions.d.cts +25 -0
- package/dist/cjs/query/compiler/group-by.cjs +5 -10
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs +23 -17
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +12 -3
- package/dist/cjs/query/compiler/joins.cjs +61 -12
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.cjs +4 -34
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/types.d.cts +2 -2
- package/dist/cjs/query/live-query-collection.cjs +54 -12
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/optimizer.cjs +45 -7
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/query/optimizer.d.cts +13 -3
- package/dist/cjs/transactions.cjs +5 -4
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +31 -0
- package/dist/cjs/utils/array-utils.d.cts +8 -0
- package/dist/cjs/utils/btree.cjs +677 -0
- package/dist/cjs/utils/btree.cjs.map +1 -0
- package/dist/cjs/utils/btree.d.cts +197 -0
- package/dist/cjs/utils/comparison.cjs +52 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -0
- package/dist/cjs/utils/comparison.d.cts +11 -0
- package/dist/cjs/utils/index-optimization.cjs +270 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -0
- package/dist/cjs/utils/index-optimization.d.cts +29 -0
- package/dist/esm/change-events.d.ts +49 -0
- package/dist/esm/change-events.js +141 -0
- package/dist/esm/change-events.js.map +1 -0
- package/dist/esm/collection.d.ts +95 -20
- package/dist/esm/collection.js +232 -84
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/errors.d.ts +225 -1
- package/dist/esm/errors.js +510 -2
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.js +81 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.d.ts +9 -0
- package/dist/esm/indexes/auto-index.js +64 -0
- package/dist/esm/indexes/auto-index.js.map +1 -0
- package/dist/esm/indexes/base-index.d.ts +54 -0
- package/dist/esm/indexes/base-index.js +46 -0
- package/dist/esm/indexes/base-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +74 -0
- package/dist/esm/indexes/btree-index.js +191 -0
- package/dist/esm/indexes/btree-index.js.map +1 -0
- package/dist/esm/indexes/index-options.d.ts +13 -0
- package/dist/esm/indexes/lazy-index.d.ts +96 -0
- package/dist/esm/indexes/lazy-index.js +193 -0
- package/dist/esm/indexes/lazy-index.js.map +1 -0
- package/dist/esm/local-storage.js +9 -15
- package/dist/esm/local-storage.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +4 -0
- package/dist/esm/query/builder/functions.js +11 -0
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.js +6 -7
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +12 -0
- package/dist/esm/query/builder/ref-proxy.js +37 -0
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/compiler/evaluators.d.ts +8 -0
- package/dist/esm/query/compiler/evaluators.js +84 -59
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/expressions.d.ts +25 -0
- package/dist/esm/query/compiler/expressions.js +61 -0
- package/dist/esm/query/compiler/expressions.js.map +1 -0
- package/dist/esm/query/compiler/group-by.js +5 -10
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +12 -3
- package/dist/esm/query/compiler/index.js +23 -17
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js +61 -12
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.js +1 -31
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/types.d.ts +2 -2
- package/dist/esm/query/live-query-collection.js +54 -12
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.d.ts +13 -3
- package/dist/esm/query/optimizer.js +40 -2
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/transactions.js +5 -4
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +31 -0
- package/dist/esm/utils/array-utils.d.ts +8 -0
- package/dist/esm/utils/btree.d.ts +197 -0
- package/dist/esm/utils/btree.js +677 -0
- package/dist/esm/utils/btree.js.map +1 -0
- package/dist/esm/utils/comparison.d.ts +11 -0
- package/dist/esm/utils/comparison.js +52 -0
- package/dist/esm/utils/comparison.js.map +1 -0
- package/dist/esm/utils/index-optimization.d.ts +29 -0
- package/dist/esm/utils/index-optimization.js +270 -0
- package/dist/esm/utils/index-optimization.js.map +1 -0
- package/package.json +1 -1
- package/src/change-events.ts +257 -0
- package/src/collection.ts +316 -105
- package/src/errors.ts +545 -1
- package/src/index.ts +7 -1
- package/src/indexes/auto-index.ts +108 -0
- package/src/indexes/base-index.ts +119 -0
- package/src/indexes/btree-index.ts +263 -0
- package/src/indexes/index-options.ts +42 -0
- package/src/indexes/lazy-index.ts +251 -0
- package/src/local-storage.ts +16 -17
- package/src/query/builder/functions.ts +14 -0
- package/src/query/builder/index.ts +12 -7
- package/src/query/builder/ref-proxy.ts +65 -0
- package/src/query/compiler/evaluators.ts +114 -62
- package/src/query/compiler/expressions.ts +92 -0
- package/src/query/compiler/group-by.ts +10 -10
- package/src/query/compiler/index.ts +52 -22
- package/src/query/compiler/joins.ts +114 -15
- package/src/query/compiler/order-by.ts +1 -45
- package/src/query/compiler/types.ts +2 -2
- package/src/query/live-query-collection.ts +95 -15
- package/src/query/optimizer.ts +94 -5
- package/src/transactions.ts +10 -4
- package/src/types.ts +38 -0
- package/src/utils/array-utils.ts +28 -0
- package/src/utils/btree.ts +1010 -0
- package/src/utils/comparison.ts +79 -0
- package/src/utils/index-optimization.ts +546 -0
package/src/errors.ts
CHANGED
|
@@ -1,6 +1,550 @@
|
|
|
1
|
-
|
|
1
|
+
// Root error class for all TanStack DB errors
|
|
2
|
+
export class TanStackDBError extends Error {
|
|
3
|
+
constructor(message: string) {
|
|
4
|
+
super(message)
|
|
5
|
+
this.name = `TanStackDBError`
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Base error classes
|
|
10
|
+
export class NonRetriableError extends TanStackDBError {
|
|
2
11
|
constructor(message: string) {
|
|
3
12
|
super(message)
|
|
4
13
|
this.name = `NonRetriableError`
|
|
5
14
|
}
|
|
6
15
|
}
|
|
16
|
+
|
|
17
|
+
// Schema validation error (exported from index for backward compatibility)
|
|
18
|
+
export class SchemaValidationError extends TanStackDBError {
|
|
19
|
+
type: `insert` | `update`
|
|
20
|
+
issues: ReadonlyArray<{
|
|
21
|
+
message: string
|
|
22
|
+
path?: ReadonlyArray<string | number | symbol>
|
|
23
|
+
}>
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
type: `insert` | `update`,
|
|
27
|
+
issues: ReadonlyArray<{
|
|
28
|
+
message: string
|
|
29
|
+
path?: ReadonlyArray<string | number | symbol>
|
|
30
|
+
}>,
|
|
31
|
+
message?: string
|
|
32
|
+
) {
|
|
33
|
+
const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues
|
|
34
|
+
.map((issue) => `\n- ${issue.message} - path: ${issue.path}`)
|
|
35
|
+
.join(``)}`
|
|
36
|
+
|
|
37
|
+
super(message || defaultMessage)
|
|
38
|
+
this.name = `SchemaValidationError`
|
|
39
|
+
this.type = type
|
|
40
|
+
this.issues = issues
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Collection Configuration Errors
|
|
45
|
+
export class CollectionConfigurationError extends TanStackDBError {
|
|
46
|
+
constructor(message: string) {
|
|
47
|
+
super(message)
|
|
48
|
+
this.name = `CollectionConfigurationError`
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class CollectionRequiresConfigError extends CollectionConfigurationError {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(`Collection requires a config`)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class CollectionRequiresSyncConfigError extends CollectionConfigurationError {
|
|
59
|
+
constructor() {
|
|
60
|
+
super(`Collection requires a sync config`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class InvalidSchemaError extends CollectionConfigurationError {
|
|
65
|
+
constructor() {
|
|
66
|
+
super(`Schema must implement the standard-schema interface`)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class SchemaMustBeSynchronousError extends CollectionConfigurationError {
|
|
71
|
+
constructor() {
|
|
72
|
+
super(`Schema validation must be synchronous`)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Collection State Errors
|
|
77
|
+
export class CollectionStateError extends TanStackDBError {
|
|
78
|
+
constructor(message: string) {
|
|
79
|
+
super(message)
|
|
80
|
+
this.name = `CollectionStateError`
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class CollectionInErrorStateError extends CollectionStateError {
|
|
85
|
+
constructor(operation: string, collectionId: string) {
|
|
86
|
+
super(
|
|
87
|
+
`Cannot perform ${operation} on collection "${collectionId}" - collection is in error state. Try calling cleanup() and restarting the collection.`
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class InvalidCollectionStatusTransitionError extends CollectionStateError {
|
|
93
|
+
constructor(from: string, to: string, collectionId: string) {
|
|
94
|
+
super(
|
|
95
|
+
`Invalid collection status transition from "${from}" to "${to}" for collection "${collectionId}"`
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export class CollectionIsInErrorStateError extends CollectionStateError {
|
|
101
|
+
constructor() {
|
|
102
|
+
super(`Collection is in error state`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export class NegativeActiveSubscribersError extends CollectionStateError {
|
|
107
|
+
constructor() {
|
|
108
|
+
super(`Active subscribers count is negative - this should never happen`)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Collection Operation Errors
|
|
113
|
+
export class CollectionOperationError extends TanStackDBError {
|
|
114
|
+
constructor(message: string) {
|
|
115
|
+
super(message)
|
|
116
|
+
this.name = `CollectionOperationError`
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class UndefinedKeyError extends CollectionOperationError {
|
|
121
|
+
constructor(item: any) {
|
|
122
|
+
super(
|
|
123
|
+
`An object was created without a defined key: ${JSON.stringify(item)}`
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class DuplicateKeyError extends CollectionOperationError {
|
|
129
|
+
constructor(key: string | number) {
|
|
130
|
+
super(
|
|
131
|
+
`Cannot insert document with ID "${key}" because it already exists in the collection`
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class DuplicateKeySyncError extends CollectionOperationError {
|
|
137
|
+
constructor(key: string | number, collectionId: string) {
|
|
138
|
+
super(
|
|
139
|
+
`Cannot insert document with key "${key}" from sync because it already exists in the collection "${collectionId}"`
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export class MissingUpdateArgumentError extends CollectionOperationError {
|
|
145
|
+
constructor() {
|
|
146
|
+
super(`The first argument to update is missing`)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export class NoKeysPassedToUpdateError extends CollectionOperationError {
|
|
151
|
+
constructor() {
|
|
152
|
+
super(`No keys were passed to update`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export class UpdateKeyNotFoundError extends CollectionOperationError {
|
|
157
|
+
constructor(key: string | number) {
|
|
158
|
+
super(
|
|
159
|
+
`The key "${key}" was passed to update but an object for this key was not found in the collection`
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export class KeyUpdateNotAllowedError extends CollectionOperationError {
|
|
165
|
+
constructor(originalKey: string | number, newKey: string | number) {
|
|
166
|
+
super(
|
|
167
|
+
`Updating the key of an item is not allowed. Original key: "${originalKey}", Attempted new key: "${newKey}". Please delete the old item and create a new one if a key change is necessary.`
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export class NoKeysPassedToDeleteError extends CollectionOperationError {
|
|
173
|
+
constructor() {
|
|
174
|
+
super(`No keys were passed to delete`)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export class DeleteKeyNotFoundError extends CollectionOperationError {
|
|
179
|
+
constructor(key: string | number) {
|
|
180
|
+
super(
|
|
181
|
+
`Collection.delete was called with key '${key}' but there is no item in the collection with this key`
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Collection Handler Errors
|
|
187
|
+
export class MissingHandlerError extends TanStackDBError {
|
|
188
|
+
constructor(message: string) {
|
|
189
|
+
super(message)
|
|
190
|
+
this.name = `MissingHandlerError`
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export class MissingInsertHandlerError extends MissingHandlerError {
|
|
195
|
+
constructor() {
|
|
196
|
+
super(
|
|
197
|
+
`Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured.`
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export class MissingUpdateHandlerError extends MissingHandlerError {
|
|
203
|
+
constructor() {
|
|
204
|
+
super(
|
|
205
|
+
`Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export class MissingDeleteHandlerError extends MissingHandlerError {
|
|
211
|
+
constructor() {
|
|
212
|
+
super(
|
|
213
|
+
`Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Transaction Errors
|
|
219
|
+
export class TransactionError extends TanStackDBError {
|
|
220
|
+
constructor(message: string) {
|
|
221
|
+
super(message)
|
|
222
|
+
this.name = `TransactionError`
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export class MissingMutationFunctionError extends TransactionError {
|
|
227
|
+
constructor() {
|
|
228
|
+
super(`mutationFn is required when creating a transaction`)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export class TransactionNotPendingMutateError extends TransactionError {
|
|
233
|
+
constructor() {
|
|
234
|
+
super(
|
|
235
|
+
`You can no longer call .mutate() as the transaction is no longer pending`
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export class TransactionAlreadyCompletedRollbackError extends TransactionError {
|
|
241
|
+
constructor() {
|
|
242
|
+
super(
|
|
243
|
+
`You can no longer call .rollback() as the transaction is already completed`
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export class TransactionNotPendingCommitError extends TransactionError {
|
|
249
|
+
constructor() {
|
|
250
|
+
super(
|
|
251
|
+
`You can no longer call .commit() as the transaction is no longer pending`
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export class NoPendingSyncTransactionWriteError extends TransactionError {
|
|
257
|
+
constructor() {
|
|
258
|
+
super(`No pending sync transaction to write to`)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export class SyncTransactionAlreadyCommittedWriteError extends TransactionError {
|
|
263
|
+
constructor() {
|
|
264
|
+
super(
|
|
265
|
+
`The pending sync transaction is already committed, you can't still write to it.`
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export class NoPendingSyncTransactionCommitError extends TransactionError {
|
|
271
|
+
constructor() {
|
|
272
|
+
super(`No pending sync transaction to commit`)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export class SyncTransactionAlreadyCommittedError extends TransactionError {
|
|
277
|
+
constructor() {
|
|
278
|
+
super(
|
|
279
|
+
`The pending sync transaction is already committed, you can't commit it again.`
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Query Builder Errors
|
|
285
|
+
export class QueryBuilderError extends TanStackDBError {
|
|
286
|
+
constructor(message: string) {
|
|
287
|
+
super(message)
|
|
288
|
+
this.name = `QueryBuilderError`
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export class OnlyOneSourceAllowedError extends QueryBuilderError {
|
|
293
|
+
constructor(context: string) {
|
|
294
|
+
super(`Only one source is allowed in the ${context}`)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export class SubQueryMustHaveFromClauseError extends QueryBuilderError {
|
|
299
|
+
constructor(context: string) {
|
|
300
|
+
super(`A sub query passed to a ${context} must have a from clause itself`)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export class InvalidSourceError extends QueryBuilderError {
|
|
305
|
+
constructor() {
|
|
306
|
+
super(`Invalid source`)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export class JoinConditionMustBeEqualityError extends QueryBuilderError {
|
|
311
|
+
constructor() {
|
|
312
|
+
super(`Join condition must be an equality expression`)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export class QueryMustHaveFromClauseError extends QueryBuilderError {
|
|
317
|
+
constructor() {
|
|
318
|
+
super(`Query must have a from clause`)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Query Compilation Errors
|
|
323
|
+
export class QueryCompilationError extends TanStackDBError {
|
|
324
|
+
constructor(message: string) {
|
|
325
|
+
super(message)
|
|
326
|
+
this.name = `QueryCompilationError`
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export class DistinctRequiresSelectError extends QueryCompilationError {
|
|
331
|
+
constructor() {
|
|
332
|
+
super(`DISTINCT requires a SELECT clause.`)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export class HavingRequiresGroupByError extends QueryCompilationError {
|
|
337
|
+
constructor() {
|
|
338
|
+
super(`HAVING clause requires GROUP BY clause`)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export class LimitOffsetRequireOrderByError extends QueryCompilationError {
|
|
343
|
+
constructor() {
|
|
344
|
+
super(
|
|
345
|
+
`LIMIT and OFFSET require an ORDER BY clause to ensure deterministic results`
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export class CollectionInputNotFoundError extends QueryCompilationError {
|
|
351
|
+
constructor(collectionId: string) {
|
|
352
|
+
super(`Input for collection "${collectionId}" not found in inputs map`)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export class UnsupportedFromTypeError extends QueryCompilationError {
|
|
357
|
+
constructor(type: string) {
|
|
358
|
+
super(`Unsupported FROM type: ${type}`)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export class UnknownExpressionTypeError extends QueryCompilationError {
|
|
363
|
+
constructor(type: string) {
|
|
364
|
+
super(`Unknown expression type: ${type}`)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export class EmptyReferencePathError extends QueryCompilationError {
|
|
369
|
+
constructor() {
|
|
370
|
+
super(`Reference path cannot be empty`)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export class UnknownFunctionError extends QueryCompilationError {
|
|
375
|
+
constructor(functionName: string) {
|
|
376
|
+
super(`Unknown function: ${functionName}`)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// JOIN Operation Errors
|
|
381
|
+
export class JoinError extends TanStackDBError {
|
|
382
|
+
constructor(message: string) {
|
|
383
|
+
super(message)
|
|
384
|
+
this.name = `JoinError`
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export class UnsupportedJoinTypeError extends JoinError {
|
|
389
|
+
constructor(joinType: string) {
|
|
390
|
+
super(`Unsupported join type: ${joinType}`)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export class InvalidJoinConditionSameTableError extends JoinError {
|
|
395
|
+
constructor(tableAlias: string) {
|
|
396
|
+
super(
|
|
397
|
+
`Invalid join condition: both expressions refer to the same table "${tableAlias}"`
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export class InvalidJoinConditionTableMismatchError extends JoinError {
|
|
403
|
+
constructor(mainTableAlias: string, joinedTableAlias: string) {
|
|
404
|
+
super(
|
|
405
|
+
`Invalid join condition: expressions must reference table aliases "${mainTableAlias}" and "${joinedTableAlias}"`
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export class InvalidJoinConditionWrongTablesError extends JoinError {
|
|
411
|
+
constructor(
|
|
412
|
+
leftTableAlias: string,
|
|
413
|
+
rightTableAlias: string,
|
|
414
|
+
mainTableAlias: string,
|
|
415
|
+
joinedTableAlias: string
|
|
416
|
+
) {
|
|
417
|
+
super(
|
|
418
|
+
`Invalid join condition: expressions reference tables "${leftTableAlias}" and "${rightTableAlias}" but join is between "${mainTableAlias}" and "${joinedTableAlias}"`
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export class UnsupportedJoinSourceTypeError extends JoinError {
|
|
424
|
+
constructor(type: string) {
|
|
425
|
+
super(`Unsupported join source type: ${type}`)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// GROUP BY and Aggregation Errors
|
|
430
|
+
export class GroupByError extends TanStackDBError {
|
|
431
|
+
constructor(message: string) {
|
|
432
|
+
super(message)
|
|
433
|
+
this.name = `GroupByError`
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export class NonAggregateExpressionNotInGroupByError extends GroupByError {
|
|
438
|
+
constructor(alias: string) {
|
|
439
|
+
super(
|
|
440
|
+
`Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export class UnsupportedAggregateFunctionError extends GroupByError {
|
|
446
|
+
constructor(functionName: string) {
|
|
447
|
+
super(`Unsupported aggregate function: ${functionName}`)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export class AggregateFunctionNotInSelectError extends GroupByError {
|
|
452
|
+
constructor(functionName: string) {
|
|
453
|
+
super(
|
|
454
|
+
`Aggregate function in HAVING clause must also be in SELECT clause: ${functionName}`
|
|
455
|
+
)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export class UnknownHavingExpressionTypeError extends GroupByError {
|
|
460
|
+
constructor(type: string) {
|
|
461
|
+
super(`Unknown expression type in HAVING clause: ${type}`)
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Storage Errors
|
|
466
|
+
export class StorageError extends TanStackDBError {
|
|
467
|
+
constructor(message: string) {
|
|
468
|
+
super(message)
|
|
469
|
+
this.name = `StorageError`
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export class SerializationError extends StorageError {
|
|
474
|
+
constructor(operation: string, originalError: string) {
|
|
475
|
+
super(
|
|
476
|
+
`Cannot ${operation} item because it cannot be JSON serialized: ${originalError}`
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// LocalStorage Collection Errors
|
|
482
|
+
export class LocalStorageCollectionError extends StorageError {
|
|
483
|
+
constructor(message: string) {
|
|
484
|
+
super(message)
|
|
485
|
+
this.name = `LocalStorageCollectionError`
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export class StorageKeyRequiredError extends LocalStorageCollectionError {
|
|
490
|
+
constructor() {
|
|
491
|
+
super(`[LocalStorageCollection] storageKey must be provided.`)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export class NoStorageAvailableError extends LocalStorageCollectionError {
|
|
496
|
+
constructor() {
|
|
497
|
+
super(
|
|
498
|
+
`[LocalStorageCollection] No storage available. Please provide a storage option or ensure window.localStorage is available.`
|
|
499
|
+
)
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export class NoStorageEventApiError extends LocalStorageCollectionError {
|
|
504
|
+
constructor() {
|
|
505
|
+
super(
|
|
506
|
+
`[LocalStorageCollection] No storage event API available. Please provide a storageEventApi option or ensure window is available.`
|
|
507
|
+
)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export class InvalidStorageDataFormatError extends LocalStorageCollectionError {
|
|
512
|
+
constructor(storageKey: string, key: string) {
|
|
513
|
+
super(
|
|
514
|
+
`[LocalStorageCollection] Invalid data format in storage key "${storageKey}" for key "${key}".`
|
|
515
|
+
)
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export class InvalidStorageObjectFormatError extends LocalStorageCollectionError {
|
|
520
|
+
constructor(storageKey: string) {
|
|
521
|
+
super(
|
|
522
|
+
`[LocalStorageCollection] Invalid data format in storage key "${storageKey}". Expected object format.`
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Sync Cleanup Errors
|
|
528
|
+
export class SyncCleanupError extends TanStackDBError {
|
|
529
|
+
constructor(collectionId: string, error: Error | string) {
|
|
530
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
531
|
+
super(
|
|
532
|
+
`Collection "${collectionId}" sync cleanup function threw an error: ${message}`
|
|
533
|
+
)
|
|
534
|
+
this.name = `SyncCleanupError`
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Query Optimizer Errors
|
|
539
|
+
export class QueryOptimizerError extends TanStackDBError {
|
|
540
|
+
constructor(message: string) {
|
|
541
|
+
super(message)
|
|
542
|
+
this.name = `QueryOptimizerError`
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export class CannotCombineEmptyExpressionListError extends QueryOptimizerError {
|
|
547
|
+
constructor() {
|
|
548
|
+
super(`Cannot combine empty expression list`)
|
|
549
|
+
}
|
|
550
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,12 +3,18 @@ export * from "./collection"
|
|
|
3
3
|
export * from "./SortedMap"
|
|
4
4
|
export * from "./transactions"
|
|
5
5
|
export * from "./types"
|
|
6
|
-
export * from "./errors"
|
|
7
6
|
export * from "./proxy"
|
|
8
7
|
export * from "./query/index.js"
|
|
9
8
|
export * from "./optimistic-action"
|
|
10
9
|
export * from "./local-only"
|
|
11
10
|
export * from "./local-storage"
|
|
11
|
+
export * from "./errors"
|
|
12
|
+
|
|
13
|
+
// Index system exports
|
|
14
|
+
export * from "./indexes/base-index.js"
|
|
15
|
+
export * from "./indexes/btree-index.js"
|
|
16
|
+
export * from "./indexes/lazy-index.js"
|
|
17
|
+
export { type IndexOptions } from "./indexes/index-options.js"
|
|
12
18
|
|
|
13
19
|
// Re-export some stuff explicitly to ensure the type & value is exported
|
|
14
20
|
export type { Collection } from "./collection"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { BTreeIndex } from "./btree-index"
|
|
2
|
+
import type { BasicExpression } from "../query/ir"
|
|
3
|
+
import type { CollectionImpl } from "../collection"
|
|
4
|
+
|
|
5
|
+
export interface AutoIndexConfig {
|
|
6
|
+
autoIndex?: `off` | `eager`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Analyzes a where expression and creates indexes for all simple operations on single fields
|
|
11
|
+
*/
|
|
12
|
+
export function ensureIndexForExpression<
|
|
13
|
+
T extends Record<string, any>,
|
|
14
|
+
TKey extends string | number,
|
|
15
|
+
>(
|
|
16
|
+
expression: BasicExpression,
|
|
17
|
+
collection: CollectionImpl<T, TKey, any, any, any>
|
|
18
|
+
): void {
|
|
19
|
+
// Only proceed if auto-indexing is enabled
|
|
20
|
+
if (collection.config.autoIndex !== `eager`) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Don't auto-index during sync operations
|
|
25
|
+
if (
|
|
26
|
+
collection.status === `loading` ||
|
|
27
|
+
collection.status === `initialCommit`
|
|
28
|
+
) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Extract all indexable expressions and create indexes for them
|
|
33
|
+
const indexableExpressions = extractIndexableExpressions(expression)
|
|
34
|
+
|
|
35
|
+
for (const { fieldName, fieldPath } of indexableExpressions) {
|
|
36
|
+
// Check if we already have an index for this field
|
|
37
|
+
const existingIndex = Array.from(collection.indexes.values()).find(
|
|
38
|
+
(index) => index.matchesField(fieldPath)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if (existingIndex) {
|
|
42
|
+
continue // Index already exists
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create a new index for this field using the collection's createIndex method
|
|
46
|
+
try {
|
|
47
|
+
collection.createIndex((row) => (row as any)[fieldName], {
|
|
48
|
+
name: `auto_${fieldName}`,
|
|
49
|
+
indexType: BTreeIndex,
|
|
50
|
+
})
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn(
|
|
53
|
+
`Failed to create auto-index for field "${fieldName}":`,
|
|
54
|
+
error
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extracts all indexable expressions from a where expression
|
|
62
|
+
*/
|
|
63
|
+
function extractIndexableExpressions(
|
|
64
|
+
expression: BasicExpression
|
|
65
|
+
): Array<{ fieldName: string; fieldPath: Array<string> }> {
|
|
66
|
+
const results: Array<{ fieldName: string; fieldPath: Array<string> }> = []
|
|
67
|
+
|
|
68
|
+
function extractFromExpression(expr: BasicExpression): void {
|
|
69
|
+
if (expr.type !== `func`) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const func = expr as any
|
|
74
|
+
|
|
75
|
+
// Handle 'and' expressions by recursively processing all arguments
|
|
76
|
+
if (func.name === `and`) {
|
|
77
|
+
for (const arg of func.args) {
|
|
78
|
+
extractFromExpression(arg)
|
|
79
|
+
}
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if this is a supported operation
|
|
84
|
+
const supportedOperations = [`eq`, `gt`, `gte`, `lt`, `lte`, `in`]
|
|
85
|
+
if (!supportedOperations.includes(func.name)) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check if the first argument is a property reference (single field)
|
|
90
|
+
if (func.args.length < 1 || func.args[0].type !== `ref`) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fieldRef = func.args[0]
|
|
95
|
+
const fieldPath = fieldRef.path
|
|
96
|
+
|
|
97
|
+
// Skip if it's not a simple field (e.g., nested properties or array access)
|
|
98
|
+
if (fieldPath.length !== 1) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const fieldName = fieldPath[0]
|
|
103
|
+
results.push({ fieldName, fieldPath })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
extractFromExpression(expression)
|
|
107
|
+
return results
|
|
108
|
+
}
|