@musnows/scriverse 0.7.2 → 0.7.3

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 (48) hide show
  1. package/README.md +5 -0
  2. package/dist/ai.js +248 -250
  3. package/dist/ai.js.map +1 -1
  4. package/dist/app.js +184 -39
  5. package/dist/app.js.map +1 -1
  6. package/dist/attachment-storage.js +28 -12
  7. package/dist/attachment-storage.js.map +1 -1
  8. package/dist/backup-encryption.js +129 -0
  9. package/dist/backup-encryption.js.map +1 -0
  10. package/dist/collaboration-presence.js +200 -18
  11. package/dist/collaboration-presence.js.map +1 -1
  12. package/dist/database.js +162 -3
  13. package/dist/database.js.map +1 -1
  14. package/dist/docx-export.js +1 -1
  15. package/dist/docx-export.js.map +1 -1
  16. package/dist/domain.js +18 -0
  17. package/dist/domain.js.map +1 -1
  18. package/dist/image-metadata.js +17 -2
  19. package/dist/image-metadata.js.map +1 -1
  20. package/dist/presence-store.js +160 -0
  21. package/dist/presence-store.js.map +1 -0
  22. package/dist/public/ai-context-meter.js +27 -0
  23. package/dist/public/ai-mentions.js +14 -0
  24. package/dist/public/app.js +1017 -291
  25. package/dist/public/background-task-center.js +1 -1
  26. package/dist/public/chapter-editor-virtualization.js +52 -0
  27. package/dist/public/index.html +43 -21
  28. package/dist/public/presence-client-id.d.ts +10 -0
  29. package/dist/public/presence-client-id.js +31 -0
  30. package/dist/public/s3-backup-ui.d.ts +10 -0
  31. package/dist/public/s3-backup-ui.js +29 -0
  32. package/dist/public/setting-filters.d.ts +16 -0
  33. package/dist/public/setting-filters.js +19 -0
  34. package/dist/public/styles.css +37 -7
  35. package/dist/s3-backup.js +197 -12
  36. package/dist/s3-backup.js.map +1 -1
  37. package/dist/security.js +82 -20
  38. package/dist/security.js.map +1 -1
  39. package/dist/server-runtime.js +36 -18
  40. package/dist/server-runtime.js.map +1 -1
  41. package/dist/store.js +75 -17
  42. package/dist/store.js.map +1 -1
  43. package/dist/upload-limits.js +35 -0
  44. package/dist/upload-limits.js.map +1 -0
  45. package/dist/user-auth.js +11 -1
  46. package/dist/user-auth.js.map +1 -1
  47. package/dist/version.js +1 -1
  48. package/package.json +3 -2
@@ -4,6 +4,7 @@ import { copyFile, mkdir, open, readFile, rm, stat } from "node:fs/promises";
4
4
  import { dirname, join, resolve, sep } from "node:path";
5
5
  import sharp from "sharp";
6
6
  import { AppError } from "./errors.js";
7
+ import { DEFAULT_ATTACHMENT_IMAGE_MAX_BYTES, formatUploadLimit } from "./upload-limits.js";
7
8
  const maximumPixels = 25_000_000;
8
9
  const maximumAnimationFrames = 100;
9
10
  const maximumAnimationPixels = 50_000_000;
@@ -41,11 +42,27 @@ async function sha256File(path) {
41
42
  }
42
43
  return hash.digest("hex");
43
44
  }
45
+ async function hasGifSignature(path) {
46
+ const handle = await open(path, "r");
47
+ const header = Buffer.alloc(6);
48
+ try {
49
+ const { bytesRead } = await handle.read(header, 0, header.length, 0);
50
+ if (bytesRead !== header.length)
51
+ return false;
52
+ const signature = header.toString("ascii");
53
+ return signature === "GIF87a" || signature === "GIF89a";
54
+ }
55
+ finally {
56
+ await handle.close();
57
+ }
58
+ }
44
59
  export class AttachmentStorage {
45
60
  rootDirectory;
61
+ maximumUploadBytes;
46
62
  temporaryDirectory;
47
- constructor(rootDirectory) {
63
+ constructor(rootDirectory, maximumUploadBytes = DEFAULT_ATTACHMENT_IMAGE_MAX_BYTES) {
48
64
  this.rootDirectory = rootDirectory;
65
+ this.maximumUploadBytes = maximumUploadBytes;
49
66
  this.temporaryDirectory = join(rootDirectory, ".tmp");
50
67
  }
51
68
  async prepare() {
@@ -82,10 +99,14 @@ export class AttachmentStorage {
82
99
  async ingest(sourcePath) {
83
100
  await this.prepare();
84
101
  const originalStats = await stat(sourcePath);
102
+ if (originalStats.size > this.maximumUploadBytes) {
103
+ throw new AppError(413, "ATTACHMENT_TOO_LARGE", `图片附件不能超过 ${formatUploadLimit(this.maximumUploadBytes)}`);
104
+ }
85
105
  const originalSha256 = await sha256File(sourcePath);
106
+ const isGif = await hasGifSignature(sourcePath);
86
107
  let metadata;
87
108
  try {
88
- metadata = await sharp(sourcePath, { animated: true, limitInputPixels: maximumPixels, sequentialRead: true }).metadata();
109
+ metadata = await sharp(sourcePath, { animated: true, limitInputPixels: isGif ? false : maximumPixels, sequentialRead: true }).metadata();
89
110
  }
90
111
  catch {
91
112
  throw new AppError(415, "INVALID_ATTACHMENT_IMAGE", "附件不是有效的图片文件");
@@ -99,19 +120,19 @@ export class AttachmentStorage {
99
120
  if (!Number.isInteger(width) || width <= 0 || !Number.isInteger(pageHeight) || pageHeight <= 0) {
100
121
  throw new AppError(415, "INVALID_ATTACHMENT_IMAGE", "无法读取附件图片尺寸");
101
122
  }
102
- if (width * pageHeight > maximumPixels)
123
+ if (!isGif && width * pageHeight > maximumPixels)
103
124
  throw new AppError(413, "ATTACHMENT_IMAGE_TOO_LARGE", "附件图片像素尺寸过大");
104
- if (!Number.isInteger(pageCount) || pageCount > maximumAnimationFrames) {
125
+ if (!isGif && (!Number.isInteger(pageCount) || pageCount > maximumAnimationFrames)) {
105
126
  throw new AppError(413, "ATTACHMENT_ANIMATION_TOO_LARGE", "附件动画帧数过多");
106
127
  }
107
- if (width * pageHeight * pageCount > maximumAnimationPixels) {
128
+ if (!isGif && width * pageHeight * pageCount > maximumAnimationPixels) {
108
129
  throw new AppError(413, "ATTACHMENT_ANIMATION_TOO_LARGE", "附件动画总像素量过大");
109
130
  }
110
131
  const originalMimeType = mimeType(format);
111
132
  const candidatePath = join(this.temporaryDirectory, `${originalSha256}-${Date.now()}.webp`);
112
133
  let selectedPath = sourcePath;
113
134
  let storedMimeType = originalMimeType;
114
- if (format !== "webp") {
135
+ if (format !== "webp" && !isGif) {
115
136
  try {
116
137
  await sharp(sourcePath, { animated: true, limitInputPixels: maximumPixels, sequentialRead: true })
117
138
  .webp({ lossless: true, effort: 6 })
@@ -126,13 +147,8 @@ export class AttachmentStorage {
126
147
  storedMimeType = "image/webp";
127
148
  }
128
149
  }
129
- catch (error) {
150
+ catch {
130
151
  await rm(candidatePath, { force: true });
131
- if (format === "gif") {
132
- if (error instanceof AppError)
133
- throw error;
134
- throw new AppError(422, "ATTACHMENT_ANIMATION_CONVERSION_FAILED", "GIF 附件无法安全转换为动画 WebP");
135
- }
136
152
  }
137
153
  }
138
154
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"attachment-storage.js","sourceRoot":"","sources":["../src/attachment-storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attachment-storage.js","sourceRoot":"","sources":["../src/attachment-storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
2
+ import { AppError } from "./errors.js";
3
+ export const BACKUP_ENCRYPTION_MAGIC = "SCRIVERSE-ENC1";
4
+ const magic = Buffer.from(BACKUP_ENCRYPTION_MAGIC, "ascii");
5
+ const envelopeVersion = 1;
6
+ const aes256GcmAlgorithm = 1;
7
+ const dekLength = 32;
8
+ const ivLength = 12;
9
+ const authTagLength = 16;
10
+ const headerLength = magic.byteLength + 16;
11
+ function parseKek(value) {
12
+ if (!/^[A-Za-z0-9_-]{43}$/u.test(value)) {
13
+ throw new AppError(400, "BACKUP_KEK_INVALID", "S3 备份加密密钥格式无效");
14
+ }
15
+ const key = Buffer.from(value, "base64url");
16
+ if (key.byteLength !== 32 || key.toString("base64url") !== value) {
17
+ throw new AppError(400, "BACKUP_KEK_INVALID", "S3 备份加密密钥格式无效");
18
+ }
19
+ return key;
20
+ }
21
+ function envelopeHeader(ciphertextLength) {
22
+ const header = Buffer.alloc(headerLength);
23
+ magic.copy(header, 0);
24
+ let offset = magic.byteLength;
25
+ header.writeUInt8(envelopeVersion, offset++);
26
+ header.writeUInt8(aes256GcmAlgorithm, offset++);
27
+ header.writeUInt8(ivLength, offset++);
28
+ header.writeUInt8(authTagLength, offset++);
29
+ header.writeUInt8(ivLength, offset++);
30
+ header.writeUInt8(authTagLength, offset++);
31
+ header.writeUInt16BE(dekLength, offset);
32
+ offset += 2;
33
+ header.writeBigUInt64BE(BigInt(ciphertextLength), offset);
34
+ return header;
35
+ }
36
+ export function generateKek() {
37
+ return randomBytes(32).toString("base64url");
38
+ }
39
+ export function isEncryptedEnvelope(value) {
40
+ return value.byteLength >= magic.byteLength && value.subarray(0, magic.byteLength).equals(magic);
41
+ }
42
+ export function encryptObject(plaintext, kek) {
43
+ const key = parseKek(kek);
44
+ const dek = randomBytes(dekLength);
45
+ const wrappingIv = randomBytes(ivLength);
46
+ const payloadIv = randomBytes(ivLength);
47
+ const header = envelopeHeader(plaintext.byteLength);
48
+ const keyCipher = createCipheriv("aes-256-gcm", key, wrappingIv);
49
+ keyCipher.setAAD(header);
50
+ const wrappedDek = Buffer.concat([keyCipher.update(dek), keyCipher.final()]);
51
+ const keyTag = keyCipher.getAuthTag();
52
+ const payloadCipher = createCipheriv("aes-256-gcm", dek, payloadIv);
53
+ payloadCipher.setAAD(header);
54
+ const ciphertext = Buffer.concat([payloadCipher.update(plaintext), payloadCipher.final()]);
55
+ const payloadTag = payloadCipher.getAuthTag();
56
+ return Buffer.concat([
57
+ header,
58
+ wrappingIv,
59
+ keyTag,
60
+ payloadIv,
61
+ payloadTag,
62
+ wrappedDek,
63
+ ciphertext
64
+ ]);
65
+ }
66
+ export function decryptObject(envelope, kek) {
67
+ if (envelope.byteLength < headerLength || !isEncryptedEnvelope(envelope)) {
68
+ throw new AppError(400, "BACKUP_ENVELOPE_INVALID", "备份对象不是 Scriverse 加密信封");
69
+ }
70
+ const header = envelope.subarray(0, headerLength);
71
+ let offset = magic.byteLength;
72
+ const version = header.readUInt8(offset++);
73
+ const algorithm = header.readUInt8(offset++);
74
+ const wrappingIvLength = header.readUInt8(offset++);
75
+ const wrappingTagLength = header.readUInt8(offset++);
76
+ const payloadIvLength = header.readUInt8(offset++);
77
+ const payloadTagLength = header.readUInt8(offset++);
78
+ const wrappedDekLength = header.readUInt16BE(offset);
79
+ offset += 2;
80
+ const ciphertextLengthBigInt = header.readBigUInt64BE(offset);
81
+ if (ciphertextLengthBigInt > BigInt(Number.MAX_SAFE_INTEGER)) {
82
+ throw new AppError(400, "BACKUP_ENVELOPE_INVALID", "备份加密信封长度无效");
83
+ }
84
+ const ciphertextLength = Number(ciphertextLengthBigInt);
85
+ if (version !== envelopeVersion
86
+ || algorithm !== aes256GcmAlgorithm
87
+ || wrappingIvLength !== ivLength
88
+ || wrappingTagLength !== authTagLength
89
+ || payloadIvLength !== ivLength
90
+ || payloadTagLength !== authTagLength
91
+ || wrappedDekLength !== dekLength) {
92
+ throw new AppError(400, "BACKUP_ENVELOPE_UNSUPPORTED", "备份加密信封版本或算法不受支持");
93
+ }
94
+ const expectedLength = headerLength
95
+ + wrappingIvLength
96
+ + wrappingTagLength
97
+ + payloadIvLength
98
+ + payloadTagLength
99
+ + wrappedDekLength
100
+ + ciphertextLength;
101
+ if (expectedLength !== envelope.byteLength) {
102
+ throw new AppError(400, "BACKUP_ENVELOPE_INVALID", "备份加密信封长度无效");
103
+ }
104
+ let bodyOffset = headerLength;
105
+ const wrappingIv = envelope.subarray(bodyOffset, bodyOffset += wrappingIvLength);
106
+ const wrappingTag = envelope.subarray(bodyOffset, bodyOffset += wrappingTagLength);
107
+ const payloadIv = envelope.subarray(bodyOffset, bodyOffset += payloadIvLength);
108
+ const payloadTag = envelope.subarray(bodyOffset, bodyOffset += payloadTagLength);
109
+ const wrappedDek = envelope.subarray(bodyOffset, bodyOffset += wrappedDekLength);
110
+ const ciphertext = envelope.subarray(bodyOffset);
111
+ try {
112
+ const keyDecipher = createDecipheriv("aes-256-gcm", parseKek(kek), wrappingIv);
113
+ keyDecipher.setAAD(header);
114
+ keyDecipher.setAuthTag(wrappingTag);
115
+ const dek = Buffer.concat([keyDecipher.update(wrappedDek), keyDecipher.final()]);
116
+ if (dek.byteLength !== dekLength)
117
+ throw new Error("Invalid DEK length");
118
+ const payloadDecipher = createDecipheriv("aes-256-gcm", dek, payloadIv);
119
+ payloadDecipher.setAAD(header);
120
+ payloadDecipher.setAuthTag(payloadTag);
121
+ return Buffer.concat([payloadDecipher.update(ciphertext), payloadDecipher.final()]);
122
+ }
123
+ catch (error) {
124
+ if (error instanceof AppError)
125
+ throw error;
126
+ throw new AppError(400, "BACKUP_DECRYPTION_FAILED", "备份对象解密失败,密钥错误或对象已损坏");
127
+ }
128
+ }
129
+ //# sourceMappingURL=backup-encryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-encryption.js","sourceRoot":"","sources":["../src/backup-encryption.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,11 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { logger, sanitizeError } from "./logger.js";
3
+ /**
4
+ * 协作状态以内存作为单进程热路径,并按周期批量同步到 SQLite。
5
+ * SQLite 让计划内重启可以恢复状态,也让多进程在各自同步周期后最终可见;它不是实时消息总线,
6
+ * 因此跨进程可见性会有至多一个同步周期的延迟,异常退出也可能丢失尚未同步的瞬时状态。
7
+ * Scriverse 以本地优先、单实例部署为主,当前不为亚秒级跨实例一致性额外引入 Redis 基础设施。
8
+ */
1
9
  export const presencePageKinds = [
2
10
  "welcome",
3
11
  "editor",
@@ -69,59 +77,112 @@ export class CollaborationPresence {
69
77
  maxChanges;
70
78
  entries = new Map();
71
79
  changes = [];
72
- changeSequence = 0;
73
- constructor(timeoutMs = 45_000, now = Date.now, changeTtlMs = 120_000, maxChanges = 50) {
80
+ dirtyEntryKeys = new Set();
81
+ pendingChanges = new Map();
82
+ lastPublishedAtMsByRecipient = new Map();
83
+ persistenceStore;
84
+ cleanupIntervalMs;
85
+ minPublishIntervalMs;
86
+ maintenanceTimer = null;
87
+ lastCleanupAt;
88
+ closed = false;
89
+ constructor(timeoutMs = 45_000, now = Date.now, changeTtlMs = 120_000, maxChanges = 50, persistenceOrMinPublishInterval) {
74
90
  this.timeoutMs = timeoutMs;
75
91
  this.now = now;
76
92
  this.changeTtlMs = changeTtlMs;
77
93
  this.maxChanges = maxChanges;
94
+ const persistence = typeof persistenceOrMinPublishInterval === "number" ? undefined : persistenceOrMinPublishInterval;
95
+ this.minPublishIntervalMs = typeof persistenceOrMinPublishInterval === "number"
96
+ ? persistenceOrMinPublishInterval
97
+ : persistence?.minPublishIntervalMs ?? 30_000;
98
+ this.persistenceStore = persistence?.store ?? null;
99
+ this.cleanupIntervalMs = Math.max(1, persistence?.cleanupIntervalMs ?? 60_000);
100
+ this.lastCleanupAt = this.now();
101
+ if (!this.persistenceStore)
102
+ return;
103
+ this.restoreFromStore(this.lastCleanupAt);
104
+ const flushIntervalMs = Math.max(1, persistence?.flushIntervalMs ?? 5_000);
105
+ this.maintenanceTimer = setInterval(() => {
106
+ try {
107
+ this.flush();
108
+ }
109
+ catch (error) {
110
+ logger.warn("collaboration.presence_flush_failed", { error: sanitizeError(error) });
111
+ }
112
+ }, flushIntervalMs);
113
+ this.maintenanceTimer.unref();
78
114
  }
79
115
  heartbeat(workId, clientId, user, page) {
80
116
  const now = this.now();
81
117
  this.prune(now);
82
118
  const normalized = normalizedPage(page);
83
- this.entries.set(`${workId}:${clientId}`, {
119
+ const entryKey = `${workId}:${clientId}`;
120
+ this.entries.set(entryKey, {
84
121
  workId,
85
122
  clientId,
86
123
  ...user,
87
124
  page: normalized,
88
125
  lastSeenAt: new Date(now).toISOString(),
89
- lastSeenMs: now
126
+ lastSeenMs: now,
127
+ persistedPage: { ...page }
90
128
  });
129
+ this.dirtyEntryKeys.add(entryKey);
91
130
  return {
92
131
  participants: this.list(workId, now),
93
132
  recentChanges: this.listChanges(workId, normalized.key, clientId, now)
94
133
  };
95
134
  }
96
- publishChange(workId, pageKey, actor, label = pageLabelForKey(pageKey)) {
135
+ publishChange(workId, pageKey, actor, labelOrOptions = {}) {
97
136
  const now = this.now();
98
137
  this.prune(now);
99
- const recipientClientIds = [...new Set([...this.entries.values()]
100
- .filter((entry) => (entry.workId === workId
101
- && entry.page.key === pageKey
102
- && entry.userId !== actor.userId))
103
- .map((entry) => entry.clientId))];
104
- if (recipientClientIds.length === 0)
138
+ const options = typeof labelOrOptions === "string" ? { label: labelOrOptions } : labelOrOptions;
139
+ const action = options.action ?? "save";
140
+ const label = options.label ?? pageLabelForKey(pageKey);
141
+ const pageDeleted = options.pageDeleted === true;
142
+ const publishKey = `${workId}:${pageKey}:${actor.userId}:${action}`;
143
+ const recipients = [...this.entries.values()]
144
+ .filter((entry) => (entry.workId === workId
145
+ && entry.page.key === pageKey
146
+ && entry.userId !== actor.userId))
147
+ .map((entry) => ({ userId: entry.userId, clientId: entry.clientId }))
148
+ .filter((recipient, index, values) => values.findIndex((candidate) => (candidate.userId === recipient.userId && candidate.clientId === recipient.clientId)) === index)
149
+ .filter((recipient) => {
150
+ if (this.minPublishIntervalMs <= 0)
151
+ return true;
152
+ const throttleKey = `${publishKey}:${recipient.userId}:${recipient.clientId}`;
153
+ const lastPublishedAt = this.lastPublishedAtMsByRecipient.get(throttleKey);
154
+ return lastPublishedAt === undefined || now - lastPublishedAt >= this.minPublishIntervalMs;
155
+ });
156
+ if (recipients.length === 0)
105
157
  return null;
106
- this.changeSequence += 1;
158
+ if (this.minPublishIntervalMs > 0) {
159
+ for (const recipient of recipients) {
160
+ this.lastPublishedAtMsByRecipient.set(`${publishKey}:${recipient.userId}:${recipient.clientId}`, now);
161
+ }
162
+ }
107
163
  const change = {
108
- id: `change-${now}-${this.changeSequence}`,
164
+ id: `change-${now}-${randomUUID()}`,
109
165
  workId,
110
166
  pageKey,
111
167
  label,
168
+ action,
169
+ pageDeleted,
112
170
  actorUserId: actor.userId,
113
171
  actorDisplayName: actor.displayName,
114
172
  savedAt: new Date(now).toISOString(),
115
173
  savedAtMs: now,
116
- recipientClientIds
174
+ recipients
117
175
  };
118
176
  this.changes.push(change);
177
+ this.pendingChanges.set(change.id, change);
119
178
  while (this.changes.length > this.maxChanges)
120
179
  this.changes.shift();
121
180
  return {
122
181
  id: change.id,
123
182
  pageKey: change.pageKey,
124
183
  label: change.label,
184
+ action: change.action,
185
+ pageDeleted: change.pageDeleted,
125
186
  actorUserId: change.actorUserId,
126
187
  actorDisplayName: change.actorDisplayName,
127
188
  savedAt: change.savedAt
@@ -129,24 +190,74 @@ export class CollaborationPresence {
129
190
  }
130
191
  listChanges(workId, pageKey, receiverClientId, now = this.now()) {
131
192
  this.pruneChanges(now);
193
+ const receiverUserId = this.entries.get(`${workId}:${receiverClientId}`)?.userId;
194
+ if (!receiverUserId)
195
+ return [];
132
196
  return this.changes
133
197
  .filter((change) => (change.workId === workId
134
198
  && change.pageKey === pageKey
135
- && change.recipientClientIds.includes(receiverClientId)))
199
+ && change.recipients.some((recipient) => (recipient.userId === receiverUserId && recipient.clientId === receiverClientId))))
136
200
  .sort((left, right) => right.savedAtMs - left.savedAtMs)
137
- .map(({ workId: _workId, savedAtMs: _savedAtMs, recipientClientIds: _recipientClientIds, ...change }) => change);
201
+ .map(({ workId: _workId, savedAtMs: _savedAtMs, recipients: _recipients, ...change }) => change);
202
+ }
203
+ flush(forceCleanup = false) {
204
+ if (!this.persistenceStore)
205
+ return;
206
+ const now = this.now();
207
+ this.prune(now);
208
+ const dirtyKeys = [...this.dirtyEntryKeys];
209
+ const pendingChangeIds = [...this.pendingChanges.keys()];
210
+ const shouldCleanup = forceCleanup || now - this.lastCleanupAt >= this.cleanupIntervalMs;
211
+ this.persistenceStore.flush({
212
+ entries: dirtyKeys.flatMap((key) => {
213
+ const entry = this.entries.get(key);
214
+ return entry ? [this.persistedEntry(entry)] : [];
215
+ }),
216
+ changes: pendingChangeIds.flatMap((id) => {
217
+ const change = this.pendingChanges.get(id);
218
+ return change ? [this.persistedChange(change)] : [];
219
+ }),
220
+ ...(shouldCleanup ? {
221
+ entryExpiryCutoff: new Date(now - this.timeoutMs).toISOString(),
222
+ changeExpiryCutoff: new Date(now - this.changeTtlMs).toISOString()
223
+ } : {})
224
+ });
225
+ for (const key of dirtyKeys)
226
+ this.dirtyEntryKeys.delete(key);
227
+ for (const id of pendingChangeIds)
228
+ this.pendingChanges.delete(id);
229
+ if (shouldCleanup)
230
+ this.lastCleanupAt = now;
231
+ this.restoreFromStore(now);
232
+ }
233
+ close() {
234
+ if (this.closed)
235
+ return;
236
+ if (this.maintenanceTimer) {
237
+ clearInterval(this.maintenanceTimer);
238
+ this.maintenanceTimer = null;
239
+ }
240
+ this.flush(true);
241
+ this.closed = true;
138
242
  }
139
243
  list(workId, now) {
140
244
  this.prune(now);
141
245
  return [...this.entries.values()]
142
246
  .filter((entry) => entry.workId === workId)
143
247
  .sort((left, right) => right.lastSeenMs - left.lastSeenMs || left.displayName.localeCompare(right.displayName, "zh-CN"))
144
- .map(({ workId: _workId, lastSeenMs: _lastSeenMs, ...participant }) => participant);
248
+ .map(({ workId: _workId, lastSeenMs: _lastSeenMs, persistedPage: _persistedPage, ...participant }) => participant);
145
249
  }
146
250
  prune(now) {
147
251
  for (const [key, entry] of this.entries) {
148
- if (now - entry.lastSeenMs > this.timeoutMs)
252
+ if (now - entry.lastSeenMs > this.timeoutMs) {
149
253
  this.entries.delete(key);
254
+ this.dirtyEntryKeys.delete(key);
255
+ }
256
+ }
257
+ for (const [key, publishedAtMs] of this.lastPublishedAtMsByRecipient) {
258
+ if (this.minPublishIntervalMs <= 0 || now - publishedAtMs >= this.minPublishIntervalMs) {
259
+ this.lastPublishedAtMsByRecipient.delete(key);
260
+ }
150
261
  }
151
262
  this.pruneChanges(now);
152
263
  }
@@ -158,5 +269,76 @@ export class CollaborationPresence {
158
269
  this.changes.shift();
159
270
  }
160
271
  }
272
+ persistedEntry(entry) {
273
+ return {
274
+ workId: entry.workId,
275
+ clientId: entry.clientId,
276
+ userId: entry.userId,
277
+ username: entry.username,
278
+ displayName: entry.displayName,
279
+ avatarUrl: entry.avatarUrl,
280
+ page: { ...entry.persistedPage },
281
+ lastSeenAt: entry.lastSeenAt
282
+ };
283
+ }
284
+ persistedChange(change) {
285
+ return {
286
+ id: change.id,
287
+ workId: change.workId,
288
+ pageKey: change.pageKey,
289
+ label: change.label,
290
+ action: change.action,
291
+ pageDeleted: change.pageDeleted,
292
+ actorUserId: change.actorUserId,
293
+ actorDisplayName: change.actorDisplayName,
294
+ savedAt: change.savedAt,
295
+ recipients: change.recipients.map((recipient) => ({ ...recipient }))
296
+ };
297
+ }
298
+ restoreFromStore(now) {
299
+ if (!this.persistenceStore)
300
+ return;
301
+ const activeEntries = this.persistenceStore.loadEntries(new Date(now - this.timeoutMs).toISOString());
302
+ for (const persisted of activeEntries) {
303
+ const lastSeenMs = Date.parse(persisted.lastSeenAt);
304
+ if (!Number.isFinite(lastSeenMs) || now - lastSeenMs > this.timeoutMs)
305
+ continue;
306
+ const key = `${persisted.workId}:${persisted.clientId}`;
307
+ const current = this.entries.get(key);
308
+ if (current && current.lastSeenMs >= lastSeenMs)
309
+ continue;
310
+ this.entries.set(key, {
311
+ workId: persisted.workId,
312
+ clientId: persisted.clientId,
313
+ userId: persisted.userId,
314
+ username: persisted.username,
315
+ displayName: persisted.displayName,
316
+ avatarUrl: persisted.avatarUrl,
317
+ page: normalizedPage(persisted.page),
318
+ lastSeenAt: persisted.lastSeenAt,
319
+ lastSeenMs,
320
+ persistedPage: { ...persisted.page }
321
+ });
322
+ this.dirtyEntryKeys.delete(key);
323
+ }
324
+ const knownChangeIds = new Set(this.changes.map((change) => change.id));
325
+ for (const persisted of this.persistenceStore.loadChanges(new Date(now - this.changeTtlMs).toISOString(), this.maxChanges)) {
326
+ if (knownChangeIds.has(persisted.id))
327
+ continue;
328
+ const savedAtMs = Date.parse(persisted.savedAt);
329
+ if (!Number.isFinite(savedAtMs) || now - savedAtMs > this.changeTtlMs)
330
+ continue;
331
+ this.changes.push({
332
+ ...persisted,
333
+ savedAtMs,
334
+ recipients: persisted.recipients.filter((recipient, index, values) => values.findIndex((candidate) => (candidate.userId === recipient.userId && candidate.clientId === recipient.clientId)) === index)
335
+ });
336
+ knownChangeIds.add(persisted.id);
337
+ }
338
+ this.changes.sort((left, right) => left.savedAtMs - right.savedAtMs || left.id.localeCompare(right.id));
339
+ while (this.changes.length > this.maxChanges)
340
+ this.changes.shift();
341
+ this.prune(now);
342
+ }
161
343
  }
162
344
  //# sourceMappingURL=collaboration-presence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"collaboration-presence.js","sourceRoot":"","sources":["../src/collaboration-presence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collaboration-presence.js","sourceRoot":"","sources":["../src/collaboration-presence.ts"],"names":[],"mappings":"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"}