@tldraw/sync-core 5.3.0-internal.fba91ed55f6c → 5.3.0-next.4123e97459a9

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.
Files changed (50) hide show
  1. package/dist-cjs/index.d.ts +3 -154
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/InMemorySyncStorage.js +20 -55
  5. package/dist-cjs/lib/InMemorySyncStorage.js.map +2 -2
  6. package/dist-cjs/lib/RoomSession.js.map +1 -1
  7. package/dist-cjs/lib/SQLiteSyncStorage.js +52 -115
  8. package/dist-cjs/lib/SQLiteSyncStorage.js.map +2 -2
  9. package/dist-cjs/lib/TLSocketRoom.js +2 -27
  10. package/dist-cjs/lib/TLSocketRoom.js.map +2 -2
  11. package/dist-cjs/lib/TLSyncClient.js +1 -6
  12. package/dist-cjs/lib/TLSyncClient.js.map +2 -2
  13. package/dist-cjs/lib/TLSyncRoom.js +10 -160
  14. package/dist-cjs/lib/TLSyncRoom.js.map +3 -3
  15. package/dist-cjs/lib/TLSyncStorage.js.map +2 -2
  16. package/dist-cjs/lib/protocol.js.map +2 -2
  17. package/dist-esm/index.d.mts +3 -154
  18. package/dist-esm/index.mjs +1 -1
  19. package/dist-esm/index.mjs.map +2 -2
  20. package/dist-esm/lib/InMemorySyncStorage.mjs +20 -55
  21. package/dist-esm/lib/InMemorySyncStorage.mjs.map +2 -2
  22. package/dist-esm/lib/RoomSession.mjs.map +1 -1
  23. package/dist-esm/lib/SQLiteSyncStorage.mjs +52 -115
  24. package/dist-esm/lib/SQLiteSyncStorage.mjs.map +2 -2
  25. package/dist-esm/lib/TLSocketRoom.mjs +2 -27
  26. package/dist-esm/lib/TLSocketRoom.mjs.map +2 -2
  27. package/dist-esm/lib/TLSyncClient.mjs +1 -6
  28. package/dist-esm/lib/TLSyncClient.mjs.map +2 -2
  29. package/dist-esm/lib/TLSyncRoom.mjs +10 -160
  30. package/dist-esm/lib/TLSyncRoom.mjs.map +3 -3
  31. package/dist-esm/lib/TLSyncStorage.mjs.map +2 -2
  32. package/dist-esm/lib/protocol.mjs.map +2 -2
  33. package/package.json +6 -6
  34. package/src/index.ts +0 -3
  35. package/src/lib/InMemorySyncStorage.ts +21 -74
  36. package/src/lib/RoomSession.ts +2 -7
  37. package/src/lib/SQLiteSyncStorage.test.ts +2 -29
  38. package/src/lib/SQLiteSyncStorage.ts +59 -158
  39. package/src/lib/TLSocketRoom.ts +3 -61
  40. package/src/lib/TLSyncClient.test.ts +2 -9
  41. package/src/lib/TLSyncClient.ts +3 -15
  42. package/src/lib/TLSyncRoom.ts +10 -294
  43. package/src/lib/TLSyncStorage.ts +0 -8
  44. package/src/lib/protocol.ts +0 -17
  45. package/src/test/TLSocketRoom.test.ts +2 -37
  46. package/src/test/TLSyncRoom.test.ts +0 -25
  47. package/src/test/TestServer.ts +4 -14
  48. package/src/test/storageContractSuite.ts +0 -79
  49. package/src/test/upgradeDowngrade.test.ts +2 -144
  50. package/src/test/objectStore.test.ts +0 -630
@@ -111,41 +111,14 @@ class TLSyncRoom {
111
111
  storage;
112
112
  serializedSchema;
113
113
  documentTypes;
114
- /**
115
- * Record types served by the object-store lane. Object records ride the same wire messages
116
- * as document records but are gated by the session's `objectAccess` instead of `isReadonly`,
117
- * and are excluded from `documentTypes` so hosts can persist them in a separate lane.
118
- */
119
- objectTypes;
120
114
  presenceType;
121
115
  log;
122
116
  schema;
123
- authorizeRecord;
124
117
  sessionIdleTimeout;
125
- /**
126
- * The authorizer registered for a record type, widened to the room's record union. Each entry in
127
- * `authorizeRecord` is typed to its specific record; the cast here is the one place we can't
128
- * statically correlate a runtime `typeName` with its record type, so it lives in the library
129
- * rather than in every consumer.
130
- */
131
- authorizerFor(typeName) {
132
- const authorize = this.authorizeRecord?.[typeName];
133
- if (!authorize) return void 0;
134
- return (args) => {
135
- try {
136
- return authorize(args);
137
- } catch (e) {
138
- this.log?.error?.("record authorizer threw; rejecting the write", e);
139
- return null;
140
- }
141
- };
142
- }
143
118
  constructor(opts) {
144
119
  this.schema = opts.schema;
145
120
  this.log = opts.log;
146
121
  this.onPresenceChange = opts.onPresenceChange;
147
- this.onCommittedChanges = opts.onCommittedChanges;
148
- this.authorizeRecord = opts.authorizeRecord;
149
122
  this.storage = opts.storage;
150
123
  this.sessionIdleTimeout = opts.clientTimeout ?? SESSION_IDLE_TIMEOUT;
151
124
  assert(
@@ -153,17 +126,8 @@ class TLSyncRoom {
153
126
  "TLSyncRoom is supposed to run either on Cloudflare Workersor on a 18+ version of Node.js, which both support the native structuredClone API"
154
127
  );
155
128
  this.serializedSchema = JSON.parse(JSON.stringify(this.schema.serialize()));
156
- this.objectTypes = new Set(opts.objectTypes ?? []);
157
- for (const typeName of this.objectTypes) {
158
- const type = getOwnProperty(this.schema.types, typeName);
159
- assert(type, `TLSyncRoom: object type '${typeName}' is not registered in the schema`);
160
- assert(
161
- type.scope === "document",
162
- `TLSyncRoom: object type '${typeName}' must have scope 'document', got '${type.scope}'`
163
- );
164
- }
165
129
  this.documentTypes = new Set(
166
- Object.values(this.schema.types).filter((t) => t.scope === "document" && !this.objectTypes.has(t.typeName)).map((t) => t.typeName)
130
+ Object.values(this.schema.types).filter((t) => t.scope === "document").map((t) => t.typeName)
167
131
  );
168
132
  const presenceTypes = new Set(
169
133
  Object.values(this.schema.types).filter((t) => t.scope === "presence")
@@ -174,12 +138,6 @@ class TLSyncRoom {
174
138
  );
175
139
  }
176
140
  this.presenceType = presenceTypes.values().next()?.value ?? null;
177
- if (this.presenceType && this.authorizeRecord) {
178
- assert(
179
- !getOwnProperty(this.authorizeRecord, this.presenceType.typeName),
180
- `TLSyncRoom: authorizeRecord['${this.presenceType.typeName}'] is a presence type; presence records are not authorized`
181
- );
182
- }
183
141
  const { documentClock } = this.storage.transaction((txn) => {
184
142
  this.schema.migrateStorage(txn);
185
143
  });
@@ -306,7 +264,6 @@ class TLSyncRoom {
306
264
  cancellationTime: Date.now(),
307
265
  meta: session.meta,
308
266
  isReadonly: session.isReadonly,
309
- objectAccess: session.objectAccess,
310
267
  requiresLegacyRejection: session.requiresLegacyRejection,
311
268
  supportsStringAppend: session.supportsStringAppend
312
269
  });
@@ -398,7 +355,7 @@ class TLSyncRoom {
398
355
  * @internal
399
356
  */
400
357
  handleNewSession(opts) {
401
- const { sessionId, socket, meta, isReadonly, objectAccess } = opts;
358
+ const { sessionId, socket, meta, isReadonly } = opts;
402
359
  const existing = this.sessions.get(sessionId);
403
360
  this.sessions.set(sessionId, {
404
361
  state: RoomSessionState.AwaitingConnectMessage,
@@ -408,7 +365,6 @@ class TLSyncRoom {
408
365
  sessionStartTime: Date.now(),
409
366
  meta,
410
367
  isReadonly: isReadonly ?? false,
411
- objectAccess: objectAccess ?? "write",
412
368
  // this gets set later during handleConnectMessage
413
369
  requiresLegacyRejection: false,
414
370
  supportsStringAppend: true
@@ -428,7 +384,6 @@ class TLSyncRoom {
428
384
  socket,
429
385
  meta,
430
386
  isReadonly,
431
- objectAccess,
432
387
  serializedSchema,
433
388
  presenceId,
434
389
  presenceRecord,
@@ -449,7 +404,6 @@ class TLSyncRoom {
449
404
  outstandingDataMessages: [],
450
405
  meta,
451
406
  isReadonly,
452
- objectAccess: objectAccess ?? "write",
453
407
  requiresLegacyRejection,
454
408
  supportsStringAppend
455
409
  });
@@ -710,7 +664,6 @@ class TLSyncRoom {
710
664
  supportsStringAppend: session.supportsStringAppend,
711
665
  meta: session.meta,
712
666
  isReadonly: session.isReadonly,
713
- objectAccess: session.objectAccess,
714
667
  requiresLegacyRejection: session.requiresLegacyRejection
715
668
  });
716
669
  this._unsafe_sendMessage(session.sessionId, msg);
@@ -755,8 +708,7 @@ class TLSyncRoom {
755
708
  schema: this.schema.serialize(),
756
709
  serverClock: txn.getClock(),
757
710
  diff: { ...presenceDiff.value, ...docDiff },
758
- isReadonly: session.isReadonly,
759
- objectAccess: session.objectAccess
711
+ isReadonly: session.isReadonly
760
712
  };
761
713
  });
762
714
  this.lastDocumentClock = documentClock;
@@ -790,18 +742,13 @@ class TLSyncRoom {
790
742
  exhaustiveSwitchError(op[0]);
791
743
  }
792
744
  };
793
- const addDocument = (storage, changes2, id, _state, authorize) => {
745
+ const addDocument = (storage, changes2, id, _state) => {
794
746
  const res = session ? this.schema.migratePersistedRecord(_state, session.serializedSchema, "up") : { type: "success", value: _state };
795
747
  if (res.type === "error") {
796
748
  throw new TLSyncError(res.reason, TLSyncErrorCloseEventReason.CLIENT_TOO_OLD);
797
749
  }
798
- let { value: state } = res;
750
+ const { value: state } = res;
799
751
  const doc = storage.get(id);
800
- if (authorize) {
801
- const result2 = authorize(doc ?? null, state);
802
- if (!result2) return Result.ok(void 0);
803
- if (!doc) state = result2;
804
- }
805
752
  if (doc) {
806
753
  const recordType = assertExists(getOwnProperty(this.schema.types, doc.typeName));
807
754
  const diff = diffAndValidateRecord(doc, state, recordType);
@@ -817,7 +764,7 @@ class TLSyncRoom {
817
764
  }
818
765
  return Result.ok(void 0);
819
766
  };
820
- const patchDocument = (storage, changes2, id, patch, authorize) => {
767
+ const patchDocument = (storage, changes2, id, patch) => {
821
768
  const doc = storage.get(id);
822
769
  if (!doc) return;
823
770
  const recordType = assertExists(getOwnProperty(this.schema.types, doc.typeName));
@@ -828,7 +775,6 @@ class TLSyncRoom {
828
775
  if (downgraded.value === doc) {
829
776
  const diff = applyAndDiffRecord(doc, patch, recordType, legacyAppendMode);
830
777
  if (diff) {
831
- if (authorize && !authorize(doc, diff[1])) return;
832
778
  storage.set(id, diff[1]);
833
779
  propagateOp(changes2, id, [RecordOpType.Patch, diff[0]], doc, diff[1]);
834
780
  }
@@ -840,7 +786,6 @@ class TLSyncRoom {
840
786
  }
841
787
  const diff = diffAndValidateRecord(doc, upgraded.value, recordType, legacyAppendMode);
842
788
  if (diff) {
843
- if (authorize && !authorize(doc, upgraded.value)) return;
844
789
  storage.set(id, upgraded.value);
845
790
  propagateOp(changes2, id, [RecordOpType.Patch, diff], doc, upgraded.value);
846
791
  }
@@ -875,89 +820,21 @@ class TLSyncRoom {
875
820
  }
876
821
  }
877
822
  }
878
- const canWrite = (typeName) => !session || (this.objectTypes.has(typeName) ? session.objectAccess !== "read" : !session.isReadonly);
879
- if (message.diff) {
823
+ if (message.diff && !session?.isReadonly) {
880
824
  for (const [id, op] of objectMapEntriesIterable(message.diff)) {
881
825
  switch (op[0]) {
882
826
  case RecordOpType.Put: {
883
- if (!canWrite(op[1].typeName)) continue;
884
- if (!this.documentTypes.has(op[1].typeName) && !this.objectTypes.has(op[1].typeName)) {
827
+ if (!this.documentTypes.has(op[1].typeName)) {
885
828
  throw new TLSyncError(
886
829
  "invalid record",
887
830
  TLSyncErrorCloseEventReason.INVALID_RECORD
888
831
  );
889
832
  }
890
- const record = op[1];
891
- let authorize;
892
- if (session && this.authorizeRecord) {
893
- const prev = txn.get(id) ?? null;
894
- if (prev && prev.typeName !== record.typeName) {
895
- this.log?.warn?.(
896
- "skipping put that changes typeName",
897
- `${prev.typeName} -> ${record.typeName}`,
898
- id,
899
- "session:",
900
- session.sessionId
901
- );
902
- continue;
903
- }
904
- const authorizePut = this.authorizerFor(record.typeName);
905
- if (authorizePut) {
906
- authorize = (prevRec, next) => {
907
- const result2 = authorizePut(
908
- prevRec ? {
909
- session: { sessionId: session.sessionId, meta: session.meta },
910
- type: "update",
911
- prev: prevRec,
912
- next
913
- } : {
914
- session: { sessionId: session.sessionId, meta: session.meta },
915
- type: "create",
916
- prev: null,
917
- next
918
- }
919
- );
920
- if (!result2) {
921
- this.log?.warn?.(
922
- "authorizer vetoed put",
923
- record.typeName,
924
- id,
925
- "session:",
926
- session.sessionId
927
- );
928
- return null;
929
- }
930
- return prevRec ? next : result2;
931
- };
932
- }
933
- }
934
- addDocument(txn, docChanges, id, record, authorize);
833
+ addDocument(txn, docChanges, id, op[1]);
935
834
  break;
936
835
  }
937
836
  case RecordOpType.Patch: {
938
- const doc = txn.get(id);
939
- if (!doc) continue;
940
- if (!canWrite(doc.typeName)) continue;
941
- const authorizePatch = session && this.authorizerFor(doc.typeName);
942
- const authorize = authorizePatch ? (prev, next) => {
943
- const result2 = authorizePatch({
944
- session: { sessionId: session.sessionId, meta: session.meta },
945
- type: "update",
946
- prev,
947
- next
948
- });
949
- if (!result2) {
950
- this.log?.warn?.(
951
- "authorizer vetoed patch",
952
- doc.typeName,
953
- id,
954
- "session:",
955
- session.sessionId
956
- );
957
- }
958
- return result2;
959
- } : void 0;
960
- patchDocument(txn, docChanges, id, op[1], authorize);
837
+ patchDocument(txn, docChanges, id, op[1]);
961
838
  break;
962
839
  }
963
840
  case RecordOpType.Remove: {
@@ -965,23 +842,6 @@ class TLSyncRoom {
965
842
  if (!doc) {
966
843
  continue;
967
844
  }
968
- if (!canWrite(doc.typeName)) continue;
969
- const authorizeRemove = session && this.authorizerFor(doc.typeName);
970
- if (authorizeRemove && !authorizeRemove({
971
- session: { sessionId: session.sessionId, meta: session.meta },
972
- type: "delete",
973
- prev: doc,
974
- next: null
975
- })) {
976
- this.log?.warn?.(
977
- "authorizer vetoed delete",
978
- doc.typeName,
979
- id,
980
- "session:",
981
- session.sessionId
982
- );
983
- continue;
984
- }
985
845
  txn.delete(id);
986
846
  propagateOp(docChanges, id, op, doc, void 0);
987
847
  break;
@@ -1056,16 +916,6 @@ class TLSyncRoom {
1056
916
  this.onPresenceChange?.();
1057
917
  });
1058
918
  }
1059
- if (result.docChanges.diffs && this.onCommittedChanges) {
1060
- const diff = result.docChanges.diffs.diff;
1061
- queueMicrotask(() => {
1062
- try {
1063
- this.onCommittedChanges?.({ diff, documentClock });
1064
- } catch (e) {
1065
- this.log?.error?.("onCommittedChanges threw", e);
1066
- }
1067
- });
1068
- }
1069
919
  }
1070
920
  /**
1071
921
  * Handle the event when a client disconnects. Cleans up the session and