@tldraw/tlschema 4.1.0-canary.ccd6179e1cb2 → 4.1.0-canary.e23ee15a46bc

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 (212) hide show
  1. package/dist-cjs/TLStore.js +3 -10
  2. package/dist-cjs/TLStore.js.map +2 -2
  3. package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
  4. package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
  5. package/dist-cjs/assets/TLImageAsset.js.map +2 -2
  6. package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
  7. package/dist-cjs/bindings/TLArrowBinding.js.map +2 -2
  8. package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
  9. package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
  10. package/dist-cjs/createTLSchema.js.map +2 -2
  11. package/dist-cjs/index.d.ts +4412 -223
  12. package/dist-cjs/index.js +1 -1
  13. package/dist-cjs/index.js.map +2 -2
  14. package/dist-cjs/misc/TLColor.js.map +2 -2
  15. package/dist-cjs/misc/TLCursor.js.map +2 -2
  16. package/dist-cjs/misc/TLHandle.js.map +2 -2
  17. package/dist-cjs/misc/TLOpacity.js.map +2 -2
  18. package/dist-cjs/misc/TLRichText.js.map +2 -2
  19. package/dist-cjs/misc/TLScribble.js.map +2 -2
  20. package/dist-cjs/misc/geometry-types.js.map +2 -2
  21. package/dist-cjs/misc/id-validator.js.map +2 -2
  22. package/dist-cjs/records/TLAsset.js.map +2 -2
  23. package/dist-cjs/records/TLBinding.js.map +2 -2
  24. package/dist-cjs/records/TLCamera.js.map +2 -2
  25. package/dist-cjs/records/TLDocument.js.map +2 -2
  26. package/dist-cjs/records/TLInstance.js.map +2 -2
  27. package/dist-cjs/records/TLPage.js.map +2 -2
  28. package/dist-cjs/records/TLPageState.js.map +2 -2
  29. package/dist-cjs/records/TLPointer.js.map +2 -2
  30. package/dist-cjs/records/TLPresence.js.map +2 -2
  31. package/dist-cjs/records/TLRecord.js.map +1 -1
  32. package/dist-cjs/records/TLShape.js.map +2 -2
  33. package/dist-cjs/recordsWithProps.js.map +2 -2
  34. package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
  35. package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
  36. package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
  37. package/dist-cjs/shapes/TLBookmarkShape.js.map +2 -2
  38. package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
  39. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  40. package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
  41. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  42. package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
  43. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  44. package/dist-cjs/shapes/TLImageShape.js.map +2 -2
  45. package/dist-cjs/shapes/TLLineShape.js.map +2 -2
  46. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  47. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  48. package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
  49. package/dist-cjs/store-migrations.js.map +2 -2
  50. package/dist-cjs/styles/TLColorStyle.js.map +2 -2
  51. package/dist-cjs/styles/TLDashStyle.js.map +2 -2
  52. package/dist-cjs/styles/TLFillStyle.js.map +2 -2
  53. package/dist-cjs/styles/TLFontStyle.js.map +2 -2
  54. package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
  55. package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
  56. package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
  57. package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
  58. package/dist-cjs/translations/translations.js +1 -1
  59. package/dist-cjs/translations/translations.js.map +2 -2
  60. package/dist-cjs/util-types.js.map +1 -1
  61. package/dist-esm/TLStore.mjs +3 -10
  62. package/dist-esm/TLStore.mjs.map +2 -2
  63. package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
  64. package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
  65. package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
  66. package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
  67. package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
  68. package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
  69. package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
  70. package/dist-esm/createTLSchema.mjs.map +2 -2
  71. package/dist-esm/index.d.mts +4412 -223
  72. package/dist-esm/index.mjs +1 -1
  73. package/dist-esm/index.mjs.map +2 -2
  74. package/dist-esm/misc/TLColor.mjs.map +2 -2
  75. package/dist-esm/misc/TLCursor.mjs.map +2 -2
  76. package/dist-esm/misc/TLHandle.mjs.map +2 -2
  77. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  78. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  79. package/dist-esm/misc/TLScribble.mjs.map +2 -2
  80. package/dist-esm/misc/geometry-types.mjs.map +2 -2
  81. package/dist-esm/misc/id-validator.mjs.map +2 -2
  82. package/dist-esm/records/TLAsset.mjs.map +2 -2
  83. package/dist-esm/records/TLBinding.mjs.map +2 -2
  84. package/dist-esm/records/TLCamera.mjs.map +2 -2
  85. package/dist-esm/records/TLDocument.mjs.map +2 -2
  86. package/dist-esm/records/TLInstance.mjs.map +2 -2
  87. package/dist-esm/records/TLPage.mjs.map +2 -2
  88. package/dist-esm/records/TLPageState.mjs.map +2 -2
  89. package/dist-esm/records/TLPointer.mjs.map +2 -2
  90. package/dist-esm/records/TLPresence.mjs.map +2 -2
  91. package/dist-esm/records/TLShape.mjs.map +2 -2
  92. package/dist-esm/recordsWithProps.mjs.map +2 -2
  93. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  94. package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
  95. package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
  96. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  97. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  98. package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
  99. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  100. package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
  101. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  102. package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
  103. package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
  104. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  105. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  106. package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
  107. package/dist-esm/store-migrations.mjs.map +2 -2
  108. package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
  109. package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
  110. package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
  111. package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
  112. package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
  113. package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
  114. package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
  115. package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
  116. package/dist-esm/translations/translations.mjs +1 -1
  117. package/dist-esm/translations/translations.mjs.map +2 -2
  118. package/package.json +5 -5
  119. package/src/TLStore.test.ts +644 -0
  120. package/src/TLStore.ts +205 -20
  121. package/src/assets/TLBaseAsset.ts +90 -7
  122. package/src/assets/TLBookmarkAsset.test.ts +96 -0
  123. package/src/assets/TLBookmarkAsset.ts +52 -2
  124. package/src/assets/TLImageAsset.test.ts +213 -0
  125. package/src/assets/TLImageAsset.ts +60 -2
  126. package/src/assets/TLVideoAsset.test.ts +105 -0
  127. package/src/assets/TLVideoAsset.ts +93 -4
  128. package/src/bindings/TLArrowBinding.test.ts +55 -0
  129. package/src/bindings/TLArrowBinding.ts +132 -10
  130. package/src/bindings/TLBaseBinding.ts +140 -3
  131. package/src/createPresenceStateDerivation.test.ts +158 -0
  132. package/src/createPresenceStateDerivation.ts +71 -2
  133. package/src/createTLSchema.test.ts +181 -0
  134. package/src/createTLSchema.ts +164 -7
  135. package/src/index.ts +32 -0
  136. package/src/misc/TLColor.ts +50 -6
  137. package/src/misc/TLCursor.ts +110 -8
  138. package/src/misc/TLHandle.ts +82 -6
  139. package/src/misc/TLOpacity.ts +51 -2
  140. package/src/misc/TLRichText.ts +56 -3
  141. package/src/misc/TLScribble.ts +105 -5
  142. package/src/misc/geometry-types.ts +30 -2
  143. package/src/misc/id-validator.test.ts +50 -0
  144. package/src/misc/id-validator.ts +20 -1
  145. package/src/records/TLAsset.test.ts +234 -0
  146. package/src/records/TLAsset.ts +165 -8
  147. package/src/records/TLBinding.test.ts +22 -0
  148. package/src/records/TLBinding.ts +277 -11
  149. package/src/records/TLCamera.test.ts +19 -0
  150. package/src/records/TLCamera.ts +118 -7
  151. package/src/records/TLDocument.test.ts +35 -0
  152. package/src/records/TLDocument.ts +148 -8
  153. package/src/records/TLInstance.test.ts +201 -0
  154. package/src/records/TLInstance.ts +117 -9
  155. package/src/records/TLPage.test.ts +110 -0
  156. package/src/records/TLPage.ts +106 -8
  157. package/src/records/TLPageState.test.ts +228 -0
  158. package/src/records/TLPageState.ts +88 -7
  159. package/src/records/TLPointer.test.ts +63 -0
  160. package/src/records/TLPointer.ts +105 -7
  161. package/src/records/TLPresence.test.ts +190 -0
  162. package/src/records/TLPresence.ts +99 -5
  163. package/src/records/TLRecord.test.ts +70 -0
  164. package/src/records/TLRecord.ts +43 -1
  165. package/src/records/TLShape.test.ts +232 -0
  166. package/src/records/TLShape.ts +289 -12
  167. package/src/recordsWithProps.test.ts +188 -0
  168. package/src/recordsWithProps.ts +131 -2
  169. package/src/shapes/ShapeWithCrop.test.ts +18 -0
  170. package/src/shapes/ShapeWithCrop.ts +64 -2
  171. package/src/shapes/TLArrowShape.test.ts +505 -0
  172. package/src/shapes/TLArrowShape.ts +188 -10
  173. package/src/shapes/TLBaseShape.test.ts +142 -0
  174. package/src/shapes/TLBaseShape.ts +103 -4
  175. package/src/shapes/TLBookmarkShape.test.ts +122 -0
  176. package/src/shapes/TLBookmarkShape.ts +58 -4
  177. package/src/shapes/TLDrawShape.test.ts +177 -0
  178. package/src/shapes/TLDrawShape.ts +97 -6
  179. package/src/shapes/TLEmbedShape.test.ts +286 -0
  180. package/src/shapes/TLEmbedShape.ts +57 -4
  181. package/src/shapes/TLFrameShape.test.ts +71 -0
  182. package/src/shapes/TLFrameShape.ts +59 -4
  183. package/src/shapes/TLGeoShape.test.ts +247 -0
  184. package/src/shapes/TLGeoShape.ts +103 -7
  185. package/src/shapes/TLGroupShape.test.ts +59 -0
  186. package/src/shapes/TLGroupShape.ts +52 -4
  187. package/src/shapes/TLHighlightShape.test.ts +325 -0
  188. package/src/shapes/TLHighlightShape.ts +79 -4
  189. package/src/shapes/TLImageShape.test.ts +534 -0
  190. package/src/shapes/TLImageShape.ts +105 -5
  191. package/src/shapes/TLLineShape.test.ts +269 -0
  192. package/src/shapes/TLLineShape.ts +128 -8
  193. package/src/shapes/TLNoteShape.test.ts +1568 -0
  194. package/src/shapes/TLNoteShape.ts +97 -4
  195. package/src/shapes/TLTextShape.test.ts +407 -0
  196. package/src/shapes/TLTextShape.ts +94 -4
  197. package/src/shapes/TLVideoShape.test.ts +112 -0
  198. package/src/shapes/TLVideoShape.ts +99 -4
  199. package/src/store-migrations.test.ts +88 -0
  200. package/src/store-migrations.ts +47 -1
  201. package/src/styles/TLColorStyle.test.ts +439 -0
  202. package/src/styles/TLColorStyle.ts +228 -10
  203. package/src/styles/TLDashStyle.ts +54 -2
  204. package/src/styles/TLFillStyle.ts +54 -2
  205. package/src/styles/TLFontStyle.ts +72 -3
  206. package/src/styles/TLHorizontalAlignStyle.ts +55 -2
  207. package/src/styles/TLSizeStyle.ts +54 -2
  208. package/src/styles/TLTextAlignStyle.ts +52 -2
  209. package/src/styles/TLVerticalAlignStyle.ts +52 -2
  210. package/src/translations/translations.test.ts +378 -35
  211. package/src/translations/translations.ts +157 -10
  212. package/src/util-types.ts +51 -1
@@ -0,0 +1,213 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
3
+ import { imageAssetVersions } from './TLImageAsset'
4
+
5
+ describe('TLImageAsset', () => {
6
+ describe('AddIsAnimated migration', () => {
7
+ const { up, down } = getTestMigration(imageAssetVersions.AddIsAnimated)
8
+
9
+ it('should add isAnimated property in up migration', () => {
10
+ const assetWithoutIsAnimated = {
11
+ id: 'asset:image1',
12
+ type: 'image',
13
+ props: {
14
+ w: 100,
15
+ h: 100,
16
+ name: 'test.jpg',
17
+ mimeType: 'image/jpeg',
18
+ src: 'https://example.com/test.jpg',
19
+ },
20
+ }
21
+
22
+ const result = up(assetWithoutIsAnimated)
23
+ expect(result.props.isAnimated).toBe(false)
24
+ })
25
+
26
+ it('should remove isAnimated property in down migration', () => {
27
+ const assetWithIsAnimated = {
28
+ id: 'asset:image3',
29
+ type: 'image',
30
+ props: {
31
+ w: 100,
32
+ h: 100,
33
+ name: 'test.jpg',
34
+ mimeType: 'image/jpeg',
35
+ src: 'https://example.com/test.jpg',
36
+ isAnimated: false,
37
+ },
38
+ }
39
+
40
+ const result = down(assetWithIsAnimated)
41
+ expect(result.props).not.toHaveProperty('isAnimated')
42
+ })
43
+ })
44
+
45
+ describe('RenameWidthHeight migration', () => {
46
+ const { up, down } = getTestMigration(imageAssetVersions.RenameWidthHeight)
47
+
48
+ it('should rename width and height to w and h in up migration', () => {
49
+ const assetWithWidthHeight = {
50
+ id: 'asset:image1',
51
+ type: 'image',
52
+ props: {
53
+ width: 800,
54
+ height: 600,
55
+ name: 'test.jpg',
56
+ isAnimated: false,
57
+ mimeType: 'image/jpeg',
58
+ src: 'https://example.com/test.jpg',
59
+ },
60
+ }
61
+
62
+ const result = up(assetWithWidthHeight)
63
+ expect(result.props.w).toBe(800)
64
+ expect(result.props.h).toBe(600)
65
+ expect(result.props).not.toHaveProperty('width')
66
+ expect(result.props).not.toHaveProperty('height')
67
+ })
68
+
69
+ it('should rename w and h to width and height in down migration', () => {
70
+ const assetWithWH = {
71
+ id: 'asset:image3',
72
+ type: 'image',
73
+ props: {
74
+ w: 1024,
75
+ h: 768,
76
+ name: 'test.png',
77
+ isAnimated: false,
78
+ mimeType: 'image/png',
79
+ src: 'https://example.com/test.png',
80
+ },
81
+ }
82
+
83
+ const result = down(assetWithWH)
84
+ expect(result.props.width).toBe(1024)
85
+ expect(result.props.height).toBe(768)
86
+ expect(result.props).not.toHaveProperty('w')
87
+ expect(result.props).not.toHaveProperty('h')
88
+ })
89
+ })
90
+
91
+ describe('MakeUrlsValid migration', () => {
92
+ const { up, down: _down } = getTestMigration(imageAssetVersions.MakeUrlsValid)
93
+
94
+ it('should clean invalid src URLs in up migration', () => {
95
+ const assetWithInvalidSrc = {
96
+ id: 'asset:image1',
97
+ type: 'image',
98
+ props: {
99
+ w: 100,
100
+ h: 100,
101
+ name: 'test.jpg',
102
+ isAnimated: false,
103
+ mimeType: 'image/jpeg',
104
+ src: 'invalid-url-format',
105
+ },
106
+ }
107
+
108
+ const result = up(assetWithInvalidSrc)
109
+ expect(result.props.src).toBe('')
110
+ })
111
+
112
+ it('should preserve valid src URLs in up migration', () => {
113
+ const assetWithValidSrc = {
114
+ id: 'asset:image2',
115
+ type: 'image',
116
+ props: {
117
+ w: 100,
118
+ h: 100,
119
+ name: 'test.jpg',
120
+ isAnimated: false,
121
+ mimeType: 'image/jpeg',
122
+ src: 'https://example.com/test.jpg',
123
+ },
124
+ }
125
+
126
+ const result = up(assetWithValidSrc)
127
+ expect(result.props.src).toBe('https://example.com/test.jpg')
128
+ })
129
+ })
130
+
131
+ describe('AddFileSize migration', () => {
132
+ const { up, down } = getTestMigration(imageAssetVersions.AddFileSize)
133
+
134
+ it('should add fileSize property with -1 default in up migration', () => {
135
+ const assetWithoutFileSize = {
136
+ id: 'asset:image1',
137
+ type: 'image',
138
+ props: {
139
+ w: 100,
140
+ h: 100,
141
+ name: 'test.jpg',
142
+ isAnimated: false,
143
+ mimeType: 'image/jpeg',
144
+ src: 'https://example.com/test.jpg',
145
+ },
146
+ }
147
+
148
+ const result = up(assetWithoutFileSize)
149
+ expect(result.props.fileSize).toBe(-1)
150
+ })
151
+
152
+ it('should remove fileSize property in down migration', () => {
153
+ const assetWithFileSize = {
154
+ id: 'asset:image3',
155
+ type: 'image',
156
+ props: {
157
+ w: 100,
158
+ h: 100,
159
+ name: 'test.jpg',
160
+ isAnimated: false,
161
+ mimeType: 'image/jpeg',
162
+ src: 'https://example.com/test.jpg',
163
+ fileSize: 50000,
164
+ },
165
+ }
166
+
167
+ const result = down(assetWithFileSize)
168
+ expect(result.props).not.toHaveProperty('fileSize')
169
+ })
170
+ })
171
+
172
+ describe('MakeFileSizeOptional migration', () => {
173
+ const { up, down } = getTestMigration(imageAssetVersions.MakeFileSizeOptional)
174
+
175
+ it('should convert fileSize -1 to undefined in up migration', () => {
176
+ const assetWithNegativeFileSize = {
177
+ id: 'asset:image1',
178
+ type: 'image',
179
+ props: {
180
+ w: 100,
181
+ h: 100,
182
+ name: 'test.jpg',
183
+ isAnimated: false,
184
+ mimeType: 'image/jpeg',
185
+ src: 'https://example.com/test.jpg',
186
+ fileSize: -1,
187
+ },
188
+ }
189
+
190
+ const result = up(assetWithNegativeFileSize)
191
+ expect(result.props.fileSize).toBeUndefined()
192
+ })
193
+
194
+ it('should convert undefined fileSize to -1 in down migration', () => {
195
+ const assetWithUndefinedFileSize = {
196
+ id: 'asset:image5',
197
+ type: 'image',
198
+ props: {
199
+ w: 100,
200
+ h: 100,
201
+ name: 'test.jpg',
202
+ isAnimated: false,
203
+ mimeType: 'image/jpeg',
204
+ src: 'https://example.com/test.jpg',
205
+ fileSize: undefined,
206
+ },
207
+ }
208
+
209
+ const result = down(assetWithUndefinedFileSize)
210
+ expect(result.props.fileSize).toBe(-1)
211
+ })
212
+ })
213
+ })
@@ -20,7 +20,36 @@ export type TLImageAsset = TLBaseAsset<
20
20
  }
21
21
  >
22
22
 
23
- /** @public */
23
+ /**
24
+ * Validator for image assets. Validates the structure and properties of TLImageAsset records
25
+ * to ensure data integrity when image assets are stored or retrieved from the tldraw store.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { imageAssetValidator } from '@tldraw/tlschema'
30
+ *
31
+ * const imageAsset = {
32
+ * id: 'asset:image123',
33
+ * typeName: 'asset',
34
+ * type: 'image',
35
+ * props: {
36
+ * w: 800,
37
+ * h: 600,
38
+ * name: 'photo.jpg',
39
+ * isAnimated: false,
40
+ * mimeType: 'image/jpeg',
41
+ * src: 'https://example.com/photo.jpg',
42
+ * fileSize: 156000
43
+ * },
44
+ * meta: {}
45
+ * }
46
+ *
47
+ * // Validate the asset
48
+ * const isValid = imageAssetValidator.validate(imageAsset)
49
+ * ```
50
+ *
51
+ * @public
52
+ */
24
53
  export const imageAssetValidator: T.Validator<TLImageAsset> = createAssetValidator(
25
54
  'image',
26
55
  T.object({
@@ -42,9 +71,38 @@ const Versions = createMigrationIds('com.tldraw.asset.image', {
42
71
  MakeFileSizeOptional: 5,
43
72
  } as const)
44
73
 
74
+ /**
75
+ * Migration version identifiers for image assets. These define the different schema versions
76
+ * that image assets have gone through during the evolution of the tldraw data model.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { imageAssetVersions } from '@tldraw/tlschema'
81
+ *
82
+ * // Access specific version IDs
83
+ * console.log(imageAssetVersions.AddIsAnimated) // Version when isAnimated was added
84
+ * console.log(imageAssetVersions.RenameWidthHeight) // Version when width/height became w/h
85
+ * ```
86
+ *
87
+ * @public
88
+ */
45
89
  export { Versions as imageAssetVersions }
46
90
 
47
- /** @public */
91
+ /**
92
+ * Migration sequence for image assets. Handles the evolution of the image asset schema
93
+ * over time, providing both forward (up) and backward (down) migration functions to
94
+ * maintain compatibility across different versions of the tldraw data model.
95
+ *
96
+ * The sequence includes migrations for:
97
+ * - Adding the `isAnimated` property to track animated images
98
+ * - Renaming `width`/`height` properties to shorter `w`/`h` names
99
+ * - Ensuring URLs are valid format
100
+ * - Adding file size tracking
101
+ * - Making file size optional
102
+ *
103
+ *
104
+ * @public
105
+ */
48
106
  export const imageAssetMigrations = createRecordMigrationSequence({
49
107
  sequenceId: 'com.tldraw.asset.image',
50
108
  recordType: 'asset',
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
3
+ import { videoAssetVersions } from './TLVideoAsset'
4
+
5
+ describe('TLVideoAsset', () => {
6
+ describe('migrations', () => {
7
+ it('should handle AddIsAnimated migration', () => {
8
+ const { up, down } = getTestMigration(videoAssetVersions.AddIsAnimated)
9
+
10
+ const assetWithoutIsAnimated = {
11
+ id: 'asset:video1',
12
+ type: 'video',
13
+ props: {
14
+ w: 640,
15
+ h: 480,
16
+ name: 'test.mp4',
17
+ mimeType: 'video/mp4',
18
+ src: 'https://example.com/test.mp4',
19
+ },
20
+ }
21
+
22
+ const upResult = up(assetWithoutIsAnimated)
23
+ expect(upResult.props.isAnimated).toBe(false)
24
+
25
+ const downResult = down(upResult)
26
+ expect(downResult.props).not.toHaveProperty('isAnimated')
27
+ })
28
+
29
+ it('should handle RenameWidthHeight migration', () => {
30
+ const { up, down } = getTestMigration(videoAssetVersions.RenameWidthHeight)
31
+
32
+ const assetWithWidthHeight = {
33
+ id: 'asset:video1',
34
+ type: 'video',
35
+ props: {
36
+ width: 1920,
37
+ height: 1080,
38
+ name: 'test.mp4',
39
+ isAnimated: true,
40
+ mimeType: 'video/mp4',
41
+ src: 'https://example.com/test.mp4',
42
+ },
43
+ }
44
+
45
+ const upResult = up(assetWithWidthHeight)
46
+ expect(upResult.props.w).toBe(1920)
47
+ expect(upResult.props.h).toBe(1080)
48
+ expect(upResult.props).not.toHaveProperty('width')
49
+ expect(upResult.props).not.toHaveProperty('height')
50
+
51
+ const downResult = down(upResult)
52
+ expect(downResult.props.width).toBe(1920)
53
+ expect(downResult.props.height).toBe(1080)
54
+ expect(downResult.props).not.toHaveProperty('w')
55
+ expect(downResult.props).not.toHaveProperty('h')
56
+ })
57
+
58
+ it('should handle MakeUrlsValid migration', () => {
59
+ const { up } = getTestMigration(videoAssetVersions.MakeUrlsValid)
60
+
61
+ const assetWithInvalidSrc = {
62
+ id: 'asset:video1',
63
+ type: 'video',
64
+ props: {
65
+ w: 640,
66
+ h: 480,
67
+ name: 'test.mp4',
68
+ isAnimated: true,
69
+ mimeType: 'video/mp4',
70
+ src: 'invalid-url-format',
71
+ },
72
+ }
73
+
74
+ const result = up(assetWithInvalidSrc)
75
+ expect(result.props.src).toBe('')
76
+ })
77
+
78
+ it('should handle MakeFileSizeOptional migration', () => {
79
+ const { up, down } = getTestMigration(videoAssetVersions.MakeFileSizeOptional)
80
+
81
+ const assetWithNegativeFileSize = {
82
+ id: 'asset:video1',
83
+ type: 'video',
84
+ props: {
85
+ w: 640,
86
+ h: 480,
87
+ name: 'test.mp4',
88
+ isAnimated: true,
89
+ mimeType: 'video/mp4',
90
+ src: 'https://example.com/test.mp4',
91
+ fileSize: -1,
92
+ },
93
+ }
94
+
95
+ const upResult = up(assetWithNegativeFileSize)
96
+ expect(upResult.props.fileSize).toBeUndefined()
97
+
98
+ const downResult = down({
99
+ ...assetWithNegativeFileSize,
100
+ props: { ...assetWithNegativeFileSize.props, fileSize: undefined },
101
+ })
102
+ expect(downResult.props.fileSize).toBe(-1)
103
+ })
104
+ })
105
+ })
@@ -4,23 +4,83 @@ import { TLAsset } from '../records/TLAsset'
4
4
  import { TLBaseAsset, createAssetValidator } from './TLBaseAsset'
5
5
 
6
6
  /**
7
- * An asset used for videos, used by the TLVideoShape.
7
+ * An asset record representing video files that can be displayed in video shapes.
8
+ * Video assets store metadata about video files including dimensions, MIME type,
9
+ * animation status, and file source information. They are referenced by TLVideoShape
10
+ * instances to display video content on the canvas.
8
11
  *
9
- * @public */
12
+ * @example
13
+ * ```ts
14
+ * import { TLVideoAsset } from '@tldraw/tlschema'
15
+ *
16
+ * const videoAsset: TLVideoAsset = {
17
+ * id: 'asset:video123',
18
+ * typeName: 'asset',
19
+ * type: 'video',
20
+ * props: {
21
+ * w: 1920,
22
+ * h: 1080,
23
+ * name: 'my-video.mp4',
24
+ * isAnimated: true,
25
+ * mimeType: 'video/mp4',
26
+ * src: 'https://example.com/video.mp4',
27
+ * fileSize: 5242880
28
+ * },
29
+ * meta: {}
30
+ * }
31
+ * ```
32
+ *
33
+ * @public
34
+ */
10
35
  export type TLVideoAsset = TLBaseAsset<
11
36
  'video',
12
37
  {
38
+ /** The width of the video in pixels */
13
39
  w: number
40
+ /** The height of the video in pixels */
14
41
  h: number
42
+ /** The original filename or display name of the video */
15
43
  name: string
44
+ /** Whether the video contains animation/motion (true for most videos) */
16
45
  isAnimated: boolean
46
+ /** The MIME type of the video file (e.g., 'video/mp4', 'video/webm'), null if unknown */
17
47
  mimeType: string | null
48
+ /** The source URL or data URI for the video file, null if not yet available */
18
49
  src: string | null
50
+ /** The file size in bytes, optional for backward compatibility */
19
51
  fileSize?: number
20
52
  }
21
53
  >
22
54
 
23
- /** @public */
55
+ /**
56
+ * Runtime validator for TLVideoAsset records. This validator ensures that video asset
57
+ * data conforms to the expected structure and types, providing type safety at runtime.
58
+ * It validates dimensions, file metadata, and ensures URLs are properly formatted.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * import { videoAssetValidator } from '@tldraw/tlschema'
63
+ *
64
+ * // Validate a video asset object
65
+ * const validAsset = videoAssetValidator.validate({
66
+ * id: 'asset:video123',
67
+ * typeName: 'asset',
68
+ * type: 'video',
69
+ * props: {
70
+ * w: 1920,
71
+ * h: 1080,
72
+ * name: 'video.mp4',
73
+ * isAnimated: true,
74
+ * mimeType: 'video/mp4',
75
+ * src: 'https://example.com/video.mp4',
76
+ * fileSize: 1024000
77
+ * },
78
+ * meta: {}
79
+ * })
80
+ * ```
81
+ *
82
+ * @public
83
+ */
24
84
  export const videoAssetValidator: T.Validator<TLVideoAsset> = createAssetValidator(
25
85
  'video',
26
86
  T.object({
@@ -42,9 +102,38 @@ const Versions = createMigrationIds('com.tldraw.asset.video', {
42
102
  MakeFileSizeOptional: 5,
43
103
  } as const)
44
104
 
105
+ /**
106
+ * Version identifiers for video asset migration sequences. These versions track
107
+ * the evolution of the video asset schema over time, enabling proper data migration
108
+ * when the asset structure changes.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * import { videoAssetVersions } from '@tldraw/tlschema'
113
+ *
114
+ * // Check the current version of a specific migration
115
+ * console.log(videoAssetVersions.AddFileSize) // 4
116
+ * ```
117
+ *
118
+ * @public
119
+ */
45
120
  export { Versions as videoAssetVersions }
46
121
 
47
- /** @public */
122
+ /**
123
+ * Migration sequence for video assets that handles schema evolution over time.
124
+ * This sequence defines how video asset data should be transformed when upgrading
125
+ * or downgrading between different schema versions. Each migration step handles
126
+ * specific changes like adding properties, renaming fields, or changing data formats.
127
+ *
128
+ * The migrations handle:
129
+ * - Adding animation detection (isAnimated property)
130
+ * - Renaming width/height properties to w/h for consistency
131
+ * - Ensuring URL validity for src properties
132
+ * - Adding file size tracking
133
+ * - Making file size optional for backward compatibility
134
+ *
135
+ * @public
136
+ */
48
137
  export const videoAssetMigrations = createRecordMigrationSequence({
49
138
  sequenceId: 'com.tldraw.asset.video',
50
139
  recordType: 'asset',
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
3
+ import { arrowBindingVersions } from './TLArrowBinding'
4
+
5
+ describe('TLArrowBinding', () => {
6
+ describe('AddSnap migration', () => {
7
+ const { up, down } = getTestMigration(arrowBindingVersions.AddSnap)
8
+
9
+ it('should add snap property with default value "none"', () => {
10
+ const oldRecord = {
11
+ props: {
12
+ terminal: 'end',
13
+ normalizedAnchor: { x: 0.5, y: 0.5 },
14
+ isExact: true,
15
+ isPrecise: false,
16
+ },
17
+ }
18
+
19
+ const result = up(oldRecord)
20
+ expect(result.props.snap).toBe('none')
21
+ expect(result.props.terminal).toBe('end')
22
+ })
23
+
24
+ it('should remove snap property on down migration', () => {
25
+ const newRecord = {
26
+ props: {
27
+ terminal: 'end',
28
+ normalizedAnchor: { x: 0.5, y: 0.5 },
29
+ isExact: true,
30
+ isPrecise: false,
31
+ snap: 'center',
32
+ },
33
+ }
34
+
35
+ const result = down(newRecord)
36
+ expect(result.props.snap).toBeUndefined()
37
+ expect(result.props.terminal).toBe('end')
38
+ })
39
+
40
+ it('should be reversible', () => {
41
+ const originalRecord = {
42
+ props: {
43
+ terminal: 'start',
44
+ normalizedAnchor: { x: 0.5, y: 0.5 },
45
+ isExact: true,
46
+ isPrecise: false,
47
+ },
48
+ }
49
+
50
+ const upResult = up(originalRecord)
51
+ const downResult = down(upResult)
52
+ expect(downResult.props).toEqual(originalRecord.props)
53
+ })
54
+ })
55
+ })