@tldraw/store 3.16.0-internal.51e99e128bd4 → 3.16.0-internal.a478398270c6
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/README.md +2 -2
- package/dist-cjs/index.d.ts +2 -8
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/RecordsDiff.js +3 -3
- package/dist-cjs/lib/RecordsDiff.js.map +2 -2
- package/dist-cjs/lib/StoreSchema.js +8 -23
- package/dist-cjs/lib/StoreSchema.js.map +3 -3
- package/dist-esm/index.d.mts +2 -8
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/RecordsDiff.mjs +3 -3
- package/dist-esm/lib/RecordsDiff.mjs.map +2 -2
- package/dist-esm/lib/StoreSchema.mjs +8 -23
- package/dist-esm/lib/StoreSchema.mjs.map +3 -3
- package/package.json +19 -12
- package/src/lib/RecordsDiff.ts +3 -9
- package/src/lib/StoreSchema.ts +8 -32
- package/src/lib/test/AtomMap.test.ts +1 -2
- package/src/lib/test/dependsOn.test.ts +2 -2
- package/src/lib/test/recordStore.test.ts +37 -40
- package/src/lib/test/sortMigrations.test.ts +4 -4
- package/src/lib/test/validateMigrations.test.ts +8 -8
- package/src/lib/test/migrationCaching.test.ts +0 -209
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/store",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "3.16.0-internal.
|
|
3
|
+
"description": "A tiny little drawing app (store).",
|
|
4
|
+
"version": "3.16.0-internal.a478398270c6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"tldraw",
|
|
20
|
-
"sdk",
|
|
21
20
|
"drawing",
|
|
22
21
|
"app",
|
|
23
22
|
"development",
|
|
@@ -32,20 +31,19 @@
|
|
|
32
31
|
"src"
|
|
33
32
|
],
|
|
34
33
|
"scripts": {
|
|
35
|
-
"test-ci": "
|
|
36
|
-
"test": "yarn run -T
|
|
37
|
-
"test-coverage": "
|
|
34
|
+
"test-ci": "lazy inherit",
|
|
35
|
+
"test": "yarn run -T jest",
|
|
36
|
+
"test-coverage": "lazy inherit",
|
|
38
37
|
"build": "yarn run -T tsx ../../internal/scripts/build-package.ts",
|
|
39
38
|
"build-api": "yarn run -T tsx ../../internal/scripts/build-api.ts",
|
|
40
39
|
"prepack": "yarn run -T tsx ../../internal/scripts/prepack.ts",
|
|
41
40
|
"postpack": "../../internal/scripts/postpack.sh",
|
|
42
41
|
"pack-tarball": "yarn pack",
|
|
43
|
-
"lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
|
|
44
|
-
"context": "yarn run -T tsx ../../internal/scripts/context.ts"
|
|
42
|
+
"lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
|
|
45
43
|
},
|
|
46
44
|
"dependencies": {
|
|
47
|
-
"@tldraw/state": "3.16.0-internal.
|
|
48
|
-
"@tldraw/utils": "3.16.0-internal.
|
|
45
|
+
"@tldraw/state": "3.16.0-internal.a478398270c6",
|
|
46
|
+
"@tldraw/utils": "3.16.0-internal.a478398270c6"
|
|
49
47
|
},
|
|
50
48
|
"peerDependencies": {
|
|
51
49
|
"react": "^18.2.0 || ^19.0.0"
|
|
@@ -53,8 +51,17 @@
|
|
|
53
51
|
"devDependencies": {
|
|
54
52
|
"@peculiar/webcrypto": "^1.5.0",
|
|
55
53
|
"lazyrepo": "0.0.0-alpha.27",
|
|
56
|
-
"raf": "^3.4.1"
|
|
57
|
-
|
|
54
|
+
"raf": "^3.4.1"
|
|
55
|
+
},
|
|
56
|
+
"jest": {
|
|
57
|
+
"preset": "../../internal/config/jest/node/jest-preset.js",
|
|
58
|
+
"setupFiles": [
|
|
59
|
+
"./setupTests.js",
|
|
60
|
+
"raf/polyfill"
|
|
61
|
+
],
|
|
62
|
+
"moduleNameMapper": {
|
|
63
|
+
"^~(.*)": "<rootDir>/src/$1"
|
|
64
|
+
}
|
|
58
65
|
},
|
|
59
66
|
"module": "dist-esm/index.mjs",
|
|
60
67
|
"source": "src/index.ts",
|
package/src/lib/RecordsDiff.ts
CHANGED
|
@@ -42,21 +42,15 @@ export function isRecordsDiffEmpty<T extends UnknownRecord>(diff: RecordsDiff<T>
|
|
|
42
42
|
* Squash a collection of diffs into a single diff.
|
|
43
43
|
*
|
|
44
44
|
* @param diffs - An array of diffs to squash.
|
|
45
|
-
* @param options - An optional object with a `mutateFirstDiff` property. If `mutateFirstDiff` is true, the first diff in the array will be mutated in-place.
|
|
46
45
|
* @returns A single diff that represents the squashed diffs.
|
|
47
46
|
* @public
|
|
48
47
|
*/
|
|
49
48
|
export function squashRecordDiffs<T extends UnknownRecord>(
|
|
50
|
-
diffs: RecordsDiff<T>[]
|
|
51
|
-
options?: {
|
|
52
|
-
mutateFirstDiff?: boolean
|
|
53
|
-
}
|
|
49
|
+
diffs: RecordsDiff<T>[]
|
|
54
50
|
): RecordsDiff<T> {
|
|
55
|
-
const result =
|
|
56
|
-
? diffs[0]
|
|
57
|
-
: ({ added: {}, removed: {}, updated: {} } as RecordsDiff<T>)
|
|
51
|
+
const result = { added: {}, removed: {}, updated: {} } as RecordsDiff<T>
|
|
58
52
|
|
|
59
|
-
squashRecordDiffsMutable(result,
|
|
53
|
+
squashRecordDiffsMutable(result, diffs)
|
|
60
54
|
return result
|
|
61
55
|
}
|
|
62
56
|
|
package/src/lib/StoreSchema.ts
CHANGED
|
@@ -107,7 +107,6 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
107
107
|
|
|
108
108
|
readonly migrations: Record<string, MigrationSequence> = {}
|
|
109
109
|
readonly sortedMigrations: readonly Migration[]
|
|
110
|
-
private readonly migrationCache = new WeakMap<SerializedSchema, Result<Migration[], string>>()
|
|
111
110
|
|
|
112
111
|
private constructor(
|
|
113
112
|
public readonly types: {
|
|
@@ -159,17 +158,10 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
160
|
|
|
161
|
+
// TODO: use a weakmap to store the result of this function
|
|
162
162
|
public getMigrationsSince(persistedSchema: SerializedSchema): Result<Migration[], string> {
|
|
163
|
-
// Check cache first
|
|
164
|
-
const cached = this.migrationCache.get(persistedSchema)
|
|
165
|
-
if (cached) {
|
|
166
|
-
return cached
|
|
167
|
-
}
|
|
168
|
-
|
|
169
163
|
const upgradeResult = upgradeSchema(persistedSchema)
|
|
170
164
|
if (!upgradeResult.ok) {
|
|
171
|
-
// Cache the error result
|
|
172
|
-
this.migrationCache.set(persistedSchema, upgradeResult)
|
|
173
165
|
return upgradeResult
|
|
174
166
|
}
|
|
175
167
|
const schema = upgradeResult.value
|
|
@@ -186,10 +178,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
186
178
|
}
|
|
187
179
|
|
|
188
180
|
if (sequenceIdsToInclude.size === 0) {
|
|
189
|
-
|
|
190
|
-
// Cache the empty result
|
|
191
|
-
this.migrationCache.set(persistedSchema, result)
|
|
192
|
-
return result
|
|
181
|
+
return Result.ok([])
|
|
193
182
|
}
|
|
194
183
|
|
|
195
184
|
const allMigrationsToInclude = new Set<MigrationId>()
|
|
@@ -208,10 +197,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
208
197
|
const idx = this.migrations[sequenceId].sequence.findIndex((m) => m.id === theirVersionId)
|
|
209
198
|
// todo: better error handling
|
|
210
199
|
if (idx === -1) {
|
|
211
|
-
|
|
212
|
-
// Cache the error result
|
|
213
|
-
this.migrationCache.set(persistedSchema, result)
|
|
214
|
-
return result
|
|
200
|
+
return Result.err('Incompatible schema?')
|
|
215
201
|
}
|
|
216
202
|
for (const migration of this.migrations[sequenceId].sequence.slice(idx + 1)) {
|
|
217
203
|
allMigrationsToInclude.add(migration.id)
|
|
@@ -219,12 +205,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
219
205
|
}
|
|
220
206
|
|
|
221
207
|
// collect any migrations
|
|
222
|
-
|
|
223
|
-
this.sortedMigrations.filter(({ id }) => allMigrationsToInclude.has(id))
|
|
224
|
-
)
|
|
225
|
-
// Cache the result
|
|
226
|
-
this.migrationCache.set(persistedSchema, result)
|
|
227
|
-
return result
|
|
208
|
+
return Result.ok(this.sortedMigrations.filter(({ id }) => allMigrationsToInclude.has(id)))
|
|
228
209
|
}
|
|
229
210
|
|
|
230
211
|
migratePersistedRecord(
|
|
@@ -282,10 +263,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
282
263
|
return { type: 'success', value: record }
|
|
283
264
|
}
|
|
284
265
|
|
|
285
|
-
migrateStoreSnapshot(
|
|
286
|
-
snapshot: StoreSnapshot<R>,
|
|
287
|
-
opts?: { mutateInputStore?: boolean }
|
|
288
|
-
): MigrationResult<SerializedStore<R>> {
|
|
266
|
+
migrateStoreSnapshot(snapshot: StoreSnapshot<R>): MigrationResult<SerializedStore<R>> {
|
|
289
267
|
let { store } = snapshot
|
|
290
268
|
const migrations = this.getMigrationsSince(snapshot.schema)
|
|
291
269
|
if (!migrations.ok) {
|
|
@@ -298,9 +276,7 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
298
276
|
return { type: 'success', value: store }
|
|
299
277
|
}
|
|
300
278
|
|
|
301
|
-
|
|
302
|
-
store = structuredClone(store)
|
|
303
|
-
}
|
|
279
|
+
store = structuredClone(store)
|
|
304
280
|
|
|
305
281
|
try {
|
|
306
282
|
for (const migration of migrationsToApply) {
|
|
@@ -310,13 +286,13 @@ export class StoreSchema<R extends UnknownRecord, P = unknown> {
|
|
|
310
286
|
if (!shouldApply) continue
|
|
311
287
|
const result = migration.up!(record as any)
|
|
312
288
|
if (result) {
|
|
313
|
-
store[id as keyof typeof store] = result as any
|
|
289
|
+
store[id as keyof typeof store] = structuredClone(result) as any
|
|
314
290
|
}
|
|
315
291
|
}
|
|
316
292
|
} else if (migration.scope === 'store') {
|
|
317
293
|
const result = migration.up!(store)
|
|
318
294
|
if (result) {
|
|
319
|
-
store = result as any
|
|
295
|
+
store = structuredClone(result) as any
|
|
320
296
|
}
|
|
321
297
|
} else {
|
|
322
298
|
exhaustiveSwitchError(migration)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { react, transaction } from '@tldraw/state'
|
|
2
|
-
import { vi } from 'vitest'
|
|
3
2
|
import { AtomMap } from '../AtomMap'
|
|
4
3
|
|
|
5
4
|
describe('AtomMap', () => {
|
|
@@ -12,7 +11,7 @@ describe('AtomMap', () => {
|
|
|
12
11
|
})
|
|
13
12
|
|
|
14
13
|
function testReactor(name: string, fn: () => any) {
|
|
15
|
-
const cb =
|
|
14
|
+
const cb = jest.fn(fn)
|
|
16
15
|
const cleanup = react(name, cb)
|
|
17
16
|
cleanupFns.push(() => cleanup())
|
|
18
17
|
return cb
|
|
@@ -26,7 +26,7 @@ describe('dependsOn', () => {
|
|
|
26
26
|
}
|
|
27
27
|
)
|
|
28
28
|
}).toThrowErrorMatchingInlineSnapshot(
|
|
29
|
-
`
|
|
29
|
+
`"Migration 'foo/1' depends on missing migration 'bar/1'"`
|
|
30
30
|
)
|
|
31
31
|
})
|
|
32
32
|
|
|
@@ -108,7 +108,7 @@ describe('standalone dependsOn', () => {
|
|
|
108
108
|
}
|
|
109
109
|
)
|
|
110
110
|
}).toThrowErrorMatchingInlineSnapshot(
|
|
111
|
-
`
|
|
111
|
+
`"Migration 'foo/1' depends on missing migration 'bar/1'"`
|
|
112
112
|
)
|
|
113
113
|
})
|
|
114
114
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Computed, react, RESET_VALUE, transact } from '@tldraw/state'
|
|
2
|
-
import { vi } from 'vitest'
|
|
3
2
|
import { BaseRecord, RecordId } from '../BaseRecord'
|
|
4
3
|
import { createMigrationSequence } from '../migrate'
|
|
5
4
|
import { RecordsDiff, reverseRecordsDiff } from '../RecordsDiff'
|
|
@@ -207,7 +206,7 @@ describe('Store', () => {
|
|
|
207
206
|
|
|
208
207
|
it('allows adding onAfterChange callbacks that see the final state of the world', () => {
|
|
209
208
|
/* ADDING */
|
|
210
|
-
const onAfterCreate =
|
|
209
|
+
const onAfterCreate = jest.fn((current) => {
|
|
211
210
|
expect(current).toEqual(
|
|
212
211
|
Author.create({ name: 'J.R.R Tolkein', id: Author.createId('tolkein') })
|
|
213
212
|
)
|
|
@@ -219,7 +218,7 @@ describe('Store', () => {
|
|
|
219
218
|
expect(onAfterCreate).toHaveBeenCalledTimes(1)
|
|
220
219
|
|
|
221
220
|
/* UPDATING */
|
|
222
|
-
const onAfterChange =
|
|
221
|
+
const onAfterChange = jest.fn((prev, current) => {
|
|
223
222
|
expect(prev.name).toBe('J.R.R Tolkein')
|
|
224
223
|
expect(current.name).toBe('Butch Cassidy')
|
|
225
224
|
|
|
@@ -232,7 +231,7 @@ describe('Store', () => {
|
|
|
232
231
|
expect(onAfterChange).toHaveBeenCalledTimes(1)
|
|
233
232
|
|
|
234
233
|
/* REMOVING */
|
|
235
|
-
const onAfterDelete =
|
|
234
|
+
const onAfterDelete = jest.fn((prev) => {
|
|
236
235
|
if (prev.typeName === 'author') {
|
|
237
236
|
expect(prev.name).toBe('Butch Cassidy')
|
|
238
237
|
}
|
|
@@ -310,7 +309,7 @@ describe('Store', () => {
|
|
|
310
309
|
})
|
|
311
310
|
|
|
312
311
|
it('supports listening for changes to the whole store', async () => {
|
|
313
|
-
const listener =
|
|
312
|
+
const listener = jest.fn()
|
|
314
313
|
store.listen(listener)
|
|
315
314
|
|
|
316
315
|
transact(() => {
|
|
@@ -337,7 +336,7 @@ describe('Store', () => {
|
|
|
337
336
|
|
|
338
337
|
await new Promise((resolve) => requestAnimationFrame(resolve))
|
|
339
338
|
expect(listener).toHaveBeenCalledTimes(1)
|
|
340
|
-
expect(listener.mock.lastCall
|
|
339
|
+
expect(listener.mock.lastCall[0]).toMatchInlineSnapshot(`
|
|
341
340
|
{
|
|
342
341
|
"changes": {
|
|
343
342
|
"added": {
|
|
@@ -392,7 +391,7 @@ describe('Store', () => {
|
|
|
392
391
|
await new Promise((resolve) => requestAnimationFrame(resolve))
|
|
393
392
|
expect(listener).toHaveBeenCalledTimes(2)
|
|
394
393
|
|
|
395
|
-
expect(listener.mock.lastCall
|
|
394
|
+
expect(listener.mock.lastCall[0]).toMatchInlineSnapshot(`
|
|
396
395
|
{
|
|
397
396
|
"changes": {
|
|
398
397
|
"added": {},
|
|
@@ -445,7 +444,7 @@ describe('Store', () => {
|
|
|
445
444
|
await new Promise((resolve) => requestAnimationFrame(resolve))
|
|
446
445
|
expect(listener).toHaveBeenCalledTimes(3)
|
|
447
446
|
|
|
448
|
-
expect(listener.mock.lastCall
|
|
447
|
+
expect(listener.mock.lastCall[0]).toMatchInlineSnapshot(`
|
|
449
448
|
{
|
|
450
449
|
"changes": {
|
|
451
450
|
"added": {},
|
|
@@ -481,7 +480,7 @@ describe('Store', () => {
|
|
|
481
480
|
})
|
|
482
481
|
|
|
483
482
|
it('supports filtering history by scope', () => {
|
|
484
|
-
const listener =
|
|
483
|
+
const listener = jest.fn()
|
|
485
484
|
store.listen(listener, {
|
|
486
485
|
scope: 'session',
|
|
487
486
|
})
|
|
@@ -522,7 +521,7 @@ describe('Store', () => {
|
|
|
522
521
|
})
|
|
523
522
|
|
|
524
523
|
it('supports filtering history by scope (2)', () => {
|
|
525
|
-
const listener =
|
|
524
|
+
const listener = jest.fn()
|
|
526
525
|
store.listen(listener, {
|
|
527
526
|
scope: 'document',
|
|
528
527
|
})
|
|
@@ -551,7 +550,7 @@ describe('Store', () => {
|
|
|
551
550
|
})
|
|
552
551
|
|
|
553
552
|
it('supports filtering history by source', () => {
|
|
554
|
-
const listener =
|
|
553
|
+
const listener = jest.fn()
|
|
555
554
|
store.listen(listener, {
|
|
556
555
|
source: 'remote',
|
|
557
556
|
})
|
|
@@ -601,7 +600,7 @@ describe('Store', () => {
|
|
|
601
600
|
})
|
|
602
601
|
|
|
603
602
|
it('supports filtering history by source (user)', () => {
|
|
604
|
-
const listener =
|
|
603
|
+
const listener = jest.fn()
|
|
605
604
|
store.listen(listener, {
|
|
606
605
|
source: 'user',
|
|
607
606
|
})
|
|
@@ -659,7 +658,7 @@ describe('Store', () => {
|
|
|
659
658
|
// @ts-expect-error
|
|
660
659
|
globalThis.__FORCE_RAF_IN_TESTS__ = true
|
|
661
660
|
store.put([Author.create({ name: 'J.R.R Tolkein', id: Author.createId('tolkein') })])
|
|
662
|
-
const firstListener =
|
|
661
|
+
const firstListener = jest.fn()
|
|
663
662
|
store.listen(firstListener)
|
|
664
663
|
expect(firstListener).toHaveBeenCalledTimes(0)
|
|
665
664
|
|
|
@@ -667,7 +666,7 @@ describe('Store', () => {
|
|
|
667
666
|
|
|
668
667
|
expect(firstListener).toHaveBeenCalledTimes(0)
|
|
669
668
|
|
|
670
|
-
const secondListener =
|
|
669
|
+
const secondListener = jest.fn()
|
|
671
670
|
|
|
672
671
|
store.listen(secondListener)
|
|
673
672
|
|
|
@@ -708,7 +707,7 @@ describe('Store', () => {
|
|
|
708
707
|
const id = Author.createId('tolkein')
|
|
709
708
|
store.put([Author.create({ name: 'J.R.R Tolkein', id })])
|
|
710
709
|
|
|
711
|
-
const listener =
|
|
710
|
+
const listener = jest.fn()
|
|
712
711
|
store.listen(listener)
|
|
713
712
|
|
|
714
713
|
// Return the exact same value that came in
|
|
@@ -718,7 +717,7 @@ describe('Store', () => {
|
|
|
718
717
|
})
|
|
719
718
|
|
|
720
719
|
it('tells listeners the source of the changes so they can decide if they want to run or not', async () => {
|
|
721
|
-
const listener =
|
|
720
|
+
const listener = jest.fn()
|
|
722
721
|
store.listen(listener)
|
|
723
722
|
|
|
724
723
|
store.put([Author.create({ name: 'Jimmy Beans', id: Author.createId('jimmy') })])
|
|
@@ -825,7 +824,7 @@ describe('snapshots', () => {
|
|
|
825
824
|
expect(() => {
|
|
826
825
|
// @ts-expect-error
|
|
827
826
|
store2.loadStoreSnapshot(snapshot1)
|
|
828
|
-
}).toThrowErrorMatchingInlineSnapshot(`
|
|
827
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Missing definition for record type author"`)
|
|
829
828
|
})
|
|
830
829
|
|
|
831
830
|
it('throws errors when loading a snapshot with a different schema', () => {
|
|
@@ -840,12 +839,12 @@ describe('snapshots', () => {
|
|
|
840
839
|
|
|
841
840
|
expect(() => {
|
|
842
841
|
store2.loadStoreSnapshot(snapshot1 as any)
|
|
843
|
-
}).toThrowErrorMatchingInlineSnapshot(`
|
|
842
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Missing definition for record type author"`)
|
|
844
843
|
})
|
|
845
844
|
|
|
846
845
|
it('migrates the snapshot', () => {
|
|
847
846
|
const snapshot1 = store.getStoreSnapshot()
|
|
848
|
-
const up =
|
|
847
|
+
const up = jest.fn((s: any) => {
|
|
849
848
|
s['book:lotr'].numPages = 42
|
|
850
849
|
})
|
|
851
850
|
|
|
@@ -970,7 +969,7 @@ describe('diffs', () => {
|
|
|
970
969
|
})
|
|
971
970
|
it('produces diffs from `addHistoryInterceptor`', () => {
|
|
972
971
|
const diffs: any[] = []
|
|
973
|
-
const interceptor =
|
|
972
|
+
const interceptor = jest.fn((diff) => diffs.push(diff))
|
|
974
973
|
store.addHistoryInterceptor(interceptor)
|
|
975
974
|
|
|
976
975
|
store.put([
|
|
@@ -1096,15 +1095,15 @@ describe('callbacks', () => {
|
|
|
1096
1095
|
numPages: 1,
|
|
1097
1096
|
})
|
|
1098
1097
|
|
|
1099
|
-
let onAfterCreate:
|
|
1100
|
-
let onAfterChange:
|
|
1101
|
-
let onAfterDelete:
|
|
1098
|
+
let onAfterCreate: jest.Mock
|
|
1099
|
+
let onAfterChange: jest.Mock
|
|
1100
|
+
let onAfterDelete: jest.Mock
|
|
1102
1101
|
|
|
1103
|
-
let onBeforeCreate:
|
|
1104
|
-
let onBeforeChange:
|
|
1105
|
-
let onBeforeDelete:
|
|
1102
|
+
let onBeforeCreate: jest.Mock
|
|
1103
|
+
let onBeforeChange: jest.Mock
|
|
1104
|
+
let onBeforeDelete: jest.Mock
|
|
1106
1105
|
|
|
1107
|
-
let onOperationComplete:
|
|
1106
|
+
let onOperationComplete: jest.Mock
|
|
1108
1107
|
|
|
1109
1108
|
beforeEach(() => {
|
|
1110
1109
|
store = new Store({
|
|
@@ -1114,15 +1113,15 @@ describe('callbacks', () => {
|
|
|
1114
1113
|
}),
|
|
1115
1114
|
})
|
|
1116
1115
|
|
|
1117
|
-
onAfterCreate =
|
|
1118
|
-
onAfterChange =
|
|
1119
|
-
onAfterDelete =
|
|
1116
|
+
onAfterCreate = jest.fn((record) => callbacks.push({ type: 'create', record }))
|
|
1117
|
+
onAfterChange = jest.fn((from, to) => callbacks.push({ type: 'change', from, to }))
|
|
1118
|
+
onAfterDelete = jest.fn((record) => callbacks.push({ type: 'delete', record }))
|
|
1120
1119
|
|
|
1121
|
-
onBeforeCreate =
|
|
1122
|
-
onBeforeChange =
|
|
1123
|
-
onBeforeDelete =
|
|
1120
|
+
onBeforeCreate = jest.fn((record) => record)
|
|
1121
|
+
onBeforeChange = jest.fn((_from, to) => to)
|
|
1122
|
+
onBeforeDelete = jest.fn((_record) => {})
|
|
1124
1123
|
|
|
1125
|
-
onOperationComplete =
|
|
1124
|
+
onOperationComplete = jest.fn(() => callbacks.push({ type: 'complete' }))
|
|
1126
1125
|
callbacks = []
|
|
1127
1126
|
|
|
1128
1127
|
store.sideEffects.registerAfterCreateHandler('book', onAfterCreate)
|
|
@@ -1162,12 +1161,12 @@ describe('callbacks', () => {
|
|
|
1162
1161
|
|
|
1163
1162
|
it('bails out if too many callbacks are fired', () => {
|
|
1164
1163
|
let limit = 10
|
|
1165
|
-
onAfterCreate.mockImplementation((record
|
|
1164
|
+
onAfterCreate.mockImplementation((record) => {
|
|
1166
1165
|
if (record.numPages < limit) {
|
|
1167
1166
|
store.put([{ ...record, numPages: record.numPages + 1 }])
|
|
1168
1167
|
}
|
|
1169
1168
|
})
|
|
1170
|
-
onAfterChange.mockImplementation((from
|
|
1169
|
+
onAfterChange.mockImplementation((from, to) => {
|
|
1171
1170
|
if (to.numPages < limit) {
|
|
1172
1171
|
store.put([{ ...to, numPages: to.numPages + 1 }])
|
|
1173
1172
|
}
|
|
@@ -1182,9 +1181,7 @@ describe('callbacks', () => {
|
|
|
1182
1181
|
store.clear()
|
|
1183
1182
|
expect(() => {
|
|
1184
1183
|
store.put([book2])
|
|
1185
|
-
}).toThrowErrorMatchingInlineSnapshot(
|
|
1186
|
-
`[Error: Maximum store update depth exceeded, bailing out]`
|
|
1187
|
-
)
|
|
1184
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Maximum store update depth exceeded, bailing out"`)
|
|
1188
1185
|
})
|
|
1189
1186
|
|
|
1190
1187
|
it('keeps firing operation complete callbacks until all are cleared', () => {
|
|
@@ -1200,7 +1197,7 @@ describe('callbacks', () => {
|
|
|
1200
1197
|
|
|
1201
1198
|
store.put([book1])
|
|
1202
1199
|
|
|
1203
|
-
onAfterChange.mockImplementation((prev
|
|
1200
|
+
onAfterChange.mockImplementation((prev, next) => {
|
|
1204
1201
|
if ([0, 1, 2, 5, 6].includes(step)) {
|
|
1205
1202
|
step++
|
|
1206
1203
|
store.put([{ ...next, numPages: next.numPages + 1 }])
|
|
@@ -34,18 +34,18 @@ describe(sortMigrations, () => {
|
|
|
34
34
|
it('should fail if a cycle is created', () => {
|
|
35
35
|
expect(() => {
|
|
36
36
|
sort([m('foo/1', { dependsOn: ['foo/1'] })])
|
|
37
|
-
}).toThrowErrorMatchingInlineSnapshot(`
|
|
37
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Circular dependency in migrations: foo/1"`)
|
|
38
38
|
|
|
39
39
|
expect(() => {
|
|
40
40
|
sort([m('foo/1', { dependsOn: ['foo/2'] }), m('foo/2')])
|
|
41
|
-
}).toThrowErrorMatchingInlineSnapshot(`
|
|
41
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Circular dependency in migrations: foo/1"`)
|
|
42
42
|
|
|
43
43
|
expect(() => {
|
|
44
44
|
sort([m('foo/1', { dependsOn: ['bar/1'] }), m('bar/1', { dependsOn: ['foo/1'] })])
|
|
45
|
-
}).toThrowErrorMatchingInlineSnapshot(`
|
|
45
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Circular dependency in migrations: foo/1"`)
|
|
46
46
|
|
|
47
47
|
expect(() => {
|
|
48
48
|
sort([m('bar/1', { dependsOn: ['foo/1'] }), m('foo/1', { dependsOn: ['bar/1'] })])
|
|
49
|
-
}).toThrowErrorMatchingInlineSnapshot(`
|
|
49
|
+
}).toThrowErrorMatchingInlineSnapshot(`"Circular dependency in migrations: bar/1"`)
|
|
50
50
|
})
|
|
51
51
|
})
|
|
@@ -18,7 +18,7 @@ describe(validateMigrations, () => {
|
|
|
18
18
|
sequenceId: 'foo',
|
|
19
19
|
})
|
|
20
20
|
).toThrowErrorMatchingInlineSnapshot(
|
|
21
|
-
`
|
|
21
|
+
`"Every migration in sequence 'foo' must have an id starting with 'foo/'. Got invalid id: 'foo.1'"`
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
expect(() =>
|
|
@@ -36,7 +36,7 @@ describe(validateMigrations, () => {
|
|
|
36
36
|
|
|
37
37
|
sequenceId: 'foo',
|
|
38
38
|
})
|
|
39
|
-
).toThrowErrorMatchingInlineSnapshot(`
|
|
39
|
+
).toThrowErrorMatchingInlineSnapshot(`"Invalid migration id: 'foo/one'"`)
|
|
40
40
|
|
|
41
41
|
expect(() =>
|
|
42
42
|
validateMigrations({
|
|
@@ -61,7 +61,7 @@ describe(validateMigrations, () => {
|
|
|
61
61
|
sequenceId: 'foo',
|
|
62
62
|
})
|
|
63
63
|
).toThrowErrorMatchingInlineSnapshot(
|
|
64
|
-
`
|
|
64
|
+
`"Every migration in sequence 'foo' must have an id starting with 'foo/'. Got invalid id: 'foo.2'"`
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
expect(() =>
|
|
@@ -86,7 +86,7 @@ describe(validateMigrations, () => {
|
|
|
86
86
|
|
|
87
87
|
sequenceId: 'foo',
|
|
88
88
|
})
|
|
89
|
-
).toThrowErrorMatchingInlineSnapshot(`
|
|
89
|
+
).toThrowErrorMatchingInlineSnapshot(`"Invalid migration id: 'foo/two'"`)
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
it('should throw if the sequenceId is invalid', () => {
|
|
@@ -96,7 +96,7 @@ describe(validateMigrations, () => {
|
|
|
96
96
|
sequence: [],
|
|
97
97
|
sequenceId: 'foo/bar',
|
|
98
98
|
})
|
|
99
|
-
).toThrowErrorMatchingInlineSnapshot(`
|
|
99
|
+
).toThrowErrorMatchingInlineSnapshot(`"sequenceId cannot contain a '/', got foo/bar"`)
|
|
100
100
|
|
|
101
101
|
expect(() =>
|
|
102
102
|
validateMigrations({
|
|
@@ -104,7 +104,7 @@ describe(validateMigrations, () => {
|
|
|
104
104
|
sequence: [],
|
|
105
105
|
sequenceId: '',
|
|
106
106
|
})
|
|
107
|
-
).toThrowErrorMatchingInlineSnapshot(`
|
|
107
|
+
).toThrowErrorMatchingInlineSnapshot(`"sequenceId must be a non-empty string"`)
|
|
108
108
|
})
|
|
109
109
|
|
|
110
110
|
it('should throw if the version numbers do not start at 1', () => {
|
|
@@ -124,7 +124,7 @@ describe(validateMigrations, () => {
|
|
|
124
124
|
sequenceId: 'foo',
|
|
125
125
|
})
|
|
126
126
|
).toThrowErrorMatchingInlineSnapshot(
|
|
127
|
-
`
|
|
127
|
+
`"Expected the first migrationId to be 'foo/1' but got 'foo/2'"`
|
|
128
128
|
)
|
|
129
129
|
})
|
|
130
130
|
|
|
@@ -159,7 +159,7 @@ describe(validateMigrations, () => {
|
|
|
159
159
|
sequenceId: 'foo',
|
|
160
160
|
})
|
|
161
161
|
).toThrowErrorMatchingInlineSnapshot(
|
|
162
|
-
`
|
|
162
|
+
`"Migration id numbers must increase in increments of 1, expected foo/3 but got 'foo/4'"`
|
|
163
163
|
)
|
|
164
164
|
})
|
|
165
165
|
})
|