@tldraw/tlschema 4.3.0-next.2d181ae353a2 → 4.3.0-next.40e4536afc8e

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.
Files changed (97) hide show
  1. package/dist-cjs/index.d.ts +82 -34
  2. package/dist-cjs/index.js +4 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/misc/TLOpacity.js +1 -5
  5. package/dist-cjs/misc/TLOpacity.js.map +2 -2
  6. package/dist-cjs/misc/TLRichText.js +5 -1
  7. package/dist-cjs/misc/TLRichText.js.map +2 -2
  8. package/dist-cjs/misc/b64Vecs.js +224 -0
  9. package/dist-cjs/misc/b64Vecs.js.map +7 -0
  10. package/dist-cjs/shapes/TLArrowShape.js +30 -13
  11. package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
  12. package/dist-cjs/shapes/TLDrawShape.js +37 -4
  13. package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
  14. package/dist-cjs/shapes/TLEmbedShape.js +17 -0
  15. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  16. package/dist-cjs/shapes/TLGeoShape.js +12 -1
  17. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  18. package/dist-cjs/shapes/TLHighlightShape.js +29 -2
  19. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  20. package/dist-cjs/shapes/TLNoteShape.js +12 -1
  21. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  22. package/dist-cjs/shapes/TLTextShape.js +12 -1
  23. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  24. package/dist-cjs/store-migrations.js +14 -14
  25. package/dist-cjs/store-migrations.js.map +2 -2
  26. package/dist-esm/index.d.mts +82 -34
  27. package/dist-esm/index.mjs +5 -1
  28. package/dist-esm/index.mjs.map +2 -2
  29. package/dist-esm/misc/TLOpacity.mjs +1 -5
  30. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  31. package/dist-esm/misc/TLRichText.mjs +5 -1
  32. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  33. package/dist-esm/misc/b64Vecs.mjs +204 -0
  34. package/dist-esm/misc/b64Vecs.mjs.map +7 -0
  35. package/dist-esm/shapes/TLArrowShape.mjs +30 -13
  36. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  37. package/dist-esm/shapes/TLDrawShape.mjs +37 -4
  38. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  39. package/dist-esm/shapes/TLEmbedShape.mjs +17 -0
  40. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  41. package/dist-esm/shapes/TLGeoShape.mjs +12 -1
  42. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  43. package/dist-esm/shapes/TLHighlightShape.mjs +29 -2
  44. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  45. package/dist-esm/shapes/TLNoteShape.mjs +12 -1
  46. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  47. package/dist-esm/shapes/TLTextShape.mjs +12 -1
  48. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  49. package/dist-esm/store-migrations.mjs +14 -14
  50. package/dist-esm/store-migrations.mjs.map +2 -2
  51. package/package.json +8 -8
  52. package/src/__tests__/migrationTestUtils.ts +9 -3
  53. package/src/index.ts +3 -0
  54. package/src/migrations.test.ts +149 -1
  55. package/src/misc/TLOpacity.ts +1 -5
  56. package/src/misc/TLRichText.ts +6 -1
  57. package/src/misc/b64Vecs.ts +308 -0
  58. package/src/shapes/TLArrowShape.ts +36 -13
  59. package/src/shapes/TLDrawShape.ts +59 -12
  60. package/src/shapes/TLEmbedShape.ts +17 -0
  61. package/src/shapes/TLGeoShape.ts +14 -1
  62. package/src/shapes/TLHighlightShape.ts +37 -0
  63. package/src/shapes/TLNoteShape.ts +15 -1
  64. package/src/shapes/TLTextShape.ts +16 -2
  65. package/src/store-migrations.ts +15 -15
  66. package/src/assets/TLBookmarkAsset.test.ts +0 -96
  67. package/src/assets/TLImageAsset.test.ts +0 -213
  68. package/src/assets/TLVideoAsset.test.ts +0 -105
  69. package/src/bindings/TLArrowBinding.test.ts +0 -55
  70. package/src/misc/id-validator.test.ts +0 -50
  71. package/src/records/TLAsset.test.ts +0 -234
  72. package/src/records/TLBinding.test.ts +0 -22
  73. package/src/records/TLCamera.test.ts +0 -19
  74. package/src/records/TLDocument.test.ts +0 -35
  75. package/src/records/TLInstance.test.ts +0 -201
  76. package/src/records/TLPage.test.ts +0 -110
  77. package/src/records/TLPageState.test.ts +0 -228
  78. package/src/records/TLPointer.test.ts +0 -63
  79. package/src/records/TLPresence.test.ts +0 -190
  80. package/src/records/TLRecord.test.ts +0 -82
  81. package/src/records/TLShape.test.ts +0 -232
  82. package/src/shapes/ShapeWithCrop.test.ts +0 -18
  83. package/src/shapes/TLArrowShape.test.ts +0 -505
  84. package/src/shapes/TLBaseShape.test.ts +0 -142
  85. package/src/shapes/TLBookmarkShape.test.ts +0 -122
  86. package/src/shapes/TLDrawShape.test.ts +0 -177
  87. package/src/shapes/TLEmbedShape.test.ts +0 -286
  88. package/src/shapes/TLFrameShape.test.ts +0 -71
  89. package/src/shapes/TLGeoShape.test.ts +0 -247
  90. package/src/shapes/TLGroupShape.test.ts +0 -59
  91. package/src/shapes/TLHighlightShape.test.ts +0 -325
  92. package/src/shapes/TLImageShape.test.ts +0 -534
  93. package/src/shapes/TLLineShape.test.ts +0 -269
  94. package/src/shapes/TLNoteShape.test.ts +0 -1568
  95. package/src/shapes/TLTextShape.test.ts +0 -407
  96. package/src/shapes/TLVideoShape.test.ts +0 -112
  97. package/src/styles/TLColorStyle.test.ts +0 -439
@@ -1,234 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { getTestMigration } from '../__tests__/migrationTestUtils'
3
- import {
4
- assetMigrations,
5
- AssetRecordType,
6
- assetValidator,
7
- assetVersions,
8
- TLAssetId,
9
- } from './TLAsset'
10
-
11
- describe('TLAsset', () => {
12
- describe('assetValidator', () => {
13
- it('should validate different asset types using discriminated union', () => {
14
- const imageAsset = {
15
- id: 'asset:test_image',
16
- typeName: 'asset' as const,
17
- type: 'image' as const,
18
- props: {
19
- w: 100,
20
- h: 100,
21
- name: 'test.jpg',
22
- isAnimated: false,
23
- mimeType: 'image/jpeg',
24
- src: 'https://example.com/test.jpg',
25
- },
26
- meta: {},
27
- }
28
-
29
- const videoAsset = {
30
- id: 'asset:test_video',
31
- typeName: 'asset' as const,
32
- type: 'video' as const,
33
- props: {
34
- w: 640,
35
- h: 480,
36
- name: 'test.mp4',
37
- isAnimated: true,
38
- mimeType: 'video/mp4',
39
- src: 'https://example.com/test.mp4',
40
- },
41
- meta: {},
42
- }
43
-
44
- const bookmarkAsset = {
45
- id: 'asset:test_bookmark',
46
- typeName: 'asset' as const,
47
- type: 'bookmark' as const,
48
- props: {
49
- title: 'Test Site',
50
- description: 'A test bookmark',
51
- image: 'https://example.com/image.png',
52
- favicon: 'https://example.com/favicon.ico',
53
- src: 'https://example.com',
54
- },
55
- meta: {},
56
- }
57
-
58
- expect(() => assetValidator.validate(imageAsset)).not.toThrow()
59
- expect(() => assetValidator.validate(videoAsset)).not.toThrow()
60
- expect(() => assetValidator.validate(bookmarkAsset)).not.toThrow()
61
-
62
- expect(assetValidator.validate(imageAsset).type).toBe('image')
63
- expect(assetValidator.validate(videoAsset).type).toBe('video')
64
- expect(assetValidator.validate(bookmarkAsset).type).toBe('bookmark')
65
- })
66
-
67
- it('should reject invalid asset types', () => {
68
- const invalidAsset = {
69
- id: 'asset:invalid',
70
- typeName: 'asset' as const,
71
- type: 'invalid_type' as any,
72
- props: {
73
- w: 100,
74
- h: 100,
75
- name: 'test.jpg',
76
- },
77
- meta: {},
78
- }
79
-
80
- expect(() => assetValidator.validate(invalidAsset)).toThrow()
81
- })
82
-
83
- it('should require valid asset structure', () => {
84
- // Wrong typeName
85
- const wrongTypeName = {
86
- id: 'asset:wrong_typename',
87
- typeName: 'shape' as const,
88
- type: 'image' as const,
89
- props: {
90
- w: 100,
91
- h: 100,
92
- name: 'test.jpg',
93
- isAnimated: false,
94
- mimeType: 'image/jpeg',
95
- src: 'https://example.com/test.jpg',
96
- },
97
- meta: {},
98
- }
99
- expect(() => assetValidator.validate(wrongTypeName)).toThrow()
100
-
101
- // Missing meta
102
- const noMeta = {
103
- id: 'asset:no_meta',
104
- typeName: 'asset' as const,
105
- type: 'image' as const,
106
- props: {
107
- w: 100,
108
- h: 100,
109
- name: 'test.jpg',
110
- isAnimated: false,
111
- mimeType: 'image/jpeg',
112
- src: 'https://example.com/test.jpg',
113
- },
114
- }
115
- expect(() => assetValidator.validate(noMeta)).toThrow()
116
-
117
- // Wrong ID prefix
118
- const wrongId = {
119
- id: 'shape:wrong_prefix',
120
- typeName: 'asset' as const,
121
- type: 'image' as const,
122
- props: {
123
- w: 100,
124
- h: 100,
125
- name: 'test.jpg',
126
- isAnimated: false,
127
- mimeType: 'image/jpeg',
128
- src: 'https://example.com/test.jpg',
129
- },
130
- meta: {},
131
- }
132
- expect(() => assetValidator.validate(wrongId)).toThrow()
133
- })
134
- })
135
-
136
- describe('assetMigrations', () => {
137
- it('should have correct migration structure', () => {
138
- expect(assetMigrations.sequenceId).toBe('com.tldraw.asset')
139
- expect(assetMigrations.sequence).toHaveLength(1)
140
- expect(assetMigrations.sequence[0].id).toBe(assetVersions.AddMeta)
141
- })
142
- })
143
-
144
- describe('AddMeta migration', () => {
145
- const { up } = getTestMigration(assetVersions.AddMeta)
146
-
147
- it('should add empty meta property', () => {
148
- const assetWithoutMeta = {
149
- id: 'asset:test',
150
- typeName: 'asset',
151
- type: 'image',
152
- props: {
153
- w: 100,
154
- h: 100,
155
- name: 'test.jpg',
156
- isAnimated: false,
157
- mimeType: 'image/jpeg',
158
- src: 'https://example.com/test.jpg',
159
- },
160
- }
161
-
162
- const result = up(assetWithoutMeta)
163
- expect(result.meta).toEqual({})
164
- expect(result.props).toEqual(assetWithoutMeta.props)
165
- })
166
-
167
- it('should overwrite existing meta property', () => {
168
- const assetWithMeta = {
169
- id: 'asset:test',
170
- typeName: 'asset',
171
- type: 'image',
172
- props: {
173
- w: 100,
174
- h: 100,
175
- name: 'test.jpg',
176
- isAnimated: false,
177
- mimeType: 'image/jpeg',
178
- src: 'https://example.com/test.jpg',
179
- },
180
- meta: { existing: 'data' },
181
- }
182
-
183
- const result = up(assetWithMeta)
184
- expect(result.meta).toEqual({}) // Migration always sets to empty object
185
- })
186
-
187
- it('should preserve other properties during migration', () => {
188
- const assetWithExtraProps = {
189
- id: 'asset:extra_props',
190
- typeName: 'asset',
191
- type: 'image',
192
- props: {
193
- w: 200,
194
- h: 150,
195
- name: 'extra.png',
196
- isAnimated: false,
197
- mimeType: 'image/png',
198
- src: 'https://example.com/extra.png',
199
- },
200
- customProperty: 'should be preserved',
201
- }
202
-
203
- const result = up(assetWithExtraProps)
204
- expect(result.meta).toEqual({})
205
- expect(result.customProperty).toBe('should be preserved')
206
- })
207
- })
208
-
209
- describe('AssetRecordType', () => {
210
- it('should have correct configuration', () => {
211
- expect(AssetRecordType.typeName).toBe('asset')
212
- expect(AssetRecordType.scope).toBe('document')
213
- expect(AssetRecordType.validator).toBe(assetValidator)
214
- })
215
-
216
- it('should create records with default meta property', () => {
217
- const assetRecord = AssetRecordType.create({
218
- id: 'asset:test' as TLAssetId,
219
- type: 'image',
220
- props: {
221
- w: 100,
222
- h: 100,
223
- name: 'test.jpg',
224
- isAnimated: false,
225
- mimeType: 'image/jpeg',
226
- src: 'https://example.com/test.jpg',
227
- },
228
- })
229
-
230
- expect(assetRecord.meta).toEqual({})
231
- expect(assetRecord.typeName).toBe('asset')
232
- })
233
- })
234
- })
@@ -1,22 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { createBindingId, rootBindingMigrations } from './TLBinding'
3
-
4
- describe('TLBinding', () => {
5
- describe('createBindingId function', () => {
6
- it('should generate IDs starting with binding:', () => {
7
- const id = createBindingId()
8
- expect(id.startsWith('binding:')).toBe(true)
9
- })
10
-
11
- it('should use custom ID when provided', () => {
12
- expect(createBindingId('test')).toBe('binding:test')
13
- })
14
- })
15
-
16
- describe('rootBindingMigrations', () => {
17
- it('should have correct structure', () => {
18
- expect(rootBindingMigrations.sequenceId).toBe('com.tldraw.binding')
19
- expect(Array.isArray(rootBindingMigrations.sequence)).toBe(true)
20
- })
21
- })
22
- })
@@ -1,19 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { cameraMigrations, cameraVersions } from './TLCamera'
3
-
4
- describe('cameraMigrations', () => {
5
- it('should apply AddMeta migration correctly', () => {
6
- const addMetaMigration = cameraMigrations.sequence.find((m) => m.id === cameraVersions.AddMeta)!
7
-
8
- const oldRecord: any = {
9
- typeName: 'camera',
10
- id: 'camera:test',
11
- x: 100,
12
- y: 200,
13
- z: 0.5,
14
- }
15
-
16
- addMetaMigration.up(oldRecord)
17
- expect(oldRecord.meta).toEqual({})
18
- })
19
- })
@@ -1,35 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { documentMigrations, documentVersions, TLDOCUMENT_ID } from './TLDocument'
3
-
4
- describe('documentMigrations', () => {
5
- it('should apply AddName migration correctly', () => {
6
- const addNameMigration = documentMigrations.sequence.find(
7
- (m) => m.id === documentVersions.AddName
8
- )!
9
-
10
- const oldRecord: any = {
11
- typeName: 'document',
12
- id: TLDOCUMENT_ID,
13
- gridSize: 10,
14
- }
15
-
16
- addNameMigration.up(oldRecord)
17
- expect(oldRecord.name).toBe('')
18
- })
19
-
20
- it('should apply AddMeta migration correctly', () => {
21
- const addMetaMigration = documentMigrations.sequence.find(
22
- (m) => m.id === documentVersions.AddMeta
23
- )!
24
-
25
- const oldRecord: any = {
26
- typeName: 'document',
27
- id: TLDOCUMENT_ID,
28
- gridSize: 10,
29
- name: 'Test',
30
- }
31
-
32
- addMetaMigration.up(oldRecord)
33
- expect(oldRecord.meta).toEqual({})
34
- })
35
- })
@@ -1,201 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { StyleProp } from '../styles/StyleProp'
3
- import {
4
- createInstanceRecordType,
5
- instanceIdValidator,
6
- instanceMigrations,
7
- instanceVersions,
8
- pluckPreservingValues,
9
- shouldKeyBePreservedBetweenSessions,
10
- TLInstance,
11
- TLINSTANCE_ID,
12
- } from './TLInstance'
13
-
14
- // Mock style prop for testing
15
- const mockColorStyle = {
16
- type: 'color',
17
- defaultValue: 'black',
18
- getDefaultValue: () => 'black',
19
- } as unknown as StyleProp<string>
20
-
21
- const mockStylesMap = new Map([['color', mockColorStyle]])
22
- createInstanceRecordType(mockStylesMap)
23
-
24
- describe('shouldKeyBePreservedBetweenSessions', () => {
25
- it('should preserve user preferences', () => {
26
- const userPreferences = [
27
- 'isFocusMode',
28
- 'isDebugMode',
29
- 'isToolLocked',
30
- 'exportBackground',
31
- 'isGridMode',
32
- 'isReadonly',
33
- ]
34
-
35
- userPreferences.forEach((key) => {
36
- expect(
37
- shouldKeyBePreservedBetweenSessions[key as keyof typeof shouldKeyBePreservedBetweenSessions]
38
- ).toBe(true)
39
- })
40
- })
41
-
42
- it('should not preserve temporary state', () => {
43
- const temporaryState = [
44
- 'currentPageId',
45
- 'opacityForNextShape',
46
- 'stylesForNextShape',
47
- 'followingUserId',
48
- 'brush',
49
- 'cursor',
50
- 'scribbles',
51
- 'zoomBrush',
52
- 'chatMessage',
53
- 'isChatting',
54
- 'isPenMode',
55
- 'isHoveringCanvas',
56
- 'openMenus',
57
- 'isChangingStyle',
58
- 'duplicateProps',
59
- ]
60
-
61
- temporaryState.forEach((key) => {
62
- expect(
63
- shouldKeyBePreservedBetweenSessions[key as keyof typeof shouldKeyBePreservedBetweenSessions]
64
- ).toBe(false)
65
- })
66
- })
67
- })
68
-
69
- describe('pluckPreservingValues', () => {
70
- it('should return null for null or undefined input', () => {
71
- expect(pluckPreservingValues(null)).toBe(null)
72
- expect(pluckPreservingValues(undefined)).toBe(null)
73
- })
74
-
75
- it('should filter properties according to preservation rules', () => {
76
- const fullInstance: TLInstance = {
77
- id: TLINSTANCE_ID,
78
- typeName: 'instance',
79
- currentPageId: 'page:page1' as any,
80
- opacityForNextShape: 0.5,
81
- stylesForNextShape: {},
82
- followingUserId: null,
83
- highlightedUserIds: [],
84
- brush: null,
85
- cursor: { type: 'default', rotation: 0 },
86
- scribbles: [],
87
- isFocusMode: true,
88
- isDebugMode: false,
89
- isToolLocked: true,
90
- exportBackground: true,
91
- screenBounds: { x: 0, y: 0, w: 1920, h: 1080 },
92
- insets: [false, false, false, false],
93
- zoomBrush: null,
94
- chatMessage: '',
95
- isChatting: false,
96
- isPenMode: false,
97
- isGridMode: true,
98
- isFocused: true,
99
- devicePixelRatio: 2,
100
- isCoarsePointer: false,
101
- isHoveringCanvas: null,
102
- openMenus: [],
103
- isChangingStyle: false,
104
- isReadonly: false,
105
- meta: {},
106
- duplicateProps: null,
107
- }
108
-
109
- const preserved = pluckPreservingValues(fullInstance)
110
-
111
- // Should preserve user preferences
112
- expect(preserved?.isFocusMode).toBe(true)
113
- expect(preserved?.isDebugMode).toBe(false)
114
- expect(preserved?.isToolLocked).toBe(true)
115
- expect(preserved?.exportBackground).toBe(true)
116
- expect(preserved?.isGridMode).toBe(true)
117
-
118
- // Should not preserve temporary state
119
- expect(preserved?.currentPageId).toBeUndefined()
120
- expect(preserved?.opacityForNextShape).toBeUndefined()
121
- expect(preserved?.brush).toBeUndefined()
122
- expect(preserved?.cursor).toBeUndefined()
123
- expect(preserved?.chatMessage).toBeUndefined()
124
- expect(preserved?.openMenus).toBeUndefined()
125
- })
126
- })
127
-
128
- describe('instanceIdValidator', () => {
129
- it('should validate correct instance IDs and reject invalid ones', () => {
130
- expect(() => instanceIdValidator.validate('instance:instance')).not.toThrow()
131
- expect(() => instanceIdValidator.validate('instance:test')).not.toThrow()
132
- expect(() => instanceIdValidator.validate(TLINSTANCE_ID)).not.toThrow()
133
-
134
- expect(() => instanceIdValidator.validate('invalid')).toThrow()
135
- expect(() => instanceIdValidator.validate('page:instance')).toThrow()
136
- expect(() => instanceIdValidator.validate('')).toThrow()
137
- })
138
- })
139
-
140
- describe('createInstanceRecordType', () => {
141
- it('should create a valid record type with correct configuration', () => {
142
- const recordType = createInstanceRecordType(mockStylesMap)
143
- expect(recordType.typeName).toBe('instance')
144
- expect(recordType.scope).toBe('session')
145
- })
146
- })
147
-
148
- describe('instanceMigrations', () => {
149
- it('should have correct migration configuration', () => {
150
- expect(instanceMigrations.sequenceId).toBe('com.tldraw.instance')
151
- expect(Array.isArray(instanceMigrations.sequence)).toBe(true)
152
- expect(instanceMigrations.sequence.length).toBe(25)
153
- })
154
-
155
- it('should migrate HoistOpacity correctly', () => {
156
- const migration = instanceMigrations.sequence.find(
157
- (m) => m.id === instanceVersions.HoistOpacity
158
- )!
159
- const oldRecord: any = {
160
- id: TLINSTANCE_ID,
161
- typeName: 'instance',
162
- propsForNextShape: {
163
- opacity: '0.5',
164
- color: 'red',
165
- },
166
- }
167
- const result = migration.up(oldRecord)
168
-
169
- expect((result as any).opacityForNextShape).toBe(0.5)
170
- expect((result as any).propsForNextShape.opacity).toBeUndefined()
171
- expect((result as any).propsForNextShape.color).toBe('red')
172
- })
173
-
174
- it('should migrate RemoveDialog correctly', () => {
175
- const migration = instanceMigrations.sequence.find(
176
- (m) => m.id === instanceVersions.RemoveDialog
177
- )!
178
- const oldRecord: any = {
179
- id: TLINSTANCE_ID,
180
- typeName: 'instance',
181
- dialog: 'some-dialog',
182
- otherProp: 'keep-me',
183
- }
184
- const result = migration.up(oldRecord)
185
-
186
- expect((result as any).dialog).toBeUndefined()
187
- expect((result as any).otherProp).toBe('keep-me')
188
- })
189
-
190
- it('should have bidirectional migrations where applicable', () => {
191
- const addInsetMigration = instanceMigrations.sequence.find(
192
- (m) => m.id === instanceVersions.AddInset
193
- )!
194
- expect(addInsetMigration.down).toBeDefined()
195
-
196
- const removeCameraMigration = instanceMigrations.sequence.find(
197
- (m) => m.id === instanceVersions.RemoveCanMoveCamera
198
- )!
199
- expect(removeCameraMigration.down).toBeDefined()
200
- })
201
- })
@@ -1,110 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import {
3
- isPageId,
4
- pageIdValidator,
5
- pageMigrations,
6
- PageRecordType,
7
- pageValidator,
8
- pageVersions,
9
- TLPageId,
10
- } from './TLPage'
11
-
12
- describe('pageIdValidator', () => {
13
- it('should validate correct page IDs', () => {
14
- expect(() => pageIdValidator.validate('page:main')).not.toThrow()
15
- expect(() => pageIdValidator.validate('page:page1')).not.toThrow()
16
- })
17
-
18
- it('should reject invalid page IDs', () => {
19
- expect(() => pageIdValidator.validate('invalid')).toThrow()
20
- expect(() => pageIdValidator.validate('shape:page1')).toThrow()
21
- expect(() => pageIdValidator.validate('')).toThrow()
22
- })
23
- })
24
-
25
- describe('pageValidator', () => {
26
- it('should validate valid page records', () => {
27
- const validPage = {
28
- typeName: 'page',
29
- id: 'page:test' as TLPageId,
30
- name: 'Test Page',
31
- index: 'a1' as any,
32
- meta: {},
33
- }
34
-
35
- expect(() => pageValidator.validate(validPage)).not.toThrow()
36
- })
37
-
38
- it('should reject pages with invalid typeName', () => {
39
- const invalidPage = {
40
- typeName: 'not-page',
41
- id: 'page:test' as TLPageId,
42
- name: 'Test',
43
- index: 'a1' as any,
44
- meta: {},
45
- }
46
-
47
- expect(() => pageValidator.validate(invalidPage)).toThrow()
48
- })
49
-
50
- it('should reject pages with missing required fields', () => {
51
- const incompletePages = [
52
- {
53
- typeName: 'page',
54
- id: 'page:test' as TLPageId,
55
- // missing name, index, meta
56
- },
57
- {
58
- typeName: 'page',
59
- id: 'page:test' as TLPageId,
60
- name: 'Test',
61
- // missing index, meta
62
- },
63
- ]
64
-
65
- incompletePages.forEach((page) => {
66
- expect(() => pageValidator.validate(page)).toThrow()
67
- })
68
- })
69
- })
70
-
71
- describe('pageMigrations', () => {
72
- it('should apply AddMeta migration correctly', () => {
73
- const addMetaMigration = pageMigrations.sequence.find((m) => m.id === pageVersions.AddMeta)!
74
-
75
- const oldRecord: any = {
76
- typeName: 'page',
77
- id: 'page:test',
78
- name: 'Test Page',
79
- index: 'a1',
80
- }
81
-
82
- addMetaMigration.up(oldRecord)
83
- expect(oldRecord.meta).toEqual({})
84
- })
85
- })
86
-
87
- describe('PageRecordType', () => {
88
- it('should create page records with defaults', () => {
89
- const page = PageRecordType.create({
90
- id: 'page:test' as TLPageId,
91
- name: 'Test Page',
92
- index: 'a1' as any,
93
- })
94
-
95
- expect(page.meta).toEqual({})
96
- })
97
- })
98
-
99
- describe('isPageId', () => {
100
- it('should return true for valid page IDs', () => {
101
- expect(isPageId('page:main')).toBe(true)
102
- expect(isPageId('page:page1')).toBe(true)
103
- })
104
-
105
- it('should return false for invalid page IDs', () => {
106
- expect(isPageId('shape:main')).toBe(false)
107
- expect(isPageId('invalid')).toBe(false)
108
- expect(isPageId('')).toBe(false)
109
- })
110
- })