@shaxpir/duiduidui-models 1.25.11 → 1.26.0

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.
@@ -54,36 +54,25 @@ class Chat extends SharedContent_1.SharedContent {
54
54
  */
55
55
  async modelUpdated() {
56
56
  this.checkDisposed('Chat.modelUpdated');
57
- const chatId = this.id;
58
- const title = this.title;
59
- console.log(`[Chat.modelUpdated] START chat=${chatId} title=${JSON.stringify(title)} acquireCount=${this.acquireCount}`);
60
57
  // Update manifests for all participants
61
58
  await super.modelUpdated();
62
- console.log(`[Chat.modelUpdated] after super.modelUpdated chat=${chatId} acquireCount=${this.acquireCount}`);
63
59
  // Update workspace stubs for all participants
64
60
  const stub = this.toStub();
65
61
  const userIds = this.getAllSharedUserIds();
66
- console.log(`[Chat.modelUpdated] stub.title=${JSON.stringify(stub.title)} userIds=${JSON.stringify(userIds)}`);
67
62
  for (const userId of userIds) {
68
63
  try {
69
64
  const workspaceId = shaxpir_common_1.CachingHasher.makeMd5Base62Hash(userId + "-" + ContentKind_1.ContentKind.WORKSPACE);
70
65
  const workspace = await this.shareSync.acquire(ContentKind_1.ContentKind.WORKSPACE, workspaceId);
71
66
  if (workspace && workspace.exists()) {
72
- console.log(`[Chat.modelUpdated] upserting stub for user=${userId} workspace=${workspaceId}`);
73
67
  // Cast needed to avoid circular dependency between Chat and Workspace
74
68
  workspace.upsertChatStub(stub);
75
69
  workspace.release();
76
70
  }
77
- else {
78
- console.log(`[Chat.modelUpdated] workspace not found for user=${userId} workspace=${workspaceId}`);
79
- }
80
71
  }
81
72
  catch (err) {
82
- console.error(`[Chat.modelUpdated] ERROR for user=${userId}:`, err);
83
73
  this.shareSync.onError(err, 'Chat.modelUpdated.workspaceStub');
84
74
  }
85
75
  }
86
- console.log(`[Chat.modelUpdated] END chat=${chatId}`);
87
76
  }
88
77
  // ---- Getters ----
89
78
  get payload() {
@@ -18,6 +18,14 @@ export declare abstract class Model {
18
18
  protected shareSync: ShareSync;
19
19
  private _subscribingPromise;
20
20
  private _acquireCount;
21
+ private _disposeTimer;
22
+ /**
23
+ * How long to wait (ms) after refcount hits 0 before actually disposing.
24
+ * This allows rapid acquire/release cycles (e.g., multiple concurrent
25
+ * modelUpdated calls) to reuse the model without it being destroyed
26
+ * and re-fetched in between.
27
+ */
28
+ private static readonly DISPOSE_DELAY_MS;
21
29
  constructor(doc: Doc, shouldAcquire: boolean, shareSync: ShareSync);
22
30
  get kind(): ContentKind;
23
31
  get ref(): ContentRef;
@@ -13,6 +13,7 @@ class Model {
13
13
  this._clearEventListeners = null;
14
14
  this._subscribingPromise = null;
15
15
  this._acquireCount = 0;
16
+ this._disposeTimer = null;
16
17
  const model = this;
17
18
  model.doc = doc;
18
19
  model.shareSync = shareSync;
@@ -95,6 +96,11 @@ class Model {
95
96
  return this._acquireCount;
96
97
  }
97
98
  async acquire(minVersion) {
99
+ // Cancel any pending deferred disposal — this model is being reused
100
+ if (this._disposeTimer) {
101
+ clearTimeout(this._disposeTimer);
102
+ this._disposeTimer = null;
103
+ }
98
104
  this._acquireCount++;
99
105
  if (!this._hasEventListeners) {
100
106
  this._setupEventListeners();
@@ -113,7 +119,17 @@ class Model {
113
119
  this.log(`RELEASE: (${this.compoundKey}); acquireCount = ${this.acquireCount}`);
114
120
  if (this._acquireCount <= 0) {
115
121
  this._acquireCount = 0;
116
- this.dispose();
122
+ // Defer disposal to allow rapid re-acquire without destroying the doc.
123
+ // If acquire() is called before the timer fires, it cancels the timer.
124
+ if (!this._disposeTimer) {
125
+ this._disposeTimer = setTimeout(() => {
126
+ this._disposeTimer = null;
127
+ // Only dispose if still unreferenced
128
+ if (this._acquireCount <= 0) {
129
+ this.dispose();
130
+ }
131
+ }, Model.DISPOSE_DELAY_MS);
132
+ }
117
133
  }
118
134
  }
119
135
  dispose() {
@@ -301,3 +317,10 @@ class Model {
301
317
  }
302
318
  exports.Model = Model;
303
319
  Model.CHANGED = "MODEL_CHANGED";
320
+ /**
321
+ * How long to wait (ms) after refcount hits 0 before actually disposing.
322
+ * This allows rapid acquire/release cycles (e.g., multiple concurrent
323
+ * modelUpdated calls) to reuse the model without it being destroyed
324
+ * and re-fetched in between.
325
+ */
326
+ Model.DISPOSE_DELAY_MS = 5000;
@@ -28,27 +28,21 @@ class SharedContent extends Content_1.Content {
28
28
  async modelUpdated() {
29
29
  this.checkDisposed("SharedContent.modelUpdated");
30
30
  this.acquire();
31
- const contentId = this.id;
32
31
  const userIds = this.getAllSharedUserIds();
33
32
  for (const userId of userIds) {
34
33
  try {
35
34
  const manifestId = Manifest_1.Manifest.makeManifestId(userId);
36
- console.log(`[SharedContent.modelUpdated] acquiring manifest ${manifestId} for content=${contentId}`);
37
35
  const manifest = await this.shareSync.acquire(ContentKind_1.ContentKind.MANIFEST, manifestId);
38
- console.log(`[SharedContent.modelUpdated] acquired manifest ${manifestId} exists=${manifest?.exists()}`);
39
36
  if (manifest && manifest.exists()) {
40
37
  manifest.update(this);
41
38
  manifest.release();
42
- console.log(`[SharedContent.modelUpdated] updated and released manifest ${manifestId}`);
43
39
  }
44
40
  }
45
41
  catch (err) {
46
- console.error(`[SharedContent.modelUpdated] ERROR acquiring manifest for user=${userId}:`, err);
47
42
  this.shareSync.onError(err, 'SharedContent.modelUpdated.manifest');
48
43
  }
49
44
  }
50
45
  this.release();
51
- console.log(`[SharedContent.modelUpdated] released content=${contentId} acquireCount=${this.acquireCount}`);
52
46
  }
53
47
  }
54
48
  exports.SharedContent = SharedContent;
@@ -389,28 +389,15 @@ class ShareSync {
389
389
  // First, try getting the content from the cache
390
390
  const compoundKey = `${kind}/${id}`;
391
391
  if (this._modelCache.has(compoundKey)) {
392
- const cached = this._modelCache.get(compoundKey);
393
- console.log(`[ShareSync.load] cache hit: ${compoundKey} isDisposed=${cached._isDisposed} acquireCount=${cached.acquireCount}`);
394
- return cached;
392
+ return this._modelCache.get(compoundKey);
395
393
  }
396
394
  // If not in the cache, get the model doc from the connection.
397
395
  // Then wrap it and put it in the cache for future reference.
398
396
  const doc = this._connection.get(kind, id);
399
397
  if (!doc) {
400
- console.error(`[ShareSync.load] connection.get(${kind}, ${id}) returned null/undefined doc`);
398
+ console.error(`[ShareSync] connection.get(${kind}, ${id}) returned null/undefined doc`);
401
399
  return null;
402
400
  }
403
- const docAny = doc;
404
- const docState = {
405
- hasData: typeof doc.data !== 'undefined',
406
- version: doc.version,
407
- type: doc.type ? 'set' : 'null',
408
- wantsDestroy: docAny._wantsDestroy,
409
- inflightFetch: docAny.inflightFetch?.length,
410
- pendingFetch: docAny.pendingFetch?.length,
411
- hasPending: docAny.hasPending?.(),
412
- };
413
- console.log(`[ShareSync.load] cache miss: ${compoundKey} doc=${JSON.stringify(docState)}`);
414
401
  const model = this.wrap(kind, doc, false);
415
402
  this._modelCache.set(compoundKey, model);
416
403
  return model;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaxpir/duiduidui-models",
3
- "version": "1.25.11",
3
+ "version": "1.26.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/shaxpir/duiduidui-models"