@tldraw/store 4.1.0-canary.a5989c7a02c8 → 4.1.0-canary.a6e63b3bbde6
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 +1884 -153
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/AtomMap.js +241 -1
- package/dist-cjs/lib/AtomMap.js.map +2 -2
- package/dist-cjs/lib/BaseRecord.js.map +2 -2
- package/dist-cjs/lib/ImmutableMap.js +141 -0
- package/dist-cjs/lib/ImmutableMap.js.map +2 -2
- package/dist-cjs/lib/IncrementalSetConstructor.js +45 -5
- package/dist-cjs/lib/IncrementalSetConstructor.js.map +2 -2
- package/dist-cjs/lib/RecordType.js +116 -21
- package/dist-cjs/lib/RecordType.js.map +2 -2
- package/dist-cjs/lib/RecordsDiff.js.map +2 -2
- package/dist-cjs/lib/Store.js +233 -39
- package/dist-cjs/lib/Store.js.map +2 -2
- package/dist-cjs/lib/StoreQueries.js +135 -22
- package/dist-cjs/lib/StoreQueries.js.map +2 -2
- package/dist-cjs/lib/StoreSchema.js +207 -2
- package/dist-cjs/lib/StoreSchema.js.map +2 -2
- package/dist-cjs/lib/StoreSideEffects.js +102 -10
- package/dist-cjs/lib/StoreSideEffects.js.map +2 -2
- package/dist-cjs/lib/executeQuery.js.map +2 -2
- package/dist-cjs/lib/migrate.js.map +2 -2
- package/dist-cjs/lib/setUtils.js.map +2 -2
- package/dist-esm/index.d.mts +1884 -153
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/AtomMap.mjs +241 -1
- package/dist-esm/lib/AtomMap.mjs.map +2 -2
- package/dist-esm/lib/BaseRecord.mjs.map +2 -2
- package/dist-esm/lib/ImmutableMap.mjs +141 -0
- package/dist-esm/lib/ImmutableMap.mjs.map +2 -2
- package/dist-esm/lib/IncrementalSetConstructor.mjs +45 -5
- package/dist-esm/lib/IncrementalSetConstructor.mjs.map +2 -2
- package/dist-esm/lib/RecordType.mjs +116 -21
- package/dist-esm/lib/RecordType.mjs.map +2 -2
- package/dist-esm/lib/RecordsDiff.mjs.map +2 -2
- package/dist-esm/lib/Store.mjs +233 -39
- package/dist-esm/lib/Store.mjs.map +2 -2
- package/dist-esm/lib/StoreQueries.mjs +135 -22
- package/dist-esm/lib/StoreQueries.mjs.map +2 -2
- package/dist-esm/lib/StoreSchema.mjs +207 -2
- package/dist-esm/lib/StoreSchema.mjs.map +2 -2
- package/dist-esm/lib/StoreSideEffects.mjs +102 -10
- package/dist-esm/lib/StoreSideEffects.mjs.map +2 -2
- package/dist-esm/lib/executeQuery.mjs.map +2 -2
- package/dist-esm/lib/migrate.mjs.map +2 -2
- package/dist-esm/lib/setUtils.mjs.map +2 -2
- package/package.json +3 -3
- package/src/lib/AtomMap.ts +241 -1
- package/src/lib/BaseRecord.test.ts +44 -0
- package/src/lib/BaseRecord.ts +118 -4
- package/src/lib/ImmutableMap.test.ts +103 -0
- package/src/lib/ImmutableMap.ts +212 -0
- package/src/lib/IncrementalSetConstructor.test.ts +111 -0
- package/src/lib/IncrementalSetConstructor.ts +63 -6
- package/src/lib/RecordType.ts +149 -25
- package/src/lib/RecordsDiff.test.ts +144 -0
- package/src/lib/RecordsDiff.ts +145 -10
- package/src/lib/Store.test.ts +827 -0
- package/src/lib/Store.ts +533 -67
- package/src/lib/StoreQueries.test.ts +627 -0
- package/src/lib/StoreQueries.ts +194 -27
- package/src/lib/StoreSchema.test.ts +226 -0
- package/src/lib/StoreSchema.ts +386 -8
- package/src/lib/StoreSideEffects.test.ts +239 -19
- package/src/lib/StoreSideEffects.ts +266 -19
- package/src/lib/devFreeze.test.ts +137 -0
- package/src/lib/executeQuery.test.ts +481 -0
- package/src/lib/executeQuery.ts +80 -2
- package/src/lib/migrate.test.ts +400 -0
- package/src/lib/migrate.ts +187 -14
- package/src/lib/setUtils.test.ts +105 -0
- package/src/lib/setUtils.ts +44 -4
package/dist-esm/index.mjs
CHANGED
package/dist-esm/lib/AtomMap.mjs
CHANGED
|
@@ -2,6 +2,21 @@ import { atom, transact, UNINITIALIZED } from "@tldraw/state";
|
|
|
2
2
|
import { assert } from "@tldraw/utils";
|
|
3
3
|
import { emptyMap } from "./ImmutableMap.mjs";
|
|
4
4
|
class AtomMap {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new AtomMap instance.
|
|
7
|
+
*
|
|
8
|
+
* name - A unique name for this map, used for atom identification
|
|
9
|
+
* entries - Optional initial entries to populate the map with
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* // Create an empty map
|
|
13
|
+
* const map = new AtomMap('userMap')
|
|
14
|
+
*
|
|
15
|
+
* // Create a map with initial data
|
|
16
|
+
* const initialData: [string, number][] = [['a', 1], ['b', 2]]
|
|
17
|
+
* const mapWithData = new AtomMap('numbersMap', initialData)
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
5
20
|
constructor(name, entries) {
|
|
6
21
|
this.name = name;
|
|
7
22
|
let atoms = emptyMap();
|
|
@@ -15,7 +30,13 @@ class AtomMap {
|
|
|
15
30
|
this.atoms = atom(`${name}:atoms`, atoms);
|
|
16
31
|
}
|
|
17
32
|
atoms;
|
|
18
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves the underlying atom for a given key.
|
|
35
|
+
*
|
|
36
|
+
* @param key - The key to retrieve the atom for
|
|
37
|
+
* @returns The atom containing the value, or undefined if the key doesn't exist
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
19
40
|
getAtom(key) {
|
|
20
41
|
const valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key);
|
|
21
42
|
if (!valueAtom) {
|
|
@@ -24,11 +45,38 @@ class AtomMap {
|
|
|
24
45
|
}
|
|
25
46
|
return valueAtom;
|
|
26
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Gets the value associated with a key. Returns undefined if the key doesn't exist.
|
|
50
|
+
* This method is reactive and will cause reactive contexts to update when the value changes.
|
|
51
|
+
*
|
|
52
|
+
* @param key - The key to retrieve the value for
|
|
53
|
+
* @returns The value associated with the key, or undefined if not found
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const map = new AtomMap('myMap')
|
|
57
|
+
* map.set('name', 'Alice')
|
|
58
|
+
* console.log(map.get('name')) // 'Alice'
|
|
59
|
+
* console.log(map.get('missing')) // undefined
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
27
62
|
get(key) {
|
|
28
63
|
const value = this.getAtom(key)?.get();
|
|
29
64
|
assert(value !== UNINITIALIZED);
|
|
30
65
|
return value;
|
|
31
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Gets the value associated with a key without creating reactive dependencies.
|
|
69
|
+
* This method will not cause reactive contexts to update when the value changes.
|
|
70
|
+
*
|
|
71
|
+
* @param key - The key to retrieve the value for
|
|
72
|
+
* @returns The value associated with the key, or undefined if not found
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const map = new AtomMap('myMap')
|
|
76
|
+
* map.set('count', 42)
|
|
77
|
+
* const value = map.__unsafe__getWithoutCapture('count') // No reactive subscription
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
32
80
|
__unsafe__getWithoutCapture(key) {
|
|
33
81
|
const valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key);
|
|
34
82
|
if (!valueAtom) return void 0;
|
|
@@ -36,6 +84,20 @@ class AtomMap {
|
|
|
36
84
|
assert(value !== UNINITIALIZED);
|
|
37
85
|
return value;
|
|
38
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Checks whether a key exists in the map.
|
|
89
|
+
* This method is reactive and will cause reactive contexts to update when keys are added or removed.
|
|
90
|
+
*
|
|
91
|
+
* @param key - The key to check for
|
|
92
|
+
* @returns True if the key exists in the map, false otherwise
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* const map = new AtomMap('myMap')
|
|
96
|
+
* console.log(map.has('name')) // false
|
|
97
|
+
* map.set('name', 'Alice')
|
|
98
|
+
* console.log(map.has('name')) // true
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
39
101
|
has(key) {
|
|
40
102
|
const valueAtom = this.getAtom(key);
|
|
41
103
|
if (!valueAtom) {
|
|
@@ -43,12 +105,38 @@ class AtomMap {
|
|
|
43
105
|
}
|
|
44
106
|
return valueAtom.get() !== UNINITIALIZED;
|
|
45
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Checks whether a key exists in the map without creating reactive dependencies.
|
|
110
|
+
* This method will not cause reactive contexts to update when keys are added or removed.
|
|
111
|
+
*
|
|
112
|
+
* @param key - The key to check for
|
|
113
|
+
* @returns True if the key exists in the map, false otherwise
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* const map = new AtomMap('myMap')
|
|
117
|
+
* map.set('active', true)
|
|
118
|
+
* const exists = map.__unsafe__hasWithoutCapture('active') // No reactive subscription
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
46
121
|
__unsafe__hasWithoutCapture(key) {
|
|
47
122
|
const valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key);
|
|
48
123
|
if (!valueAtom) return false;
|
|
49
124
|
assert(valueAtom.__unsafe__getWithoutCapture() !== UNINITIALIZED);
|
|
50
125
|
return true;
|
|
51
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Sets a value for the given key. If the key already exists, its value is updated.
|
|
129
|
+
* If the key doesn't exist, a new entry is created.
|
|
130
|
+
*
|
|
131
|
+
* @param key - The key to set the value for
|
|
132
|
+
* @param value - The value to associate with the key
|
|
133
|
+
* @returns This AtomMap instance for method chaining
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const map = new AtomMap('myMap')
|
|
137
|
+
* map.set('name', 'Alice').set('age', 30)
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
52
140
|
set(key, value) {
|
|
53
141
|
const existingAtom = this.atoms.__unsafe__getWithoutCapture().get(key);
|
|
54
142
|
if (existingAtom) {
|
|
@@ -60,6 +148,19 @@ class AtomMap {
|
|
|
60
148
|
}
|
|
61
149
|
return this;
|
|
62
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Updates an existing value using an updater function.
|
|
153
|
+
*
|
|
154
|
+
* @param key - The key of the value to update
|
|
155
|
+
* @param updater - A function that receives the current value and returns the new value
|
|
156
|
+
* @throws Error if the key doesn't exist in the map
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* const map = new AtomMap('myMap')
|
|
160
|
+
* map.set('count', 5)
|
|
161
|
+
* map.update('count', count => count + 1) // count is now 6
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
63
164
|
update(key, updater) {
|
|
64
165
|
const valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key);
|
|
65
166
|
if (!valueAtom) {
|
|
@@ -69,6 +170,19 @@ class AtomMap {
|
|
|
69
170
|
assert(value !== UNINITIALIZED);
|
|
70
171
|
valueAtom.set(updater(value));
|
|
71
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Removes a key-value pair from the map.
|
|
175
|
+
*
|
|
176
|
+
* @param key - The key to remove
|
|
177
|
+
* @returns True if the key existed and was removed, false if it didn't exist
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* const map = new AtomMap('myMap')
|
|
181
|
+
* map.set('temp', 'value')
|
|
182
|
+
* console.log(map.delete('temp')) // true
|
|
183
|
+
* console.log(map.delete('missing')) // false
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
72
186
|
delete(key) {
|
|
73
187
|
const valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key);
|
|
74
188
|
if (!valueAtom) {
|
|
@@ -82,6 +196,19 @@ class AtomMap {
|
|
|
82
196
|
});
|
|
83
197
|
return true;
|
|
84
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Removes multiple key-value pairs from the map in a single transaction.
|
|
201
|
+
*
|
|
202
|
+
* @param keys - An iterable of keys to remove
|
|
203
|
+
* @returns An array of [key, value] pairs that were actually deleted
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* const map = new AtomMap('myMap')
|
|
207
|
+
* map.set('a', 1).set('b', 2).set('c', 3)
|
|
208
|
+
* const deleted = map.deleteMany(['a', 'c', 'missing'])
|
|
209
|
+
* console.log(deleted) // [['a', 1], ['c', 3]]
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
85
212
|
deleteMany(keys) {
|
|
86
213
|
return transact(() => {
|
|
87
214
|
const deleted = [];
|
|
@@ -102,6 +229,17 @@ class AtomMap {
|
|
|
102
229
|
return deleted;
|
|
103
230
|
});
|
|
104
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Removes all key-value pairs from the map.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* const map = new AtomMap('myMap')
|
|
238
|
+
* map.set('a', 1).set('b', 2)
|
|
239
|
+
* map.clear()
|
|
240
|
+
* console.log(map.size) // 0
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
105
243
|
clear() {
|
|
106
244
|
return transact(() => {
|
|
107
245
|
for (const valueAtom of this.atoms.__unsafe__getWithoutCapture().values()) {
|
|
@@ -110,6 +248,20 @@ class AtomMap {
|
|
|
110
248
|
this.atoms.set(emptyMap());
|
|
111
249
|
});
|
|
112
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Returns an iterator that yields [key, value] pairs for each entry in the map.
|
|
253
|
+
* This method is reactive and will cause reactive contexts to update when entries change.
|
|
254
|
+
*
|
|
255
|
+
* @returns A generator that yields [key, value] tuples
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* const map = new AtomMap('myMap')
|
|
259
|
+
* map.set('a', 1).set('b', 2)
|
|
260
|
+
* for (const [key, value] of map.entries()) {
|
|
261
|
+
* console.log(`${key}: ${value}`)
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
113
265
|
*entries() {
|
|
114
266
|
for (const [key, valueAtom] of this.atoms.get()) {
|
|
115
267
|
const value = valueAtom.get();
|
|
@@ -117,11 +269,39 @@ class AtomMap {
|
|
|
117
269
|
yield [key, value];
|
|
118
270
|
}
|
|
119
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Returns an iterator that yields all keys in the map.
|
|
274
|
+
* This method is reactive and will cause reactive contexts to update when keys change.
|
|
275
|
+
*
|
|
276
|
+
* @returns A generator that yields keys
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* const map = new AtomMap('myMap')
|
|
280
|
+
* map.set('name', 'Alice').set('age', 30)
|
|
281
|
+
* for (const key of map.keys()) {
|
|
282
|
+
* console.log(key) // 'name', 'age'
|
|
283
|
+
* }
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
120
286
|
*keys() {
|
|
121
287
|
for (const key of this.atoms.get().keys()) {
|
|
122
288
|
yield key;
|
|
123
289
|
}
|
|
124
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Returns an iterator that yields all values in the map.
|
|
293
|
+
* This method is reactive and will cause reactive contexts to update when values change.
|
|
294
|
+
*
|
|
295
|
+
* @returns A generator that yields values
|
|
296
|
+
* @example
|
|
297
|
+
* ```ts
|
|
298
|
+
* const map = new AtomMap('myMap')
|
|
299
|
+
* map.set('name', 'Alice').set('age', 30)
|
|
300
|
+
* for (const value of map.values()) {
|
|
301
|
+
* console.log(value) // 'Alice', 30
|
|
302
|
+
* }
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
125
305
|
*values() {
|
|
126
306
|
for (const valueAtom of this.atoms.get().values()) {
|
|
127
307
|
const value = valueAtom.get();
|
|
@@ -129,18 +309,78 @@ class AtomMap {
|
|
|
129
309
|
yield value;
|
|
130
310
|
}
|
|
131
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* The number of key-value pairs in the map.
|
|
314
|
+
* This property is reactive and will cause reactive contexts to update when the size changes.
|
|
315
|
+
*
|
|
316
|
+
* @returns The number of entries in the map
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* const map = new AtomMap('myMap')
|
|
320
|
+
* console.log(map.size) // 0
|
|
321
|
+
* map.set('a', 1)
|
|
322
|
+
* console.log(map.size) // 1
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
132
325
|
// eslint-disable-next-line no-restricted-syntax
|
|
133
326
|
get size() {
|
|
134
327
|
return this.atoms.get().size;
|
|
135
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Executes a provided function once for each key-value pair in the map.
|
|
331
|
+
* This method is reactive and will cause reactive contexts to update when entries change.
|
|
332
|
+
*
|
|
333
|
+
* @param callbackfn - Function to execute for each entry
|
|
334
|
+
* - value - The value of the current entry
|
|
335
|
+
* - key - The key of the current entry
|
|
336
|
+
* - map - The AtomMap being traversed
|
|
337
|
+
* @param thisArg - Value to use as `this` when executing the callback
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* const map = new AtomMap('myMap')
|
|
341
|
+
* map.set('a', 1).set('b', 2)
|
|
342
|
+
* map.forEach((value, key) => {
|
|
343
|
+
* console.log(`${key} = ${value}`)
|
|
344
|
+
* })
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
136
347
|
forEach(callbackfn, thisArg) {
|
|
137
348
|
for (const [key, value] of this.entries()) {
|
|
138
349
|
callbackfn.call(thisArg, value, key, this);
|
|
139
350
|
}
|
|
140
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Returns the default iterator for the map, which is the same as entries().
|
|
354
|
+
* This allows the map to be used in for...of loops and other iterable contexts.
|
|
355
|
+
*
|
|
356
|
+
* @returns The same iterator as entries()
|
|
357
|
+
* @example
|
|
358
|
+
* ```ts
|
|
359
|
+
* const map = new AtomMap('myMap')
|
|
360
|
+
* map.set('a', 1).set('b', 2)
|
|
361
|
+
*
|
|
362
|
+
* // These are equivalent:
|
|
363
|
+
* for (const [key, value] of map) {
|
|
364
|
+
* console.log(`${key}: ${value}`)
|
|
365
|
+
* }
|
|
366
|
+
*
|
|
367
|
+
* for (const [key, value] of map.entries()) {
|
|
368
|
+
* console.log(`${key}: ${value}`)
|
|
369
|
+
* }
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
141
372
|
[Symbol.iterator]() {
|
|
142
373
|
return this.entries();
|
|
143
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* The string tag used by Object.prototype.toString for this class.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* const map = new AtomMap('myMap')
|
|
381
|
+
* console.log(Object.prototype.toString.call(map)) // '[object AtomMap]'
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
144
384
|
[Symbol.toStringTag] = "AtomMap";
|
|
145
385
|
}
|
|
146
386
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/AtomMap.ts"],
|
|
4
|
-
"sourcesContent": ["import { atom, Atom, transact, UNINITIALIZED } from '@tldraw/state'\nimport { assert } from '@tldraw/utils'\nimport { emptyMap, ImmutableMap } from './ImmutableMap'\n\n/**\n * A drop-in replacement for Map that stores values in atoms and can be used in reactive contexts.\n * @public\n */\nexport class AtomMap<K, V> implements Map<K, V> {\n\tprivate atoms: Atom<ImmutableMap<K, Atom<V | UNINITIALIZED>>>\n\n\tconstructor(\n\t\tprivate readonly name: string,\n\t\tentries?: Iterable<readonly [K, V]>\n\t) {\n\t\tlet atoms = emptyMap<K, Atom<V>>()\n\t\tif (entries) {\n\t\t\tatoms = atoms.withMutations((atoms) => {\n\t\t\t\tfor (const [k, v] of entries) {\n\t\t\t\t\tatoms.set(k, atom(`${name}:${String(k)}`, v))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tthis.atoms = atom(`${name}:atoms`, atoms)\n\t}\n\n\t/** @internal */\n\tgetAtom(key: K): Atom<V | UNINITIALIZED> | undefined {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) {\n\t\t\t// if the value is missing, we want to track whether it's in the present keys set\n\t\t\tthis.atoms.get()\n\t\t\treturn undefined\n\t\t}\n\t\treturn valueAtom\n\t}\n\n\tget(key: K): V | undefined {\n\t\tconst value = this.getAtom(key)?.get()\n\t\tassert(value !== UNINITIALIZED)\n\t\treturn value\n\t}\n\n\t__unsafe__getWithoutCapture(key: K): V | undefined {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) return undefined\n\t\tconst value = valueAtom.__unsafe__getWithoutCapture()\n\t\tassert(value !== UNINITIALIZED)\n\t\treturn value\n\t}\n\n\thas(key: K): boolean {\n\t\tconst valueAtom = this.getAtom(key)\n\t\tif (!valueAtom) {\n\t\t\treturn false\n\t\t}\n\t\treturn valueAtom.get() !== UNINITIALIZED\n\t}\n\n\t__unsafe__hasWithoutCapture(key: K): boolean {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) return false\n\t\tassert(valueAtom.__unsafe__getWithoutCapture() !== UNINITIALIZED)\n\t\treturn true\n\t}\n\n\tset(key: K, value: V) {\n\t\tconst existingAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (existingAtom) {\n\t\t\texistingAtom.set(value)\n\t\t} else {\n\t\t\tthis.atoms.update((atoms) => {\n\t\t\t\treturn atoms.set(key, atom(`${this.name}:${String(key)}`, value))\n\t\t\t})\n\t\t}\n\t\treturn this\n\t}\n\n\tupdate(key: K, updater: (value: V) => V) {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) {\n\t\t\tthrow new Error(`AtomMap: key ${key} not found`)\n\t\t}\n\t\tconst value = valueAtom.__unsafe__getWithoutCapture()\n\t\tassert(value !== UNINITIALIZED)\n\t\tvalueAtom.set(updater(value))\n\t}\n\n\tdelete(key: K) {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) {\n\t\t\treturn false\n\t\t}\n\n\t\ttransact(() => {\n\t\t\tvalueAtom.set(UNINITIALIZED)\n\t\t\tthis.atoms.update((atoms) => {\n\t\t\t\treturn atoms.delete(key)\n\t\t\t})\n\t\t})\n\t\treturn true\n\t}\n\n\tdeleteMany(keys: Iterable<K>): [K, V][] {\n\t\treturn transact(() => {\n\t\t\tconst deleted: [K, V][] = []\n\t\t\tconst newAtoms = this.atoms.get().withMutations((atoms) => {\n\t\t\t\tfor (const key of keys) {\n\t\t\t\t\tconst valueAtom = atoms.get(key)\n\t\t\t\t\tif (!valueAtom) continue\n\t\t\t\t\tconst oldValue = valueAtom.get()\n\t\t\t\t\tassert(oldValue !== UNINITIALIZED)\n\n\t\t\t\t\tdeleted.push([key, oldValue])\n\n\t\t\t\t\tatoms.delete(key)\n\t\t\t\t\tvalueAtom.set(UNINITIALIZED)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tif (deleted.length) {\n\t\t\t\tthis.atoms.set(newAtoms)\n\t\t\t}\n\n\t\t\treturn deleted\n\t\t})\n\t}\n\n\tclear() {\n\t\treturn transact(() => {\n\t\t\tfor (const valueAtom of this.atoms.__unsafe__getWithoutCapture().values()) {\n\t\t\t\tvalueAtom.set(UNINITIALIZED)\n\t\t\t}\n\t\t\tthis.atoms.set(emptyMap())\n\t\t})\n\t}\n\n\t*entries(): Generator<[K, V], undefined, unknown> {\n\t\tfor (const [key, valueAtom] of this.atoms.get()) {\n\t\t\tconst value = valueAtom.get()\n\t\t\tassert(value !== UNINITIALIZED)\n\t\t\tyield [key, value]\n\t\t}\n\t}\n\n\t*keys(): Generator<K, undefined, unknown> {\n\t\tfor (const key of this.atoms.get().keys()) {\n\t\t\tyield key\n\t\t}\n\t}\n\n\t*values(): Generator<V, undefined, unknown> {\n\t\tfor (const valueAtom of this.atoms.get().values()) {\n\t\t\tconst value = valueAtom.get()\n\t\t\tassert(value !== UNINITIALIZED)\n\t\t\tyield value\n\t\t}\n\t}\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget size() {\n\t\treturn this.atoms.get().size\n\t}\n\n\tforEach(callbackfn: (value: V, key: K, map: AtomMap<K, V>) => void, thisArg?: any): void {\n\t\tfor (const [key, value] of this.entries()) {\n\t\t\tcallbackfn.call(thisArg, value, key, this)\n\t\t}\n\t}\n\n\t[Symbol.iterator]() {\n\t\treturn this.entries()\n\t}\n\n\t[Symbol.toStringTag] = 'AtomMap'\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,MAAY,UAAU,qBAAqB;AACpD,SAAS,cAAc;AACvB,SAAS,gBAA8B;AAMhC,MAAM,QAAmC;AAAA,
|
|
4
|
+
"sourcesContent": ["import { atom, Atom, transact, UNINITIALIZED } from '@tldraw/state'\nimport { assert } from '@tldraw/utils'\nimport { emptyMap, ImmutableMap } from './ImmutableMap'\n\n/**\n * A drop-in replacement for Map that stores values in atoms and can be used in reactive contexts.\n * @public\n */\nexport class AtomMap<K, V> implements Map<K, V> {\n\tprivate atoms: Atom<ImmutableMap<K, Atom<V | UNINITIALIZED>>>\n\n\t/**\n\t * Creates a new AtomMap instance.\n\t *\n\t * name - A unique name for this map, used for atom identification\n\t * entries - Optional initial entries to populate the map with\n\t * @example\n\t * ```ts\n\t * // Create an empty map\n\t * const map = new AtomMap('userMap')\n\t *\n\t * // Create a map with initial data\n\t * const initialData: [string, number][] = [['a', 1], ['b', 2]]\n\t * const mapWithData = new AtomMap('numbersMap', initialData)\n\t * ```\n\t */\n\tconstructor(\n\t\tprivate readonly name: string,\n\t\tentries?: Iterable<readonly [K, V]>\n\t) {\n\t\tlet atoms = emptyMap<K, Atom<V>>()\n\t\tif (entries) {\n\t\t\tatoms = atoms.withMutations((atoms) => {\n\t\t\t\tfor (const [k, v] of entries) {\n\t\t\t\t\tatoms.set(k, atom(`${name}:${String(k)}`, v))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tthis.atoms = atom(`${name}:atoms`, atoms)\n\t}\n\n\t/**\n\t * Retrieves the underlying atom for a given key.\n\t *\n\t * @param key - The key to retrieve the atom for\n\t * @returns The atom containing the value, or undefined if the key doesn't exist\n\t * @internal\n\t */\n\tgetAtom(key: K): Atom<V | UNINITIALIZED> | undefined {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) {\n\t\t\t// if the value is missing, we want to track whether it's in the present keys set\n\t\t\tthis.atoms.get()\n\t\t\treturn undefined\n\t\t}\n\t\treturn valueAtom\n\t}\n\n\t/**\n\t * Gets the value associated with a key. Returns undefined if the key doesn't exist.\n\t * This method is reactive and will cause reactive contexts to update when the value changes.\n\t *\n\t * @param key - The key to retrieve the value for\n\t * @returns The value associated with the key, or undefined if not found\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('name', 'Alice')\n\t * console.log(map.get('name')) // 'Alice'\n\t * console.log(map.get('missing')) // undefined\n\t * ```\n\t */\n\tget(key: K): V | undefined {\n\t\tconst value = this.getAtom(key)?.get()\n\t\tassert(value !== UNINITIALIZED)\n\t\treturn value\n\t}\n\n\t/**\n\t * Gets the value associated with a key without creating reactive dependencies.\n\t * This method will not cause reactive contexts to update when the value changes.\n\t *\n\t * @param key - The key to retrieve the value for\n\t * @returns The value associated with the key, or undefined if not found\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('count', 42)\n\t * const value = map.__unsafe__getWithoutCapture('count') // No reactive subscription\n\t * ```\n\t */\n\t__unsafe__getWithoutCapture(key: K): V | undefined {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) return undefined\n\t\tconst value = valueAtom.__unsafe__getWithoutCapture()\n\t\tassert(value !== UNINITIALIZED)\n\t\treturn value\n\t}\n\n\t/**\n\t * Checks whether a key exists in the map.\n\t * This method is reactive and will cause reactive contexts to update when keys are added or removed.\n\t *\n\t * @param key - The key to check for\n\t * @returns True if the key exists in the map, false otherwise\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * console.log(map.has('name')) // false\n\t * map.set('name', 'Alice')\n\t * console.log(map.has('name')) // true\n\t * ```\n\t */\n\thas(key: K): boolean {\n\t\tconst valueAtom = this.getAtom(key)\n\t\tif (!valueAtom) {\n\t\t\treturn false\n\t\t}\n\t\treturn valueAtom.get() !== UNINITIALIZED\n\t}\n\n\t/**\n\t * Checks whether a key exists in the map without creating reactive dependencies.\n\t * This method will not cause reactive contexts to update when keys are added or removed.\n\t *\n\t * @param key - The key to check for\n\t * @returns True if the key exists in the map, false otherwise\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('active', true)\n\t * const exists = map.__unsafe__hasWithoutCapture('active') // No reactive subscription\n\t * ```\n\t */\n\t__unsafe__hasWithoutCapture(key: K): boolean {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) return false\n\t\tassert(valueAtom.__unsafe__getWithoutCapture() !== UNINITIALIZED)\n\t\treturn true\n\t}\n\n\t/**\n\t * Sets a value for the given key. If the key already exists, its value is updated.\n\t * If the key doesn't exist, a new entry is created.\n\t *\n\t * @param key - The key to set the value for\n\t * @param value - The value to associate with the key\n\t * @returns This AtomMap instance for method chaining\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('name', 'Alice').set('age', 30)\n\t * ```\n\t */\n\tset(key: K, value: V) {\n\t\tconst existingAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (existingAtom) {\n\t\t\texistingAtom.set(value)\n\t\t} else {\n\t\t\tthis.atoms.update((atoms) => {\n\t\t\t\treturn atoms.set(key, atom(`${this.name}:${String(key)}`, value))\n\t\t\t})\n\t\t}\n\t\treturn this\n\t}\n\n\t/**\n\t * Updates an existing value using an updater function.\n\t *\n\t * @param key - The key of the value to update\n\t * @param updater - A function that receives the current value and returns the new value\n\t * @throws Error if the key doesn't exist in the map\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('count', 5)\n\t * map.update('count', count => count + 1) // count is now 6\n\t * ```\n\t */\n\tupdate(key: K, updater: (value: V) => V) {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) {\n\t\t\tthrow new Error(`AtomMap: key ${key} not found`)\n\t\t}\n\t\tconst value = valueAtom.__unsafe__getWithoutCapture()\n\t\tassert(value !== UNINITIALIZED)\n\t\tvalueAtom.set(updater(value))\n\t}\n\n\t/**\n\t * Removes a key-value pair from the map.\n\t *\n\t * @param key - The key to remove\n\t * @returns True if the key existed and was removed, false if it didn't exist\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('temp', 'value')\n\t * console.log(map.delete('temp')) // true\n\t * console.log(map.delete('missing')) // false\n\t * ```\n\t */\n\tdelete(key: K) {\n\t\tconst valueAtom = this.atoms.__unsafe__getWithoutCapture().get(key)\n\t\tif (!valueAtom) {\n\t\t\treturn false\n\t\t}\n\n\t\ttransact(() => {\n\t\t\tvalueAtom.set(UNINITIALIZED)\n\t\t\tthis.atoms.update((atoms) => {\n\t\t\t\treturn atoms.delete(key)\n\t\t\t})\n\t\t})\n\t\treturn true\n\t}\n\n\t/**\n\t * Removes multiple key-value pairs from the map in a single transaction.\n\t *\n\t * @param keys - An iterable of keys to remove\n\t * @returns An array of [key, value] pairs that were actually deleted\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('a', 1).set('b', 2).set('c', 3)\n\t * const deleted = map.deleteMany(['a', 'c', 'missing'])\n\t * console.log(deleted) // [['a', 1], ['c', 3]]\n\t * ```\n\t */\n\tdeleteMany(keys: Iterable<K>): [K, V][] {\n\t\treturn transact(() => {\n\t\t\tconst deleted: [K, V][] = []\n\t\t\tconst newAtoms = this.atoms.get().withMutations((atoms) => {\n\t\t\t\tfor (const key of keys) {\n\t\t\t\t\tconst valueAtom = atoms.get(key)\n\t\t\t\t\tif (!valueAtom) continue\n\t\t\t\t\tconst oldValue = valueAtom.get()\n\t\t\t\t\tassert(oldValue !== UNINITIALIZED)\n\n\t\t\t\t\tdeleted.push([key, oldValue])\n\n\t\t\t\t\tatoms.delete(key)\n\t\t\t\t\tvalueAtom.set(UNINITIALIZED)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tif (deleted.length) {\n\t\t\t\tthis.atoms.set(newAtoms)\n\t\t\t}\n\n\t\t\treturn deleted\n\t\t})\n\t}\n\n\t/**\n\t * Removes all key-value pairs from the map.\n\t *\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('a', 1).set('b', 2)\n\t * map.clear()\n\t * console.log(map.size) // 0\n\t * ```\n\t */\n\tclear() {\n\t\treturn transact(() => {\n\t\t\tfor (const valueAtom of this.atoms.__unsafe__getWithoutCapture().values()) {\n\t\t\t\tvalueAtom.set(UNINITIALIZED)\n\t\t\t}\n\t\t\tthis.atoms.set(emptyMap())\n\t\t})\n\t}\n\n\t/**\n\t * Returns an iterator that yields [key, value] pairs for each entry in the map.\n\t * This method is reactive and will cause reactive contexts to update when entries change.\n\t *\n\t * @returns A generator that yields [key, value] tuples\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('a', 1).set('b', 2)\n\t * for (const [key, value] of map.entries()) {\n\t * console.log(`${key}: ${value}`)\n\t * }\n\t * ```\n\t */\n\t*entries(): Generator<[K, V], undefined, unknown> {\n\t\tfor (const [key, valueAtom] of this.atoms.get()) {\n\t\t\tconst value = valueAtom.get()\n\t\t\tassert(value !== UNINITIALIZED)\n\t\t\tyield [key, value]\n\t\t}\n\t}\n\n\t/**\n\t * Returns an iterator that yields all keys in the map.\n\t * This method is reactive and will cause reactive contexts to update when keys change.\n\t *\n\t * @returns A generator that yields keys\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('name', 'Alice').set('age', 30)\n\t * for (const key of map.keys()) {\n\t * console.log(key) // 'name', 'age'\n\t * }\n\t * ```\n\t */\n\t*keys(): Generator<K, undefined, unknown> {\n\t\tfor (const key of this.atoms.get().keys()) {\n\t\t\tyield key\n\t\t}\n\t}\n\n\t/**\n\t * Returns an iterator that yields all values in the map.\n\t * This method is reactive and will cause reactive contexts to update when values change.\n\t *\n\t * @returns A generator that yields values\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('name', 'Alice').set('age', 30)\n\t * for (const value of map.values()) {\n\t * console.log(value) // 'Alice', 30\n\t * }\n\t * ```\n\t */\n\t*values(): Generator<V, undefined, unknown> {\n\t\tfor (const valueAtom of this.atoms.get().values()) {\n\t\t\tconst value = valueAtom.get()\n\t\t\tassert(value !== UNINITIALIZED)\n\t\t\tyield value\n\t\t}\n\t}\n\n\t/**\n\t * The number of key-value pairs in the map.\n\t * This property is reactive and will cause reactive contexts to update when the size changes.\n\t *\n\t * @returns The number of entries in the map\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * console.log(map.size) // 0\n\t * map.set('a', 1)\n\t * console.log(map.size) // 1\n\t * ```\n\t */\n\t// eslint-disable-next-line no-restricted-syntax\n\tget size() {\n\t\treturn this.atoms.get().size\n\t}\n\n\t/**\n\t * Executes a provided function once for each key-value pair in the map.\n\t * This method is reactive and will cause reactive contexts to update when entries change.\n\t *\n\t * @param callbackfn - Function to execute for each entry\n\t * - value - The value of the current entry\n\t * - key - The key of the current entry\n\t * - map - The AtomMap being traversed\n\t * @param thisArg - Value to use as `this` when executing the callback\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('a', 1).set('b', 2)\n\t * map.forEach((value, key) => {\n\t * console.log(`${key} = ${value}`)\n\t * })\n\t * ```\n\t */\n\tforEach(callbackfn: (value: V, key: K, map: AtomMap<K, V>) => void, thisArg?: any): void {\n\t\tfor (const [key, value] of this.entries()) {\n\t\t\tcallbackfn.call(thisArg, value, key, this)\n\t\t}\n\t}\n\n\t/**\n\t * Returns the default iterator for the map, which is the same as entries().\n\t * This allows the map to be used in for...of loops and other iterable contexts.\n\t *\n\t * @returns The same iterator as entries()\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * map.set('a', 1).set('b', 2)\n\t *\n\t * // These are equivalent:\n\t * for (const [key, value] of map) {\n\t * console.log(`${key}: ${value}`)\n\t * }\n\t *\n\t * for (const [key, value] of map.entries()) {\n\t * console.log(`${key}: ${value}`)\n\t * }\n\t * ```\n\t */\n\t[Symbol.iterator]() {\n\t\treturn this.entries()\n\t}\n\n\t/**\n\t * The string tag used by Object.prototype.toString for this class.\n\t *\n\t * @example\n\t * ```ts\n\t * const map = new AtomMap('myMap')\n\t * console.log(Object.prototype.toString.call(map)) // '[object AtomMap]'\n\t * ```\n\t */\n\t[Symbol.toStringTag] = 'AtomMap'\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["atoms"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/BaseRecord.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * A branded string type that represents a unique identifier for a record.\n * The brand ensures type safety by preventing mixing of IDs between different record types.\n *\n * @example\n * ```ts\n * // Define a Book record\n * interface Book extends BaseRecord<'book', RecordId<Book>> {\n * title: string\n * author: string\n * }\n *\n * const bookId: RecordId<Book> = 'book:abc123' as RecordId<Book>\n * const authorId: RecordId<Author> = 'author:xyz789' as RecordId<Author>\n *\n * // TypeScript prevents mixing different record ID types\n * // bookId = authorId // Type error!\n * ```\n *\n * @public\n */\nexport type RecordId<R extends UnknownRecord> = string & { __type__: R }\n\n/**\n * Utility type that extracts the ID type from a record type.\n * This is useful when you need to work with record IDs without having the full record type.\n *\n * @example\n * ```ts\n * interface Book extends BaseRecord<'book', RecordId<Book>> {\n * title: string\n * author: string\n * }\n *\n * // Extract the ID type from the Book record\n * type BookId = IdOf<Book> // RecordId<Book>\n *\n * function findBook(id: IdOf<Book>): Book | undefined {\n * return store.get(id)\n * }\n * ```\n *\n * @public\n */\nexport type IdOf<R extends UnknownRecord> = R['id']\n\n/**\n * The base record interface that all records in the store must extend.\n * This interface provides the fundamental structure required for all records: a unique ID and a type name.\n * The type parameters ensure type safety and prevent mixing of different record types.\n *\n * @example\n * ```ts\n * // Define a Book record that extends BaseRecord\n * interface Book extends BaseRecord<'book', RecordId<Book>> {\n * title: string\n * author: string\n * publishedYear: number\n * }\n *\n * // Define an Author record\n * interface Author extends BaseRecord<'author', RecordId<Author>> {\n * name: string\n * birthYear: number\n * }\n *\n * // Usage with RecordType\n * const Book = createRecordType<Book>('book', { scope: 'document' })\n * const book = Book.create({\n * title: '1984',\n * author: 'George Orwell',\n * publishedYear: 1949\n * })\n * // Results in: { id: 'book:abc123', typeName: 'book', title: '1984', ... }\n * ```\n *\n * @public\n */\nexport interface BaseRecord<TypeName extends string, Id extends RecordId<UnknownRecord>> {\n\treadonly id: Id\n\treadonly typeName: TypeName\n}\n\n/**\n * A generic type representing any record that extends BaseRecord.\n * This is useful for type constraints when you need to work with records of unknown types,\n * but still want to ensure they follow the BaseRecord structure.\n *\n * @example\n * ```ts\n * // Function that works with any type of record\n * function logRecord(record: UnknownRecord): void {\n * console.log(`Record ${record.id} of type ${record.typeName}`)\n * }\n *\n * // Can be used with any record type\n * const book: Book = { id: 'book:123' as RecordId<Book>, typeName: 'book', title: '1984' }\n * const author: Author = { id: 'author:456' as RecordId<Author>, typeName: 'author', name: 'Orwell' }\n *\n * logRecord(book) // \"Record book:123 of type book\"\n * logRecord(author) // \"Record author:456 of type author\"\n * ```\n *\n * @public\n */\nexport type UnknownRecord = BaseRecord<string, RecordId<UnknownRecord>>\n\n/**\n * Type guard function that checks if an unknown value is a valid record.\n * A valid record must be an object with both `id` and `typeName` properties.\n *\n * @param record - The unknown value to check\n * @returns `true` if the value is a valid UnknownRecord, `false` otherwise\n *\n * @example\n * ```ts\n * const maybeRecord: unknown = { id: 'book:123', typeName: 'book', title: '1984' }\n * const notARecord: unknown = { title: '1984', author: 'Orwell' }\n * const nullValue: unknown = null\n *\n * if (isRecord(maybeRecord)) {\n * // TypeScript now knows maybeRecord is UnknownRecord\n * console.log(maybeRecord.id) // 'book:123'\n * console.log(maybeRecord.typeName) // 'book'\n * }\n *\n * console.log(isRecord(maybeRecord)) // true\n * console.log(isRecord(notARecord)) // false (missing id and typeName)\n * console.log(isRecord(nullValue)) // false\n * ```\n *\n * @public\n */\nexport function isRecord(record: unknown): record is UnknownRecord {\n\treturn typeof record === 'object' && record !== null && 'id' in record && 'typeName' in record\n}\n"],
|
|
5
|
+
"mappings": "AAqIO,SAAS,SAAS,QAA0C;AAClE,SAAO,OAAO,WAAW,YAAY,WAAW,QAAQ,QAAQ,UAAU,cAAc;AACzF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -148,6 +148,22 @@ class ImmutableMap {
|
|
|
148
148
|
__hash;
|
|
149
149
|
// @ts-ignore
|
|
150
150
|
__altered;
|
|
151
|
+
/**
|
|
152
|
+
* Creates a new ImmutableMap instance.
|
|
153
|
+
*
|
|
154
|
+
* @param value - An iterable of key-value pairs to populate the map, or null/undefined for an empty map
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* // Create from array of pairs
|
|
158
|
+
* const map1 = new ImmutableMap([['a', 1], ['b', 2]])
|
|
159
|
+
*
|
|
160
|
+
* // Create empty map
|
|
161
|
+
* const map2 = new ImmutableMap()
|
|
162
|
+
*
|
|
163
|
+
* // Create from another map
|
|
164
|
+
* const map3 = new ImmutableMap(map1)
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
151
167
|
constructor(value) {
|
|
152
168
|
return value === void 0 || value === null ? emptyMap() : value instanceof ImmutableMap ? value : emptyMap().withMutations((map) => {
|
|
153
169
|
for (const [k, v] of value) {
|
|
@@ -155,15 +171,67 @@ class ImmutableMap {
|
|
|
155
171
|
}
|
|
156
172
|
});
|
|
157
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Gets the value associated with the specified key, with a fallback value.
|
|
176
|
+
*
|
|
177
|
+
* @param k - The key to look up
|
|
178
|
+
* @param notSetValue - The value to return if the key is not found
|
|
179
|
+
* @returns The value associated with the key, or the fallback value if not found
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* const map = new ImmutableMap([['key1', 'value1']])
|
|
183
|
+
* console.log(map.get('key1', 'default')) // 'value1'
|
|
184
|
+
* console.log(map.get('missing', 'default')) // 'default'
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
158
187
|
get(k, notSetValue) {
|
|
159
188
|
return this._root ? this._root.get(0, void 0, k, notSetValue) : notSetValue;
|
|
160
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Returns a new ImmutableMap with the specified key-value pair added or updated.
|
|
192
|
+
* If the key already exists, its value is replaced. Otherwise, a new entry is created.
|
|
193
|
+
*
|
|
194
|
+
* @param k - The key to set
|
|
195
|
+
* @param v - The value to associate with the key
|
|
196
|
+
* @returns A new ImmutableMap with the key-value pair set
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const map = new ImmutableMap([['a', 1]])
|
|
200
|
+
* const updated = map.set('b', 2) // New map with both 'a' and 'b'
|
|
201
|
+
* const replaced = map.set('a', 10) // New map with 'a' updated to 10
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
161
204
|
set(k, v) {
|
|
162
205
|
return updateMap(this, k, v);
|
|
163
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Returns a new ImmutableMap with the specified key removed.
|
|
209
|
+
* If the key doesn't exist, returns the same map instance.
|
|
210
|
+
*
|
|
211
|
+
* @param k - The key to remove
|
|
212
|
+
* @returns A new ImmutableMap with the key removed, or the same instance if key not found
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* const map = new ImmutableMap([['a', 1], ['b', 2]])
|
|
216
|
+
* const smaller = map.delete('a') // New map with only 'b'
|
|
217
|
+
* const same = map.delete('missing') // Returns original map
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
164
220
|
delete(k) {
|
|
165
221
|
return updateMap(this, k, NOT_SET);
|
|
166
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Returns a new ImmutableMap with all specified keys removed.
|
|
225
|
+
* This is more efficient than calling delete() multiple times.
|
|
226
|
+
*
|
|
227
|
+
* @param keys - An iterable of keys to remove
|
|
228
|
+
* @returns A new ImmutableMap with all specified keys removed
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* const map = new ImmutableMap([['a', 1], ['b', 2], ['c', 3]])
|
|
232
|
+
* const smaller = map.deleteAll(['a', 'c']) // New map with only 'b'
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
167
235
|
deleteAll(keys) {
|
|
168
236
|
return this.withMutations((map) => {
|
|
169
237
|
for (const key of keys) {
|
|
@@ -185,26 +253,99 @@ class ImmutableMap {
|
|
|
185
253
|
}
|
|
186
254
|
return makeMap(this.size, this._root, ownerID, this.__hash);
|
|
187
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Applies multiple mutations efficiently by creating a mutable copy,
|
|
258
|
+
* applying all changes, then returning an immutable result.
|
|
259
|
+
* This is more efficient than chaining multiple set/delete operations.
|
|
260
|
+
*
|
|
261
|
+
* @param fn - Function that receives a mutable copy and applies changes
|
|
262
|
+
* @returns A new ImmutableMap with all mutations applied, or the same instance if no changes
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* const map = new ImmutableMap([['a', 1]])
|
|
266
|
+
* const updated = map.withMutations(mutable => {
|
|
267
|
+
* mutable.set('b', 2)
|
|
268
|
+
* mutable.set('c', 3)
|
|
269
|
+
* mutable.delete('a')
|
|
270
|
+
* }) // Efficiently applies all changes at once
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
188
273
|
withMutations(fn) {
|
|
189
274
|
const mutable = this.asMutable();
|
|
190
275
|
fn(mutable);
|
|
191
276
|
return mutable.wasAltered() ? mutable.__ensureOwner(this.__ownerID) : this;
|
|
192
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Checks if this map instance has been altered during a mutation operation.
|
|
280
|
+
* This is used internally to optimize mutations.
|
|
281
|
+
*
|
|
282
|
+
* @returns True if the map was altered, false otherwise
|
|
283
|
+
* @internal
|
|
284
|
+
*/
|
|
193
285
|
wasAltered() {
|
|
194
286
|
return this.__altered;
|
|
195
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Returns a mutable copy of this map that can be efficiently modified.
|
|
290
|
+
* Multiple changes to the mutable copy are batched together.
|
|
291
|
+
*
|
|
292
|
+
* @returns A mutable copy of this map
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
196
295
|
asMutable() {
|
|
197
296
|
return this.__ownerID ? this : this.__ensureOwner(new OwnerID());
|
|
198
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Makes the map iterable, yielding key-value pairs.
|
|
300
|
+
*
|
|
301
|
+
* @returns An iterator over [key, value] pairs
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* const map = new ImmutableMap([['a', 1], ['b', 2]])
|
|
305
|
+
* for (const [key, value] of map) {
|
|
306
|
+
* console.log(key, value) // 'a' 1, then 'b' 2
|
|
307
|
+
* }
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
199
310
|
[Symbol.iterator]() {
|
|
200
311
|
return this.entries()[Symbol.iterator]();
|
|
201
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Returns an iterable of key-value pairs.
|
|
315
|
+
*
|
|
316
|
+
* @returns An iterable over [key, value] pairs
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* const map = new ImmutableMap([['a', 1], ['b', 2]])
|
|
320
|
+
* const entries = Array.from(map.entries()) // [['a', 1], ['b', 2]]
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
202
323
|
entries() {
|
|
203
324
|
return new MapIterator(this, ITERATE_ENTRIES, false);
|
|
204
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Returns an iterable of keys.
|
|
328
|
+
*
|
|
329
|
+
* @returns An iterable over keys
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* const map = new ImmutableMap([['a', 1], ['b', 2]])
|
|
333
|
+
* const keys = Array.from(map.keys()) // ['a', 'b']
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
205
336
|
keys() {
|
|
206
337
|
return new MapIterator(this, ITERATE_KEYS, false);
|
|
207
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Returns an iterable of values.
|
|
341
|
+
*
|
|
342
|
+
* @returns An iterable over values
|
|
343
|
+
* @example
|
|
344
|
+
* ```ts
|
|
345
|
+
* const map = new ImmutableMap([['a', 1], ['b', 2]])
|
|
346
|
+
* const values = Array.from(map.values()) // [1, 2]
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
208
349
|
values() {
|
|
209
350
|
return new MapIterator(this, ITERATE_VALUES, false);
|
|
210
351
|
}
|