teamplay 0.4.0-alpha.72 → 0.4.0-alpha.74

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.
@@ -913,6 +913,19 @@ After `useBatch()` stops throwing in compat mode, immediate reads via
913
913
  `useLocal(...).get(...)` for already requested batch entities should not produce
914
914
  transient `undefined` caused by materialization races.
915
915
 
916
+ ### Missing ShareDB Docs
917
+
918
+ Compat now mirrors Racer behavior for **missing public docs** (`type === null`,
919
+ `version === 0`) after subscribe/fetch:
920
+
921
+ - `connection.get(collection, id).data` becomes a truthy empty observable object;
922
+ - but the compat/model path still stays unresolved, so `$.collection[id].get()`
923
+ continues to return `undefined` until the document is actually created.
924
+
925
+ This matters for legacy consumers which read `shareDoc.data` directly (for
926
+ example readonly rich-text paths) while still expecting normal public-doc
927
+ creation semantics from model mutators.
928
+
916
929
  ## Examples
917
930
 
918
931
  ### useDoc with Suspense
@@ -46,7 +46,7 @@ import universal$ from '../../react/universal$.js'
46
46
 
47
47
  class SignalCompat extends Signal {
48
48
  static ID_FIELDS = ['_id', 'id']
49
- static [GETTERS] = [...DEFAULT_GETTERS, 'getCopy', 'getDeepCopy']
49
+ static [GETTERS] = [...DEFAULT_GETTERS, 'at', 'scope', 'getCopy', 'getDeepCopy']
50
50
 
51
51
  get root () {
52
52
  return this.scope()
@@ -5,6 +5,7 @@ import * as promiseBatcher from '../../react/promiseBatcher.js'
5
5
  import { getRaw } from '../dataTree.js'
6
6
  import { getConnection } from '../connection.js'
7
7
  import { isCompatEnv } from '../compatEnv.js'
8
+ import { isMissingShareDoc } from '../missingDoc.js'
8
9
 
9
10
  const $root = getRootSignal({ rootId: GLOBAL_ROOT_ID, rootFunction: universal$ })
10
11
  const emittedCompatWarnings = new Set()
@@ -393,7 +394,7 @@ function isDocReady (segments) {
393
394
  const [collection, id] = segments
394
395
  const shareDoc = getShareDoc(collection, id)
395
396
  // Missing docs should not block the batch barrier forever.
396
- return !!(shareDoc && shareDoc.type === null && shareDoc.data == null)
397
+ return isMissingShareDoc(shareDoc)
397
398
  }
398
399
 
399
400
  function getShareDoc (collection, id) {
package/orm/Doc.js CHANGED
@@ -7,6 +7,7 @@ import SubscriptionState from './SubscriptionState.js'
7
7
  import { getIdFieldsForSegments, injectIdFields, isPlainObject } from './idFields.js'
8
8
  import { emitModelChange, isModelEventsEnabled } from './Compat/modelEvents.js'
9
9
  import { getSubscriptionGcDelay } from './subscriptionGcDelay.js'
10
+ import { isMissingShareDoc } from './missingDoc.js'
10
11
 
11
12
  const ERROR_ON_EXCESSIVE_UNSUBSCRIBES = false
12
13
 
@@ -103,6 +104,20 @@ class Doc {
103
104
 
104
105
  _refData () {
105
106
  const doc = getConnection().get(this.collection, this.docId)
107
+ // Racer/react-sharedb-hooks normalizes a missing ShareDB doc into a truthy
108
+ // observable placeholder on the shareDoc itself (`observable(undefined) -> {}`),
109
+ // while still keeping the model tree path unresolved. Some legacy consumers
110
+ // (for example readonly RTEditor paths) rely on this exact contract by reading
111
+ // `connection.get(...).data` directly and only checking for truthiness.
112
+ //
113
+ // We intentionally mirror that behavior here:
114
+ // - missing doc => keep model path undefined
115
+ // - but make shareDoc.data truthy/observable so direct ShareDB consumers behave
116
+ // the same way they do under Racer.
117
+ if (isMissingShareDoc(doc) && doc.data === undefined) {
118
+ if (!isObservable(doc.data)) doc.data = observable(undefined)
119
+ return
120
+ }
106
121
  if (doc.data == null) return
107
122
  const idFields = getIdFieldsForSegments([this.collection, this.docId])
108
123
  if (isPlainObject(doc.data)) injectIdFields(doc.data, idFields, this.docId)
package/orm/dataTree.js CHANGED
@@ -7,6 +7,7 @@ import { getIdFieldsForSegments, injectIdFields, stripIdFields, isPlainObject }
7
7
  import { emitModelChange, isModelEventsEnabled } from './Compat/modelEvents.js'
8
8
  import { isSilentContextActive } from './Compat/silentContext.js'
9
9
  import { isCompatEnv } from './compatEnv.js'
10
+ import { isMissingShareDoc } from './missingDoc.js'
10
11
 
11
12
  const ALLOW_PARTIAL_DOC_CREATION = false
12
13
 
@@ -344,6 +345,10 @@ function resolvePublicDocState ({
344
345
  }) {
345
346
  ensureLocalDocSyncedWithShareDoc({ collection, docId, doc, idFields })
346
347
 
348
+ if (isMissingShareDoc(doc)) {
349
+ return { exists: false, snapshot: undefined, source: 'none' }
350
+ }
351
+
347
352
  if (doc?.data != null) {
348
353
  return {
349
354
  exists: true,
@@ -390,6 +395,7 @@ function ensureLocalDocSyncedWithShareDoc ({
390
395
  doc,
391
396
  idFields
392
397
  }) {
398
+ if (isMissingShareDoc(doc)) return
393
399
  if (doc?.data == null) return
394
400
  if (isPlainObject(doc.data)) injectIdFields(doc.data, idFields, docId)
395
401
  const shared = raw(doc.data)
@@ -0,0 +1,3 @@
1
+ export function isMissingShareDoc (doc) {
2
+ return !!doc && doc.type === null && doc.version === 0
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamplay",
3
- "version": "0.4.0-alpha.72",
3
+ "version": "0.4.0-alpha.74",
4
4
  "description": "Full-stack signals ORM with multiplayer",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -83,5 +83,5 @@
83
83
  ]
84
84
  },
85
85
  "license": "MIT",
86
- "gitHead": "204b5dd49e950162c6930bb9fcfbceeea51382ec"
86
+ "gitHead": "52e6aafb0dc6fba829f54375997a5880f4a32d6c"
87
87
  }