@pmate/chat-sdk 0.12.0-next.37 → 0.12.0-next.39
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/lib/runtime/chatSession.js +1 -1
- package/lib/runtime/messageClient.js +17 -10
- package/lib/runtime/readIndex.d.ts +11 -2
- package/lib/runtime/readIndex.js +41 -9
- package/lib/runtime/readSync.d.ts +1 -0
- package/lib/runtime/readSync.js +8 -5
- package/lib/runtime/readingClient.d.ts +1 -0
- package/lib/runtime/readingClient.js +4 -3
- package/package.json +1 -1
|
@@ -67,7 +67,7 @@ export class ChatSession {
|
|
|
67
67
|
return options.connection.onReach(event);
|
|
68
68
|
throw new Error("REACH_HANDLER_UNAVAILABLE");
|
|
69
69
|
} });
|
|
70
|
-
this.reading = new ReadingClient({ ...options.reading, teamId: scope.teamId, storageScope: JSON.stringify([scope.accountId]), migrateLegacy: true });
|
|
70
|
+
this.reading = new ReadingClient({ ...options.reading, teamId: scope.teamId, storageScope: JSON.stringify([scope.accountId]), migrateLegacy: true, metadataOnly: true });
|
|
71
71
|
this.readers = new MessageReadersClient({ appId: scope.appId, profileId: scope.profileId, token: options.reading.sessionToken, scope, access: () => this.messages.scopeAccess(), onError: options.onError, hubHttpUrl: options.reading.hubHttpUrl, fetch: options.reading.fetch });
|
|
72
72
|
this.sender = new DurableChatSender(this.connection, scope, options.onError, { access: () => this.messages.scopeAccess() });
|
|
73
73
|
this.catchUp = new WindowedTasks(1000, error => { if (!this.closed)
|
|
@@ -63,7 +63,7 @@ export class MessageClient {
|
|
|
63
63
|
const current = await this.storage.scopeAccess();
|
|
64
64
|
if (!this.closed && (this.scopeVersion < version || current.revoked)) {
|
|
65
65
|
this.denyScope();
|
|
66
|
-
options.onError?.(
|
|
66
|
+
options.onError?.(accessRevoked(), "");
|
|
67
67
|
}
|
|
68
68
|
}).catch(error => options.onError?.(error, ""));
|
|
69
69
|
}
|
|
@@ -76,7 +76,7 @@ export class MessageClient {
|
|
|
76
76
|
if (this.closed || (current && !current.revoked && (current.resetVersion ?? 0) >= denied.version))
|
|
77
77
|
return;
|
|
78
78
|
this.denyView(denied.thread, denied.version);
|
|
79
|
-
options.onError?.(
|
|
79
|
+
options.onError?.(accessRevoked(), denied.thread);
|
|
80
80
|
})();
|
|
81
81
|
}
|
|
82
82
|
else if (event.data?.type === "legacy" && typeof event.data.thread === "string" && this.watches.has(event.data.thread)) {
|
|
@@ -139,7 +139,7 @@ export class MessageClient {
|
|
|
139
139
|
const task = (async () => {
|
|
140
140
|
await this.ensureScope();
|
|
141
141
|
if (this.scopeDenied)
|
|
142
|
-
throw
|
|
142
|
+
throw accessRevoked();
|
|
143
143
|
const local = await this.storage.read(thread, limit);
|
|
144
144
|
if ((local.coverage?.scopeVersion ?? 0) !== this.scopeVersion) {
|
|
145
145
|
this.denyScope();
|
|
@@ -192,6 +192,8 @@ export class MessageClient {
|
|
|
192
192
|
return cached;
|
|
193
193
|
}
|
|
194
194
|
const snapshot = await this.reload(thread);
|
|
195
|
+
if (this.scopeDenied || this.revoked.has(thread))
|
|
196
|
+
throw accessRevoked();
|
|
195
197
|
if (local.coverage?.complete && Date.now() - (local.coverage.syncedAt ?? 0) > (this.options.freshnessMs ?? 30_000)
|
|
196
198
|
&& (typeof navigator === "undefined" || navigator.onLine !== false))
|
|
197
199
|
this.invalidate(thread);
|
|
@@ -211,6 +213,8 @@ export class MessageClient {
|
|
|
211
213
|
}
|
|
212
214
|
async fallback(thread, limit, before) {
|
|
213
215
|
await this.ensureScope();
|
|
216
|
+
if (this.scopeDenied || this.revoked.has(thread))
|
|
217
|
+
throw accessRevoked();
|
|
214
218
|
const page = await this.storage.readFallback(thread, limit, before);
|
|
215
219
|
if ((page.coverage?.scopeVersion ?? 0) !== this.scopeVersion) {
|
|
216
220
|
this.denyScope();
|
|
@@ -310,7 +314,7 @@ export class MessageClient {
|
|
|
310
314
|
if (this.closed || this.closing)
|
|
311
315
|
return Promise.reject(new Error("MESSAGE_CLIENT_UNAVAILABLE"));
|
|
312
316
|
if (this.revoked.has(thread))
|
|
313
|
-
return Promise.reject(
|
|
317
|
+
return Promise.reject(accessRevoked());
|
|
314
318
|
if (messages.length > 1000 || this.pendingRecords + messages.length > 4096)
|
|
315
319
|
return Promise.reject(new Error("MESSAGE_STORAGE_BACKPRESSURE"));
|
|
316
320
|
let bytes = 0;
|
|
@@ -331,7 +335,7 @@ export class MessageClient {
|
|
|
331
335
|
const committed = new Promise((yes, no) => { resolve = yes; reject = no; });
|
|
332
336
|
const enqueue = this.ensureScope().then(() => {
|
|
333
337
|
if (this.closed || this.scopeDenied)
|
|
334
|
-
throw
|
|
338
|
+
throw accessRevoked();
|
|
335
339
|
if (!this.scopeAllowed)
|
|
336
340
|
throw new Error("MESSAGE_SCOPE_NOT_READY");
|
|
337
341
|
const queue = this.writes.get(thread) ?? [];
|
|
@@ -457,7 +461,7 @@ export class MessageClient {
|
|
|
457
461
|
}
|
|
458
462
|
for (;;) {
|
|
459
463
|
if (this.closed || this.scopeDenied || this.revoked.has(thread))
|
|
460
|
-
throw
|
|
464
|
+
throw accessRevoked();
|
|
461
465
|
if (page.resetRequired)
|
|
462
466
|
throw new Error("MESSAGE_SYNC_RESET_REQUIRED");
|
|
463
467
|
await this.persist(thread, page.messages, page.checkpoint, true, after ? undefined : page.nextBefore, Date.now(), version, scopeVersion);
|
|
@@ -500,11 +504,11 @@ export class MessageClient {
|
|
|
500
504
|
async earlier(thread, before, limit = 100, expand = false) {
|
|
501
505
|
await this.ensureScope();
|
|
502
506
|
if (this.scopeDenied)
|
|
503
|
-
throw
|
|
507
|
+
throw accessRevoked();
|
|
504
508
|
if (this.closed)
|
|
505
509
|
throw new Error("MESSAGE_CLIENT_CLOSED");
|
|
506
510
|
if (this.revoked.has(thread))
|
|
507
|
-
throw
|
|
511
|
+
throw accessRevoked();
|
|
508
512
|
try {
|
|
509
513
|
if (!(await this.storage.read(thread, 1)).coverage?.complete) {
|
|
510
514
|
if (typeof navigator !== "undefined" && navigator.onLine === false)
|
|
@@ -571,6 +575,8 @@ export class MessageClient {
|
|
|
571
575
|
await task;
|
|
572
576
|
page = await this.storage.read(thread, limit, before);
|
|
573
577
|
}
|
|
578
|
+
if (this.scopeDenied || this.revoked.has(thread))
|
|
579
|
+
throw accessRevoked();
|
|
574
580
|
return { messages: page.messages, hasEarlier: page.hasEarlier || Boolean(page.coverage?.nextBefore), resetVersion: page.coverage?.resetVersion ?? 0, complete: page.coverage?.complete ?? false };
|
|
575
581
|
}
|
|
576
582
|
denyView(thread, version) {
|
|
@@ -598,7 +604,7 @@ export class MessageClient {
|
|
|
598
604
|
for (const thread of this.watches.keys())
|
|
599
605
|
this.denyView(thread);
|
|
600
606
|
for (const queue of this.writes.values()) {
|
|
601
|
-
queue.forEach(write => write.reject(
|
|
607
|
+
queue.forEach(write => write.reject(accessRevoked()));
|
|
602
608
|
this.release(queue);
|
|
603
609
|
}
|
|
604
610
|
this.writes.clear();
|
|
@@ -625,7 +631,7 @@ export class MessageClient {
|
|
|
625
631
|
const queue = this.writes.get(thread);
|
|
626
632
|
this.writes.delete(thread);
|
|
627
633
|
if (queue) {
|
|
628
|
-
queue.forEach(write => write.reject(
|
|
634
|
+
queue.forEach(write => write.reject(accessRevoked()));
|
|
629
635
|
this.release(queue);
|
|
630
636
|
}
|
|
631
637
|
try {
|
|
@@ -663,3 +669,4 @@ export class MessageClient {
|
|
|
663
669
|
}
|
|
664
670
|
}
|
|
665
671
|
}
|
|
672
|
+
function accessRevoked() { return Object.assign(new Error("MESSAGE_ACCESS_REVOKED"), { status: 403 }); }
|
|
@@ -7,9 +7,16 @@ export type IndexedReadMessage = {
|
|
|
7
7
|
mentions: string[];
|
|
8
8
|
readPolicy: "visible" | "explicit";
|
|
9
9
|
recalled: boolean;
|
|
10
|
-
/**
|
|
10
|
+
/** Legacy standalone readers may retain payloads; unified sessions use the message store. */
|
|
11
11
|
payload: unknown;
|
|
12
|
+
/** Body-free control classification retained by metadata-only reading indexes. */
|
|
13
|
+
readingControl?: boolean;
|
|
12
14
|
};
|
|
15
|
+
/** Retain read semantics without a second durable copy of message bodies. */
|
|
16
|
+
export declare function readingMetadata<T extends {
|
|
17
|
+
appId: string;
|
|
18
|
+
threads: IndexedReadThread[];
|
|
19
|
+
}>(state: T): T;
|
|
13
20
|
export type ReadingOperation = {
|
|
14
21
|
operationId: string;
|
|
15
22
|
threadId: string;
|
|
@@ -98,6 +105,7 @@ export interface ReadIndexPersistence {
|
|
|
98
105
|
export declare class IndexedDbReadIndex implements ReadIndexPersistence {
|
|
99
106
|
private storageScope?;
|
|
100
107
|
private migrateLegacy;
|
|
108
|
+
private metadataOnly;
|
|
101
109
|
private database;
|
|
102
110
|
private profiles;
|
|
103
111
|
private threadRows;
|
|
@@ -106,11 +114,12 @@ export declare class IndexedDbReadIndex implements ReadIndexPersistence {
|
|
|
106
114
|
private normalizedStates;
|
|
107
115
|
private versions;
|
|
108
116
|
private closed;
|
|
109
|
-
constructor(factory?: IDBFactory, name?: string, storageScope?: string | undefined, migrateLegacy?: boolean);
|
|
117
|
+
constructor(factory?: IDBFactory, name?: string, storageScope?: string | undefined, migrateLegacy?: boolean, metadataOnly?: boolean);
|
|
110
118
|
private transaction;
|
|
111
119
|
private key;
|
|
112
120
|
private hydrate;
|
|
113
121
|
private loadCurrent;
|
|
122
|
+
private loadStored;
|
|
114
123
|
/** Message bodies are immutable for local read observations: keep them out of
|
|
115
124
|
* profile/cursor writes, and update only changed normalized rows on sync. */
|
|
116
125
|
private persist;
|
package/lib/runtime/readIndex.js
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { Dexie } from "dexie";
|
|
2
2
|
import { isReadingControl } from "./readingContent.js";
|
|
3
3
|
import { isValidChatTeamId } from "./thread.js";
|
|
4
|
+
/** Retain read semantics without a second durable copy of message bodies. */
|
|
5
|
+
export function readingMetadata(state) {
|
|
6
|
+
let changed = false;
|
|
7
|
+
const threads = state.threads.map(thread => {
|
|
8
|
+
let different = false;
|
|
9
|
+
const messages = thread.messages.map(message => {
|
|
10
|
+
if (message.payload === null && typeof message.readingControl === "boolean")
|
|
11
|
+
return message;
|
|
12
|
+
different = true;
|
|
13
|
+
return { ...message, payload: null, readingControl: Boolean(message.readingControl) || isReadingControl(message.payload, state.appId) };
|
|
14
|
+
});
|
|
15
|
+
if (!different)
|
|
16
|
+
return thread;
|
|
17
|
+
changed = true;
|
|
18
|
+
return { ...thread, messages };
|
|
19
|
+
});
|
|
20
|
+
return changed ? { ...state, threads } : state;
|
|
21
|
+
}
|
|
4
22
|
export class ReadSyncGap extends Error {
|
|
5
23
|
cursor;
|
|
6
24
|
constructor(cursor) {
|
|
@@ -159,7 +177,7 @@ export function unreadInThread(state, threadId, hidden) {
|
|
|
159
177
|
if (!thread)
|
|
160
178
|
throw new Error("UNKNOWN_READ_THREAD");
|
|
161
179
|
const explicit = new Set(thread.explicitReadMessageIds);
|
|
162
|
-
const messages = thread.messages.filter(message => !message.recalled && !hidden?.get(message.id)?.has(message.from) && !isReadingControl(message.payload, state.appId) && message.from !== state.profileId && message.recipients.includes(state.profileId)
|
|
180
|
+
const messages = thread.messages.filter(message => !message.recalled && !hidden?.get(message.id)?.has(message.from) && !(message.readingControl || isReadingControl(message.payload, state.appId)) && message.from !== state.profileId && message.recipients.includes(state.profileId)
|
|
163
181
|
&& (message.readPolicy === "explicit" ? !explicit.has(message.id) : message.seq > thread.readThroughSeq));
|
|
164
182
|
const unreadMessageCount = messages.length;
|
|
165
183
|
const unreadMentionCount = messages.filter(message => message.mentions.includes(state.profileId)).length;
|
|
@@ -189,6 +207,7 @@ export function removeReadTeam(state, teamId, knownThreadIds = []) {
|
|
|
189
207
|
export class IndexedDbReadIndex {
|
|
190
208
|
storageScope;
|
|
191
209
|
migrateLegacy;
|
|
210
|
+
metadataOnly;
|
|
192
211
|
database;
|
|
193
212
|
profiles;
|
|
194
213
|
threadRows;
|
|
@@ -197,9 +216,10 @@ export class IndexedDbReadIndex {
|
|
|
197
216
|
normalizedStates = new WeakSet();
|
|
198
217
|
versions;
|
|
199
218
|
closed = false;
|
|
200
|
-
constructor(factory = globalThis.indexedDB, name = "pmate-social-read-index", storageScope, migrateLegacy = false) {
|
|
219
|
+
constructor(factory = globalThis.indexedDB, name = "pmate-social-read-index", storageScope, migrateLegacy = false, metadataOnly = false) {
|
|
201
220
|
this.storageScope = storageScope;
|
|
202
221
|
this.migrateLegacy = migrateLegacy;
|
|
222
|
+
this.metadataOnly = metadataOnly;
|
|
203
223
|
if (!factory)
|
|
204
224
|
throw new Error("READ_STORAGE_UNAVAILABLE");
|
|
205
225
|
this.database = new Dexie(name, { indexedDB: factory, IDBKeyRange: globalThis.IDBKeyRange });
|
|
@@ -274,13 +294,23 @@ export class IndexedDbReadIndex {
|
|
|
274
294
|
return current;
|
|
275
295
|
}
|
|
276
296
|
async loadCurrent(appId, profileId) {
|
|
297
|
+
const current = await this.loadStored(appId, profileId);
|
|
298
|
+
if (!current || !this.metadataOnly)
|
|
299
|
+
return current;
|
|
300
|
+
const metadata = readingMetadata(current);
|
|
301
|
+
if (metadata !== current)
|
|
302
|
+
await this.persist(appId, profileId, current, metadata);
|
|
303
|
+
return metadata;
|
|
304
|
+
}
|
|
305
|
+
async loadStored(appId, profileId) {
|
|
277
306
|
const current = await this.profiles.get(this.key(appId, profileId));
|
|
278
307
|
if (current?.normalized)
|
|
279
308
|
return this.hydrate(current, this.key(appId, profileId));
|
|
280
309
|
if (current) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
310
|
+
const next = this.metadataOnly ? readingMetadata(current) : current;
|
|
311
|
+
if (this.migrateLegacy || this.metadataOnly)
|
|
312
|
+
await this.persist(appId, profileId, undefined, next, true);
|
|
313
|
+
return next;
|
|
284
314
|
}
|
|
285
315
|
if (!this.migrateLegacy || this.storageScope === undefined)
|
|
286
316
|
return undefined;
|
|
@@ -291,7 +321,8 @@ export class IndexedDbReadIndex {
|
|
|
291
321
|
const legacyRow = await this.profiles.get([appId, profileId]);
|
|
292
322
|
if (!legacyRow)
|
|
293
323
|
return undefined;
|
|
294
|
-
const
|
|
324
|
+
const hydrated = await this.hydrate(legacyRow, [appId, profileId]);
|
|
325
|
+
const legacy = this.metadataOnly ? readingMetadata(hydrated) : hydrated;
|
|
295
326
|
if (legacy.appId !== appId || legacy.profileId !== profileId || legacy.schemaVersion !== 1)
|
|
296
327
|
throw new Error("READ_IDENTITY_MISMATCH");
|
|
297
328
|
const ownership = { ...createReadIndex(appId, profileId), legacyOwner: this.storageScope };
|
|
@@ -380,10 +411,10 @@ export class IndexedDbReadIndex {
|
|
|
380
411
|
this.normalizedStates.add(next);
|
|
381
412
|
}
|
|
382
413
|
async load(appId, profileId) {
|
|
383
|
-
return this.transaction(this.migrateLegacy ? "rw" : "r", () => this.loadCurrent(appId, profileId));
|
|
414
|
+
return this.transaction(this.migrateLegacy || this.metadataOnly ? "rw" : "r", () => this.loadCurrent(appId, profileId));
|
|
384
415
|
}
|
|
385
416
|
async loadVersioned(appId, profileId) {
|
|
386
|
-
return this.transaction(this.migrateLegacy ? "rw" : "r", async () => ({
|
|
417
|
+
return this.transaction(this.migrateLegacy || this.metadataOnly ? "rw" : "r", async () => ({
|
|
387
418
|
state: await this.loadCurrent(appId, profileId), versions: (await this.versions.get(this.key(appId, profileId)))?.teams ?? {},
|
|
388
419
|
}));
|
|
389
420
|
}
|
|
@@ -407,7 +438,8 @@ export class IndexedDbReadIndex {
|
|
|
407
438
|
async update(appId, profileId, transform) {
|
|
408
439
|
return this.transaction("rw", async () => {
|
|
409
440
|
const previous = await this.loadCurrent(appId, profileId);
|
|
410
|
-
const
|
|
441
|
+
const transformed = transform(previous ?? createReadIndex(appId, profileId), (await this.versions.get(this.key(appId, profileId)))?.teams ?? {});
|
|
442
|
+
const next = this.metadataOnly ? readingMetadata(transformed) : transformed;
|
|
411
443
|
if (next.appId !== appId || next.profileId !== profileId || next.schemaVersion !== 1)
|
|
412
444
|
throw new Error("READ_IDENTITY_MISMATCH");
|
|
413
445
|
await this.persist(appId, profileId, previous, next);
|
package/lib/runtime/readSync.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertReadIndexVersions, applyReadChange, ReadSyncGap, createReadIndex, installReadSnapshot } from "./readIndex.js";
|
|
1
|
+
import { readingMetadata, assertReadIndexVersions, applyReadChange, ReadSyncGap, createReadIndex, installReadSnapshot } from "./readIndex.js";
|
|
2
2
|
/** Keeps local operations until an authoritative projection covers them. */
|
|
3
3
|
export class ReadSyncClient {
|
|
4
4
|
options;
|
|
@@ -9,8 +9,11 @@ export class ReadSyncClient {
|
|
|
9
9
|
this.options = options;
|
|
10
10
|
}
|
|
11
11
|
async loadLocal() {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const { appId, profileId, persistence, metadataOnly } = this.options;
|
|
13
|
+
const state = await persistence.load(appId, profileId) ?? createReadIndex(appId, profileId);
|
|
14
|
+
if (!metadataOnly || readingMetadata(state) === state)
|
|
15
|
+
return state;
|
|
16
|
+
return persistence.update(appId, profileId, readingMetadata);
|
|
14
17
|
}
|
|
15
18
|
stop() { this.controller.abort(); }
|
|
16
19
|
synchronize() {
|
|
@@ -84,7 +87,7 @@ export class ReadSyncClient {
|
|
|
84
87
|
throw new Error("READ_SYNC_STOPPED");
|
|
85
88
|
const next = await persistence.update(appId, profileId, (state, currentVersions) => {
|
|
86
89
|
assertReadIndexVersions(versions, currentVersions);
|
|
87
|
-
const applied = delta.changes.reduce(applyReadChange, state);
|
|
90
|
+
const applied = delta.changes.reduce((current, change) => applyReadChange(current, this.options.metadataOnly ? { ...change, thread: readingMetadata({ appId, threads: [change.thread] }).threads[0] } : change), state);
|
|
88
91
|
if (applied.syncCursor !== delta.syncCursor)
|
|
89
92
|
throw new ReadSyncGap(applied.syncCursor);
|
|
90
93
|
return applied;
|
|
@@ -107,7 +110,7 @@ export class ReadSyncClient {
|
|
|
107
110
|
// Merge into the latest transaction, including reads made while HTTP was pending.
|
|
108
111
|
const next = await persistence.update(appId, profileId, (state, currentVersions) => {
|
|
109
112
|
assertReadIndexVersions(versions, currentVersions);
|
|
110
|
-
return installReadSnapshot(state, snapshot);
|
|
113
|
+
return installReadSnapshot(state, this.options.metadataOnly ? readingMetadata(snapshot) : snapshot);
|
|
111
114
|
});
|
|
112
115
|
if (!next.pending.length)
|
|
113
116
|
this.readAfter = 0;
|
|
@@ -3,7 +3,7 @@ import { isOffline, isOfflineNetworkFailure } from "./connectivity.js";
|
|
|
3
3
|
import { recordConversationRead } from "./conversationRead.js";
|
|
4
4
|
import { BatchedReadIndex } from "./batchedReadIndex.js";
|
|
5
5
|
import { WindowedTasks } from "./coalescer.js";
|
|
6
|
-
import { IndexedDbReadIndex, recordReadIntent, assertReadIndexVersions } from "./readIndex.js";
|
|
6
|
+
import { IndexedDbReadIndex, readingMetadata, recordReadIntent, assertReadIndexVersions } from "./readIndex.js";
|
|
7
7
|
import { ReadSyncClient } from "./readSync.js";
|
|
8
8
|
/** Owns durable local reads, network coalescing, retries and state notifications.
|
|
9
9
|
* Hosts report actual visibility; loading or subscribing never marks messages read. */
|
|
@@ -30,7 +30,7 @@ export class ReadingClient {
|
|
|
30
30
|
this.options = options;
|
|
31
31
|
if (options.teamId !== undefined && !isValidChatTeamId(options.teamId))
|
|
32
32
|
throw new Error("INVALID_READ_TEAM");
|
|
33
|
-
const storage = options.persistence ?? (this.storage = new IndexedDbReadIndex(globalThis.indexedDB, "pmate-social-read-index", options.storageScope, options.migrateLegacy));
|
|
33
|
+
const storage = options.persistence ?? (this.storage = new IndexedDbReadIndex(globalThis.indexedDB, "pmate-social-read-index", options.storageScope, options.migrateLegacy, options.metadataOnly));
|
|
34
34
|
this.backing = storage;
|
|
35
35
|
this.persistence = new BatchedReadIndex(storage);
|
|
36
36
|
this.tasks = new WindowedTasks(1000, error => this.failed(error));
|
|
@@ -112,7 +112,8 @@ export class ReadingClient {
|
|
|
112
112
|
const expected = await versions;
|
|
113
113
|
const next = await this.persistence.update(this.options.appId, this.options.profileId, (state, currentVersions) => {
|
|
114
114
|
assertReadIndexVersions(expected, currentVersions, teamId);
|
|
115
|
-
|
|
115
|
+
const next = transform(state);
|
|
116
|
+
return this.options.metadataOnly ? readingMetadata(next) : next;
|
|
116
117
|
});
|
|
117
118
|
if (!this.closed) {
|
|
118
119
|
this.publish(next);
|
package/package.json
CHANGED