@tldraw/tlschema 4.1.0-canary.8351372cc72b → 4.1.0-canary.8659480a0467

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 (214) 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 +4416 -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 +0 -10
  40. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  41. package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
  42. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  43. package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
  44. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  45. package/dist-cjs/shapes/TLImageShape.js.map +2 -2
  46. package/dist-cjs/shapes/TLLineShape.js.map +2 -2
  47. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  48. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  49. package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
  50. package/dist-cjs/store-migrations.js.map +2 -2
  51. package/dist-cjs/styles/TLColorStyle.js.map +2 -2
  52. package/dist-cjs/styles/TLDashStyle.js.map +2 -2
  53. package/dist-cjs/styles/TLFillStyle.js.map +2 -2
  54. package/dist-cjs/styles/TLFontStyle.js.map +2 -2
  55. package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
  56. package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
  57. package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
  58. package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
  59. package/dist-cjs/translations/translations.js +1 -1
  60. package/dist-cjs/translations/translations.js.map +2 -2
  61. package/dist-cjs/util-types.js.map +1 -1
  62. package/dist-esm/TLStore.mjs +3 -10
  63. package/dist-esm/TLStore.mjs.map +2 -2
  64. package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
  65. package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
  66. package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
  67. package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
  68. package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
  69. package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
  70. package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
  71. package/dist-esm/createTLSchema.mjs.map +2 -2
  72. package/dist-esm/index.d.mts +4416 -223
  73. package/dist-esm/index.mjs +1 -1
  74. package/dist-esm/index.mjs.map +2 -2
  75. package/dist-esm/misc/TLColor.mjs.map +2 -2
  76. package/dist-esm/misc/TLCursor.mjs.map +2 -2
  77. package/dist-esm/misc/TLHandle.mjs.map +2 -2
  78. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  79. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  80. package/dist-esm/misc/TLScribble.mjs.map +2 -2
  81. package/dist-esm/misc/geometry-types.mjs.map +2 -2
  82. package/dist-esm/misc/id-validator.mjs.map +2 -2
  83. package/dist-esm/records/TLAsset.mjs.map +2 -2
  84. package/dist-esm/records/TLBinding.mjs.map +2 -2
  85. package/dist-esm/records/TLCamera.mjs.map +2 -2
  86. package/dist-esm/records/TLDocument.mjs.map +2 -2
  87. package/dist-esm/records/TLInstance.mjs.map +2 -2
  88. package/dist-esm/records/TLPage.mjs.map +2 -2
  89. package/dist-esm/records/TLPageState.mjs.map +2 -2
  90. package/dist-esm/records/TLPointer.mjs.map +2 -2
  91. package/dist-esm/records/TLPresence.mjs.map +2 -2
  92. package/dist-esm/records/TLShape.mjs.map +2 -2
  93. package/dist-esm/recordsWithProps.mjs.map +2 -2
  94. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  95. package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
  96. package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
  97. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  98. package/dist-esm/shapes/TLEmbedShape.mjs +0 -10
  99. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  100. package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
  101. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  102. package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
  103. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  104. package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
  105. package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
  106. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  107. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  108. package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
  109. package/dist-esm/store-migrations.mjs.map +2 -2
  110. package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
  111. package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
  112. package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
  113. package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
  114. package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
  115. package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
  116. package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
  117. package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
  118. package/dist-esm/translations/translations.mjs +1 -1
  119. package/dist-esm/translations/translations.mjs.map +2 -2
  120. package/package.json +5 -5
  121. package/src/TLStore.test.ts +644 -0
  122. package/src/TLStore.ts +205 -20
  123. package/src/assets/TLBaseAsset.ts +90 -7
  124. package/src/assets/TLBookmarkAsset.test.ts +96 -0
  125. package/src/assets/TLBookmarkAsset.ts +52 -2
  126. package/src/assets/TLImageAsset.test.ts +213 -0
  127. package/src/assets/TLImageAsset.ts +60 -2
  128. package/src/assets/TLVideoAsset.test.ts +105 -0
  129. package/src/assets/TLVideoAsset.ts +93 -4
  130. package/src/bindings/TLArrowBinding.test.ts +55 -0
  131. package/src/bindings/TLArrowBinding.ts +132 -10
  132. package/src/bindings/TLBaseBinding.ts +140 -3
  133. package/src/createPresenceStateDerivation.test.ts +158 -0
  134. package/src/createPresenceStateDerivation.ts +71 -2
  135. package/src/createTLSchema.test.ts +181 -0
  136. package/src/createTLSchema.ts +164 -7
  137. package/src/index.ts +32 -0
  138. package/src/misc/TLColor.ts +50 -6
  139. package/src/misc/TLCursor.ts +110 -8
  140. package/src/misc/TLHandle.ts +86 -6
  141. package/src/misc/TLOpacity.ts +51 -2
  142. package/src/misc/TLRichText.ts +56 -3
  143. package/src/misc/TLScribble.ts +105 -5
  144. package/src/misc/geometry-types.ts +30 -2
  145. package/src/misc/id-validator.test.ts +50 -0
  146. package/src/misc/id-validator.ts +20 -1
  147. package/src/records/TLAsset.test.ts +234 -0
  148. package/src/records/TLAsset.ts +165 -8
  149. package/src/records/TLBinding.test.ts +22 -0
  150. package/src/records/TLBinding.ts +277 -11
  151. package/src/records/TLCamera.test.ts +19 -0
  152. package/src/records/TLCamera.ts +118 -7
  153. package/src/records/TLDocument.test.ts +35 -0
  154. package/src/records/TLDocument.ts +148 -8
  155. package/src/records/TLInstance.test.ts +201 -0
  156. package/src/records/TLInstance.ts +117 -9
  157. package/src/records/TLPage.test.ts +110 -0
  158. package/src/records/TLPage.ts +106 -8
  159. package/src/records/TLPageState.test.ts +228 -0
  160. package/src/records/TLPageState.ts +88 -7
  161. package/src/records/TLPointer.test.ts +63 -0
  162. package/src/records/TLPointer.ts +105 -7
  163. package/src/records/TLPresence.test.ts +190 -0
  164. package/src/records/TLPresence.ts +99 -5
  165. package/src/records/TLRecord.test.ts +70 -0
  166. package/src/records/TLRecord.ts +43 -1
  167. package/src/records/TLShape.test.ts +232 -0
  168. package/src/records/TLShape.ts +289 -12
  169. package/src/recordsWithProps.test.ts +188 -0
  170. package/src/recordsWithProps.ts +131 -2
  171. package/src/shapes/ShapeWithCrop.test.ts +18 -0
  172. package/src/shapes/ShapeWithCrop.ts +64 -2
  173. package/src/shapes/TLArrowShape.test.ts +505 -0
  174. package/src/shapes/TLArrowShape.ts +188 -10
  175. package/src/shapes/TLBaseShape.test.ts +142 -0
  176. package/src/shapes/TLBaseShape.ts +103 -4
  177. package/src/shapes/TLBookmarkShape.test.ts +122 -0
  178. package/src/shapes/TLBookmarkShape.ts +58 -4
  179. package/src/shapes/TLDrawShape.test.ts +177 -0
  180. package/src/shapes/TLDrawShape.ts +97 -6
  181. package/src/shapes/TLEmbedShape.test.ts +286 -0
  182. package/src/shapes/TLEmbedShape.ts +57 -14
  183. package/src/shapes/TLFrameShape.test.ts +71 -0
  184. package/src/shapes/TLFrameShape.ts +59 -4
  185. package/src/shapes/TLGeoShape.test.ts +247 -0
  186. package/src/shapes/TLGeoShape.ts +103 -7
  187. package/src/shapes/TLGroupShape.test.ts +59 -0
  188. package/src/shapes/TLGroupShape.ts +52 -4
  189. package/src/shapes/TLHighlightShape.test.ts +325 -0
  190. package/src/shapes/TLHighlightShape.ts +79 -4
  191. package/src/shapes/TLImageShape.test.ts +534 -0
  192. package/src/shapes/TLImageShape.ts +105 -5
  193. package/src/shapes/TLLineShape.test.ts +269 -0
  194. package/src/shapes/TLLineShape.ts +128 -8
  195. package/src/shapes/TLNoteShape.test.ts +1568 -0
  196. package/src/shapes/TLNoteShape.ts +97 -4
  197. package/src/shapes/TLTextShape.test.ts +407 -0
  198. package/src/shapes/TLTextShape.ts +94 -4
  199. package/src/shapes/TLVideoShape.test.ts +112 -0
  200. package/src/shapes/TLVideoShape.ts +99 -4
  201. package/src/store-migrations.test.ts +88 -0
  202. package/src/store-migrations.ts +47 -1
  203. package/src/styles/TLColorStyle.test.ts +439 -0
  204. package/src/styles/TLColorStyle.ts +228 -10
  205. package/src/styles/TLDashStyle.ts +54 -2
  206. package/src/styles/TLFillStyle.ts +54 -2
  207. package/src/styles/TLFontStyle.ts +72 -3
  208. package/src/styles/TLHorizontalAlignStyle.ts +55 -2
  209. package/src/styles/TLSizeStyle.ts +54 -2
  210. package/src/styles/TLTextAlignStyle.ts +52 -2
  211. package/src/styles/TLVerticalAlignStyle.ts +52 -2
  212. package/src/translations/translations.test.ts +378 -35
  213. package/src/translations/translations.ts +157 -10
  214. package/src/util-types.ts +51 -1
@@ -0,0 +1,505 @@
1
+ import { describe, expect, it, test } from 'vitest'
2
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
3
+ import { arrowShapeMigrations, arrowShapeProps, arrowShapeVersions } from './TLArrowShape'
4
+
5
+ describe('TLArrowShape', () => {
6
+ describe('arrowShapeMigrations - AddLabelColor migration', () => {
7
+ const { up, down } = getTestMigration(arrowShapeVersions.AddLabelColor)
8
+
9
+ describe('AddLabelColor up migration', () => {
10
+ it('should add labelColor property with default value "black"', () => {
11
+ const oldRecord = {
12
+ id: 'shape:arrow1',
13
+ typeName: 'shape',
14
+ type: 'arrow',
15
+ x: 100,
16
+ y: 200,
17
+ rotation: 0,
18
+ index: 'a1',
19
+ parentId: 'page:main',
20
+ isLocked: false,
21
+ opacity: 1,
22
+ props: {
23
+ color: 'blue',
24
+ fill: 'none',
25
+ dash: 'solid',
26
+ size: 'm',
27
+ arrowheadStart: 'none',
28
+ arrowheadEnd: 'arrow',
29
+ font: 'draw',
30
+ start: { x: 0, y: 0 },
31
+ end: { x: 100, y: 100 },
32
+ },
33
+ meta: {},
34
+ }
35
+
36
+ const result = up(oldRecord)
37
+ expect(result.props.labelColor).toBe('black')
38
+ expect(result.props.color).toBe('blue') // Preserve other props
39
+ })
40
+
41
+ it('should preserve all existing properties during migration', () => {
42
+ const oldRecord = {
43
+ id: 'shape:arrow2',
44
+ typeName: 'shape',
45
+ type: 'arrow',
46
+ x: 50,
47
+ y: 75,
48
+ rotation: 0.5,
49
+ index: 'b1',
50
+ parentId: 'page:test',
51
+ isLocked: true,
52
+ opacity: 0.8,
53
+ props: {
54
+ color: 'red',
55
+ fill: 'solid',
56
+ dash: 'dashed',
57
+ size: 'l',
58
+ arrowheadStart: 'triangle',
59
+ arrowheadEnd: 'diamond',
60
+ font: 'sans',
61
+ start: { x: 25, y: 50 },
62
+ end: { x: 200, y: 150 },
63
+ },
64
+ meta: { custom: 'data' },
65
+ }
66
+
67
+ const result = up(oldRecord)
68
+ expect(result.props.labelColor).toBe('black')
69
+ expect(result.props.color).toBe('red')
70
+ expect(result.props.fill).toBe('solid')
71
+ expect(result.props.dash).toBe('dashed')
72
+ expect(result.props.size).toBe('l')
73
+ expect(result.props.arrowheadStart).toBe('triangle')
74
+ expect(result.props.arrowheadEnd).toBe('diamond')
75
+ expect(result.props.font).toBe('sans')
76
+ expect(result.props.start).toEqual({ x: 25, y: 50 })
77
+ expect(result.props.end).toEqual({ x: 200, y: 150 })
78
+ expect(result.meta).toEqual({ custom: 'data' })
79
+ })
80
+
81
+ test('should not modify labelColor if it already exists', () => {
82
+ const recordWithLabelColor = {
83
+ id: 'shape:arrow3',
84
+ typeName: 'shape',
85
+ type: 'arrow',
86
+ props: {
87
+ labelColor: 'red', // Already has labelColor
88
+ color: 'blue',
89
+ },
90
+ }
91
+
92
+ const result = up(recordWithLabelColor)
93
+ expect(result.props.labelColor).toBe('black') // Migration sets default regardless
94
+ })
95
+ })
96
+
97
+ describe('AddLabelColor down migration', () => {
98
+ it('should be retired (no down migration)', () => {
99
+ // Based on the source code, the down migration is 'retired'
100
+ // The getTestMigration utility should throw when trying to access down migration
101
+ expect(() => {
102
+ // This should throw since the migration is retired
103
+ down({})
104
+ }).toThrow('Migration com.tldraw.shape.arrow/1 does not have a down function')
105
+ })
106
+ })
107
+ })
108
+
109
+ describe('arrowShapeMigrations - AddIsPrecise migration', () => {
110
+ const { up, down } = getTestMigration(arrowShapeVersions.AddIsPrecise)
111
+
112
+ describe('AddIsPrecise up migration', () => {
113
+ it('should add isPrecise property to binding start and end', () => {
114
+ const oldRecord = {
115
+ id: 'shape:arrow1',
116
+ props: {
117
+ start: {
118
+ type: 'binding',
119
+ boundShapeId: 'shape:rect1',
120
+ normalizedAnchor: { x: 0.5, y: 0.5 },
121
+ },
122
+ end: {
123
+ type: 'binding',
124
+ boundShapeId: 'shape:rect2',
125
+ normalizedAnchor: { x: 0.5, y: 0.5 },
126
+ },
127
+ },
128
+ }
129
+
130
+ const result = up(oldRecord)
131
+ expect(result.props.start.isPrecise).toBe(false) // 0.5, 0.5 is not precise
132
+ expect(result.props.end.isPrecise).toBe(false)
133
+ })
134
+
135
+ it('should set isPrecise to true for non-center anchors', () => {
136
+ const oldRecord = {
137
+ id: 'shape:arrow1',
138
+ props: {
139
+ start: {
140
+ type: 'binding',
141
+ boundShapeId: 'shape:rect1',
142
+ normalizedAnchor: { x: 0.25, y: 0.75 }, // Not center
143
+ },
144
+ end: {
145
+ type: 'binding',
146
+ boundShapeId: 'shape:rect2',
147
+ normalizedAnchor: { x: 1, y: 0 }, // Not center
148
+ },
149
+ },
150
+ }
151
+
152
+ const result = up(oldRecord)
153
+ expect(result.props.start.isPrecise).toBe(true)
154
+ expect(result.props.end.isPrecise).toBe(true)
155
+ })
156
+
157
+ it('should not modify non-binding terminals', () => {
158
+ const oldRecord = {
159
+ id: 'shape:arrow1',
160
+ props: {
161
+ start: { type: 'point', x: 0, y: 0 },
162
+ end: { type: 'point', x: 100, y: 100 },
163
+ },
164
+ }
165
+
166
+ const result = up(oldRecord)
167
+ expect(result.props.start.isPrecise).toBeUndefined()
168
+ expect(result.props.end.isPrecise).toBeUndefined()
169
+ })
170
+
171
+ it('should handle mixed binding and point terminals', () => {
172
+ const oldRecord = {
173
+ id: 'shape:arrow1',
174
+ props: {
175
+ start: {
176
+ type: 'binding',
177
+ boundShapeId: 'shape:rect1',
178
+ normalizedAnchor: { x: 0, y: 0 }, // Precise
179
+ },
180
+ end: { type: 'point', x: 100, y: 100 },
181
+ },
182
+ }
183
+
184
+ const result = up(oldRecord)
185
+ expect(result.props.start.isPrecise).toBe(true)
186
+ expect(result.props.end.isPrecise).toBeUndefined()
187
+ })
188
+ })
189
+
190
+ describe('AddIsPrecise down migration', () => {
191
+ it('should remove isPrecise property and adjust normalizedAnchor if not precise', () => {
192
+ const newRecord = {
193
+ id: 'shape:arrow1',
194
+ props: {
195
+ start: {
196
+ type: 'binding',
197
+ boundShapeId: 'shape:rect1',
198
+ normalizedAnchor: { x: 0.25, y: 0.75 },
199
+ isPrecise: false,
200
+ },
201
+ end: {
202
+ type: 'binding',
203
+ boundShapeId: 'shape:rect2',
204
+ normalizedAnchor: { x: 0.1, y: 0.9 },
205
+ isPrecise: true,
206
+ },
207
+ },
208
+ }
209
+
210
+ const result = down(newRecord)
211
+ expect(result.props.start.isPrecise).toBeUndefined()
212
+ expect(result.props.start.normalizedAnchor).toEqual({ x: 0.5, y: 0.5 }) // Reset to center
213
+ expect(result.props.end.isPrecise).toBeUndefined()
214
+ expect(result.props.end.normalizedAnchor).toEqual({ x: 0.1, y: 0.9 }) // Keep precise anchor
215
+ })
216
+
217
+ it('should not modify non-binding terminals', () => {
218
+ const newRecord = {
219
+ id: 'shape:arrow1',
220
+ props: {
221
+ start: { type: 'point', x: 0, y: 0 },
222
+ end: { type: 'point', x: 100, y: 100 },
223
+ },
224
+ }
225
+
226
+ const result = down(newRecord)
227
+ expect(result.props.start).toEqual({ type: 'point', x: 0, y: 0 })
228
+ expect(result.props.end).toEqual({ type: 'point', x: 100, y: 100 })
229
+ })
230
+ })
231
+ })
232
+
233
+ describe('arrowShapeMigrations - AddLabelPosition migration', () => {
234
+ const { up, down } = getTestMigration(arrowShapeVersions.AddLabelPosition)
235
+
236
+ describe('AddLabelPosition up migration', () => {
237
+ it('should add labelPosition property with default value 0.5', () => {
238
+ const oldRecord = {
239
+ id: 'shape:arrow1',
240
+ props: {
241
+ color: 'blue',
242
+ start: { x: 0, y: 0 },
243
+ end: { x: 100, y: 100 },
244
+ },
245
+ }
246
+
247
+ const result = up(oldRecord)
248
+ expect(result.props.labelPosition).toBe(0.5)
249
+ })
250
+
251
+ it('should preserve existing properties during migration', () => {
252
+ const oldRecord = {
253
+ id: 'shape:arrow1',
254
+ props: {
255
+ color: 'red',
256
+ fill: 'solid',
257
+ start: { x: 25, y: 50 },
258
+ end: { x: 200, y: 150 },
259
+ bend: 0.3,
260
+ },
261
+ }
262
+
263
+ const result = up(oldRecord)
264
+ expect(result.props.labelPosition).toBe(0.5)
265
+ expect(result.props.color).toBe('red')
266
+ expect(result.props.fill).toBe('solid')
267
+ expect(result.props.start).toEqual({ x: 25, y: 50 })
268
+ expect(result.props.end).toEqual({ x: 200, y: 150 })
269
+ expect(result.props.bend).toBe(0.3)
270
+ })
271
+ })
272
+
273
+ describe('AddLabelPosition down migration', () => {
274
+ it('should remove labelPosition property', () => {
275
+ const newRecord = {
276
+ id: 'shape:arrow1',
277
+ props: {
278
+ color: 'blue',
279
+ start: { x: 0, y: 0 },
280
+ end: { x: 100, y: 100 },
281
+ labelPosition: 0.7,
282
+ },
283
+ }
284
+
285
+ const result = down(newRecord)
286
+ expect(result.props.labelPosition).toBeUndefined()
287
+ expect(result.props.color).toBe('blue') // Preserve other props
288
+ })
289
+ })
290
+ })
291
+
292
+ describe('arrowShapeMigrations - ExtractBindings migration', () => {
293
+ const migration = arrowShapeMigrations.sequence.find(
294
+ (m) => 'id' in m && m.id === arrowShapeVersions.ExtractBindings
295
+ )
296
+
297
+ it('should be a store-scope migration', () => {
298
+ expect(migration).toBeDefined()
299
+ if (migration && 'scope' in migration) {
300
+ expect(migration.scope).toBe('store')
301
+ }
302
+ })
303
+
304
+ it('should have up function for extracting bindings', () => {
305
+ if (migration && 'up' in migration) {
306
+ expect(migration.up).toBeDefined()
307
+ expect(typeof migration.up).toBe('function')
308
+ }
309
+ })
310
+
311
+ // Note: This migration is complex and modifies the entire store
312
+ // Testing the full migration would require setting up a mock store
313
+ // The migration extracts binding information from arrow terminals
314
+ // and creates separate binding records
315
+ })
316
+
317
+ describe('arrowShapeMigrations - AddScale migration', () => {
318
+ const { up, down } = getTestMigration(arrowShapeVersions.AddScale)
319
+
320
+ describe('AddScale up migration', () => {
321
+ it('should add scale property with default value 1', () => {
322
+ const oldRecord = {
323
+ id: 'shape:arrow1',
324
+ props: {
325
+ color: 'blue',
326
+ start: { x: 0, y: 0 },
327
+ end: { x: 100, y: 100 },
328
+ },
329
+ }
330
+
331
+ const result = up(oldRecord)
332
+ expect(result.props.scale).toBe(1)
333
+ })
334
+
335
+ it('should preserve existing properties during migration', () => {
336
+ const oldRecord = {
337
+ id: 'shape:arrow1',
338
+ props: {
339
+ color: 'red',
340
+ labelPosition: 0.3,
341
+ start: { x: 10, y: 20 },
342
+ end: { x: 200, y: 150 },
343
+ },
344
+ }
345
+
346
+ const result = up(oldRecord)
347
+ expect(result.props.scale).toBe(1)
348
+ expect(result.props.color).toBe('red')
349
+ expect(result.props.labelPosition).toBe(0.3)
350
+ })
351
+ })
352
+
353
+ describe('AddScale down migration', () => {
354
+ it('should remove scale property', () => {
355
+ const newRecord = {
356
+ id: 'shape:arrow1',
357
+ props: {
358
+ color: 'blue',
359
+ start: { x: 0, y: 0 },
360
+ end: { x: 100, y: 100 },
361
+ scale: 1.5,
362
+ },
363
+ }
364
+
365
+ const result = down(newRecord)
366
+ expect(result.props.scale).toBeUndefined()
367
+ expect(result.props.color).toBe('blue') // Preserve other props
368
+ })
369
+ })
370
+ })
371
+
372
+ describe('arrowShapeMigrations - AddElbow migration', () => {
373
+ const { up, down } = getTestMigration(arrowShapeVersions.AddElbow)
374
+
375
+ describe('AddElbow up migration', () => {
376
+ it('should add kind and elbowMidPoint properties with default values', () => {
377
+ const oldRecord = {
378
+ id: 'shape:arrow1',
379
+ props: {
380
+ color: 'blue',
381
+ start: { x: 0, y: 0 },
382
+ end: { x: 100, y: 100 },
383
+ },
384
+ }
385
+
386
+ const result = up(oldRecord)
387
+ expect(result.props.kind).toBe('arc')
388
+ expect(result.props.elbowMidPoint).toBe(0.5)
389
+ })
390
+
391
+ it('should preserve existing properties during migration', () => {
392
+ const oldRecord = {
393
+ id: 'shape:arrow1',
394
+ props: {
395
+ color: 'red',
396
+ scale: 1.2,
397
+ labelPosition: 0.7,
398
+ start: { x: 25, y: 50 },
399
+ end: { x: 200, y: 150 },
400
+ },
401
+ }
402
+
403
+ const result = up(oldRecord)
404
+ expect(result.props.kind).toBe('arc')
405
+ expect(result.props.elbowMidPoint).toBe(0.5)
406
+ expect(result.props.color).toBe('red')
407
+ expect(result.props.scale).toBe(1.2)
408
+ expect(result.props.labelPosition).toBe(0.7)
409
+ })
410
+ })
411
+
412
+ describe('AddElbow down migration', () => {
413
+ it('should remove kind and elbowMidPoint properties', () => {
414
+ const newRecord = {
415
+ id: 'shape:arrow1',
416
+ props: {
417
+ kind: 'elbow',
418
+ elbowMidPoint: 0.3,
419
+ color: 'blue',
420
+ start: { x: 0, y: 0 },
421
+ end: { x: 100, y: 100 },
422
+ },
423
+ }
424
+
425
+ const result = down(newRecord)
426
+ expect(result.props.kind).toBeUndefined()
427
+ expect(result.props.elbowMidPoint).toBeUndefined()
428
+ expect(result.props.color).toBe('blue') // Preserve other props
429
+ })
430
+ })
431
+ })
432
+
433
+ describe('arrowShapeMigrations - AddRichText migration', () => {
434
+ const { up } = getTestMigration(arrowShapeVersions.AddRichText)
435
+
436
+ describe('AddRichText up migration', () => {
437
+ it('should convert text property to richText', () => {
438
+ const oldRecord = {
439
+ id: 'shape:arrow1',
440
+ props: {
441
+ text: 'Simple text label',
442
+ color: 'blue',
443
+ start: { x: 0, y: 0 },
444
+ end: { x: 100, y: 100 },
445
+ },
446
+ }
447
+
448
+ const result = up(oldRecord)
449
+ expect(result.props.richText).toBeDefined()
450
+ expect(result.props.text).toBeUndefined()
451
+ })
452
+
453
+ it('should handle empty text', () => {
454
+ const oldRecord = {
455
+ id: 'shape:arrow1',
456
+ props: {
457
+ text: '',
458
+ color: 'red',
459
+ start: { x: 10, y: 20 },
460
+ end: { x: 200, y: 150 },
461
+ },
462
+ }
463
+
464
+ const result = up(oldRecord)
465
+ expect(result.props.richText).toBeDefined()
466
+ expect(result.props.text).toBeUndefined()
467
+ })
468
+
469
+ it('should preserve other properties during migration', () => {
470
+ const oldRecord = {
471
+ id: 'shape:arrow1',
472
+ props: {
473
+ text: 'Label text',
474
+ kind: 'elbow',
475
+ elbowMidPoint: 0.3,
476
+ color: 'green',
477
+ scale: 1.5,
478
+ },
479
+ }
480
+
481
+ const result = up(oldRecord)
482
+ expect(result.props.richText).toBeDefined()
483
+ expect(result.props.text).toBeUndefined()
484
+ expect(result.props.kind).toBe('elbow')
485
+ expect(result.props.elbowMidPoint).toBe(0.3)
486
+ expect(result.props.color).toBe('green')
487
+ expect(result.props.scale).toBe(1.5)
488
+ })
489
+ })
490
+
491
+ // Note: The down migration is explicitly not defined (forced client update)
492
+ // so we don't test it
493
+ })
494
+
495
+ describe('edge cases and error handling', () => {
496
+ it('should handle zero scale validation correctly', () => {
497
+ // Zero should be invalid for scale (nonZeroNumber)
498
+ expect(() => arrowShapeProps.scale.validate(0)).toThrow()
499
+
500
+ // Very small positive numbers should be valid, but negative numbers should be invalid
501
+ expect(() => arrowShapeProps.scale.validate(0.0001)).not.toThrow()
502
+ expect(() => arrowShapeProps.scale.validate(-0.0001)).toThrow()
503
+ })
504
+ })
505
+ })