[chore] move schema construction to tlschema package (#1334)
Our private tlsync package currently depends on the editor package, which balloons the size of the cloudflare worker. It also makes it so that any change to any package triggers a worker refresh, which makes working on multiplayer stuff kinda miserable. This is the first PR to fix that problem. The second PR will need to resolve TLSyncClient's dependency on the debugFlags somehow. Easiest would be to just remove the offending flag, but we might want cross-bublic debug flags at some point in the future so I'll try to find a low-cost way to make that happen while making `tlsync` not depend on `editor`. cc @TodePond since you added the flag in question (`tldrawResetConnectionEveryPing`) ### Release Note - internal moving stuff around
This commit is contained in:
parent
1f90c3f2b4
commit
67f5c25c73
7 changed files with 175 additions and 95 deletions
|
@ -1,36 +1,18 @@
|
|||
import {
|
||||
CLIENT_FIXUP_SCRIPT,
|
||||
TLAsset,
|
||||
TLCamera,
|
||||
TLDOCUMENT_ID,
|
||||
TLDocument,
|
||||
TLInstance,
|
||||
TLInstanceId,
|
||||
TLInstancePageState,
|
||||
TLInstancePresence,
|
||||
TLPage,
|
||||
TLRecord,
|
||||
TLShape,
|
||||
TLStore,
|
||||
TLStoreProps,
|
||||
TLUser,
|
||||
TLUserDocument,
|
||||
TLUserId,
|
||||
TLUserPresence,
|
||||
ensureStoreIsUsable,
|
||||
onValidationFailure,
|
||||
rootShapeTypeMigrations,
|
||||
storeMigrations,
|
||||
createTLSchema,
|
||||
} from '@tldraw/tlschema'
|
||||
import {
|
||||
RecordType,
|
||||
Store,
|
||||
StoreSchema,
|
||||
StoreSnapshot,
|
||||
createRecordType,
|
||||
defineMigrations,
|
||||
} from '@tldraw/tlstore'
|
||||
import { T } from '@tldraw/tlvalidate'
|
||||
import { RecordType, Store, StoreSchema, StoreSnapshot } from '@tldraw/tlstore'
|
||||
import { Signal } from 'signia'
|
||||
import { TLArrowShapeDef } from '../app/shapeutils/TLArrowUtil/TLArrowUtil'
|
||||
import { TLBookmarkShapeDef } from '../app/shapeutils/TLBookmarkUtil/TLBookmarkUtil'
|
||||
|
@ -46,23 +28,6 @@ import { TLTextShapeDef } from '../app/shapeutils/TLTextUtil/TLTextUtil'
|
|||
import { TLVideoShapeDef } from '../app/shapeutils/TLVideoUtil/TLVideoUtil'
|
||||
import { StateNodeConstructor } from '../app/statechart/StateNode'
|
||||
import { TLShapeDef, TLUnknownShapeDef } from './TLShapeDefinition'
|
||||
import { defaultDerivePresenceState } from './defaultDerivePresenceState'
|
||||
|
||||
const CORE_SHAPE_DEFS = () =>
|
||||
[
|
||||
TLDrawShapeDef,
|
||||
TLTextShapeDef,
|
||||
TLLineShapeDef,
|
||||
TLArrowShapeDef,
|
||||
TLImageShapeDef,
|
||||
TLVideoShapeDef,
|
||||
TLGeoShapeDef,
|
||||
TLNoteShapeDef,
|
||||
TLGroupShapeDef,
|
||||
TLBookmarkShapeDef,
|
||||
TLEmbedShapeDef,
|
||||
TLFrameShapeDef,
|
||||
] as const
|
||||
|
||||
/** @public */
|
||||
export class TldrawEditorConfig {
|
||||
|
@ -80,68 +45,35 @@ export class TldrawEditorConfig {
|
|||
/** @internal */
|
||||
derivePresenceState?: (store: TLStore) => Signal<TLInstancePresence | null>
|
||||
}) {
|
||||
const {
|
||||
shapes = [],
|
||||
tools = [],
|
||||
allowUnknownShapes = false,
|
||||
derivePresenceState = defaultDerivePresenceState,
|
||||
} = args
|
||||
const { shapes = [], tools = [], allowUnknownShapes = false, derivePresenceState } = args
|
||||
this.tools = tools
|
||||
|
||||
const allShapeDefs = [...CORE_SHAPE_DEFS(), ...shapes]
|
||||
this.shapes = allShapeDefs
|
||||
this.shapes = [
|
||||
TLArrowShapeDef,
|
||||
TLBookmarkShapeDef,
|
||||
TLDrawShapeDef,
|
||||
TLEmbedShapeDef,
|
||||
TLFrameShapeDef,
|
||||
TLGeoShapeDef,
|
||||
TLGroupShapeDef,
|
||||
TLImageShapeDef,
|
||||
TLLineShapeDef,
|
||||
TLNoteShapeDef,
|
||||
TLTextShapeDef,
|
||||
TLVideoShapeDef,
|
||||
...shapes,
|
||||
]
|
||||
|
||||
const typeSet = new Set<string>()
|
||||
for (const shapeDef of allShapeDefs) {
|
||||
if (typeSet.has(shapeDef.type)) {
|
||||
throw new Error(`Shape type ${shapeDef.type} is already defined`)
|
||||
}
|
||||
typeSet.add(shapeDef.type)
|
||||
}
|
||||
|
||||
const shapeTypeMigrations = defineMigrations({
|
||||
currentVersion: rootShapeTypeMigrations.currentVersion,
|
||||
firstVersion: rootShapeTypeMigrations.firstVersion,
|
||||
migrators: rootShapeTypeMigrations.migrators,
|
||||
subTypeKey: 'type',
|
||||
subTypeMigrations: Object.fromEntries(allShapeDefs.map((def) => [def.type, def.migrations])),
|
||||
this.storeSchema = createTLSchema({
|
||||
allowUnknownShapes,
|
||||
customShapeDefs: shapes,
|
||||
derivePresenceState,
|
||||
})
|
||||
|
||||
let shapeValidator = T.union('type', {
|
||||
...Object.fromEntries(allShapeDefs.map((def) => [def.type, def.validator])),
|
||||
}) as T.UnionValidator<'type', any, any>
|
||||
if (allowUnknownShapes) {
|
||||
shapeValidator = shapeValidator.validateUnknownVariants((shape) => shape as any)
|
||||
}
|
||||
|
||||
const shapeRecord = createRecordType<TLShape>('shape', {
|
||||
migrations: shapeTypeMigrations,
|
||||
validator: T.model('shape', shapeValidator),
|
||||
scope: 'document',
|
||||
}).withDefaultProperties(() => ({ x: 0, y: 0, rotation: 0, isLocked: false }))
|
||||
this.TLShape = shapeRecord
|
||||
|
||||
this.storeSchema = StoreSchema.create<TLRecord, TLStoreProps>(
|
||||
{
|
||||
asset: TLAsset,
|
||||
camera: TLCamera,
|
||||
document: TLDocument,
|
||||
instance: TLInstance,
|
||||
instance_page_state: TLInstancePageState,
|
||||
page: TLPage,
|
||||
shape: shapeRecord,
|
||||
user: TLUser,
|
||||
user_document: TLUserDocument,
|
||||
user_presence: TLUserPresence,
|
||||
instance_presence: TLInstancePresence,
|
||||
},
|
||||
{
|
||||
snapshotMigrations: storeMigrations,
|
||||
onValidationFailure,
|
||||
ensureStoreIsUsable,
|
||||
derivePresenceState,
|
||||
}
|
||||
)
|
||||
this.TLShape = this.storeSchema.types.shape as RecordType<
|
||||
TLShape,
|
||||
'type' | 'props' | 'index' | 'parentId'
|
||||
>
|
||||
}
|
||||
|
||||
createStore(config: {
|
||||
|
|
|
@ -5,13 +5,16 @@
|
|||
```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';
|
||||
import { Signal } from 'signia';
|
||||
import { Store } from '@tldraw/tlstore';
|
||||
import { StoreSchema } from '@tldraw/tlstore';
|
||||
import { StoreSchemaOptions } from '@tldraw/tlstore';
|
||||
import { StoreSnapshot } from '@tldraw/tlstore';
|
||||
import { StoreValidator } from '@tldraw/tlstore';
|
||||
import { T } from '@tldraw/tlvalidate';
|
||||
|
||||
// @internal (undocumented)
|
||||
|
@ -102,15 +105,32 @@ export function createShapeValidator<Type extends string, Props extends object>(
|
|||
props: Props;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function createTLSchema({ customShapeDefs, allowUnknownShapes, derivePresenceState, }: {
|
||||
customShapeDefs?: readonly CustomShapeTypeInfo[];
|
||||
allowUnknownShapes?: boolean;
|
||||
derivePresenceState?: (store: TLStore) => Signal<null | TLInstancePresence>;
|
||||
}): StoreSchema<TLRecord, TLStoreProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const cursorTypeValidator: T.Validator<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const cursorValidator: T.Validator<TLCursor>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type CustomShapeTypeInfo = {
|
||||
type: string;
|
||||
migrations: ReturnType<typeof defineMigrations>;
|
||||
validator?: StoreValidator<TLShape>;
|
||||
};
|
||||
|
||||
// @internal (undocumented)
|
||||
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;
|
||||
|
||||
|
|
|
@ -63,5 +63,8 @@
|
|||
"@tldraw/tlvalidate": "workspace:*",
|
||||
"@tldraw/utils": "workspace:*",
|
||||
"nanoid": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"signia": "*"
|
||||
}
|
||||
}
|
||||
|
|
119
packages/tlschema/src/createTLSchema.ts
Normal file
119
packages/tlschema/src/createTLSchema.ts
Normal file
|
@ -0,0 +1,119 @@
|
|||
import { StoreSchema, StoreValidator, createRecordType, defineMigrations } from '@tldraw/tlstore'
|
||||
import { T } from '@tldraw/tlvalidate'
|
||||
import { Signal } from 'signia'
|
||||
import { TLRecord } from './TLRecord'
|
||||
import { TLStore, TLStoreProps, ensureStoreIsUsable, onValidationFailure } from './TLStore'
|
||||
import { defaultDerivePresenceState } from './defaultDerivePresenceState'
|
||||
import { TLAsset } from './records/TLAsset'
|
||||
import { TLCamera } from './records/TLCamera'
|
||||
import { TLDocument } from './records/TLDocument'
|
||||
import { TLInstance } from './records/TLInstance'
|
||||
import { TLInstancePageState } from './records/TLInstancePageState'
|
||||
import { TLInstancePresence } from './records/TLInstancePresence'
|
||||
import { TLPage } from './records/TLPage'
|
||||
import { TLShape, rootShapeTypeMigrations } from './records/TLShape'
|
||||
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'
|
||||
|
||||
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: 'bookmark',
|
||||
migrations: bookmarkShapeMigrations,
|
||||
validator: bookmarkShapeTypeValidator,
|
||||
},
|
||||
{ type: 'frame', migrations: frameShapeMigrations, validator: frameShapeTypeValidator },
|
||||
{ type: 'embed', migrations: embedShapeMigrations, validator: embedShapeTypeValidator },
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export type CustomShapeTypeInfo = {
|
||||
type: string
|
||||
migrations: ReturnType<typeof defineMigrations>
|
||||
validator?: StoreValidator<TLShape>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createTLSchema({
|
||||
customShapeDefs,
|
||||
allowUnknownShapes,
|
||||
derivePresenceState,
|
||||
}: {
|
||||
customShapeDefs?: readonly CustomShapeTypeInfo[]
|
||||
allowUnknownShapes?: boolean
|
||||
derivePresenceState?: (store: TLStore) => Signal<TLInstancePresence | null>
|
||||
}) {
|
||||
const allShapeDefs = [...CORE_SHAPE_DEFS, ...(customShapeDefs ?? [])]
|
||||
const typeSet = new Set<string>()
|
||||
for (const shapeDef of allShapeDefs) {
|
||||
if (typeSet.has(shapeDef.type)) {
|
||||
throw new Error(`Shape type ${shapeDef.type} is already defined`)
|
||||
}
|
||||
typeSet.add(shapeDef.type)
|
||||
}
|
||||
|
||||
const shapeTypeMigrations = defineMigrations({
|
||||
currentVersion: rootShapeTypeMigrations.currentVersion,
|
||||
firstVersion: rootShapeTypeMigrations.firstVersion,
|
||||
migrators: rootShapeTypeMigrations.migrators,
|
||||
subTypeKey: 'type',
|
||||
subTypeMigrations: Object.fromEntries(allShapeDefs.map((def) => [def.type, def.migrations])),
|
||||
})
|
||||
|
||||
let shapeValidator = T.union('type', {
|
||||
...Object.fromEntries(allShapeDefs.map((def) => [def.type, def.validator ?? (T.any as any)])),
|
||||
}) as T.UnionValidator<'type', any, any>
|
||||
if (allowUnknownShapes) {
|
||||
shapeValidator = shapeValidator.validateUnknownVariants((shape) => shape as any)
|
||||
}
|
||||
|
||||
const shapeRecord = createRecordType<TLShape>('shape', {
|
||||
migrations: shapeTypeMigrations,
|
||||
validator: T.model('shape', shapeValidator),
|
||||
scope: 'document',
|
||||
}).withDefaultProperties(() => ({ x: 0, y: 0, rotation: 0, isLocked: false }))
|
||||
|
||||
return StoreSchema.create<TLRecord, TLStoreProps>(
|
||||
{
|
||||
asset: TLAsset,
|
||||
camera: TLCamera,
|
||||
document: TLDocument,
|
||||
instance: TLInstance,
|
||||
instance_page_state: TLInstancePageState,
|
||||
page: TLPage,
|
||||
shape: shapeRecord,
|
||||
user: TLUser,
|
||||
user_document: TLUserDocument,
|
||||
user_presence: TLUserPresence,
|
||||
instance_presence: TLInstancePresence,
|
||||
},
|
||||
{
|
||||
snapshotMigrations: storeMigrations,
|
||||
onValidationFailure,
|
||||
ensureStoreIsUsable,
|
||||
derivePresenceState: derivePresenceState ?? defaultDerivePresenceState,
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { TLInstancePresence, TLStore } from '@tldraw/tlschema'
|
||||
import { Signal, computed } from 'signia'
|
||||
import { TLStore } from './TLStore'
|
||||
import { TLInstancePresence } from './records/TLInstancePresence'
|
||||
|
||||
/** @internal */
|
||||
export const defaultDerivePresenceState = (store: TLStore): Signal<TLInstancePresence | null> => {
|
|
@ -24,6 +24,9 @@ export {
|
|||
type TLVideoAsset,
|
||||
} from './assets/TLVideoAsset'
|
||||
export { createAssetValidator, type TLBaseAsset } from './assets/asset-validation'
|
||||
export { createTLSchema } from './createTLSchema'
|
||||
export type { CustomShapeTypeInfo } from './createTLSchema'
|
||||
export { defaultDerivePresenceState } from './defaultDerivePresenceState'
|
||||
export { CLIENT_FIXUP_SCRIPT, fixupRecord } from './fixup'
|
||||
export { type Box2dModel, type Vec2dModel } from './geometry-types'
|
||||
export {
|
||||
|
|
|
@ -4542,6 +4542,8 @@ __metadata:
|
|||
kleur: ^4.1.5
|
||||
lazyrepo: 0.0.0-alpha.26
|
||||
nanoid: ^3.0.0
|
||||
peerDependencies:
|
||||
signia: "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue