@shaxpir/duiduidui-models 1.25.11 → 1.26.1
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/models/Chat.js +0 -11
- package/dist/models/Model.d.ts +8 -0
- package/dist/models/Model.js +24 -1
- package/dist/models/SharedContent.js +0 -6
- package/dist/repo/ShareSync.js +2 -15
- package/dist/util/VersionTag.d.ts +68 -0
- package/dist/util/VersionTag.js +86 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/package.json +2 -2
package/dist/models/Chat.js
CHANGED
|
@@ -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() {
|
package/dist/models/Model.d.ts
CHANGED
|
@@ -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;
|
package/dist/models/Model.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/repo/ShareSync.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { CompactDate } from '@shaxpir/shaxpir-common';
|
|
2
|
+
declare enum VersionTagBrand {
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* A date-based version identifier in YYYYMMDDx format, where x is a
|
|
6
|
+
* lowercase letter (a-z) allowing multiple versions per day.
|
|
7
|
+
*
|
|
8
|
+
* Used for:
|
|
9
|
+
* - System prompt versions (e.g., "20260321e")
|
|
10
|
+
* - Tool definition versions (e.g., "20260321e")
|
|
11
|
+
* - Built-in database releases (e.g., "20260316a")
|
|
12
|
+
*
|
|
13
|
+
* Examples: "20260321a", "20260321b", "20250118a"
|
|
14
|
+
*
|
|
15
|
+
* Version tags sort lexicographically in chronological order.
|
|
16
|
+
*/
|
|
17
|
+
export type VersionTag = string & VersionTagBrand;
|
|
18
|
+
export declare class VersionTagModel {
|
|
19
|
+
private static readonly PATTERN;
|
|
20
|
+
/**
|
|
21
|
+
* Validate and create a branded VersionTag from a string.
|
|
22
|
+
* Returns null if the format is invalid.
|
|
23
|
+
*/
|
|
24
|
+
static from(value: string): VersionTag | null;
|
|
25
|
+
/**
|
|
26
|
+
* Validate and create a branded VersionTag from a string.
|
|
27
|
+
* Throws if the format is invalid.
|
|
28
|
+
*/
|
|
29
|
+
static fromOrThrow(value: string): VersionTag;
|
|
30
|
+
/**
|
|
31
|
+
* Validate YYYYMMDDx format (where x is a lowercase letter a-z).
|
|
32
|
+
*/
|
|
33
|
+
static isValid(value: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Compare two version tags.
|
|
36
|
+
* Returns: negative if a < b, 0 if equal, positive if a > b.
|
|
37
|
+
*/
|
|
38
|
+
static compare(a: VersionTag, b: VersionTag): number;
|
|
39
|
+
/**
|
|
40
|
+
* Returns true if a comes before b chronologically.
|
|
41
|
+
*/
|
|
42
|
+
static isBefore(a: VersionTag, b: VersionTag): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Returns true if a comes after b chronologically.
|
|
45
|
+
*/
|
|
46
|
+
static isAfter(a: VersionTag, b: VersionTag): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Extract the date portion as a CompactDate (YYYYMMDD).
|
|
49
|
+
* Example: "20260321e" → "20260321"
|
|
50
|
+
*/
|
|
51
|
+
static date(tag: VersionTag): CompactDate;
|
|
52
|
+
/**
|
|
53
|
+
* Extract the letter suffix.
|
|
54
|
+
* Example: "20260321e" → "e"
|
|
55
|
+
*/
|
|
56
|
+
static letter(tag: VersionTag): string;
|
|
57
|
+
/**
|
|
58
|
+
* Generate the next version tag in sequence for the same day.
|
|
59
|
+
* Example: "20260321a" → "20260321b"
|
|
60
|
+
* Returns null if already at 'z'.
|
|
61
|
+
*/
|
|
62
|
+
static next(tag: VersionTag): VersionTag | null;
|
|
63
|
+
/**
|
|
64
|
+
* Check if two version tags share the same date.
|
|
65
|
+
*/
|
|
66
|
+
static isSameDay(a: VersionTag, b: VersionTag): boolean;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VersionTagModel = void 0;
|
|
4
|
+
var VersionTagBrand;
|
|
5
|
+
(function (VersionTagBrand) {
|
|
6
|
+
})(VersionTagBrand || (VersionTagBrand = {}));
|
|
7
|
+
class VersionTagModel {
|
|
8
|
+
/**
|
|
9
|
+
* Validate and create a branded VersionTag from a string.
|
|
10
|
+
* Returns null if the format is invalid.
|
|
11
|
+
*/
|
|
12
|
+
static from(value) {
|
|
13
|
+
if (!VersionTagModel.isValid(value))
|
|
14
|
+
return null;
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validate and create a branded VersionTag from a string.
|
|
19
|
+
* Throws if the format is invalid.
|
|
20
|
+
*/
|
|
21
|
+
static fromOrThrow(value) {
|
|
22
|
+
if (!VersionTagModel.isValid(value)) {
|
|
23
|
+
throw new Error(`Invalid VersionTag format: "${value}". Expected YYYYMMDDx (e.g., "20260321a")`);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate YYYYMMDDx format (where x is a lowercase letter a-z).
|
|
29
|
+
*/
|
|
30
|
+
static isValid(value) {
|
|
31
|
+
return VersionTagModel.PATTERN.test(value);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Compare two version tags.
|
|
35
|
+
* Returns: negative if a < b, 0 if equal, positive if a > b.
|
|
36
|
+
*/
|
|
37
|
+
static compare(a, b) {
|
|
38
|
+
return a.localeCompare(b);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns true if a comes before b chronologically.
|
|
42
|
+
*/
|
|
43
|
+
static isBefore(a, b) {
|
|
44
|
+
return VersionTagModel.compare(a, b) < 0;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns true if a comes after b chronologically.
|
|
48
|
+
*/
|
|
49
|
+
static isAfter(a, b) {
|
|
50
|
+
return VersionTagModel.compare(a, b) > 0;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract the date portion as a CompactDate (YYYYMMDD).
|
|
54
|
+
* Example: "20260321e" → "20260321"
|
|
55
|
+
*/
|
|
56
|
+
static date(tag) {
|
|
57
|
+
return tag.substring(0, 8);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract the letter suffix.
|
|
61
|
+
* Example: "20260321e" → "e"
|
|
62
|
+
*/
|
|
63
|
+
static letter(tag) {
|
|
64
|
+
return tag.charAt(8);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate the next version tag in sequence for the same day.
|
|
68
|
+
* Example: "20260321a" → "20260321b"
|
|
69
|
+
* Returns null if already at 'z'.
|
|
70
|
+
*/
|
|
71
|
+
static next(tag) {
|
|
72
|
+
const letter = VersionTagModel.letter(tag);
|
|
73
|
+
if (letter === 'z')
|
|
74
|
+
return null;
|
|
75
|
+
const nextLetter = String.fromCharCode(letter.charCodeAt(0) + 1);
|
|
76
|
+
return (tag.substring(0, 8) + nextLetter);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if two version tags share the same date.
|
|
80
|
+
*/
|
|
81
|
+
static isSameDay(a, b) {
|
|
82
|
+
return a.substring(0, 8) === b.substring(0, 8);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.VersionTagModel = VersionTagModel;
|
|
86
|
+
VersionTagModel.PATTERN = /^\d{8}[a-z]$/;
|
package/dist/util/index.d.ts
CHANGED
package/dist/util/index.js
CHANGED
|
@@ -25,3 +25,4 @@ __exportStar(require("./PinyinValidator"), exports);
|
|
|
25
25
|
__exportStar(require("./SearchPreprocessor"), exports);
|
|
26
26
|
__exportStar(require("./SearchTokenizer"), exports);
|
|
27
27
|
__exportStar(require("./SenseRankEncoder"), exports);
|
|
28
|
+
__exportStar(require("./VersionTag"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shaxpir/duiduidui-models",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/shaxpir/duiduidui-models"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@shaxpir/duiduidui-models": "^1.10.3",
|
|
20
20
|
"@shaxpir/sharedb": "^6.0.6",
|
|
21
|
-
"@shaxpir/shaxpir-common": "^1.4.
|
|
21
|
+
"@shaxpir/shaxpir-common": "^1.4.2",
|
|
22
22
|
"ot-json1": "1.0.1",
|
|
23
23
|
"ot-text-unicode": "4.0.0",
|
|
24
24
|
"reconnecting-websocket": "4.4.0"
|