@tldraw/store 4.2.0-next.e824a30c434e → 4.2.0-next.f100cedfc45b
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/index.d.ts +21 -12
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/StoreQueries.js +73 -27
- package/dist-cjs/lib/StoreQueries.js.map +2 -2
- package/dist-cjs/lib/executeQuery.js +38 -14
- package/dist-cjs/lib/executeQuery.js.map +2 -2
- package/dist-esm/index.d.mts +21 -12
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/StoreQueries.mjs +73 -27
- package/dist-esm/lib/StoreQueries.mjs.map +2 -2
- package/dist-esm/lib/executeQuery.mjs +38 -14
- package/dist-esm/lib/executeQuery.mjs.map +2 -2
- package/package.json +3 -3
- package/src/lib/StoreQueries.ts +102 -51
- package/src/lib/executeQuery.test.ts +928 -4
- package/src/lib/executeQuery.ts +78 -36
package/src/lib/StoreQueries.ts
CHANGED
|
@@ -31,10 +31,7 @@ import { CollectionDiff } from './Store'
|
|
|
31
31
|
*
|
|
32
32
|
* @public
|
|
33
33
|
*/
|
|
34
|
-
export type RSIndexDiff<
|
|
35
|
-
R extends UnknownRecord,
|
|
36
|
-
Property extends string & keyof R = string & keyof R,
|
|
37
|
-
> = Map<R[Property], CollectionDiff<IdOf<R>>>
|
|
34
|
+
export type RSIndexDiff<R extends UnknownRecord> = Map<any, CollectionDiff<IdOf<R>>>
|
|
38
35
|
|
|
39
36
|
/**
|
|
40
37
|
* A type representing a reactive store index as a map from property values to sets of record IDs.
|
|
@@ -51,10 +48,7 @@ export type RSIndexDiff<
|
|
|
51
48
|
*
|
|
52
49
|
* @public
|
|
53
50
|
*/
|
|
54
|
-
export type RSIndexMap<
|
|
55
|
-
R extends UnknownRecord,
|
|
56
|
-
Property extends string & keyof R = string & keyof R,
|
|
57
|
-
> = Map<R[Property], Set<IdOf<R>>>
|
|
51
|
+
export type RSIndexMap<R extends UnknownRecord> = Map<any, Set<IdOf<R>>>
|
|
58
52
|
|
|
59
53
|
/**
|
|
60
54
|
* A reactive computed index that provides efficient lookups of records by property values.
|
|
@@ -71,10 +65,7 @@ export type RSIndexMap<
|
|
|
71
65
|
*
|
|
72
66
|
* @public
|
|
73
67
|
*/
|
|
74
|
-
export type RSIndex<
|
|
75
|
-
R extends UnknownRecord,
|
|
76
|
-
Property extends string & keyof R = string & keyof R,
|
|
77
|
-
> = Computed<RSIndexMap<R, Property>, RSIndexDiff<R, Property>>
|
|
68
|
+
export type RSIndex<R extends UnknownRecord> = Computed<RSIndexMap<R>, RSIndexDiff<R>>
|
|
78
69
|
|
|
79
70
|
/**
|
|
80
71
|
* A class that provides reactive querying capabilities for a record store.
|
|
@@ -121,6 +112,49 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
121
112
|
*/
|
|
122
113
|
private historyCache = new Map<string, Computed<number, RecordsDiff<R>>>()
|
|
123
114
|
|
|
115
|
+
/**
|
|
116
|
+
* @internal
|
|
117
|
+
*/
|
|
118
|
+
public getAllIdsForType<TypeName extends R['typeName']>(
|
|
119
|
+
typeName: TypeName
|
|
120
|
+
): Set<IdOf<Extract<R, { typeName: TypeName }>>> {
|
|
121
|
+
type S = Extract<R, { typeName: TypeName }>
|
|
122
|
+
const ids = new Set<IdOf<S>>()
|
|
123
|
+
for (const record of this.recordMap.values()) {
|
|
124
|
+
if (record.typeName === typeName) {
|
|
125
|
+
ids.add(record.id as IdOf<S>)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return ids
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
public getRecordById<TypeName extends R['typeName']>(
|
|
135
|
+
typeName: TypeName,
|
|
136
|
+
id: IdOf<Extract<R, { typeName: TypeName }>>
|
|
137
|
+
): Extract<R, { typeName: TypeName }> | undefined {
|
|
138
|
+
const record = this.recordMap.get(id as IdOf<R>)
|
|
139
|
+
if (record && record.typeName === typeName) {
|
|
140
|
+
return record as Extract<R, { typeName: TypeName }>
|
|
141
|
+
}
|
|
142
|
+
return undefined
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Helper to extract nested property value using pre-split path parts.
|
|
147
|
+
* @internal
|
|
148
|
+
*/
|
|
149
|
+
private getNestedValue(obj: any, pathParts: string[]): any {
|
|
150
|
+
let current = obj
|
|
151
|
+
for (const part of pathParts) {
|
|
152
|
+
if (current == null || typeof current !== 'object') return undefined
|
|
153
|
+
current = current[part]
|
|
154
|
+
}
|
|
155
|
+
return current
|
|
156
|
+
}
|
|
157
|
+
|
|
124
158
|
/**
|
|
125
159
|
* Creates a reactive computed that tracks the change history for records of a specific type.
|
|
126
160
|
* The returned computed provides incremental diffs showing what records of the given type
|
|
@@ -237,8 +271,10 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
237
271
|
* The index automatically updates when records are added, updated, or removed, and results are cached
|
|
238
272
|
* for performance.
|
|
239
273
|
*
|
|
274
|
+
* Supports nested property paths using backslash separator (e.g., 'metadata\\sessionId').
|
|
275
|
+
*
|
|
240
276
|
* @param typeName - The type name of records to index
|
|
241
|
-
* @param
|
|
277
|
+
* @param path - The property name or backslash-delimited path to index by
|
|
242
278
|
* @returns A reactive computed containing the index map with change diffs
|
|
243
279
|
*
|
|
244
280
|
* @example
|
|
@@ -250,24 +286,24 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
250
286
|
* const authorBooks = booksByAuthor.get().get('author:leguin')
|
|
251
287
|
* console.log(authorBooks) // Set<RecordId<Book>>
|
|
252
288
|
*
|
|
253
|
-
* // Index by
|
|
254
|
-
* const
|
|
255
|
-
* const
|
|
289
|
+
* // Index by nested property using backslash separator
|
|
290
|
+
* const booksBySession = store.query.index('book', 'metadata\\sessionId')
|
|
291
|
+
* const sessionBooks = booksBySession.get().get('session:alpha')
|
|
256
292
|
* ```
|
|
257
293
|
*
|
|
258
294
|
* @public
|
|
259
295
|
*/
|
|
260
|
-
public index<
|
|
261
|
-
TypeName
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const cacheKey = typeName + ':' +
|
|
296
|
+
public index<TypeName extends R['typeName']>(
|
|
297
|
+
typeName: TypeName,
|
|
298
|
+
path: string
|
|
299
|
+
): RSIndex<Extract<R, { typeName: TypeName }>> {
|
|
300
|
+
const cacheKey = typeName + ':' + path
|
|
265
301
|
|
|
266
302
|
if (this.indexCache.has(cacheKey)) {
|
|
267
303
|
return this.indexCache.get(cacheKey) as any
|
|
268
304
|
}
|
|
269
305
|
|
|
270
|
-
const index = this.__uncached_createIndex(typeName,
|
|
306
|
+
const index = this.__uncached_createIndex(typeName, path)
|
|
271
307
|
|
|
272
308
|
this.indexCache.set(cacheKey, index as any)
|
|
273
309
|
|
|
@@ -278,40 +314,51 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
278
314
|
* Creates a new index without checking the cache. This method performs the actual work
|
|
279
315
|
* of building the reactive index computation that tracks property values to record ID sets.
|
|
280
316
|
*
|
|
317
|
+
* Supports nested property paths using backslash separator.
|
|
318
|
+
*
|
|
281
319
|
* @param typeName - The type name of records to index
|
|
282
|
-
* @param
|
|
320
|
+
* @param path - The property name or backslash-delimited path to index by
|
|
283
321
|
* @returns A reactive computed containing the index map with change diffs
|
|
284
322
|
*
|
|
285
323
|
* @internal
|
|
286
324
|
*/
|
|
287
|
-
__uncached_createIndex<
|
|
288
|
-
TypeName
|
|
289
|
-
|
|
290
|
-
|
|
325
|
+
__uncached_createIndex<TypeName extends R['typeName']>(
|
|
326
|
+
typeName: TypeName,
|
|
327
|
+
path: string
|
|
328
|
+
): RSIndex<Extract<R, { typeName: TypeName }>> {
|
|
291
329
|
type S = Extract<R, { typeName: TypeName }>
|
|
292
330
|
|
|
293
331
|
const typeHistory = this.filterHistory(typeName)
|
|
294
332
|
|
|
333
|
+
// Create closure for efficient property value extraction
|
|
334
|
+
const pathParts = path.split('\\')
|
|
335
|
+
const getPropertyValue =
|
|
336
|
+
pathParts.length > 1
|
|
337
|
+
? (obj: S) => this.getNestedValue(obj, pathParts)
|
|
338
|
+
: (obj: S) => obj[path as keyof S]
|
|
339
|
+
|
|
295
340
|
const fromScratch = () => {
|
|
296
341
|
// deref typeHistory early so that the first time the incremental version runs
|
|
297
342
|
// it gets a diff to work with instead of having to bail to this from-scratch version
|
|
298
343
|
typeHistory.get()
|
|
299
|
-
const res = new Map<
|
|
344
|
+
const res = new Map<any, Set<IdOf<S>>>()
|
|
300
345
|
for (const record of this.recordMap.values()) {
|
|
301
346
|
if (record.typeName === typeName) {
|
|
302
|
-
const value = (record as S)
|
|
303
|
-
if (
|
|
304
|
-
res.
|
|
347
|
+
const value = getPropertyValue(record as S)
|
|
348
|
+
if (value !== undefined) {
|
|
349
|
+
if (!res.has(value)) {
|
|
350
|
+
res.set(value, new Set())
|
|
351
|
+
}
|
|
352
|
+
res.get(value)!.add(record.id)
|
|
305
353
|
}
|
|
306
|
-
res.get(value)!.add(record.id)
|
|
307
354
|
}
|
|
308
355
|
}
|
|
309
356
|
|
|
310
357
|
return res
|
|
311
358
|
}
|
|
312
359
|
|
|
313
|
-
return computed<RSIndexMap<S
|
|
314
|
-
'index:' + typeName + ':' +
|
|
360
|
+
return computed<RSIndexMap<S>, RSIndexDiff<S>>(
|
|
361
|
+
'index:' + typeName + ':' + path,
|
|
315
362
|
(prevValue, lastComputedEpoch) => {
|
|
316
363
|
if (isUninitialized(prevValue)) return fromScratch()
|
|
317
364
|
|
|
@@ -322,7 +369,7 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
322
369
|
|
|
323
370
|
const setConstructors = new Map<any, IncrementalSetConstructor<IdOf<S>>>()
|
|
324
371
|
|
|
325
|
-
const add = (value:
|
|
372
|
+
const add = (value: any, id: IdOf<S>) => {
|
|
326
373
|
let setConstructor = setConstructors.get(value)
|
|
327
374
|
if (!setConstructor)
|
|
328
375
|
setConstructor = new IncrementalSetConstructor<IdOf<S>>(
|
|
@@ -332,7 +379,7 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
332
379
|
setConstructors.set(value, setConstructor)
|
|
333
380
|
}
|
|
334
381
|
|
|
335
|
-
const remove = (value:
|
|
382
|
+
const remove = (value: any, id: IdOf<S>) => {
|
|
336
383
|
let set = setConstructors.get(value)
|
|
337
384
|
if (!set) set = new IncrementalSetConstructor<IdOf<S>>(prevValue.get(value) ?? new Set())
|
|
338
385
|
set.remove(id)
|
|
@@ -342,30 +389,38 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
342
389
|
for (const changes of history) {
|
|
343
390
|
for (const record of objectMapValues(changes.added)) {
|
|
344
391
|
if (record.typeName === typeName) {
|
|
345
|
-
const value = (record as S)
|
|
346
|
-
|
|
392
|
+
const value = getPropertyValue(record as S)
|
|
393
|
+
if (value !== undefined) {
|
|
394
|
+
add(value, record.id)
|
|
395
|
+
}
|
|
347
396
|
}
|
|
348
397
|
}
|
|
349
398
|
for (const [from, to] of objectMapValues(changes.updated)) {
|
|
350
399
|
if (to.typeName === typeName) {
|
|
351
|
-
const prev = (from as S)
|
|
352
|
-
const next = (to as S)
|
|
400
|
+
const prev = getPropertyValue(from as S)
|
|
401
|
+
const next = getPropertyValue(to as S)
|
|
353
402
|
if (prev !== next) {
|
|
354
|
-
|
|
355
|
-
|
|
403
|
+
if (prev !== undefined) {
|
|
404
|
+
remove(prev, to.id)
|
|
405
|
+
}
|
|
406
|
+
if (next !== undefined) {
|
|
407
|
+
add(next, to.id)
|
|
408
|
+
}
|
|
356
409
|
}
|
|
357
410
|
}
|
|
358
411
|
}
|
|
359
412
|
for (const record of objectMapValues(changes.removed)) {
|
|
360
413
|
if (record.typeName === typeName) {
|
|
361
|
-
const value = (record as S)
|
|
362
|
-
|
|
414
|
+
const value = getPropertyValue(record as S)
|
|
415
|
+
if (value !== undefined) {
|
|
416
|
+
remove(value, record.id)
|
|
417
|
+
}
|
|
363
418
|
}
|
|
364
419
|
}
|
|
365
420
|
}
|
|
366
421
|
|
|
367
|
-
let nextValue: undefined | RSIndexMap<S
|
|
368
|
-
let nextDiff: undefined | RSIndexDiff<S
|
|
422
|
+
let nextValue: undefined | RSIndexMap<S> = undefined
|
|
423
|
+
let nextDiff: undefined | RSIndexDiff<S> = undefined
|
|
369
424
|
|
|
370
425
|
for (const [value, setConstructor] of setConstructors) {
|
|
371
426
|
const result = setConstructor.get()
|
|
@@ -513,11 +568,7 @@ export class StoreQueries<R extends UnknownRecord> {
|
|
|
513
568
|
typeHistory.get()
|
|
514
569
|
const query: QueryExpression<S> = queryCreator()
|
|
515
570
|
if (Object.keys(query).length === 0) {
|
|
516
|
-
|
|
517
|
-
for (const record of this.recordMap.values()) {
|
|
518
|
-
if (record.typeName === typeName) ids.add(record.id)
|
|
519
|
-
}
|
|
520
|
-
return ids
|
|
571
|
+
return this.getAllIdsForType(typeName)
|
|
521
572
|
}
|
|
522
573
|
|
|
523
574
|
return executeQuery(this, typeName, query)
|