@tanstack/db 0.1.0 → 0.1.3
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/collection.cjs +22 -12
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +15 -2
- package/dist/cjs/indexes/btree-index.cjs +3 -3
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/local-only.d.cts +2 -14
- package/dist/cjs/local-storage.cjs.map +1 -1
- package/dist/cjs/local-storage.d.cts +3 -14
- package/dist/cjs/query/builder/index.cjs +9 -2
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +2 -2
- package/dist/cjs/query/builder/types.d.cts +27 -6
- package/dist/cjs/query/compiler/order-by.cjs +19 -21
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +2 -1
- package/dist/cjs/transactions.cjs +1 -0
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/utils/comparison.cjs +29 -7
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/comparison.d.cts +6 -2
- package/dist/esm/collection.d.ts +15 -2
- package/dist/esm/collection.js +22 -12
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/indexes/btree-index.js +3 -3
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/local-only.d.ts +2 -14
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/local-storage.d.ts +3 -14
- package/dist/esm/local-storage.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +2 -2
- package/dist/esm/query/builder/index.js +9 -2
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +27 -6
- package/dist/esm/query/compiler/order-by.js +20 -22
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/ir.d.ts +2 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/transactions.js +1 -0
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/utils/comparison.d.ts +6 -2
- package/dist/esm/utils/comparison.js +30 -8
- package/dist/esm/utils/comparison.js.map +1 -1
- package/package.json +2 -2
- package/src/collection.ts +108 -16
- package/src/indexes/btree-index.ts +3 -3
- package/src/local-only.ts +6 -1
- package/src/local-storage.ts +6 -1
- package/src/query/builder/index.ts +19 -2
- package/src/query/builder/types.ts +48 -15
- package/src/query/compiler/order-by.ts +22 -26
- package/src/query/ir.ts +2 -1
- package/src/transactions.ts +1 -0
- package/src/utils/comparison.ts +40 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CollectionImpl } from "../../collection.js"
|
|
2
|
-
import type { Aggregate, BasicExpression } from "../ir.js"
|
|
2
|
+
import type { Aggregate, BasicExpression, OrderByDirection } from "../ir.js"
|
|
3
3
|
import type { QueryBuilder } from "./index.js"
|
|
4
|
+
import type { ResolveType } from "../../types.js"
|
|
4
5
|
|
|
5
6
|
export interface Context {
|
|
6
7
|
// The collections available in the base schema
|
|
@@ -27,13 +28,16 @@ export type Source = {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
// Helper type to infer collection type from CollectionImpl
|
|
31
|
+
// This uses ResolveType directly to ensure consistency with collection creation logic
|
|
30
32
|
export type InferCollectionType<T> =
|
|
31
|
-
T extends CollectionImpl<infer U
|
|
33
|
+
T extends CollectionImpl<infer U, any, any, infer TSchema, any>
|
|
34
|
+
? ResolveType<U, TSchema, U>
|
|
35
|
+
: never
|
|
32
36
|
|
|
33
37
|
// Helper type to create schema from source
|
|
34
38
|
export type SchemaFromSource<T extends Source> = Prettify<{
|
|
35
|
-
[K in keyof T]: T[K] extends CollectionImpl<
|
|
36
|
-
?
|
|
39
|
+
[K in keyof T]: T[K] extends CollectionImpl<any, any, any, any, any>
|
|
40
|
+
? InferCollectionType<T[K]>
|
|
37
41
|
: T[K] extends QueryBuilder<infer TContext>
|
|
38
42
|
? GetResult<TContext>
|
|
39
43
|
: never
|
|
@@ -58,16 +62,18 @@ export type SelectObject<
|
|
|
58
62
|
// Helper type to get the result type from a select object
|
|
59
63
|
export type ResultTypeFromSelect<TSelectObject> = {
|
|
60
64
|
[K in keyof TSelectObject]: TSelectObject[K] extends RefProxy<infer T>
|
|
61
|
-
?
|
|
62
|
-
T
|
|
65
|
+
? T
|
|
63
66
|
: TSelectObject[K] extends BasicExpression<infer T>
|
|
64
67
|
? T
|
|
65
68
|
: TSelectObject[K] extends Aggregate<infer T>
|
|
66
69
|
? T
|
|
67
70
|
: TSelectObject[K] extends RefProxyFor<infer T>
|
|
68
|
-
?
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
? T
|
|
72
|
+
: TSelectObject[K] extends undefined
|
|
73
|
+
? undefined
|
|
74
|
+
: TSelectObject[K] extends { __type: infer U }
|
|
75
|
+
? U
|
|
76
|
+
: never
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
// Callback type for orderBy clauses
|
|
@@ -75,6 +81,29 @@ export type OrderByCallback<TContext extends Context> = (
|
|
|
75
81
|
refs: RefProxyForContext<TContext>
|
|
76
82
|
) => any
|
|
77
83
|
|
|
84
|
+
export type OrderByOptions = {
|
|
85
|
+
direction?: OrderByDirection
|
|
86
|
+
nulls?: `first` | `last`
|
|
87
|
+
} & StringSortOpts
|
|
88
|
+
|
|
89
|
+
export type StringSortOpts =
|
|
90
|
+
| {
|
|
91
|
+
stringSort?: `lexical`
|
|
92
|
+
}
|
|
93
|
+
| {
|
|
94
|
+
stringSort?: `locale`
|
|
95
|
+
locale?: string
|
|
96
|
+
localeOptions?: object
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type CompareOptions = {
|
|
100
|
+
direction: OrderByDirection
|
|
101
|
+
nulls: `first` | `last`
|
|
102
|
+
stringSort: `lexical` | `locale`
|
|
103
|
+
locale?: string
|
|
104
|
+
localeOptions?: object
|
|
105
|
+
}
|
|
106
|
+
|
|
78
107
|
// Callback type for groupBy clauses
|
|
79
108
|
export type GroupByCallback<TContext extends Context> = (
|
|
80
109
|
refs: RefProxyForContext<TContext>
|
|
@@ -119,12 +148,11 @@ export type RefProxyFor<T> = OmitRefProxy<
|
|
|
119
148
|
? // T is optional (T | undefined) but not exactly undefined
|
|
120
149
|
NonUndefined<T> extends Record<string, any>
|
|
121
150
|
? {
|
|
122
|
-
|
|
123
|
-
[K in keyof NonUndefined<T>]: NonUndefined<T>[K] extends Record<
|
|
151
|
+
[K in keyof NonUndefined<T>]-?: NonUndefined<T>[K] extends Record<
|
|
124
152
|
string,
|
|
125
153
|
any
|
|
126
154
|
>
|
|
127
|
-
? RefProxyFor<NonUndefined<T>[K]
|
|
155
|
+
? RefProxyFor<NonUndefined<T>[K]> &
|
|
128
156
|
RefProxy<NonUndefined<T>[K] | undefined>
|
|
129
157
|
: RefProxy<NonUndefined<T>[K] | undefined>
|
|
130
158
|
} & RefProxy<T>
|
|
@@ -132,9 +160,14 @@ export type RefProxyFor<T> = OmitRefProxy<
|
|
|
132
160
|
: // T is not optional
|
|
133
161
|
T extends Record<string, any>
|
|
134
162
|
? {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
// Make all properties required, but for optional ones, include undefined in the RefProxy type
|
|
164
|
+
[K in keyof T]-?: undefined extends T[K]
|
|
165
|
+
? T[K] extends Record<string, any>
|
|
166
|
+
? RefProxyFor<T[K]> & RefProxy<T[K]>
|
|
167
|
+
: RefProxy<T[K]>
|
|
168
|
+
: T[K] extends Record<string, any>
|
|
169
|
+
? RefProxyFor<T[K]> & RefProxy<T[K]>
|
|
170
|
+
: RefProxy<T[K]>
|
|
138
171
|
} & RefProxy<T>
|
|
139
172
|
: RefProxy<T>
|
|
140
173
|
>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { orderByWithFractionalIndex } from "@tanstack/db-ivm"
|
|
2
|
-
import {
|
|
2
|
+
import { defaultComparator, makeComparator } from "../../utils/comparison.js"
|
|
3
3
|
import { compileExpression } from "./evaluators.js"
|
|
4
4
|
import type { OrderByClause } from "../ir.js"
|
|
5
5
|
import type { NamespacedAndKeyedStream, NamespacedRow } from "../../types.js"
|
|
@@ -19,7 +19,7 @@ export function processOrderBy(
|
|
|
19
19
|
// Pre-compile all order by expressions
|
|
20
20
|
const compiledOrderBy = orderByClause.map((clause) => ({
|
|
21
21
|
compiledExpression: compileExpression(clause.expression),
|
|
22
|
-
|
|
22
|
+
compareOptions: clause.compareOptions,
|
|
23
23
|
}))
|
|
24
24
|
|
|
25
25
|
// Create a value extractor function for the orderBy operator
|
|
@@ -53,35 +53,31 @@ export function processOrderBy(
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// Create a multi-property comparator that respects the order and direction of each property
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (result !== 0) {
|
|
68
|
-
return result
|
|
69
|
-
}
|
|
56
|
+
const comparator = (a: unknown, b: unknown) => {
|
|
57
|
+
// If we're comparing arrays (multiple properties), compare each property in order
|
|
58
|
+
if (orderByClause.length > 1) {
|
|
59
|
+
const arrayA = a as Array<unknown>
|
|
60
|
+
const arrayB = b as Array<unknown>
|
|
61
|
+
for (let i = 0; i < orderByClause.length; i++) {
|
|
62
|
+
const clause = orderByClause[i]!
|
|
63
|
+
const compareFn = makeComparator(clause.compareOptions)
|
|
64
|
+
const result = compareFn(arrayA[i], arrayB[i])
|
|
65
|
+
if (result !== 0) {
|
|
66
|
+
return result
|
|
70
67
|
}
|
|
71
|
-
return arrayA.length - arrayB.length
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Single property comparison
|
|
75
|
-
if (orderByClause.length === 1) {
|
|
76
|
-
const direction = orderByClause[0]!.direction
|
|
77
|
-
return direction === `desc` ? descComparator(a, b) : ascComparator(a, b)
|
|
78
68
|
}
|
|
69
|
+
return arrayA.length - arrayB.length
|
|
70
|
+
}
|
|
79
71
|
|
|
80
|
-
|
|
72
|
+
// Single property comparison
|
|
73
|
+
if (orderByClause.length === 1) {
|
|
74
|
+
const clause = orderByClause[0]!
|
|
75
|
+
const compareFn = makeComparator(clause.compareOptions)
|
|
76
|
+
return compareFn(a, b)
|
|
81
77
|
}
|
|
82
|
-
}
|
|
83
78
|
|
|
84
|
-
|
|
79
|
+
return defaultComparator(a, b)
|
|
80
|
+
}
|
|
85
81
|
|
|
86
82
|
// Use fractional indexing and return the tuple [value, index]
|
|
87
83
|
return pipeline.pipe(
|
package/src/query/ir.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
This is the intermediate representation of the query.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { CompareOptions } from "./builder/types"
|
|
5
6
|
import type { CollectionImpl } from "../collection"
|
|
6
7
|
import type { NamespacedRow } from "../types"
|
|
7
8
|
|
|
@@ -48,7 +49,7 @@ export type OrderBy = Array<OrderByClause>
|
|
|
48
49
|
|
|
49
50
|
export type OrderByClause = {
|
|
50
51
|
expression: BasicExpression
|
|
51
|
-
|
|
52
|
+
compareOptions: CompareOptions
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export type OrderByDirection = `asc` | `desc`
|
package/src/transactions.ts
CHANGED
package/src/utils/comparison.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CompareOptions } from "../query/builder/types"
|
|
2
|
+
|
|
1
3
|
// WeakMap to store stable IDs for objects
|
|
2
4
|
const objectIds = new WeakMap<object, number>()
|
|
3
5
|
let nextObjectId = 1
|
|
@@ -19,21 +21,26 @@ function getObjectId(obj: object): number {
|
|
|
19
21
|
* Handles null/undefined, strings, arrays, dates, objects, and primitives
|
|
20
22
|
* Always sorts null/undefined values first
|
|
21
23
|
*/
|
|
22
|
-
export const ascComparator = (a: any, b: any): number => {
|
|
24
|
+
export const ascComparator = (a: any, b: any, opts: CompareOptions): number => {
|
|
25
|
+
const { nulls } = opts
|
|
26
|
+
|
|
23
27
|
// Handle null/undefined
|
|
24
28
|
if (a == null && b == null) return 0
|
|
25
|
-
if (a == null) return -1
|
|
26
|
-
if (b == null) return 1
|
|
29
|
+
if (a == null) return nulls === `first` ? -1 : 1
|
|
30
|
+
if (b == null) return nulls === `first` ? 1 : -1
|
|
27
31
|
|
|
28
32
|
// if a and b are both strings, compare them based on locale
|
|
29
33
|
if (typeof a === `string` && typeof b === `string`) {
|
|
30
|
-
|
|
34
|
+
if (opts.stringSort === `locale`) {
|
|
35
|
+
return a.localeCompare(b, opts.locale, opts.localeOptions)
|
|
36
|
+
}
|
|
37
|
+
// For lexical sort we rely on direct comparison for primitive values
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
// if a and b are both arrays, compare them element by element
|
|
34
41
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
35
42
|
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
36
|
-
const result = ascComparator(a[i], b[i])
|
|
43
|
+
const result = ascComparator(a[i], b[i], opts)
|
|
37
44
|
if (result !== 0) {
|
|
38
45
|
return result
|
|
39
46
|
}
|
|
@@ -74,6 +81,32 @@ export const ascComparator = (a: any, b: any): number => {
|
|
|
74
81
|
* Descending comparator function for ordering values
|
|
75
82
|
* Handles null/undefined as largest values (opposite of ascending)
|
|
76
83
|
*/
|
|
77
|
-
export const descComparator = (
|
|
78
|
-
|
|
84
|
+
export const descComparator = (
|
|
85
|
+
a: unknown,
|
|
86
|
+
b: unknown,
|
|
87
|
+
opts: CompareOptions
|
|
88
|
+
): number => {
|
|
89
|
+
return ascComparator(b, a, {
|
|
90
|
+
...opts,
|
|
91
|
+
nulls: opts.nulls === `first` ? `last` : `first`,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function makeComparator(
|
|
96
|
+
opts: CompareOptions
|
|
97
|
+
): (a: any, b: any) => number {
|
|
98
|
+
return (a, b) => {
|
|
99
|
+
if (opts.direction === `asc`) {
|
|
100
|
+
return ascComparator(a, b, opts)
|
|
101
|
+
} else {
|
|
102
|
+
return descComparator(a, b, opts)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
79
105
|
}
|
|
106
|
+
|
|
107
|
+
/** Default comparator orders values in ascending order with nulls first and locale string comparison. */
|
|
108
|
+
export const defaultComparator = makeComparator({
|
|
109
|
+
direction: `asc`,
|
|
110
|
+
nulls: `first`,
|
|
111
|
+
stringSort: `locale`,
|
|
112
|
+
})
|