@tanstack/db 0.0.26 → 0.0.29
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 +236 -90
- 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/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/indexes/ordered-index.cjs +227 -0
- package/dist/cjs/indexes/ordered-index.cjs.map +1 -0
- package/dist/cjs/indexes/ordered-index.d.cts +72 -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.cjs +18 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +8 -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 +234 -88
- 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/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/indexes/ordered-index.d.ts +72 -0
- package/dist/esm/indexes/ordered-index.js +227 -0
- package/dist/esm/indexes/ordered-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/array-utils.js +18 -0
- package/dist/esm/utils/array-utils.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 +3 -2
- package/src/change-events.ts +257 -0
- package/src/collection.ts +321 -110
- 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/index-options.ts +42 -0
- package/src/indexes/lazy-index.ts +251 -0
- package/src/indexes/ordered-index.ts +305 -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/comparison.ts +79 -0
- package/src/utils/index-optimization.ts +546 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
function isConstructor(resolver) {
|
|
4
|
+
return typeof resolver === `function` && resolver.prototype !== void 0 && resolver.prototype.constructor === resolver;
|
|
5
|
+
}
|
|
6
|
+
async function resolveIndexConstructor(resolver) {
|
|
7
|
+
if (isConstructor(resolver)) {
|
|
8
|
+
return resolver;
|
|
9
|
+
} else {
|
|
10
|
+
return await resolver();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class LazyIndexWrapper {
|
|
14
|
+
constructor(id, expression, name, resolver, options, collectionEntries) {
|
|
15
|
+
this.id = id;
|
|
16
|
+
this.expression = expression;
|
|
17
|
+
this.name = name;
|
|
18
|
+
this.resolver = resolver;
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.collectionEntries = collectionEntries;
|
|
21
|
+
this.indexPromise = null;
|
|
22
|
+
this.resolvedIndex = null;
|
|
23
|
+
if (isConstructor(this.resolver)) {
|
|
24
|
+
this.resolvedIndex = new this.resolver(
|
|
25
|
+
this.id,
|
|
26
|
+
this.expression,
|
|
27
|
+
this.name,
|
|
28
|
+
this.options
|
|
29
|
+
);
|
|
30
|
+
if (this.collectionEntries) {
|
|
31
|
+
this.resolvedIndex.build(this.collectionEntries);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the actual index
|
|
37
|
+
*/
|
|
38
|
+
async resolve() {
|
|
39
|
+
if (this.resolvedIndex) {
|
|
40
|
+
return this.resolvedIndex;
|
|
41
|
+
}
|
|
42
|
+
if (!this.indexPromise) {
|
|
43
|
+
this.indexPromise = this.createIndex();
|
|
44
|
+
}
|
|
45
|
+
this.resolvedIndex = await this.indexPromise;
|
|
46
|
+
return this.resolvedIndex;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if already resolved
|
|
50
|
+
*/
|
|
51
|
+
isResolved() {
|
|
52
|
+
return this.resolvedIndex !== null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get resolved index (throws if not ready)
|
|
56
|
+
*/
|
|
57
|
+
getResolved() {
|
|
58
|
+
if (!this.resolvedIndex) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Index ${this.id} has not been resolved yet. Ensure collection is synced.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return this.resolvedIndex;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get the index ID
|
|
67
|
+
*/
|
|
68
|
+
getId() {
|
|
69
|
+
return this.id;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the index name
|
|
73
|
+
*/
|
|
74
|
+
getName() {
|
|
75
|
+
return this.name;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the index expression
|
|
79
|
+
*/
|
|
80
|
+
getExpression() {
|
|
81
|
+
return this.expression;
|
|
82
|
+
}
|
|
83
|
+
async createIndex() {
|
|
84
|
+
const IndexClass = await resolveIndexConstructor(this.resolver);
|
|
85
|
+
return new IndexClass(this.id, this.expression, this.name, this.options);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
class IndexProxy {
|
|
89
|
+
constructor(indexId, lazyIndex) {
|
|
90
|
+
this.indexId = indexId;
|
|
91
|
+
this.lazyIndex = lazyIndex;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the resolved index (throws if not ready)
|
|
95
|
+
*/
|
|
96
|
+
get index() {
|
|
97
|
+
return this.lazyIndex.getResolved();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if index is ready
|
|
101
|
+
*/
|
|
102
|
+
get isReady() {
|
|
103
|
+
return this.lazyIndex.isResolved();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Wait for index to be ready
|
|
107
|
+
*/
|
|
108
|
+
async whenReady() {
|
|
109
|
+
return await this.lazyIndex.resolve();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get the index ID
|
|
113
|
+
*/
|
|
114
|
+
get id() {
|
|
115
|
+
return this.indexId;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the index name (throws if not ready)
|
|
119
|
+
*/
|
|
120
|
+
get name() {
|
|
121
|
+
if (this.isReady) {
|
|
122
|
+
return this.index.name;
|
|
123
|
+
}
|
|
124
|
+
return this.lazyIndex.getName();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get the index expression (available immediately)
|
|
128
|
+
*/
|
|
129
|
+
get expression() {
|
|
130
|
+
return this.lazyIndex.getExpression();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if index supports an operation (throws if not ready)
|
|
134
|
+
*/
|
|
135
|
+
supports(operation) {
|
|
136
|
+
return this.index.supports(operation);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get index statistics (throws if not ready)
|
|
140
|
+
*/
|
|
141
|
+
getStats() {
|
|
142
|
+
return this.index.getStats();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if index matches a field path (available immediately)
|
|
146
|
+
*/
|
|
147
|
+
matchesField(fieldPath) {
|
|
148
|
+
const expr = this.expression;
|
|
149
|
+
return expr.type === `ref` && expr.path.length === fieldPath.length && expr.path.every((part, i) => part === fieldPath[i]);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get the key count (throws if not ready)
|
|
153
|
+
*/
|
|
154
|
+
get keyCount() {
|
|
155
|
+
return this.index.keyCount;
|
|
156
|
+
}
|
|
157
|
+
// Test compatibility properties - delegate to resolved index
|
|
158
|
+
get indexedKeysSet() {
|
|
159
|
+
const resolved = this.index;
|
|
160
|
+
return resolved.indexedKeysSet;
|
|
161
|
+
}
|
|
162
|
+
get orderedEntriesArray() {
|
|
163
|
+
const resolved = this.index;
|
|
164
|
+
return resolved.orderedEntriesArray;
|
|
165
|
+
}
|
|
166
|
+
get valueMapData() {
|
|
167
|
+
const resolved = this.index;
|
|
168
|
+
return resolved.valueMapData;
|
|
169
|
+
}
|
|
170
|
+
// BTreeIndex compatibility methods
|
|
171
|
+
equalityLookup(value) {
|
|
172
|
+
var _a;
|
|
173
|
+
const resolved = this.index;
|
|
174
|
+
return ((_a = resolved.equalityLookup) == null ? void 0 : _a.call(resolved, value)) ?? /* @__PURE__ */ new Set();
|
|
175
|
+
}
|
|
176
|
+
rangeQuery(options) {
|
|
177
|
+
var _a;
|
|
178
|
+
const resolved = this.index;
|
|
179
|
+
return ((_a = resolved.rangeQuery) == null ? void 0 : _a.call(resolved, options)) ?? /* @__PURE__ */ new Set();
|
|
180
|
+
}
|
|
181
|
+
inArrayLookup(values) {
|
|
182
|
+
var _a;
|
|
183
|
+
const resolved = this.index;
|
|
184
|
+
return ((_a = resolved.inArrayLookup) == null ? void 0 : _a.call(resolved, values)) ?? /* @__PURE__ */ new Set();
|
|
185
|
+
}
|
|
186
|
+
// Internal method for the collection to get the lazy wrapper
|
|
187
|
+
_getLazyWrapper() {
|
|
188
|
+
return this.lazyIndex;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.IndexProxy = IndexProxy;
|
|
192
|
+
exports.LazyIndexWrapper = LazyIndexWrapper;
|
|
193
|
+
//# sourceMappingURL=lazy-index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-index.cjs","sources":["../../../src/indexes/lazy-index.ts"],"sourcesContent":["import type {\n BaseIndex,\n IndexConstructor,\n IndexResolver,\n} from \"./base-index.js\"\nimport type { BasicExpression } from \"../query/ir.js\"\n\n/**\n * Utility to determine if a resolver is a constructor or async loader\n */\nfunction isConstructor<TKey extends string | number>(\n resolver: IndexResolver<TKey>\n): resolver is IndexConstructor<TKey> {\n // Check if it's a function with a prototype (constructor)\n return (\n typeof resolver === `function` &&\n resolver.prototype !== undefined &&\n resolver.prototype.constructor === resolver\n )\n}\n\n/**\n * Resolve index constructor from resolver\n */\nasync function resolveIndexConstructor<TKey extends string | number>(\n resolver: IndexResolver<TKey>\n): Promise<IndexConstructor<TKey>> {\n if (isConstructor(resolver)) {\n return resolver\n } else {\n // It's an async loader function\n return await resolver()\n }\n}\n\n/**\n * Wrapper that defers index creation until first sync\n */\nexport class LazyIndexWrapper<TKey extends string | number = string | number> {\n private indexPromise: Promise<BaseIndex<TKey>> | null = null\n private resolvedIndex: BaseIndex<TKey> | null = null\n\n constructor(\n private id: number,\n private expression: BasicExpression,\n private name: string | undefined,\n private resolver: IndexResolver<TKey>,\n private options: any,\n private collectionEntries?: Iterable<[TKey, any]>\n ) {\n // For synchronous constructors, resolve immediately\n if (isConstructor(this.resolver)) {\n this.resolvedIndex = new this.resolver(\n this.id,\n this.expression,\n this.name,\n this.options\n )\n // Build with initial data if provided\n if (this.collectionEntries) {\n this.resolvedIndex.build(this.collectionEntries)\n }\n }\n }\n\n /**\n * Resolve the actual index\n */\n async resolve(): Promise<BaseIndex<TKey>> {\n if (this.resolvedIndex) {\n return this.resolvedIndex\n }\n\n if (!this.indexPromise) {\n this.indexPromise = this.createIndex()\n }\n\n this.resolvedIndex = await this.indexPromise\n return this.resolvedIndex\n }\n\n /**\n * Check if already resolved\n */\n isResolved(): boolean {\n return this.resolvedIndex !== null\n }\n\n /**\n * Get resolved index (throws if not ready)\n */\n getResolved(): BaseIndex<TKey> {\n if (!this.resolvedIndex) {\n throw new Error(\n `Index ${this.id} has not been resolved yet. Ensure collection is synced.`\n )\n }\n return this.resolvedIndex\n }\n\n /**\n * Get the index ID\n */\n getId(): number {\n return this.id\n }\n\n /**\n * Get the index name\n */\n getName(): string | undefined {\n return this.name\n }\n\n /**\n * Get the index expression\n */\n getExpression(): BasicExpression {\n return this.expression\n }\n\n private async createIndex(): Promise<BaseIndex<TKey>> {\n const IndexClass = await resolveIndexConstructor(this.resolver)\n return new IndexClass(this.id, this.expression, this.name, this.options)\n }\n}\n\n/**\n * Proxy that provides synchronous interface while index loads asynchronously\n */\nexport class IndexProxy<TKey extends string | number = string | number> {\n constructor(\n private indexId: number,\n private lazyIndex: LazyIndexWrapper<TKey>\n ) {}\n\n /**\n * Get the resolved index (throws if not ready)\n */\n get index(): BaseIndex<TKey> {\n return this.lazyIndex.getResolved()\n }\n\n /**\n * Check if index is ready\n */\n get isReady(): boolean {\n return this.lazyIndex.isResolved()\n }\n\n /**\n * Wait for index to be ready\n */\n async whenReady(): Promise<BaseIndex<TKey>> {\n return await this.lazyIndex.resolve()\n }\n\n /**\n * Get the index ID\n */\n get id(): number {\n return this.indexId\n }\n\n /**\n * Get the index name (throws if not ready)\n */\n get name(): string | undefined {\n if (this.isReady) {\n return this.index.name\n }\n return this.lazyIndex.getName()\n }\n\n /**\n * Get the index expression (available immediately)\n */\n get expression(): BasicExpression {\n return this.lazyIndex.getExpression()\n }\n\n /**\n * Check if index supports an operation (throws if not ready)\n */\n supports(operation: any): boolean {\n return this.index.supports(operation)\n }\n\n /**\n * Get index statistics (throws if not ready)\n */\n getStats() {\n return this.index.getStats()\n }\n\n /**\n * Check if index matches a field path (available immediately)\n */\n matchesField(fieldPath: Array<string>): boolean {\n const expr = this.expression\n return (\n expr.type === `ref` &&\n expr.path.length === fieldPath.length &&\n expr.path.every((part, i) => part === fieldPath[i])\n )\n }\n\n /**\n * Get the key count (throws if not ready)\n */\n get keyCount(): number {\n return this.index.keyCount\n }\n\n // Test compatibility properties - delegate to resolved index\n get indexedKeysSet(): Set<TKey> {\n const resolved = this.index as any\n return resolved.indexedKeysSet\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n const resolved = this.index as any\n return resolved.orderedEntriesArray\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n const resolved = this.index as any\n return resolved.valueMapData\n }\n\n // BTreeIndex compatibility methods\n equalityLookup(value: any): Set<TKey> {\n const resolved = this.index as any\n return resolved.equalityLookup?.(value) ?? new Set()\n }\n\n rangeQuery(options: any): Set<TKey> {\n const resolved = this.index as any\n return resolved.rangeQuery?.(options) ?? new Set()\n }\n\n inArrayLookup(values: Array<any>): Set<TKey> {\n const resolved = this.index as any\n return resolved.inArrayLookup?.(values) ?? new Set()\n }\n\n // Internal method for the collection to get the lazy wrapper\n _getLazyWrapper(): LazyIndexWrapper<TKey> {\n return this.lazyIndex\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { BaseIndex, IndexResolver } from './base-index.js';
|
|
2
|
+
import { BasicExpression } from '../query/ir.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper that defers index creation until first sync
|
|
5
|
+
*/
|
|
6
|
+
export declare class LazyIndexWrapper<TKey extends string | number = string | number> {
|
|
7
|
+
private id;
|
|
8
|
+
private expression;
|
|
9
|
+
private name;
|
|
10
|
+
private resolver;
|
|
11
|
+
private options;
|
|
12
|
+
private collectionEntries?;
|
|
13
|
+
private indexPromise;
|
|
14
|
+
private resolvedIndex;
|
|
15
|
+
constructor(id: number, expression: BasicExpression, name: string | undefined, resolver: IndexResolver<TKey>, options: any, collectionEntries?: Iterable<[TKey, any]> | undefined);
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the actual index
|
|
18
|
+
*/
|
|
19
|
+
resolve(): Promise<BaseIndex<TKey>>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if already resolved
|
|
22
|
+
*/
|
|
23
|
+
isResolved(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Get resolved index (throws if not ready)
|
|
26
|
+
*/
|
|
27
|
+
getResolved(): BaseIndex<TKey>;
|
|
28
|
+
/**
|
|
29
|
+
* Get the index ID
|
|
30
|
+
*/
|
|
31
|
+
getId(): number;
|
|
32
|
+
/**
|
|
33
|
+
* Get the index name
|
|
34
|
+
*/
|
|
35
|
+
getName(): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Get the index expression
|
|
38
|
+
*/
|
|
39
|
+
getExpression(): BasicExpression;
|
|
40
|
+
private createIndex;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Proxy that provides synchronous interface while index loads asynchronously
|
|
44
|
+
*/
|
|
45
|
+
export declare class IndexProxy<TKey extends string | number = string | number> {
|
|
46
|
+
private indexId;
|
|
47
|
+
private lazyIndex;
|
|
48
|
+
constructor(indexId: number, lazyIndex: LazyIndexWrapper<TKey>);
|
|
49
|
+
/**
|
|
50
|
+
* Get the resolved index (throws if not ready)
|
|
51
|
+
*/
|
|
52
|
+
get index(): BaseIndex<TKey>;
|
|
53
|
+
/**
|
|
54
|
+
* Check if index is ready
|
|
55
|
+
*/
|
|
56
|
+
get isReady(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Wait for index to be ready
|
|
59
|
+
*/
|
|
60
|
+
whenReady(): Promise<BaseIndex<TKey>>;
|
|
61
|
+
/**
|
|
62
|
+
* Get the index ID
|
|
63
|
+
*/
|
|
64
|
+
get id(): number;
|
|
65
|
+
/**
|
|
66
|
+
* Get the index name (throws if not ready)
|
|
67
|
+
*/
|
|
68
|
+
get name(): string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Get the index expression (available immediately)
|
|
71
|
+
*/
|
|
72
|
+
get expression(): BasicExpression;
|
|
73
|
+
/**
|
|
74
|
+
* Check if index supports an operation (throws if not ready)
|
|
75
|
+
*/
|
|
76
|
+
supports(operation: any): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Get index statistics (throws if not ready)
|
|
79
|
+
*/
|
|
80
|
+
getStats(): import('./base-index.js').IndexStats;
|
|
81
|
+
/**
|
|
82
|
+
* Check if index matches a field path (available immediately)
|
|
83
|
+
*/
|
|
84
|
+
matchesField(fieldPath: Array<string>): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Get the key count (throws if not ready)
|
|
87
|
+
*/
|
|
88
|
+
get keyCount(): number;
|
|
89
|
+
get indexedKeysSet(): Set<TKey>;
|
|
90
|
+
get orderedEntriesArray(): Array<[any, Set<TKey>]>;
|
|
91
|
+
get valueMapData(): Map<any, Set<TKey>>;
|
|
92
|
+
equalityLookup(value: any): Set<TKey>;
|
|
93
|
+
rangeQuery(options: any): Set<TKey>;
|
|
94
|
+
inArrayLookup(values: Array<any>): Set<TKey>;
|
|
95
|
+
_getLazyWrapper(): LazyIndexWrapper<TKey>;
|
|
96
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const comparison = require("../utils/comparison.cjs");
|
|
4
|
+
const arrayUtils = require("../utils/array-utils.cjs");
|
|
5
|
+
const baseIndex = require("./base-index.cjs");
|
|
6
|
+
class OrderedIndex extends baseIndex.BaseIndex {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.supportedOperations = /* @__PURE__ */ new Set([
|
|
10
|
+
`eq`,
|
|
11
|
+
`gt`,
|
|
12
|
+
`gte`,
|
|
13
|
+
`lt`,
|
|
14
|
+
`lte`,
|
|
15
|
+
`in`
|
|
16
|
+
]);
|
|
17
|
+
this.orderedEntries = [];
|
|
18
|
+
this.valueMap = /* @__PURE__ */ new Map();
|
|
19
|
+
this.indexedKeys = /* @__PURE__ */ new Set();
|
|
20
|
+
this.compareFn = comparison.ascComparator;
|
|
21
|
+
}
|
|
22
|
+
initialize(options) {
|
|
23
|
+
this.compareFn = (options == null ? void 0 : options.compareFn) ?? comparison.ascComparator;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Adds a value to the index
|
|
27
|
+
*/
|
|
28
|
+
add(key, item) {
|
|
29
|
+
let indexedValue;
|
|
30
|
+
try {
|
|
31
|
+
indexedValue = this.evaluateIndexExpression(item);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Failed to evaluate index expression for key ${key}: ${error}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (this.valueMap.has(indexedValue)) {
|
|
38
|
+
this.valueMap.get(indexedValue).add(key);
|
|
39
|
+
} else {
|
|
40
|
+
const keySet = /* @__PURE__ */ new Set([key]);
|
|
41
|
+
this.valueMap.set(indexedValue, keySet);
|
|
42
|
+
const insertIndex = arrayUtils.findInsertPosition(
|
|
43
|
+
this.orderedEntries,
|
|
44
|
+
indexedValue,
|
|
45
|
+
this.compareFn
|
|
46
|
+
);
|
|
47
|
+
this.orderedEntries.splice(insertIndex, 0, [indexedValue, keySet]);
|
|
48
|
+
}
|
|
49
|
+
this.indexedKeys.add(key);
|
|
50
|
+
this.updateTimestamp();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Removes a value from the index
|
|
54
|
+
*/
|
|
55
|
+
remove(key, item) {
|
|
56
|
+
let indexedValue;
|
|
57
|
+
try {
|
|
58
|
+
indexedValue = this.evaluateIndexExpression(item);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.warn(
|
|
61
|
+
`Failed to evaluate index expression for key ${key} during removal:`,
|
|
62
|
+
error
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (this.valueMap.has(indexedValue)) {
|
|
67
|
+
const keySet = this.valueMap.get(indexedValue);
|
|
68
|
+
keySet.delete(key);
|
|
69
|
+
if (keySet.size === 0) {
|
|
70
|
+
this.valueMap.delete(indexedValue);
|
|
71
|
+
const index = this.orderedEntries.findIndex(
|
|
72
|
+
([value]) => this.compareFn(value, indexedValue) === 0
|
|
73
|
+
);
|
|
74
|
+
if (index !== -1) {
|
|
75
|
+
this.orderedEntries.splice(index, 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.indexedKeys.delete(key);
|
|
80
|
+
this.updateTimestamp();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Updates a value in the index
|
|
84
|
+
*/
|
|
85
|
+
update(key, oldItem, newItem) {
|
|
86
|
+
this.remove(key, oldItem);
|
|
87
|
+
this.add(key, newItem);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Builds the index from a collection of entries
|
|
91
|
+
*/
|
|
92
|
+
build(entries) {
|
|
93
|
+
this.clear();
|
|
94
|
+
for (const [key, item] of entries) {
|
|
95
|
+
this.add(key, item);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Clears all data from the index
|
|
100
|
+
*/
|
|
101
|
+
clear() {
|
|
102
|
+
this.orderedEntries = [];
|
|
103
|
+
this.valueMap.clear();
|
|
104
|
+
this.indexedKeys.clear();
|
|
105
|
+
this.updateTimestamp();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Performs a lookup operation
|
|
109
|
+
*/
|
|
110
|
+
lookup(operation, value) {
|
|
111
|
+
const startTime = performance.now();
|
|
112
|
+
let result;
|
|
113
|
+
switch (operation) {
|
|
114
|
+
case `eq`:
|
|
115
|
+
result = this.equalityLookup(value);
|
|
116
|
+
break;
|
|
117
|
+
case `gt`:
|
|
118
|
+
result = this.rangeQuery({ from: value, fromInclusive: false });
|
|
119
|
+
break;
|
|
120
|
+
case `gte`:
|
|
121
|
+
result = this.rangeQuery({ from: value, fromInclusive: true });
|
|
122
|
+
break;
|
|
123
|
+
case `lt`:
|
|
124
|
+
result = this.rangeQuery({ to: value, toInclusive: false });
|
|
125
|
+
break;
|
|
126
|
+
case `lte`:
|
|
127
|
+
result = this.rangeQuery({ to: value, toInclusive: true });
|
|
128
|
+
break;
|
|
129
|
+
case `in`:
|
|
130
|
+
result = this.inArrayLookup(value);
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
throw new Error(`Operation ${operation} not supported by OrderedIndex`);
|
|
134
|
+
}
|
|
135
|
+
this.trackLookup(startTime);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Gets the number of indexed keys
|
|
140
|
+
*/
|
|
141
|
+
get keyCount() {
|
|
142
|
+
return this.indexedKeys.size;
|
|
143
|
+
}
|
|
144
|
+
// Public methods for backward compatibility (used by tests)
|
|
145
|
+
/**
|
|
146
|
+
* Performs an equality lookup
|
|
147
|
+
*/
|
|
148
|
+
equalityLookup(value) {
|
|
149
|
+
return new Set(this.valueMap.get(value) ?? []);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Performs a range query with options
|
|
153
|
+
* This is more efficient for compound queries like "WHERE a > 5 AND a < 10"
|
|
154
|
+
*/
|
|
155
|
+
rangeQuery(options = {}) {
|
|
156
|
+
const { from, to, fromInclusive = true, toInclusive = true } = options;
|
|
157
|
+
const result = /* @__PURE__ */ new Set();
|
|
158
|
+
if (this.orderedEntries.length === 0) {
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
let startIndex = 0;
|
|
162
|
+
if (from !== void 0) {
|
|
163
|
+
const fromInsertIndex = arrayUtils.findInsertPosition(
|
|
164
|
+
this.orderedEntries,
|
|
165
|
+
from,
|
|
166
|
+
this.compareFn
|
|
167
|
+
);
|
|
168
|
+
if (fromInclusive) {
|
|
169
|
+
startIndex = fromInsertIndex;
|
|
170
|
+
} else {
|
|
171
|
+
startIndex = fromInsertIndex;
|
|
172
|
+
if (startIndex < this.orderedEntries.length && this.compareFn(this.orderedEntries[startIndex][0], from) === 0) {
|
|
173
|
+
startIndex++;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
let endIndex = this.orderedEntries.length;
|
|
178
|
+
if (to !== void 0) {
|
|
179
|
+
const toInsertIndex = arrayUtils.findInsertPosition(
|
|
180
|
+
this.orderedEntries,
|
|
181
|
+
to,
|
|
182
|
+
this.compareFn
|
|
183
|
+
);
|
|
184
|
+
if (toInclusive) {
|
|
185
|
+
endIndex = toInsertIndex;
|
|
186
|
+
if (toInsertIndex < this.orderedEntries.length && this.compareFn(this.orderedEntries[toInsertIndex][0], to) === 0) {
|
|
187
|
+
endIndex = toInsertIndex + 1;
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
endIndex = toInsertIndex;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (startIndex >= endIndex) {
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
197
|
+
const keys = this.orderedEntries[i][1];
|
|
198
|
+
keys.forEach((key) => result.add(key));
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Performs an IN array lookup
|
|
204
|
+
*/
|
|
205
|
+
inArrayLookup(values) {
|
|
206
|
+
const result = /* @__PURE__ */ new Set();
|
|
207
|
+
for (const value of values) {
|
|
208
|
+
const keys = this.valueMap.get(value);
|
|
209
|
+
if (keys) {
|
|
210
|
+
keys.forEach((key) => result.add(key));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
// Getter methods for testing compatibility
|
|
216
|
+
get indexedKeysSet() {
|
|
217
|
+
return this.indexedKeys;
|
|
218
|
+
}
|
|
219
|
+
get orderedEntriesArray() {
|
|
220
|
+
return this.orderedEntries;
|
|
221
|
+
}
|
|
222
|
+
get valueMapData() {
|
|
223
|
+
return this.valueMap;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.OrderedIndex = OrderedIndex;
|
|
227
|
+
//# sourceMappingURL=ordered-index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ordered-index.cjs","sources":["../../../src/indexes/ordered-index.ts"],"sourcesContent":["import { ascComparator } from \"../utils/comparison.js\"\nimport { findInsertPosition } from \"../utils/array-utils.js\"\nimport { BaseIndex } from \"./base-index.js\"\nimport type { IndexOperation } from \"./base-index.js\"\n\n/**\n * Options for Ordered index\n */\nexport interface OrderedIndexOptions {\n compareFn?: (a: any, b: any) => number\n}\n\n/**\n * Options for range queries\n */\nexport interface RangeQueryOptions {\n from?: any\n to?: any\n fromInclusive?: boolean\n toInclusive?: boolean\n}\n\n/**\n * Ordered index for sorted data with range queries\n * This maintains items in sorted order and provides efficient range operations\n */\nexport class OrderedIndex<\n TKey extends string | number = string | number,\n> extends BaseIndex<TKey> {\n public readonly supportedOperations = new Set<IndexOperation>([\n `eq`,\n `gt`,\n `gte`,\n `lt`,\n `lte`,\n `in`,\n ])\n\n // Internal data structures - private to hide implementation details\n private orderedEntries: Array<[any, Set<TKey>]> = []\n private valueMap = new Map<any, Set<TKey>>()\n private indexedKeys = new Set<TKey>()\n private compareFn: (a: any, b: any) => number = ascComparator\n\n protected initialize(options?: OrderedIndexOptions): void {\n this.compareFn = options?.compareFn ?? ascComparator\n }\n\n /**\n * Adds a value to the index\n */\n add(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n throw new Error(\n `Failed to evaluate index expression for key ${key}: ${error}`\n )\n }\n\n // Check if this value already exists\n if (this.valueMap.has(indexedValue)) {\n // Add to existing set\n this.valueMap.get(indexedValue)!.add(key)\n } else {\n // Create new set for this value\n const keySet = new Set<TKey>([key])\n this.valueMap.set(indexedValue, keySet)\n\n // Find correct position in ordered entries using binary search\n const insertIndex = findInsertPosition(\n this.orderedEntries,\n indexedValue,\n this.compareFn\n )\n this.orderedEntries.splice(insertIndex, 0, [indexedValue, keySet])\n }\n\n this.indexedKeys.add(key)\n this.updateTimestamp()\n }\n\n /**\n * Removes a value from the index\n */\n remove(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n console.warn(\n `Failed to evaluate index expression for key ${key} during removal:`,\n error\n )\n return\n }\n\n if (this.valueMap.has(indexedValue)) {\n const keySet = this.valueMap.get(indexedValue)!\n keySet.delete(key)\n\n // If set is now empty, remove the entry entirely\n if (keySet.size === 0) {\n this.valueMap.delete(indexedValue)\n\n // Find and remove from ordered entries\n const index = this.orderedEntries.findIndex(\n ([value]) => this.compareFn(value, indexedValue) === 0\n )\n if (index !== -1) {\n this.orderedEntries.splice(index, 1)\n }\n }\n }\n\n this.indexedKeys.delete(key)\n this.updateTimestamp()\n }\n\n /**\n * Updates a value in the index\n */\n update(key: TKey, oldItem: any, newItem: any): void {\n this.remove(key, oldItem)\n this.add(key, newItem)\n }\n\n /**\n * Builds the index from a collection of entries\n */\n build(entries: Iterable<[TKey, any]>): void {\n this.clear()\n\n for (const [key, item] of entries) {\n this.add(key, item)\n }\n }\n\n /**\n * Clears all data from the index\n */\n clear(): void {\n this.orderedEntries = []\n this.valueMap.clear()\n this.indexedKeys.clear()\n this.updateTimestamp()\n }\n\n /**\n * Performs a lookup operation\n */\n lookup(operation: IndexOperation, value: any): Set<TKey> {\n const startTime = performance.now()\n\n let result: Set<TKey>\n\n switch (operation) {\n case `eq`:\n result = this.equalityLookup(value)\n break\n case `gt`:\n result = this.rangeQuery({ from: value, fromInclusive: false })\n break\n case `gte`:\n result = this.rangeQuery({ from: value, fromInclusive: true })\n break\n case `lt`:\n result = this.rangeQuery({ to: value, toInclusive: false })\n break\n case `lte`:\n result = this.rangeQuery({ to: value, toInclusive: true })\n break\n case `in`:\n result = this.inArrayLookup(value)\n break\n default:\n throw new Error(`Operation ${operation} not supported by OrderedIndex`)\n }\n\n this.trackLookup(startTime)\n return result\n }\n\n /**\n * Gets the number of indexed keys\n */\n get keyCount(): number {\n return this.indexedKeys.size\n }\n\n // Public methods for backward compatibility (used by tests)\n\n /**\n * Performs an equality lookup\n */\n equalityLookup(value: any): Set<TKey> {\n return new Set(this.valueMap.get(value) ?? [])\n }\n\n /**\n * Performs a range query with options\n * This is more efficient for compound queries like \"WHERE a > 5 AND a < 10\"\n */\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n const result = new Set<TKey>()\n\n if (this.orderedEntries.length === 0) {\n return result\n }\n\n // Find start position\n let startIndex = 0\n if (from !== undefined) {\n const fromInsertIndex = findInsertPosition(\n this.orderedEntries,\n from,\n this.compareFn\n )\n\n if (fromInclusive) {\n // Include values equal to 'from'\n startIndex = fromInsertIndex\n } else {\n // Exclude values equal to 'from'\n startIndex = fromInsertIndex\n // Skip the value if it exists at this position\n if (\n startIndex < this.orderedEntries.length &&\n this.compareFn(this.orderedEntries[startIndex]![0], from) === 0\n ) {\n startIndex++\n }\n }\n }\n\n // Find end position\n let endIndex = this.orderedEntries.length\n if (to !== undefined) {\n const toInsertIndex = findInsertPosition(\n this.orderedEntries,\n to,\n this.compareFn\n )\n\n if (toInclusive) {\n // Include values equal to 'to'\n endIndex = toInsertIndex\n // Include the value if it exists at this position\n if (\n toInsertIndex < this.orderedEntries.length &&\n this.compareFn(this.orderedEntries[toInsertIndex]![0], to) === 0\n ) {\n endIndex = toInsertIndex + 1\n }\n } else {\n // Exclude values equal to 'to'\n endIndex = toInsertIndex\n }\n }\n\n // Ensure startIndex doesn't exceed endIndex\n if (startIndex >= endIndex) {\n return result\n }\n\n // Collect keys from the range\n for (let i = startIndex; i < endIndex; i++) {\n const keys = this.orderedEntries[i]![1]\n keys.forEach((key) => result.add(key))\n }\n\n return result\n }\n\n /**\n * Performs an IN array lookup\n */\n inArrayLookup(values: Array<any>): Set<TKey> {\n const result = new Set<TKey>()\n\n for (const value of values) {\n const keys = this.valueMap.get(value)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n\n return result\n }\n\n // Getter methods for testing compatibility\n get indexedKeysSet(): Set<TKey> {\n return this.indexedKeys\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n return this.orderedEntries\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.valueMap\n }\n}\n"],"names":["BaseIndex","ascComparator","findInsertPosition"],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { BaseIndex, IndexOperation } from './base-index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for Ordered index
|
|
4
|
+
*/
|
|
5
|
+
export interface OrderedIndexOptions {
|
|
6
|
+
compareFn?: (a: any, b: any) => number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Options for range queries
|
|
10
|
+
*/
|
|
11
|
+
export interface RangeQueryOptions {
|
|
12
|
+
from?: any;
|
|
13
|
+
to?: any;
|
|
14
|
+
fromInclusive?: boolean;
|
|
15
|
+
toInclusive?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Ordered index for sorted data with range queries
|
|
19
|
+
* This maintains items in sorted order and provides efficient range operations
|
|
20
|
+
*/
|
|
21
|
+
export declare class OrderedIndex<TKey extends string | number = string | number> extends BaseIndex<TKey> {
|
|
22
|
+
readonly supportedOperations: Set<"eq" | "gt" | "gte" | "lt" | "lte" | "in" | "like" | "ilike">;
|
|
23
|
+
private orderedEntries;
|
|
24
|
+
private valueMap;
|
|
25
|
+
private indexedKeys;
|
|
26
|
+
private compareFn;
|
|
27
|
+
protected initialize(options?: OrderedIndexOptions): void;
|
|
28
|
+
/**
|
|
29
|
+
* Adds a value to the index
|
|
30
|
+
*/
|
|
31
|
+
add(key: TKey, item: any): void;
|
|
32
|
+
/**
|
|
33
|
+
* Removes a value from the index
|
|
34
|
+
*/
|
|
35
|
+
remove(key: TKey, item: any): void;
|
|
36
|
+
/**
|
|
37
|
+
* Updates a value in the index
|
|
38
|
+
*/
|
|
39
|
+
update(key: TKey, oldItem: any, newItem: any): void;
|
|
40
|
+
/**
|
|
41
|
+
* Builds the index from a collection of entries
|
|
42
|
+
*/
|
|
43
|
+
build(entries: Iterable<[TKey, any]>): void;
|
|
44
|
+
/**
|
|
45
|
+
* Clears all data from the index
|
|
46
|
+
*/
|
|
47
|
+
clear(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Performs a lookup operation
|
|
50
|
+
*/
|
|
51
|
+
lookup(operation: IndexOperation, value: any): Set<TKey>;
|
|
52
|
+
/**
|
|
53
|
+
* Gets the number of indexed keys
|
|
54
|
+
*/
|
|
55
|
+
get keyCount(): number;
|
|
56
|
+
/**
|
|
57
|
+
* Performs an equality lookup
|
|
58
|
+
*/
|
|
59
|
+
equalityLookup(value: any): Set<TKey>;
|
|
60
|
+
/**
|
|
61
|
+
* Performs a range query with options
|
|
62
|
+
* This is more efficient for compound queries like "WHERE a > 5 AND a < 10"
|
|
63
|
+
*/
|
|
64
|
+
rangeQuery(options?: RangeQueryOptions): Set<TKey>;
|
|
65
|
+
/**
|
|
66
|
+
* Performs an IN array lookup
|
|
67
|
+
*/
|
|
68
|
+
inArrayLookup(values: Array<any>): Set<TKey>;
|
|
69
|
+
get indexedKeysSet(): Set<TKey>;
|
|
70
|
+
get orderedEntriesArray(): Array<[any, Set<TKey>]>;
|
|
71
|
+
get valueMapData(): Map<any, Set<TKey>>;
|
|
72
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const errors = require("./errors.cjs");
|
|
3
4
|
function validateJsonSerializable(value, operation) {
|
|
4
5
|
try {
|
|
5
6
|
JSON.stringify(value);
|
|
6
7
|
} catch (error) {
|
|
7
|
-
throw new
|
|
8
|
-
|
|
8
|
+
throw new errors.SerializationError(
|
|
9
|
+
operation,
|
|
10
|
+
error instanceof Error ? error.message : String(error)
|
|
9
11
|
);
|
|
10
12
|
}
|
|
11
13
|
}
|
|
@@ -14,19 +16,15 @@ function generateUuid() {
|
|
|
14
16
|
}
|
|
15
17
|
function localStorageCollectionOptions(config) {
|
|
16
18
|
if (!config.storageKey) {
|
|
17
|
-
throw new
|
|
19
|
+
throw new errors.StorageKeyRequiredError();
|
|
18
20
|
}
|
|
19
21
|
const storage = config.storage || (typeof window !== `undefined` ? window.localStorage : null);
|
|
20
22
|
if (!storage) {
|
|
21
|
-
throw new
|
|
22
|
-
`[LocalStorageCollection] No storage available. Please provide a storage option or ensure window.localStorage is available.`
|
|
23
|
-
);
|
|
23
|
+
throw new errors.NoStorageAvailableError();
|
|
24
24
|
}
|
|
25
25
|
const storageEventApi = config.storageEventApi || (typeof window !== `undefined` ? window : null);
|
|
26
26
|
if (!storageEventApi) {
|
|
27
|
-
throw new
|
|
28
|
-
`[LocalStorageCollection] No storage event API available. Please provide a storageEventApi option or ensure window is available.`
|
|
29
|
-
);
|
|
27
|
+
throw new errors.NoStorageEventApiError();
|
|
30
28
|
}
|
|
31
29
|
const lastKnownData = /* @__PURE__ */ new Map();
|
|
32
30
|
const sync = createLocalStorageSync(
|
|
@@ -167,15 +165,11 @@ function loadFromStorage(storageKey, storage) {
|
|
|
167
165
|
const storedItem = value;
|
|
168
166
|
dataMap.set(key, storedItem);
|
|
169
167
|
} else {
|
|
170
|
-
throw new
|
|
171
|
-
`[LocalStorageCollection] Invalid data format in storage key "${storageKey}" for key "${key}".`
|
|
172
|
-
);
|
|
168
|
+
throw new errors.InvalidStorageDataFormatError(storageKey, key);
|
|
173
169
|
}
|
|
174
170
|
});
|
|
175
171
|
} else {
|
|
176
|
-
throw new
|
|
177
|
-
`[LocalStorageCollection] Invalid data format in storage key "${storageKey}". Expected object format.`
|
|
178
|
-
);
|
|
172
|
+
throw new errors.InvalidStorageObjectFormatError(storageKey);
|
|
179
173
|
}
|
|
180
174
|
return dataMap;
|
|
181
175
|
} catch (error) {
|