[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/tldraw-assets.json
apps/webdriver/www/index.js
apps/vscode/extension/editor/*
apps/examples/www

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
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 { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { App } from '../../App'
@ -202,7 +202,7 @@ export const TLNoteShapeDef = defineShape<TLNoteShape, TLNoteUtil>({
getShapeUtil: () => TLNoteUtil,
type: 'note',
validator: noteShapeTypeValidator,
migrations: noteShapeMigrations,
migrations: noteShapeTypeMigrations,
})
function getGrowY(app: App, shape: TLNoteShape, prevGrowY = 0) {

View file

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

View file

@ -1,5 +1,5 @@
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 { track } from 'signia-react'
import { HTMLContainer } from '../../../components/HTMLContainer'
@ -54,7 +54,7 @@ export const TLVideoShapeDef = defineShape<TLVideoShape, TLVideoUtil>({
type: 'video',
getShapeUtil: () => TLVideoUtil,
validator: videoShapeTypeValidator,
migrations: videoShapeMigrations,
migrations: videoShapeTypeMigrations,
})
// 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',
getShapeUtil: () => __TopLeftSnapOnlyShapeUtil,
validator: { validate: (record) => record as __TopLeftSnapOnlyShape },
migrations: defineMigrations({ currentVersion: 0, firstVersion: 0, migrators: {} }),
migrations: defineMigrations({}),
})
const configWithCustomShape = new TldrawEditorConfig({ shapes: [__TopLeftSnapOnlyShapeDef] })

View file

@ -5,7 +5,6 @@
```ts
import { BaseRecord } from '@tldraw/tlstore';
import { defineMigrations } from '@tldraw/tlstore';
import { ID } from '@tldraw/tlstore';
import { Migrations } 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">;
// @public (undocumented)
export const arrowShapeMigrations: Migrations;
export const arrowShapeTypeMigrations: Migrations;
// @public (undocumented)
export const arrowShapeTypeValidator: T.Validator<TLArrowShape>;
@ -48,7 +47,7 @@ export const bookmarkAssetMigrations: Migrations;
export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset>;
// @public (undocumented)
export const bookmarkShapeMigrations: Migrations;
export const bookmarkShapeTypeMigrations: Migrations;
// @public (undocumented)
export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape>;
@ -65,9 +64,6 @@ export interface Box2dModel {
y: number;
}
// @public (undocumented)
export const cameraTypeMigrations: Migrations;
// @public (undocumented)
export const cameraTypeValidator: T.Validator<TLCamera>;
@ -124,7 +120,7 @@ export const cursorValidator: T.Validator<TLCursor>;
// @public (undocumented)
export type CustomShapeTypeInfo = {
type: string;
migrations: ReturnType<typeof defineMigrations>;
migrations?: Migrations;
validator?: StoreValidator<TLShape>;
};
@ -134,14 +130,11 @@ export const dashValidator: T.Validator<"dashed" | "dotted" | "draw" | "solid">;
// @internal (undocumented)
export const defaultDerivePresenceState: (store: TLStore) => Signal<null | TLInstancePresence>;
// @public (undocumented)
export const documentTypeMigrations: Migrations;
// @public (undocumented)
export const documentTypeValidator: T.Validator<TLDocument>;
// @public (undocumented)
export const drawShapeMigrations: Migrations;
export const drawShapeTypeMigrations: Migrations;
// @public (undocumented)
export const drawShapeTypeValidator: T.Validator<TLDrawShape>;
@ -334,7 +327,7 @@ export type EmbedDefinition = {
};
// @public (undocumented)
export const embedShapeMigrations: Migrations;
export const embedShapeTypeMigrations: Migrations;
// @public (undocumented)
export const embedShapeTypeValidator: T.Validator<TLEmbedShape>;
@ -351,14 +344,11 @@ export function fixupRecord(oldRecord: TLRecord): {
// @internal (undocumented)
export const fontValidator: T.Validator<"draw" | "mono" | "sans" | "serif">;
// @public (undocumented)
export const frameShapeMigrations: Migrations;
// @public (undocumented)
export const frameShapeTypeValidator: T.Validator<TLFrameShape>;
// @public (undocumented)
export const geoShapeMigrations: Migrations;
export const geoShapeTypeMigrations: Migrations;
// @public (undocumented)
export const geoShapeTypeValidator: T.Validator<TLGeoShape>;
@ -366,18 +356,12 @@ export const geoShapeTypeValidator: T.Validator<TLGeoShape>;
// @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">;
// @public (undocumented)
export const groupShapeMigrations: Migrations;
// @public (undocumented)
export const groupShapeTypeValidator: T.Validator<TLGroupShape>;
// @public (undocumented)
export const handleTypeValidator: T.Validator<TLHandle>;
// @public (undocumented)
export const iconShapeMigrations: Migrations;
// @public (undocumented)
export const iconShapeTypeValidator: T.Validator<TLIconShape>;
@ -394,7 +378,7 @@ export const imageAssetMigrations: Migrations;
export const imageAssetTypeValidator: T.Validator<TLImageAsset>;
// @public (undocumented)
export const imageShapeMigrations: Migrations;
export const imageShapeTypeMigrations: Migrations;
// @public (undocumented)
export const imageShapeTypeValidator: T.Validator<TLImageShape>;
@ -420,14 +404,11 @@ export function isShape(record?: BaseRecord<string>): record is TLShape;
// @public (undocumented)
export function isShapeId(id?: string): id is TLShapeId;
// @public (undocumented)
export const lineShapeMigrations: Migrations;
// @public (undocumented)
export const lineShapeTypeValidator: T.Validator<TLLineShape>;
// @public (undocumented)
export const noteShapeMigrations: Migrations;
export const noteShapeTypeMigrations: Migrations;
// @public (undocumented)
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)
export const pageIdValidator: T.Validator<TLPageId>;
// @public (undocumented)
export const pageTypeMigrations: Migrations;
// @public (undocumented)
export const pageTypeValidator: T.Validator<TLPage>;
@ -477,7 +455,7 @@ export const splineValidator: T.Validator<"cubic" | "line">;
export const storeMigrations: Migrations;
// @public (undocumented)
export const textShapeMigrations: Migrations;
export const textShapeTypeMigrations: Migrations;
// @public (undocumented)
export const textShapeTypeValidator: T.Validator<TLTextShape>;
@ -1390,9 +1368,6 @@ export const userPresenceTypeMigrations: Migrations;
// @public (undocumented)
export const userPresenceTypeValidator: T.Validator<TLUserPresence>;
// @public (undocumented)
export const userTypeMigrations: Migrations;
// @public (undocumented)
export const userTypeValidator: T.Validator<TLUser>;
@ -1413,7 +1388,7 @@ export const videoAssetMigrations: Migrations;
export const videoAssetTypeValidator: T.Validator<TLVideoAsset>;
// @public (undocumented)
export const videoShapeMigrations: Migrations;
export const videoShapeTypeMigrations: Migrations;
// @public (undocumented)
export const videoShapeTypeValidator: T.Validator<TLVideoShape>;

View file

@ -33,9 +33,6 @@
"scripts": {
"test": "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",
"format": "yarn run -T prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
"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 */
export const bookmarkAssetTypeValidator: T.Validator<TLBookmarkAsset> = createAssetValidator(
'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 */
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: {},
})
export const bookmarkAssetMigrations = defineMigrations({})

View file

@ -16,7 +16,6 @@ export type TLImageAsset = TLBaseAsset<
}
>
// --- VALIDATION ---
/** @public */
export const imageAssetTypeValidator: T.Validator<TLImageAsset> = createAssetValidator(
'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 = {
Initial: 0,
AddIsAnimated: 1,
RenameWidthHeight: 2,
} as const
/** @public */
export const imageAssetMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.RenameWidthHeight,
// STEP 3: Add an up+down migration for the new version here
migrators: {
[Versions.AddIsAnimated]: {
up: (asset) => {

View file

@ -17,7 +17,6 @@ export type TLVideoAsset = TLBaseAsset<
}
>
// --- VALIDATION ---
/** @public */
export const videoAssetTypeValidator: T.Validator<TLVideoAsset> = createAssetValidator(
'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 = {
Initial: 0,
AddIsAnimated: 1,
RenameWidthHeight: 2,
} as const
/** @public */
export const videoAssetMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.RenameWidthHeight,
// STEP 3: Add an up+down migration for the new version here
migrators: {
[Versions.AddIsAnimated]: {
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 { Signal } from 'signia'
import { TLRecord } from './TLRecord'
@ -16,42 +22,42 @@ import { TLUser } from './records/TLUser'
import { TLUserDocument } from './records/TLUserDocument'
import { TLUserPresence } from './records/TLUserPresence'
import { storeMigrations } from './schema'
import { arrowShapeMigrations, arrowShapeTypeValidator } from './shapes/TLArrowShape'
import { bookmarkShapeMigrations, bookmarkShapeTypeValidator } from './shapes/TLBookmarkShape'
import { drawShapeMigrations, drawShapeTypeValidator } from './shapes/TLDrawShape'
import { embedShapeMigrations, embedShapeTypeValidator } from './shapes/TLEmbedShape'
import { frameShapeMigrations, frameShapeTypeValidator } from './shapes/TLFrameShape'
import { geoShapeMigrations, geoShapeTypeValidator } from './shapes/TLGeoShape'
import { groupShapeMigrations, groupShapeTypeValidator } from './shapes/TLGroupShape'
import { imageShapeMigrations, imageShapeTypeValidator } from './shapes/TLImageShape'
import { lineShapeMigrations, lineShapeTypeValidator } from './shapes/TLLineShape'
import { noteShapeMigrations, noteShapeTypeValidator } from './shapes/TLNoteShape'
import { textShapeMigrations, textShapeTypeValidator } from './shapes/TLTextShape'
import { videoShapeMigrations, videoShapeTypeValidator } from './shapes/TLVideoShape'
import { arrowShapeTypeMigrations, arrowShapeTypeValidator } from './shapes/TLArrowShape'
import { bookmarkShapeTypeMigrations, bookmarkShapeTypeValidator } from './shapes/TLBookmarkShape'
import { drawShapeTypeMigrations, drawShapeTypeValidator } from './shapes/TLDrawShape'
import { embedShapeTypeMigrations, embedShapeTypeValidator } from './shapes/TLEmbedShape'
import { frameShapeTypeMigrations, frameShapeTypeValidator } from './shapes/TLFrameShape'
import { geoShapeTypeMigrations, geoShapeTypeValidator } from './shapes/TLGeoShape'
import { groupShapeTypeMigrations, groupShapeTypeValidator } from './shapes/TLGroupShape'
import { imageShapeTypeMigrations, imageShapeTypeValidator } from './shapes/TLImageShape'
import { lineShapeTypeMigrations, lineShapeTypeValidator } from './shapes/TLLineShape'
import { noteShapeTypeMigrations, noteShapeTypeValidator } from './shapes/TLNoteShape'
import { textShapeTypeMigrations, textShapeTypeValidator } from './shapes/TLTextShape'
import { videoShapeTypeMigrations, videoShapeTypeValidator } from './shapes/TLVideoShape'
const CORE_SHAPE_DEFS: readonly CustomShapeTypeInfo[] = [
{ type: 'draw', migrations: drawShapeMigrations, validator: drawShapeTypeValidator },
{ type: 'text', migrations: textShapeMigrations, validator: textShapeTypeValidator },
{ type: 'line', migrations: lineShapeMigrations, validator: lineShapeTypeValidator },
{ type: 'arrow', migrations: arrowShapeMigrations, validator: arrowShapeTypeValidator },
{ type: 'image', migrations: imageShapeMigrations, validator: imageShapeTypeValidator },
{ type: 'video', migrations: videoShapeMigrations, validator: videoShapeTypeValidator },
{ type: 'geo', migrations: geoShapeMigrations, validator: geoShapeTypeValidator },
{ type: 'note', migrations: noteShapeMigrations, validator: noteShapeTypeValidator },
{ type: 'group', migrations: groupShapeMigrations, validator: groupShapeTypeValidator },
{ type: 'draw', migrations: drawShapeTypeMigrations, validator: drawShapeTypeValidator },
{ type: 'text', migrations: textShapeTypeMigrations, validator: textShapeTypeValidator },
{ type: 'line', migrations: lineShapeTypeMigrations, validator: lineShapeTypeValidator },
{ type: 'arrow', migrations: arrowShapeTypeMigrations, validator: arrowShapeTypeValidator },
{ type: 'image', migrations: imageShapeTypeMigrations, validator: imageShapeTypeValidator },
{ type: 'video', migrations: videoShapeTypeMigrations, validator: videoShapeTypeValidator },
{ type: 'geo', migrations: geoShapeTypeMigrations, validator: geoShapeTypeValidator },
{ type: 'note', migrations: noteShapeTypeMigrations, validator: noteShapeTypeValidator },
{ type: 'group', migrations: groupShapeTypeMigrations, validator: groupShapeTypeValidator },
{
type: 'bookmark',
migrations: bookmarkShapeMigrations,
migrations: bookmarkShapeTypeMigrations,
validator: bookmarkShapeTypeValidator,
},
{ type: 'frame', migrations: frameShapeMigrations, validator: frameShapeTypeValidator },
{ type: 'embed', migrations: embedShapeMigrations, validator: embedShapeTypeValidator },
{ type: 'frame', migrations: frameShapeTypeMigrations, validator: frameShapeTypeValidator },
{ type: 'embed', migrations: embedShapeTypeMigrations, validator: embedShapeTypeValidator },
]
/** @public */
export type CustomShapeTypeInfo = {
type: string
migrations: ReturnType<typeof defineMigrations>
migrations?: Migrations
validator?: StoreValidator<TLShape>
}
@ -79,7 +85,9 @@ export function createTLSchema({
firstVersion: rootShapeTypeMigrations.firstVersion,
migrators: rootShapeTypeMigrations.migrators,
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', {

View file

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

View file

@ -10,15 +10,15 @@ import { rootShapeTypeMigrations, TLShape } from './records/TLShape'
import { userDocumentTypeMigrations, userDocumentVersions } from './records/TLUserDocument'
import { userPresenceTypeMigrations } from './records/TLUserPresence'
import { storeMigrations } from './schema'
import { arrowShapeMigrations } from './shapes/TLArrowShape'
import { bookmarkShapeMigrations } from './shapes/TLBookmarkShape'
import { drawShapeMigrations } from './shapes/TLDrawShape'
import { embedShapeMigrations } from './shapes/TLEmbedShape'
import { geoShapeMigrations } from './shapes/TLGeoShape'
import { imageShapeMigrations } from './shapes/TLImageShape'
import { noteShapeMigrations } from './shapes/TLNoteShape'
import { textShapeMigrations } from './shapes/TLTextShape'
import { videoShapeMigrations } from './shapes/TLVideoShape'
import { arrowShapeTypeMigrations } from './shapes/TLArrowShape'
import { bookmarkShapeTypeMigrations } from './shapes/TLBookmarkShape'
import { drawShapeTypeMigrations } from './shapes/TLDrawShape'
import { embedShapeTypeMigrations } from './shapes/TLEmbedShape'
import { geoShapeTypeMigrations } from './shapes/TLGeoShape'
import { imageShapeTypeMigrations } from './shapes/TLImageShape'
import { noteShapeTypeMigrations } from './shapes/TLNoteShape'
import { textShapeTypeMigrations } from './shapes/TLTextShape'
import { videoShapeTypeMigrations } from './shapes/TLVideoShape'
const assetModules = fs
.readdirSync('src/assets')
@ -90,7 +90,9 @@ for (const [fileName, module] of allModules) {
test('all modules export migrations', () => {
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)
// 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', () => {
for (const [name, { up, down }] of [
['video shape', videoShapeMigrations.migrators[1]],
['note shape', noteShapeMigrations.migrators[1]],
['geo shape', geoShapeMigrations.migrators[1]],
['image shape', imageShapeMigrations.migrators[1]],
['video shape', videoShapeTypeMigrations.migrators[1]],
['note shape', noteShapeTypeMigrations.migrators[1]],
['geo shape', geoShapeTypeMigrations.migrators[1]],
['image shape', imageShapeTypeMigrations.migrators[1]],
] as const) {
test(`${name}: up works as expected`, () => {
const before = { props: {} }
@ -300,7 +302,7 @@ describe('Adding url props', () => {
})
describe('Bookmark null asset id', () => {
const { up, down } = bookmarkShapeMigrations.migrators[1]
const { up, down } = bookmarkShapeTypeMigrations.migrators[1]
test('up works as expected', () => {
const before = { props: {} }
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', () => {
const { up, down } = embedShapeMigrations.migrators[1]
const { up, down } = embedShapeTypeMigrations.migrators[1]
test('up works as expected', () => {
expect(up({ props: { url: 'https://codepen.io/Rplus/embed/PWZYRM' } })).toEqual({
props: {
@ -417,7 +419,7 @@ describe('Generating original URL from embed URL in GenOriginalUrlInEmbed', () =
})
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', () => {
expect(
@ -502,8 +504,8 @@ describe('Adding isLocked prop', () => {
describe('Adding labelColor prop to geo / arrow shapes', () => {
for (const [name, { up, down }] of [
['arrow shape', arrowShapeMigrations.migrators[1]],
['geo shape', geoShapeMigrations.migrators[2]],
['arrow shape', arrowShapeTypeMigrations.migrators[1]],
['geo shape', geoShapeTypeMigrations.migrators[2]],
] as const) {
test(`${name}: up works as expected`, () => {
expect(up({ props: { color: 'red' } })).toEqual({
@ -600,9 +602,9 @@ describe('Adding zoomBrush prop to instance', () => {
describe('Removing align=justify from shape align props', () => {
for (const [name, { up, down }] of [
['text', textShapeMigrations.migrators[1]],
['note', noteShapeMigrations.migrators[2]],
['geo', geoShapeMigrations.migrators[3]],
['text', textShapeTypeMigrations.migrators[1]],
['note', noteShapeTypeMigrations.migrators[2]],
['geo', geoShapeTypeMigrations.migrators[3]],
] as const) {
test(`${name}: up works as expected`, () => {
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', () => {
const { up, down } = imageShapeMigrations.migrators[2]
const { up, down } = imageShapeTypeMigrations.migrators[2]
test('up works as expected', () => {
expect(up({ props: { w: 100 } })).toEqual({
props: { w: 100, crop: null },
@ -655,7 +657,7 @@ describe('Adding instance_presence to the schema', () => {
})
describe('Adding check-box to geo shape', () => {
const { up, down } = geoShapeMigrations.migrators[4]
const { up, down } = geoShapeTypeMigrations.migrators[4]
test('up works as expected', () => {
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', () => {
const { up, down } = geoShapeMigrations.migrators[5]
const { up, down } = geoShapeTypeMigrations.migrators[5]
test('up works as expected', () => {
expect(up({ props: { type: 'ellipse' } })).toEqual({

View file

@ -14,7 +14,6 @@ import { TLShape } from './TLShape'
/** @public */
export type TLAsset = TLImageAsset | TLVideoAsset | TLBookmarkAsset
// --- VALIDATION ---
/** @public */
export const assetTypeValidator: T.Validator<TLAsset> = T.model(
'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 */
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',
subTypeMigrations: {
image: imageAssetMigrations,

View file

@ -16,7 +16,6 @@ export interface TLCamera extends BaseRecord<'camera'> {
/** @public */
export type TLCameraId = ID<TLCamera>
// --- VALIDATION ---
/** @public */
export const cameraTypeValidator: T.Validator<TLCamera> = T.model(
'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 */
export const TLCamera = createRecordType<TLCamera>('camera', {
migrations: cameraTypeMigrations,
validator: cameraTypeValidator,
scope: 'instance',
}).withDefaultProperties(
@ -57,3 +39,6 @@ export const TLCamera = createRecordType<TLCamera>('camera', {
z: 1,
})
)
/** @public */
export const cameraTypeMigrations = defineMigrations({})

View file

@ -10,7 +10,6 @@ export interface TLDocument extends BaseRecord<'document'> {
gridSize: number
}
// --- VALIDATION ---
/** @public */
export const documentTypeValidator: T.Validator<TLDocument> = T.model(
'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 */
export const TLDocument = createRecordType<TLDocument>('document', {
migrations: documentTypeMigrations,
validator: documentTypeValidator,
scope: 'document',
}).withDefaultProperties(
@ -51,3 +33,6 @@ export const TLDocument = createRecordType<TLDocument>('document', {
// all document records have the same ID: 'document:document'
/** @public */
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 */
export type TLInstanceId = ID<TLInstance>
// --- VALIDATION ---
/** @public */
export const instanceTypeValidator: T.Validator<TLInstance> = T.model(
'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 = {
Initial: 0,
AddTransparentExportBgs: 1,
RemoveDialog: 2,
AddToolLockMode: 3,
@ -110,10 +105,7 @@ const Versions = {
/** @public */
export const instanceTypeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddScribbleDelay,
// STEP 3: Add an up+down migration for the new version here
migrators: {
[Versions.AddTransparentExportBgs]: {
up: (instance: TLInstance) => {

View file

@ -26,7 +26,6 @@ export interface TLInstancePageState extends BaseRecord<'instance_page_state'> {
focusLayerId: TLShapeId | null
}
// --- VALIDATION ---
/** @public */
export const instancePageStateTypeValidator: T.Validator<TLInstancePageState> = T.model(
'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 = {
Initial: 0,
AddCroppingId: 1,
} as const
/** @public */
export const instancePageStateMigrations = defineMigrations({
firstVersion: Versions.Initial,
currentVersion: Versions.AddCroppingId,
migrators: {
[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 = {
Initial: 0,
AddScribbleDelay: 1,
} as const
export const instancePresenceTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddScribbleDelay,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddScribbleDelay]: {
up: (instance) => {
if (instance.scribble !== null) {

View file

@ -15,7 +15,6 @@ export interface TLPage extends BaseRecord<'page'> {
/** @public */
export type TLPageId = ID<TLPage>
// --- VALIDATION ---
/** @public */
export const pageTypeValidator: T.Validator<TLPage> = T.model(
'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 */
export const TLPage = createRecordType<TLPage>('page', {
migrations: pageTypeMigrations,
validator: pageTypeValidator,
scope: 'document',
})
/** @public */
export const pageTypeMigrations = defineMigrations({})

View file

@ -68,21 +68,14 @@ export type TLParentId = TLPageId | TLShapeId
/** @public */
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 = {
Initial: 0,
AddIsLocked: 1,
} as const
/** @internal */
export const rootShapeTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddIsLocked,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddIsLocked]: {
up: (record) => {
return {

View file

@ -15,7 +15,6 @@ export interface TLUser extends BaseRecord<'user'> {
/** @public */
export type TLUserId = ID<TLUser>
// --- VALIDATION ---
/** @public */
export const userTypeValidator: T.Validator<TLUser> = T.model(
'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 */
export const TLUser = createRecordType<TLUser>('user', {
migrations: userTypeMigrations,
validator: userTypeValidator,
scope: 'instance',
}).withDefaultProperties((): Omit<TLUser, 'id' | 'typeName'> => {
@ -59,3 +40,6 @@ export const TLUser = createRecordType<TLUser>('user', {
locale,
}
})
/** @public */
export const userTypeMigrations = defineMigrations({})

View file

@ -26,7 +26,6 @@ export interface TLUserDocument extends BaseRecord<'user_document'> {
/** @public */
export type TLUserDocumentId = ID<TLUserDocument>
// --- VALIDATION ---
/** @public */
export const userDocumentTypeValidator: T.Validator<TLUserDocument> = T.model(
'user_document',
@ -44,11 +43,7 @@ export const userDocumentTypeValidator: T.Validator<TLUserDocument> = 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
export const userDocumentVersions = {
Initial: 0,
export const Versions = {
AddSnapMode: 1,
AddMissingIsMobileMode: 2,
RemoveIsReadOnly: 3,
@ -56,12 +51,9 @@ export const userDocumentVersions = {
/** @public */
export const userDocumentTypeMigrations = defineMigrations({
firstVersion: userDocumentVersions.Initial,
// 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
currentVersion: Versions.RemoveIsReadOnly,
migrators: {
[userDocumentVersions.AddSnapMode]: {
[Versions.AddSnapMode]: {
up: (userDocument: TLUserDocument) => {
return { ...userDocument, isSnapMode: false }
},
@ -69,7 +61,7 @@ export const userDocumentTypeMigrations = defineMigrations({
return userDocument
},
},
[userDocumentVersions.AddMissingIsMobileMode]: {
[Versions.AddMissingIsMobileMode]: {
up: (userDocument: TLUserDocument) => {
return { ...userDocument, isMobileMode: userDocument.isMobileMode ?? false }
},
@ -77,7 +69,7 @@ export const userDocumentTypeMigrations = defineMigrations({
return userDocument
},
},
[userDocumentVersions.RemoveIsReadOnly]: {
[Versions.RemoveIsReadOnly]: {
up: ({ isReadOnly: _, ...userDocument }: TLUserDocument & { isReadOnly: boolean }) => {
return userDocument
},
@ -107,3 +99,5 @@ export const TLUserDocument = createRecordType<TLUserDocument>('user_document',
lastUsedTabId: null,
})
)
export { Versions as userDocumentVersions }

View file

@ -18,7 +18,6 @@ export interface TLUserPresence extends BaseRecord<'user_presence'> {
/** @public */
export type TLUserPresenceId = ID<TLUserPresence>
// --- VALIDATION ---
/** @public */
export const userPresenceTypeValidator: T.Validator<TLUserPresence> = T.model(
'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 = {
Initial: 0,
AddViewportPageBounds: 1,
} as const
/** @public */
export const userPresenceTypeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
currentVersion: Versions.AddViewportPageBounds,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddViewportPageBounds]: {
up: (record) => {
return {

View file

@ -1,22 +1,15 @@
import { defineMigrations, StoreSnapshot } from '@tldraw/tlstore'
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 = {
Initial: 0,
RemoveCodeAndIconShapeTypes: 1,
AddInstancePresenceType: 2,
} as const
/** @public */
export const storeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
currentVersion: Versions.AddInstancePresenceType,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.RemoveCodeAndIconShapeTypes]: {
up: (store: StoreSnapshot<TLRecord>) => {
return Object.fromEntries(

View file

@ -70,7 +70,6 @@ export interface TLArrowHeadModel {
type: TLArrowheadType
}
// --- VALIDATION ---
/** @public */
export const arrowTerminalTypeValidator: T.Validator<TLArrowTerminal> = T.union('type', {
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 = {
Initial: 0,
AddLabelColor: 1,
} as const
/** @public */
export const arrowShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
export const arrowShapeTypeMigrations = defineMigrations({
currentVersion: Versions.AddLabelColor,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddLabelColor]: {
up: (record) => {
return {

View file

@ -17,7 +17,6 @@ export type TLBookmarkShapeProps = {
/** @public */
export type TLBookmarkShape = TLBaseShape<'bookmark', TLBookmarkShapeProps>
// --- VALIDATION ---
/** @public */
export const bookmarkShapeTypeValidator: T.Validator<TLBookmarkShape> = createShapeValidator(
'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 = {
Initial: 0,
NullAssetId: 1,
} as const
/** @public */
export const bookmarkShapeMigrations = defineMigrations({
firstVersion: Versions.Initial,
// STEP 2: Update the current version to point to your latest version
export const bookmarkShapeTypeMigrations = defineMigrations({
currentVersion: Versions.NullAssetId,
// STEP 3: Add an up+down migration for the new version here
migrators: {
[Versions.NullAssetId]: {
up: (shape: TLBookmarkShape) => {

View file

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

View file

@ -78,7 +78,6 @@ export type TLEmbedShapeProps = {
/** @public */
export type TLEmbedShape = TLBaseShape<'embed', TLEmbedShapeProps>
// --- VALIDATION ---
/** @public */
export const embedShapeTypeValidator: T.Validator<TLEmbedShape> = createShapeValidator(
'embed',
@ -605,21 +604,14 @@ export const EMBED_DEFINITIONS = [
},
] 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 = {
Initial: 0,
GenOriginalUrlInEmbed: 1,
} as const
/** @public */
export const embedShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
export const embedShapeTypeMigrations = defineMigrations({
currentVersion: Versions.GenOriginalUrlInEmbed,
firstVersion: Versions.Initial,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.GenOriginalUrlInEmbed]: {
// add tmpOldUrl property
up: (shape) => {

View file

@ -15,7 +15,6 @@ export type TLFrameShapeProps = {
/** @public */
export type TLFrameShape = TLBaseShape<'frame', TLFrameShapeProps>
// --- VALIDATION ---
/** @public */
export const frameShapeTypeValidator: T.Validator<TLFrameShape> = createShapeValidator(
'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 */
export const frameShapeMigrations = 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
},
})
export const frameShapeTypeMigrations = defineMigrations({})

View file

@ -46,7 +46,6 @@ export type TLGeoShapeProps = {
/** @public */
export type TLGeoShape = TLBaseShape<'geo', TLGeoShapeProps>
// --- VALIDATION ---
/** @public */
export const geoShapeTypeValidator: T.Validator<TLGeoShape> = createShapeValidator(
'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 = {
Initial: 0,
AddUrlProp: 1,
AddLabelColor: 2,
RemoveJustify: 3,
@ -82,12 +77,9 @@ const Versions = {
} as const
/** @public */
export const geoShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
export const geoShapeTypeMigrations = defineMigrations({
currentVersion: Versions.AddVerticalAlign,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: {
up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -12,7 +12,6 @@ export type TLGroupShapeProps = {
/** @public */
export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps>
// --- VALIDATION ---
/** @public */
export const groupShapeTypeValidator: T.Validator<TLGroupShape> = createShapeValidator(
'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 */
export const groupShapeMigrations = 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
},
})
export const groupShapeTypeMigrations = defineMigrations({})

View file

@ -23,7 +23,6 @@ export type TLIconShapeProps = {
/** @public */
export type TLIconShape = TLBaseShape<'icon', TLIconShapeProps>
// --- VALIDATION ---
/** @public */
export const iconShapeTypeValidator: T.Validator<TLIconShape> = createShapeValidator(
'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 */
export const iconShapeMigrations = 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
},
})
export const iconShapeTypeMigrations = defineMigrations({})

View file

@ -32,7 +32,6 @@ export const cropValidator = T.object({
/** @public */
export type TLImageShape = TLBaseShape<'image', TLImageShapeProps>
// --- VALIDATION ---
/** @public */
export const imageShapeTypeValidator: T.Validator<TLImageShape> = createShapeValidator(
'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 = {
Initial: 0,
AddUrlProp: 1,
AddCropProp: 2,
} as const
/** @public */
export const imageShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
export const imageShapeTypeMigrations = defineMigrations({
currentVersion: Versions.AddCropProp,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: {
up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -26,7 +26,6 @@ export type TLLineShapeProps = {
/** @public */
export type TLLineShape = TLBaseShape<'line', TLLineShapeProps>
// --- VALIDATION ---
/** @public */
export const lineShapeTypeValidator: T.Validator<TLLineShape> = createShapeValidator(
'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 */
export const lineShapeMigrations = 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
},
})
export const lineShapeTypeMigrations = defineMigrations({})

View file

@ -25,7 +25,6 @@ export type TLNoteShapeProps = {
/** @public */
export type TLNoteShape = TLBaseShape<'note', TLNoteShapeProps>
// --- VALIDATION ---
/** @public */
export const noteShapeTypeValidator: T.Validator<TLNoteShape> = createShapeValidator(
'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 = {
Initial: 0,
AddUrlProp: 1,
RemoveJustify: 2,
} as const
/** @public */
export const noteShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
export const noteShapeTypeMigrations = defineMigrations({
currentVersion: Versions.RemoveJustify,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: {
up: (shape) => {
return { ...shape, props: { ...shape.props, url: '' } }

View file

@ -26,7 +26,6 @@ export type TLTextShapeProps = {
/** @public */
export type TLTextShape = TLBaseShape<'text', TLTextShapeProps>
// --- VALIDATION ---
/** @public */
export const textShapeTypeValidator: T.Validator<TLTextShape> = createShapeValidator(
'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 = {
Initial: 0,
RemoveJustify: 1,
} as const
/** @public */
export const textShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
export const textShapeTypeMigrations = defineMigrations({
currentVersion: Versions.RemoveJustify,
migrators: {
[Versions.RemoveJustify]: {

View file

@ -19,7 +19,6 @@ export type TLVideoShapeProps = {
/** @public */
export type TLVideoShape = TLBaseShape<'video', TLVideoShapeProps>
// --- VALIDATION ---
/** @public */
export const videoShapeTypeValidator: T.Validator<TLVideoShape> = createShapeValidator(
'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 = {
Initial: 0,
AddUrlProp: 1,
} as const
/** @public */
export const videoShapeMigrations = defineMigrations({
// STEP 2: Update the current version to point to your latest version
firstVersion: Versions.Initial,
export const videoShapeTypeMigrations = defineMigrations({
currentVersion: Versions.AddUrlProp,
migrators: {
// STEP 3: Add an up+down migration for the new version here
[Versions.AddUrlProp]: {
up: (shape) => {
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'>>;
// @public (undocumented)
export function defineMigrations<FirstVersion extends number, CurrentVersion extends number>({ firstVersion, currentVersion, migrators, subTypeKey, subTypeMigrations, }: {
firstVersion: FirstVersion;
currentVersion: CurrentVersion;
migrators: {
export function defineMigrations<FirstVersion extends EMPTY_SYMBOL | number = EMPTY_SYMBOL, CurrentVersion extends EMPTY_SYMBOL | Exclude<number, 0> = EMPTY_SYMBOL>(opts: {
firstVersion?: CurrentVersion extends number ? FirstVersion : never;
currentVersion?: CurrentVersion;
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<1, CurrentVersion>, 0>]: Migration;
} : never;
subTypeKey?: string;
subTypeMigrations?: Record<string, BaseMigrationsInfo>;
}): Migrations;
@ -106,9 +110,9 @@ export function migrateRecord<R extends BaseRecord>({ record, migrations, fromVe
}): MigrationResult<R>;
// @public (undocumented)
export type Migration<T = any> = {
up: (oldState: T) => T;
down: (newState: T) => T;
export type Migration<Before = any, After = any> = {
up: (oldState: Before) => After;
down: (newState: After) => Before;
};
// @public (undocumented)

View file

@ -1,29 +1,49 @@
import { BaseRecord, isRecord } from './BaseRecord'
import { SerializedSchema } from './StoreSchema'
type EMPTY_SYMBOL = symbol
/** @public */
export function defineMigrations<FirstVersion extends number, CurrentVersion extends number>({
firstVersion,
currentVersion,
migrators,
subTypeKey,
subTypeMigrations,
}: {
firstVersion: FirstVersion
currentVersion: CurrentVersion
migrators: {
[version in Exclude<Range<FirstVersion, CurrentVersion>, FirstVersion>]: Migration
}
export function defineMigrations<
FirstVersion extends number | EMPTY_SYMBOL = EMPTY_SYMBOL,
CurrentVersion extends Exclude<number, 0> | EMPTY_SYMBOL = EMPTY_SYMBOL
>(opts: {
firstVersion?: CurrentVersion extends number ? FirstVersion : never
currentVersion?: CurrentVersion
migrators?: CurrentVersion extends number
? FirstVersion extends number
? CurrentVersion extends FirstVersion
? { [version in Exclude<Range<1, CurrentVersion>, 0>]: Migration }
: { [version in Exclude<Range<FirstVersion, CurrentVersion>, FirstVersion>]: Migration }
: { [version in Exclude<Range<1, CurrentVersion>, 0>]: Migration }
: never
subTypeKey?: string
subTypeMigrations?: Record<string, BaseMigrationsInfo>
}): 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 */
export type Migration<T = any> = {
up: (oldState: T) => T
down: (newState: T) => T
export type Migration<Before = any, After = any> = {
up: (oldState: Before) => After
down: (newState: After) => Before
}
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 { defineMigrations } from '../migrate'
const UserVersion = {
Initial: 0,
} as const
/** A user of tldraw */
interface User extends BaseRecord<'user'> {
name: string
}
const userMigrations = defineMigrations({
currentVersion: UserVersion.Initial,
firstVersion: UserVersion.Initial,
migrators: {},
})
const userMigrations = defineMigrations({})
const User = createRecordType<User>('user', {
migrations: userMigrations,
@ -32,14 +24,6 @@ const User = createRecordType<User>('user', {
scope: 'document',
})
const ShapeVersion = {
Initial: 0,
} as const
const RectangleVersion = {
Initial: 0,
} as const
interface Shape<Props> extends BaseRecord<'shape'> {
type: string
x: number
@ -58,22 +42,15 @@ interface OvalProps {
borderStyle: 'solid' | 'dashed'
}
const shapeMigrations = defineMigrations({
currentVersion: ShapeVersion.Initial,
firstVersion: ShapeVersion.Initial,
migrators: {},
const shapeTypeMigrations = defineMigrations({
subTypeKey: 'type',
subTypeMigrations: {
rectangle: defineMigrations({
currentVersion: RectangleVersion.Initial,
firstVersion: RectangleVersion.Initial,
migrators: {},
}),
rectangle: defineMigrations({}),
},
})
const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', {
migrations: shapeMigrations,
migrations: shapeTypeMigrations,
validator: {
validate: (record) => {
assert(
@ -100,7 +77,7 @@ interface Org extends BaseRecord<'org'> {
}
const Org = createRecordType<Org>('org', {
migrations: defineMigrations({ currentVersion: 0, firstVersion: 0, migrators: {} }),
migrations: defineMigrations({}),
validator: {
validate: (record) => {
assert(
@ -119,6 +96,6 @@ export const testSchemaV0 = StoreSchema.create(
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'
const UserVersion = {
Initial: 0,
AddLocale: 1,
AddPhoneNumber: 2,
} as const
@ -20,7 +19,6 @@ interface User extends BaseRecord<'user'> {
const userMigrations = defineMigrations({
currentVersion: UserVersion.AddPhoneNumber,
firstVersion: UserVersion.Initial,
migrators: {
[UserVersion.AddLocale]: {
up: (record) => ({
@ -69,18 +67,15 @@ const User = createRecordType<User>('user', {
}))
const ShapeVersion = {
Initial: 0,
AddRotation: 1,
AddParent: 2,
} as const
const RectangleVersion = {
Initial: 0,
AddOpacity: 1,
} as const
const OvalVersion = {
Initial: 0,
AddBorderStyle: 1,
} as const
@ -104,9 +99,8 @@ interface OvalProps {
borderStyle: 'solid' | 'dashed'
}
const shapeMigrations = defineMigrations({
const shapeTypeMigrations = defineMigrations({
currentVersion: ShapeVersion.AddParent,
firstVersion: ShapeVersion.Initial,
migrators: {
[ShapeVersion.AddRotation]: {
up: (record) => ({
@ -135,7 +129,6 @@ const shapeMigrations = defineMigrations({
subTypeMigrations: {
rectangle: defineMigrations({
currentVersion: RectangleVersion.AddOpacity,
firstVersion: RectangleVersion.Initial,
migrators: {
[RectangleVersion.AddOpacity]: {
up: (record) => ({
@ -157,7 +150,6 @@ const shapeMigrations = defineMigrations({
}),
oval: defineMigrations({
currentVersion: OvalVersion.AddBorderStyle,
firstVersion: OvalVersion.Initial,
migrators: {
[OvalVersion.AddBorderStyle]: {
up: (record) => ({
@ -181,7 +173,7 @@ const shapeMigrations = defineMigrations({
})
const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', {
migrations: shapeMigrations,
migrations: shapeTypeMigrations,
validator: {
validate: (record) => {
assert(record && typeof record === 'object')
@ -202,13 +194,11 @@ const Shape = createRecordType<Shape<RectangleProps | OvalProps>>('shape', {
}))
const StoreVersions = {
Initial: 0,
RemoveOrg: 1,
}
const snapshotMigrations = defineMigrations({
currentVersion: StoreVersions.RemoveOrg,
firstVersion: StoreVersions.Initial,
migrators: {
[StoreVersions.RemoveOrg]: {
up: (store: StoreSnapshot<any>) => {