@tldraw/sync-core 5.2.0-canary.6421dbe4e25a → 5.2.0-canary.667cbd6bb6ff
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/ClientWebSocketAdapter.js +7 -1
- package/dist-cjs/lib/ClientWebSocketAdapter.js.map +2 -2
- package/dist-cjs/lib/TLSyncRoom.js +24 -1
- package/dist-cjs/lib/TLSyncRoom.js.map +2 -2
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ClientWebSocketAdapter.mjs +7 -1
- package/dist-esm/lib/ClientWebSocketAdapter.mjs.map +2 -2
- package/dist-esm/lib/TLSyncRoom.mjs +24 -1
- package/dist-esm/lib/TLSyncRoom.mjs.map +2 -2
- package/package.json +9 -6
- package/src/lib/ClientWebSocketAdapter.test.ts +19 -0
- package/src/lib/ClientWebSocketAdapter.ts +7 -1
- package/src/lib/TLSyncRoom.ts +29 -2
- package/src/test/TLSyncRoom.test.ts +42 -0
- package/src/test/upgradeDowngrade.test.ts +3 -3
package/src/lib/TLSyncRoom.ts
CHANGED
|
@@ -838,6 +838,28 @@ export class TLSyncRoom<R extends UnknownRecord, SessionMeta> {
|
|
|
838
838
|
this.broadcastPatch(diff)
|
|
839
839
|
}
|
|
840
840
|
|
|
841
|
+
/**
|
|
842
|
+
* Work out whether a client we can't reconcile schemas with is running a newer or older SDK
|
|
843
|
+
* than us.
|
|
844
|
+
*/
|
|
845
|
+
private getVersionMismatchReason(theirSchema: SerializedSchema) {
|
|
846
|
+
const ourSchema = this.serializedSchema
|
|
847
|
+
|
|
848
|
+
if (theirSchema.schemaVersion > ourSchema.schemaVersion) {
|
|
849
|
+
return TLSyncErrorCloseEventReason.SERVER_TOO_OLD
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (theirSchema.schemaVersion === 2 && ourSchema.schemaVersion === 2) {
|
|
853
|
+
for (const [sequenceId, theirVersion] of Object.entries(theirSchema.sequences)) {
|
|
854
|
+
const ourVersion = ourSchema.sequences[sequenceId]
|
|
855
|
+
if (ourVersion === undefined || theirVersion > ourVersion) {
|
|
856
|
+
return TLSyncErrorCloseEventReason.SERVER_TOO_OLD
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return TLSyncErrorCloseEventReason.CLIENT_TOO_OLD
|
|
861
|
+
}
|
|
862
|
+
|
|
841
863
|
private handleConnectRequest(
|
|
842
864
|
session: RoomSession<R, SessionMeta>,
|
|
843
865
|
message: Extract<TLSocketClientSentEvent<R>, { type: 'connect' }>
|
|
@@ -874,8 +896,13 @@ export class TLSyncRoom<R extends UnknownRecord, SessionMeta> {
|
|
|
874
896
|
return
|
|
875
897
|
}
|
|
876
898
|
const migrations = this.schema.getMigrationsSince(message.schema)
|
|
877
|
-
|
|
878
|
-
|
|
899
|
+
if (!migrations.ok) {
|
|
900
|
+
this.rejectSession(session.sessionId, this.getVersionMismatchReason(message.schema))
|
|
901
|
+
return
|
|
902
|
+
}
|
|
903
|
+
// The client's schema is older than ours, but we can't migrate our data down to their
|
|
904
|
+
// version (a migration isn't record-scoped or has no down migration), so they're too old.
|
|
905
|
+
if (migrations.value.some((m) => m.scope !== 'record' || !m.down)) {
|
|
879
906
|
this.rejectSession(session.sessionId, TLSyncErrorCloseEventReason.CLIENT_TOO_OLD)
|
|
880
907
|
return
|
|
881
908
|
}
|
|
@@ -845,6 +845,48 @@ describe('23. Connect handshake (HS)', () => {
|
|
|
845
845
|
)
|
|
846
846
|
})
|
|
847
847
|
|
|
848
|
+
it('rejects a client running a newer schema with SERVER_TOO_OLD, not CLIENT_TOO_OLD', () => {
|
|
849
|
+
// Regression test for #6169
|
|
850
|
+
// A client running a newer SDK than the server: its schema has a sequence version higher
|
|
851
|
+
// than anything this server knows about. The server can't reconcile it, but the server is
|
|
852
|
+
// the one that's behind, so it should report SERVER_TOO_OLD (not CLIENT_TOO_OLD).
|
|
853
|
+
const serializedSchema = schema.serialize()
|
|
854
|
+
const newerSerializedSchema: SerializedSchemaV2 = {
|
|
855
|
+
schemaVersion: 2,
|
|
856
|
+
sequences: {
|
|
857
|
+
...serializedSchema.sequences,
|
|
858
|
+
'com.tldraw.shape.arrow': 999,
|
|
859
|
+
},
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const { room } = makeRoom()
|
|
863
|
+
const socket = makeSocket()
|
|
864
|
+
|
|
865
|
+
room.handleNewSession({
|
|
866
|
+
sessionId: 'newer-client-session',
|
|
867
|
+
socket,
|
|
868
|
+
meta: undefined,
|
|
869
|
+
isReadonly: false,
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
room.handleMessage('newer-client-session', {
|
|
873
|
+
connectRequestId: 'connect-1',
|
|
874
|
+
lastServerClock: 0,
|
|
875
|
+
protocolVersion: getTlsyncProtocolVersion(),
|
|
876
|
+
schema: newerSerializedSchema,
|
|
877
|
+
type: 'connect',
|
|
878
|
+
})
|
|
879
|
+
|
|
880
|
+
// Session should not be connected - it was rejected due to incompatible schema
|
|
881
|
+
expect(room.sessions.get('newer-client-session')?.state).not.toBe(RoomSessionState.Connected)
|
|
882
|
+
|
|
883
|
+
// Socket should be closed with SERVER_TOO_OLD, since the client is on a newer version
|
|
884
|
+
expect(socket.close).toHaveBeenCalledWith(
|
|
885
|
+
TLSyncErrorCloseEventCode,
|
|
886
|
+
TLSyncErrorCloseEventReason.SERVER_TOO_OLD
|
|
887
|
+
)
|
|
888
|
+
})
|
|
889
|
+
|
|
848
890
|
it('[HS4] handles a client with lastServerClock greater than the current documentClock', () => {
|
|
849
891
|
const { room } = makeRoom({ snapshot: makeSnapshot(records, { documentClock: 10 }) })
|
|
850
892
|
|
|
@@ -482,9 +482,9 @@ it('[HS3] when the client is too new it cannot connect', () => {
|
|
|
482
482
|
|
|
483
483
|
expect(v2_socket.close).toHaveBeenCalledWith(
|
|
484
484
|
4099,
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
TLSyncErrorCloseEventReason.
|
|
485
|
+
// the client is on a newer schema than the server, so from its perspective the server is
|
|
486
|
+
// the one that's too old.
|
|
487
|
+
TLSyncErrorCloseEventReason.SERVER_TOO_OLD
|
|
488
488
|
)
|
|
489
489
|
})
|
|
490
490
|
|