@openclaw/matrix 2026.8.1 → 2026.8.2
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/{account-selection-C8nnBTxi.js → account-selection-DA-jfx22.js} +11 -9
- package/dist/{accounts-fC57n-Jo.js → accounts-DR1c4xF_.js} +2 -2
- package/dist/api.js +5 -5
- package/dist/{approval-handler.runtime-BBjFZw1t.js → approval-handler.runtime-xcVrZw9o.js} +5 -5
- package/dist/{approval-ids-DTIdXV-m.js → approval-ids-hmvhDNBj.js} +1 -1
- package/dist/{approval-reaction-auth-BDONXxnF.js → approval-reaction-auth-DziFGHSk.js} +2 -2
- package/dist/{approval-reactions-tXh9KYrW.js → approval-reactions-1fwEhoK9.js} +1 -1
- package/dist/{channel-BkEcVUs_.js → channel-D6-ajB_u.js} +51 -53
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-CTLSBFsP.js → channel.runtime-BMA0dG2k.js} +16 -12
- package/dist/{channel.setup-CSacbORr.js → channel.setup-OIEzCzSs.js} +21 -19
- package/dist/{cli-DRVtgrq5.js → cli-B6HF7Hmn.js} +38 -29
- package/dist/{cli-metadata-Ddx_y4Os.js → cli-metadata-Mtzu5-AY.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{client-CZ_JvrO0.js → client-B0yMDho1.js} +2 -2
- package/dist/{client-kSi51NKp.js → client-DFsRJEgB.js} +69 -16
- package/dist/{client-bootstrap-D8irSL2i.js → client-bootstrap-DjXCKEtg.js} +1 -1
- package/dist/{config-update-DC0fWBxc.js → config-update-BnX26jCO.js} +46 -28
- package/dist/contract-api.js +2 -2
- package/dist/{credentials-BdAaWuiR.js → credentials-DJj2xs3q.js} +4 -8
- package/dist/{credentials-read-B37cQknZ.js → credentials-read-mexhw-Ur.js} +7 -5
- package/dist/{credentials-write.runtime-nAV2-5uR.js → credentials-write.runtime-DQt-kgJq.js} +1 -1
- package/dist/{crypto-runtime-C4WDYxlP.js → crypto-runtime-QvlgyCkj.js} +6 -6
- package/dist/{deps-CcVJfvRM.js → deps-mtxF2mHI.js} +1 -1
- package/dist/{directory-live-DKr0w35b.js → directory-live-Drh343TW.js} +8 -6
- package/dist/{doctor-EWf9CDso.js → doctor-CJE8wO6y.js} +1 -1
- package/dist/{doctor-contract-DEt4O4Zt.js → doctor-contract-HyWFZQVG.js} +5 -4
- package/dist/doctor-contract-api.js +7 -7
- package/dist/{draft-stream-0CEpNrBk.js → draft-stream-DhGHVdO4.js} +1 -1
- package/dist/{encryption-guidance-D4ZdAqzM.js → encryption-guidance-BNcZNQZI.js} +3 -2
- package/dist/helper-api.js +1 -1
- package/dist/{http-client-D8o8b9u6.js → http-client-DOmFvKvB.js} +10 -7
- package/dist/{idb-persistence-DfgW720p.js → idb-persistence-D4NGNQxt.js} +2 -2
- package/dist/{idb-persistence-lock-DAJ49nZX.js → idb-persistence-lock-B3fZXyF_.js} +1 -1
- package/dist/{inbound-dedupe-RvSLqdsz.js → inbound-dedupe-Ch33GU06.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{messages-DaGRt-tu.js → messages-DlMQaC02.js} +2 -2
- package/dist/{monitor-CLPbPcR9.js → monitor-U2LcVtHt.js} +49 -74
- package/dist/plugin-entry.handlers.runtime.js +1 -1
- package/dist/probe.runtime-CNo-5_iz.js +2 -0
- package/dist/{profile-update-zXVCMg7y.js → profile-update-CsyKA9jI.js} +5 -4
- package/dist/{profile-D-Wubo0A.js → profile-vmjsmhh4.js} +1 -1
- package/dist/{reaction-events-CjVUiqcY.js → reaction-events-lwNc5I1X.js} +8 -6
- package/dist/{resolve-targets-qG6gceps.js → resolve-targets-D3Eakgzs.js} +1 -1
- package/dist/{resolver.runtime-zX3TR3TN.js → resolver.runtime-DJQxR35r.js} +1 -1
- package/dist/{room-info-yheP1mod.js → room-info-DIU67XLP.js} +3 -3
- package/dist/runtime-api.js +2 -2
- package/dist/{sdk-TZYyo0XQ.js → sdk-Jl2CgAbJ.js} +518 -23
- package/dist/{send-BL_iH5Ox.js → send-DfS5ciUL.js} +13 -9
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/{setup-bootstrap-D8M7FmcO.js → setup-bootstrap-CWoIWudw.js} +2 -2
- package/dist/{setup-core-r6L0nno-.js → setup-core-CZM496H-.js} +7 -6
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-g174ElFO.js → setup-surface-DSZtOsWK.js} +83 -77
- package/dist/{startup-verification-Cs08ca8u.js → startup-verification-1Ney1mmI.js} +2 -2
- package/dist/{storage-BsGuHngK.js → storage-BVf4uucD.js} +5 -4
- package/dist/{subagent-hooks-CMoZaARG.js → subagent-hooks-DeICLbU5.js} +5 -1
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/sync-cache-state-d-A_OhbU.js +234 -0
- package/dist/{thread-bindings-ERWNousC.js → thread-bindings-2HN2AyLu.js} +2 -2
- package/dist/{tool-actions.runtime-DcBCSwY4.js → tool-actions.runtime-CFZZPG5g.js} +225 -163
- package/dist/{verification-4vsW_WSn.js → verification-CD7k5vE9.js} +2 -2
- package/node_modules/matrix-js-sdk/lib/client.d.ts +2 -1
- package/node_modules/matrix-js-sdk/lib/client.js +6 -2
- package/node_modules/matrix-js-sdk/lib/sync.js +8 -8
- package/node_modules/matrix-js-sdk/src/client.ts +7 -3
- package/node_modules/matrix-js-sdk/src/sync.ts +8 -8
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/package.json +1 -1
- package/package.json +5 -5
- package/dist/file-sync-store-DjskBX2o.js +0 -436
- package/dist/probe.runtime-Bxpr1FFy.js +0 -2
- package/dist/recovery-key-store-CognuDys.js +0 -276
|
@@ -955,7 +955,7 @@ export class SyncApi {
|
|
|
955
955
|
user.setPresenceEvent(presenceEvent);
|
|
956
956
|
client.store.storeUser(user);
|
|
957
957
|
}
|
|
958
|
-
client.emit(ClientEvent.Event, presenceEvent);
|
|
958
|
+
client.emit(ClientEvent.Event, presenceEvent, syncEventData.fromCache);
|
|
959
959
|
});
|
|
960
960
|
}
|
|
961
961
|
|
|
@@ -1065,7 +1065,7 @@ export class SyncApi {
|
|
|
1065
1065
|
room.recalculate();
|
|
1066
1066
|
}
|
|
1067
1067
|
stateEvents.forEach(function (e) {
|
|
1068
|
-
client.emit(ClientEvent.Event, e);
|
|
1068
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1069
1069
|
});
|
|
1070
1070
|
}));
|
|
1071
1071
|
|
|
@@ -1243,7 +1243,7 @@ export class SyncApi {
|
|
|
1243
1243
|
client.emit(ClientEvent.Room, room);
|
|
1244
1244
|
}
|
|
1245
1245
|
this.processEventsForNotifs(room, timelineEvents);
|
|
1246
|
-
const emitEvent = e => client.emit(ClientEvent.Event, e);
|
|
1246
|
+
const emitEvent = e => client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1247
1247
|
// this fires a couple of times for some events. (eg state events are in the timeline and the state)
|
|
1248
1248
|
// should this get a sync section as an additional event emission param (e, syncSection))?
|
|
1249
1249
|
stateEvents.forEach(emitEvent);
|
|
@@ -1278,16 +1278,16 @@ export class SyncApi {
|
|
|
1278
1278
|
}
|
|
1279
1279
|
this.processEventsForNotifs(room, timelineEvents);
|
|
1280
1280
|
stateEvents?.forEach(function (e) {
|
|
1281
|
-
client.emit(ClientEvent.Event, e);
|
|
1281
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1282
1282
|
});
|
|
1283
1283
|
stateAfterEvents?.forEach(function (e) {
|
|
1284
|
-
client.emit(ClientEvent.Event, e);
|
|
1284
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1285
1285
|
});
|
|
1286
1286
|
timelineEvents.forEach(function (e) {
|
|
1287
|
-
client.emit(ClientEvent.Event, e);
|
|
1287
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1288
1288
|
});
|
|
1289
1289
|
accountDataEvents.forEach(function (e) {
|
|
1290
|
-
client.emit(ClientEvent.Event, e);
|
|
1290
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1291
1291
|
});
|
|
1292
1292
|
}));
|
|
1293
1293
|
|
|
@@ -1305,7 +1305,7 @@ export class SyncApi {
|
|
|
1305
1305
|
room.recalculate();
|
|
1306
1306
|
}
|
|
1307
1307
|
stateEvents.forEach(function (e) {
|
|
1308
|
-
client.emit(ClientEvent.Event, e);
|
|
1308
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1309
1309
|
});
|
|
1310
1310
|
}));
|
|
1311
1311
|
|
|
@@ -1017,6 +1017,7 @@ export enum ClientEvent {
|
|
|
1017
1017
|
* events received over context, search, or pagination APIs.
|
|
1018
1018
|
*
|
|
1019
1019
|
* The payload is the matrix event which caused this event to fire.
|
|
1020
|
+
* The optional second argument is true when replaying a saved sync response.
|
|
1020
1021
|
* @example
|
|
1021
1022
|
* ```
|
|
1022
1023
|
* matrixClient.on("event", function(event){
|
|
@@ -1158,7 +1159,7 @@ export type EmittedEvents =
|
|
|
1158
1159
|
|
|
1159
1160
|
export type ClientEventHandlerMap = {
|
|
1160
1161
|
[ClientEvent.Sync]: (state: SyncState, prevState: SyncState | null, data?: ISyncStateData) => void;
|
|
1161
|
-
[ClientEvent.Event]: (event: MatrixEvent) => void;
|
|
1162
|
+
[ClientEvent.Event]: (event: MatrixEvent, fromCache?: boolean) => void;
|
|
1162
1163
|
[ClientEvent.ToDeviceEvent]: (event: MatrixEvent) => void;
|
|
1163
1164
|
[ClientEvent.ReceivedToDeviceMessage]: (payload: ReceivedToDeviceMessage) => void;
|
|
1164
1165
|
[ClientEvent.AccountData]: (event: MatrixEvent, lastEvent?: MatrixEvent) => void;
|
|
@@ -2049,8 +2050,11 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|
|
2049
2050
|
// attach the event listeners needed by RustCrypto
|
|
2050
2051
|
this.on(RoomMemberEvent.Membership, rustCrypto.onRoomMembership.bind(rustCrypto));
|
|
2051
2052
|
this.on(RoomStateEvent.Events, rustCrypto.onRoomStateEvent.bind(rustCrypto));
|
|
2052
|
-
this.on(ClientEvent.Event, (event) => {
|
|
2053
|
-
|
|
2053
|
+
this.on(ClientEvent.Event, (event, fromCache) => {
|
|
2054
|
+
// Restored room history must not restart completed verification requests.
|
|
2055
|
+
if (!fromCache) {
|
|
2056
|
+
void rustCrypto.onLiveEventFromSync(event);
|
|
2057
|
+
}
|
|
2054
2058
|
});
|
|
2055
2059
|
|
|
2056
2060
|
// re-emit the events emitted by the crypto impl
|
|
@@ -1131,7 +1131,7 @@ export class SyncApi {
|
|
|
1131
1131
|
user.setPresenceEvent(presenceEvent);
|
|
1132
1132
|
client.store.storeUser(user);
|
|
1133
1133
|
}
|
|
1134
|
-
client.emit(ClientEvent.Event, presenceEvent);
|
|
1134
|
+
client.emit(ClientEvent.Event, presenceEvent, syncEventData.fromCache);
|
|
1135
1135
|
});
|
|
1136
1136
|
}
|
|
1137
1137
|
|
|
@@ -1249,7 +1249,7 @@ export class SyncApi {
|
|
|
1249
1249
|
room.recalculate();
|
|
1250
1250
|
}
|
|
1251
1251
|
stateEvents.forEach(function (e) {
|
|
1252
|
-
client.emit(ClientEvent.Event, e);
|
|
1252
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1253
1253
|
});
|
|
1254
1254
|
}),
|
|
1255
1255
|
);
|
|
@@ -1477,7 +1477,7 @@ export class SyncApi {
|
|
|
1477
1477
|
|
|
1478
1478
|
this.processEventsForNotifs(room, timelineEvents);
|
|
1479
1479
|
|
|
1480
|
-
const emitEvent = (e: MatrixEvent): boolean => client.emit(ClientEvent.Event, e);
|
|
1480
|
+
const emitEvent = (e: MatrixEvent): boolean => client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1481
1481
|
// this fires a couple of times for some events. (eg state events are in the timeline and the state)
|
|
1482
1482
|
// should this get a sync section as an additional event emission param (e, syncSection))?
|
|
1483
1483
|
stateEvents.forEach(emitEvent);
|
|
@@ -1518,16 +1518,16 @@ export class SyncApi {
|
|
|
1518
1518
|
this.processEventsForNotifs(room, timelineEvents);
|
|
1519
1519
|
|
|
1520
1520
|
stateEvents?.forEach(function (e) {
|
|
1521
|
-
client.emit(ClientEvent.Event, e);
|
|
1521
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1522
1522
|
});
|
|
1523
1523
|
stateAfterEvents?.forEach(function (e) {
|
|
1524
|
-
client.emit(ClientEvent.Event, e);
|
|
1524
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1525
1525
|
});
|
|
1526
1526
|
timelineEvents.forEach(function (e) {
|
|
1527
|
-
client.emit(ClientEvent.Event, e);
|
|
1527
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1528
1528
|
});
|
|
1529
1529
|
accountDataEvents.forEach(function (e) {
|
|
1530
|
-
client.emit(ClientEvent.Event, e);
|
|
1530
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1531
1531
|
});
|
|
1532
1532
|
}),
|
|
1533
1533
|
);
|
|
@@ -1549,7 +1549,7 @@ export class SyncApi {
|
|
|
1549
1549
|
room.recalculate();
|
|
1550
1550
|
}
|
|
1551
1551
|
stateEvents.forEach(function (e) {
|
|
1552
|
-
client.emit(ClientEvent.Event, e);
|
|
1552
|
+
client.emit(ClientEvent.Event, e, syncEventData.fromCache);
|
|
1553
1553
|
});
|
|
1554
1554
|
}),
|
|
1555
1555
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]
|
|
1
|
+
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\[\x20-\x7e])*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is an Email
|
|
4
4
|
* @specification https://datatracker.ietf.org/doc/html/rfc5322
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as FormatBidi from './format/bidi.mjs';
|
|
2
2
|
import * as LabelUnicode from './label/unicode.mjs';
|
|
3
3
|
import * as LabelPuny from './label/puny.mjs';
|
|
4
|
+
// ------------------------------------------------------------------
|
|
5
|
+
// IsLabel
|
|
6
|
+
// ------------------------------------------------------------------
|
|
4
7
|
function IsValidLabelLength(value) {
|
|
5
8
|
return value.length > 0 && value.length <= 63;
|
|
6
9
|
}
|
|
@@ -8,9 +11,24 @@ function IsLabel(value) {
|
|
|
8
11
|
return IsValidLabelLength(value) && (LabelPuny.IsPunyLabel(value) ||
|
|
9
12
|
LabelUnicode.IsUnicodeLabel(value));
|
|
10
13
|
}
|
|
14
|
+
// ------------------------------------------------------------------
|
|
15
|
+
// NormalizeHostname
|
|
16
|
+
//
|
|
17
|
+
// Normalizes a hostname for label validation. Normalizes for NFC
|
|
18
|
+
// such that equivalent codepoint sequences can be uniformly
|
|
19
|
+
// compared. It also removes codepoints that IDNA mapping ignores,
|
|
20
|
+
// and converts full stop variants to ASCII '.' so the hostname can
|
|
21
|
+
// be split into labels.
|
|
22
|
+
// ------------------------------------------------------------------
|
|
11
23
|
function NormalizeHostname(value) {
|
|
12
|
-
return value
|
|
24
|
+
return value
|
|
25
|
+
.normalize('NFC')
|
|
26
|
+
.replace(/[\u00ad\u034f\u180b-\u180d\u200b\ufe00-\ufe0f\u{e0100}-\u{e01ef}]/gu, '') // RE_IGNORED_MAPPING
|
|
27
|
+
.replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.'); // RE_FULL_STOP_MAPPING
|
|
13
28
|
}
|
|
29
|
+
// ------------------------------------------------------------------
|
|
30
|
+
// IsIdnHostname
|
|
31
|
+
// ------------------------------------------------------------------
|
|
14
32
|
export function IsIdnHostname(value) {
|
|
15
33
|
if (value.length === 0 || value.includes(' '))
|
|
16
34
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/matrix",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.2",
|
|
4
4
|
"description": "OpenClaw Matrix channel plugin for rooms and direct messages.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
"markdown-it": "15.0.0",
|
|
15
15
|
"matrix-js-sdk": "42.2.0",
|
|
16
16
|
"music-metadata": "11.15.0",
|
|
17
|
-
"typebox": "1.3.
|
|
17
|
+
"typebox": "1.3.17",
|
|
18
18
|
"zod": "4.4.3"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"openclaw": ">=2026.8.
|
|
21
|
+
"openclaw": ">=2026.8.2"
|
|
22
22
|
},
|
|
23
23
|
"peerDependenciesMeta": {
|
|
24
24
|
"openclaw": {
|
|
@@ -167,10 +167,10 @@
|
|
|
167
167
|
"allowInvalidConfigRecovery": true
|
|
168
168
|
},
|
|
169
169
|
"compat": {
|
|
170
|
-
"pluginApi": ">=2026.8.
|
|
170
|
+
"pluginApi": ">=2026.8.2"
|
|
171
171
|
},
|
|
172
172
|
"build": {
|
|
173
|
-
"openclawVersion": "2026.8.
|
|
173
|
+
"openclawVersion": "2026.8.2"
|
|
174
174
|
},
|
|
175
175
|
"release": {
|
|
176
176
|
"publishToClawHub": true,
|
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { t as getMatrixRuntime } from "./runtime-6S3DNFNv.js";
|
|
3
|
-
import { M as resolveMatrixSqliteStateEnv, t as claimCurrentTokenStorageState } from "./storage-BsGuHngK.js";
|
|
4
|
-
import { n as LogService } from "./logger-B_71hN6W.js";
|
|
5
|
-
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import fs from "node:fs/promises";
|
|
9
|
-
import { Category, MemoryStore, SyncAccumulator } from "matrix-js-sdk/lib/matrix.js";
|
|
10
|
-
//#region extensions/matrix/src/matrix/async-lock.ts
|
|
11
|
-
function createAsyncLock() {
|
|
12
|
-
let lock = Promise.resolve();
|
|
13
|
-
return async function withLock(fn) {
|
|
14
|
-
const previous = lock;
|
|
15
|
-
let release;
|
|
16
|
-
lock = new Promise((resolve) => {
|
|
17
|
-
release = resolve;
|
|
18
|
-
});
|
|
19
|
-
await previous;
|
|
20
|
-
try {
|
|
21
|
-
return await fn();
|
|
22
|
-
} finally {
|
|
23
|
-
release?.();
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region extensions/matrix/src/matrix/client/file-sync-store.ts
|
|
29
|
-
var file_sync_store_exports = /* @__PURE__ */ __exportAll({
|
|
30
|
-
SqliteBackedMatrixSyncStore: () => SqliteBackedMatrixSyncStore,
|
|
31
|
-
hasMatrixSyncCacheStateInStore: () => hasMatrixSyncCacheStateInStore,
|
|
32
|
-
openMatrixSyncCacheStoreOptions: () => openMatrixSyncCacheStoreOptions,
|
|
33
|
-
readLegacyMatrixSyncCacheState: () => readLegacyMatrixSyncCacheState,
|
|
34
|
-
writeMatrixSyncCacheStateToStore: () => writeMatrixSyncCacheStateToStore
|
|
35
|
-
});
|
|
36
|
-
const STORE_VERSION = 1;
|
|
37
|
-
const PERSIST_DEBOUNCE_MS = 250;
|
|
38
|
-
const SYNC_CACHE_NAMESPACE = "sync-cache";
|
|
39
|
-
const SYNC_CACHE_MAX_ENTRIES = 2e4;
|
|
40
|
-
const SYNC_CACHE_MAX_CHUNKS = Math.floor((SYNC_CACHE_MAX_ENTRIES - 1) / 2);
|
|
41
|
-
const SYNC_CACHE_STATE_KEY = "current";
|
|
42
|
-
const SYNC_CACHE_CHUNK_BYTES = 24e3;
|
|
43
|
-
function normalizeRoomsData(value) {
|
|
44
|
-
if (!isRecord(value)) return null;
|
|
45
|
-
return {
|
|
46
|
-
[Category.Join]: isRecord(value[Category.Join]) ? value[Category.Join] : {},
|
|
47
|
-
[Category.Invite]: isRecord(value[Category.Invite]) ? value[Category.Invite] : {},
|
|
48
|
-
[Category.Leave]: isRecord(value[Category.Leave]) ? value[Category.Leave] : {},
|
|
49
|
-
[Category.Knock]: isRecord(value[Category.Knock]) ? value[Category.Knock] : {}
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
function toPersistedSyncData(value) {
|
|
53
|
-
if (!isRecord(value)) return null;
|
|
54
|
-
if (typeof value.nextBatch === "string" && value.nextBatch.trim()) {
|
|
55
|
-
const roomsData = normalizeRoomsData(value.roomsData);
|
|
56
|
-
if (!Array.isArray(value.accountData) || !roomsData) return null;
|
|
57
|
-
return {
|
|
58
|
-
nextBatch: value.nextBatch,
|
|
59
|
-
accountData: value.accountData,
|
|
60
|
-
roomsData
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
if (typeof value.next_batch === "string" && value.next_batch.trim()) {
|
|
64
|
-
const roomsData = normalizeRoomsData(value.rooms);
|
|
65
|
-
if (!roomsData) return null;
|
|
66
|
-
return {
|
|
67
|
-
nextBatch: value.next_batch,
|
|
68
|
-
accountData: isRecord(value.account_data) && Array.isArray(value.account_data.events) ? value.account_data.events : [],
|
|
69
|
-
roomsData
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
function normalizePersistedStore(value) {
|
|
75
|
-
if (!isRecord(value) || value.version !== STORE_VERSION) return null;
|
|
76
|
-
return {
|
|
77
|
-
version: STORE_VERSION,
|
|
78
|
-
savedSync: toPersistedSyncData(value.savedSync),
|
|
79
|
-
clientOptions: isRecord(value.clientOptions) ? value.clientOptions : void 0,
|
|
80
|
-
cleanShutdown: value.cleanShutdown === true
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function normalizeLegacyPersistedStore(value) {
|
|
84
|
-
const persisted = normalizePersistedStore(value);
|
|
85
|
-
if (persisted) return persisted;
|
|
86
|
-
return {
|
|
87
|
-
version: STORE_VERSION,
|
|
88
|
-
savedSync: toPersistedSyncData(value),
|
|
89
|
-
cleanShutdown: false
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function cloneJson(value) {
|
|
93
|
-
return structuredClone(value);
|
|
94
|
-
}
|
|
95
|
-
function syncDataToSyncResponse(syncData) {
|
|
96
|
-
return {
|
|
97
|
-
next_batch: syncData.nextBatch,
|
|
98
|
-
rooms: syncData.roomsData,
|
|
99
|
-
account_data: { events: syncData.accountData }
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
var SqliteBackedMatrixSyncStore = class extends MemoryStore {
|
|
103
|
-
constructor(storageRootDir) {
|
|
104
|
-
super();
|
|
105
|
-
this.storageRootDir = storageRootDir;
|
|
106
|
-
this.persistLock = createAsyncLock();
|
|
107
|
-
this.accumulator = new SyncAccumulator();
|
|
108
|
-
this.savedSync = null;
|
|
109
|
-
this.cleanShutdown = false;
|
|
110
|
-
this.dirty = false;
|
|
111
|
-
this.frozen = false;
|
|
112
|
-
this.persistTimer = null;
|
|
113
|
-
this.persistPromise = null;
|
|
114
|
-
this.stateKey = SYNC_CACHE_STATE_KEY;
|
|
115
|
-
let restoredSavedSync = null;
|
|
116
|
-
let restoredClientOptions;
|
|
117
|
-
let restoredCleanShutdown = false;
|
|
118
|
-
let syncCacheStore = createNoopMatrixSyncCacheStore();
|
|
119
|
-
let syncCacheStoreUnavailableError;
|
|
120
|
-
try {
|
|
121
|
-
syncCacheStore = openMatrixSyncCacheStore(storageRootDir);
|
|
122
|
-
const persisted = readPersistedStoreFromSyncStore(syncCacheStore, this.stateKey);
|
|
123
|
-
if (persisted) {
|
|
124
|
-
restoredSavedSync = persisted.savedSync;
|
|
125
|
-
restoredClientOptions = persisted.clientOptions;
|
|
126
|
-
restoredCleanShutdown = persisted.cleanShutdown === true;
|
|
127
|
-
}
|
|
128
|
-
} catch (err) {
|
|
129
|
-
syncCacheStoreUnavailableError = err;
|
|
130
|
-
LogService.warn("MatrixSyncCacheStore", "Failed to load Matrix sync cache:", err);
|
|
131
|
-
}
|
|
132
|
-
this.store = syncCacheStore;
|
|
133
|
-
this.storeUnavailableError = syncCacheStoreUnavailableError;
|
|
134
|
-
this.savedSync = restoredSavedSync;
|
|
135
|
-
this.savedClientOptions = restoredClientOptions;
|
|
136
|
-
this.hadSavedSyncOnLoad = restoredSavedSync !== null;
|
|
137
|
-
this.hadCleanShutdownOnLoad = this.hadSavedSyncOnLoad && restoredCleanShutdown;
|
|
138
|
-
this.cleanShutdown = this.hadCleanShutdownOnLoad;
|
|
139
|
-
if (this.savedSync) {
|
|
140
|
-
this.accumulator.accumulate(syncDataToSyncResponse(this.savedSync), true);
|
|
141
|
-
super.setSyncToken(this.savedSync.nextBatch);
|
|
142
|
-
}
|
|
143
|
-
if (this.savedClientOptions) super.storeClientOptions(this.savedClientOptions);
|
|
144
|
-
}
|
|
145
|
-
hasSavedSync() {
|
|
146
|
-
return this.hadSavedSyncOnLoad;
|
|
147
|
-
}
|
|
148
|
-
hasSavedSyncFromCleanShutdown() {
|
|
149
|
-
return this.hadCleanShutdownOnLoad;
|
|
150
|
-
}
|
|
151
|
-
getSavedSync() {
|
|
152
|
-
return Promise.resolve(this.savedSync ? cloneJson(this.savedSync) : null);
|
|
153
|
-
}
|
|
154
|
-
getSavedSyncToken() {
|
|
155
|
-
return Promise.resolve(this.savedSync?.nextBatch ?? null);
|
|
156
|
-
}
|
|
157
|
-
setSyncData(syncData) {
|
|
158
|
-
if (this.frozen) return Promise.resolve();
|
|
159
|
-
this.accumulator.accumulate(syncData);
|
|
160
|
-
this.savedSync = this.accumulator.getJSON();
|
|
161
|
-
this.markDirtyAndSchedulePersist();
|
|
162
|
-
return Promise.resolve();
|
|
163
|
-
}
|
|
164
|
-
getClientOptions() {
|
|
165
|
-
return Promise.resolve(this.savedClientOptions ? cloneJson(this.savedClientOptions) : void 0);
|
|
166
|
-
}
|
|
167
|
-
storeClientOptions(options) {
|
|
168
|
-
if (this.frozen) return Promise.resolve();
|
|
169
|
-
this.savedClientOptions = cloneJson(options);
|
|
170
|
-
super.storeClientOptions(options);
|
|
171
|
-
this.markDirtyAndSchedulePersist();
|
|
172
|
-
return Promise.resolve();
|
|
173
|
-
}
|
|
174
|
-
save(force = false) {
|
|
175
|
-
if (force) return this.flush();
|
|
176
|
-
return Promise.resolve();
|
|
177
|
-
}
|
|
178
|
-
wantsSave() {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
async deleteAllData() {
|
|
182
|
-
this.assertStoreAvailable();
|
|
183
|
-
if (this.persistTimer) {
|
|
184
|
-
clearTimeout(this.persistTimer);
|
|
185
|
-
this.persistTimer = null;
|
|
186
|
-
}
|
|
187
|
-
this.dirty = false;
|
|
188
|
-
await this.persistPromise?.catch(() => void 0);
|
|
189
|
-
await super.deleteAllData();
|
|
190
|
-
this.savedSync = null;
|
|
191
|
-
this.savedClientOptions = void 0;
|
|
192
|
-
this.cleanShutdown = false;
|
|
193
|
-
this.store.delete(metaKey(this.stateKey));
|
|
194
|
-
for (const row of this.store.entries()) if (row.key.startsWith(chunkKeyPrefix(this.stateKey))) this.store.delete(row.key);
|
|
195
|
-
await fs.rm(resolveLegacySyncCachePath(this.storageRootDir), { force: true }).catch(() => void 0);
|
|
196
|
-
}
|
|
197
|
-
markCleanShutdown() {
|
|
198
|
-
this.cleanShutdown = true;
|
|
199
|
-
this.dirty = true;
|
|
200
|
-
}
|
|
201
|
-
async freezeSyncCursorPersistence() {
|
|
202
|
-
this.frozen = true;
|
|
203
|
-
if (this.persistTimer) {
|
|
204
|
-
clearTimeout(this.persistTimer);
|
|
205
|
-
this.persistTimer = null;
|
|
206
|
-
}
|
|
207
|
-
await this.persistPromise;
|
|
208
|
-
}
|
|
209
|
-
discardPendingSyncCursorPersistence() {
|
|
210
|
-
this.frozen = true;
|
|
211
|
-
if (this.persistTimer) {
|
|
212
|
-
clearTimeout(this.persistTimer);
|
|
213
|
-
this.persistTimer = null;
|
|
214
|
-
}
|
|
215
|
-
this.cleanShutdown = false;
|
|
216
|
-
this.dirty = false;
|
|
217
|
-
}
|
|
218
|
-
async flush() {
|
|
219
|
-
if (this.persistTimer) {
|
|
220
|
-
clearTimeout(this.persistTimer);
|
|
221
|
-
this.persistTimer = null;
|
|
222
|
-
}
|
|
223
|
-
while (this.dirty || this.persistPromise) {
|
|
224
|
-
if (this.dirty && !this.persistPromise) this.persistPromise = this.persist().finally(() => {
|
|
225
|
-
this.persistPromise = null;
|
|
226
|
-
});
|
|
227
|
-
await this.persistPromise;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
markDirtyAndSchedulePersist() {
|
|
231
|
-
if (this.frozen) return;
|
|
232
|
-
this.cleanShutdown = false;
|
|
233
|
-
this.dirty = true;
|
|
234
|
-
if (this.persistTimer) return;
|
|
235
|
-
this.persistTimer = setTimeout(() => {
|
|
236
|
-
this.persistTimer = null;
|
|
237
|
-
this.flush().catch((err) => {
|
|
238
|
-
LogService.warn("MatrixSyncCacheStore", "Failed to persist Matrix sync store:", err);
|
|
239
|
-
});
|
|
240
|
-
}, PERSIST_DEBOUNCE_MS);
|
|
241
|
-
this.persistTimer.unref?.();
|
|
242
|
-
}
|
|
243
|
-
async persist() {
|
|
244
|
-
this.assertStoreAvailable();
|
|
245
|
-
this.dirty = false;
|
|
246
|
-
const payload = {
|
|
247
|
-
version: STORE_VERSION,
|
|
248
|
-
savedSync: this.savedSync ? cloneJson(this.savedSync) : null,
|
|
249
|
-
cleanShutdown: this.cleanShutdown,
|
|
250
|
-
...this.savedClientOptions ? { clientOptions: cloneJson(this.savedClientOptions) } : {}
|
|
251
|
-
};
|
|
252
|
-
try {
|
|
253
|
-
await this.persistLock(async () => {
|
|
254
|
-
this.writePersistedStore(payload);
|
|
255
|
-
claimCurrentTokenStorageState({ rootDir: this.storageRootDir });
|
|
256
|
-
});
|
|
257
|
-
} catch (err) {
|
|
258
|
-
this.dirty = true;
|
|
259
|
-
throw err;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
writePersistedStore(payload) {
|
|
263
|
-
const rows = buildSyncCacheRows(this.stateKey, payload);
|
|
264
|
-
for (const row of rows.chunks) this.store.register(row.key, row.value);
|
|
265
|
-
this.store.register(rows.meta.key, rows.meta.value);
|
|
266
|
-
for (const row of this.store.entries()) if (row.key.startsWith(chunkKeyPrefix(this.stateKey)) && !rows.nextChunkKeys.has(row.key)) this.store.delete(row.key);
|
|
267
|
-
}
|
|
268
|
-
assertStoreAvailable() {
|
|
269
|
-
if (this.storeUnavailableError == null) return;
|
|
270
|
-
throw new Error("Matrix sync cache SQLite store is unavailable; cannot persist sync state", { cause: this.storeUnavailableError });
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
function createNoopMatrixSyncCacheStore() {
|
|
274
|
-
return {
|
|
275
|
-
register: () => {},
|
|
276
|
-
registerIfAbsent: () => false,
|
|
277
|
-
lookup: () => void 0,
|
|
278
|
-
consume: () => void 0,
|
|
279
|
-
delete: () => false,
|
|
280
|
-
entries: () => [],
|
|
281
|
-
clear: () => {}
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
function readPersistedStoreFromSyncStore(store, stateKey) {
|
|
285
|
-
const meta = store.lookup(metaKey(stateKey));
|
|
286
|
-
if (!isSyncCacheMeta(meta)) return null;
|
|
287
|
-
const chunks = [];
|
|
288
|
-
for (let index = 0; index < meta.chunkCount; index += 1) {
|
|
289
|
-
const chunk = store.lookup(chunkKey(stateKey, meta.generation, index));
|
|
290
|
-
if (!isSyncCacheChunk(chunk) || chunk.index !== index) return normalizePersistedStore({
|
|
291
|
-
version: STORE_VERSION,
|
|
292
|
-
savedSync: null,
|
|
293
|
-
clientOptions: meta.clientOptions,
|
|
294
|
-
cleanShutdown: false
|
|
295
|
-
});
|
|
296
|
-
chunks.push(chunk.data);
|
|
297
|
-
}
|
|
298
|
-
let savedSync = null;
|
|
299
|
-
if (chunks.length > 0) {
|
|
300
|
-
const syncJson = chunks.join("");
|
|
301
|
-
if (meta.syncDigest !== digestText(syncJson)) return normalizePersistedStore({
|
|
302
|
-
version: STORE_VERSION,
|
|
303
|
-
savedSync: null,
|
|
304
|
-
clientOptions: meta.clientOptions,
|
|
305
|
-
cleanShutdown: false
|
|
306
|
-
});
|
|
307
|
-
try {
|
|
308
|
-
savedSync = toPersistedSyncData(JSON.parse(syncJson));
|
|
309
|
-
} catch {
|
|
310
|
-
savedSync = null;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return normalizePersistedStore({
|
|
314
|
-
version: STORE_VERSION,
|
|
315
|
-
savedSync,
|
|
316
|
-
clientOptions: meta.clientOptions,
|
|
317
|
-
cleanShutdown: meta.cleanShutdown
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
function openMatrixSyncCacheStore(storageRootDir) {
|
|
321
|
-
return getMatrixRuntime().state.openSyncKeyedStore(openMatrixSyncCacheStoreOptions(storageRootDir));
|
|
322
|
-
}
|
|
323
|
-
function metaKey(stateKey) {
|
|
324
|
-
return `${stateKey}:meta`;
|
|
325
|
-
}
|
|
326
|
-
function chunkKeyPrefix(stateKey) {
|
|
327
|
-
return `${stateKey}:sync:`;
|
|
328
|
-
}
|
|
329
|
-
function chunkKey(stateKey, generation, index) {
|
|
330
|
-
return `${chunkKeyPrefix(stateKey)}${generation}:${index}`;
|
|
331
|
-
}
|
|
332
|
-
function resolveLegacySyncCachePath(storageRootDir) {
|
|
333
|
-
return path.join(storageRootDir, "bot-storage.json");
|
|
334
|
-
}
|
|
335
|
-
function digestText(value) {
|
|
336
|
-
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
337
|
-
}
|
|
338
|
-
function isSyncCacheMeta(value) {
|
|
339
|
-
return isRecord(value) && value.kind === "meta" && value.version === STORE_VERSION && typeof value.generation === "string" && value.generation.trim() !== "" && typeof value.chunkCount === "number" && Number.isSafeInteger(value.chunkCount) && value.chunkCount >= 0 && value.chunkCount <= SYNC_CACHE_MAX_CHUNKS;
|
|
340
|
-
}
|
|
341
|
-
function isSyncCacheChunk(value) {
|
|
342
|
-
return isRecord(value) && value.kind === "sync-chunk" && typeof value.index === "number" && Number.isSafeInteger(value.index) && value.index >= 0 && typeof value.data === "string";
|
|
343
|
-
}
|
|
344
|
-
function chunkSyncCacheJson(value) {
|
|
345
|
-
const chunks = [];
|
|
346
|
-
const pushChunk = (chunk) => {
|
|
347
|
-
if (chunks.length >= SYNC_CACHE_MAX_CHUNKS) throw new Error("Matrix sync cache exceeds SQLite chunk limit");
|
|
348
|
-
chunks.push(chunk);
|
|
349
|
-
};
|
|
350
|
-
let current = "";
|
|
351
|
-
let currentBytes = 0;
|
|
352
|
-
for (const char of value) {
|
|
353
|
-
const charBytes = Buffer.byteLength(char, "utf8");
|
|
354
|
-
if (current && currentBytes + charBytes > SYNC_CACHE_CHUNK_BYTES) {
|
|
355
|
-
pushChunk(current);
|
|
356
|
-
current = "";
|
|
357
|
-
currentBytes = 0;
|
|
358
|
-
}
|
|
359
|
-
current += char;
|
|
360
|
-
currentBytes += charBytes;
|
|
361
|
-
}
|
|
362
|
-
if (current) pushChunk(current);
|
|
363
|
-
return chunks;
|
|
364
|
-
}
|
|
365
|
-
function buildSyncCacheRows(stateKey, payload) {
|
|
366
|
-
const generation = randomUUID().replaceAll("-", "");
|
|
367
|
-
const syncJson = payload.savedSync ? JSON.stringify(payload.savedSync) : "";
|
|
368
|
-
const chunks = (syncJson ? chunkSyncCacheJson(syncJson) : []).map((data, index) => ({
|
|
369
|
-
key: chunkKey(stateKey, generation, index),
|
|
370
|
-
value: {
|
|
371
|
-
kind: "sync-chunk",
|
|
372
|
-
index,
|
|
373
|
-
data
|
|
374
|
-
}
|
|
375
|
-
}));
|
|
376
|
-
return {
|
|
377
|
-
chunks,
|
|
378
|
-
nextChunkKeys: new Set(chunks.map((chunk) => chunk.key)),
|
|
379
|
-
meta: {
|
|
380
|
-
key: metaKey(stateKey),
|
|
381
|
-
value: {
|
|
382
|
-
kind: "meta",
|
|
383
|
-
version: STORE_VERSION,
|
|
384
|
-
generation,
|
|
385
|
-
chunkCount: chunks.length,
|
|
386
|
-
...syncJson ? { syncDigest: digestText(syncJson) } : {},
|
|
387
|
-
...payload.clientOptions ? { clientOptions: payload.clientOptions } : {},
|
|
388
|
-
cleanShutdown: payload.cleanShutdown === true
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
async function readLegacyMatrixSyncCacheState(storageRootDir) {
|
|
394
|
-
try {
|
|
395
|
-
const raw = await fs.readFile(resolveLegacySyncCachePath(storageRootDir), "utf8");
|
|
396
|
-
const persisted = normalizeLegacyPersistedStore(JSON.parse(raw));
|
|
397
|
-
if (!persisted?.savedSync && !persisted?.clientOptions) return null;
|
|
398
|
-
return persisted;
|
|
399
|
-
} catch {
|
|
400
|
-
return null;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
async function hasMatrixSyncCacheStateInStore(params) {
|
|
404
|
-
const stateKey = SYNC_CACHE_STATE_KEY;
|
|
405
|
-
const meta = await params.store.lookup(metaKey(stateKey));
|
|
406
|
-
if (!isSyncCacheMeta(meta) || meta.chunkCount <= 0) return false;
|
|
407
|
-
const chunks = [];
|
|
408
|
-
for (let index = 0; index < meta.chunkCount; index += 1) {
|
|
409
|
-
const chunk = await params.store.lookup(chunkKey(stateKey, meta.generation, index));
|
|
410
|
-
if (!isSyncCacheChunk(chunk) || chunk.index !== index) return false;
|
|
411
|
-
chunks.push(chunk.data);
|
|
412
|
-
}
|
|
413
|
-
const syncJson = chunks.join("");
|
|
414
|
-
if (meta.syncDigest !== digestText(syncJson)) return false;
|
|
415
|
-
try {
|
|
416
|
-
return toPersistedSyncData(JSON.parse(syncJson)) !== null;
|
|
417
|
-
} catch {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
async function writeMatrixSyncCacheStateToStore(params) {
|
|
422
|
-
const stateKey = SYNC_CACHE_STATE_KEY;
|
|
423
|
-
const rows = buildSyncCacheRows(stateKey, params.payload);
|
|
424
|
-
for (const row of rows.chunks) await params.store.register(row.key, row.value);
|
|
425
|
-
await params.store.register(rows.meta.key, rows.meta.value);
|
|
426
|
-
for (const row of await params.store.entries()) if (row.key.startsWith(chunkKeyPrefix(stateKey)) && !rows.nextChunkKeys.has(row.key)) await params.store.delete(row.key);
|
|
427
|
-
}
|
|
428
|
-
function openMatrixSyncCacheStoreOptions(storageRootDir) {
|
|
429
|
-
return {
|
|
430
|
-
namespace: SYNC_CACHE_NAMESPACE,
|
|
431
|
-
maxEntries: SYNC_CACHE_MAX_ENTRIES,
|
|
432
|
-
env: resolveMatrixSqliteStateEnv({ stateDir: storageRootDir })
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
//#endregion
|
|
436
|
-
export { readLegacyMatrixSyncCacheState as a, openMatrixSyncCacheStoreOptions as i, file_sync_store_exports as n, writeMatrixSyncCacheStateToStore as o, hasMatrixSyncCacheStateInStore as r, SqliteBackedMatrixSyncStore as t };
|