Simplify tlsync types (#3139)
Replace enums with (const) object types. Was supposed to include https://github.com/tldraw/tldraw/pull/3144, but had to bail out ### Change Type <!-- ❗ Please select a 'Scope' label ❗️ --> - [ ] `sdk` — Changes the tldraw SDK - [ ] `dotcom` — Changes the tldraw.com web app - [ ] `docs` — Changes to the documentation, examples, or templates. - [ ] `vs code` — Changes to the vscode plugin - [x] `internal` — Does not affect user-facing stuff <!-- ❗ Please select a 'Type' label ❗️ --> - [ ] `bugfix` — Bug fix - [ ] `feature` — New feature - [ ] `improvement` — Improving existing features - [ ] `chore` — Updating dependencies, other boring stuff - [ ] `galaxy brain` — Architectural changes - [ ] `tests` — Changes to any test code - [ ] `tools` — Changes to infrastructure, CI, internal scripts, debugging tools, etc. - [x] `dunno` — I don't know
This commit is contained in:
parent
3767a68f0f
commit
a933aaf619
5 changed files with 59 additions and 48 deletions
|
@ -2,11 +2,13 @@ import { SerializedSchema, UnknownRecord } from '@tldraw/store'
|
||||||
import { TLRoomSocket } from './TLSyncRoom'
|
import { TLRoomSocket } from './TLSyncRoom'
|
||||||
import { TLSocketServerSentDataEvent } from './protocol'
|
import { TLSocketServerSentDataEvent } from './protocol'
|
||||||
|
|
||||||
export enum RoomSessionState {
|
export const RoomSessionState = {
|
||||||
AWAITING_CONNECT_MESSAGE = 'awaiting-connect-message',
|
AwaitingConnectMessage: 'awaiting-connect-message',
|
||||||
AWAITING_REMOVAL = 'awaiting-removal',
|
AwaitingRemoval: 'awaiting-removal',
|
||||||
CONNECTED = 'connected',
|
Connected: 'connected',
|
||||||
}
|
} as const
|
||||||
|
|
||||||
|
export type RoomSessionState = (typeof RoomSessionState)[keyof typeof RoomSessionState]
|
||||||
|
|
||||||
export const SESSION_START_WAIT_TIME = 10000
|
export const SESSION_START_WAIT_TIME = 10000
|
||||||
export const SESSION_REMOVAL_WAIT_TIME = 10000
|
export const SESSION_REMOVAL_WAIT_TIME = 10000
|
||||||
|
@ -14,21 +16,21 @@ export const SESSION_IDLE_TIMEOUT = 20000
|
||||||
|
|
||||||
export type RoomSession<R extends UnknownRecord> =
|
export type RoomSession<R extends UnknownRecord> =
|
||||||
| {
|
| {
|
||||||
state: RoomSessionState.AWAITING_CONNECT_MESSAGE
|
state: typeof RoomSessionState.AwaitingConnectMessage
|
||||||
sessionKey: string
|
sessionKey: string
|
||||||
presenceId: string
|
presenceId: string
|
||||||
socket: TLRoomSocket<R>
|
socket: TLRoomSocket<R>
|
||||||
sessionStartTime: number
|
sessionStartTime: number
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
state: RoomSessionState.AWAITING_REMOVAL
|
state: typeof RoomSessionState.AwaitingRemoval
|
||||||
sessionKey: string
|
sessionKey: string
|
||||||
presenceId: string
|
presenceId: string
|
||||||
socket: TLRoomSocket<R>
|
socket: TLRoomSocket<R>
|
||||||
cancellationTime: number
|
cancellationTime: number
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
state: RoomSessionState.CONNECTED
|
state: typeof RoomSessionState.Connected
|
||||||
sessionKey: string
|
sessionKey: string
|
||||||
presenceId: string
|
presenceId: string
|
||||||
socket: TLRoomSocket<R>
|
socket: TLRoomSocket<R>
|
||||||
|
|
|
@ -140,14 +140,14 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
pruneSessions = () => {
|
pruneSessions = () => {
|
||||||
for (const client of this.sessions.values()) {
|
for (const client of this.sessions.values()) {
|
||||||
switch (client.state) {
|
switch (client.state) {
|
||||||
case RoomSessionState.CONNECTED: {
|
case RoomSessionState.Connected: {
|
||||||
const hasTimedOut = timeSince(client.lastInteractionTime) > SESSION_IDLE_TIMEOUT
|
const hasTimedOut = timeSince(client.lastInteractionTime) > SESSION_IDLE_TIMEOUT
|
||||||
if (hasTimedOut || !client.socket.isOpen) {
|
if (hasTimedOut || !client.socket.isOpen) {
|
||||||
this.cancelSession(client.sessionKey)
|
this.cancelSession(client.sessionKey)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case RoomSessionState.AWAITING_CONNECT_MESSAGE: {
|
case RoomSessionState.AwaitingConnectMessage: {
|
||||||
const hasTimedOut = timeSince(client.sessionStartTime) > SESSION_START_WAIT_TIME
|
const hasTimedOut = timeSince(client.sessionStartTime) > SESSION_START_WAIT_TIME
|
||||||
if (hasTimedOut || !client.socket.isOpen) {
|
if (hasTimedOut || !client.socket.isOpen) {
|
||||||
// remove immediately
|
// remove immediately
|
||||||
|
@ -155,7 +155,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case RoomSessionState.AWAITING_REMOVAL: {
|
case RoomSessionState.AwaitingRemoval: {
|
||||||
const hasTimedOut = timeSince(client.cancellationTime) > SESSION_REMOVAL_WAIT_TIME
|
const hasTimedOut = timeSince(client.cancellationTime) > SESSION_REMOVAL_WAIT_TIME
|
||||||
if (hasTimedOut) {
|
if (hasTimedOut) {
|
||||||
this.removeSession(client.sessionKey)
|
this.removeSession(client.sessionKey)
|
||||||
|
@ -397,7 +397,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
console.warn('Tried to send message to unknown session', message.type)
|
console.warn('Tried to send message to unknown session', message.type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (session.state !== RoomSessionState.CONNECTED) {
|
if (session.state !== RoomSessionState.Connected) {
|
||||||
console.warn('Tried to send message to disconnected client', message.type)
|
console.warn('Tried to send message to disconnected client', message.type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -433,7 +433,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
_flushDataMessages(sessionKey: string) {
|
_flushDataMessages(sessionKey: string) {
|
||||||
const session = this.sessions.get(sessionKey)
|
const session = this.sessions.get(sessionKey)
|
||||||
|
|
||||||
if (!session || session.state !== RoomSessionState.CONNECTED) {
|
if (!session || session.state !== RoomSessionState.Connected) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,13 +489,13 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.state === RoomSessionState.AWAITING_REMOVAL) {
|
if (session.state === RoomSessionState.AwaitingRemoval) {
|
||||||
console.warn('Tried to cancel session that is already awaiting removal')
|
console.warn('Tried to cancel session that is already awaiting removal')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sessions.set(sessionKey, {
|
this.sessions.set(sessionKey, {
|
||||||
state: RoomSessionState.AWAITING_REMOVAL,
|
state: RoomSessionState.AwaitingRemoval,
|
||||||
sessionKey,
|
sessionKey,
|
||||||
presenceId: session.presenceId,
|
presenceId: session.presenceId,
|
||||||
socket: session.socket,
|
socket: session.socket,
|
||||||
|
@ -517,7 +517,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
sourceSessionKey: string
|
sourceSessionKey: string
|
||||||
}) {
|
}) {
|
||||||
this.sessions.forEach((session) => {
|
this.sessions.forEach((session) => {
|
||||||
if (session.state !== RoomSessionState.CONNECTED) return
|
if (session.state !== RoomSessionState.Connected) return
|
||||||
if (sourceSessionKey === session.sessionKey) return
|
if (sourceSessionKey === session.sessionKey) return
|
||||||
if (!session.socket.isOpen) {
|
if (!session.socket.isOpen) {
|
||||||
this.cancelSession(session.sessionKey)
|
this.cancelSession(session.sessionKey)
|
||||||
|
@ -556,7 +556,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
handleNewSession = (sessionKey: string, socket: TLRoomSocket<R>) => {
|
handleNewSession = (sessionKey: string, socket: TLRoomSocket<R>) => {
|
||||||
const existing = this.sessions.get(sessionKey)
|
const existing = this.sessions.get(sessionKey)
|
||||||
this.sessions.set(sessionKey, {
|
this.sessions.set(sessionKey, {
|
||||||
state: RoomSessionState.AWAITING_CONNECT_MESSAGE,
|
state: RoomSessionState.AwaitingConnectMessage,
|
||||||
sessionKey,
|
sessionKey,
|
||||||
socket,
|
socket,
|
||||||
presenceId: existing?.presenceId ?? this.presenceType.createId(),
|
presenceId: existing?.presenceId ?? this.presenceType.createId(),
|
||||||
|
@ -628,7 +628,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
return this.handlePushRequest(session, message)
|
return this.handlePushRequest(session, message)
|
||||||
}
|
}
|
||||||
case 'ping': {
|
case 'ping': {
|
||||||
if (session.state === RoomSessionState.CONNECTED) {
|
if (session.state === RoomSessionState.Connected) {
|
||||||
session.lastInteractionTime = Date.now()
|
session.lastInteractionTime = Date.now()
|
||||||
}
|
}
|
||||||
return this.sendMessage(session.sessionKey, { type: 'pong' })
|
return this.sendMessage(session.sessionKey, { type: 'pong' })
|
||||||
|
@ -685,7 +685,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
|
|
||||||
const connect = (msg: TLSocketServerSentEvent<R>) => {
|
const connect = (msg: TLSocketServerSentEvent<R>) => {
|
||||||
this.sessions.set(session.sessionKey, {
|
this.sessions.set(session.sessionKey, {
|
||||||
state: RoomSessionState.CONNECTED,
|
state: RoomSessionState.Connected,
|
||||||
sessionKey: session.sessionKey,
|
sessionKey: session.sessionKey,
|
||||||
presenceId: session.presenceId,
|
presenceId: session.presenceId,
|
||||||
socket: session.socket,
|
socket: session.socket,
|
||||||
|
@ -786,7 +786,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
||||||
message: Extract<TLSocketClientSentEvent<R>, { type: 'push' }>
|
message: Extract<TLSocketClientSentEvent<R>, { type: 'push' }>
|
||||||
) {
|
) {
|
||||||
// We must be connected to handle push requests
|
// We must be connected to handle push requests
|
||||||
if (session.state !== RoomSessionState.CONNECTED) {
|
if (session.state !== RoomSessionState.Connected) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,20 @@ import { objectMapEntries, objectMapValues } from '@tldraw/utils'
|
||||||
import isEqual from 'lodash.isequal'
|
import isEqual from 'lodash.isequal'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export enum RecordOpType {
|
export const RecordOpType = {
|
||||||
Put = 'put',
|
Put: 'put',
|
||||||
Patch = 'patch',
|
Patch: 'patch',
|
||||||
Remove = 'remove',
|
Remove: 'remove',
|
||||||
}
|
} as const
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export type RecordOpType = (typeof RecordOpType)[keyof typeof RecordOpType]
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type RecordOp<R extends UnknownRecord> =
|
export type RecordOp<R extends UnknownRecord> =
|
||||||
| [RecordOpType.Put, R]
|
| [typeof RecordOpType.Put, R]
|
||||||
| [RecordOpType.Patch, ObjectDiff]
|
| [typeof RecordOpType.Patch, ObjectDiff]
|
||||||
| [RecordOpType.Remove]
|
| [typeof RecordOpType.Remove]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A one-way (non-reversible) diff designed for small json footprint. These are mainly intended to
|
* A one-way (non-reversible) diff designed for small json footprint. These are mainly intended to
|
||||||
|
@ -60,20 +63,22 @@ export const getNetworkDiff = <R extends UnknownRecord>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export enum ValueOpType {
|
export const ValueOpType = {
|
||||||
Put = 'put',
|
Put: 'put',
|
||||||
Delete = 'delete',
|
Delete: 'delete',
|
||||||
Append = 'append',
|
Append: 'append',
|
||||||
Patch = 'patch',
|
Patch: 'patch',
|
||||||
}
|
} as const
|
||||||
|
export type ValueOpType = (typeof ValueOpType)[keyof typeof ValueOpType]
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type PutOp = [type: ValueOpType.Put, value: unknown]
|
export type PutOp = [type: typeof ValueOpType.Put, value: unknown]
|
||||||
/** @public */
|
/** @public */
|
||||||
export type AppendOp = [type: ValueOpType.Append, values: unknown[], offset: number]
|
export type AppendOp = [type: typeof ValueOpType.Append, values: unknown[], offset: number]
|
||||||
/** @public */
|
/** @public */
|
||||||
export type PatchOp = [type: ValueOpType.Patch, diff: ObjectDiff]
|
export type PatchOp = [type: typeof ValueOpType.Patch, diff: ObjectDiff]
|
||||||
/** @public */
|
/** @public */
|
||||||
export type DeleteOp = [type: ValueOpType.Delete]
|
export type DeleteOp = [type: typeof ValueOpType.Delete]
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type ValueOp = PutOp | AppendOp | PatchOp | DeleteOp
|
export type ValueOp = PutOp | AppendOp | PatchOp | DeleteOp
|
||||||
|
|
|
@ -5,12 +5,16 @@ import { NetworkDiff, ObjectDiff, RecordOpType } from './diff'
|
||||||
export const TLSYNC_PROTOCOL_VERSION = 5
|
export const TLSYNC_PROTOCOL_VERSION = 5
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export enum TLIncompatibilityReason {
|
export const TLIncompatibilityReason = {
|
||||||
ClientTooOld = 'clientTooOld',
|
ClientTooOld: 'clientTooOld',
|
||||||
ServerTooOld = 'serverTooOld',
|
ServerTooOld: 'serverTooOld',
|
||||||
InvalidRecord = 'invalidRecord',
|
InvalidRecord: 'invalidRecord',
|
||||||
InvalidOperation = 'invalidOperation',
|
InvalidOperation: 'invalidOperation',
|
||||||
}
|
} as const
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export type TLIncompatibilityReason =
|
||||||
|
(typeof TLIncompatibilityReason)[keyof typeof TLIncompatibilityReason]
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLSocketServerSentEvent<R extends UnknownRecord> =
|
export type TLSocketServerSentEvent<R extends UnknownRecord> =
|
||||||
|
@ -55,7 +59,7 @@ export type TLPushRequest<R extends UnknownRecord> =
|
||||||
| {
|
| {
|
||||||
type: 'push'
|
type: 'push'
|
||||||
clientClock: number
|
clientClock: number
|
||||||
presence: [RecordOpType.Patch, ObjectDiff] | [RecordOpType.Put, R]
|
presence: [typeof RecordOpType.Patch, ObjectDiff] | [typeof RecordOpType.Put, R]
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'push'
|
type: 'push'
|
||||||
|
|
|
@ -109,7 +109,7 @@ describe('TLServer', () => {
|
||||||
expect(server.roomState?.persistenceKey).toBe('test-persistence-key')
|
expect(server.roomState?.persistenceKey).toBe('test-persistence-key')
|
||||||
expect(server.roomState?.room.sessions.size).toBe(1)
|
expect(server.roomState?.room.sessions.size).toBe(1)
|
||||||
expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe(
|
expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe(
|
||||||
RoomSessionState.AWAITING_CONNECT_MESSAGE
|
RoomSessionState.AwaitingConnectMessage
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ describe('TLServer', () => {
|
||||||
sockets.client.on('message', onClientMessage)
|
sockets.client.on('message', onClientMessage)
|
||||||
|
|
||||||
expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe(
|
expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe(
|
||||||
RoomSessionState.AWAITING_CONNECT_MESSAGE
|
RoomSessionState.AwaitingConnectMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
for (const chunk of chunks) {
|
for (const chunk of chunks) {
|
||||||
|
@ -145,7 +145,7 @@ describe('TLServer', () => {
|
||||||
await receivedPromise
|
await receivedPromise
|
||||||
|
|
||||||
expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe(
|
expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe(
|
||||||
RoomSessionState.CONNECTED
|
RoomSessionState.Connected
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(onClientMessage).toHaveBeenCalledTimes(1)
|
expect(onClientMessage).toHaveBeenCalledTimes(1)
|
||||||
|
|
Loading…
Reference in a new issue