[refactor] record migrations (#1430)

This PR removes comments from our record types, makes initial version
optional, and unifies the order of initial / current version.

- Initial versions are zero by default
- If no current version is provided to `defineMigrations`, migrations
should be undefined
- Fixes TypeScript quirks in versioning (e.g. only initial version)

This PR also:
- Makes migrations optional when empty
- Removes reference to empty migrations

### Change Type

- [x] `major` — Breaking Change

### Test Plan

- [x] Unit Tests
- [ ] Webdriver tests

### Release Notes

- [tlschema] Improve `defineMigrations`
- [editor] Simplify migration definitions
This commit is contained in:
Steve Ruiz 2023-05-22 22:46:24 +01:00 committed by GitHub
parent d48e403ed1
commit 53be923921
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 427 additions and 691 deletions

View file

@ -13,3 +13,5 @@ apps/docs/content.json
apps/vscode/extension/editor/index.js apps/vscode/extension/editor/index.js
apps/vscode/extension/editor/tldraw-assets.json apps/vscode/extension/editor/tldraw-assets.json
apps/webdriver/www/index.js apps/webdriver/www/index.js
apps/vscode/extension/editor/*
apps/examples/www

View file

@ -11,7 +11,7 @@ import {
VecLike, VecLike,
} from '@tldraw/primitives' } from '@tldraw/primitives'
import { import {
arrowShapeMigrations, arrowShapeTypeMigrations,
arrowShapeTypeValidator, arrowShapeTypeValidator,
TLArrowheadType, TLArrowheadType,
TLArrowShape, TLArrowShape,
@ -1141,5 +1141,5 @@ export const TLArrowShapeDef = defineShape<TLArrowShape, TLArrowUtil>({
type: 'arrow', type: 'arrow',
getShapeUtil: () => TLArrowUtil, getShapeUtil: () => TLArrowUtil,
validator: arrowShapeTypeValidator, validator: arrowShapeTypeValidator,
migrations: arrowShapeMigrations, migrations: arrowShapeTypeMigrations,
}) })

View file

@ -1,6 +1,6 @@
import { toDomPrecision } from '@tldraw/primitives' import { toDomPrecision } from '@tldraw/primitives'
import { import {
bookmarkShapeMigrations, bookmarkShapeTypeMigrations,
bookmarkShapeTypeValidator, bookmarkShapeTypeValidator,
TLAsset, TLAsset,
TLAssetId, TLAssetId,
@ -197,5 +197,5 @@ export const TLBookmarkShapeDef = defineShape<TLBookmarkShape, TLBookmarkUtil>({
type: 'bookmark', type: 'bookmark',
getShapeUtil: () => TLBookmarkUtil, getShapeUtil: () => TLBookmarkUtil,
validator: bookmarkShapeTypeValidator, validator: bookmarkShapeTypeValidator,
migrations: bookmarkShapeMigrations, migrations: bookmarkShapeTypeMigrations,
}) })

View file

@ -10,7 +10,7 @@ import {
VecLike, VecLike,
} from '@tldraw/primitives' } from '@tldraw/primitives'
import { import {
drawShapeMigrations, drawShapeTypeMigrations,
drawShapeTypeValidator, drawShapeTypeValidator,
TLDrawShape, TLDrawShape,
TLDrawShapeSegment, TLDrawShapeSegment,
@ -314,7 +314,7 @@ export class TLDrawUtil extends TLShapeUtil<TLDrawShape> {
export const TLDrawShapeDef = defineShape<TLDrawShape, TLDrawUtil>({ export const TLDrawShapeDef = defineShape<TLDrawShape, TLDrawUtil>({
type: 'draw', type: 'draw',
getShapeUtil: () => TLDrawUtil, getShapeUtil: () => TLDrawUtil,
migrations: drawShapeMigrations, migrations: drawShapeTypeMigrations,
validator: drawShapeTypeValidator, validator: drawShapeTypeValidator,
}) })

View file

@ -1,7 +1,7 @@
/* eslint-disable react-hooks/rules-of-hooks */ /* eslint-disable react-hooks/rules-of-hooks */
import { toDomPrecision } from '@tldraw/primitives' import { toDomPrecision } from '@tldraw/primitives'
import { import {
embedShapeMigrations, embedShapeTypeMigrations,
embedShapeTypeValidator, embedShapeTypeValidator,
TLEmbedShape, TLEmbedShape,
tlEmbedShapePermissionDefaults, tlEmbedShapePermissionDefaults,
@ -236,5 +236,5 @@ export const TLEmbedShapeDef = defineShape<TLEmbedShape, TLEmbedUtil>({
type: 'embed', type: 'embed',
getShapeUtil: () => TLEmbedUtil, getShapeUtil: () => TLEmbedUtil,
validator: embedShapeTypeValidator, validator: embedShapeTypeValidator,
migrations: embedShapeMigrations, migrations: embedShapeTypeMigrations,
}) })

View file

@ -1,6 +1,5 @@
import { canolicalizeRotation, SelectionEdge, toDomPrecision } from '@tldraw/primitives' import { canolicalizeRotation, SelectionEdge, toDomPrecision } from '@tldraw/primitives'
import { import {
frameShapeMigrations,
frameShapeTypeValidator, frameShapeTypeValidator,
TLFrameShape, TLFrameShape,
TLShape, TLShape,
@ -218,5 +217,4 @@ export const TLFrameShapeDef = defineShape<TLFrameShape, TLFrameUtil>({
type: 'frame', type: 'frame',
getShapeUtil: () => TLFrameUtil, getShapeUtil: () => TLFrameUtil,
validator: frameShapeTypeValidator, validator: frameShapeTypeValidator,
migrations: frameShapeMigrations,
}) })

View file

@ -13,7 +13,7 @@ import {
VecLike, VecLike,
} from '@tldraw/primitives' } from '@tldraw/primitives'
import { import {
geoShapeMigrations, geoShapeTypeMigrations,
geoShapeTypeValidator, geoShapeTypeValidator,
TLDashType, TLDashType,
TLGeoShape, TLGeoShape,
@ -1008,5 +1008,5 @@ export const TLGeoShapeDef = defineShape<TLGeoShape, TLGeoUtil>({
type: 'geo', type: 'geo',
getShapeUtil: () => TLGeoUtil, getShapeUtil: () => TLGeoUtil,
validator: geoShapeTypeValidator, validator: geoShapeTypeValidator,
migrations: geoShapeMigrations, migrations: geoShapeTypeMigrations,
}) })

View file

@ -1,10 +1,5 @@
import { Box2d, Matrix2d } from '@tldraw/primitives' import { Box2d, Matrix2d } from '@tldraw/primitives'
import { import { TLGroupShape, Vec2dModel, groupShapeTypeValidator } from '@tldraw/tlschema'
TLGroupShape,
Vec2dModel,
groupShapeMigrations,
groupShapeTypeValidator,
} from '@tldraw/tlschema'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { defineShape } from '../../../config/TLShapeDefinition' import { defineShape } from '../../../config/TLShapeDefinition'
import { OnChildrenChangeHandler, TLShapeUtil } from '../TLShapeUtil' import { OnChildrenChangeHandler, TLShapeUtil } from '../TLShapeUtil'
@ -115,5 +110,4 @@ export const TLGroupShapeDef = defineShape<TLGroupShape, TLGroupUtil>({
type: 'group', type: 'group',
getShapeUtil: () => TLGroupUtil, getShapeUtil: () => TLGroupUtil,
validator: groupShapeTypeValidator, validator: groupShapeTypeValidator,
migrations: groupShapeMigrations,
}) })

View file

@ -3,7 +3,7 @@ import { Vec2d, toDomPrecision } from '@tldraw/primitives'
import { import {
TLImageShape, TLImageShape,
TLShapePartial, TLShapePartial,
imageShapeMigrations, imageShapeTypeMigrations,
imageShapeTypeValidator, imageShapeTypeValidator,
} from '@tldraw/tlschema' } from '@tldraw/tlschema'
import { deepCopy } from '@tldraw/utils' import { deepCopy } from '@tldraw/utils'
@ -275,7 +275,7 @@ export const TLImageShapeDef = defineShape<TLImageShape, TLImageUtil>({
type: 'image', type: 'image',
getShapeUtil: () => TLImageUtil, getShapeUtil: () => TLImageUtil,
validator: imageShapeTypeValidator, validator: imageShapeTypeValidator,
migrations: imageShapeMigrations, migrations: imageShapeTypeMigrations,
}) })
/** /**

View file

@ -9,12 +9,7 @@ import {
intersectLineSegmentPolyline, intersectLineSegmentPolyline,
pointNearToPolyline, pointNearToPolyline,
} from '@tldraw/primitives' } from '@tldraw/primitives'
import { import { TLHandle, TLLineShape, lineShapeTypeValidator } from '@tldraw/tlschema'
TLHandle,
TLLineShape,
lineShapeMigrations,
lineShapeTypeValidator,
} from '@tldraw/tlschema'
import { deepCopy } from '@tldraw/utils' import { deepCopy } from '@tldraw/utils'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { defineShape } from '../../../config/TLShapeDefinition' import { defineShape } from '../../../config/TLShapeDefinition'
@ -345,7 +340,6 @@ export const TLLineShapeDef = defineShape<TLLineShape, TLLineUtil>({
type: 'line', type: 'line',
getShapeUtil: () => TLLineUtil, getShapeUtil: () => TLLineUtil,
validator: lineShapeTypeValidator, validator: lineShapeTypeValidator,
migrations: lineShapeMigrations,
}) })
/** @public */ /** @public */

View file

@ -1,5 +1,5 @@
import { Box2d, toDomPrecision, Vec2d } from '@tldraw/primitives' import { Box2d, toDomPrecision, Vec2d } from '@tldraw/primitives'
import { noteShapeMigrations, noteShapeTypeValidator, TLNoteShape } from '@tldraw/tlschema' import { noteShapeTypeMigrations, noteShapeTypeValidator, TLNoteShape } from '@tldraw/tlschema'
import { defineShape } from '../../../config/TLShapeDefinition' import { defineShape } from '../../../config/TLShapeDefinition'
import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants' import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { App } from '../../App' import { App } from '../../App'
@ -202,7 +202,7 @@ export const TLNoteShapeDef = defineShape<TLNoteShape, TLNoteUtil>({
getShapeUtil: () => TLNoteUtil, getShapeUtil: () => TLNoteUtil,
type: 'note', type: 'note',
validator: noteShapeTypeValidator, validator: noteShapeTypeValidator,
migrations: noteShapeMigrations, migrations: noteShapeTypeMigrations,
}) })
function getGrowY(app: App, shape: TLNoteShape, prevGrowY = 0) { function getGrowY(app: App, shape: TLNoteShape, prevGrowY = 0) {

View file

@ -1,6 +1,6 @@
/* eslint-disable react-hooks/rules-of-hooks */ /* eslint-disable react-hooks/rules-of-hooks */
import { Box2d, toDomPrecision, Vec2d } from '@tldraw/primitives' import { Box2d, toDomPrecision, Vec2d } from '@tldraw/primitives'
import { textShapeMigrations, textShapeTypeValidator, TLTextShape } from '@tldraw/tlschema' import { textShapeTypeMigrations, textShapeTypeValidator, TLTextShape } from '@tldraw/tlschema'
import { HTMLContainer } from '../../../components/HTMLContainer' import { HTMLContainer } from '../../../components/HTMLContainer'
import { defineShape } from '../../../config/TLShapeDefinition' import { defineShape } from '../../../config/TLShapeDefinition'
import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../../../constants' import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../../../constants'
@ -374,7 +374,7 @@ export const TLTextShapeDef = defineShape<TLTextShape, TLTextUtil>({
type: 'text', type: 'text',
getShapeUtil: () => TLTextUtil, getShapeUtil: () => TLTextUtil,
validator: textShapeTypeValidator, validator: textShapeTypeValidator,
migrations: textShapeMigrations, migrations: textShapeTypeMigrations,
}) })
function getTextSize(app: App, props: TLTextShape['props']) { function getTextSize(app: App, props: TLTextShape['props']) {

View file

@ -1,5 +1,5 @@
import { toDomPrecision } from '@tldraw/primitives' import { toDomPrecision } from '@tldraw/primitives'
import { TLVideoShape, videoShapeMigrations, videoShapeTypeValidator } from '@tldraw/tlschema' import { TLVideoShape, videoShapeTypeMigrations, videoShapeTypeValidator } from '@tldraw/tlschema'
import * as React from 'react' import * as React from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { HTMLContainer } from '../../../components/HTMLContainer' import { HTMLContainer } from '../../../components/HTMLContainer'
@ -54,7 +54,7 @@ export const TLVideoShapeDef = defineShape<TLVideoShape, TLVideoUtil>({
type: 'video', type: 'video',
getShapeUtil: () => TLVideoUtil, getShapeUtil: () => TLVideoUtil,
validator: videoShapeTypeValidator, validator: videoShapeTypeValidator,
migrations: videoShapeMigrations, migrations: videoShapeTypeMigrations,
}) })
// Function from v1, could be improved bu explicitly using this.model.time (?) // Function from v1, could be improved bu explicitly using this.model.time (?)

View file

@ -45,7 +45,7 @@ const __TopLeftSnapOnlyShapeDef = defineShape<__TopLeftSnapOnlyShape, __TopLeftS
type: '__test_top_left_snap_only', type: '__test_top_left_snap_only',
getShapeUtil: () => __TopLeftSnapOnlyShapeUtil, getShapeUtil: () => __TopLeftSnapOnlyShapeUtil,
validator: { validate: (record) => record as __TopLeftSnapOnlyShape }, validator: { validate: (record) => record as __TopLeftSnapOnlyShape },
migrations: defineMigrations({ currentVersion: 0, firstVersion: 0, migrators: {} }), migrations: defineMigrations({}),
}) })
const configWithCustomShape = new TldrawEditorConfig({ shapes: [__TopLeftSnapOnlyShapeDef] }) const configWithCustomShape = new TldrawEditorConfig({ shapes: [__TopLeftSnapOnlyShapeDef] })

View file

@ -5,7 +5,6 @@
```ts ```ts
import { BaseRecord } from '@tldraw/tlstore'; import { BaseRecord } from '@tldraw/tlstore';
import { defineMigrations } from '@tldraw/tlstore';
import { ID } from '@tldraw/tlstore'; import { ID } from '@tldraw/tlstore';
import { Migrations } from '@tldraw/tlstore'; import { Migrations } from '@tldraw/tlstore';
import { RecordType } from '@tldraw/tlstore'; import { RecordType } from '@tldraw/tlstore';
@ -24,7 +23,7 @@ export const alignValidator: T.Validator<"end" | "middle" | "start">;
export const arrowheadValidator: T.Validator<"arrow" | "bar" | "diamond" | "dot" | "inverted" | "none" | "pipe" | "square" | "triangle">; export const arrowheadValidator: T.Validator<"arrow" | "bar" | "diamond" | "dot" | "inverted" | "none" | "pipe" | "square" | "triangle">;
// @public (undocumented) // @public (undocumented)
export const arrowShapeMigrations: Migrations; export const arrowShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const arrowShapeTypeValidator: T.Validator<TLArrowShape>; export const arrowShapeTypeValidator: T.Validator<TLArrowShape>;
@ -48,7 +47,7 @@ export const bookmarkAssetMigrations: Migrations;
export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset>; export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset>;
// @public (undocumented) // @public (undocumented)
export const bookmarkShapeMigrations: Migrations; export const bookmarkShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape>; export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape>;
@ -65,9 +64,6 @@ export interface Box2dModel {
y: number; y: number;
} }
// @public (undocumented)
export const cameraTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const cameraTypeValidator: T.Validator<TLCamera>; export const cameraTypeValidator: T.Validator<TLCamera>;
@ -124,7 +120,7 @@ export const cursorValidator: T.Validator<TLCursor>;
// @public (undocumented) // @public (undocumented)
export type CustomShapeTypeInfo = { export type CustomShapeTypeInfo = {
type: string; type: string;
migrations: ReturnType<typeof defineMigrations>; migrations?: Migrations;
validator?: StoreValidator<TLShape>; validator?: StoreValidator<TLShape>;
}; };
@ -134,14 +130,11 @@ export const dashValidator: T.Validator<"dashed" | "dotted" | "draw" | "solid">;
// @internal (undocumented) // @internal (undocumented)
export const defaultDerivePresenceState: (store: TLStore) => Signal<null | TLInstancePresence>; export const defaultDerivePresenceState: (store: TLStore) => Signal<null | TLInstancePresence>;
// @public (undocumented)
export const documentTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const documentTypeValidator: T.Validator<TLDocument>; export const documentTypeValidator: T.Validator<TLDocument>;
// @public (undocumented) // @public (undocumented)
export const drawShapeMigrations: Migrations; export const drawShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const drawShapeTypeValidator: T.Validator<TLDrawShape>; export const drawShapeTypeValidator: T.Validator<TLDrawShape>;
@ -334,7 +327,7 @@ export type EmbedDefinition = {
}; };
// @public (undocumented) // @public (undocumented)
export const embedShapeMigrations: Migrations; export const embedShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const embedShapeTypeValidator: T.Validator<TLEmbedShape>; export const embedShapeTypeValidator: T.Validator<TLEmbedShape>;
@ -351,14 +344,11 @@ export function fixupRecord(oldRecord: TLRecord): {
// @internal (undocumented) // @internal (undocumented)
export const fontValidator: T.Validator<"draw" | "mono" | "sans" | "serif">; export const fontValidator: T.Validator<"draw" | "mono" | "sans" | "serif">;
// @public (undocumented)
export const frameShapeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const frameShapeTypeValidator: T.Validator<TLFrameShape>; export const frameShapeTypeValidator: T.Validator<TLFrameShape>;
// @public (undocumented) // @public (undocumented)
export const geoShapeMigrations: Migrations; export const geoShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const geoShapeTypeValidator: T.Validator<TLGeoShape>; export const geoShapeTypeValidator: T.Validator<TLGeoShape>;
@ -366,18 +356,12 @@ export const geoShapeTypeValidator: T.Validator<TLGeoShape>;
// @internal (undocumented) // @internal (undocumented)
export const geoValidator: T.Validator<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">; export const geoValidator: T.Validator<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">;
// @public (undocumented)
export const groupShapeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const groupShapeTypeValidator: T.Validator<TLGroupShape>; export const groupShapeTypeValidator: T.Validator<TLGroupShape>;
// @public (undocumented) // @public (undocumented)
export const handleTypeValidator: T.Validator<TLHandle>; export const handleTypeValidator: T.Validator<TLHandle>;
// @public (undocumented)
export const iconShapeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const iconShapeTypeValidator: T.Validator<TLIconShape>; export const iconShapeTypeValidator: T.Validator<TLIconShape>;
@ -394,7 +378,7 @@ export const imageAssetMigrations: Migrations;
export const imageAssetTypeValidator: T.Validator<TLImageAsset>; export const imageAssetTypeValidator: T.Validator<TLImageAsset>;
// @public (undocumented) // @public (undocumented)
export const imageShapeMigrations: Migrations; export const imageShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const imageShapeTypeValidator: T.Validator<TLImageShape>; export const imageShapeTypeValidator: T.Validator<TLImageShape>;
@ -420,14 +404,11 @@ export function isShape(record?: BaseRecord<string>): record is TLShape;
// @public (undocumented) // @public (undocumented)
export function isShapeId(id?: string): id is TLShapeId; export function isShapeId(id?: string): id is TLShapeId;
// @public (undocumented)
export const lineShapeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const lineShapeTypeValidator: T.Validator<TLLineShape>; export const lineShapeTypeValidator: T.Validator<TLLineShape>;
// @public (undocumented) // @public (undocumented)
export const noteShapeMigrations: Migrations; export const noteShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const noteShapeTypeValidator: T.Validator<TLNoteShape>; export const noteShapeTypeValidator: T.Validator<TLNoteShape>;
@ -441,9 +422,6 @@ export const opacityValidator: T.Validator<"0.1" | "0.25" | "0.5" | "0.75" | "1"
// @internal (undocumented) // @internal (undocumented)
export const pageIdValidator: T.Validator<TLPageId>; export const pageIdValidator: T.Validator<TLPageId>;
// @public (undocumented)
export const pageTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const pageTypeValidator: T.Validator<TLPage>; export const pageTypeValidator: T.Validator<TLPage>;
@ -477,7 +455,7 @@ export const splineValidator: T.Validator<"cubic" | "line">;
export const storeMigrations: Migrations; export const storeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const textShapeMigrations: Migrations; export const textShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const textShapeTypeValidator: T.Validator<TLTextShape>; export const textShapeTypeValidator: T.Validator<TLTextShape>;
@ -1390,9 +1368,6 @@ export const userPresenceTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const userPresenceTypeValidator: T.Validator<TLUserPresence>; export const userPresenceTypeValidator: T.Validator<TLUserPresence>;
// @public (undocumented)
export const userTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const userTypeValidator: T.Validator<TLUser>; export const userTypeValidator: T.Validator<TLUser>;
@ -1413,7 +1388,7 @@ export const videoAssetMigrations: Migrations;
export const videoAssetTypeValidator: T.Validator<TLVideoAsset>; export const videoAssetTypeValidator: T.Validator<TLVideoAsset>;
// @public (undocumented) // @public (undocumented)
export const videoShapeMigrations: Migrations; export const videoShapeTypeMigrations: Migrations;
// @public (undocumented) // @public (undocumented)
export const videoShapeTypeValidator: T.Validator<TLVideoShape>; export const videoShapeTypeValidator: T.Validator<TLVideoShape>;

View file

@ -33,9 +33,6 @@
"scripts": { "scripts": {
"test": "lazy inherit", "test": "lazy inherit",
"test-coverage": "lazy inherit", "test-coverage": "lazy inherit",
"new-record": "node ./scripts/new-record.js",
"new-shape": "node ./scripts/new-shape.js",
"new-asset": "node ./scripts/new-asset.js",
"index": "node ./scripts/build-index.js && yarn format", "index": "node ./scripts/build-index.js && yarn format",
"format": "yarn run -T prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"", "format": "yarn run -T prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
"build": "yarn run -T tsx ../../scripts/build-package.ts", "build": "yarn run -T tsx ../../scripts/build-package.ts",

View file

@ -1,78 +0,0 @@
// @ts-check
/* eslint-disable */
const kleur = require('kleur')
const fs = require('fs')
const path = require('path')
const typeName = process.argv[2]
const lowerAssetName = typeName[2].toLowerCase() + typeName.slice(3)
if (!typeName.match(/^TL[A-Z][a-z]+[a-zA-Z0-9]+Asset$/)) {
console.error(
kleur.red('ERROR: Type name must start with'),
`'${kleur.bold('TL')}'`,
kleur.red('and be in'),
kleur.bold('PascalCase'),
kleur.red('and end in'),
kleur.bold('Asset')
)
process.exit(1)
}
const recordsDir = path.join(__dirname, '..', 'src', 'assets')
if (!fs.existsSync(recordsDir)) {
console.error(kleur.red("ERROR: Can't find assets directory at path"), recordsDir)
process.exit(1)
}
const filePath = path.join(recordsDir, `${typeName}.ts`)
if (fs.existsSync(filePath)) {
console.error(kleur.red('ERROR: File already exists at path'), filePath)
process.exit(1)
}
const snakeCaseName =
typeName[2].toLowerCase() +
typeName
.slice(3, -5)
.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`)
.trimStart()
fs.writeFileSync(
filePath,
`import { defineMigrations } from '@tldraw/tlstore'
import { TLAsset } from '../records/TLAsset'
declare module '../records/TLAsset' {
interface GlobalAssetPropsMap {
${snakeCaseName}: ${typeName}Props
}
}
// IMPORTANT: If you update this interface, you must also bump the version number and add a migration
export type ${typeName}Props = {}
export type ${typeName} = Extract<TLAsset, { type: '${snakeCaseName}' }>
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
export const ${lowerAssetName}Migrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})
`
)
console.log(kleur.green('Created new record type at path'), filePath)

View file

@ -1,78 +0,0 @@
// @ts-check
/* eslint-disable */
const kleur = require('kleur')
const fs = require('fs')
const path = require('path')
const typeName = process.argv[2]
if (!typeName.match(/^TL[A-Z][a-z]+[a-zA-Z0-9]+$/)) {
console.error(
kleur.red('ERROR: Type name must start with'),
`'${kleur.bold('TL')}'`,
kleur.red('and be in'),
kleur.bold('PascalCase')
)
process.exit(1)
}
const lowerCaseName = typeName[2].toLowerCase() + typeName.slice(3)
const recordsDir = path.join(__dirname, '..', 'src', 'records')
if (!fs.existsSync(recordsDir)) {
console.error(kleur.red("ERROR: Can't find records directory at path"), recordsDir)
process.exit(1)
}
const filePath = path.join(recordsDir, `${typeName}.ts`)
if (fs.existsSync(filePath)) {
console.error(kleur.red('ERROR: File already exists at path'), filePath)
process.exit(1)
}
const snakeCaseName =
typeName[2].toLowerCase() +
typeName
.slice(3)
.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`)
.trimStart()
fs.writeFileSync(
filePath,
`import { BaseRecord, defineMigrations, createRecordType, ID } from '@tldraw/tlstore'
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
export const ${lowerCaseName}TypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})
/**
* ${typeName}
*/
export interface ${typeName} extends BaseRecord<'${snakeCaseName}'> {}
export const ${typeName} = createRecordType<${typeName}>('${snakeCaseName}', {
migrations: ${lowerCaseName}TypeMigrations,
}).withDefaultProperties(() => ({
}))
export type ${typeName}Id = ID<${typeName}>
`
)
console.log(kleur.green('Created new record type at path'), filePath)

View file

@ -14,7 +14,6 @@ export type TLBookmarkAsset = TLBaseAsset<
} }
> >
// --- VALIDATION ---
/** @public */ /** @public */
export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset> = createAssetValidator( export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset> = createAssetValidator(
'bookmark', 'bookmark',
@ -26,18 +25,5 @@ export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset> = createAs
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */ /** @public */
export const bookmarkAssetMigrations = defineMigrations({ export const bookmarkAssetMigrations = defineMigrations({})
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
// STEP 3: Add an up+down migration for the new version here
migrators: {},
})

View file

@ -16,7 +16,6 @@ export type TLImageAsset = TLBaseAsset<
} }
> >
// --- VALIDATION ---
/** @public */ /** @public */
export const imageAssetTypeValidator: T.Validator<TLImageAsset> = createAssetValidator( export const imageAssetTypeValidator: T.Validator<TLImageAsset> = createAssetValidator(
'image', 'image',
@ -30,21 +29,14 @@ export const imageAssetTypeValidator: T.Validator<TLImageAsset> = createAssetVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddIsAnimated: 1, AddIsAnimated: 1,
RenameWidthHeight: 2, RenameWidthHeight: 2,
} as const } as const
/** @public */ /** @public */
export const imageAssetMigrations = defineMigrations({ export const imageAssetMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.RenameWidthHeight, currentVersion: Versions.RenameWidthHeight,
// STEP 3: Add an up+down migration for the new version here
migrators: { migrators: {
[Versions.AddIsAnimated]: { [Versions.AddIsAnimated]: {
up: (asset) => { up: (asset) => {

View file

@ -17,7 +17,6 @@ export type TLVideoAsset = TLBaseAsset<
} }
> >
// --- VALIDATION ---
/** @public */ /** @public */
export const videoAssetTypeValidator: T.Validator<TLVideoAsset> = createAssetValidator( export const videoAssetTypeValidator: T.Validator<TLVideoAsset> = createAssetValidator(
'video', 'video',
@ -31,21 +30,14 @@ export const videoAssetTypeValidator: T.Validator<TLVideoAsset> = createAssetVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddIsAnimated: 1, AddIsAnimated: 1,
RenameWidthHeight: 2, RenameWidthHeight: 2,
} as const } as const
/** @public */ /** @public */
export const videoAssetMigrations = defineMigrations({ export const videoAssetMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.RenameWidthHeight, currentVersion: Versions.RenameWidthHeight,
// STEP 3: Add an up+down migration for the new version here
migrators: { migrators: {
[Versions.AddIsAnimated]: { [Versions.AddIsAnimated]: {
up: (asset) => { up: (asset) => {

View file

@ -1,4 +1,10 @@
import { StoreSchema, StoreValidator, createRecordType, defineMigrations } from '@tldraw/tlstore' import {
Migrations,
StoreSchema,
StoreValidator,
createRecordType,
defineMigrations,
} from '@tldraw/tlstore'
import { T } from '@tldraw/tlvalidate' import { T } from '@tldraw/tlvalidate'
import { Signal } from 'signia' import { Signal } from 'signia'
import { TLRecord } from './TLRecord' import { TLRecord } from './TLRecord'
@ -16,42 +22,42 @@ import { TLUser } from './records/TLUser'
import { TLUserDocument } from './records/TLUserDocument' import { TLUserDocument } from './records/TLUserDocument'
import { TLUserPresence } from './records/TLUserPresence' import { TLUserPresence } from './records/TLUserPresence'
import { storeMigrations } from './schema' import { storeMigrations } from './schema'
import { arrowShapeMigrations, arrowShapeTypeValidator } from './shapes/TLArrowShape' import { arrowShapeTypeMigrations, arrowShapeTypeValidator } from './shapes/TLArrowShape'
import { bookmarkShapeMigrations, bookmarkShapeTypeValidator } from './shapes/TLBookmarkShape' import { bookmarkShapeTypeMigrations, bookmarkShapeTypeValidator } from './shapes/TLBookmarkShape'
import { drawShapeMigrations, drawShapeTypeValidator } from './shapes/TLDrawShape' import { drawShapeTypeMigrations, drawShapeTypeValidator } from './shapes/TLDrawShape'
import { embedShapeMigrations, embedShapeTypeValidator } from './shapes/TLEmbedShape' import { embedShapeTypeMigrations, embedShapeTypeValidator } from './shapes/TLEmbedShape'
import { frameShapeMigrations, frameShapeTypeValidator } from './shapes/TLFrameShape' import { frameShapeTypeMigrations, frameShapeTypeValidator } from './shapes/TLFrameShape'
import { geoShapeMigrations, geoShapeTypeValidator } from './shapes/TLGeoShape' import { geoShapeTypeMigrations, geoShapeTypeValidator } from './shapes/TLGeoShape'
import { groupShapeMigrations, groupShapeTypeValidator } from './shapes/TLGroupShape' import { groupShapeTypeMigrations, groupShapeTypeValidator } from './shapes/TLGroupShape'
import { imageShapeMigrations, imageShapeTypeValidator } from './shapes/TLImageShape' import { imageShapeTypeMigrations, imageShapeTypeValidator } from './shapes/TLImageShape'
import { lineShapeMigrations, lineShapeTypeValidator } from './shapes/TLLineShape' import { lineShapeTypeMigrations, lineShapeTypeValidator } from './shapes/TLLineShape'
import { noteShapeMigrations, noteShapeTypeValidator } from './shapes/TLNoteShape' import { noteShapeTypeMigrations, noteShapeTypeValidator } from './shapes/TLNoteShape'
import { textShapeMigrations, textShapeTypeValidator } from './shapes/TLTextShape' import { textShapeTypeMigrations, textShapeTypeValidator } from './shapes/TLTextShape'
import { videoShapeMigrations, videoShapeTypeValidator } from './shapes/TLVideoShape' import { videoShapeTypeMigrations, videoShapeTypeValidator } from './shapes/TLVideoShape'
const CORE_SHAPE_DEFS: readonly CustomShapeTypeInfo[] = [ const CORE_SHAPE_DEFS: readonly CustomShapeTypeInfo[] = [
{ type: 'draw', migrations: drawShapeMigrations, validator: drawShapeTypeValidator }, { type: 'draw', migrations: drawShapeTypeMigrations, validator: drawShapeTypeValidator },
{ type: 'text', migrations: textShapeMigrations, validator: textShapeTypeValidator }, { type: 'text', migrations: textShapeTypeMigrations, validator: textShapeTypeValidator },
{ type: 'line', migrations: lineShapeMigrations, validator: lineShapeTypeValidator }, { type: 'line', migrations: lineShapeTypeMigrations, validator: lineShapeTypeValidator },
{ type: 'arrow', migrations: arrowShapeMigrations, validator: arrowShapeTypeValidator }, { type: 'arrow', migrations: arrowShapeTypeMigrations, validator: arrowShapeTypeValidator },
{ type: 'image', migrations: imageShapeMigrations, validator: imageShapeTypeValidator }, { type: 'image', migrations: imageShapeTypeMigrations, validator: imageShapeTypeValidator },
{ type: 'video', migrations: videoShapeMigrations, validator: videoShapeTypeValidator }, { type: 'video', migrations: videoShapeTypeMigrations, validator: videoShapeTypeValidator },
{ type: 'geo', migrations: geoShapeMigrations, validator: geoShapeTypeValidator }, { type: 'geo', migrations: geoShapeTypeMigrations, validator: geoShapeTypeValidator },
{ type: 'note', migrations: noteShapeMigrations, validator: noteShapeTypeValidator }, { type: 'note', migrations: noteShapeTypeMigrations, validator: noteShapeTypeValidator },
{ type: 'group', migrations: groupShapeMigrations, validator: groupShapeTypeValidator }, { type: 'group', migrations: groupShapeTypeMigrations, validator: groupShapeTypeValidator },
{ {
type: 'bookmark', type: 'bookmark',
migrations: bookmarkShapeMigrations, migrations: bookmarkShapeTypeMigrations,
validator: bookmarkShapeTypeValidator, validator: bookmarkShapeTypeValidator,
}, },
{ type: 'frame', migrations: frameShapeMigrations, validator: frameShapeTypeValidator }, { type: 'frame', migrations: frameShapeTypeMigrations, validator: frameShapeTypeValidator },
{ type: 'embed', migrations: embedShapeMigrations, validator: embedShapeTypeValidator }, { type: 'embed', migrations: embedShapeTypeMigrations, validator: embedShapeTypeValidator },
] ]
/** @public */ /** @public */
export type CustomShapeTypeInfo = { export type CustomShapeTypeInfo = {
type: string type: string
migrations: ReturnType<typeof defineMigrations> migrations?: Migrations
validator?: StoreValidator<TLShape> validator?: StoreValidator<TLShape>
} }
@ -79,7 +85,9 @@ export function createTLSchema({
firstVersion: rootShapeTypeMigrations.firstVersion, firstVersion: rootShapeTypeMigrations.firstVersion,
migrators: rootShapeTypeMigrations.migrators, migrators: rootShapeTypeMigrations.migrators,
subTypeKey: 'type', subTypeKey: 'type',
subTypeMigrations: Object.fromEntries(allShapeDefs.map((def) => [def.type, def.migrations])), subTypeMigrations: Object.fromEntries(
allShapeDefs.map((def) => [def.type, def.migrations ?? {}])
) as Record<string, Migrations>,
}) })
let shapeValidator = T.union('type', { let shapeValidator = T.union('type', {

View file

@ -37,18 +37,8 @@ export {
type TLAssetPartial, type TLAssetPartial,
type TLAssetShape, type TLAssetShape,
} from './records/TLAsset' } from './records/TLAsset'
export { export { TLCamera, cameraTypeValidator, type TLCameraId } from './records/TLCamera'
TLCamera, export { TLDOCUMENT_ID, TLDocument, documentTypeValidator } from './records/TLDocument'
cameraTypeMigrations,
cameraTypeValidator,
type TLCameraId,
} from './records/TLCamera'
export {
TLDOCUMENT_ID,
TLDocument,
documentTypeMigrations,
documentTypeValidator,
} from './records/TLDocument'
export { export {
TLInstance, TLInstance,
instanceTypeMigrations, instanceTypeMigrations,
@ -63,7 +53,7 @@ export {
type TLInstancePageStateId, type TLInstancePageStateId,
} from './records/TLInstancePageState' } from './records/TLInstancePageState'
export { TLInstancePresence } from './records/TLInstancePresence' export { TLInstancePresence } from './records/TLInstancePresence'
export { TLPage, pageTypeMigrations, pageTypeValidator, type TLPageId } from './records/TLPage' export { TLPage, pageTypeValidator, type TLPageId } from './records/TLPage'
export { export {
createCustomShapeId, createCustomShapeId,
createShapeId, createShapeId,
@ -80,7 +70,7 @@ export {
type TLShapeType, type TLShapeType,
type TLUnknownShape, type TLUnknownShape,
} from './records/TLShape' } from './records/TLShape'
export { TLUser, userTypeMigrations, userTypeValidator, type TLUserId } from './records/TLUser' export { TLUser, userTypeValidator, type TLUserId } from './records/TLUser'
export { export {
TLUserDocument, TLUserDocument,
userDocumentTypeMigrations, userDocumentTypeMigrations,
@ -96,7 +86,7 @@ export {
export { storeMigrations } from './schema' export { storeMigrations } from './schema'
export { export {
TL_ARROW_TERMINAL_TYPE, TL_ARROW_TERMINAL_TYPE,
arrowShapeMigrations, arrowShapeTypeMigrations,
arrowShapeTypeValidator, arrowShapeTypeValidator,
arrowTerminalTypeValidator, arrowTerminalTypeValidator,
type TLArrowHeadModel, type TLArrowHeadModel,
@ -106,14 +96,14 @@ export {
type TLArrowTerminalType, type TLArrowTerminalType,
} from './shapes/TLArrowShape' } from './shapes/TLArrowShape'
export { export {
bookmarkShapeMigrations, bookmarkShapeTypeMigrations,
bookmarkShapeTypeValidator, bookmarkShapeTypeValidator,
type TLBookmarkShape, type TLBookmarkShape,
type TLBookmarkShapeProps, type TLBookmarkShapeProps,
} from './shapes/TLBookmarkShape' } from './shapes/TLBookmarkShape'
export { export {
TL_DRAW_SHAPE_SEGMENT_TYPE, TL_DRAW_SHAPE_SEGMENT_TYPE,
drawShapeMigrations, drawShapeTypeMigrations,
drawShapeTypeValidator, drawShapeTypeValidator,
type TLDrawShape, type TLDrawShape,
type TLDrawShapeProps, type TLDrawShapeProps,
@ -121,7 +111,7 @@ export {
} from './shapes/TLDrawShape' } from './shapes/TLDrawShape'
export { export {
EMBED_DEFINITIONS, EMBED_DEFINITIONS,
embedShapeMigrations, embedShapeTypeMigrations,
embedShapeTypeValidator, embedShapeTypeValidator,
tlEmbedShapePermissionDefaults, tlEmbedShapePermissionDefaults,
type EmbedDefinition, type EmbedDefinition,
@ -131,56 +121,52 @@ export {
type TLEmbedShapeProps, type TLEmbedShapeProps,
} from './shapes/TLEmbedShape' } from './shapes/TLEmbedShape'
export { export {
frameShapeMigrations,
frameShapeTypeValidator, frameShapeTypeValidator,
type TLFrameShape, type TLFrameShape,
type TLFrameShapeProps, type TLFrameShapeProps,
} from './shapes/TLFrameShape' } from './shapes/TLFrameShape'
export { export {
geoShapeMigrations, geoShapeTypeMigrations,
geoShapeTypeValidator, geoShapeTypeValidator,
type TLGeoShape, type TLGeoShape,
type TLGeoShapeProps, type TLGeoShapeProps,
} from './shapes/TLGeoShape' } from './shapes/TLGeoShape'
export { export {
groupShapeMigrations,
groupShapeTypeValidator, groupShapeTypeValidator,
type TLGroupShape, type TLGroupShape,
type TLGroupShapeProps, type TLGroupShapeProps,
} from './shapes/TLGroupShape' } from './shapes/TLGroupShape'
export { export {
iconShapeMigrations,
iconShapeTypeValidator, iconShapeTypeValidator,
type TLIconShape, type TLIconShape,
type TLIconShapeProps, type TLIconShapeProps,
} from './shapes/TLIconShape' } from './shapes/TLIconShape'
export { export {
imageShapeMigrations, imageShapeTypeMigrations,
imageShapeTypeValidator, imageShapeTypeValidator,
type TLImageCrop, type TLImageCrop,
type TLImageShape, type TLImageShape,
type TLImageShapeProps, type TLImageShapeProps,
} from './shapes/TLImageShape' } from './shapes/TLImageShape'
export { export {
lineShapeMigrations,
lineShapeTypeValidator, lineShapeTypeValidator,
type TLLineShape, type TLLineShape,
type TLLineShapeProps, type TLLineShapeProps,
} from './shapes/TLLineShape' } from './shapes/TLLineShape'
export { export {
noteShapeMigrations, noteShapeTypeMigrations,
noteShapeTypeValidator, noteShapeTypeValidator,
type TLNoteShape, type TLNoteShape,
type TLNoteShapeProps, type TLNoteShapeProps,
} from './shapes/TLNoteShape' } from './shapes/TLNoteShape'
export { export {
textShapeMigrations, textShapeTypeMigrations,
textShapeTypeValidator, textShapeTypeValidator,
type TLTextShape, type TLTextShape,
type TLTextShapeProps, type TLTextShapeProps,
} from './shapes/TLTextShape' } from './shapes/TLTextShape'
export { export {
videoShapeMigrations, videoShapeTypeMigrations,
videoShapeTypeValidator, videoShapeTypeValidator,
type TLVideoShape, type TLVideoShape,
type TLVideoShapeProps, type TLVideoShapeProps,

View file

@ -10,15 +10,15 @@ import { rootShapeTypeMigrations, TLShape } from './records/TLShape'
import { userDocumentTypeMigrations, userDocumentVersions } from './records/TLUserDocument' import { userDocumentTypeMigrations, userDocumentVersions } from './records/TLUserDocument'
import { userPresenceTypeMigrations } from './records/TLUserPresence' import { userPresenceTypeMigrations } from './records/TLUserPresence'
import { storeMigrations } from './schema' import { storeMigrations } from './schema'
import { arrowShapeMigrations } from './shapes/TLArrowShape' import { arrowShapeTypeMigrations } from './shapes/TLArrowShape'
import { bookmarkShapeMigrations } from './shapes/TLBookmarkShape' import { bookmarkShapeTypeMigrations } from './shapes/TLBookmarkShape'
import { drawShapeMigrations } from './shapes/TLDrawShape' import { drawShapeTypeMigrations } from './shapes/TLDrawShape'
import { embedShapeMigrations } from './shapes/TLEmbedShape' import { embedShapeTypeMigrations } from './shapes/TLEmbedShape'
import { geoShapeMigrations } from './shapes/TLGeoShape' import { geoShapeTypeMigrations } from './shapes/TLGeoShape'
import { imageShapeMigrations } from './shapes/TLImageShape' import { imageShapeTypeMigrations } from './shapes/TLImageShape'
import { noteShapeMigrations } from './shapes/TLNoteShape' import { noteShapeTypeMigrations } from './shapes/TLNoteShape'
import { textShapeMigrations } from './shapes/TLTextShape' import { textShapeTypeMigrations } from './shapes/TLTextShape'
import { videoShapeMigrations } from './shapes/TLVideoShape' import { videoShapeTypeMigrations } from './shapes/TLVideoShape'
const assetModules = fs const assetModules = fs
.readdirSync('src/assets') .readdirSync('src/assets')
@ -90,7 +90,9 @@ for (const [fileName, module] of allModules) {
test('all modules export migrations', () => { test('all modules export migrations', () => {
const modulesWithoutMigrations = allModules const modulesWithoutMigrations = allModules
.filter(([, module]) => !Object.keys(module).find((k) => k.endsWith('igrations'))) .filter(([, module]) => {
return !Object.keys(module).find((k) => k.endsWith('igrations'))
})
.map(([fileName]) => fileName) .map(([fileName]) => fileName)
// IF THIS LINE IS FAILING YOU NEED TO MAKE SURE THE MIGRATIONS ARE EXPORTED // IF THIS LINE IS FAILING YOU NEED TO MAKE SURE THE MIGRATIONS ARE EXPORTED
@ -280,10 +282,10 @@ describe('Adding snap mode', () => {
describe('Adding url props', () => { describe('Adding url props', () => {
for (const [name, { up, down }] of [ for (const [name, { up, down }] of [
['video shape', videoShapeMigrations.migrators[1]], ['video shape', videoShapeTypeMigrations.migrators[1]],
['note shape', noteShapeMigrations.migrators[1]], ['note shape', noteShapeTypeMigrations.migrators[1]],
['geo shape', geoShapeMigrations.migrators[1]], ['geo shape', geoShapeTypeMigrations.migrators[1]],
['image shape', imageShapeMigrations.migrators[1]], ['image shape', imageShapeTypeMigrations.migrators[1]],
] as const) { ] as const) {
test(`${name}: up works as expected`, () => { test(`${name}: up works as expected`, () => {
const before = { props: {} } const before = { props: {} }
@ -300,7 +302,7 @@ describe('Adding url props', () => {
}) })
describe('Bookmark null asset id', () => { describe('Bookmark null asset id', () => {
const { up, down } = bookmarkShapeMigrations.migrators[1] const { up, down } = bookmarkShapeTypeMigrations.migrators[1]
test('up works as expected', () => { test('up works as expected', () => {
const before = { props: {} } const before = { props: {} }
const after = { props: { assetId: null } } const after = { props: { assetId: null } }
@ -376,7 +378,7 @@ describe('Cleaning up junk data in instance.propsForNextShape', () => {
}) })
describe('Generating original URL from embed URL in GenOriginalUrlInEmbed', () => { describe('Generating original URL from embed URL in GenOriginalUrlInEmbed', () => {
const { up, down } = embedShapeMigrations.migrators[1] const { up, down } = embedShapeTypeMigrations.migrators[1]
test('up works as expected', () => { test('up works as expected', () => {
expect(up({ props: { url: 'https://codepen.io/Rplus/embed/PWZYRM' } })).toEqual({ expect(up({ props: { url: 'https://codepen.io/Rplus/embed/PWZYRM' } })).toEqual({
props: { props: {
@ -417,7 +419,7 @@ describe('Generating original URL from embed URL in GenOriginalUrlInEmbed', () =
}) })
describe('Adding isPen prop', () => { describe('Adding isPen prop', () => {
const { up, down } = drawShapeMigrations.migrators[1] const { up, down } = drawShapeTypeMigrations.migrators[1]
test('up works as expected with a shape that is not a pen shape', () => { test('up works as expected with a shape that is not a pen shape', () => {
expect( expect(
@ -502,8 +504,8 @@ describe('Adding isLocked prop', () => {
describe('Adding labelColor prop to geo / arrow shapes', () => { describe('Adding labelColor prop to geo / arrow shapes', () => {
for (const [name, { up, down }] of [ for (const [name, { up, down }] of [
['arrow shape', arrowShapeMigrations.migrators[1]], ['arrow shape', arrowShapeTypeMigrations.migrators[1]],
['geo shape', geoShapeMigrations.migrators[2]], ['geo shape', geoShapeTypeMigrations.migrators[2]],
] as const) { ] as const) {
test(`${name}: up works as expected`, () => { test(`${name}: up works as expected`, () => {
expect(up({ props: { color: 'red' } })).toEqual({ expect(up({ props: { color: 'red' } })).toEqual({
@ -600,9 +602,9 @@ describe('Adding zoomBrush prop to instance', () => {
describe('Removing align=justify from shape align props', () => { describe('Removing align=justify from shape align props', () => {
for (const [name, { up, down }] of [ for (const [name, { up, down }] of [
['text', textShapeMigrations.migrators[1]], ['text', textShapeTypeMigrations.migrators[1]],
['note', noteShapeMigrations.migrators[2]], ['note', noteShapeTypeMigrations.migrators[2]],
['geo', geoShapeMigrations.migrators[3]], ['geo', geoShapeTypeMigrations.migrators[3]],
] as const) { ] as const) {
test(`${name}: up works as expected`, () => { test(`${name}: up works as expected`, () => {
expect(up({ props: { align: 'justify' } })).toEqual({ expect(up({ props: { align: 'justify' } })).toEqual({
@ -622,7 +624,7 @@ describe('Removing align=justify from shape align props', () => {
}) })
describe('Add crop=null to image shapes', () => { describe('Add crop=null to image shapes', () => {
const { up, down } = imageShapeMigrations.migrators[2] const { up, down } = imageShapeTypeMigrations.migrators[2]
test('up works as expected', () => { test('up works as expected', () => {
expect(up({ props: { w: 100 } })).toEqual({ expect(up({ props: { w: 100 } })).toEqual({
props: { w: 100, crop: null }, props: { w: 100, crop: null },
@ -655,7 +657,7 @@ describe('Adding instance_presence to the schema', () => {
}) })
describe('Adding check-box to geo shape', () => { describe('Adding check-box to geo shape', () => {
const { up, down } = geoShapeMigrations.migrators[4] const { up, down } = geoShapeTypeMigrations.migrators[4]
test('up works as expected', () => { test('up works as expected', () => {
expect(up({ props: { geo: 'rectangle' } })).toEqual({ props: { geo: 'rectangle' } }) expect(up({ props: { geo: 'rectangle' } })).toEqual({ props: { geo: 'rectangle' } })
@ -667,7 +669,7 @@ describe('Adding check-box to geo shape', () => {
}) })
describe('Add verticalAlign to geo shape', () => { describe('Add verticalAlign to geo shape', () => {
const { up, down } = geoShapeMigrations.migrators[5] const { up, down } = geoShapeTypeMigrations.migrators[5]
test('up works as expected', () => { test('up works as expected', () => {
expect(up({ props: { type: 'ellipse' } })).toEqual({ expect(up({ props: { type: 'ellipse' } })).toEqual({

View file

@ -14,7 +14,6 @@ import { TLShape } from './TLShape'
/** @public */ /** @public */
export type TLAsset = TLImageAsset | TLVideoAsset | TLBookmarkAsset export type TLAsset = TLImageAsset | TLVideoAsset | TLBookmarkAsset
// --- VALIDATION ---
/** @public */ /** @public */
export const assetTypeValidator: T.Validator<TLAsset> = T.model( export const assetTypeValidator: T.Validator<TLAsset> = T.model(
'asset', 'asset',
@ -25,20 +24,8 @@ export const assetTypeValidator: T.Validator<TLAsset> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */ /** @public */
export const assetTypeMigrations = defineMigrations({ export const assetTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
// STEP 3: Add an up+down migration for the new version here
migrators: {},
subTypeKey: 'type', subTypeKey: 'type',
subTypeMigrations: { subTypeMigrations: {
image: imageAssetMigrations, image: imageAssetMigrations,

View file

@ -16,7 +16,6 @@ export interface TLCamera extends BaseRecord<'camera'> {
/** @public */ /** @public */
export type TLCameraId = ID<TLCamera> export type TLCameraId = ID<TLCamera>
// --- VALIDATION ---
/** @public */ /** @public */
export const cameraTypeValidator: T.Validator<TLCamera> = T.model( export const cameraTypeValidator: T.Validator<TLCamera> = T.model(
'camera', 'camera',
@ -29,25 +28,8 @@ export const cameraTypeValidator: T.Validator<TLCamera> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */
export const cameraTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
// STEP 3: Add an up+down migration for the new version here
migrators: {},
})
/** @public */ /** @public */
export const TLCamera = createRecordType<TLCamera>('camera', { export const TLCamera = createRecordType<TLCamera>('camera', {
migrations: cameraTypeMigrations,
validator: cameraTypeValidator, validator: cameraTypeValidator,
scope: 'instance', scope: 'instance',
}).withDefaultProperties( }).withDefaultProperties(
@ -57,3 +39,6 @@ export const TLCamera = createRecordType<TLCamera>('camera', {
z: 1, z: 1,
}) })
) )
/** @public */
export const cameraTypeMigrations = defineMigrations({})

View file

@ -10,7 +10,6 @@ export interface TLDocument extends BaseRecord<'document'> {
gridSize: number gridSize: number
} }
// --- VALIDATION ---
/** @public */ /** @public */
export const documentTypeValidator: T.Validator<TLDocument> = T.model( export const documentTypeValidator: T.Validator<TLDocument> = T.model(
'document', 'document',
@ -21,25 +20,8 @@ export const documentTypeValidator: T.Validator<TLDocument> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */
export const documentTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
// STEP 3: Add an up+down migration for the new version here
migrators: {},
})
/** @public */ /** @public */
export const TLDocument = createRecordType<TLDocument>('document', { export const TLDocument = createRecordType<TLDocument>('document', {
migrations: documentTypeMigrations,
validator: documentTypeValidator, validator: documentTypeValidator,
scope: 'document', scope: 'document',
}).withDefaultProperties( }).withDefaultProperties(
@ -51,3 +33,6 @@ export const TLDocument = createRecordType<TLDocument>('document', {
// all document records have the same ID: 'document:document' // all document records have the same ID: 'document:document'
/** @public */ /** @public */
export const TLDOCUMENT_ID: ID<TLDocument> = TLDocument.createCustomId('document') export const TLDOCUMENT_ID: ID<TLDocument> = TLDocument.createCustomId('document')
/** @public */
export const documentTypeMigrations = defineMigrations({})

View file

@ -53,7 +53,6 @@ export interface TLInstance extends BaseRecord<'instance'> {
/** @public */ /** @public */
export type TLInstanceId = ID<TLInstance> export type TLInstanceId = ID<TLInstance>
// --- VALIDATION ---
/** @public */ /** @public */
export const instanceTypeValidator: T.Validator<TLInstance> = T.model( export const instanceTypeValidator: T.Validator<TLInstance> = T.model(
'instance', 'instance',
@ -91,11 +90,7 @@ export const instanceTypeValidator: T.Validator<TLInstance> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddTransparentExportBgs: 1, AddTransparentExportBgs: 1,
RemoveDialog: 2, RemoveDialog: 2,
AddToolLockMode: 3, AddToolLockMode: 3,
@ -110,10 +105,7 @@ const Versions = {
/** @public */ /** @public */
export const instanceTypeMigrations = defineMigrations({ export const instanceTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddScribbleDelay, currentVersion: Versions.AddScribbleDelay,
// STEP 3: Add an up+down migration for the new version here
migrators: { migrators: {
[Versions.AddTransparentExportBgs]: { [Versions.AddTransparentExportBgs]: {
up: (instance: TLInstance) => { up: (instance: TLInstance) => {

View file

@ -26,7 +26,6 @@ export interface TLInstancePageState extends BaseRecord<'instance_page_state'> {
focusLayerId: TLShapeId | null focusLayerId: TLShapeId | null
} }
// --- VALIDATION ---
/** @public */ /** @public */
export const instancePageStateTypeValidator: T.Validator<TLInstancePageState> = T.model( export const instancePageStateTypeValidator: T.Validator<TLInstancePageState> = T.model(
'instance_page_state', 'instance_page_state',
@ -46,17 +45,12 @@ export const instancePageStateTypeValidator: T.Validator<TLInstancePageState> =
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddCroppingId: 1, AddCroppingId: 1,
} as const } as const
/** @public */ /** @public */
export const instancePageStateMigrations = defineMigrations({ export const instancePageStateMigrations = defineMigrations({
firstVersion: Versions.Initial,
currentVersion: Versions.AddCroppingId, currentVersion: Versions.AddCroppingId,
migrators: { migrators: {
[Versions.AddCroppingId]: { [Versions.AddCroppingId]: {

View file

@ -65,20 +65,13 @@ export const instancePresenceTypeValidator: T.Validator<TLInstancePresence> = T.
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddScribbleDelay: 1, AddScribbleDelay: 1,
} as const } as const
export const instancePresenceTypeMigrations = defineMigrations({ export const instancePresenceTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddScribbleDelay, currentVersion: Versions.AddScribbleDelay,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddScribbleDelay]: { [Versions.AddScribbleDelay]: {
up: (instance) => { up: (instance) => {
if (instance.scribble !== null) { if (instance.scribble !== null) {

View file

@ -15,7 +15,6 @@ export interface TLPage extends BaseRecord<'page'> {
/** @public */ /** @public */
export type TLPageId = ID<TLPage> export type TLPageId = ID<TLPage>
// --- VALIDATION ---
/** @public */ /** @public */
export const pageTypeValidator: T.Validator<TLPage> = T.model( export const pageTypeValidator: T.Validator<TLPage> = T.model(
'page', 'page',
@ -27,25 +26,11 @@ export const pageTypeValidator: T.Validator<TLPage> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */
export const pageTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
// STEP 3: Add an up+down migration for the new version here
migrators: {},
})
/** @public */ /** @public */
export const TLPage = createRecordType<TLPage>('page', { export const TLPage = createRecordType<TLPage>('page', {
migrations: pageTypeMigrations,
validator: pageTypeValidator, validator: pageTypeValidator,
scope: 'document', scope: 'document',
}) })
/** @public */
export const pageTypeMigrations = defineMigrations({})

View file

@ -68,21 +68,14 @@ export type TLParentId = TLPageId | TLShapeId
/** @public */ /** @public */
export type TLNullableShapeProps = { [K in TLShapeProp]?: TLShapeProps[K] | null } export type TLNullableShapeProps = { [K in TLShapeProp]?: TLShapeProps[K] | null }
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddIsLocked: 1, AddIsLocked: 1,
} as const } as const
/** @internal */ /** @internal */
export const rootShapeTypeMigrations = defineMigrations({ export const rootShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddIsLocked, currentVersion: Versions.AddIsLocked,
firstVersion: Versions.Initial,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddIsLocked]: { [Versions.AddIsLocked]: {
up: (record) => { up: (record) => {
return { return {

View file

@ -15,7 +15,6 @@ export interface TLUser extends BaseRecord<'user'> {
/** @public */ /** @public */
export type TLUserId = ID<TLUser> export type TLUserId = ID<TLUser>
// --- VALIDATION ---
/** @public */ /** @public */
export const userTypeValidator: T.Validator<TLUser> = T.model( export const userTypeValidator: T.Validator<TLUser> = T.model(
'user', 'user',
@ -27,26 +26,8 @@ export const userTypeValidator: T.Validator<TLUser> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */
export const userTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})
/** @public */ /** @public */
export const TLUser = createRecordType<TLUser>('user', { export const TLUser = createRecordType<TLUser>('user', {
migrations: userTypeMigrations,
validator: userTypeValidator, validator: userTypeValidator,
scope: 'instance', scope: 'instance',
}).withDefaultProperties((): Omit<TLUser, 'id' | 'typeName'> => { }).withDefaultProperties((): Omit<TLUser, 'id' | 'typeName'> => {
@ -59,3 +40,6 @@ export const TLUser = createRecordType<TLUser>('user', {
locale, locale,
} }
}) })
/** @public */
export const userTypeMigrations = defineMigrations({})

View file

@ -26,7 +26,6 @@ export interface TLUserDocument extends BaseRecord<'user_document'> {
/** @public */ /** @public */
export type TLUserDocumentId = ID<TLUserDocument> export type TLUserDocumentId = ID<TLUserDocument>
// --- VALIDATION ---
/** @public */ /** @public */
export const userDocumentTypeValidator: T.Validator<TLUserDocument> = T.model( export const userDocumentTypeValidator: T.Validator<TLUserDocument> = T.model(
'user_document', 'user_document',
@ -44,11 +43,7 @@ export const userDocumentTypeValidator: T.Validator<TLUserDocument> = T.model(
}) })
) )
// --- MIGRATIONS --- export const Versions = {
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
export const userDocumentVersions = {
Initial: 0,
AddSnapMode: 1, AddSnapMode: 1,
AddMissingIsMobileMode: 2, AddMissingIsMobileMode: 2,
RemoveIsReadOnly: 3, RemoveIsReadOnly: 3,
@ -56,12 +51,9 @@ export const userDocumentVersions = {
/** @public */ /** @public */
export const userDocumentTypeMigrations = defineMigrations({ export const userDocumentTypeMigrations = defineMigrations({
firstVersion: userDocumentVersions.Initial, currentVersion: Versions.RemoveIsReadOnly,
// STEP 2: Update the current version to point to your latest version
currentVersion: userDocumentVersions.RemoveIsReadOnly,
// STEP 3: Add an up+down migration for the new version here
migrators: { migrators: {
[userDocumentVersions.AddSnapMode]: { [Versions.AddSnapMode]: {
up: (userDocument: TLUserDocument) => { up: (userDocument: TLUserDocument) => {
return { ...userDocument, isSnapMode: false } return { ...userDocument, isSnapMode: false }
}, },
@ -69,7 +61,7 @@ export const userDocumentTypeMigrations = defineMigrations({
return userDocument return userDocument
}, },
}, },
[userDocumentVersions.AddMissingIsMobileMode]: { [Versions.AddMissingIsMobileMode]: {
up: (userDocument: TLUserDocument) => { up: (userDocument: TLUserDocument) => {
return { ...userDocument, isMobileMode: userDocument.isMobileMode ?? false } return { ...userDocument, isMobileMode: userDocument.isMobileMode ?? false }
}, },
@ -77,7 +69,7 @@ export const userDocumentTypeMigrations = defineMigrations({
return userDocument return userDocument
}, },
}, },
[userDocumentVersions.RemoveIsReadOnly]: { [Versions.RemoveIsReadOnly]: {
up: ({ isReadOnly: _, ...userDocument }: TLUserDocument & { isReadOnly: boolean }) => { up: ({ isReadOnly: _, ...userDocument }: TLUserDocument & { isReadOnly: boolean }) => {
return userDocument return userDocument
}, },
@ -107,3 +99,5 @@ export const TLUserDocument = createRecordType<TLUserDocument>('user_document',
lastUsedTabId: null, lastUsedTabId: null,
}) })
) )
export { Versions as userDocumentVersions }

View file

@ -18,7 +18,6 @@ export interface TLUserPresence extends BaseRecord<'user_presence'> {
/** @public */ /** @public */
export type TLUserPresenceId = ID<TLUserPresence> export type TLUserPresenceId = ID<TLUserPresence>
// --- VALIDATION ---
/** @public */ /** @public */
export const userPresenceTypeValidator: T.Validator<TLUserPresence> = T.model( export const userPresenceTypeValidator: T.Validator<TLUserPresence> = T.model(
'user_presence', 'user_presence',
@ -34,21 +33,14 @@ export const userPresenceTypeValidator: T.Validator<TLUserPresence> = T.model(
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddViewportPageBounds: 1, AddViewportPageBounds: 1,
} as const } as const
/** @public */ /** @public */
export const userPresenceTypeMigrations = defineMigrations({ export const userPresenceTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddViewportPageBounds, currentVersion: Versions.AddViewportPageBounds,
firstVersion: Versions.Initial,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddViewportPageBounds]: { [Versions.AddViewportPageBounds]: {
up: (record) => { up: (record) => {
return { return {

View file

@ -1,22 +1,15 @@
import { defineMigrations, StoreSnapshot } from '@tldraw/tlstore' import { defineMigrations, StoreSnapshot } from '@tldraw/tlstore'
import { TLRecord } from './TLRecord' import { TLRecord } from './TLRecord'
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
RemoveCodeAndIconShapeTypes: 1, RemoveCodeAndIconShapeTypes: 1,
AddInstancePresenceType: 2, AddInstancePresenceType: 2,
} as const } as const
/** @public */ /** @public */
export const storeMigrations = defineMigrations({ export const storeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddInstancePresenceType, currentVersion: Versions.AddInstancePresenceType,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.RemoveCodeAndIconShapeTypes]: { [Versions.RemoveCodeAndIconShapeTypes]: {
up: (store: StoreSnapshot<TLRecord>) => { up: (store: StoreSnapshot<TLRecord>) => {
return Object.fromEntries( return Object.fromEntries(

View file

@ -70,7 +70,6 @@ export interface TLArrowHeadModel {
type: TLArrowheadType type: TLArrowheadType
} }
// --- VALIDATION ---
/** @public */ /** @public */
export const arrowTerminalTypeValidator: T.Validator<TLArrowTerminal> = T.union('type', { export const arrowTerminalTypeValidator: T.Validator<TLArrowTerminal> = T.union('type', {
binding: T.object({ binding: T.object({
@ -106,21 +105,14 @@ export const arrowShapeTypeValidator: T.Validator<TLArrowShape> = createShapeVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddLabelColor: 1, AddLabelColor: 1,
} as const } as const
/** @public */ /** @public */
export const arrowShapeMigrations = defineMigrations({ export const arrowShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddLabelColor, currentVersion: Versions.AddLabelColor,
firstVersion: Versions.Initial,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddLabelColor]: { [Versions.AddLabelColor]: {
up: (record) => { up: (record) => {
return { return {

View file

@ -17,7 +17,6 @@ export type TLBookmarkShapeProps = {
/** @public */ /** @public */
export type TLBookmarkShape = TLBaseShape<'bookmark', TLBookmarkShapeProps> export type TLBookmarkShape = TLBaseShape<'bookmark', TLBookmarkShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape> = createShapeValidator( export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape> = createShapeValidator(
'bookmark', 'bookmark',
@ -30,20 +29,13 @@ export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape> = createSh
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
NullAssetId: 1, NullAssetId: 1,
} as const } as const
/** @public */ /** @public */
export const bookmarkShapeMigrations = defineMigrations({ export const bookmarkShapeTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.NullAssetId, currentVersion: Versions.NullAssetId,
// STEP 3: Add an up+down migration for the new version here
migrators: { migrators: {
[Versions.NullAssetId]: { [Versions.NullAssetId]: {
up: (shape: TLBookmarkShape) => { up: (shape: TLBookmarkShape) => {

View file

@ -37,7 +37,6 @@ export type TLDrawShapeProps = {
/** @public */ /** @public */
export type TLDrawShape = TLBaseShape<'draw', TLDrawShapeProps> export type TLDrawShape = TLBaseShape<'draw', TLDrawShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const drawShapeTypeValidator: T.Validator<TLDrawShape> = createShapeValidator( export const drawShapeTypeValidator: T.Validator<TLDrawShape> = createShapeValidator(
'draw', 'draw',
@ -59,21 +58,14 @@ export const drawShapeTypeValidator: T.Validator<TLDrawShape> = createShapeValid
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddInPen: 1, AddInPen: 1,
} as const } as const
/** @public */ /** @public */
export const drawShapeMigrations = defineMigrations({ export const drawShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddInPen, currentVersion: Versions.AddInPen,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddInPen]: { [Versions.AddInPen]: {
up: (shape) => { up: (shape) => {
// Rather than checking to see whether the shape is a pen at runtime, // Rather than checking to see whether the shape is a pen at runtime,

View file

@ -78,7 +78,6 @@ export type TLEmbedShapeProps = {
/** @public */ /** @public */
export type TLEmbedShape = TLBaseShape<'embed', TLEmbedShapeProps> export type TLEmbedShape = TLBaseShape<'embed', TLEmbedShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const embedShapeTypeValidator: T.Validator<TLEmbedShape> = createShapeValidator( export const embedShapeTypeValidator: T.Validator<TLEmbedShape> = createShapeValidator(
'embed', 'embed',
@ -605,21 +604,14 @@ export const EMBED_DEFINITIONS = [
}, },
] as const satisfies readonly EmbedDefinition[] ] as const satisfies readonly EmbedDefinition[]
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
GenOriginalUrlInEmbed: 1, GenOriginalUrlInEmbed: 1,
} as const } as const
/** @public */ /** @public */
export const embedShapeMigrations = defineMigrations({ export const embedShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.GenOriginalUrlInEmbed, currentVersion: Versions.GenOriginalUrlInEmbed,
firstVersion: Versions.Initial,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.GenOriginalUrlInEmbed]: { [Versions.GenOriginalUrlInEmbed]: {
// add tmpOldUrl property // add tmpOldUrl property
up: (shape) => { up: (shape) => {

View file

@ -15,7 +15,6 @@ export type TLFrameShapeProps = {
/** @public */ /** @public */
export type TLFrameShape = TLBaseShape<'frame', TLFrameShapeProps> export type TLFrameShape = TLBaseShape<'frame', TLFrameShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const frameShapeTypeValidator: T.Validator<TLFrameShape> = createShapeValidator( export const frameShapeTypeValidator: T.Validator<TLFrameShape> = createShapeValidator(
'frame', 'frame',
@ -27,19 +26,5 @@ export const frameShapeTypeValidator: T.Validator<TLFrameShape> = createShapeVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */ /** @public */
export const frameShapeMigrations = defineMigrations({ export const frameShapeTypeMigrations = defineMigrations({})
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})

View file

@ -46,7 +46,6 @@ export type TLGeoShapeProps = {
/** @public */ /** @public */
export type TLGeoShape = TLBaseShape<'geo', TLGeoShapeProps> export type TLGeoShape = TLBaseShape<'geo', TLGeoShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const geoShapeTypeValidator: T.Validator<TLGeoShape> = createShapeValidator( export const geoShapeTypeValidator: T.Validator<TLGeoShape> = createShapeValidator(
'geo', 'geo',
@ -69,11 +68,7 @@ export const geoShapeTypeValidator: T.Validator<TLGeoShape> = createShapeValidat
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddUrlProp: 1, AddUrlProp: 1,
AddLabelColor: 2, AddLabelColor: 2,
RemoveJustify: 3, RemoveJustify: 3,
@ -82,12 +77,9 @@ const Versions = {
} as const } as const
/** @public */ /** @public */
export const geoShapeMigrations = defineMigrations({ export const geoShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddVerticalAlign, currentVersion: Versions.AddVerticalAlign,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: { [Versions.AddUrlProp]: {
up: (shape) => { up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } } return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -12,7 +12,6 @@ export type TLGroupShapeProps = {
/** @public */ /** @public */
export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps> export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const groupShapeTypeValidator: T.Validator<TLGroupShape> = createShapeValidator( export const groupShapeTypeValidator: T.Validator<TLGroupShape> = createShapeValidator(
'group', 'group',
@ -21,19 +20,5 @@ export const groupShapeTypeValidator: T.Validator<TLGroupShape> = createShapeVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */ /** @public */
export const groupShapeMigrations = defineMigrations({ export const groupShapeTypeMigrations = defineMigrations({})
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})

View file

@ -23,7 +23,6 @@ export type TLIconShapeProps = {
/** @public */ /** @public */
export type TLIconShape = TLBaseShape<'icon', TLIconShapeProps> export type TLIconShape = TLBaseShape<'icon', TLIconShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const iconShapeTypeValidator: T.Validator<TLIconShape> = createShapeValidator( export const iconShapeTypeValidator: T.Validator<TLIconShape> = createShapeValidator(
'icon', 'icon',
@ -37,19 +36,5 @@ export const iconShapeTypeValidator: T.Validator<TLIconShape> = createShapeValid
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */ /** @public */
export const iconShapeMigrations = defineMigrations({ export const iconShapeTypeMigrations = defineMigrations({})
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})

View file

@ -32,7 +32,6 @@ export const cropValidator = T.object({
/** @public */ /** @public */
export type TLImageShape = TLBaseShape<'image', TLImageShapeProps> export type TLImageShape = TLBaseShape<'image', TLImageShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const imageShapeTypeValidator: T.Validator<TLImageShape> = createShapeValidator( export const imageShapeTypeValidator: T.Validator<TLImageShape> = createShapeValidator(
'image', 'image',
@ -47,22 +46,15 @@ export const imageShapeTypeValidator: T.Validator<TLImageShape> = createShapeVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddUrlProp: 1, AddUrlProp: 1,
AddCropProp: 2, AddCropProp: 2,
} as const } as const
/** @public */ /** @public */
export const imageShapeMigrations = defineMigrations({ export const imageShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddCropProp, currentVersion: Versions.AddCropProp,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: { [Versions.AddUrlProp]: {
up: (shape) => { up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } } return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -26,7 +26,6 @@ export type TLLineShapeProps = {
/** @public */ /** @public */
export type TLLineShape = TLBaseShape<'line', TLLineShapeProps> export type TLLineShape = TLBaseShape<'line', TLLineShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const lineShapeTypeValidator: T.Validator<TLLineShape> = createShapeValidator( export const lineShapeTypeValidator: T.Validator<TLLineShape> = createShapeValidator(
'line', 'line',
@ -40,19 +39,5 @@ export const lineShapeTypeValidator: T.Validator<TLLineShape> = createShapeValid
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = {
Initial: 0,
} as const
/** @public */ /** @public */
export const lineShapeMigrations = defineMigrations({ export const lineShapeTypeMigrations = defineMigrations({})
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.Initial,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
},
})

View file

@ -25,7 +25,6 @@ export type TLNoteShapeProps = {
/** @public */ /** @public */
export type TLNoteShape = TLBaseShape<'note', TLNoteShapeProps> export type TLNoteShape = TLBaseShape<'note', TLNoteShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const noteShapeTypeValidator: T.Validator<TLNoteShape> = createShapeValidator( export const noteShapeTypeValidator: T.Validator<TLNoteShape> = createShapeValidator(
'note', 'note',
@ -41,22 +40,15 @@ export const noteShapeTypeValidator: T.Validator<TLNoteShape> = createShapeValid
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddUrlProp: 1, AddUrlProp: 1,
RemoveJustify: 2, RemoveJustify: 2,
} as const } as const
/** @public */ /** @public */
export const noteShapeMigrations = defineMigrations({ export const noteShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.RemoveJustify, currentVersion: Versions.RemoveJustify,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: { [Versions.AddUrlProp]: {
up: (shape) => { up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } } return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -26,7 +26,6 @@ export type TLTextShapeProps = {
/** @public */ /** @public */
export type TLTextShape = TLBaseShape<'text', TLTextShapeProps> export type TLTextShape = TLBaseShape<'text', TLTextShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const textShapeTypeValidator: T.Validator<TLTextShape> = createShapeValidator( export const textShapeTypeValidator: T.Validator<TLTextShape> = createShapeValidator(
'text', 'text',
@ -43,18 +42,12 @@ export const textShapeTypeValidator: T.Validator<TLTextShape> = createShapeValid
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
RemoveJustify: 1, RemoveJustify: 1,
} as const } as const
/** @public */ /** @public */
export const textShapeMigrations = defineMigrations({ export const textShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.RemoveJustify, currentVersion: Versions.RemoveJustify,
migrators: { migrators: {
[Versions.RemoveJustify]: { [Versions.RemoveJustify]: {

View file

@ -19,7 +19,6 @@ export type TLVideoShapeProps = {
/** @public */ /** @public */
export type TLVideoShape = TLBaseShape<'video', TLVideoShapeProps> export type TLVideoShape = TLBaseShape<'video', TLVideoShapeProps>
// --- VALIDATION ---
/** @public */ /** @public */
export const videoShapeTypeValidator: T.Validator<TLVideoShape> = createShapeValidator( export const videoShapeTypeValidator: T.Validator<TLVideoShape> = createShapeValidator(
'video', 'video',
@ -34,21 +33,14 @@ export const videoShapeTypeValidator: T.Validator<TLVideoShape> = createShapeVal
}) })
) )
// --- MIGRATIONS ---
// STEP 1: Add a new version number here, give it a meaningful name.
// It should be 1 higher than the current version
const Versions = { const Versions = {
Initial: 0,
AddUrlProp: 1, AddUrlProp: 1,
} as const } as const
/** @public */ /** @public */
export const videoShapeMigrations = defineMigrations({ export const videoShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddUrlProp, currentVersion: Versions.AddUrlProp,
migrators: { migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: { [Versions.AddUrlProp]: {
up: (shape) => { up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } } return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -47,12 +47,16 @@ export function createRecordType<R extends BaseRecord>(typeName: R['typeName'],
}): RecordType<R, keyof Omit<R, 'id' | 'typeName'>>; }): RecordType<R, keyof Omit<R, 'id' | 'typeName'>>;
// @public (undocumented) // @public (undocumented)
export function defineMigrations<FirstVersion extends number, CurrentVersion extends number>({ firstVersion, currentVersion, migrators, subTypeKey, subTypeMigrations, }: { export function defineMigrations<FirstVersion extends EMPTY_SYMBOL | number = EMPTY_SYMBOL, CurrentVersion extends EMPTY_SYMBOL | Exclude<number, 0> = EMPTY_SYMBOL>(opts: {
firstVersion: FirstVersion; firstVersion?: CurrentVersion extends number ? FirstVersion : never;
currentVersion: CurrentVersion; currentVersion?: CurrentVersion;
migrators: { migrators?: CurrentVersion extends number ? FirstVersion extends number ? CurrentVersion extends FirstVersion ? {
[version in Exclude<Range_2<1, CurrentVersion>, 0>]: Migration;
} : {
[version in Exclude<Range_2<FirstVersion, CurrentVersion>, FirstVersion>]: Migration; [version in Exclude<Range_2<FirstVersion, CurrentVersion>, FirstVersion>]: Migration;
}; } : {
[version in Exclude<Range_2<1, CurrentVersion>, 0>]: Migration;
} : never;
subTypeKey?: string; subTypeKey?: string;
subTypeMigrations?: Record<string, BaseMigrationsInfo>; subTypeMigrations?: Record<string, BaseMigrationsInfo>;
}): Migrations; }): Migrations;
@ -106,9 +110,9 @@ export function migrateRecord<R extends BaseRecord>({ record, migrations, fromVe
}): MigrationResult<R>; }): MigrationResult<R>;
// @public (undocumented) // @public (undocumented)
export type Migration<T = any> = { export type Migration<Before = any, After = any> = {
up: (oldState: T) => T; up: (oldState: Before) => After;
down: (newState: T) => T; down: (newState: After) => Before;
}; };
// @public (undocumented) // @public (undocumented)

View file

@ -1,29 +1,49 @@
import { BaseRecord, isRecord } from './BaseRecord' import { BaseRecord, isRecord } from './BaseRecord'
import { SerializedSchema } from './StoreSchema' import { SerializedSchema } from './StoreSchema'
type EMPTY_SYMBOL = symbol
/** @public */ /** @public */
export function defineMigrations<FirstVersion extends number, CurrentVersion extends number>({ export function defineMigrations<
firstVersion, FirstVersion extends number | EMPTY_SYMBOL = EMPTY_SYMBOL,
currentVersion, CurrentVersion extends Exclude<number, 0> | EMPTY_SYMBOL = EMPTY_SYMBOL
migrators, >(opts: {
subTypeKey, firstVersion?: CurrentVersion extends number ? FirstVersion : never
subTypeMigrations, currentVersion?: CurrentVersion
}: { migrators?: CurrentVersion extends number
firstVersion: FirstVersion ? FirstVersion extends number
currentVersion: CurrentVersion ? CurrentVersion extends FirstVersion
migrators: { ? { [version in Exclude<Range<1, CurrentVersion>, 0>]: Migration }
[version in Exclude<Range<FirstVersion, CurrentVersion>, FirstVersion>]: Migration : { [version in Exclude<Range<FirstVersion, CurrentVersion>, FirstVersion>]: Migration }
} : { [version in Exclude<Range<1, CurrentVersion>, 0>]: Migration }
: never
subTypeKey?: string subTypeKey?: string
subTypeMigrations?: Record<string, BaseMigrationsInfo> subTypeMigrations?: Record<string, BaseMigrationsInfo>
}): Migrations { }): Migrations {
return { currentVersion, firstVersion, migrators, subTypeKey, subTypeMigrations } const { currentVersion, firstVersion, migrators = {}, subTypeKey, subTypeMigrations } = opts
// Some basic guards against impossible version combinations, some of which will be caught by TypeScript
if (typeof currentVersion === 'number' && typeof firstVersion === 'number') {
if ((currentVersion as number) === (firstVersion as number)) {
throw Error(`Current version is equal to initial version.`)
} else if (currentVersion < firstVersion) {
throw Error(`Current version is lower than initial version.`)
}
}
return {
firstVersion: (firstVersion as number) ?? 0, // defaults
currentVersion: (currentVersion as number) ?? 0, // defaults
migrators,
subTypeKey,
subTypeMigrations,
}
} }
/** @public */ /** @public */
export type Migration<T = any> = { export type Migration<Before = any, After = any> = {
up: (oldState: T) => T up: (oldState: Before) => After
down: (newState: T) => T down: (newState: After) => Before
} }
interface BaseMigrationsInfo { interface BaseMigrationsInfo {

View file

@ -0,0 +1,229 @@
import { defineMigrations } from '../migrate'
const Versions = {
Initial: 0,
January: 1,
February: 2,
March: 3,
} as const
describe('define migrations tests', () => {
it('defines migrations', () => {
expect(() => {
// no versions
defineMigrations({
// @ts-expect-error first version without current version
firstVersion: Versions.Initial,
})
}).not.toThrowError()
expect(() => {
// no versions
defineMigrations({
// @ts-expect-error first version without current version
firstVersion: Versions.February,
})
}).not.toThrowError()
expect(() => {
// empty migrators
defineMigrations({
// @ts-expect-error
migrators: {},
})
}).not.toThrowError()
expect(() => {
// no versions!
defineMigrations({
// @ts-expect-error
migrators: {
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
// wrong current version!
defineMigrations({
currentVersion: Versions.January,
migrators: {
// @ts-expect-error
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
defineMigrations({
currentVersion: Versions.February,
migrators: {
// has a default zero version
[Versions.January]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
// has a current version
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
// can't provide only first version
defineMigrations({
// @ts-expect-error first version without current version
firstVersion: Versions.January,
// @ts-expect-error migrators without current version
migrators: {},
})
}).not.toThrowError()
expect(() => {
// same version
defineMigrations({
firstVersion: Versions.Initial,
currentVersion: Versions.Initial,
migrators: {},
})
}).toThrowError()
expect(() => {
// only first version
defineMigrations({
// @ts-expect-error
firstVersion: Versions.January,
// @ts-expect-error
migrators: {},
})
}).not.toThrowError()
expect(() => {
// missing only version
defineMigrations({
firstVersion: Versions.January,
currentVersion: Versions.January,
// @ts-expect-error
migrators: {},
})
}).toThrowError()
expect(() => {
// only version, explicit start and current
defineMigrations({
firstVersion: Versions.January,
currentVersion: Versions.January,
migrators: {
[Versions.January]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).toThrowError()
expect(() => {
// missing later versions
defineMigrations({
firstVersion: Versions.January,
currentVersion: Versions.February,
// @ts-expect-error
migrators: {},
})
}).not.toThrowError()
expect(() => {
// missing later versions
defineMigrations({
firstVersion: Versions.Initial,
currentVersion: Versions.February,
// @ts-expect-error
migrators: {
[Versions.January]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
// missing earlier versions
defineMigrations({
firstVersion: Versions.Initial,
currentVersion: Versions.February,
// @ts-expect-error
migrators: {
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
// got em all
defineMigrations({
firstVersion: Versions.Initial,
currentVersion: Versions.February,
migrators: {
[Versions.January]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
// got em all starting later
defineMigrations({
firstVersion: Versions.January,
currentVersion: Versions.March,
migrators: {
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
[Versions.March]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
expect(() => {
// first migration should be first version + 1
defineMigrations({
firstVersion: Versions.February,
currentVersion: Versions.March,
migrators: {
// @ts-expect-error
[Versions.February]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
[Versions.March]: {
up: (rec: any) => rec,
down: (rec: any) => rec,
},
},
})
}).not.toThrowError()
})
})

View file

@ -4,20 +4,12 @@ import { createRecordType } from '../RecordType'
import { StoreSchema } from '../StoreSchema' import { StoreSchema } from '../StoreSchema'
import { defineMigrations } from '../migrate' import { defineMigrations } from '../migrate'
const UserVersion = {
Initial: 0,
} as const
/** A user of tldraw */ /** A user of tldraw */
interface User extends BaseRecord<'user'> { interface User extends BaseRecord<'user'> {
name: string name: string
} }
const userMigrations = defineMigrations({ const userMigrations = defineMigrations({})
currentVersion: UserVersion.Initial,
firstVersion: UserVersion.Initial,
migrators: {},
})
const User = createRecordType<User>('user', { const User = createRecordType<User>('user', {
migrations: userMigrations, migrations: userMigrations,
@ -32,14 +24,6 @@ const User = createRecordType<User>('user', {
scope: 'document', scope: 'document',
}) })
const ShapeVersion = {
Initial: 0,
} as const
const RectangleVersion = {
Initial: 0,
} as const
interface Shape<Props> extends BaseRecord<'shape'> { interface Shape<Props> extends BaseRecord<'shape'> {
type: string type: string
x: number x: number
@ -58,22 +42,15 @@ interface OvalProps {
borderStyle: 'solid' | 'dashed' borderStyle: 'solid' | 'dashed'
} }
const shapeMigrations = defineMigrations({ const shapeTypeMigrations = defineMigrations({
currentVersion: ShapeVersion.Initial,
firstVersion: ShapeVersion.Initial,
migrators: {},
subTypeKey: 'type', subTypeKey: 'type',
subTypeMigrations: { subTypeMigrations: {
rectangle: defineMigrations({ rectangle: defineMigrations({}),
currentVersion: RectangleVersion.Initial,
firstVersion: RectangleVersion.Initial,
migrators: {},
}),
}, },
}) })
const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', { const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', {
migrations: shapeMigrations, migrations: shapeTypeMigrations,
validator: { validator: {
validate: (record) => { validate: (record) => {
assert( assert(
@ -100,7 +77,7 @@ interface Org extends BaseRecord<'org'> {
} }
const Org = createRecordType<Org>('org', { const Org = createRecordType<Org>('org', {
migrations: defineMigrations({ currentVersion: 0, firstVersion: 0, migrators: {} }), migrations: defineMigrations({}),
validator: { validator: {
validate: (record) => { validate: (record) => {
assert( assert(
@ -119,6 +96,6 @@ export const testSchemaV0 = StoreSchema.create(
org: Org, org: Org,
}, },
{ {
snapshotMigrations: defineMigrations({ currentVersion: 0, firstVersion: 0, migrators: {} }), snapshotMigrations: defineMigrations({}),
} }
) )

View file

@ -6,7 +6,6 @@ import { StoreSchema } from '../StoreSchema'
import { defineMigrations } from '../migrate' import { defineMigrations } from '../migrate'
const UserVersion = { const UserVersion = {
Initial: 0,
AddLocale: 1, AddLocale: 1,
AddPhoneNumber: 2, AddPhoneNumber: 2,
} as const } as const
@ -20,7 +19,6 @@ interface User extends BaseRecord<'user'> {
const userMigrations = defineMigrations({ const userMigrations = defineMigrations({
currentVersion: UserVersion.AddPhoneNumber, currentVersion: UserVersion.AddPhoneNumber,
firstVersion: UserVersion.Initial,
migrators: { migrators: {
[UserVersion.AddLocale]: { [UserVersion.AddLocale]: {
up: (record) => ({ up: (record) => ({
@ -69,18 +67,15 @@ const User = createRecordType<User>('user', {
})) }))
const ShapeVersion = { const ShapeVersion = {
Initial: 0,
AddRotation: 1, AddRotation: 1,
AddParent: 2, AddParent: 2,
} as const } as const
const RectangleVersion = { const RectangleVersion = {
Initial: 0,
AddOpacity: 1, AddOpacity: 1,
} as const } as const
const OvalVersion = { const OvalVersion = {
Initial: 0,
AddBorderStyle: 1, AddBorderStyle: 1,
} as const } as const
@ -104,9 +99,8 @@ interface OvalProps {
borderStyle: 'solid' | 'dashed' borderStyle: 'solid' | 'dashed'
} }
const shapeMigrations = defineMigrations({ const shapeTypeMigrations = defineMigrations({
currentVersion: ShapeVersion.AddParent, currentVersion: ShapeVersion.AddParent,
firstVersion: ShapeVersion.Initial,
migrators: { migrators: {
[ShapeVersion.AddRotation]: { [ShapeVersion.AddRotation]: {
up: (record) => ({ up: (record) => ({
@ -135,7 +129,6 @@ const shapeMigrations = defineMigrations({
subTypeMigrations: { subTypeMigrations: {
rectangle: defineMigrations({ rectangle: defineMigrations({
currentVersion: RectangleVersion.AddOpacity, currentVersion: RectangleVersion.AddOpacity,
firstVersion: RectangleVersion.Initial,
migrators: { migrators: {
[RectangleVersion.AddOpacity]: { [RectangleVersion.AddOpacity]: {
up: (record) => ({ up: (record) => ({
@ -157,7 +150,6 @@ const shapeMigrations = defineMigrations({
}), }),
oval: defineMigrations({ oval: defineMigrations({
currentVersion: OvalVersion.AddBorderStyle, currentVersion: OvalVersion.AddBorderStyle,
firstVersion: OvalVersion.Initial,
migrators: { migrators: {
[OvalVersion.AddBorderStyle]: { [OvalVersion.AddBorderStyle]: {
up: (record) => ({ up: (record) => ({
@ -181,7 +173,7 @@ const shapeMigrations = defineMigrations({
}) })
const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', { const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', {
migrations: shapeMigrations, migrations: shapeTypeMigrations,
validator: { validator: {
validate: (record) => { validate: (record) => {
assert(record && typeof record === 'object') assert(record && typeof record === 'object')
@ -202,13 +194,11 @@ const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', {
})) }))
const StoreVersions = { const StoreVersions = {
Initial: 0,
RemoveOrg: 1, RemoveOrg: 1,
} }
const snapshotMigrations = defineMigrations({ const snapshotMigrations = defineMigrations({
currentVersion: StoreVersions.RemoveOrg, currentVersion: StoreVersions.RemoveOrg,
firstVersion: StoreVersions.Initial,
migrators: { migrators: {
[StoreVersions.RemoveOrg]: { [StoreVersions.RemoveOrg]: {
up: (store: StoreSnapshot<any>) => { up: (store: StoreSnapshot<any>) => {