@tldraw/store 4.2.0-next.3abbdad51d2e → 4.2.0-next.67908ea044c6
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/RecordType.js.map +2 -2
- 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/RecordType.mjs.map +2 -2
- 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/RecordType.ts +1 -1
- package/src/lib/StoreQueries.ts +102 -51
- package/src/lib/executeQuery.test.ts +928 -4
- package/src/lib/executeQuery.ts +78 -36
package/dist-cjs/index.d.ts
CHANGED
|
@@ -768,12 +768,18 @@ export declare interface MigrationSequence {
|
|
|
768
768
|
* const notByAuthor: QueryExpression<Book> = {
|
|
769
769
|
* authorId: { neq: 'author:tolkien' }
|
|
770
770
|
* }
|
|
771
|
+
*
|
|
772
|
+
* // Query with nested properties
|
|
773
|
+
* const nestedQuery: QueryExpression<Book> = {
|
|
774
|
+
* metadata: { sessionId: { eq: 'session:alpha' } }
|
|
775
|
+
* }
|
|
771
776
|
* ```
|
|
772
777
|
*
|
|
773
778
|
* @public
|
|
774
779
|
*/
|
|
780
|
+
/** @public */
|
|
775
781
|
export declare type QueryExpression<R extends object> = {
|
|
776
|
-
[k in keyof R & string]?: QueryValueMatcher<R[k]>;
|
|
782
|
+
[k in keyof R & string]?: R[k] extends boolean | null | number | string | undefined ? QueryValueMatcher<R[k]> : R[k] extends object ? QueryExpression<R[k]> : QueryValueMatcher<R[k]>;
|
|
777
783
|
};
|
|
778
784
|
|
|
779
785
|
/**
|
|
@@ -1140,7 +1146,7 @@ export declare function reverseRecordsDiff(diff: RecordsDiff<any>): RecordsDiff<
|
|
|
1140
1146
|
*
|
|
1141
1147
|
* @public
|
|
1142
1148
|
*/
|
|
1143
|
-
export declare type RSIndex<R extends UnknownRecord
|
|
1149
|
+
export declare type RSIndex<R extends UnknownRecord> = Computed<RSIndexMap<R>, RSIndexDiff<R>>;
|
|
1144
1150
|
|
|
1145
1151
|
/**
|
|
1146
1152
|
* A type representing the diff of changes to a reactive store index.
|
|
@@ -1157,7 +1163,7 @@ export declare type RSIndex<R extends UnknownRecord, Property extends string & k
|
|
|
1157
1163
|
*
|
|
1158
1164
|
* @public
|
|
1159
1165
|
*/
|
|
1160
|
-
export declare type RSIndexDiff<R extends UnknownRecord
|
|
1166
|
+
export declare type RSIndexDiff<R extends UnknownRecord> = Map<any, CollectionDiff<IdOf<R>>>;
|
|
1161
1167
|
|
|
1162
1168
|
/**
|
|
1163
1169
|
* A type representing a reactive store index as a map from property values to sets of record IDs.
|
|
@@ -1174,7 +1180,7 @@ export declare type RSIndexDiff<R extends UnknownRecord, Property extends string
|
|
|
1174
1180
|
*
|
|
1175
1181
|
* @public
|
|
1176
1182
|
*/
|
|
1177
|
-
export declare type RSIndexMap<R extends UnknownRecord
|
|
1183
|
+
export declare type RSIndexMap<R extends UnknownRecord> = Map<any, Set<IdOf<R>>>;
|
|
1178
1184
|
|
|
1179
1185
|
/**
|
|
1180
1186
|
* Union type representing all supported serialized schema formats.
|
|
@@ -2051,6 +2057,9 @@ export declare class StoreQueries<R extends UnknownRecord> {
|
|
|
2051
2057
|
/* Excluded from this release type: __constructor */
|
|
2052
2058
|
/* Excluded from this release type: indexCache */
|
|
2053
2059
|
/* Excluded from this release type: historyCache */
|
|
2060
|
+
/* Excluded from this release type: getAllIdsForType */
|
|
2061
|
+
/* Excluded from this release type: getRecordById */
|
|
2062
|
+
/* Excluded from this release type: getNestedValue */
|
|
2054
2063
|
/**
|
|
2055
2064
|
* Creates a reactive computed that tracks the change history for records of a specific type.
|
|
2056
2065
|
* The returned computed provides incremental diffs showing what records of the given type
|
|
@@ -2081,8 +2090,10 @@ export declare class StoreQueries<R extends UnknownRecord> {
|
|
|
2081
2090
|
* The index automatically updates when records are added, updated, or removed, and results are cached
|
|
2082
2091
|
* for performance.
|
|
2083
2092
|
*
|
|
2093
|
+
* Supports nested property paths using backslash separator (e.g., 'metadata\\sessionId').
|
|
2094
|
+
*
|
|
2084
2095
|
* @param typeName - The type name of records to index
|
|
2085
|
-
* @param
|
|
2096
|
+
* @param path - The property name or backslash-delimited path to index by
|
|
2086
2097
|
* @returns A reactive computed containing the index map with change diffs
|
|
2087
2098
|
*
|
|
2088
2099
|
* @example
|
|
@@ -2094,18 +2105,16 @@ export declare class StoreQueries<R extends UnknownRecord> {
|
|
|
2094
2105
|
* const authorBooks = booksByAuthor.get().get('author:leguin')
|
|
2095
2106
|
* console.log(authorBooks) // Set<RecordId<Book>>
|
|
2096
2107
|
*
|
|
2097
|
-
* // Index by
|
|
2098
|
-
* const
|
|
2099
|
-
* const
|
|
2108
|
+
* // Index by nested property using backslash separator
|
|
2109
|
+
* const booksBySession = store.query.index('book', 'metadata\\sessionId')
|
|
2110
|
+
* const sessionBooks = booksBySession.get().get('session:alpha')
|
|
2100
2111
|
* ```
|
|
2101
2112
|
*
|
|
2102
2113
|
* @public
|
|
2103
2114
|
*/
|
|
2104
|
-
index<TypeName extends R['typeName'],
|
|
2115
|
+
index<TypeName extends R['typeName']>(typeName: TypeName, path: string): RSIndex<Extract<R, {
|
|
2105
2116
|
typeName: TypeName;
|
|
2106
|
-
}
|
|
2107
|
-
typeName: TypeName;
|
|
2108
|
-
}>, Property>;
|
|
2117
|
+
}>>;
|
|
2109
2118
|
/* Excluded from this release type: __uncached_createIndex */
|
|
2110
2119
|
/**
|
|
2111
2120
|
* Creates a reactive query that returns the first record matching the given query criteria.
|
package/dist-cjs/index.js
CHANGED
|
@@ -54,7 +54,7 @@ var import_StoreSchema = require("./lib/StoreSchema");
|
|
|
54
54
|
var import_StoreSideEffects = require("./lib/StoreSideEffects");
|
|
55
55
|
(0, import_utils.registerTldrawLibraryVersion)(
|
|
56
56
|
"@tldraw/store",
|
|
57
|
-
"4.2.0-next.
|
|
57
|
+
"4.2.0-next.67908ea044c6",
|
|
58
58
|
"cjs"
|
|
59
59
|
);
|
|
60
60
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/RecordType.ts"],
|
|
4
|
-
"sourcesContent": ["import { Expand, objectMapEntries, structuredClone, uniqueId } from '@tldraw/utils'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { StoreValidator } from './Store'\n\n/**\n * Utility type that extracts the record type from a RecordType instance.\n *\n * @example\n * ```ts\n * const Book = createRecordType<BookRecord>('book', { scope: 'document' })\n * type BookFromType = RecordTypeRecord<typeof Book> // BookRecord\n * ```\n *\n * @public\n */\nexport type RecordTypeRecord<R extends RecordType<any, any>> = ReturnType<R['create']>\n\n/**\n * Defines the scope of the record\n *\n * session: The record belongs to a single instance of the store. It should not be synced, and any persistence logic should 'de-instance-ize' the record before persisting it, and apply the reverse when rehydrating.\n * document: The record is persisted and synced. It is available to all store instances.\n * presence: The record belongs to a single instance of the store. It may be synced to other instances, but other instances should not make changes to it. It should not be persisted.\n *\n * @public\n * */\nexport type RecordScope = 'session' | 'document' | 'presence'\n\n/**\n * A record type is a type that can be stored in a record store. It is created with\n * `createRecordType`.\n *\n * @public\n */\nexport class RecordType<\n\tR extends UnknownRecord,\n\tRequiredProperties extends keyof Omit<R, 'id' | 'typeName'>,\n> {\n\t/**\n\t * Factory function that creates default properties for new records.\n\t * @public\n\t */\n\treadonly createDefaultProperties: () => Exclude<Omit<R, 'id' | 'typeName'>, RequiredProperties>\n\n\t/**\n\t * Validator function used to validate records of this type.\n\t * @public\n\t */\n\treadonly validator: StoreValidator<R>\n\n\t/**\n\t * Optional configuration specifying which record properties are ephemeral.\n\t * Ephemeral properties are not included in snapshots or synchronization.\n\t * @public\n\t */\n\treadonly ephemeralKeys?: { readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean }\n\n\t/**\n\t * Set of property names that are marked as ephemeral for efficient lookup.\n\t * @public\n\t */\n\treadonly ephemeralKeySet: ReadonlySet<string>\n\n\t/**\n\t * The scope that determines how records of this type are persisted and synchronized.\n\t * @public\n\t */\n\treadonly scope: RecordScope\n\n\t/**\n\t * Creates a new RecordType instance.\n\t *\n\t * typeName - The unique type name for records created by this RecordType\n\t * config - Configuration object for the RecordType\n\t * - createDefaultProperties - Function that returns default properties for new records\n\t * - validator - Optional validator function for record validation\n\t * - scope - Optional scope determining persistence behavior (defaults to 'document')\n\t * - ephemeralKeys - Optional mapping of property names to ephemeral status\n\t * @public\n\t */\n\tconstructor(\n\t\t/**\n\t\t * The unique type associated with this record.\n\t\t *\n\t\t * @public\n\t\t * @readonly\n\t\t */\n\t\tpublic readonly typeName: R['typeName'],\n\t\tconfig: {\n\t\t\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\t\t\treadonly createDefaultProperties: () => Exclude<\n\t\t\t\tOmit<R, 'id' | 'typeName'>,\n\t\t\t\tRequiredProperties\n\t\t\t>\n\t\t\treadonly validator?: StoreValidator<R>\n\t\t\treadonly scope?: RecordScope\n\t\t\treadonly ephemeralKeys?: { readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean }\n\t\t}\n\t) {\n\t\tthis.createDefaultProperties = config.createDefaultProperties\n\t\tthis.validator = config.validator ?? { validate: (r: unknown) => r as R }\n\t\tthis.scope = config.scope ?? 'document'\n\t\tthis.ephemeralKeys = config.ephemeralKeys\n\n\t\tconst ephemeralKeySet = new Set<string>()\n\t\tif (config.ephemeralKeys) {\n\t\t\tfor (const [key, isEphemeral] of objectMapEntries(config.ephemeralKeys)) {\n\t\t\t\tif (isEphemeral) ephemeralKeySet.add(key)\n\t\t\t}\n\t\t}\n\t\tthis.ephemeralKeySet = ephemeralKeySet\n\t}\n\n\t/**\n\t * Creates a new record of this type with the given properties.\n\t *\n\t * Properties are merged with default properties from the RecordType configuration.\n\t * If no id is provided, a unique id will be generated automatically.\n\t *\n\t * @example\n\t * ```ts\n\t * const book = Book.create({\n\t * title: 'The Great Gatsby',\n\t * author: 'F. Scott Fitzgerald'\n\t * })\n\t * // Result: { id: 'book:abc123', typeName: 'book', title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', inStock: true }\n\t * ```\n\t *\n\t * @param properties - The properties for the new record, including both required and optional fields\n\t * @returns The newly created record with generated id and typeName\n\t * @public\n\t */\n\tcreate(\n\t\tproperties: Expand<Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>>\n\t): R {\n\t\tconst result = {\n\t\t\t...this.createDefaultProperties(),\n\t\t\tid: 'id' in properties ? properties.id : this.createId(),\n\t\t} as any\n\n\t\tfor (const [k, v] of Object.entries(properties)) {\n\t\t\tif (v !== undefined) {\n\t\t\t\tresult[k] = v\n\t\t\t}\n\t\t}\n\n\t\tresult.typeName = this.typeName\n\n\t\treturn result as R\n\t}\n\n\t/**\n\t * Creates a deep copy of an existing record with a new unique id.\n\t *\n\t * This method performs a deep clone of all properties while generating a fresh id,\n\t * making it useful for duplicating records without id conflicts.\n\t *\n\t * @example\n\t * ```ts\n\t * const originalBook = Book.create({ title: '1984', author: 'George Orwell' })\n\t * const duplicatedBook = Book.clone(originalBook)\n\t * // duplicatedBook has same properties but different id\n\t * ```\n\t *\n\t * @param record - The record to clone\n\t * @returns A new record with the same properties but a different id\n\t * @public\n\t */\n\tclone(record: R): R {\n\t\treturn { ...structuredClone(record), id: this.createId() }\n\t}\n\n\t/**\n\t * Create a new ID for this record type.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * const id = recordType.createId()\n\t * ```\n\t *\n\t * @returns The new ID.\n\t * @public\n\t */\n\tcreateId(customUniquePart?: string): IdOf<R> {\n\t\treturn (this.typeName + ':' + (customUniquePart ?? uniqueId())) as IdOf<R>\n\t}\n\n\t/**\n\t * Extracts the unique identifier part from a full record id.\n\t *\n\t * Record ids have the format `typeName:uniquePart`. This method returns just the unique part.\n\t *\n\t * @example\n\t * ```ts\n\t * const bookId = Book.createId() // 'book:abc123'\n\t * const uniquePart = Book.parseId(bookId) // 'abc123'\n\t * ```\n\t *\n\t * @param id - The full record id to parse\n\t * @returns The unique identifier portion after the colon\n\t * @throws Error if the id is not valid for this record type\n\t * @public\n\t */\n\tparseId(id: IdOf<R>): string {\n\t\tif (!this.isId(id)) {\n\t\t\tthrow new Error(`ID \"${id}\" is not a valid ID for type \"${this.typeName}\"`)\n\t\t}\n\n\t\treturn id.slice(this.typeName.length + 1)\n\t}\n\n\t/**\n\t * Type guard that checks whether a record belongs to this RecordType.\n\t *\n\t * This method performs a runtime check by comparing the record's typeName\n\t * against this RecordType's typeName.\n\t *\n\t * @example\n\t * ```ts\n\t * if (Book.isInstance(someRecord)) {\n\t * // someRecord is now typed as a book record\n\t * console.log(someRecord.title)\n\t * }\n\t * ```\n\t *\n\t * @param record - The record to check, may be undefined\n\t * @returns True if the record is an instance of this record type\n\t * @public\n\t */\n\tisInstance(record?: UnknownRecord): record is R {\n\t\treturn record?.typeName === this.typeName\n\t}\n\n\t/**\n\t * Type guard that checks whether an id string belongs to this RecordType.\n\t *\n\t * Validates that the id starts with this RecordType's typeName followed by a colon.\n\t * This is more efficient than parsing the full id when you only need to verify the type.\n\t *\n\t * @example\n\t * ```ts\n\t * if (Book.isId(someId)) {\n\t * // someId is now typed as IdOf<BookRecord>\n\t * const book = store.get(someId)\n\t * }\n\t * ```\n\t *\n\t * @param id - The id string to check, may be undefined\n\t * @returns True if the id belongs to this record type\n\t * @public\n\t */\n\tisId(id?: string): id is IdOf<R> {\n\t\tif (!id) return false\n\t\tfor (let i = 0; i < this.typeName.length; i++) {\n\t\t\tif (id[i] !== this.typeName[i]) return false\n\t\t}\n\n\t\treturn id[this.typeName.length] === ':'\n\t}\n\n\t/**\n\t * Create a new RecordType that has the same type name as this RecordType and includes the given\n\t * default properties.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * const authorType = createRecordType('author', () => ({ living: true }))\n\t * const deadAuthorType = authorType.withDefaultProperties({ living: false })\n\t * ```\n\t *\n\t * @param createDefaultProperties - A function that returns the default properties of the new RecordType.\n\t * @returns The new RecordType.\n\t */\n\twithDefaultProperties<DefaultProps extends Omit<Partial<R>, 'typeName' | 'id'>>(\n\t\tcreateDefaultProperties: () => DefaultProps\n\t): RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>> {\n\t\treturn new RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>>(this.typeName, {\n\t\t\tcreateDefaultProperties: createDefaultProperties as any,\n\t\t\tvalidator: this.validator,\n\t\t\tscope: this.scope,\n\t\t\tephemeralKeys: this.ephemeralKeys,\n\t\t})\n\t}\n\n\t/**\n\t * Validates a record against this RecordType's validator and returns it with proper typing.\n\t *\n\t * This method runs the configured validator function and throws an error if validation fails.\n\t * If a previous version of the record is provided, it may use optimized validation.\n\t *\n\t * @example\n\t * ```ts\n\t * try {\n\t * const validBook = Book.validate(untrustedData)\n\t * // validBook is now properly typed and validated\n\t * } catch (error) {\n\t * console.log('Validation failed:', error.message)\n\t * }\n\t * ```\n\t *\n\t * @param record - The unknown record data to validate\n\t * @param recordBefore - Optional previous version for optimized validation\n\t * @returns The validated and properly typed record\n\t * @throws Error if validation fails\n\t * @public\n\t */\n\tvalidate(record: unknown, recordBefore?: R): R {\n\t\tif (recordBefore && this.validator.validateUsingKnownGoodVersion) {\n\t\t\treturn this.validator.validateUsingKnownGoodVersion(recordBefore, record)\n\t\t}\n\t\treturn this.validator.validate(record)\n\t}\n}\n\n/**\n * Creates a new RecordType with the specified configuration.\n *\n * This factory function creates a RecordType that can be used to create, validate, and manage\n * records of a specific type within a store. The resulting RecordType can be extended with\n * default properties using the withDefaultProperties method.\n *\n * @example\n * ```ts\n * interface BookRecord extends BaseRecord<'book', RecordId<BookRecord>> {\n * title: string\n * author: string\n * inStock: boolean\n * }\n *\n * const Book = createRecordType<BookRecord>('book', {\n * scope: 'document',\n * validator: bookValidator\n * })\n * ```\n *\n * @param typeName - The unique type name for this record type\n * @param config - Configuration object containing validator, scope, and ephemeral keys\n * @returns A new RecordType instance for creating and managing records\n * @public\n */\nexport function createRecordType<R extends UnknownRecord>(\n\ttypeName: R['typeName'],\n\tconfig: {\n\t\tvalidator?: StoreValidator<R>\n\t\tscope: RecordScope\n\t\tephemeralKeys?: { readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean }\n\t}\n): RecordType<R, keyof Omit<R, 'id' | 'typeName'>> {\n\treturn new RecordType<R, keyof Omit<R, 'id' | 'typeName'>>(typeName, {\n\t\tcreateDefaultProperties: () => ({}) as any,\n\t\tvalidator: config.validator,\n\t\tscope: config.scope,\n\t\tephemeralKeys: config.ephemeralKeys,\n\t})\n}\n\n/**\n * Assert whether an id correspond to a record type.\n *\n * @example\n *\n * ```ts\n * assertIdType(myId, \"shape\")\n * ```\n *\n * @param id - The id to check.\n * @param type - The type of the record.\n * @public\n */\nexport function assertIdType<R extends UnknownRecord>(\n\tid: string | undefined,\n\ttype: RecordType<R, any>\n): asserts id is IdOf<R> {\n\tif (!id || !type.isId(id)) {\n\t\tthrow new Error(`string ${JSON.stringify(id)} is not a valid ${type.typeName} id`)\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAoE;AAkC7D,MAAM,WAGX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2CD,YAOiB,UAChB,QAUC;AAXe;AAYhB,SAAK,0BAA0B,OAAO;AACtC,SAAK,YAAY,OAAO,aAAa,EAAE,UAAU,CAAC,MAAe,EAAO;AACxE,SAAK,QAAQ,OAAO,SAAS;AAC7B,SAAK,gBAAgB,OAAO;AAE5B,UAAM,kBAAkB,oBAAI,IAAY;AACxC,QAAI,OAAO,eAAe;AACzB,iBAAW,CAAC,KAAK,WAAW,SAAK,+BAAiB,OAAO,aAAa,GAAG;AACxE,YAAI,YAAa,iBAAgB,IAAI,
|
|
4
|
+
"sourcesContent": ["import { Expand, objectMapEntries, structuredClone, uniqueId } from '@tldraw/utils'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { StoreValidator } from './Store'\n\n/**\n * Utility type that extracts the record type from a RecordType instance.\n *\n * @example\n * ```ts\n * const Book = createRecordType<BookRecord>('book', { scope: 'document' })\n * type BookFromType = RecordTypeRecord<typeof Book> // BookRecord\n * ```\n *\n * @public\n */\nexport type RecordTypeRecord<R extends RecordType<any, any>> = ReturnType<R['create']>\n\n/**\n * Defines the scope of the record\n *\n * session: The record belongs to a single instance of the store. It should not be synced, and any persistence logic should 'de-instance-ize' the record before persisting it, and apply the reverse when rehydrating.\n * document: The record is persisted and synced. It is available to all store instances.\n * presence: The record belongs to a single instance of the store. It may be synced to other instances, but other instances should not make changes to it. It should not be persisted.\n *\n * @public\n * */\nexport type RecordScope = 'session' | 'document' | 'presence'\n\n/**\n * A record type is a type that can be stored in a record store. It is created with\n * `createRecordType`.\n *\n * @public\n */\nexport class RecordType<\n\tR extends UnknownRecord,\n\tRequiredProperties extends keyof Omit<R, 'id' | 'typeName'>,\n> {\n\t/**\n\t * Factory function that creates default properties for new records.\n\t * @public\n\t */\n\treadonly createDefaultProperties: () => Exclude<Omit<R, 'id' | 'typeName'>, RequiredProperties>\n\n\t/**\n\t * Validator function used to validate records of this type.\n\t * @public\n\t */\n\treadonly validator: StoreValidator<R>\n\n\t/**\n\t * Optional configuration specifying which record properties are ephemeral.\n\t * Ephemeral properties are not included in snapshots or synchronization.\n\t * @public\n\t */\n\treadonly ephemeralKeys?: { readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean }\n\n\t/**\n\t * Set of property names that are marked as ephemeral for efficient lookup.\n\t * @public\n\t */\n\treadonly ephemeralKeySet: ReadonlySet<string>\n\n\t/**\n\t * The scope that determines how records of this type are persisted and synchronized.\n\t * @public\n\t */\n\treadonly scope: RecordScope\n\n\t/**\n\t * Creates a new RecordType instance.\n\t *\n\t * typeName - The unique type name for records created by this RecordType\n\t * config - Configuration object for the RecordType\n\t * - createDefaultProperties - Function that returns default properties for new records\n\t * - validator - Optional validator function for record validation\n\t * - scope - Optional scope determining persistence behavior (defaults to 'document')\n\t * - ephemeralKeys - Optional mapping of property names to ephemeral status\n\t * @public\n\t */\n\tconstructor(\n\t\t/**\n\t\t * The unique type associated with this record.\n\t\t *\n\t\t * @public\n\t\t * @readonly\n\t\t */\n\t\tpublic readonly typeName: R['typeName'],\n\t\tconfig: {\n\t\t\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\t\t\treadonly createDefaultProperties: () => Exclude<\n\t\t\t\tOmit<R, 'id' | 'typeName'>,\n\t\t\t\tRequiredProperties\n\t\t\t>\n\t\t\treadonly validator?: StoreValidator<R>\n\t\t\treadonly scope?: RecordScope\n\t\t\treadonly ephemeralKeys?: { readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean }\n\t\t}\n\t) {\n\t\tthis.createDefaultProperties = config.createDefaultProperties\n\t\tthis.validator = config.validator ?? { validate: (r: unknown) => r as R }\n\t\tthis.scope = config.scope ?? 'document'\n\t\tthis.ephemeralKeys = config.ephemeralKeys\n\n\t\tconst ephemeralKeySet = new Set<string>()\n\t\tif (config.ephemeralKeys) {\n\t\t\tfor (const [key, isEphemeral] of objectMapEntries(config.ephemeralKeys)) {\n\t\t\t\tif (isEphemeral) ephemeralKeySet.add(key as string)\n\t\t\t}\n\t\t}\n\t\tthis.ephemeralKeySet = ephemeralKeySet\n\t}\n\n\t/**\n\t * Creates a new record of this type with the given properties.\n\t *\n\t * Properties are merged with default properties from the RecordType configuration.\n\t * If no id is provided, a unique id will be generated automatically.\n\t *\n\t * @example\n\t * ```ts\n\t * const book = Book.create({\n\t * title: 'The Great Gatsby',\n\t * author: 'F. Scott Fitzgerald'\n\t * })\n\t * // Result: { id: 'book:abc123', typeName: 'book', title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', inStock: true }\n\t * ```\n\t *\n\t * @param properties - The properties for the new record, including both required and optional fields\n\t * @returns The newly created record with generated id and typeName\n\t * @public\n\t */\n\tcreate(\n\t\tproperties: Expand<Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>>\n\t): R {\n\t\tconst result = {\n\t\t\t...this.createDefaultProperties(),\n\t\t\tid: 'id' in properties ? properties.id : this.createId(),\n\t\t} as any\n\n\t\tfor (const [k, v] of Object.entries(properties)) {\n\t\t\tif (v !== undefined) {\n\t\t\t\tresult[k] = v\n\t\t\t}\n\t\t}\n\n\t\tresult.typeName = this.typeName\n\n\t\treturn result as R\n\t}\n\n\t/**\n\t * Creates a deep copy of an existing record with a new unique id.\n\t *\n\t * This method performs a deep clone of all properties while generating a fresh id,\n\t * making it useful for duplicating records without id conflicts.\n\t *\n\t * @example\n\t * ```ts\n\t * const originalBook = Book.create({ title: '1984', author: 'George Orwell' })\n\t * const duplicatedBook = Book.clone(originalBook)\n\t * // duplicatedBook has same properties but different id\n\t * ```\n\t *\n\t * @param record - The record to clone\n\t * @returns A new record with the same properties but a different id\n\t * @public\n\t */\n\tclone(record: R): R {\n\t\treturn { ...structuredClone(record), id: this.createId() }\n\t}\n\n\t/**\n\t * Create a new ID for this record type.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * const id = recordType.createId()\n\t * ```\n\t *\n\t * @returns The new ID.\n\t * @public\n\t */\n\tcreateId(customUniquePart?: string): IdOf<R> {\n\t\treturn (this.typeName + ':' + (customUniquePart ?? uniqueId())) as IdOf<R>\n\t}\n\n\t/**\n\t * Extracts the unique identifier part from a full record id.\n\t *\n\t * Record ids have the format `typeName:uniquePart`. This method returns just the unique part.\n\t *\n\t * @example\n\t * ```ts\n\t * const bookId = Book.createId() // 'book:abc123'\n\t * const uniquePart = Book.parseId(bookId) // 'abc123'\n\t * ```\n\t *\n\t * @param id - The full record id to parse\n\t * @returns The unique identifier portion after the colon\n\t * @throws Error if the id is not valid for this record type\n\t * @public\n\t */\n\tparseId(id: IdOf<R>): string {\n\t\tif (!this.isId(id)) {\n\t\t\tthrow new Error(`ID \"${id}\" is not a valid ID for type \"${this.typeName}\"`)\n\t\t}\n\n\t\treturn id.slice(this.typeName.length + 1)\n\t}\n\n\t/**\n\t * Type guard that checks whether a record belongs to this RecordType.\n\t *\n\t * This method performs a runtime check by comparing the record's typeName\n\t * against this RecordType's typeName.\n\t *\n\t * @example\n\t * ```ts\n\t * if (Book.isInstance(someRecord)) {\n\t * // someRecord is now typed as a book record\n\t * console.log(someRecord.title)\n\t * }\n\t * ```\n\t *\n\t * @param record - The record to check, may be undefined\n\t * @returns True if the record is an instance of this record type\n\t * @public\n\t */\n\tisInstance(record?: UnknownRecord): record is R {\n\t\treturn record?.typeName === this.typeName\n\t}\n\n\t/**\n\t * Type guard that checks whether an id string belongs to this RecordType.\n\t *\n\t * Validates that the id starts with this RecordType's typeName followed by a colon.\n\t * This is more efficient than parsing the full id when you only need to verify the type.\n\t *\n\t * @example\n\t * ```ts\n\t * if (Book.isId(someId)) {\n\t * // someId is now typed as IdOf<BookRecord>\n\t * const book = store.get(someId)\n\t * }\n\t * ```\n\t *\n\t * @param id - The id string to check, may be undefined\n\t * @returns True if the id belongs to this record type\n\t * @public\n\t */\n\tisId(id?: string): id is IdOf<R> {\n\t\tif (!id) return false\n\t\tfor (let i = 0; i < this.typeName.length; i++) {\n\t\t\tif (id[i] !== this.typeName[i]) return false\n\t\t}\n\n\t\treturn id[this.typeName.length] === ':'\n\t}\n\n\t/**\n\t * Create a new RecordType that has the same type name as this RecordType and includes the given\n\t * default properties.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * const authorType = createRecordType('author', () => ({ living: true }))\n\t * const deadAuthorType = authorType.withDefaultProperties({ living: false })\n\t * ```\n\t *\n\t * @param createDefaultProperties - A function that returns the default properties of the new RecordType.\n\t * @returns The new RecordType.\n\t */\n\twithDefaultProperties<DefaultProps extends Omit<Partial<R>, 'typeName' | 'id'>>(\n\t\tcreateDefaultProperties: () => DefaultProps\n\t): RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>> {\n\t\treturn new RecordType<R, Exclude<RequiredProperties, keyof DefaultProps>>(this.typeName, {\n\t\t\tcreateDefaultProperties: createDefaultProperties as any,\n\t\t\tvalidator: this.validator,\n\t\t\tscope: this.scope,\n\t\t\tephemeralKeys: this.ephemeralKeys,\n\t\t})\n\t}\n\n\t/**\n\t * Validates a record against this RecordType's validator and returns it with proper typing.\n\t *\n\t * This method runs the configured validator function and throws an error if validation fails.\n\t * If a previous version of the record is provided, it may use optimized validation.\n\t *\n\t * @example\n\t * ```ts\n\t * try {\n\t * const validBook = Book.validate(untrustedData)\n\t * // validBook is now properly typed and validated\n\t * } catch (error) {\n\t * console.log('Validation failed:', error.message)\n\t * }\n\t * ```\n\t *\n\t * @param record - The unknown record data to validate\n\t * @param recordBefore - Optional previous version for optimized validation\n\t * @returns The validated and properly typed record\n\t * @throws Error if validation fails\n\t * @public\n\t */\n\tvalidate(record: unknown, recordBefore?: R): R {\n\t\tif (recordBefore && this.validator.validateUsingKnownGoodVersion) {\n\t\t\treturn this.validator.validateUsingKnownGoodVersion(recordBefore, record)\n\t\t}\n\t\treturn this.validator.validate(record)\n\t}\n}\n\n/**\n * Creates a new RecordType with the specified configuration.\n *\n * This factory function creates a RecordType that can be used to create, validate, and manage\n * records of a specific type within a store. The resulting RecordType can be extended with\n * default properties using the withDefaultProperties method.\n *\n * @example\n * ```ts\n * interface BookRecord extends BaseRecord<'book', RecordId<BookRecord>> {\n * title: string\n * author: string\n * inStock: boolean\n * }\n *\n * const Book = createRecordType<BookRecord>('book', {\n * scope: 'document',\n * validator: bookValidator\n * })\n * ```\n *\n * @param typeName - The unique type name for this record type\n * @param config - Configuration object containing validator, scope, and ephemeral keys\n * @returns A new RecordType instance for creating and managing records\n * @public\n */\nexport function createRecordType<R extends UnknownRecord>(\n\ttypeName: R['typeName'],\n\tconfig: {\n\t\tvalidator?: StoreValidator<R>\n\t\tscope: RecordScope\n\t\tephemeralKeys?: { readonly [K in Exclude<keyof R, 'id' | 'typeName'>]: boolean }\n\t}\n): RecordType<R, keyof Omit<R, 'id' | 'typeName'>> {\n\treturn new RecordType<R, keyof Omit<R, 'id' | 'typeName'>>(typeName, {\n\t\tcreateDefaultProperties: () => ({}) as any,\n\t\tvalidator: config.validator,\n\t\tscope: config.scope,\n\t\tephemeralKeys: config.ephemeralKeys,\n\t})\n}\n\n/**\n * Assert whether an id correspond to a record type.\n *\n * @example\n *\n * ```ts\n * assertIdType(myId, \"shape\")\n * ```\n *\n * @param id - The id to check.\n * @param type - The type of the record.\n * @public\n */\nexport function assertIdType<R extends UnknownRecord>(\n\tid: string | undefined,\n\ttype: RecordType<R, any>\n): asserts id is IdOf<R> {\n\tif (!id || !type.isId(id)) {\n\t\tthrow new Error(`string ${JSON.stringify(id)} is not a valid ${type.typeName} id`)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -51,6 +51,40 @@ class StoreQueries {
|
|
|
51
51
|
* @internal
|
|
52
52
|
*/
|
|
53
53
|
historyCache = /* @__PURE__ */ new Map();
|
|
54
|
+
/**
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
getAllIdsForType(typeName) {
|
|
58
|
+
const ids = /* @__PURE__ */ new Set();
|
|
59
|
+
for (const record of this.recordMap.values()) {
|
|
60
|
+
if (record.typeName === typeName) {
|
|
61
|
+
ids.add(record.id);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return ids;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
getRecordById(typeName, id) {
|
|
70
|
+
const record = this.recordMap.get(id);
|
|
71
|
+
if (record && record.typeName === typeName) {
|
|
72
|
+
return record;
|
|
73
|
+
}
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Helper to extract nested property value using pre-split path parts.
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
getNestedValue(obj, pathParts) {
|
|
81
|
+
let current = obj;
|
|
82
|
+
for (const part of pathParts) {
|
|
83
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
84
|
+
current = current[part];
|
|
85
|
+
}
|
|
86
|
+
return current;
|
|
87
|
+
}
|
|
54
88
|
/**
|
|
55
89
|
* Creates a reactive computed that tracks the change history for records of a specific type.
|
|
56
90
|
* The returned computed provides incremental diffs showing what records of the given type
|
|
@@ -151,8 +185,10 @@ class StoreQueries {
|
|
|
151
185
|
* The index automatically updates when records are added, updated, or removed, and results are cached
|
|
152
186
|
* for performance.
|
|
153
187
|
*
|
|
188
|
+
* Supports nested property paths using backslash separator (e.g., 'metadata\\sessionId').
|
|
189
|
+
*
|
|
154
190
|
* @param typeName - The type name of records to index
|
|
155
|
-
* @param
|
|
191
|
+
* @param path - The property name or backslash-delimited path to index by
|
|
156
192
|
* @returns A reactive computed containing the index map with change diffs
|
|
157
193
|
*
|
|
158
194
|
* @example
|
|
@@ -164,19 +200,19 @@ class StoreQueries {
|
|
|
164
200
|
* const authorBooks = booksByAuthor.get().get('author:leguin')
|
|
165
201
|
* console.log(authorBooks) // Set<RecordId<Book>>
|
|
166
202
|
*
|
|
167
|
-
* // Index by
|
|
168
|
-
* const
|
|
169
|
-
* const
|
|
203
|
+
* // Index by nested property using backslash separator
|
|
204
|
+
* const booksBySession = store.query.index('book', 'metadata\\sessionId')
|
|
205
|
+
* const sessionBooks = booksBySession.get().get('session:alpha')
|
|
170
206
|
* ```
|
|
171
207
|
*
|
|
172
208
|
* @public
|
|
173
209
|
*/
|
|
174
|
-
index(typeName,
|
|
175
|
-
const cacheKey = typeName + ":" +
|
|
210
|
+
index(typeName, path) {
|
|
211
|
+
const cacheKey = typeName + ":" + path;
|
|
176
212
|
if (this.indexCache.has(cacheKey)) {
|
|
177
213
|
return this.indexCache.get(cacheKey);
|
|
178
214
|
}
|
|
179
|
-
const index = this.__uncached_createIndex(typeName,
|
|
215
|
+
const index = this.__uncached_createIndex(typeName, path);
|
|
180
216
|
this.indexCache.set(cacheKey, index);
|
|
181
217
|
return index;
|
|
182
218
|
}
|
|
@@ -184,30 +220,36 @@ class StoreQueries {
|
|
|
184
220
|
* Creates a new index without checking the cache. This method performs the actual work
|
|
185
221
|
* of building the reactive index computation that tracks property values to record ID sets.
|
|
186
222
|
*
|
|
223
|
+
* Supports nested property paths using backslash separator.
|
|
224
|
+
*
|
|
187
225
|
* @param typeName - The type name of records to index
|
|
188
|
-
* @param
|
|
226
|
+
* @param path - The property name or backslash-delimited path to index by
|
|
189
227
|
* @returns A reactive computed containing the index map with change diffs
|
|
190
228
|
*
|
|
191
229
|
* @internal
|
|
192
230
|
*/
|
|
193
|
-
__uncached_createIndex(typeName,
|
|
231
|
+
__uncached_createIndex(typeName, path) {
|
|
194
232
|
const typeHistory = this.filterHistory(typeName);
|
|
233
|
+
const pathParts = path.split("\\");
|
|
234
|
+
const getPropertyValue = pathParts.length > 1 ? (obj) => this.getNestedValue(obj, pathParts) : (obj) => obj[path];
|
|
195
235
|
const fromScratch = () => {
|
|
196
236
|
typeHistory.get();
|
|
197
237
|
const res = /* @__PURE__ */ new Map();
|
|
198
238
|
for (const record of this.recordMap.values()) {
|
|
199
239
|
if (record.typeName === typeName) {
|
|
200
|
-
const value = record
|
|
201
|
-
if (
|
|
202
|
-
res.
|
|
240
|
+
const value = getPropertyValue(record);
|
|
241
|
+
if (value !== void 0) {
|
|
242
|
+
if (!res.has(value)) {
|
|
243
|
+
res.set(value, /* @__PURE__ */ new Set());
|
|
244
|
+
}
|
|
245
|
+
res.get(value).add(record.id);
|
|
203
246
|
}
|
|
204
|
-
res.get(value).add(record.id);
|
|
205
247
|
}
|
|
206
248
|
}
|
|
207
249
|
return res;
|
|
208
250
|
};
|
|
209
251
|
return (0, import_state.computed)(
|
|
210
|
-
"index:" + typeName + ":" +
|
|
252
|
+
"index:" + typeName + ":" + path,
|
|
211
253
|
(prevValue, lastComputedEpoch) => {
|
|
212
254
|
if ((0, import_state.isUninitialized)(prevValue)) return fromScratch();
|
|
213
255
|
const history = typeHistory.getDiffSince(lastComputedEpoch);
|
|
@@ -233,24 +275,32 @@ class StoreQueries {
|
|
|
233
275
|
for (const changes of history) {
|
|
234
276
|
for (const record of (0, import_utils.objectMapValues)(changes.added)) {
|
|
235
277
|
if (record.typeName === typeName) {
|
|
236
|
-
const value = record
|
|
237
|
-
|
|
278
|
+
const value = getPropertyValue(record);
|
|
279
|
+
if (value !== void 0) {
|
|
280
|
+
add(value, record.id);
|
|
281
|
+
}
|
|
238
282
|
}
|
|
239
283
|
}
|
|
240
284
|
for (const [from, to] of (0, import_utils.objectMapValues)(changes.updated)) {
|
|
241
285
|
if (to.typeName === typeName) {
|
|
242
|
-
const prev = from
|
|
243
|
-
const next = to
|
|
286
|
+
const prev = getPropertyValue(from);
|
|
287
|
+
const next = getPropertyValue(to);
|
|
244
288
|
if (prev !== next) {
|
|
245
|
-
|
|
246
|
-
|
|
289
|
+
if (prev !== void 0) {
|
|
290
|
+
remove(prev, to.id);
|
|
291
|
+
}
|
|
292
|
+
if (next !== void 0) {
|
|
293
|
+
add(next, to.id);
|
|
294
|
+
}
|
|
247
295
|
}
|
|
248
296
|
}
|
|
249
297
|
}
|
|
250
298
|
for (const record of (0, import_utils.objectMapValues)(changes.removed)) {
|
|
251
299
|
if (record.typeName === typeName) {
|
|
252
|
-
const value = record
|
|
253
|
-
|
|
300
|
+
const value = getPropertyValue(record);
|
|
301
|
+
if (value !== void 0) {
|
|
302
|
+
remove(value, record.id);
|
|
303
|
+
}
|
|
254
304
|
}
|
|
255
305
|
}
|
|
256
306
|
}
|
|
@@ -374,11 +424,7 @@ class StoreQueries {
|
|
|
374
424
|
typeHistory.get();
|
|
375
425
|
const query = queryCreator();
|
|
376
426
|
if (Object.keys(query).length === 0) {
|
|
377
|
-
|
|
378
|
-
for (const record of this.recordMap.values()) {
|
|
379
|
-
if (record.typeName === typeName) ids.add(record.id);
|
|
380
|
-
}
|
|
381
|
-
return ids;
|
|
427
|
+
return this.getAllIdsForType(typeName);
|
|
382
428
|
}
|
|
383
429
|
return (0, import_executeQuery.executeQuery)(this, typeName, query);
|
|
384
430
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/StoreQueries.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tAtom,\n\tcomputed,\n\tComputed,\n\tEMPTY_ARRAY,\n\tisUninitialized,\n\tRESET_VALUE,\n\twithDiff,\n} from '@tldraw/state'\nimport { areArraysShallowEqual, isEqual, objectMapValues } from '@tldraw/utils'\nimport { AtomMap } from './AtomMap'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { executeQuery, objectMatchesQuery, QueryExpression } from './executeQuery'\nimport { IncrementalSetConstructor } from './IncrementalSetConstructor'\nimport { RecordsDiff } from './RecordsDiff'\nimport { diffSets } from './setUtils'\nimport { CollectionDiff } from './Store'\n\n/**\n * A type representing the diff of changes to a reactive store index.\n * Maps property values to the collection differences for record IDs that have that property value.\n *\n * @example\n * ```ts\n * // For an index on book titles, the diff might look like:\n * const titleIndexDiff: RSIndexDiff<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', { added: new Set(['book:1']), removed: new Set() }],\n * ['Animal Farm', { added: new Set(), removed: new Set(['book:2']) }]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexDiff<\n\tR extends UnknownRecord,\n\tProperty extends string & keyof R = string & keyof R,\n> = Map<R[Property], CollectionDiff<IdOf<R>>>\n\n/**\n * A type representing a reactive store index as a map from property values to sets of record IDs.\n * This is used to efficiently look up records by a specific property value.\n *\n * @example\n * ```ts\n * // Index mapping book titles to the IDs of books with that title\n * const titleIndex: RSIndexMap<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', new Set(['book:1'])],\n * ['Animal Farm', new Set(['book:2', 'book:3'])]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexMap<\n\tR extends UnknownRecord,\n\tProperty extends string & keyof R = string & keyof R,\n> = Map<R[Property], Set<IdOf<R>>>\n\n/**\n * A reactive computed index that provides efficient lookups of records by property values.\n * Returns a computed value containing an RSIndexMap with diffs for change tracking.\n *\n * @example\n * ```ts\n * // Create an index on book authors\n * const authorIndex: RSIndex<Book, 'authorId'> = store.query.index('book', 'authorId')\n *\n * // Get all books by a specific author\n * const leguinBooks = authorIndex.get().get('author:leguin')\n * ```\n *\n * @public\n */\nexport type RSIndex<\n\tR extends UnknownRecord,\n\tProperty extends string & keyof R = string & keyof R,\n> = Computed<RSIndexMap<R, Property>, RSIndexDiff<R, Property>>\n\n/**\n * A class that provides reactive querying capabilities for a record store.\n * Offers methods to create indexes, filter records, and perform efficient lookups with automatic cache management.\n * All queries are reactive and will automatically update when the underlying store data changes.\n *\n * @example\n * ```ts\n * // Create a store with books\n * const store = new Store({ schema: StoreSchema.create({ book: Book, author: Author }) })\n *\n * // Get reactive queries for books\n * const booksByAuthor = store.query.index('book', 'authorId')\n * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n * ```\n *\n * @public\n */\nexport class StoreQueries<R extends UnknownRecord> {\n\t/**\n\t * Creates a new StoreQueries instance.\n\t *\n\t * recordMap - The atom map containing all records in the store\n\t * history - The atom tracking the store's change history with diffs\n\t *\n\t * @internal\n\t */\n\tconstructor(\n\t\tprivate readonly recordMap: AtomMap<IdOf<R>, R>,\n\t\tprivate readonly history: Atom<number, RecordsDiff<R>>\n\t) {}\n\n\t/**\n\t * A cache of derivations (indexes).\n\t *\n\t * @internal\n\t */\n\tprivate indexCache = new Map<string, RSIndex<R>>()\n\n\t/**\n\t * A cache of derivations (filtered histories).\n\t *\n\t * @internal\n\t */\n\tprivate historyCache = new Map<string, Computed<number, RecordsDiff<R>>>()\n\n\t/**\n\t * Creates a reactive computed that tracks the change history for records of a specific type.\n\t * The returned computed provides incremental diffs showing what records of the given type\n\t * have been added, updated, or removed.\n\t *\n\t * @param typeName - The type name to filter the history by\n\t * @returns A computed value containing the current epoch and diffs of changes for the specified type\n\t *\n\t * @example\n\t * ```ts\n\t * // Track changes to book records only\n\t * const bookHistory = store.query.filterHistory('book')\n\t *\n\t * // React to book changes\n\t * react('book-changes', () => {\n\t * const currentEpoch = bookHistory.get()\n\t * console.log('Books updated at epoch:', currentEpoch)\n\t * })\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic filterHistory<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Computed<number, RecordsDiff<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tif (this.historyCache.has(typeName)) {\n\t\t\treturn this.historyCache.get(typeName) as any\n\t\t}\n\n\t\tconst filtered = computed<number, RecordsDiff<S>>(\n\t\t\t'filterHistory:' + typeName,\n\t\t\t(lastValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(lastValue)) {\n\t\t\t\t\treturn this.history.get()\n\t\t\t\t}\n\n\t\t\t\tconst diff = this.history.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (diff === RESET_VALUE) return this.history.get()\n\n\t\t\t\tconst res = { added: {}, removed: {}, updated: {} } as RecordsDiff<S>\n\t\t\t\tlet numAdded = 0\n\t\t\t\tlet numRemoved = 0\n\t\t\t\tlet numUpdated = 0\n\n\t\t\t\tfor (const changes of diff) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.removed[added.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tconst original = res.removed[added.id as IdOf<S>]\n\t\t\t\t\t\t\t\tdelete res.removed[added.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumRemoved--\n\t\t\t\t\t\t\t\tif (original !== added) {\n\t\t\t\t\t\t\t\t\tres.updated[added.id as IdOf<S>] = [original, added as S]\n\t\t\t\t\t\t\t\t\tnumUpdated++\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.added[added.id as IdOf<S>] = added as S\n\t\t\t\t\t\t\t\tnumAdded++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.added[to.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tres.added[to.id as IdOf<S>] = to as S\n\t\t\t\t\t\t\t} else if (res.updated[to.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tres.updated[to.id as IdOf<S>] = [res.updated[to.id as IdOf<S>][0], to as S]\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.updated[to.id as IdOf<S>] = [from as S, to as S]\n\t\t\t\t\t\t\t\tnumUpdated++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.added[removed.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\t// was added during this diff sequence, so just undo the add\n\t\t\t\t\t\t\t\tdelete res.added[removed.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumAdded--\n\t\t\t\t\t\t\t} else if (res.updated[removed.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\t// remove oldest version\n\t\t\t\t\t\t\t\tres.removed[removed.id as IdOf<S>] = res.updated[removed.id as IdOf<S>][0]\n\t\t\t\t\t\t\t\tdelete res.updated[removed.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumUpdated--\n\t\t\t\t\t\t\t\tnumRemoved++\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.removed[removed.id as IdOf<S>] = removed as S\n\t\t\t\t\t\t\t\tnumRemoved++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (numAdded || numRemoved || numUpdated) {\n\t\t\t\t\treturn withDiff(this.history.get(), res)\n\t\t\t\t} else {\n\t\t\t\t\treturn lastValue\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\n\t\tthis.historyCache.set(typeName, filtered)\n\n\t\treturn filtered\n\t}\n\n\t/**\n\t * Creates a reactive index that maps property values to sets of record IDs for efficient lookups.\n\t * The index automatically updates when records are added, updated, or removed, and results are cached\n\t * for performance.\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param property - The property name to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Create an index of books by author ID\n\t * const booksByAuthor = store.query.index('book', 'authorId')\n\t *\n\t * // Get all books by a specific author\n\t * const authorBooks = booksByAuthor.get().get('author:leguin')\n\t * console.log(authorBooks) // Set<RecordId<Book>>\n\t *\n\t * // Index by title for quick title lookups\n\t * const booksByTitle = store.query.index('book', 'title')\n\t * const booksLatheOfHeaven = booksByTitle.get().get('The Lathe of Heaven')\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic index<\n\t\tTypeName extends R['typeName'],\n\t\tProperty extends string & keyof Extract<R, { typeName: TypeName }>,\n\t>(typeName: TypeName, property: Property): RSIndex<Extract<R, { typeName: TypeName }>, Property> {\n\t\tconst cacheKey = typeName + ':' + property\n\n\t\tif (this.indexCache.has(cacheKey)) {\n\t\t\treturn this.indexCache.get(cacheKey) as any\n\t\t}\n\n\t\tconst index = this.__uncached_createIndex(typeName, property)\n\n\t\tthis.indexCache.set(cacheKey, index as any)\n\n\t\treturn index\n\t}\n\n\t/**\n\t * Creates a new index without checking the cache. This method performs the actual work\n\t * of building the reactive index computation that tracks property values to record ID sets.\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param property - The property name to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @internal\n\t */\n\t__uncached_createIndex<\n\t\tTypeName extends R['typeName'],\n\t\tProperty extends string & keyof Extract<R, { typeName: TypeName }>,\n\t>(typeName: TypeName, property: Property): RSIndex<Extract<R, { typeName: TypeName }>, Property> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref typeHistory early so that the first time the incremental version runs\n\t\t\t// it gets a diff to work with instead of having to bail to this from-scratch version\n\t\t\ttypeHistory.get()\n\t\t\tconst res = new Map<S[Property], Set<IdOf<S>>>()\n\t\t\tfor (const record of this.recordMap.values()) {\n\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\tconst value = (record as S)[property]\n\t\t\t\t\tif (!res.has(value)) {\n\t\t\t\t\t\tres.set(value, new Set())\n\t\t\t\t\t}\n\t\t\t\t\tres.get(value)!.add(record.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn res\n\t\t}\n\n\t\treturn computed<RSIndexMap<S, Property>, RSIndexDiff<S, Property>>(\n\t\t\t'index:' + typeName + ':' + property,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(prevValue)) return fromScratch()\n\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\tconst setConstructors = new Map<any, IncrementalSetConstructor<IdOf<S>>>()\n\n\t\t\t\tconst add = (value: S[Property], id: IdOf<S>) => {\n\t\t\t\t\tlet setConstructor = setConstructors.get(value)\n\t\t\t\t\tif (!setConstructor)\n\t\t\t\t\t\tsetConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\t\t\tprevValue.get(value) ?? new Set()\n\t\t\t\t\t\t)\n\t\t\t\t\tsetConstructor.add(id)\n\t\t\t\t\tsetConstructors.set(value, setConstructor)\n\t\t\t\t}\n\n\t\t\t\tconst remove = (value: S[Property], id: IdOf<S>) => {\n\t\t\t\t\tlet set = setConstructors.get(value)\n\t\t\t\t\tif (!set) set = new IncrementalSetConstructor<IdOf<S>>(prevValue.get(value) ?? new Set())\n\t\t\t\t\tset.remove(id)\n\t\t\t\t\tsetConstructors.set(value, set)\n\t\t\t\t}\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const record of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = (record as S)[property]\n\t\t\t\t\t\t\tadd(value, record.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tconst prev = (from as S)[property]\n\t\t\t\t\t\t\tconst next = (to as S)[property]\n\t\t\t\t\t\t\tif (prev !== next) {\n\t\t\t\t\t\t\t\tremove(prev, to.id)\n\t\t\t\t\t\t\t\tadd(next, to.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const record of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = (record as S)[property]\n\t\t\t\t\t\t\tremove(value, record.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet nextValue: undefined | RSIndexMap<S, Property> = undefined\n\t\t\t\tlet nextDiff: undefined | RSIndexDiff<S, Property> = undefined\n\n\t\t\t\tfor (const [value, setConstructor] of setConstructors) {\n\t\t\t\t\tconst result = setConstructor.get()\n\t\t\t\t\tif (!result) continue\n\t\t\t\t\tif (!nextValue) nextValue = new Map(prevValue)\n\t\t\t\t\tif (!nextDiff) nextDiff = new Map()\n\t\t\t\t\tif (result.value.size === 0) {\n\t\t\t\t\t\tnextValue.delete(value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextValue.set(value, result.value)\n\t\t\t\t\t}\n\t\t\t\t\tnextDiff.set(value, result.diff)\n\t\t\t\t}\n\n\t\t\t\tif (nextValue && nextDiff) {\n\t\t\t\t\treturn withDiff(nextValue, nextDiff)\n\t\t\t\t}\n\n\t\t\t\treturn prevValue\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns the first record matching the given query criteria.\n\t * Returns undefined if no matching record is found. The query automatically updates\n\t * when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing the first matching record or undefined\n\t *\n\t * @example\n\t * ```ts\n\t * // Find the first book with a specific title\n\t * const bookLatheOfHeaven = store.query.record('book', () => ({ title: { eq: 'The Lathe of Heaven' } }))\n\t * console.log(bookLatheOfHeaven.get()?.title) // 'The Lathe of Heaven' or undefined\n\t *\n\t * // Find any book in stock\n\t * const anyInStockBook = store.query.record('book', () => ({ inStock: { eq: true } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\trecord<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'record:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<Extract<R, { typeName: TypeName }> | undefined> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, name)\n\n\t\treturn computed<S | undefined>(name, () => {\n\t\t\tfor (const id of ids.get()) {\n\t\t\t\treturn this.recordMap.get(id) as S | undefined\n\t\t\t}\n\t\t\treturn undefined\n\t\t})\n\t}\n\n\t/**\n\t * Creates a reactive query that returns an array of all records matching the given query criteria.\n\t * The array automatically updates when records are added, updated, or removed.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing an array of all matching records\n\t *\n\t * @example\n\t * ```ts\n\t * // Get all books in stock\n\t * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBooks.get()) // Book[]\n\t *\n\t * // Get all books by a specific author\n\t * const leguinBooks = store.query.records('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t *\n\t * // Get all books (no filter)\n\t * const allBooks = store.query.records('book')\n\t * ```\n\t *\n\t * @public\n\t */\n\trecords<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'records:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<Array<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, 'ids:' + name)\n\n\t\treturn computed<S[]>(\n\t\t\tname,\n\t\t\t() => {\n\t\t\t\treturn Array.from(ids.get(), (id) => this.recordMap.get(id) as S)\n\t\t\t},\n\t\t\t{\n\t\t\t\tisEqual: areArraysShallowEqual,\n\t\t\t}\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns a set of record IDs matching the given query criteria.\n\t * This is more efficient than `records()` when you only need the IDs and not the full record objects.\n\t * The set automatically updates with collection diffs when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing a set of matching record IDs with collection diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Get IDs of all books in stock\n\t * const inStockBookIds = store.query.ids('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBookIds.get()) // Set<RecordId<Book>>\n\t *\n\t * // Get all book IDs (no filter)\n\t * const allBookIds = store.query.ids('book')\n\t *\n\t * // Use with other queries for efficient lookups\n\t * const authorBookIds = store.query.ids('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\tids<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'ids:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<\n\t\tSet<IdOf<Extract<R, { typeName: TypeName }>>>,\n\t\tCollectionDiff<IdOf<Extract<R, { typeName: TypeName }>>>\n\t> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref type history early to allow first incremental update to use diffs\n\t\t\ttypeHistory.get()\n\t\t\tconst query: QueryExpression<S> = queryCreator()\n\t\t\tif (Object.keys(query).length === 0) {\n\t\t\t\tconst ids = new Set<IdOf<S>>()\n\t\t\t\tfor (const record of this.recordMap.values()) {\n\t\t\t\t\tif (record.typeName === typeName) ids.add(record.id)\n\t\t\t\t}\n\t\t\t\treturn ids\n\t\t\t}\n\n\t\t\treturn executeQuery(this, typeName, query)\n\t\t}\n\n\t\tconst fromScratchWithDiff = (prevValue: Set<IdOf<S>>) => {\n\t\t\tconst nextValue = fromScratch()\n\t\t\tconst diff = diffSets(prevValue, nextValue)\n\t\t\tif (diff) {\n\t\t\t\treturn withDiff(nextValue, diff)\n\t\t\t} else {\n\t\t\t\treturn prevValue\n\t\t\t}\n\t\t}\n\t\tconst cachedQuery = computed('ids_query:' + name, queryCreator, {\n\t\t\tisEqual,\n\t\t})\n\n\t\treturn computed(\n\t\t\t'query:' + name,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tconst query = cachedQuery.get()\n\t\t\t\tif (isUninitialized(prevValue)) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\t// if the query changed since last time this ran then we need to start again\n\t\t\t\tif (lastComputedEpoch < cachedQuery.lastChangedEpoch) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\t// otherwise iterate over the changes from the store and apply them to the previous value if needed\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\tconst setConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\tprevValue\n\t\t\t\t) as IncrementalSetConstructor<IdOf<S>>\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName && objectMatchesQuery(query, added)) {\n\t\t\t\t\t\t\tsetConstructor.add(added.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [_, updated] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (updated.typeName === typeName) {\n\t\t\t\t\t\t\tif (objectMatchesQuery(query, updated)) {\n\t\t\t\t\t\t\t\tsetConstructor.add(updated.id)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsetConstructor.remove(updated.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tsetConstructor.remove(removed.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst result = setConstructor.get()\n\t\t\t\tif (!result) {\n\t\t\t\t\treturn prevValue\n\t\t\t\t}\n\n\t\t\t\treturn withDiff(result.value, result.diff)\n\t\t\t},\n\t\t\t{ historyLength: 50 }\n\t\t)\n\t}\n\n\t/**\n\t * Executes a one-time query against the current store state and returns matching records.\n\t * This is a non-reactive query that returns results immediately without creating a computed value.\n\t * Use this when you need a snapshot of data at a specific point in time.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param query - The query expression object to match against\n\t * @returns An array of records that match the query at the current moment\n\t *\n\t * @example\n\t * ```ts\n\t * // Get current in-stock books (non-reactive)\n\t * const currentInStockBooks = store.query.exec('book', { inStock: { eq: true } })\n\t * console.log(currentInStockBooks) // Book[]\n\t *\n\t * // Unlike records(), this won't update when the data changes\n\t * const staticBookList = store.query.exec('book', { authorId: { eq: 'author:leguin' } })\n\t * ```\n\t *\n\t * @public\n\t */\n\texec<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tquery: QueryExpression<Extract<R, { typeName: TypeName }>>\n\t): Array<Extract<R, { typeName: TypeName }>> {\n\t\tconst ids = executeQuery(this, typeName, query)\n\t\tif (ids.size === 0) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\t\treturn Array.from(ids, (id) => this.recordMap.get(id) as Extract<R, { typeName: TypeName }>)\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAQO;AACP,mBAAgE;AAGhE,0BAAkE;AAClE,uCAA0C;AAE1C,sBAAyB;
|
|
4
|
+
"sourcesContent": ["import {\n\tAtom,\n\tcomputed,\n\tComputed,\n\tEMPTY_ARRAY,\n\tisUninitialized,\n\tRESET_VALUE,\n\twithDiff,\n} from '@tldraw/state'\nimport { areArraysShallowEqual, isEqual, objectMapValues } from '@tldraw/utils'\nimport { AtomMap } from './AtomMap'\nimport { IdOf, UnknownRecord } from './BaseRecord'\nimport { executeQuery, objectMatchesQuery, QueryExpression } from './executeQuery'\nimport { IncrementalSetConstructor } from './IncrementalSetConstructor'\nimport { RecordsDiff } from './RecordsDiff'\nimport { diffSets } from './setUtils'\nimport { CollectionDiff } from './Store'\n\n/**\n * A type representing the diff of changes to a reactive store index.\n * Maps property values to the collection differences for record IDs that have that property value.\n *\n * @example\n * ```ts\n * // For an index on book titles, the diff might look like:\n * const titleIndexDiff: RSIndexDiff<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', { added: new Set(['book:1']), removed: new Set() }],\n * ['Animal Farm', { added: new Set(), removed: new Set(['book:2']) }]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexDiff<R extends UnknownRecord> = Map<any, CollectionDiff<IdOf<R>>>\n\n/**\n * A type representing a reactive store index as a map from property values to sets of record IDs.\n * This is used to efficiently look up records by a specific property value.\n *\n * @example\n * ```ts\n * // Index mapping book titles to the IDs of books with that title\n * const titleIndex: RSIndexMap<Book, 'title'> = new Map([\n * ['The Lathe of Heaven', new Set(['book:1'])],\n * ['Animal Farm', new Set(['book:2', 'book:3'])]\n * ])\n * ```\n *\n * @public\n */\nexport type RSIndexMap<R extends UnknownRecord> = Map<any, Set<IdOf<R>>>\n\n/**\n * A reactive computed index that provides efficient lookups of records by property values.\n * Returns a computed value containing an RSIndexMap with diffs for change tracking.\n *\n * @example\n * ```ts\n * // Create an index on book authors\n * const authorIndex: RSIndex<Book, 'authorId'> = store.query.index('book', 'authorId')\n *\n * // Get all books by a specific author\n * const leguinBooks = authorIndex.get().get('author:leguin')\n * ```\n *\n * @public\n */\nexport type RSIndex<R extends UnknownRecord> = Computed<RSIndexMap<R>, RSIndexDiff<R>>\n\n/**\n * A class that provides reactive querying capabilities for a record store.\n * Offers methods to create indexes, filter records, and perform efficient lookups with automatic cache management.\n * All queries are reactive and will automatically update when the underlying store data changes.\n *\n * @example\n * ```ts\n * // Create a store with books\n * const store = new Store({ schema: StoreSchema.create({ book: Book, author: Author }) })\n *\n * // Get reactive queries for books\n * const booksByAuthor = store.query.index('book', 'authorId')\n * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n * ```\n *\n * @public\n */\nexport class StoreQueries<R extends UnknownRecord> {\n\t/**\n\t * Creates a new StoreQueries instance.\n\t *\n\t * recordMap - The atom map containing all records in the store\n\t * history - The atom tracking the store's change history with diffs\n\t *\n\t * @internal\n\t */\n\tconstructor(\n\t\tprivate readonly recordMap: AtomMap<IdOf<R>, R>,\n\t\tprivate readonly history: Atom<number, RecordsDiff<R>>\n\t) {}\n\n\t/**\n\t * A cache of derivations (indexes).\n\t *\n\t * @internal\n\t */\n\tprivate indexCache = new Map<string, RSIndex<R>>()\n\n\t/**\n\t * A cache of derivations (filtered histories).\n\t *\n\t * @internal\n\t */\n\tprivate historyCache = new Map<string, Computed<number, RecordsDiff<R>>>()\n\n\t/**\n\t * @internal\n\t */\n\tpublic getAllIdsForType<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Set<IdOf<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = new Set<IdOf<S>>()\n\t\tfor (const record of this.recordMap.values()) {\n\t\t\tif (record.typeName === typeName) {\n\t\t\t\tids.add(record.id as IdOf<S>)\n\t\t\t}\n\t\t}\n\t\treturn ids\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic getRecordById<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tid: IdOf<Extract<R, { typeName: TypeName }>>\n\t): Extract<R, { typeName: TypeName }> | undefined {\n\t\tconst record = this.recordMap.get(id as IdOf<R>)\n\t\tif (record && record.typeName === typeName) {\n\t\t\treturn record as Extract<R, { typeName: TypeName }>\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Helper to extract nested property value using pre-split path parts.\n\t * @internal\n\t */\n\tprivate getNestedValue(obj: any, pathParts: string[]): any {\n\t\tlet current = obj\n\t\tfor (const part of pathParts) {\n\t\t\tif (current == null || typeof current !== 'object') return undefined\n\t\t\tcurrent = current[part]\n\t\t}\n\t\treturn current\n\t}\n\n\t/**\n\t * Creates a reactive computed that tracks the change history for records of a specific type.\n\t * The returned computed provides incremental diffs showing what records of the given type\n\t * have been added, updated, or removed.\n\t *\n\t * @param typeName - The type name to filter the history by\n\t * @returns A computed value containing the current epoch and diffs of changes for the specified type\n\t *\n\t * @example\n\t * ```ts\n\t * // Track changes to book records only\n\t * const bookHistory = store.query.filterHistory('book')\n\t *\n\t * // React to book changes\n\t * react('book-changes', () => {\n\t * const currentEpoch = bookHistory.get()\n\t * console.log('Books updated at epoch:', currentEpoch)\n\t * })\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic filterHistory<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName\n\t): Computed<number, RecordsDiff<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tif (this.historyCache.has(typeName)) {\n\t\t\treturn this.historyCache.get(typeName) as any\n\t\t}\n\n\t\tconst filtered = computed<number, RecordsDiff<S>>(\n\t\t\t'filterHistory:' + typeName,\n\t\t\t(lastValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(lastValue)) {\n\t\t\t\t\treturn this.history.get()\n\t\t\t\t}\n\n\t\t\t\tconst diff = this.history.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (diff === RESET_VALUE) return this.history.get()\n\n\t\t\t\tconst res = { added: {}, removed: {}, updated: {} } as RecordsDiff<S>\n\t\t\t\tlet numAdded = 0\n\t\t\t\tlet numRemoved = 0\n\t\t\t\tlet numUpdated = 0\n\n\t\t\t\tfor (const changes of diff) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.removed[added.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tconst original = res.removed[added.id as IdOf<S>]\n\t\t\t\t\t\t\t\tdelete res.removed[added.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumRemoved--\n\t\t\t\t\t\t\t\tif (original !== added) {\n\t\t\t\t\t\t\t\t\tres.updated[added.id as IdOf<S>] = [original, added as S]\n\t\t\t\t\t\t\t\t\tnumUpdated++\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.added[added.id as IdOf<S>] = added as S\n\t\t\t\t\t\t\t\tnumAdded++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.added[to.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tres.added[to.id as IdOf<S>] = to as S\n\t\t\t\t\t\t\t} else if (res.updated[to.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\tres.updated[to.id as IdOf<S>] = [res.updated[to.id as IdOf<S>][0], to as S]\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.updated[to.id as IdOf<S>] = [from as S, to as S]\n\t\t\t\t\t\t\t\tnumUpdated++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tif (res.added[removed.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\t// was added during this diff sequence, so just undo the add\n\t\t\t\t\t\t\t\tdelete res.added[removed.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumAdded--\n\t\t\t\t\t\t\t} else if (res.updated[removed.id as IdOf<S>]) {\n\t\t\t\t\t\t\t\t// remove oldest version\n\t\t\t\t\t\t\t\tres.removed[removed.id as IdOf<S>] = res.updated[removed.id as IdOf<S>][0]\n\t\t\t\t\t\t\t\tdelete res.updated[removed.id as IdOf<S>]\n\t\t\t\t\t\t\t\tnumUpdated--\n\t\t\t\t\t\t\t\tnumRemoved++\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tres.removed[removed.id as IdOf<S>] = removed as S\n\t\t\t\t\t\t\t\tnumRemoved++\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (numAdded || numRemoved || numUpdated) {\n\t\t\t\t\treturn withDiff(this.history.get(), res)\n\t\t\t\t} else {\n\t\t\t\t\treturn lastValue\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\n\t\tthis.historyCache.set(typeName, filtered)\n\n\t\treturn filtered\n\t}\n\n\t/**\n\t * Creates a reactive index that maps property values to sets of record IDs for efficient lookups.\n\t * The index automatically updates when records are added, updated, or removed, and results are cached\n\t * for performance.\n\t *\n\t * Supports nested property paths using backslash separator (e.g., 'metadata\\\\sessionId').\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param path - The property name or backslash-delimited path to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Create an index of books by author ID\n\t * const booksByAuthor = store.query.index('book', 'authorId')\n\t *\n\t * // Get all books by a specific author\n\t * const authorBooks = booksByAuthor.get().get('author:leguin')\n\t * console.log(authorBooks) // Set<RecordId<Book>>\n\t *\n\t * // Index by nested property using backslash separator\n\t * const booksBySession = store.query.index('book', 'metadata\\\\sessionId')\n\t * const sessionBooks = booksBySession.get().get('session:alpha')\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic index<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tpath: string\n\t): RSIndex<Extract<R, { typeName: TypeName }>> {\n\t\tconst cacheKey = typeName + ':' + path\n\n\t\tif (this.indexCache.has(cacheKey)) {\n\t\t\treturn this.indexCache.get(cacheKey) as any\n\t\t}\n\n\t\tconst index = this.__uncached_createIndex(typeName, path)\n\n\t\tthis.indexCache.set(cacheKey, index as any)\n\n\t\treturn index\n\t}\n\n\t/**\n\t * Creates a new index without checking the cache. This method performs the actual work\n\t * of building the reactive index computation that tracks property values to record ID sets.\n\t *\n\t * Supports nested property paths using backslash separator.\n\t *\n\t * @param typeName - The type name of records to index\n\t * @param path - The property name or backslash-delimited path to index by\n\t * @returns A reactive computed containing the index map with change diffs\n\t *\n\t * @internal\n\t */\n\t__uncached_createIndex<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tpath: string\n\t): RSIndex<Extract<R, { typeName: TypeName }>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\t// Create closure for efficient property value extraction\n\t\tconst pathParts = path.split('\\\\')\n\t\tconst getPropertyValue =\n\t\t\tpathParts.length > 1\n\t\t\t\t? (obj: S) => this.getNestedValue(obj, pathParts)\n\t\t\t\t: (obj: S) => obj[path as keyof S]\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref typeHistory early so that the first time the incremental version runs\n\t\t\t// it gets a diff to work with instead of having to bail to this from-scratch version\n\t\t\ttypeHistory.get()\n\t\t\tconst res = new Map<any, Set<IdOf<S>>>()\n\t\t\tfor (const record of this.recordMap.values()) {\n\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\tif (!res.has(value)) {\n\t\t\t\t\t\t\tres.set(value, new Set())\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres.get(value)!.add(record.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn res\n\t\t}\n\n\t\treturn computed<RSIndexMap<S>, RSIndexDiff<S>>(\n\t\t\t'index:' + typeName + ':' + path,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tif (isUninitialized(prevValue)) return fromScratch()\n\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\tconst setConstructors = new Map<any, IncrementalSetConstructor<IdOf<S>>>()\n\n\t\t\t\tconst add = (value: any, id: IdOf<S>) => {\n\t\t\t\t\tlet setConstructor = setConstructors.get(value)\n\t\t\t\t\tif (!setConstructor)\n\t\t\t\t\t\tsetConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\t\t\tprevValue.get(value) ?? new Set()\n\t\t\t\t\t\t)\n\t\t\t\t\tsetConstructor.add(id)\n\t\t\t\t\tsetConstructors.set(value, setConstructor)\n\t\t\t\t}\n\n\t\t\t\tconst remove = (value: any, id: IdOf<S>) => {\n\t\t\t\t\tlet set = setConstructors.get(value)\n\t\t\t\t\tif (!set) set = new IncrementalSetConstructor<IdOf<S>>(prevValue.get(value) ?? new Set())\n\t\t\t\t\tset.remove(id)\n\t\t\t\t\tsetConstructors.set(value, set)\n\t\t\t\t}\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const record of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tadd(value, record.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [from, to] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (to.typeName === typeName) {\n\t\t\t\t\t\t\tconst prev = getPropertyValue(from as S)\n\t\t\t\t\t\t\tconst next = getPropertyValue(to as S)\n\t\t\t\t\t\t\tif (prev !== next) {\n\t\t\t\t\t\t\t\tif (prev !== undefined) {\n\t\t\t\t\t\t\t\t\tremove(prev, to.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (next !== undefined) {\n\t\t\t\t\t\t\t\t\tadd(next, to.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const record of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (record.typeName === typeName) {\n\t\t\t\t\t\t\tconst value = getPropertyValue(record as S)\n\t\t\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\t\t\tremove(value, record.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet nextValue: undefined | RSIndexMap<S> = undefined\n\t\t\t\tlet nextDiff: undefined | RSIndexDiff<S> = undefined\n\n\t\t\t\tfor (const [value, setConstructor] of setConstructors) {\n\t\t\t\t\tconst result = setConstructor.get()\n\t\t\t\t\tif (!result) continue\n\t\t\t\t\tif (!nextValue) nextValue = new Map(prevValue)\n\t\t\t\t\tif (!nextDiff) nextDiff = new Map()\n\t\t\t\t\tif (result.value.size === 0) {\n\t\t\t\t\t\tnextValue.delete(value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextValue.set(value, result.value)\n\t\t\t\t\t}\n\t\t\t\t\tnextDiff.set(value, result.diff)\n\t\t\t\t}\n\n\t\t\t\tif (nextValue && nextDiff) {\n\t\t\t\t\treturn withDiff(nextValue, nextDiff)\n\t\t\t\t}\n\n\t\t\t\treturn prevValue\n\t\t\t},\n\t\t\t{ historyLength: 100 }\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns the first record matching the given query criteria.\n\t * Returns undefined if no matching record is found. The query automatically updates\n\t * when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing the first matching record or undefined\n\t *\n\t * @example\n\t * ```ts\n\t * // Find the first book with a specific title\n\t * const bookLatheOfHeaven = store.query.record('book', () => ({ title: { eq: 'The Lathe of Heaven' } }))\n\t * console.log(bookLatheOfHeaven.get()?.title) // 'The Lathe of Heaven' or undefined\n\t *\n\t * // Find any book in stock\n\t * const anyInStockBook = store.query.record('book', () => ({ inStock: { eq: true } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\trecord<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'record:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<Extract<R, { typeName: TypeName }> | undefined> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, name)\n\n\t\treturn computed<S | undefined>(name, () => {\n\t\t\tfor (const id of ids.get()) {\n\t\t\t\treturn this.recordMap.get(id) as S | undefined\n\t\t\t}\n\t\t\treturn undefined\n\t\t})\n\t}\n\n\t/**\n\t * Creates a reactive query that returns an array of all records matching the given query criteria.\n\t * The array automatically updates when records are added, updated, or removed.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing an array of all matching records\n\t *\n\t * @example\n\t * ```ts\n\t * // Get all books in stock\n\t * const inStockBooks = store.query.records('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBooks.get()) // Book[]\n\t *\n\t * // Get all books by a specific author\n\t * const leguinBooks = store.query.records('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t *\n\t * // Get all books (no filter)\n\t * const allBooks = store.query.records('book')\n\t * ```\n\t *\n\t * @public\n\t */\n\trecords<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'records:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<Array<Extract<R, { typeName: TypeName }>>> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\t\tconst ids = this.ids(typeName, queryCreator, 'ids:' + name)\n\n\t\treturn computed<S[]>(\n\t\t\tname,\n\t\t\t() => {\n\t\t\t\treturn Array.from(ids.get(), (id) => this.recordMap.get(id) as S)\n\t\t\t},\n\t\t\t{\n\t\t\t\tisEqual: areArraysShallowEqual,\n\t\t\t}\n\t\t)\n\t}\n\n\t/**\n\t * Creates a reactive query that returns a set of record IDs matching the given query criteria.\n\t * This is more efficient than `records()` when you only need the IDs and not the full record objects.\n\t * The set automatically updates with collection diffs when records change.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param queryCreator - Function that returns the query expression object to match against\n\t * @param name - Optional name for the query computation (used for debugging)\n\t * @returns A computed value containing a set of matching record IDs with collection diffs\n\t *\n\t * @example\n\t * ```ts\n\t * // Get IDs of all books in stock\n\t * const inStockBookIds = store.query.ids('book', () => ({ inStock: { eq: true } }))\n\t * console.log(inStockBookIds.get()) // Set<RecordId<Book>>\n\t *\n\t * // Get all book IDs (no filter)\n\t * const allBookIds = store.query.ids('book')\n\t *\n\t * // Use with other queries for efficient lookups\n\t * const authorBookIds = store.query.ids('book', () => ({ authorId: { eq: 'author:leguin' } }))\n\t * ```\n\t *\n\t * @public\n\t */\n\tids<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tqueryCreator: () => QueryExpression<Extract<R, { typeName: TypeName }>> = () => ({}),\n\t\tname = 'ids:' + typeName + (queryCreator ? ':' + queryCreator.toString() : '')\n\t): Computed<\n\t\tSet<IdOf<Extract<R, { typeName: TypeName }>>>,\n\t\tCollectionDiff<IdOf<Extract<R, { typeName: TypeName }>>>\n\t> {\n\t\ttype S = Extract<R, { typeName: TypeName }>\n\n\t\tconst typeHistory = this.filterHistory(typeName)\n\n\t\tconst fromScratch = () => {\n\t\t\t// deref type history early to allow first incremental update to use diffs\n\t\t\ttypeHistory.get()\n\t\t\tconst query: QueryExpression<S> = queryCreator()\n\t\t\tif (Object.keys(query).length === 0) {\n\t\t\t\treturn this.getAllIdsForType(typeName)\n\t\t\t}\n\n\t\t\treturn executeQuery(this, typeName, query)\n\t\t}\n\n\t\tconst fromScratchWithDiff = (prevValue: Set<IdOf<S>>) => {\n\t\t\tconst nextValue = fromScratch()\n\t\t\tconst diff = diffSets(prevValue, nextValue)\n\t\t\tif (diff) {\n\t\t\t\treturn withDiff(nextValue, diff)\n\t\t\t} else {\n\t\t\t\treturn prevValue\n\t\t\t}\n\t\t}\n\t\tconst cachedQuery = computed('ids_query:' + name, queryCreator, {\n\t\t\tisEqual,\n\t\t})\n\n\t\treturn computed(\n\t\t\t'query:' + name,\n\t\t\t(prevValue, lastComputedEpoch) => {\n\t\t\t\tconst query = cachedQuery.get()\n\t\t\t\tif (isUninitialized(prevValue)) {\n\t\t\t\t\treturn fromScratch()\n\t\t\t\t}\n\n\t\t\t\t// if the query changed since last time this ran then we need to start again\n\t\t\t\tif (lastComputedEpoch < cachedQuery.lastChangedEpoch) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\t// otherwise iterate over the changes from the store and apply them to the previous value if needed\n\t\t\t\tconst history = typeHistory.getDiffSince(lastComputedEpoch)\n\t\t\t\tif (history === RESET_VALUE) {\n\t\t\t\t\treturn fromScratchWithDiff(prevValue)\n\t\t\t\t}\n\n\t\t\t\tconst setConstructor = new IncrementalSetConstructor<IdOf<S>>(\n\t\t\t\t\tprevValue\n\t\t\t\t) as IncrementalSetConstructor<IdOf<S>>\n\n\t\t\t\tfor (const changes of history) {\n\t\t\t\t\tfor (const added of objectMapValues(changes.added)) {\n\t\t\t\t\t\tif (added.typeName === typeName && objectMatchesQuery(query, added)) {\n\t\t\t\t\t\t\tsetConstructor.add(added.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [_, updated] of objectMapValues(changes.updated)) {\n\t\t\t\t\t\tif (updated.typeName === typeName) {\n\t\t\t\t\t\t\tif (objectMatchesQuery(query, updated)) {\n\t\t\t\t\t\t\t\tsetConstructor.add(updated.id)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsetConstructor.remove(updated.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const removed of objectMapValues(changes.removed)) {\n\t\t\t\t\t\tif (removed.typeName === typeName) {\n\t\t\t\t\t\t\tsetConstructor.remove(removed.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst result = setConstructor.get()\n\t\t\t\tif (!result) {\n\t\t\t\t\treturn prevValue\n\t\t\t\t}\n\n\t\t\t\treturn withDiff(result.value, result.diff)\n\t\t\t},\n\t\t\t{ historyLength: 50 }\n\t\t)\n\t}\n\n\t/**\n\t * Executes a one-time query against the current store state and returns matching records.\n\t * This is a non-reactive query that returns results immediately without creating a computed value.\n\t * Use this when you need a snapshot of data at a specific point in time.\n\t *\n\t * @param typeName - The type name of records to query\n\t * @param query - The query expression object to match against\n\t * @returns An array of records that match the query at the current moment\n\t *\n\t * @example\n\t * ```ts\n\t * // Get current in-stock books (non-reactive)\n\t * const currentInStockBooks = store.query.exec('book', { inStock: { eq: true } })\n\t * console.log(currentInStockBooks) // Book[]\n\t *\n\t * // Unlike records(), this won't update when the data changes\n\t * const staticBookList = store.query.exec('book', { authorId: { eq: 'author:leguin' } })\n\t * ```\n\t *\n\t * @public\n\t */\n\texec<TypeName extends R['typeName']>(\n\t\ttypeName: TypeName,\n\t\tquery: QueryExpression<Extract<R, { typeName: TypeName }>>\n\t): Array<Extract<R, { typeName: TypeName }>> {\n\t\tconst ids = executeQuery(this, typeName, query)\n\t\tif (ids.size === 0) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\t\treturn Array.from(ids, (id) => this.recordMap.get(id) as Extract<R, { typeName: TypeName }>)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -23,46 +23,70 @@ __export(executeQuery_exports, {
|
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(executeQuery_exports);
|
|
25
25
|
var import_setUtils = require("./setUtils");
|
|
26
|
+
function isQueryValueMatcher(value) {
|
|
27
|
+
if (typeof value !== "object" || value === null) return false;
|
|
28
|
+
return "eq" in value || "neq" in value || "gt" in value;
|
|
29
|
+
}
|
|
30
|
+
function extractMatcherPaths(query, prefix = "") {
|
|
31
|
+
const paths = [];
|
|
32
|
+
for (const [key, value] of Object.entries(query)) {
|
|
33
|
+
const currentPath = prefix ? `${prefix}\\${key}` : key;
|
|
34
|
+
if (isQueryValueMatcher(value)) {
|
|
35
|
+
paths.push({ path: currentPath, matcher: value });
|
|
36
|
+
} else if (typeof value === "object" && value !== null) {
|
|
37
|
+
paths.push(...extractMatcherPaths(value, currentPath));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return paths;
|
|
41
|
+
}
|
|
26
42
|
function objectMatchesQuery(query, object) {
|
|
27
|
-
for (const [key,
|
|
28
|
-
const matcher = _matcher;
|
|
43
|
+
for (const [key, matcher] of Object.entries(query)) {
|
|
29
44
|
const value = object[key];
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
if (isQueryValueMatcher(matcher)) {
|
|
46
|
+
if ("eq" in matcher && value !== matcher.eq) return false;
|
|
47
|
+
if ("neq" in matcher && value === matcher.neq) return false;
|
|
48
|
+
if ("gt" in matcher && (typeof value !== "number" || value <= matcher.gt)) return false;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value !== "object" || value === null) return false;
|
|
52
|
+
if (!objectMatchesQuery(matcher, value)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
33
55
|
}
|
|
34
56
|
return true;
|
|
35
57
|
}
|
|
36
58
|
function executeQuery(store, typeName, query) {
|
|
37
|
-
const
|
|
38
|
-
|
|
59
|
+
const matcherPaths = extractMatcherPaths(query);
|
|
60
|
+
const matchIds = Object.fromEntries(matcherPaths.map(({ path }) => [path, /* @__PURE__ */ new Set()]));
|
|
61
|
+
for (const { path, matcher } of matcherPaths) {
|
|
62
|
+
const index = store.index(typeName, path);
|
|
39
63
|
if ("eq" in matcher) {
|
|
40
|
-
const index = store.index(typeName, k);
|
|
41
64
|
const ids = index.get().get(matcher.eq);
|
|
42
65
|
if (ids) {
|
|
43
66
|
for (const id of ids) {
|
|
44
|
-
matchIds[
|
|
67
|
+
matchIds[path].add(id);
|
|
45
68
|
}
|
|
46
69
|
}
|
|
47
70
|
} else if ("neq" in matcher) {
|
|
48
|
-
const index = store.index(typeName, k);
|
|
49
71
|
for (const [value, ids] of index.get()) {
|
|
50
72
|
if (value !== matcher.neq) {
|
|
51
73
|
for (const id of ids) {
|
|
52
|
-
matchIds[
|
|
74
|
+
matchIds[path].add(id);
|
|
53
75
|
}
|
|
54
76
|
}
|
|
55
77
|
}
|
|
56
78
|
} else if ("gt" in matcher) {
|
|
57
|
-
const index = store.index(typeName, k);
|
|
58
79
|
for (const [value, ids] of index.get()) {
|
|
59
|
-
if (value > matcher.gt) {
|
|
80
|
+
if (typeof value === "number" && value > matcher.gt) {
|
|
60
81
|
for (const id of ids) {
|
|
61
|
-
matchIds[
|
|
82
|
+
matchIds[path].add(id);
|
|
62
83
|
}
|
|
63
84
|
}
|
|
64
85
|
}
|
|
65
86
|
}
|
|
87
|
+
if (matchIds[path].size === 0) {
|
|
88
|
+
return /* @__PURE__ */ new Set();
|
|
89
|
+
}
|
|
66
90
|
}
|
|
67
91
|
return (0, import_setUtils.intersectSets)(Object.values(matchIds));
|
|
68
92
|
}
|