@tldraw/store 4.3.0-canary.da35795ba8e2 → 4.3.0-canary.e1766dd4eab3
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 +28 -1
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/StoreSchema.js +80 -24
- package/dist-cjs/lib/StoreSchema.js.map +2 -2
- package/dist-cjs/lib/StoreSideEffects.js +1 -1
- package/dist-cjs/lib/StoreSideEffects.js.map +1 -1
- package/dist-cjs/lib/devFreeze.js +5 -3
- package/dist-cjs/lib/devFreeze.js.map +2 -2
- package/dist-cjs/lib/isDev.js +37 -0
- package/dist-cjs/lib/isDev.js.map +7 -0
- package/dist-cjs/lib/migrate.js.map +2 -2
- package/dist-esm/index.d.mts +28 -1
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/StoreSchema.mjs +83 -25
- package/dist-esm/lib/StoreSchema.mjs.map +2 -2
- package/dist-esm/lib/StoreSideEffects.mjs +1 -1
- package/dist-esm/lib/StoreSideEffects.mjs.map +1 -1
- package/dist-esm/lib/devFreeze.mjs +5 -3
- package/dist-esm/lib/devFreeze.mjs.map +2 -2
- package/dist-esm/lib/isDev.mjs +16 -0
- package/dist-esm/lib/isDev.mjs.map +7 -0
- package/dist-esm/lib/migrate.mjs.map +2 -2
- package/package.json +3 -3
- package/src/index.ts +2 -0
- package/src/lib/StoreSchema.ts +90 -30
- package/src/lib/StoreSideEffects.ts +1 -1
- package/src/lib/devFreeze.test.ts +6 -2
- package/src/lib/devFreeze.ts +7 -3
- package/src/lib/isDev.ts +20 -0
- package/src/lib/migrate.ts +29 -0
- package/src/lib/test/recordStore.test.ts +182 -0
package/src/lib/isDev.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
let _isDev = false
|
|
2
|
+
try {
|
|
3
|
+
_isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
|
|
4
|
+
} catch (_e) {
|
|
5
|
+
/* noop */
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
_isDev =
|
|
9
|
+
_isDev ||
|
|
10
|
+
(import.meta as any).env.DEV ||
|
|
11
|
+
(import.meta as any).env.TEST ||
|
|
12
|
+
(import.meta as any).env.MODE === 'development' ||
|
|
13
|
+
(import.meta as any).env.MODE === 'test'
|
|
14
|
+
} catch (_e) {
|
|
15
|
+
/* noop */
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isDev() {
|
|
19
|
+
return _isDev
|
|
20
|
+
}
|
package/src/lib/migrate.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert, objectMapEntries } from '@tldraw/utils'
|
|
2
2
|
import { UnknownRecord } from './BaseRecord'
|
|
3
3
|
import { SerializedStore } from './Store'
|
|
4
|
+
import { SerializedSchema } from './StoreSchema'
|
|
4
5
|
|
|
5
6
|
function squashDependsOn(sequence: Array<Migration | StandaloneDependsOn>): Migration[] {
|
|
6
7
|
const result: Migration[] = []
|
|
@@ -219,8 +220,36 @@ export type Migration = {
|
|
|
219
220
|
newState: SerializedStore<UnknownRecord>
|
|
220
221
|
) => void | SerializedStore<UnknownRecord>
|
|
221
222
|
}
|
|
223
|
+
| {
|
|
224
|
+
readonly scope: 'storage'
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
226
|
+
readonly up: (storage: SynchronousRecordStorage<UnknownRecord>) => void
|
|
227
|
+
readonly down?: never
|
|
228
|
+
}
|
|
222
229
|
)
|
|
223
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Abstraction over the store that can be used to perform migrations.
|
|
233
|
+
* @public
|
|
234
|
+
*/
|
|
235
|
+
export interface SynchronousRecordStorage<R extends UnknownRecord> {
|
|
236
|
+
get(id: string): R | undefined
|
|
237
|
+
set(id: string, record: R): void
|
|
238
|
+
delete(id: string): void
|
|
239
|
+
keys(): Iterable<string>
|
|
240
|
+
values(): Iterable<R>
|
|
241
|
+
entries(): Iterable<[string, R]>
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Abstraction over the storage that can be used to perform migrations.
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
export interface SynchronousStorage<R extends UnknownRecord> extends SynchronousRecordStorage<R> {
|
|
249
|
+
getSchema(): SerializedSchema
|
|
250
|
+
setSchema(schema: SerializedSchema): void
|
|
251
|
+
}
|
|
252
|
+
|
|
224
253
|
/**
|
|
225
254
|
* Base interface for legacy migration information.
|
|
226
255
|
*
|
|
@@ -883,6 +883,188 @@ describe('snapshots', () => {
|
|
|
883
883
|
expect(up).toHaveBeenCalledTimes(1)
|
|
884
884
|
expect(store2.get(Book.createId('lotr'))!.numPages).toBe(42)
|
|
885
885
|
})
|
|
886
|
+
|
|
887
|
+
it('migrates the snapshot with storage scope', () => {
|
|
888
|
+
const snapshot1 = store.getStoreSnapshot()
|
|
889
|
+
const up = vi.fn((storage: any) => {
|
|
890
|
+
const book = storage.get('book:lotr')
|
|
891
|
+
storage.set('book:lotr', { ...book, numPages: 42 })
|
|
892
|
+
})
|
|
893
|
+
|
|
894
|
+
expect((snapshot1.store as any)['book:lotr'].numPages).toBe(1000)
|
|
895
|
+
|
|
896
|
+
const store2 = new Store({
|
|
897
|
+
props: {},
|
|
898
|
+
schema: StoreSchema.create<Book | Author>(
|
|
899
|
+
{
|
|
900
|
+
book: Book,
|
|
901
|
+
author: Author,
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
migrations: [
|
|
905
|
+
createMigrationSequence({
|
|
906
|
+
sequenceId: 'com.tldraw',
|
|
907
|
+
retroactive: true,
|
|
908
|
+
sequence: [
|
|
909
|
+
{
|
|
910
|
+
id: `com.tldraw/1`,
|
|
911
|
+
scope: 'storage',
|
|
912
|
+
up,
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
}),
|
|
916
|
+
],
|
|
917
|
+
}
|
|
918
|
+
),
|
|
919
|
+
})
|
|
920
|
+
|
|
921
|
+
expect(() => {
|
|
922
|
+
store2.loadStoreSnapshot(snapshot1)
|
|
923
|
+
}).not.toThrow()
|
|
924
|
+
|
|
925
|
+
expect(up).toHaveBeenCalledTimes(1)
|
|
926
|
+
expect(store2.get(Book.createId('lotr'))!.numPages).toBe(42)
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
it('storage scope migration can delete records', () => {
|
|
930
|
+
const snapshot1 = store.getStoreSnapshot()
|
|
931
|
+
const up = vi.fn((storage: any) => {
|
|
932
|
+
storage.delete('author:mcavoy')
|
|
933
|
+
})
|
|
934
|
+
|
|
935
|
+
expect((snapshot1.store as any)['author:mcavoy']).toBeDefined()
|
|
936
|
+
|
|
937
|
+
const store2 = new Store({
|
|
938
|
+
props: {},
|
|
939
|
+
schema: StoreSchema.create<Book | Author>(
|
|
940
|
+
{
|
|
941
|
+
book: Book,
|
|
942
|
+
author: Author,
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
migrations: [
|
|
946
|
+
createMigrationSequence({
|
|
947
|
+
sequenceId: 'com.tldraw',
|
|
948
|
+
retroactive: true,
|
|
949
|
+
sequence: [
|
|
950
|
+
{
|
|
951
|
+
id: `com.tldraw/1`,
|
|
952
|
+
scope: 'storage',
|
|
953
|
+
up,
|
|
954
|
+
},
|
|
955
|
+
],
|
|
956
|
+
}),
|
|
957
|
+
],
|
|
958
|
+
}
|
|
959
|
+
),
|
|
960
|
+
})
|
|
961
|
+
|
|
962
|
+
expect(() => {
|
|
963
|
+
store2.loadStoreSnapshot(snapshot1)
|
|
964
|
+
}).not.toThrow()
|
|
965
|
+
|
|
966
|
+
expect(up).toHaveBeenCalledTimes(1)
|
|
967
|
+
expect(store2.get(Author.createId('mcavoy'))).toBeUndefined()
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
it('storage scope migration can iterate records', () => {
|
|
971
|
+
const snapshot1 = store.getStoreSnapshot()
|
|
972
|
+
const up = vi.fn((storage: any) => {
|
|
973
|
+
for (const [id, record] of storage.entries()) {
|
|
974
|
+
if (record.typeName === 'book') {
|
|
975
|
+
storage.set(id, { ...record, numPages: record.numPages + 100 })
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
})
|
|
979
|
+
|
|
980
|
+
expect((snapshot1.store as any)['book:lotr'].numPages).toBe(1000)
|
|
981
|
+
expect((snapshot1.store as any)['book:hobbit'].numPages).toBe(300)
|
|
982
|
+
|
|
983
|
+
const store2 = new Store({
|
|
984
|
+
props: {},
|
|
985
|
+
schema: StoreSchema.create<Book | Author>(
|
|
986
|
+
{
|
|
987
|
+
book: Book,
|
|
988
|
+
author: Author,
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
migrations: [
|
|
992
|
+
createMigrationSequence({
|
|
993
|
+
sequenceId: 'com.tldraw',
|
|
994
|
+
retroactive: true,
|
|
995
|
+
sequence: [
|
|
996
|
+
{
|
|
997
|
+
id: `com.tldraw/1`,
|
|
998
|
+
scope: 'storage',
|
|
999
|
+
up,
|
|
1000
|
+
},
|
|
1001
|
+
],
|
|
1002
|
+
}),
|
|
1003
|
+
],
|
|
1004
|
+
}
|
|
1005
|
+
),
|
|
1006
|
+
})
|
|
1007
|
+
|
|
1008
|
+
expect(() => {
|
|
1009
|
+
store2.loadStoreSnapshot(snapshot1)
|
|
1010
|
+
}).not.toThrow()
|
|
1011
|
+
|
|
1012
|
+
expect(up).toHaveBeenCalledTimes(1)
|
|
1013
|
+
expect(store2.get(Book.createId('lotr'))!.numPages).toBe(1100)
|
|
1014
|
+
expect(store2.get(Book.createId('hobbit'))!.numPages).toBe(400)
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
it('storage scope migration can use values() and keys()', () => {
|
|
1018
|
+
const snapshot1 = store.getStoreSnapshot()
|
|
1019
|
+
const keysCollected: string[] = []
|
|
1020
|
+
const valuesCollected: any[] = []
|
|
1021
|
+
|
|
1022
|
+
const up = vi.fn((storage: any) => {
|
|
1023
|
+
for (const key of storage.keys()) {
|
|
1024
|
+
keysCollected.push(key)
|
|
1025
|
+
}
|
|
1026
|
+
for (const value of storage.values()) {
|
|
1027
|
+
valuesCollected.push(value)
|
|
1028
|
+
}
|
|
1029
|
+
})
|
|
1030
|
+
|
|
1031
|
+
const store2 = new Store({
|
|
1032
|
+
props: {},
|
|
1033
|
+
schema: StoreSchema.create<Book | Author>(
|
|
1034
|
+
{
|
|
1035
|
+
book: Book,
|
|
1036
|
+
author: Author,
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
migrations: [
|
|
1040
|
+
createMigrationSequence({
|
|
1041
|
+
sequenceId: 'com.tldraw',
|
|
1042
|
+
retroactive: true,
|
|
1043
|
+
sequence: [
|
|
1044
|
+
{
|
|
1045
|
+
id: `com.tldraw/1`,
|
|
1046
|
+
scope: 'storage',
|
|
1047
|
+
up,
|
|
1048
|
+
},
|
|
1049
|
+
],
|
|
1050
|
+
}),
|
|
1051
|
+
],
|
|
1052
|
+
}
|
|
1053
|
+
),
|
|
1054
|
+
})
|
|
1055
|
+
|
|
1056
|
+
expect(() => {
|
|
1057
|
+
store2.loadStoreSnapshot(snapshot1)
|
|
1058
|
+
}).not.toThrow()
|
|
1059
|
+
|
|
1060
|
+
expect(up).toHaveBeenCalledTimes(1)
|
|
1061
|
+
expect(keysCollected).toContain('book:lotr')
|
|
1062
|
+
expect(keysCollected).toContain('book:hobbit')
|
|
1063
|
+
expect(keysCollected).toContain('author:tolkein')
|
|
1064
|
+
expect(keysCollected).toContain('author:mcavoy')
|
|
1065
|
+
expect(keysCollected).toContain('author:cassidy')
|
|
1066
|
+
expect(valuesCollected.length).toBe(5)
|
|
1067
|
+
})
|
|
886
1068
|
})
|
|
887
1069
|
|
|
888
1070
|
describe('diffs', () => {
|