@tanstack/db 0.5.32 → 0.6.0
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/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +24 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +12 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/effect.cjs +602 -0
- package/dist/cjs/query/effect.cjs.map +1 -0
- package/dist/cjs/query/effect.d.cts +94 -0
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +493 -66
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +33 -100
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +0 -1
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +219 -0
- package/dist/cjs/query/live/utils.cjs.map +1 -0
- package/dist/cjs/query/live/utils.d.cts +110 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +12 -3
- package/dist/esm/index.js +27 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/effect.d.ts +94 -0
- package/dist/esm/query/effect.js +602 -0
- package/dist/esm/query/effect.js.map +1 -0
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +492 -65
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.d.ts +0 -1
- package/dist/esm/query/live/collection-subscriber.js +31 -98
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +110 -0
- package/dist/esm/query/live/utils.js +219 -0
- package/dist/esm/query/live/utils.js.map +1 -0
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +2 -2
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +57 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/effect.ts +1119 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +778 -104
- package/src/query/live/collection-subscriber.ts +40 -156
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +417 -0
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Dev Mode - Helps developers identify when indexes would improve performance
|
|
3
|
+
*
|
|
4
|
+
* Dev mode suggestions are ON by default in non-production builds.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Dev mode detection settings - ON by default in non-production
|
|
8
|
+
let devModeConfig: IndexDevModeConfig = {
|
|
9
|
+
enabled: true,
|
|
10
|
+
collectionSizeThreshold: 1000,
|
|
11
|
+
slowQueryThresholdMs: 10,
|
|
12
|
+
onSuggestion: null,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IndexDevModeConfig {
|
|
16
|
+
/** Enable dev mode index suggestions */
|
|
17
|
+
enabled: boolean
|
|
18
|
+
/** Suggest indexes when collection has more than this many items */
|
|
19
|
+
collectionSizeThreshold: number
|
|
20
|
+
/** Suggest indexes when queries take longer than this (ms) */
|
|
21
|
+
slowQueryThresholdMs: number
|
|
22
|
+
/** Custom handler for index suggestions */
|
|
23
|
+
onSuggestion: ((suggestion: IndexSuggestion) => void) | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IndexSuggestion {
|
|
27
|
+
type: `collection-size` | `slow-query` | `frequent-field`
|
|
28
|
+
collectionId: string
|
|
29
|
+
fieldPath: Array<string>
|
|
30
|
+
message: string
|
|
31
|
+
collectionSize?: number
|
|
32
|
+
queryTimeMs?: number
|
|
33
|
+
queryCount?: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Track query patterns for dev mode
|
|
37
|
+
const queryPatterns = new Map<
|
|
38
|
+
string,
|
|
39
|
+
{
|
|
40
|
+
fieldPath: Array<string>
|
|
41
|
+
queryCount: number
|
|
42
|
+
totalTimeMs: number
|
|
43
|
+
avgTimeMs: number
|
|
44
|
+
}
|
|
45
|
+
>()
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Configure dev mode for index suggestions
|
|
49
|
+
*/
|
|
50
|
+
export function configureIndexDevMode(
|
|
51
|
+
config: Partial<IndexDevModeConfig>,
|
|
52
|
+
): void {
|
|
53
|
+
devModeConfig = { ...devModeConfig, ...config }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current dev mode configuration
|
|
58
|
+
*/
|
|
59
|
+
export function getIndexDevModeConfig(): IndexDevModeConfig {
|
|
60
|
+
return devModeConfig
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if dev mode is enabled
|
|
65
|
+
*/
|
|
66
|
+
export function isDevModeEnabled(): boolean {
|
|
67
|
+
return devModeConfig.enabled && process.env.NODE_ENV !== `production`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Emit an index suggestion (dev mode only)
|
|
72
|
+
*/
|
|
73
|
+
export function emitIndexSuggestion(suggestion: IndexSuggestion): void {
|
|
74
|
+
if (!isDevModeEnabled()) return
|
|
75
|
+
|
|
76
|
+
if (devModeConfig.onSuggestion) {
|
|
77
|
+
try {
|
|
78
|
+
devModeConfig.onSuggestion(suggestion)
|
|
79
|
+
} catch {
|
|
80
|
+
// Don't let a buggy callback crash query execution
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
// Default: log to console with helpful formatting
|
|
84
|
+
console.warn(
|
|
85
|
+
`[TanStack DB] Index suggestion for "${suggestion.collectionId}":\n` +
|
|
86
|
+
` ${suggestion.message}\n` +
|
|
87
|
+
` Field: ${suggestion.fieldPath.join(`.`)}\n` +
|
|
88
|
+
` Add index: collection.createIndex((row) => row.${suggestion.fieldPath.join(`.`)})`,
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Track a query for dev mode analysis
|
|
95
|
+
*/
|
|
96
|
+
export function trackQuery(
|
|
97
|
+
collectionId: string,
|
|
98
|
+
fieldPath: Array<string>,
|
|
99
|
+
executionTimeMs: number,
|
|
100
|
+
): void {
|
|
101
|
+
if (!isDevModeEnabled()) return
|
|
102
|
+
|
|
103
|
+
const key = `${collectionId}:${fieldPath.join(`.`)}`
|
|
104
|
+
const existing = queryPatterns.get(key)
|
|
105
|
+
|
|
106
|
+
if (existing) {
|
|
107
|
+
existing.queryCount++
|
|
108
|
+
existing.totalTimeMs += executionTimeMs
|
|
109
|
+
existing.avgTimeMs = existing.totalTimeMs / existing.queryCount
|
|
110
|
+
} else {
|
|
111
|
+
queryPatterns.set(key, {
|
|
112
|
+
fieldPath,
|
|
113
|
+
queryCount: 1,
|
|
114
|
+
totalTimeMs: executionTimeMs,
|
|
115
|
+
avgTimeMs: executionTimeMs,
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check if we should suggest an index
|
|
120
|
+
const pattern = queryPatterns.get(key)!
|
|
121
|
+
if (pattern.avgTimeMs > devModeConfig.slowQueryThresholdMs) {
|
|
122
|
+
emitIndexSuggestion({
|
|
123
|
+
type: `slow-query`,
|
|
124
|
+
collectionId,
|
|
125
|
+
fieldPath,
|
|
126
|
+
message: `Queries on "${fieldPath.join(`.`)}" are slow (avg ${pattern.avgTimeMs.toFixed(1)}ms). Consider adding an index.`,
|
|
127
|
+
queryTimeMs: pattern.avgTimeMs,
|
|
128
|
+
queryCount: pattern.queryCount,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check collection size and suggest index if needed (dev mode)
|
|
135
|
+
*/
|
|
136
|
+
export function checkCollectionSizeForIndex(
|
|
137
|
+
collectionId: string,
|
|
138
|
+
collectionSize: number,
|
|
139
|
+
fieldPath: Array<string>,
|
|
140
|
+
): void {
|
|
141
|
+
if (!isDevModeEnabled()) return
|
|
142
|
+
|
|
143
|
+
if (collectionSize > devModeConfig.collectionSizeThreshold) {
|
|
144
|
+
emitIndexSuggestion({
|
|
145
|
+
type: `collection-size`,
|
|
146
|
+
collectionId,
|
|
147
|
+
fieldPath,
|
|
148
|
+
message: `Collection has ${collectionSize} items. Queries on "${fieldPath.join(`.`)}" may benefit from an index.`,
|
|
149
|
+
collectionSize,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Clear query pattern tracking (useful for tests)
|
|
156
|
+
*/
|
|
157
|
+
export function clearQueryPatterns(): void {
|
|
158
|
+
queryPatterns.clear()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get query patterns (useful for debugging/testing)
|
|
163
|
+
*/
|
|
164
|
+
export function getQueryPatterns(): Map<
|
|
165
|
+
string,
|
|
166
|
+
{
|
|
167
|
+
fieldPath: Array<string>
|
|
168
|
+
queryCount: number
|
|
169
|
+
totalTimeMs: number
|
|
170
|
+
avgTimeMs: number
|
|
171
|
+
}
|
|
172
|
+
> {
|
|
173
|
+
return new Map(queryPatterns)
|
|
174
|
+
}
|
package/src/local-only.ts
CHANGED
|
@@ -314,9 +314,16 @@ function createLocalOnlySync<T extends object, TKey extends string | number>(
|
|
|
314
314
|
syncWrite = write
|
|
315
315
|
syncCommit = commit
|
|
316
316
|
collection = params.collection
|
|
317
|
+
params.collection._state.isLocalOnly = true
|
|
317
318
|
|
|
318
319
|
// Apply initial data if provided
|
|
319
320
|
if (initialData && initialData.length > 0) {
|
|
321
|
+
// Mark initial data as local so $origin is 'local' for local-only collections
|
|
322
|
+
for (const item of initialData) {
|
|
323
|
+
const key = params.collection.getKeyFromItem(item)
|
|
324
|
+
params.collection._state.pendingLocalChanges.add(key)
|
|
325
|
+
}
|
|
326
|
+
|
|
320
327
|
begin()
|
|
321
328
|
initialData.forEach((item) => {
|
|
322
329
|
write({
|
|
@@ -2,7 +2,13 @@ import { Aggregate, Func } from '../ir'
|
|
|
2
2
|
import { toExpression } from './ref-proxy.js'
|
|
3
3
|
import type { BasicExpression } from '../ir'
|
|
4
4
|
import type { RefProxy } from './ref-proxy.js'
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
Context,
|
|
7
|
+
GetRawResult,
|
|
8
|
+
RefLeaf,
|
|
9
|
+
StringifiableScalar,
|
|
10
|
+
} from './types.js'
|
|
11
|
+
import type { QueryBuilder } from './index.js'
|
|
6
12
|
|
|
7
13
|
type StringRef =
|
|
8
14
|
| RefLeaf<string>
|
|
@@ -37,8 +43,20 @@ type ComparisonOperandPrimitive<T extends string | number | boolean> =
|
|
|
37
43
|
| undefined
|
|
38
44
|
| null
|
|
39
45
|
|
|
40
|
-
// Helper type for
|
|
41
|
-
type ExpressionLike =
|
|
46
|
+
// Helper type for values that can be lowered to expressions.
|
|
47
|
+
type ExpressionLike =
|
|
48
|
+
| Aggregate
|
|
49
|
+
| BasicExpression
|
|
50
|
+
| RefProxy<any>
|
|
51
|
+
| RefLeaf<any>
|
|
52
|
+
| string
|
|
53
|
+
| number
|
|
54
|
+
| boolean
|
|
55
|
+
| bigint
|
|
56
|
+
| Date
|
|
57
|
+
| null
|
|
58
|
+
| undefined
|
|
59
|
+
| Array<unknown>
|
|
42
60
|
|
|
43
61
|
// Helper type to extract the underlying type from various expression types
|
|
44
62
|
type ExtractType<T> =
|
|
@@ -276,20 +294,61 @@ export function length<T extends ExpressionLike>(
|
|
|
276
294
|
return new Func(`length`, [toExpression(arg)]) as NumericFunctionReturnType<T>
|
|
277
295
|
}
|
|
278
296
|
|
|
297
|
+
export function concat<T extends StringifiableScalar>(
|
|
298
|
+
arg: ToArrayWrapper<T>,
|
|
299
|
+
): ConcatToArrayWrapper<T>
|
|
300
|
+
export function concat(...args: Array<ExpressionLike>): BasicExpression<string>
|
|
279
301
|
export function concat(
|
|
280
|
-
...args: Array<ExpressionLike
|
|
281
|
-
): BasicExpression<string> {
|
|
302
|
+
...args: Array<ExpressionLike | ToArrayWrapper<any>>
|
|
303
|
+
): BasicExpression<string> | ConcatToArrayWrapper<any> {
|
|
304
|
+
const toArrayArg = args.find(
|
|
305
|
+
(arg): arg is ToArrayWrapper<any> => arg instanceof ToArrayWrapper,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
if (toArrayArg) {
|
|
309
|
+
if (args.length !== 1) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`concat(toArray(...)) currently supports only a single toArray(...) argument`,
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
return new ConcatToArrayWrapper(toArrayArg.query)
|
|
315
|
+
}
|
|
316
|
+
|
|
282
317
|
return new Func(
|
|
283
318
|
`concat`,
|
|
284
319
|
args.map((arg) => toExpression(arg)),
|
|
285
320
|
)
|
|
286
321
|
}
|
|
287
322
|
|
|
288
|
-
|
|
323
|
+
// Helper type for coalesce: extracts non-nullish value types from all args
|
|
324
|
+
type CoalesceArgTypes<T extends Array<ExpressionLike>> = {
|
|
325
|
+
[K in keyof T]: NonNullable<ExtractType<T[K]>>
|
|
326
|
+
}[number]
|
|
327
|
+
|
|
328
|
+
// Whether any arg in the tuple is statically guaranteed non-null (i.e., does not include null | undefined)
|
|
329
|
+
type HasGuaranteedNonNull<T extends Array<ExpressionLike>> = {
|
|
330
|
+
[K in keyof T]: null extends ExtractType<T[K]>
|
|
331
|
+
? false
|
|
332
|
+
: undefined extends ExtractType<T[K]>
|
|
333
|
+
? false
|
|
334
|
+
: true
|
|
335
|
+
}[number] extends false
|
|
336
|
+
? false
|
|
337
|
+
: true
|
|
338
|
+
|
|
339
|
+
// coalesce() return type: union of all non-null arg types; null included unless a guaranteed non-null arg exists
|
|
340
|
+
type CoalesceReturnType<T extends Array<ExpressionLike>> =
|
|
341
|
+
HasGuaranteedNonNull<T> extends true
|
|
342
|
+
? BasicExpression<CoalesceArgTypes<T>>
|
|
343
|
+
: BasicExpression<CoalesceArgTypes<T> | null>
|
|
344
|
+
|
|
345
|
+
export function coalesce<T extends [ExpressionLike, ...Array<ExpressionLike>]>(
|
|
346
|
+
...args: T
|
|
347
|
+
): CoalesceReturnType<T> {
|
|
289
348
|
return new Func(
|
|
290
349
|
`coalesce`,
|
|
291
350
|
args.map((arg) => toExpression(arg)),
|
|
292
|
-
)
|
|
351
|
+
) as CoalesceReturnType<T>
|
|
293
352
|
}
|
|
294
353
|
|
|
295
354
|
export function add<T1 extends ExpressionLike, T2 extends ExpressionLike>(
|
|
@@ -376,3 +435,21 @@ export const operators = [
|
|
|
376
435
|
] as const
|
|
377
436
|
|
|
378
437
|
export type OperatorName = (typeof operators)[number]
|
|
438
|
+
|
|
439
|
+
export class ToArrayWrapper<_T = unknown> {
|
|
440
|
+
declare readonly _type: `toArray`
|
|
441
|
+
declare readonly _result: _T
|
|
442
|
+
constructor(public readonly query: QueryBuilder<any>) {}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export class ConcatToArrayWrapper<_T = unknown> {
|
|
446
|
+
declare readonly _type: `concatToArray`
|
|
447
|
+
declare readonly _result: _T
|
|
448
|
+
constructor(public readonly query: QueryBuilder<any>) {}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function toArray<TContext extends Context>(
|
|
452
|
+
query: QueryBuilder<TContext>,
|
|
453
|
+
): ToArrayWrapper<GetRawResult<TContext>> {
|
|
454
|
+
return new ToArrayWrapper(query)
|
|
455
|
+
}
|