@tldraw/tlschema 4.1.0-canary.e2133d922c9e → 4.1.0-canary.e259b517a450

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 +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.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 +4416 -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 +86 -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
package/src/TLStore.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  StoreSnapshot,
7
7
  StoreValidationFailure,
8
8
  } from '@tldraw/store'
9
- import { IndexKey, JsonObject, annotateError, structuredClone } from '@tldraw/utils'
9
+ import { IndexKey, JsonObject, annotateError, sortByIndex, structuredClone } from '@tldraw/utils'
10
10
  import { TLAsset, TLAssetId } from './records/TLAsset'
11
11
  import { CameraRecordType, TLCameraId } from './records/TLCamera'
12
12
  import { DocumentRecordType, TLDOCUMENT_ID } from './records/TLDocument'
@@ -16,16 +16,14 @@ import { InstancePageStateRecordType, TLInstancePageStateId } from './records/TL
16
16
  import { PointerRecordType, TLPOINTER_ID } from './records/TLPointer'
17
17
  import { TLRecord } from './records/TLRecord'
18
18
 
19
- function sortByIndex<T extends { index: string }>(a: T, b: T) {
20
- if (a.index < b.index) {
21
- return -1
22
- } else if (a.index > b.index) {
23
- return 1
24
- }
25
- return 0
26
- }
27
-
28
- function redactRecordForErrorReporting(record: any) {
19
+ /**
20
+ * Redacts the source of an asset record for error reporting.
21
+ *
22
+ * @param record - The asset record to redact
23
+ * @returns The redacted record
24
+ * @internal
25
+ */
26
+ export function redactRecordForErrorReporting(record: any) {
29
27
  if (record.typeName === 'asset') {
30
28
  if ('src' in record) {
31
29
  record.src = '<redacted>'
@@ -37,16 +35,75 @@ function redactRecordForErrorReporting(record: any) {
37
35
  }
38
36
  }
39
37
 
40
- /** @public */
38
+ /**
39
+ * The complete schema type for a tldraw store, defining the structure and validation rules
40
+ * for all tldraw records and store properties.
41
+ *
42
+ * @public
43
+ * @example
44
+ * ```ts
45
+ * import { createTLSchema } from '@tldraw/tlschema'
46
+ *
47
+ * const schema = createTLSchema()
48
+ * const storeSchema: TLStoreSchema = schema
49
+ * ```
50
+ */
41
51
  export type TLStoreSchema = StoreSchema<TLRecord, TLStoreProps>
42
52
 
43
- /** @public */
53
+ /**
54
+ * A serialized representation of a tldraw store that can be persisted or transmitted.
55
+ * Contains all store records in a JSON-serializable format.
56
+ *
57
+ * @public
58
+ * @example
59
+ * ```ts
60
+ * // Serialize a store
61
+ * const serializedStore: TLSerializedStore = store.serialize()
62
+ *
63
+ * // Save to localStorage
64
+ * localStorage.setItem('drawing', JSON.stringify(serializedStore))
65
+ * ```
66
+ */
44
67
  export type TLSerializedStore = SerializedStore<TLRecord>
45
68
 
46
- /** @public */
69
+ /**
70
+ * A snapshot of a tldraw store at a specific point in time, containing all records
71
+ * and metadata. Used for persistence, synchronization, and creating store backups.
72
+ *
73
+ * @public
74
+ * @example
75
+ * ```ts
76
+ * // Create a snapshot
77
+ * const snapshot: TLStoreSnapshot = store.getSnapshot()
78
+ *
79
+ * // Restore from snapshot
80
+ * store.loadSnapshot(snapshot)
81
+ * ```
82
+ */
47
83
  export type TLStoreSnapshot = StoreSnapshot<TLRecord>
48
84
 
49
- /** @public */
85
+ /**
86
+ * Context information provided when resolving asset URLs, containing details about
87
+ * the current rendering environment and user's connection to optimize asset delivery.
88
+ *
89
+ * @public
90
+ * @example
91
+ * ```ts
92
+ * const assetStore: TLAssetStore = {
93
+ * async resolve(asset, context: TLAssetContext) {
94
+ * // Use low resolution for slow connections
95
+ * if (context.networkEffectiveType === 'slow-2g') {
96
+ * return `${asset.props.src}?quality=low`
97
+ * }
98
+ * // Use high DPI version for retina displays
99
+ * if (context.dpr > 1) {
100
+ * return `${asset.props.src}@2x`
101
+ * }
102
+ * return asset.props.src
103
+ * }
104
+ * }
105
+ * ```
106
+ */
50
107
  export interface TLAssetContext {
51
108
  /**
52
109
  * The scale at which the asset is being rendered on-screen relative to its native dimensions.
@@ -76,6 +133,9 @@ export interface TLAssetContext {
76
133
  }
77
134
 
78
135
  /**
136
+ * Interface for storing and managing assets (images, videos, etc.) in tldraw.
137
+ * Provides methods for uploading, resolving, and removing assets from storage.
138
+ *
79
139
  * A `TLAssetStore` sits alongside the main {@link TLStore} and is responsible for storing and
80
140
  * retrieving large assets such as images. Generally, this should be part of a wider sync system:
81
141
  *
@@ -87,6 +147,24 @@ export interface TLAssetContext {
87
147
  * e.g. an S3 bucket.
88
148
  *
89
149
  * @public
150
+ * @example
151
+ * ```ts
152
+ * // Simple in-memory asset store
153
+ * const assetStore: TLAssetStore = {
154
+ * async upload(asset, file) {
155
+ * const dataUrl = await fileToDataUrl(file)
156
+ * return { src: dataUrl }
157
+ * },
158
+ *
159
+ * async resolve(asset, context) {
160
+ * return asset.props.src
161
+ * },
162
+ *
163
+ * async remove(assetIds) {
164
+ * // Clean up if needed
165
+ * }
166
+ * }
167
+ * ```
90
168
  */
91
169
  export interface TLAssetStore {
92
170
  /**
@@ -122,24 +200,106 @@ export interface TLAssetStore {
122
200
  remove?(assetIds: TLAssetId[]): Promise<void>
123
201
  }
124
202
 
125
- /** @public */
203
+ /**
204
+ * Configuration properties for a tldraw store, defining its behavior and integrations.
205
+ * These props are passed when creating a new store instance.
206
+ *
207
+ * @public
208
+ * @example
209
+ * ```ts
210
+ * const storeProps: TLStoreProps = {
211
+ * defaultName: 'My Drawing',
212
+ * assets: myAssetStore,
213
+ * onMount: (editor) => {
214
+ * console.log('Editor mounted')
215
+ * return () => console.log('Editor unmounted')
216
+ * },
217
+ * collaboration: {
218
+ * status: statusSignal,
219
+ * mode: modeSignal
220
+ * }
221
+ * }
222
+ *
223
+ * const store = new Store({ schema, props: storeProps })
224
+ * ```
225
+ */
126
226
  export interface TLStoreProps {
227
+ /** Default name for new documents created in this store */
127
228
  defaultName: string
229
+ /** Asset store implementation for handling file uploads and storage */
128
230
  assets: Required<TLAssetStore>
129
231
  /**
130
- * Called an {@link @tldraw/editor#Editor} connected to this store is mounted.
232
+ * Called when an {@link @tldraw/editor#Editor} connected to this store is mounted.
233
+ * Can optionally return a cleanup function that will be called when unmounted.
234
+ *
235
+ * @param editor - The editor instance that was mounted
236
+ * @returns Optional cleanup function
131
237
  */
132
238
  onMount(editor: unknown): void | (() => void)
239
+ /** Optional collaboration configuration for multiplayer features */
133
240
  collaboration?: {
241
+ /** Signal indicating online/offline collaboration status */
134
242
  status: Signal<'online' | 'offline'> | null
243
+ /** Signal indicating collaboration mode permissions */
135
244
  mode?: Signal<'readonly' | 'readwrite'> | null
136
245
  }
137
246
  }
138
247
 
139
- /** @public */
248
+ /**
249
+ * The main tldraw store type, representing a reactive database of tldraw records
250
+ * with associated store properties. This is the central data structure that holds
251
+ * all shapes, assets, pages, and user state.
252
+ *
253
+ * @public
254
+ * @example
255
+ * ```ts
256
+ * import { Store } from '@tldraw/store'
257
+ * import { createTLSchema } from '@tldraw/tlschema'
258
+ *
259
+ * const schema = createTLSchema()
260
+ * const store: TLStore = new Store({
261
+ * schema,
262
+ * props: {
263
+ * defaultName: 'Untitled',
264
+ * assets: myAssetStore,
265
+ * onMount: () => console.log('Store mounted')
266
+ * }
267
+ * })
268
+ * ```
269
+ */
140
270
  export type TLStore = Store<TLRecord, TLStoreProps>
141
271
 
142
- /** @public */
272
+ /**
273
+ * Default validation failure handler for tldraw stores. This function is called
274
+ * when a record fails validation during store operations. It annotates errors
275
+ * with debugging information and determines whether to allow invalid records
276
+ * during store initialization.
277
+ *
278
+ * @param options - The validation failure details
279
+ * - error - The validation error that occurred
280
+ * - phase - The store operation phase when validation failed
281
+ * - record - The invalid record that caused the failure
282
+ * - recordBefore - The previous state of the record (if applicable)
283
+ * @returns The record to use (typically throws the annotated error)
284
+ * @throws The original validation error with additional debugging context
285
+ *
286
+ * @public
287
+ * @example
288
+ * ```ts
289
+ * const store = new Store({
290
+ * schema,
291
+ * props: storeProps,
292
+ * onValidationFailure // Use this as the validation failure handler
293
+ * })
294
+ *
295
+ * // The handler will be called automatically when validation fails
296
+ * try {
297
+ * store.put([invalidRecord])
298
+ * } catch (error) {
299
+ * // Error will contain debugging information added by onValidationFailure
300
+ * }
301
+ * ```
302
+ */
143
303
  export function onValidationFailure({
144
304
  error,
145
305
  phase,
@@ -179,7 +339,32 @@ function getDefaultPages() {
179
339
  ]
180
340
  }
181
341
 
182
- /** @internal */
342
+ /**
343
+ * Creates an integrity checker function that ensures the tldraw store maintains
344
+ * a consistent and usable state. The checker validates that required records exist
345
+ * and relationships between records are maintained.
346
+ *
347
+ * The integrity checker ensures:
348
+ * - Document and pointer records exist
349
+ * - At least one page exists
350
+ * - Instance state references valid pages
351
+ * - Page states and cameras exist for all pages
352
+ * - Shape references in page states are valid
353
+ *
354
+ * @param store - The tldraw store to check for integrity
355
+ * @returns A function that when called, validates and fixes store integrity
356
+ *
357
+ * @internal
358
+ * @example
359
+ * ```ts
360
+ * const checker = createIntegrityChecker(store)
361
+ *
362
+ * // Run integrity check (typically called automatically)
363
+ * checker()
364
+ *
365
+ * // The checker will create missing records and fix invalid references
366
+ * ```
367
+ */
183
368
  export function createIntegrityChecker(store: Store<TLRecord, TLStoreProps>): () => void {
184
369
  const $pageIds = store.query.ids('page')
185
370
  const $pageStates = store.query.records('instance_page_state')
@@ -4,26 +4,109 @@ import { T } from '@tldraw/validate'
4
4
  import { idValidator } from '../misc/id-validator'
5
5
  import { TLAssetId } from '../records/TLAsset'
6
6
 
7
- /** @public */
7
+ /**
8
+ * Base interface for all asset records in tldraw. Assets represent external resources
9
+ * like images, videos, or bookmarks that shapes can reference. This interface extends
10
+ * the base record system with asset-specific typing.
11
+ *
12
+ * @param Type - The specific asset type identifier (e.g., 'image', 'video', 'bookmark')
13
+ * @param Props - The properties object specific to this asset type
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // Define a custom asset type
18
+ * interface MyCustomAsset extends TLBaseAsset<'custom', { url: string; title: string }> {}
19
+ *
20
+ * const customAsset: MyCustomAsset = {
21
+ * id: 'asset:custom123',
22
+ * typeName: 'asset',
23
+ * type: 'custom',
24
+ * props: {
25
+ * url: 'https://example.com',
26
+ * title: 'My Custom Asset'
27
+ * },
28
+ * meta: {}
29
+ * }
30
+ * ```
31
+ *
32
+ * @public
33
+ */
8
34
  export interface TLBaseAsset<Type extends string, Props> extends BaseRecord<'asset', TLAssetId> {
35
+ /** The specific type of this asset (e.g., 'image', 'video', 'bookmark') */
9
36
  type: Type
37
+ /** Type-specific properties for this asset */
10
38
  props: Props
39
+ /** User-defined metadata that can be attached to this asset */
11
40
  meta: JsonObject
12
41
  }
13
42
 
14
43
  /**
15
- * A validator for asset record type Ids.
44
+ * A validator for asset record type IDs. This validator ensures that asset IDs
45
+ * follow the correct format and type structure required by tldraw's asset system.
46
+ * Asset IDs are prefixed with 'asset:' followed by a unique identifier.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { assetIdValidator } from '@tldraw/tlschema'
51
+ *
52
+ * // Valid asset ID
53
+ * const validId = 'asset:abc123'
54
+ * console.log(assetIdValidator.isValid(validId)) // true
16
55
  *
17
- * @public */
56
+ * // Invalid asset ID
57
+ * const invalidId = 'shape:abc123'
58
+ * console.log(assetIdValidator.isValid(invalidId)) // false
59
+ * ```
60
+ *
61
+ * @public
62
+ */
18
63
  export const assetIdValidator = idValidator<TLAssetId>('asset')
19
64
 
20
65
  /**
21
- * Create a validator for an asset record type.
66
+ * Creates a validator for a specific asset record type. This factory function generates
67
+ * a complete validator that validates the entire asset record structure including the
68
+ * base properties (id, typeName, type, meta) and the type-specific props.
69
+ *
70
+ * @param type - The asset type identifier (e.g., 'image', 'video', 'bookmark')
71
+ * @param props - The validator for the asset's type-specific properties
72
+ * @returns A complete validator for the asset record type
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * import { createAssetValidator, TLBaseAsset } from '@tldraw/tlschema'
77
+ * import { T } from '@tldraw/validate'
78
+ *
79
+ * // Define a custom asset type
80
+ * type TLCustomAsset = TLBaseAsset<'custom', {
81
+ * url: string
82
+ * title: string
83
+ * description?: string
84
+ * }>
85
+ *
86
+ * // Create validator for the custom asset
87
+ * const customAssetValidator = createAssetValidator('custom', T.object({
88
+ * url: T.string,
89
+ * title: T.string,
90
+ * description: T.string.optional()
91
+ * }))
92
+ *
93
+ * // Use the validator
94
+ * const assetData = {
95
+ * id: 'asset:custom123',
96
+ * typeName: 'asset' as const,
97
+ * type: 'custom' as const,
98
+ * props: {
99
+ * url: 'https://example.com',
100
+ * title: 'My Custom Asset'
101
+ * },
102
+ * meta: {}
103
+ * }
22
104
  *
23
- * @param type - The type of the asset
24
- * @param props - The validator for the asset's props
105
+ * const validatedAsset = customAssetValidator.validate(assetData)
106
+ * ```
25
107
  *
26
- * @public */
108
+ * @public
109
+ */
27
110
  export function createAssetValidator<Type extends string, Props extends JsonObject>(
28
111
  type: Type,
29
112
  props: T.Validator<Props>
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
3
+ import { bookmarkAssetVersions } from './TLBookmarkAsset'
4
+
5
+ describe('TLBookmarkAsset', () => {
6
+ describe('MakeUrlsValid migration', () => {
7
+ const { up, down } = getTestMigration(bookmarkAssetVersions.MakeUrlsValid)
8
+
9
+ it('should clean invalid src URLs and preserve valid ones', () => {
10
+ const assetWithInvalidSrc = {
11
+ id: 'asset:bookmark1',
12
+ type: 'bookmark',
13
+ props: {
14
+ title: 'Test Bookmark',
15
+ description: 'Test Description',
16
+ image: 'https://example.com/image.jpg',
17
+ src: 'invalid-url-format',
18
+ },
19
+ }
20
+
21
+ const result = up(assetWithInvalidSrc)
22
+ expect(result.props.src).toBe('')
23
+
24
+ // Test valid URL is preserved
25
+ const assetWithValidSrc = {
26
+ ...assetWithInvalidSrc,
27
+ props: { ...assetWithInvalidSrc.props, src: 'https://example.com' },
28
+ }
29
+ const validResult = up(assetWithValidSrc)
30
+ expect(validResult.props.src).toBe('https://example.com')
31
+ })
32
+
33
+ it('should be a no-op down migration', () => {
34
+ const asset = {
35
+ id: 'asset:bookmark1',
36
+ type: 'bookmark',
37
+ props: {
38
+ title: 'Test Bookmark',
39
+ description: 'Test Description',
40
+ image: 'https://example.com/image.jpg',
41
+ src: 'https://example.com',
42
+ },
43
+ }
44
+
45
+ const result = down(asset)
46
+ expect(result).toEqual(asset)
47
+ })
48
+ })
49
+
50
+ describe('AddFavicon migration', () => {
51
+ const { up, down } = getTestMigration(bookmarkAssetVersions.AddFavicon)
52
+
53
+ it('should add favicon property and clean invalid URLs', () => {
54
+ // Test adding favicon property to asset without one
55
+ const assetWithoutFavicon = {
56
+ id: 'asset:bookmark1',
57
+ type: 'bookmark',
58
+ props: {
59
+ title: 'Test Bookmark',
60
+ description: 'Test Description',
61
+ image: 'https://example.com/image.jpg',
62
+ src: 'https://example.com',
63
+ },
64
+ }
65
+
66
+ const result = up(assetWithoutFavicon)
67
+ expect(result.props.favicon).toBe('')
68
+
69
+ // Test cleaning invalid favicon URL
70
+ const assetWithInvalidFavicon = {
71
+ ...assetWithoutFavicon,
72
+ props: { ...assetWithoutFavicon.props, favicon: 'invalid-url' },
73
+ }
74
+ const cleanResult = up(assetWithInvalidFavicon)
75
+ expect(cleanResult.props.favicon).toBe('')
76
+ })
77
+
78
+ it('should remove favicon property in down migration', () => {
79
+ const assetWithFavicon = {
80
+ id: 'asset:bookmark1',
81
+ type: 'bookmark',
82
+ props: {
83
+ title: 'Test Bookmark',
84
+ description: 'Test Description',
85
+ image: 'https://example.com/image.jpg',
86
+ src: 'https://example.com',
87
+ favicon: 'https://example.com/favicon.ico',
88
+ },
89
+ }
90
+
91
+ const result = down(assetWithFavicon)
92
+ expect(result.props).not.toHaveProperty('favicon')
93
+ expect(result.props.title).toBe('Test Bookmark')
94
+ })
95
+ })
96
+ })
@@ -18,7 +18,30 @@ export type TLBookmarkAsset = TLBaseAsset<
18
18
  }
19
19
  >
20
20
 
21
- /** @public */
21
+ /**
22
+ * Validator for TLBookmarkAsset records. Validates the structure and data types
23
+ * of bookmark asset properties including title, description, image, favicon, and source URL.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const bookmarkData = {
28
+ * id: 'asset:bookmark1',
29
+ * typeName: 'asset',
30
+ * type: 'bookmark',
31
+ * props: {
32
+ * title: 'Example Website',
33
+ * description: 'A great example site',
34
+ * image: 'https://example.com/preview.jpg',
35
+ * favicon: 'https://example.com/favicon.ico',
36
+ * src: 'https://example.com'
37
+ * }
38
+ * }
39
+ *
40
+ * const isValid = bookmarkAssetValidator.isValid(bookmarkData)
41
+ * ```
42
+ *
43
+ * @public
44
+ */
22
45
  export const bookmarkAssetValidator: T.Validator<TLBookmarkAsset> = createAssetValidator(
23
46
  'bookmark',
24
47
  T.object({
@@ -35,9 +58,36 @@ const Versions = createMigrationIds('com.tldraw.asset.bookmark', {
35
58
  AddFavicon: 2,
36
59
  } as const)
37
60
 
61
+ /**
62
+ * Migration version identifiers for bookmark assets. These versions track
63
+ * the evolution of the bookmark asset schema over time.
64
+ *
65
+ * Available versions:
66
+ * - `MakeUrlsValid` (v1): Ensures src URLs are valid or empty
67
+ * - `AddFavicon` (v2): Adds favicon property to bookmark assets
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { bookmarkAssetVersions } from '@tldraw/tlschema'
72
+ *
73
+ * // Check if a migration exists
74
+ * console.log(bookmarkAssetVersions.AddFavicon) // 2
75
+ * ```
76
+ *
77
+ * @public
78
+ */
38
79
  export { Versions as bookmarkAssetVersions }
39
80
 
40
- /** @public */
81
+ /**
82
+ * Migration sequence for bookmark assets. Handles the evolution of bookmark asset
83
+ * data structure over time, ensuring backward and forward compatibility.
84
+ *
85
+ * The migration sequence includes:
86
+ * 1. **MakeUrlsValid** (v1): Validates and cleans up src URLs, setting invalid URLs to empty string
87
+ * 2. **AddFavicon** (v2): Adds the favicon property and validates it, setting invalid favicons to empty string
88
+ *
89
+ * @public
90
+ */
41
91
  export const bookmarkAssetMigrations = createRecordMigrationSequence({
42
92
  sequenceId: 'com.tldraw.asset.bookmark',
43
93
  recordType: 'asset',