@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.
- package/dist-cjs/TLStore.js +3 -10
- package/dist-cjs/TLStore.js.map +2 -2
- package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
- package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
- package/dist-cjs/assets/TLImageAsset.js.map +2 -2
- package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
- package/dist-cjs/bindings/TLArrowBinding.js.map +2 -2
- package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
- package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
- package/dist-cjs/createTLSchema.js.map +2 -2
- package/dist-cjs/index.d.ts +4412 -223
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/misc/TLColor.js.map +2 -2
- package/dist-cjs/misc/TLCursor.js.map +2 -2
- package/dist-cjs/misc/TLHandle.js.map +2 -2
- package/dist-cjs/misc/TLOpacity.js.map +2 -2
- package/dist-cjs/misc/TLRichText.js.map +2 -2
- package/dist-cjs/misc/TLScribble.js.map +2 -2
- package/dist-cjs/misc/geometry-types.js.map +2 -2
- package/dist-cjs/misc/id-validator.js.map +2 -2
- package/dist-cjs/records/TLAsset.js.map +2 -2
- package/dist-cjs/records/TLBinding.js.map +2 -2
- package/dist-cjs/records/TLCamera.js.map +2 -2
- package/dist-cjs/records/TLDocument.js.map +2 -2
- package/dist-cjs/records/TLInstance.js.map +2 -2
- package/dist-cjs/records/TLPage.js.map +2 -2
- package/dist-cjs/records/TLPageState.js.map +2 -2
- package/dist-cjs/records/TLPointer.js.map +2 -2
- package/dist-cjs/records/TLPresence.js.map +2 -2
- package/dist-cjs/records/TLRecord.js.map +1 -1
- package/dist-cjs/records/TLShape.js.map +2 -2
- package/dist-cjs/recordsWithProps.js.map +2 -2
- package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
- package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
- package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
- package/dist-cjs/shapes/TLBookmarkShape.js.map +2 -2
- package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
- package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
- package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
- package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
- package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
- package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
- package/dist-cjs/shapes/TLImageShape.js.map +2 -2
- package/dist-cjs/shapes/TLLineShape.js.map +2 -2
- package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
- package/dist-cjs/shapes/TLTextShape.js.map +2 -2
- package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
- package/dist-cjs/store-migrations.js.map +2 -2
- package/dist-cjs/styles/TLColorStyle.js.map +2 -2
- package/dist-cjs/styles/TLDashStyle.js.map +2 -2
- package/dist-cjs/styles/TLFillStyle.js.map +2 -2
- package/dist-cjs/styles/TLFontStyle.js.map +2 -2
- package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
- package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
- package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
- package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
- package/dist-cjs/translations/translations.js +1 -1
- package/dist-cjs/translations/translations.js.map +2 -2
- package/dist-cjs/util-types.js.map +1 -1
- package/dist-esm/TLStore.mjs +3 -10
- package/dist-esm/TLStore.mjs.map +2 -2
- package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
- package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
- package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
- package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
- package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
- package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
- package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
- package/dist-esm/createTLSchema.mjs.map +2 -2
- package/dist-esm/index.d.mts +4412 -223
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/misc/TLColor.mjs.map +2 -2
- package/dist-esm/misc/TLCursor.mjs.map +2 -2
- package/dist-esm/misc/TLHandle.mjs.map +2 -2
- package/dist-esm/misc/TLOpacity.mjs.map +2 -2
- package/dist-esm/misc/TLRichText.mjs.map +2 -2
- package/dist-esm/misc/TLScribble.mjs.map +2 -2
- package/dist-esm/misc/geometry-types.mjs.map +2 -2
- package/dist-esm/misc/id-validator.mjs.map +2 -2
- package/dist-esm/records/TLAsset.mjs.map +2 -2
- package/dist-esm/records/TLBinding.mjs.map +2 -2
- package/dist-esm/records/TLCamera.mjs.map +2 -2
- package/dist-esm/records/TLDocument.mjs.map +2 -2
- package/dist-esm/records/TLInstance.mjs.map +2 -2
- package/dist-esm/records/TLPage.mjs.map +2 -2
- package/dist-esm/records/TLPageState.mjs.map +2 -2
- package/dist-esm/records/TLPointer.mjs.map +2 -2
- package/dist-esm/records/TLPresence.mjs.map +2 -2
- package/dist-esm/records/TLShape.mjs.map +2 -2
- package/dist-esm/recordsWithProps.mjs.map +2 -2
- package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
- package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
- package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
- package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
- package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
- package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
- package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
- package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
- package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
- package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
- package/dist-esm/store-migrations.mjs.map +2 -2
- package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
- package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
- package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
- package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
- package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
- package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
- package/dist-esm/translations/translations.mjs +1 -1
- package/dist-esm/translations/translations.mjs.map +2 -2
- package/package.json +5 -5
- package/src/TLStore.test.ts +644 -0
- package/src/TLStore.ts +205 -20
- package/src/assets/TLBaseAsset.ts +90 -7
- package/src/assets/TLBookmarkAsset.test.ts +96 -0
- package/src/assets/TLBookmarkAsset.ts +52 -2
- package/src/assets/TLImageAsset.test.ts +213 -0
- package/src/assets/TLImageAsset.ts +60 -2
- package/src/assets/TLVideoAsset.test.ts +105 -0
- package/src/assets/TLVideoAsset.ts +93 -4
- package/src/bindings/TLArrowBinding.test.ts +55 -0
- package/src/bindings/TLArrowBinding.ts +132 -10
- package/src/bindings/TLBaseBinding.ts +140 -3
- package/src/createPresenceStateDerivation.test.ts +158 -0
- package/src/createPresenceStateDerivation.ts +71 -2
- package/src/createTLSchema.test.ts +181 -0
- package/src/createTLSchema.ts +164 -7
- package/src/index.ts +32 -0
- package/src/misc/TLColor.ts +50 -6
- package/src/misc/TLCursor.ts +110 -8
- package/src/misc/TLHandle.ts +82 -6
- package/src/misc/TLOpacity.ts +51 -2
- package/src/misc/TLRichText.ts +56 -3
- package/src/misc/TLScribble.ts +105 -5
- package/src/misc/geometry-types.ts +30 -2
- package/src/misc/id-validator.test.ts +50 -0
- package/src/misc/id-validator.ts +20 -1
- package/src/records/TLAsset.test.ts +234 -0
- package/src/records/TLAsset.ts +165 -8
- package/src/records/TLBinding.test.ts +22 -0
- package/src/records/TLBinding.ts +277 -11
- package/src/records/TLCamera.test.ts +19 -0
- package/src/records/TLCamera.ts +118 -7
- package/src/records/TLDocument.test.ts +35 -0
- package/src/records/TLDocument.ts +148 -8
- package/src/records/TLInstance.test.ts +201 -0
- package/src/records/TLInstance.ts +117 -9
- package/src/records/TLPage.test.ts +110 -0
- package/src/records/TLPage.ts +106 -8
- package/src/records/TLPageState.test.ts +228 -0
- package/src/records/TLPageState.ts +88 -7
- package/src/records/TLPointer.test.ts +63 -0
- package/src/records/TLPointer.ts +105 -7
- package/src/records/TLPresence.test.ts +190 -0
- package/src/records/TLPresence.ts +99 -5
- package/src/records/TLRecord.test.ts +70 -0
- package/src/records/TLRecord.ts +43 -1
- package/src/records/TLShape.test.ts +232 -0
- package/src/records/TLShape.ts +289 -12
- package/src/recordsWithProps.test.ts +188 -0
- package/src/recordsWithProps.ts +131 -2
- package/src/shapes/ShapeWithCrop.test.ts +18 -0
- package/src/shapes/ShapeWithCrop.ts +64 -2
- package/src/shapes/TLArrowShape.test.ts +505 -0
- package/src/shapes/TLArrowShape.ts +188 -10
- package/src/shapes/TLBaseShape.test.ts +142 -0
- package/src/shapes/TLBaseShape.ts +103 -4
- package/src/shapes/TLBookmarkShape.test.ts +122 -0
- package/src/shapes/TLBookmarkShape.ts +58 -4
- package/src/shapes/TLDrawShape.test.ts +177 -0
- package/src/shapes/TLDrawShape.ts +97 -6
- package/src/shapes/TLEmbedShape.test.ts +286 -0
- package/src/shapes/TLEmbedShape.ts +57 -4
- package/src/shapes/TLFrameShape.test.ts +71 -0
- package/src/shapes/TLFrameShape.ts +59 -4
- package/src/shapes/TLGeoShape.test.ts +247 -0
- package/src/shapes/TLGeoShape.ts +103 -7
- package/src/shapes/TLGroupShape.test.ts +59 -0
- package/src/shapes/TLGroupShape.ts +52 -4
- package/src/shapes/TLHighlightShape.test.ts +325 -0
- package/src/shapes/TLHighlightShape.ts +79 -4
- package/src/shapes/TLImageShape.test.ts +534 -0
- package/src/shapes/TLImageShape.ts +105 -5
- package/src/shapes/TLLineShape.test.ts +269 -0
- package/src/shapes/TLLineShape.ts +128 -8
- package/src/shapes/TLNoteShape.test.ts +1568 -0
- package/src/shapes/TLNoteShape.ts +97 -4
- package/src/shapes/TLTextShape.test.ts +407 -0
- package/src/shapes/TLTextShape.ts +94 -4
- package/src/shapes/TLVideoShape.test.ts +112 -0
- package/src/shapes/TLVideoShape.ts +99 -4
- package/src/store-migrations.test.ts +88 -0
- package/src/store-migrations.ts +47 -1
- package/src/styles/TLColorStyle.test.ts +439 -0
- package/src/styles/TLColorStyle.ts +228 -10
- package/src/styles/TLDashStyle.ts +54 -2
- package/src/styles/TLFillStyle.ts +54 -2
- package/src/styles/TLFontStyle.ts +72 -3
- package/src/styles/TLHorizontalAlignStyle.ts +55 -2
- package/src/styles/TLSizeStyle.ts +54 -2
- package/src/styles/TLTextAlignStyle.ts +52 -2
- package/src/styles/TLVerticalAlignStyle.ts +52 -2
- package/src/translations/translations.test.ts +378 -35
- package/src/translations/translations.ts +157 -10
- package/src/util-types.ts +51 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { T } from '@tldraw/validate'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { getTestMigration } from '../__tests__/migrationTestUtils'
|
|
4
|
+
import { TLAssetId } from '../records/TLAsset'
|
|
5
|
+
import { videoShapeMigrations, videoShapeProps, videoShapeVersions } from './TLVideoShape'
|
|
6
|
+
|
|
7
|
+
describe('TLVideoShape', () => {
|
|
8
|
+
describe('videoShapeProps validation', () => {
|
|
9
|
+
it('should validate dimensions as nonZeroNumber', () => {
|
|
10
|
+
expect(() => videoShapeProps.w.validate(1)).not.toThrow()
|
|
11
|
+
expect(() => videoShapeProps.h.validate(100)).not.toThrow()
|
|
12
|
+
expect(() => videoShapeProps.w.validate(0)).toThrow()
|
|
13
|
+
expect(() => videoShapeProps.h.validate(-1)).toThrow()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should validate time as number including negatives', () => {
|
|
17
|
+
expect(() => videoShapeProps.time.validate(0)).not.toThrow()
|
|
18
|
+
expect(() => videoShapeProps.time.validate(-5)).not.toThrow()
|
|
19
|
+
expect(() => videoShapeProps.time.validate(30.5)).not.toThrow()
|
|
20
|
+
expect(() => videoShapeProps.time.validate('not-number')).toThrow()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should validate boolean properties', () => {
|
|
24
|
+
expect(() => videoShapeProps.playing.validate(true)).not.toThrow()
|
|
25
|
+
expect(() => videoShapeProps.autoplay.validate(false)).not.toThrow()
|
|
26
|
+
expect(() => videoShapeProps.playing.validate('true')).toThrow()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should validate URLs and assetIds', () => {
|
|
30
|
+
expect(() => videoShapeProps.url.validate('')).not.toThrow()
|
|
31
|
+
expect(() => videoShapeProps.url.validate('https://example.com/video.mp4')).not.toThrow()
|
|
32
|
+
expect(() => videoShapeProps.assetId.validate(null)).not.toThrow()
|
|
33
|
+
expect(() => videoShapeProps.assetId.validate('asset:video123' as TLAssetId)).not.toThrow()
|
|
34
|
+
expect(() => videoShapeProps.altText.validate('Alt text')).not.toThrow()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should validate complete props object', () => {
|
|
38
|
+
const validator = T.object(videoShapeProps)
|
|
39
|
+
const validProps = {
|
|
40
|
+
w: 640,
|
|
41
|
+
h: 480,
|
|
42
|
+
time: 0,
|
|
43
|
+
playing: false,
|
|
44
|
+
autoplay: true,
|
|
45
|
+
url: 'https://example.com/video.mp4',
|
|
46
|
+
assetId: 'asset:video123' as TLAssetId,
|
|
47
|
+
altText: 'Test video',
|
|
48
|
+
}
|
|
49
|
+
expect(() => validator.validate(validProps)).not.toThrow()
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('videoShapeVersions', () => {
|
|
54
|
+
it('should have expected migration versions', () => {
|
|
55
|
+
expect(videoShapeVersions.AddUrlProp).toBeDefined()
|
|
56
|
+
expect(videoShapeVersions.MakeUrlsValid).toBeDefined()
|
|
57
|
+
expect(videoShapeVersions.AddAltText).toBeDefined()
|
|
58
|
+
expect(videoShapeVersions.AddAutoplay).toBeDefined()
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('videoShapeMigrations', () => {
|
|
63
|
+
it('should add url property in AddUrlProp migration', () => {
|
|
64
|
+
const { up } = getTestMigration(videoShapeVersions.AddUrlProp)
|
|
65
|
+
const oldRecord = {
|
|
66
|
+
props: { w: 640, h: 480, time: 30, playing: true, assetId: 'asset:video123' },
|
|
67
|
+
}
|
|
68
|
+
const result = up(oldRecord)
|
|
69
|
+
expect(result.props.url).toBe('')
|
|
70
|
+
expect(result.props.w).toBe(640)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should clear invalid URLs in MakeUrlsValid migration', () => {
|
|
74
|
+
const { up } = getTestMigration(videoShapeVersions.MakeUrlsValid)
|
|
75
|
+
const oldRecord = {
|
|
76
|
+
props: { url: 'invalid-url', w: 400, h: 300 },
|
|
77
|
+
}
|
|
78
|
+
const result = up(oldRecord)
|
|
79
|
+
expect(result.props.url).toBe('')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should add altText in AddAltText migration', () => {
|
|
83
|
+
const { up, down } = getTestMigration(videoShapeVersions.AddAltText)
|
|
84
|
+
const oldRecord = { props: { w: 800, h: 600 } }
|
|
85
|
+
const result = up(oldRecord)
|
|
86
|
+
expect(result.props.altText).toBe('')
|
|
87
|
+
|
|
88
|
+
// Test down migration removes altText
|
|
89
|
+
const newRecord = { props: { w: 640, h: 480, altText: 'Test' } }
|
|
90
|
+
const downResult = down(newRecord)
|
|
91
|
+
expect(downResult.props.altText).toBeUndefined()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should add autoplay in AddAutoplay migration', () => {
|
|
95
|
+
const { up, down } = getTestMigration(videoShapeVersions.AddAutoplay)
|
|
96
|
+
const oldRecord = { props: { w: 480, h: 270 } }
|
|
97
|
+
const result = up(oldRecord)
|
|
98
|
+
expect(result.props.autoplay).toBe(true)
|
|
99
|
+
|
|
100
|
+
// Test down migration removes autoplay
|
|
101
|
+
const newRecord = { props: { w: 800, h: 450, autoplay: false } }
|
|
102
|
+
const downResult = down(newRecord)
|
|
103
|
+
expect(downResult.props.autoplay).toBeUndefined()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should have migrations for all versions', () => {
|
|
107
|
+
expect(videoShapeMigrations.sequence).toBeDefined()
|
|
108
|
+
expect(Array.isArray(videoShapeMigrations.sequence)).toBe(true)
|
|
109
|
+
expect(videoShapeMigrations.sequence.length).toBeGreaterThanOrEqual(4)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -5,7 +5,26 @@ import { createShapePropsMigrationIds, createShapePropsMigrationSequence } from
|
|
|
5
5
|
import { RecordProps } from '../recordsWithProps'
|
|
6
6
|
import { TLBaseShape } from './TLBaseShape'
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Configuration interface defining properties for video shapes in tldraw.
|
|
10
|
+
* Video shapes can display video content from URLs or asset references,
|
|
11
|
+
* with controls for playback state, timing, and accessibility.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const videoProps: TLVideoShapeProps = {
|
|
17
|
+
* w: 640,
|
|
18
|
+
* h: 480,
|
|
19
|
+
* time: 0,
|
|
20
|
+
* playing: false,
|
|
21
|
+
* autoplay: true,
|
|
22
|
+
* url: 'https://example.com/video.mp4',
|
|
23
|
+
* assetId: 'asset:video123',
|
|
24
|
+
* altText: 'Educational video about shapes'
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
9
28
|
export interface TLVideoShapeProps {
|
|
10
29
|
w: number
|
|
11
30
|
h: number
|
|
@@ -17,10 +36,60 @@ export interface TLVideoShapeProps {
|
|
|
17
36
|
altText: string
|
|
18
37
|
}
|
|
19
38
|
|
|
20
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* A video shape that can display video content with playback controls and timing.
|
|
41
|
+
* Video shapes support both direct URL references and asset-based video storage,
|
|
42
|
+
* with accessibility features and playback state management.
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const videoShape: TLVideoShape = {
|
|
48
|
+
* id: 'shape:video123',
|
|
49
|
+
* typeName: 'shape',
|
|
50
|
+
* type: 'video',
|
|
51
|
+
* x: 100,
|
|
52
|
+
* y: 100,
|
|
53
|
+
* rotation: 0,
|
|
54
|
+
* index: 'a1',
|
|
55
|
+
* parentId: 'page:main',
|
|
56
|
+
* isLocked: false,
|
|
57
|
+
* opacity: 1,
|
|
58
|
+
* props: {
|
|
59
|
+
* w: 640,
|
|
60
|
+
* h: 480,
|
|
61
|
+
* time: 15.5,
|
|
62
|
+
* playing: false,
|
|
63
|
+
* autoplay: false,
|
|
64
|
+
* url: 'https://example.com/video.mp4',
|
|
65
|
+
* assetId: 'asset:video123',
|
|
66
|
+
* altText: 'Product demo video'
|
|
67
|
+
* },
|
|
68
|
+
* meta: {}
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
21
72
|
export type TLVideoShape = TLBaseShape<'video', TLVideoShapeProps>
|
|
22
73
|
|
|
23
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Validation schema for video shape properties. This defines the runtime validation
|
|
76
|
+
* rules that ensure video shape data integrity, including URL validation, numeric
|
|
77
|
+
* constraints, and proper asset ID formatting.
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* import { videoShapeProps } from '@tldraw/tlschema'
|
|
83
|
+
*
|
|
84
|
+
* // Validate video URL
|
|
85
|
+
* const isValidUrl = videoShapeProps.url.isValid('https://example.com/video.mp4')
|
|
86
|
+
* const isValidTime = videoShapeProps.time.isValid(42.5)
|
|
87
|
+
*
|
|
88
|
+
* if (isValidUrl && isValidTime) {
|
|
89
|
+
* // Video properties are valid
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
24
93
|
export const videoShapeProps: RecordProps<TLVideoShape> = {
|
|
25
94
|
w: T.nonZeroNumber,
|
|
26
95
|
h: T.nonZeroNumber,
|
|
@@ -39,9 +108,35 @@ const Versions = createShapePropsMigrationIds('video', {
|
|
|
39
108
|
AddAutoplay: 4,
|
|
40
109
|
})
|
|
41
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Version identifiers for video shape migrations. These constants track
|
|
113
|
+
* the evolution of the video shape schema over time.
|
|
114
|
+
*
|
|
115
|
+
* @public
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* import { videoShapeVersions } from '@tldraw/tlschema'
|
|
119
|
+
*
|
|
120
|
+
* // Check if shape data needs migration
|
|
121
|
+
* if (shapeVersion < videoShapeVersions.AddAltText) {
|
|
122
|
+
* // Apply alt text migration for accessibility
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
42
126
|
export { Versions as videoShapeVersions }
|
|
43
127
|
|
|
44
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Migration sequence for video shape schema evolution. This handles transforming
|
|
130
|
+
* video shape data between different versions as the schema evolves over time.
|
|
131
|
+
*
|
|
132
|
+
* Key migrations include:
|
|
133
|
+
* - AddUrlProp: Added URL property for direct video links
|
|
134
|
+
* - MakeUrlsValid: Ensured all URLs conform to link URL validation
|
|
135
|
+
* - AddAltText: Added accessibility support with alternative text
|
|
136
|
+
* - AddAutoplay: Added autoplay control for video playback
|
|
137
|
+
*
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
45
140
|
export const videoShapeMigrations = createShapePropsMigrationSequence({
|
|
46
141
|
sequence: [
|
|
47
142
|
{
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getTestMigration } from './__tests__/migrationTestUtils'
|
|
3
|
+
import { storeMigrations, storeVersions } from './store-migrations'
|
|
4
|
+
|
|
5
|
+
describe('storeMigrations', () => {
|
|
6
|
+
it('has valid migration sequence structure', () => {
|
|
7
|
+
expect(storeMigrations.sequenceId).toBe('com.tldraw.store')
|
|
8
|
+
expect(storeMigrations.retroactive).toBe(false)
|
|
9
|
+
expect(storeMigrations.sequence.length).toBeGreaterThan(0)
|
|
10
|
+
|
|
11
|
+
// Verify all migrations have required fields
|
|
12
|
+
for (const migration of storeMigrations.sequence) {
|
|
13
|
+
expect(migration.id).toBeDefined()
|
|
14
|
+
expect(migration.scope).toBeDefined()
|
|
15
|
+
expect(migration.up).toBeInstanceOf(Function)
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('RemoveCodeAndIconShapeTypes migration', () => {
|
|
21
|
+
const { up } = getTestMigration(storeVersions.RemoveCodeAndIconShapeTypes)
|
|
22
|
+
|
|
23
|
+
it('removes icon and code shapes from store', () => {
|
|
24
|
+
const store = {
|
|
25
|
+
'shape:icon1': { typeName: 'shape', type: 'icon' },
|
|
26
|
+
'shape:code1': { typeName: 'shape', type: 'code' },
|
|
27
|
+
'shape:geo1': { typeName: 'shape', type: 'geo' },
|
|
28
|
+
'page:test': { typeName: 'page' },
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const result = up(store)
|
|
32
|
+
|
|
33
|
+
expect(result['shape:icon1']).toBeUndefined()
|
|
34
|
+
expect(result['shape:code1']).toBeUndefined()
|
|
35
|
+
expect(result['shape:geo1']).toBeDefined()
|
|
36
|
+
expect(result['page:test']).toBeDefined()
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('AddInstancePresenceType migration', () => {
|
|
41
|
+
const { up } = getTestMigration(storeVersions.AddInstancePresenceType)
|
|
42
|
+
|
|
43
|
+
it('is a no-op migration', () => {
|
|
44
|
+
const store = { 'page:test': { typeName: 'page' } }
|
|
45
|
+
const result = up(store)
|
|
46
|
+
expect(result).toEqual(store)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('RemoveTLUserAndPresenceAndAddPointer migration', () => {
|
|
51
|
+
const { up } = getTestMigration(storeVersions.RemoveTLUserAndPresenceAndAddPointer)
|
|
52
|
+
|
|
53
|
+
it('removes user and user_presence records', () => {
|
|
54
|
+
const store = {
|
|
55
|
+
'user:123': { typeName: 'user' },
|
|
56
|
+
'user_presence:456': { typeName: 'user_presence' },
|
|
57
|
+
'instance:789': { typeName: 'instance' },
|
|
58
|
+
'user_something:abc': { typeName: 'user_something' }, // Should not match
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result = up(store)
|
|
62
|
+
|
|
63
|
+
expect(result['user:123']).toBeUndefined()
|
|
64
|
+
expect(result['user_presence:456']).toBeUndefined()
|
|
65
|
+
expect(result['instance:789']).toBeDefined()
|
|
66
|
+
expect(result['user_something:abc']).toBeDefined() // Uses exact regex match
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('RemoveUserDocument migration', () => {
|
|
71
|
+
const { up } = getTestMigration(storeVersions.RemoveUserDocument)
|
|
72
|
+
|
|
73
|
+
it('removes user_document records', () => {
|
|
74
|
+
const store = {
|
|
75
|
+
'user_document:123': { typeName: 'user_document' },
|
|
76
|
+
'user_document_meta:456': { typeName: 'user_document_meta' }, // Also matches due to substring
|
|
77
|
+
'document:789': { typeName: 'document' },
|
|
78
|
+
'instance:abc': { typeName: 'instance' },
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = up(store)
|
|
82
|
+
|
|
83
|
+
expect(result['user_document:123']).toBeUndefined()
|
|
84
|
+
expect(result['user_document_meta:456']).toBeUndefined() // Uses match() which finds substring
|
|
85
|
+
expect(result['document:789']).toBeDefined()
|
|
86
|
+
expect(result['instance:abc']).toBeDefined()
|
|
87
|
+
})
|
|
88
|
+
})
|
package/src/store-migrations.ts
CHANGED
|
@@ -4,6 +4,12 @@ import { TLPage } from './records/TLPage'
|
|
|
4
4
|
import { TLShape } from './records/TLShape'
|
|
5
5
|
import { TLLineShape } from './shapes/TLLineShape'
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Migration version constants for store-level schema changes.
|
|
9
|
+
* Each version represents a breaking change that requires data transformation.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
7
13
|
const Versions = createMigrationIds('com.tldraw.store', {
|
|
8
14
|
RemoveCodeAndIconShapeTypes: 1,
|
|
9
15
|
AddInstancePresenceType: 2,
|
|
@@ -12,9 +18,49 @@ const Versions = createMigrationIds('com.tldraw.store', {
|
|
|
12
18
|
FixIndexKeys: 5,
|
|
13
19
|
} as const)
|
|
14
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Migration version identifiers for store-level migrations.
|
|
23
|
+
* These versions track changes to the overall store structure and data model.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { storeVersions } from '@tldraw/tlschema'
|
|
28
|
+
*
|
|
29
|
+
* // Check if a specific migration version exists
|
|
30
|
+
* const hasRemoveCodeShapes = storeVersions.RemoveCodeAndIconShapeTypes
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
15
35
|
export { Versions as storeVersions }
|
|
16
36
|
|
|
17
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Store-level migration sequence that handles evolution of the tldraw data model.
|
|
39
|
+
* These migrations run when the store schema version changes and ensure backward
|
|
40
|
+
* compatibility by transforming old data structures to new formats.
|
|
41
|
+
*
|
|
42
|
+
* The migrations handle:
|
|
43
|
+
* - Removal of deprecated shape types (code, icon)
|
|
44
|
+
* - Addition of new record types (instance presence)
|
|
45
|
+
* - Cleanup of obsolete user and presence data
|
|
46
|
+
* - Removal of deprecated user document records
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { storeMigrations } from '@tldraw/tlschema'
|
|
51
|
+
* import { migrate } from '@tldraw/store'
|
|
52
|
+
*
|
|
53
|
+
* // Apply store migrations to old data
|
|
54
|
+
* const migratedStore = migrate({
|
|
55
|
+
* store: oldStoreData,
|
|
56
|
+
* migrations: storeMigrations,
|
|
57
|
+
* fromVersion: 0,
|
|
58
|
+
* toVersion: storeMigrations.currentVersion
|
|
59
|
+
* })
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
18
64
|
export const storeMigrations = createMigrationSequence({
|
|
19
65
|
sequenceId: 'com.tldraw.store',
|
|
20
66
|
retroactive: false,
|