@awiki/dsh-plugin 0.3.0-rc.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -16
- package/README.zh.md +15 -11
- package/THIRD_PARTY_NOTICES.md +0 -7
- package/lib/client.js +5572 -8534
- package/lib/client.js.map +1 -1
- package/lib/index.js +373 -36
- package/lib/provider.js +1 -1
- package/lib/{sdk-adapter-DLhLrLI6.mjs → sdk-adapter-BIbsJmRj.mjs} +28 -4
- package/lib/typert.host.js +221 -81
- package/lib/typert.remote-client.d.ts +5 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +221 -81
- package/lib/types/client/AwikiGroupAccessNotice.d.ts +12 -0
- package/lib/types/client/AwikiGroupAccessNotice.d.ts.map +1 -0
- package/lib/types/client/AwikiGroupAccessNotice.js +31 -0
- package/lib/types/client/AwikiGroupAccessNotice.js.map +1 -0
- package/lib/types/client/AwikiGroupDetails.d.ts +8 -1
- package/lib/types/client/AwikiGroupDetails.d.ts.map +1 -1
- package/lib/types/client/AwikiGroupDetails.js +40 -4
- package/lib/types/client/AwikiGroupDetails.js.map +1 -1
- package/lib/types/client/AwikiIdentityAccess.d.ts +2 -1
- package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -1
- package/lib/types/client/AwikiIdentityAccess.js +10 -2
- package/lib/types/client/AwikiIdentityAccess.js.map +1 -1
- package/lib/types/client/AwikiMail.d.ts.map +1 -1
- package/lib/types/client/AwikiMail.js +5 -1
- package/lib/types/client/AwikiMail.js.map +1 -1
- package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
- package/lib/types/client/AwikiOverlay.js +72 -11
- package/lib/types/client/AwikiOverlay.js.map +1 -1
- package/lib/types/client/AwikiRecoveryForm.d.ts +3 -0
- package/lib/types/client/AwikiRecoveryForm.d.ts.map +1 -1
- package/lib/types/client/AwikiRecoveryForm.js +10 -6
- package/lib/types/client/AwikiRecoveryForm.js.map +1 -1
- package/lib/types/client/AwikiSettingsSection.d.ts +4 -18
- package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
- package/lib/types/client/AwikiSettingsSection.js +7 -271
- package/lib/types/client/AwikiSettingsSection.js.map +1 -1
- package/lib/types/client/awiki-client-bridge.d.ts +18 -0
- package/lib/types/client/awiki-client-bridge.d.ts.map +1 -0
- package/lib/types/client/awiki-client-bridge.js +18 -0
- package/lib/types/client/awiki-client-bridge.js.map +1 -0
- package/lib/types/client/controller.d.ts +38 -2
- package/lib/types/client/controller.d.ts.map +1 -1
- package/lib/types/client/controller.js +473 -47
- package/lib/types/client/controller.js.map +1 -1
- package/lib/types/client/index.d.ts +3 -3
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +7 -46
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/settings-locales.d.ts +2 -5
- package/lib/types/client/settings-locales.d.ts.map +1 -1
- package/lib/types/client/settings-locales.js +3 -183
- package/lib/types/client/settings-locales.js.map +1 -1
- package/lib/types/client/slots.d.ts +6 -0
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/conversation-preferences.d.ts +20 -0
- package/lib/types/conversation-preferences.d.ts.map +1 -0
- package/lib/types/conversation-preferences.js +294 -0
- package/lib/types/conversation-preferences.js.map +1 -0
- package/lib/types/index.d.ts +6 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +39 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +35 -4
- package/lib/types/sdk-adapter.js.map +1 -1
- package/lib/types/types.d.ts +32 -1
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/package.json +57 -55
- package/lib/types/client/AwikiOnboarding.d.ts +0 -22
- package/lib/types/client/AwikiOnboarding.d.ts.map +0 -1
- package/lib/types/client/AwikiOnboarding.js +0 -104
- package/lib/types/client/AwikiOnboarding.js.map +0 -1
- package/lib/types/client/RechargeComingSoonDialog.d.ts +0 -11
- package/lib/types/client/RechargeComingSoonDialog.d.ts.map +0 -1
- package/lib/types/client/RechargeComingSoonDialog.js +0 -8
- package/lib/types/client/RechargeComingSoonDialog.js.map +0 -1
- package/lib/types/client/model-availability-controller.d.ts +0 -27
- package/lib/types/client/model-availability-controller.d.ts.map +0 -1
- package/lib/types/client/model-availability-controller.js +0 -111
- package/lib/types/client/model-availability-controller.js.map +0 -1
- package/lib/types/client/model-proxy-controller.d.ts +0 -45
- package/lib/types/client/model-proxy-controller.d.ts.map +0 -1
- package/lib/types/client/model-proxy-controller.js +0 -306
- package/lib/types/client/model-proxy-controller.js.map +0 -1
- package/lib/types/client/recharge-availability.d.ts +0 -6
- package/lib/types/client/recharge-availability.d.ts.map +0 -1
- package/lib/types/client/recharge-availability.js +0 -6
- package/lib/types/client/recharge-availability.js.map +0 -1
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as downloadedAttachment } from "./sdk-adapter-
|
|
1
|
+
import { n as downloadedAttachment } from "./sdk-adapter-BIbsJmRj.mjs";
|
|
2
2
|
import "@deepseek-ai/cordis";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { isAbsolute, join } from "node:path";
|
|
@@ -592,15 +592,15 @@ const AWIKI_SETTINGS_RPC_ENDPOINTS = {
|
|
|
592
592
|
//#endregion
|
|
593
593
|
//#region lib/types/settings-rpc.js
|
|
594
594
|
/** Loopback-only Host transport for AWiki's durable plugin settings. */
|
|
595
|
-
function isRecord$
|
|
595
|
+
function isRecord$2(value) {
|
|
596
596
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
597
597
|
}
|
|
598
598
|
function expectedRevision(payload) {
|
|
599
|
-
if (!isRecord$
|
|
599
|
+
if (!isRecord$2(payload) || !Number.isSafeInteger(payload.expectedRevision) || payload.expectedRevision < 0) return;
|
|
600
600
|
return payload.expectedRevision;
|
|
601
601
|
}
|
|
602
602
|
function sanitizeLayer(value) {
|
|
603
|
-
if (!isRecord$
|
|
603
|
+
if (!isRecord$2(value)) return void 0;
|
|
604
604
|
if (!Object.hasOwn(value, "domain")) return {};
|
|
605
605
|
if (typeof value["domain"] !== "string") return void 0;
|
|
606
606
|
const domain = normalizeAwikiDomain(value[AWIKI_DOMAIN_FIELD]);
|
|
@@ -609,7 +609,7 @@ function sanitizeLayer(value) {
|
|
|
609
609
|
}
|
|
610
610
|
function view(provider) {
|
|
611
611
|
const descriptor = provider.describe({ redactSecrets: true }).find((candidate) => candidate.ns === AWIKI_SETTINGS_NAMESPACE);
|
|
612
|
-
if (descriptor === void 0 || !isRecord$
|
|
612
|
+
if (descriptor === void 0 || !isRecord$2(descriptor.value) || typeof descriptor.value.domain !== "string") return;
|
|
613
613
|
const domain = normalizeAwikiDomain(descriptor.value.domain);
|
|
614
614
|
if (domain !== descriptor.value.domain) return void 0;
|
|
615
615
|
const base = descriptor.base === void 0 ? void 0 : sanitizeLayer(descriptor.base);
|
|
@@ -660,7 +660,7 @@ function createAwikiSettingsRpcHandler(getProvider) {
|
|
|
660
660
|
const provider = getProvider();
|
|
661
661
|
if (provider === void 0) return unavailable();
|
|
662
662
|
if (endpoint === AWIKI_SETTINGS_RPC_ENDPOINTS.describe) {
|
|
663
|
-
if (!isRecord$
|
|
663
|
+
if (!isRecord$2(payload)) return badRequest();
|
|
664
664
|
const current = view(provider);
|
|
665
665
|
return current === void 0 ? unavailable() : {
|
|
666
666
|
ok: true,
|
|
@@ -671,7 +671,7 @@ function createAwikiSettingsRpcHandler(getProvider) {
|
|
|
671
671
|
if (revision === void 0) return badRequest();
|
|
672
672
|
let operation;
|
|
673
673
|
if (endpoint === AWIKI_SETTINGS_RPC_ENDPOINTS.setDomain) {
|
|
674
|
-
if (!isRecord$
|
|
674
|
+
if (!isRecord$2(payload) || typeof payload.domain !== "string") return badRequest();
|
|
675
675
|
let domain;
|
|
676
676
|
try {
|
|
677
677
|
domain = normalizeAwikiDomain(payload.domain);
|
|
@@ -740,7 +740,7 @@ var AwikiSessionStore = class {
|
|
|
740
740
|
if (await readFile(this.markerPath, "utf8") !== SIGNED_OUT_MARKER) throw new TypeError("awiki: local session marker is invalid");
|
|
741
741
|
return true;
|
|
742
742
|
} catch (error) {
|
|
743
|
-
if (isMissing$
|
|
743
|
+
if (isMissing$4(error)) return false;
|
|
744
744
|
throw error;
|
|
745
745
|
}
|
|
746
746
|
}
|
|
@@ -758,7 +758,7 @@ var AwikiSessionStore = class {
|
|
|
758
758
|
mode: 384
|
|
759
759
|
});
|
|
760
760
|
} catch (error) {
|
|
761
|
-
if (!isFileExists$
|
|
761
|
+
if (!isFileExists$3(error) || !await this.isSignedOut()) throw error;
|
|
762
762
|
}
|
|
763
763
|
await chmod(this.markerPath, 384);
|
|
764
764
|
}
|
|
@@ -767,7 +767,7 @@ var AwikiSessionStore = class {
|
|
|
767
767
|
try {
|
|
768
768
|
await unlink(this.markerPath);
|
|
769
769
|
} catch (error) {
|
|
770
|
-
if (!isMissing$
|
|
770
|
+
if (!isMissing$4(error)) throw error;
|
|
771
771
|
}
|
|
772
772
|
}
|
|
773
773
|
async hasPrivateHostDirectory() {
|
|
@@ -776,15 +776,15 @@ var AwikiSessionStore = class {
|
|
|
776
776
|
if (!directory.isDirectory() || directory.isSymbolicLink()) throw new TypeError("awiki: local session directory is invalid");
|
|
777
777
|
return true;
|
|
778
778
|
} catch (error) {
|
|
779
|
-
if (isMissing$
|
|
779
|
+
if (isMissing$4(error)) return false;
|
|
780
780
|
throw error;
|
|
781
781
|
}
|
|
782
782
|
}
|
|
783
783
|
};
|
|
784
|
-
function isMissing$
|
|
784
|
+
function isMissing$4(error) {
|
|
785
785
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
786
786
|
}
|
|
787
|
-
function isFileExists$
|
|
787
|
+
function isFileExists$3(error) {
|
|
788
788
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
789
789
|
}
|
|
790
790
|
//#endregion
|
|
@@ -877,7 +877,7 @@ var AwikiImageAttachmentCache = class {
|
|
|
877
877
|
force: true
|
|
878
878
|
});
|
|
879
879
|
} catch (error) {
|
|
880
|
-
if (!isMissing$
|
|
880
|
+
if (!isMissing$3(error)) throw error;
|
|
881
881
|
}
|
|
882
882
|
}
|
|
883
883
|
decode(input, ownerDid, request) {
|
|
@@ -903,7 +903,7 @@ var AwikiImageAttachmentCache = class {
|
|
|
903
903
|
if (!await this.hasPrivateDirectory(this.hostDirectory)) throw new TypeError("awiki: local Host directory is invalid");
|
|
904
904
|
await chmod(this.hostDirectory, 448);
|
|
905
905
|
await mkdir(this.directory, { mode: 448 }).catch((error) => {
|
|
906
|
-
if (!isFileExists$
|
|
906
|
+
if (!isFileExists$2(error)) throw error;
|
|
907
907
|
});
|
|
908
908
|
if (!await this.hasPrivateDirectory(this.directory)) throw new TypeError("awiki: local image cache directory is invalid");
|
|
909
909
|
await chmod(this.directory, 448);
|
|
@@ -941,33 +941,33 @@ var AwikiImageAttachmentCache = class {
|
|
|
941
941
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) throw new TypeError("awiki: local image cache path is invalid");
|
|
942
942
|
return true;
|
|
943
943
|
} catch (error) {
|
|
944
|
-
if (isMissing$
|
|
944
|
+
if (isMissing$3(error)) return false;
|
|
945
945
|
throw error;
|
|
946
946
|
}
|
|
947
947
|
}
|
|
948
948
|
};
|
|
949
|
-
function isMissing$
|
|
949
|
+
function isMissing$3(error) {
|
|
950
950
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
951
951
|
}
|
|
952
|
-
function isFileExists$
|
|
952
|
+
function isFileExists$2(error) {
|
|
953
953
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
954
954
|
}
|
|
955
955
|
//#endregion
|
|
956
956
|
//#region lib/types/sent-mail-store.js
|
|
957
957
|
/** Private, owner-bound persistence for mail successfully sent by this installation. */
|
|
958
|
-
const STORE_VERSION = 1;
|
|
959
|
-
const STORE_DIRECTORY = "sent-mail-v1";
|
|
958
|
+
const STORE_VERSION$1 = 1;
|
|
959
|
+
const STORE_DIRECTORY$1 = "sent-mail-v1";
|
|
960
960
|
const MAX_RECORDS = 200;
|
|
961
|
-
const MAX_STORE_BYTES = 16777216;
|
|
961
|
+
const MAX_STORE_BYTES$1 = 16777216;
|
|
962
962
|
const PREVIEW_CHARACTERS = 160;
|
|
963
963
|
const LOCAL_SENT_MAIL_ID_PREFIX = "awiki-sent-v1:";
|
|
964
|
-
function invalidState() {
|
|
964
|
+
function invalidState$1() {
|
|
965
965
|
throw new TypeError("awiki: local sent-mail history is invalid");
|
|
966
966
|
}
|
|
967
|
-
function isMissing$
|
|
967
|
+
function isMissing$2(error) {
|
|
968
968
|
return typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
969
969
|
}
|
|
970
|
-
function isFileExists(error) {
|
|
970
|
+
function isFileExists$1(error) {
|
|
971
971
|
return typeof error === "object" && error !== null && error.code === "EEXIST";
|
|
972
972
|
}
|
|
973
973
|
function validToken(value, maxCharacters) {
|
|
@@ -977,9 +977,9 @@ function validAddress(value) {
|
|
|
977
977
|
return typeof value === "string" && Array.from(value).length >= 3 && Array.from(value).length <= 320 && value.includes("@") && !/[\s\u0000-\u001f\u007f-\u009f]/u.test(value);
|
|
978
978
|
}
|
|
979
979
|
function decodeRecord(input) {
|
|
980
|
-
if (typeof input !== "object" || input === null || Array.isArray(input)) invalidState();
|
|
980
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) invalidState$1();
|
|
981
981
|
const value = input;
|
|
982
|
-
if (typeof value.id !== "string" || !/^awiki-sent-v1:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value.id) || value.serviceMessageId !== void 0 && !validToken(value.serviceMessageId, 2048) || value.from !== void 0 && !validAddress(value.from) || typeof value.sentAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value.sentAt) || !Number.isFinite(Date.parse(value.sentAt))) invalidState();
|
|
982
|
+
if (typeof value.id !== "string" || !/^awiki-sent-v1:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value.id) || value.serviceMessageId !== void 0 && !validToken(value.serviceMessageId, 2048) || value.from !== void 0 && !validAddress(value.from) || typeof value.sentAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value.sentAt) || !Number.isFinite(Date.parse(value.sentAt))) invalidState$1();
|
|
983
983
|
let request;
|
|
984
984
|
try {
|
|
985
985
|
request = mailSendRequest({
|
|
@@ -989,7 +989,7 @@ function decodeRecord(input) {
|
|
|
989
989
|
bodyText: value.bodyText
|
|
990
990
|
});
|
|
991
991
|
} catch {
|
|
992
|
-
invalidState();
|
|
992
|
+
invalidState$1();
|
|
993
993
|
}
|
|
994
994
|
return {
|
|
995
995
|
id: value.id,
|
|
@@ -1037,7 +1037,7 @@ var AwikiSentMailStore = class {
|
|
|
1037
1037
|
mutation = Promise.resolve();
|
|
1038
1038
|
constructor(stateRoot) {
|
|
1039
1039
|
this.hostDirectory = join(stateRoot, ".host");
|
|
1040
|
-
this.directory = join(this.hostDirectory, STORE_DIRECTORY);
|
|
1040
|
+
this.directory = join(this.hostDirectory, STORE_DIRECTORY$1);
|
|
1041
1041
|
}
|
|
1042
1042
|
async list(ownerDid, request) {
|
|
1043
1043
|
const records = request.unreadOnly === true ? [] : await this.load(ownerDid);
|
|
@@ -1091,7 +1091,7 @@ var AwikiSentMailStore = class {
|
|
|
1091
1091
|
force: true
|
|
1092
1092
|
});
|
|
1093
1093
|
} catch (error) {
|
|
1094
|
-
if (!isMissing$
|
|
1094
|
+
if (!isMissing$2(error)) throw error;
|
|
1095
1095
|
}
|
|
1096
1096
|
}
|
|
1097
1097
|
async load(ownerDid) {
|
|
@@ -1100,21 +1100,21 @@ var AwikiSentMailStore = class {
|
|
|
1100
1100
|
let text;
|
|
1101
1101
|
try {
|
|
1102
1102
|
const metadata = await lstat(path);
|
|
1103
|
-
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > MAX_STORE_BYTES) invalidState();
|
|
1103
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > MAX_STORE_BYTES$1) invalidState$1();
|
|
1104
1104
|
text = await readFile(path, "utf8");
|
|
1105
1105
|
} catch (error) {
|
|
1106
|
-
if (isMissing$
|
|
1106
|
+
if (isMissing$2(error)) return [];
|
|
1107
1107
|
throw error;
|
|
1108
1108
|
}
|
|
1109
1109
|
let parsed;
|
|
1110
1110
|
try {
|
|
1111
1111
|
parsed = JSON.parse(text);
|
|
1112
1112
|
} catch {
|
|
1113
|
-
invalidState();
|
|
1113
|
+
invalidState$1();
|
|
1114
1114
|
}
|
|
1115
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) invalidState();
|
|
1115
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) invalidState$1();
|
|
1116
1116
|
const value = parsed;
|
|
1117
|
-
if (value.version !== STORE_VERSION || value.ownerDid !== String(ownerDid) || !Array.isArray(value.records) || value.records.length > MAX_RECORDS) invalidState();
|
|
1117
|
+
if (value.version !== STORE_VERSION$1 || value.ownerDid !== String(ownerDid) || !Array.isArray(value.records) || value.records.length > MAX_RECORDS) invalidState$1();
|
|
1118
1118
|
return value.records.map(decodeRecord);
|
|
1119
1119
|
}
|
|
1120
1120
|
async write(ownerDid, records) {
|
|
@@ -1122,11 +1122,289 @@ var AwikiSentMailStore = class {
|
|
|
1122
1122
|
const path = this.path(ownerDid);
|
|
1123
1123
|
const temporary = join(this.directory, `.${this.key(ownerDid)}.${randomUUID()}.tmp`);
|
|
1124
1124
|
const text = JSON.stringify({
|
|
1125
|
-
version: STORE_VERSION,
|
|
1125
|
+
version: STORE_VERSION$1,
|
|
1126
1126
|
ownerDid: String(ownerDid),
|
|
1127
1127
|
records
|
|
1128
1128
|
});
|
|
1129
|
+
if (Buffer.byteLength(text, "utf8") > MAX_STORE_BYTES$1) invalidState$1();
|
|
1130
|
+
try {
|
|
1131
|
+
await writeFile(temporary, text, {
|
|
1132
|
+
flag: "wx",
|
|
1133
|
+
mode: 384
|
|
1134
|
+
});
|
|
1135
|
+
await rename(temporary, path);
|
|
1136
|
+
await chmod(path, 384);
|
|
1137
|
+
} finally {
|
|
1138
|
+
await unlink(temporary).catch(() => void 0);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
async ensureDirectory() {
|
|
1142
|
+
await mkdir(this.hostDirectory, {
|
|
1143
|
+
recursive: true,
|
|
1144
|
+
mode: 448
|
|
1145
|
+
});
|
|
1146
|
+
if (!await this.hasDirectory(this.hostDirectory)) invalidState$1();
|
|
1147
|
+
await chmod(this.hostDirectory, 448);
|
|
1148
|
+
await mkdir(this.directory, { mode: 448 }).catch((error) => {
|
|
1149
|
+
if (!isFileExists$1(error)) throw error;
|
|
1150
|
+
});
|
|
1151
|
+
if (!await this.hasDirectory(this.directory)) invalidState$1();
|
|
1152
|
+
await chmod(this.directory, 448);
|
|
1153
|
+
}
|
|
1154
|
+
async hasDirectory(path) {
|
|
1155
|
+
try {
|
|
1156
|
+
const metadata = await lstat(path);
|
|
1157
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) invalidState$1();
|
|
1158
|
+
return true;
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
if (isMissing$2(error)) return false;
|
|
1161
|
+
throw error;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
path(ownerDid) {
|
|
1165
|
+
return join(this.directory, `${this.key(ownerDid)}.json`);
|
|
1166
|
+
}
|
|
1167
|
+
key(ownerDid) {
|
|
1168
|
+
return createHash("sha256").update(String(ownerDid)).digest("hex");
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
//#endregion
|
|
1172
|
+
//#region lib/types/conversation-preferences.js
|
|
1173
|
+
const STORE_VERSION = 1;
|
|
1174
|
+
const STORE_DIRECTORY = "conversation-preferences";
|
|
1175
|
+
const MAX_STORE_BYTES = 2097152;
|
|
1176
|
+
const MAX_HIDDEN_CONVERSATIONS = 500;
|
|
1177
|
+
const MAX_IDENTIFIER_CHARACTERS = 2048;
|
|
1178
|
+
const MAX_TITLE_CHARACTERS = 1024;
|
|
1179
|
+
const MAX_PREVIEW_CHARACTERS = 4096;
|
|
1180
|
+
const MAX_HANDLE_CHARACTERS = 512;
|
|
1181
|
+
const MAX_RECOVERY_SIGNATURE_CHARACTERS = 128;
|
|
1182
|
+
function invalidState() {
|
|
1183
|
+
throw new TypeError("awiki: conversation preferences are invalid");
|
|
1184
|
+
}
|
|
1185
|
+
function isMissing$1(error) {
|
|
1186
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1187
|
+
}
|
|
1188
|
+
function isFileExists(error) {
|
|
1189
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
1190
|
+
}
|
|
1191
|
+
function isRecord$1(value) {
|
|
1192
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1193
|
+
}
|
|
1194
|
+
function boundedString$1(value, maximum) {
|
|
1195
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maximum * 2) invalidState();
|
|
1196
|
+
if (Array.from(value).length > maximum) invalidState();
|
|
1197
|
+
return value;
|
|
1198
|
+
}
|
|
1199
|
+
function optionalString(value, maximum) {
|
|
1200
|
+
return value === void 0 ? void 0 : boundedString$1(value, maximum);
|
|
1201
|
+
}
|
|
1202
|
+
function optionalCount(value) {
|
|
1203
|
+
if (value === void 0) return void 0;
|
|
1204
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > 4294967295) invalidState();
|
|
1205
|
+
return value;
|
|
1206
|
+
}
|
|
1207
|
+
function optionalTimestamp(value) {
|
|
1208
|
+
if (value === void 0) return void 0;
|
|
1209
|
+
if (!Number.isSafeInteger(value) || value < 0) invalidState();
|
|
1210
|
+
return value;
|
|
1211
|
+
}
|
|
1212
|
+
/** Rebuild a bounded display-only snapshot before it reaches private persistence. */
|
|
1213
|
+
function conversation(value) {
|
|
1214
|
+
if (!isRecord$1(value)) invalidState();
|
|
1215
|
+
const kind = value.kind;
|
|
1216
|
+
const id = boundedString$1(value.id, MAX_IDENTIFIER_CHARACTERS);
|
|
1217
|
+
const title = boundedString$1(value.title, MAX_TITLE_CHARACTERS);
|
|
1218
|
+
const unreadCount = optionalCount(value.unreadCount);
|
|
1219
|
+
const lastMessageAt = optionalTimestamp(value.lastMessageAt);
|
|
1220
|
+
const lastMessagePreview = optionalString(value.lastMessagePreview, MAX_PREVIEW_CHARACTERS);
|
|
1221
|
+
const common = {
|
|
1222
|
+
id,
|
|
1223
|
+
title,
|
|
1224
|
+
...unreadCount === void 0 ? {} : { unreadCount },
|
|
1225
|
+
...lastMessageAt === void 0 ? {} : { lastMessageAt },
|
|
1226
|
+
...lastMessagePreview === void 0 ? {} : { lastMessagePreview }
|
|
1227
|
+
};
|
|
1228
|
+
if (kind === "direct") {
|
|
1229
|
+
const peerDid = boundedString$1(value.peerDid, MAX_IDENTIFIER_CHARACTERS);
|
|
1230
|
+
if (!peerDid.startsWith("did:")) invalidState();
|
|
1231
|
+
const peerHandle = optionalString(value.peerHandle, MAX_HANDLE_CHARACTERS);
|
|
1232
|
+
const displayName = optionalString(value.displayName, MAX_TITLE_CHARACTERS);
|
|
1233
|
+
return {
|
|
1234
|
+
kind,
|
|
1235
|
+
...common,
|
|
1236
|
+
peerDid,
|
|
1237
|
+
...peerHandle === void 0 ? {} : { peerHandle },
|
|
1238
|
+
...displayName === void 0 ? {} : { displayName }
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
if (kind === "group") {
|
|
1242
|
+
const groupDid = boundedString$1(value.groupDid, MAX_IDENTIFIER_CHARACTERS);
|
|
1243
|
+
if (!groupDid.startsWith("did:")) invalidState();
|
|
1244
|
+
return {
|
|
1245
|
+
kind,
|
|
1246
|
+
...common,
|
|
1247
|
+
groupDid
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
invalidState();
|
|
1251
|
+
}
|
|
1252
|
+
function hiddenPreference(value) {
|
|
1253
|
+
if (!isRecord$1(value) || !Number.isSafeInteger(value.hiddenAt) || value.hiddenAt < 0) invalidState();
|
|
1254
|
+
return {
|
|
1255
|
+
conversation: conversation(value.conversation),
|
|
1256
|
+
hiddenAt: value.hiddenAt
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
function preferences(value, expectedOwnerDid) {
|
|
1260
|
+
if (!isRecord$1(value) || value.version !== STORE_VERSION || value.ownerDid !== String(expectedOwnerDid) || !Array.isArray(value.hiddenConversations) || value.hiddenConversations.length > MAX_HIDDEN_CONVERSATIONS) invalidState();
|
|
1261
|
+
const hiddenConversations = value.hiddenConversations.map(hiddenPreference);
|
|
1262
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1263
|
+
for (const hidden of hiddenConversations) {
|
|
1264
|
+
if (seen.has(hidden.conversation.id)) invalidState();
|
|
1265
|
+
seen.add(hidden.conversation.id);
|
|
1266
|
+
}
|
|
1267
|
+
const dismissedGroupRecoverySignature = optionalString(value.dismissedGroupRecoverySignature, MAX_RECOVERY_SIGNATURE_CHARACTERS);
|
|
1268
|
+
return {
|
|
1269
|
+
version: STORE_VERSION,
|
|
1270
|
+
ownerDid: String(expectedOwnerDid),
|
|
1271
|
+
hiddenConversations,
|
|
1272
|
+
...dismissedGroupRecoverySignature === void 0 ? {} : { dismissedGroupRecoverySignature }
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
function publicPreferences(value) {
|
|
1276
|
+
return {
|
|
1277
|
+
hiddenConversations: value.hiddenConversations.map((hidden) => ({
|
|
1278
|
+
conversation: { ...hidden.conversation },
|
|
1279
|
+
hiddenAt: hidden.hiddenAt
|
|
1280
|
+
})),
|
|
1281
|
+
...value.dismissedGroupRecoverySignature === void 0 ? {} : { dismissedGroupRecoverySignature: value.dismissedGroupRecoverySignature }
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
function emptyPreferences(ownerDid) {
|
|
1285
|
+
return {
|
|
1286
|
+
version: STORE_VERSION,
|
|
1287
|
+
ownerDid: String(ownerDid),
|
|
1288
|
+
hiddenConversations: []
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
/** Validate one browser mutation before entering private Host persistence. */
|
|
1292
|
+
function normalizeConversationPreferenceMutation(value) {
|
|
1293
|
+
try {
|
|
1294
|
+
if (!isRecord$1(value)) return void 0;
|
|
1295
|
+
if (value.action === "hide") return {
|
|
1296
|
+
action: "hide",
|
|
1297
|
+
conversation: conversation(value.conversation)
|
|
1298
|
+
};
|
|
1299
|
+
if (value.action === "restore") return {
|
|
1300
|
+
action: "restore",
|
|
1301
|
+
conversationId: boundedString$1(value.conversationId, MAX_IDENTIFIER_CHARACTERS)
|
|
1302
|
+
};
|
|
1303
|
+
if (value.action === "dismiss-group-recovery") return {
|
|
1304
|
+
action: "dismiss-group-recovery",
|
|
1305
|
+
signature: boundedString$1(value.signature, MAX_RECOVERY_SIGNATURE_CHARACTERS)
|
|
1306
|
+
};
|
|
1307
|
+
} catch {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
/** Atomic identity-scoped product preferences, independent of Core membership and history. */
|
|
1312
|
+
var AwikiConversationPreferenceStore = class {
|
|
1313
|
+
hostDirectory;
|
|
1314
|
+
directory;
|
|
1315
|
+
mutation = Promise.resolve();
|
|
1316
|
+
constructor(stateRoot) {
|
|
1317
|
+
this.hostDirectory = join(stateRoot, ".host");
|
|
1318
|
+
this.directory = join(this.hostDirectory, STORE_DIRECTORY);
|
|
1319
|
+
}
|
|
1320
|
+
async get(ownerDid) {
|
|
1321
|
+
await this.mutation;
|
|
1322
|
+
return publicPreferences(await this.load(ownerDid));
|
|
1323
|
+
}
|
|
1324
|
+
update(ownerDid, request) {
|
|
1325
|
+
const mutate = async () => {
|
|
1326
|
+
const normalized = normalizeConversationPreferenceMutation(request);
|
|
1327
|
+
if (normalized === void 0) invalidState();
|
|
1328
|
+
const current = await this.load(ownerDid);
|
|
1329
|
+
let next;
|
|
1330
|
+
switch (normalized.action) {
|
|
1331
|
+
case "hide": {
|
|
1332
|
+
const snapshot = normalized.conversation;
|
|
1333
|
+
const hidden = current.hiddenConversations.filter((item) => item.conversation.id !== snapshot.id);
|
|
1334
|
+
hidden.unshift({
|
|
1335
|
+
conversation: snapshot,
|
|
1336
|
+
hiddenAt: Date.now()
|
|
1337
|
+
});
|
|
1338
|
+
next = {
|
|
1339
|
+
...current,
|
|
1340
|
+
hiddenConversations: hidden.slice(0, MAX_HIDDEN_CONVERSATIONS)
|
|
1341
|
+
};
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
case "restore": {
|
|
1345
|
+
const conversationId = normalized.conversationId;
|
|
1346
|
+
next = {
|
|
1347
|
+
...current,
|
|
1348
|
+
hiddenConversations: current.hiddenConversations.filter((item) => item.conversation.id !== conversationId)
|
|
1349
|
+
};
|
|
1350
|
+
break;
|
|
1351
|
+
}
|
|
1352
|
+
case "dismiss-group-recovery": {
|
|
1353
|
+
const signature = normalized.signature;
|
|
1354
|
+
next = {
|
|
1355
|
+
...current,
|
|
1356
|
+
dismissedGroupRecoverySignature: signature
|
|
1357
|
+
};
|
|
1358
|
+
break;
|
|
1359
|
+
}
|
|
1360
|
+
default: invalidState();
|
|
1361
|
+
}
|
|
1362
|
+
await this.write(ownerDid, next);
|
|
1363
|
+
return publicPreferences(next);
|
|
1364
|
+
};
|
|
1365
|
+
const pending = this.mutation.then(mutate, mutate);
|
|
1366
|
+
this.mutation = pending.then(() => void 0, () => void 0);
|
|
1367
|
+
return pending;
|
|
1368
|
+
}
|
|
1369
|
+
async clear() {
|
|
1370
|
+
await this.mutation;
|
|
1371
|
+
try {
|
|
1372
|
+
if (!await this.hasDirectory(this.hostDirectory)) return;
|
|
1373
|
+
const metadata = await lstat(this.directory);
|
|
1374
|
+
if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
|
|
1375
|
+
await unlink(this.directory);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
await rm(this.directory, {
|
|
1379
|
+
recursive: true,
|
|
1380
|
+
force: true
|
|
1381
|
+
});
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
if (!isMissing$1(error)) throw error;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
async load(ownerDid) {
|
|
1387
|
+
if (!await this.hasDirectory(this.hostDirectory) || !await this.hasDirectory(this.directory)) return emptyPreferences(ownerDid);
|
|
1388
|
+
const path = this.path(ownerDid);
|
|
1389
|
+
try {
|
|
1390
|
+
const metadata = await lstat(path);
|
|
1391
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > MAX_STORE_BYTES) invalidState();
|
|
1392
|
+
const text = await readFile(path, "utf8");
|
|
1393
|
+
if (Buffer.byteLength(text, "utf8") > MAX_STORE_BYTES) invalidState();
|
|
1394
|
+
return preferences(JSON.parse(text), ownerDid);
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
if (isMissing$1(error)) return emptyPreferences(ownerDid);
|
|
1397
|
+
if (error instanceof SyntaxError) invalidState();
|
|
1398
|
+
throw error;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
async write(ownerDid, value) {
|
|
1402
|
+
const snapshot = preferences(value, ownerDid);
|
|
1403
|
+
const text = `${JSON.stringify(snapshot)}\n`;
|
|
1129
1404
|
if (Buffer.byteLength(text, "utf8") > MAX_STORE_BYTES) invalidState();
|
|
1405
|
+
await this.ensureDirectory();
|
|
1406
|
+
const path = this.path(ownerDid);
|
|
1407
|
+
const temporary = join(this.directory, `.${this.key(ownerDid)}.${randomUUID()}.tmp`);
|
|
1130
1408
|
try {
|
|
1131
1409
|
await writeFile(temporary, text, {
|
|
1132
1410
|
flag: "wx",
|
|
@@ -1899,6 +2177,7 @@ const FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
|
1899
2177
|
"handle-unavailable",
|
|
1900
2178
|
"not-found",
|
|
1901
2179
|
"forbidden",
|
|
2180
|
+
"identity-recovery-required",
|
|
1902
2181
|
"conflict",
|
|
1903
2182
|
"rate-limited",
|
|
1904
2183
|
"group-membership-required",
|
|
@@ -1927,6 +2206,7 @@ const FAILURE_MESSAGES = {
|
|
|
1927
2206
|
"handle-unavailable": "The requested AWiki handle is unavailable.",
|
|
1928
2207
|
"not-found": "The requested AWiki resource was not found.",
|
|
1929
2208
|
"forbidden": "The AWiki operation is not permitted.",
|
|
2209
|
+
"identity-recovery-required": "The local AWiki identity must be recovered before it can be used again.",
|
|
1930
2210
|
"conflict": "The AWiki operation conflicts with current state.",
|
|
1931
2211
|
"rate-limited": "The AWiki service rate-limited the request.",
|
|
1932
2212
|
"group-membership-required": "The active AWiki identity is not a member of this group.",
|
|
@@ -2104,11 +2384,26 @@ function publicGroupRebindRecoverySummary(value) {
|
|
|
2104
2384
|
candidate.pending,
|
|
2105
2385
|
candidate.blocked
|
|
2106
2386
|
].some((count) => !Number.isSafeInteger(count) || count < 0 || count > 4294967295)) return;
|
|
2387
|
+
if (!Array.isArray(candidate.items) || candidate.items.length > 500) return void 0;
|
|
2388
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2389
|
+
const items = [];
|
|
2390
|
+
for (const value of candidate.items) {
|
|
2391
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2392
|
+
const item = value;
|
|
2393
|
+
const groupDid = normalizeGroupDid(item.groupDid);
|
|
2394
|
+
if (groupDid === void 0 || seen.has(groupDid) || item.status !== "pending" && item.status !== "blocked") return;
|
|
2395
|
+
seen.add(groupDid);
|
|
2396
|
+
items.push({
|
|
2397
|
+
groupDid,
|
|
2398
|
+
status: item.status
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2107
2401
|
return {
|
|
2108
2402
|
processed: candidate.processed,
|
|
2109
2403
|
completed: candidate.completed,
|
|
2110
2404
|
pending: candidate.pending,
|
|
2111
|
-
blocked: candidate.blocked
|
|
2405
|
+
blocked: candidate.blocked,
|
|
2406
|
+
items
|
|
2112
2407
|
};
|
|
2113
2408
|
}
|
|
2114
2409
|
function normalizeProfileRequest(request) {
|
|
@@ -2395,6 +2690,8 @@ let AwikiService = (() => {
|
|
|
2395
2690
|
let _addGroupMember_decorators;
|
|
2396
2691
|
let _removeGroupMember_decorators;
|
|
2397
2692
|
let _resumeGroupRebindRecovery_decorators;
|
|
2693
|
+
let _getConversationPreferences_decorators;
|
|
2694
|
+
let _updateConversationPreference_decorators;
|
|
2398
2695
|
let _listConversations_decorators;
|
|
2399
2696
|
let _getHistory_decorators;
|
|
2400
2697
|
let _getLocalHistory_decorators;
|
|
@@ -2438,6 +2735,8 @@ let AwikiService = (() => {
|
|
|
2438
2735
|
_addGroupMember_decorators = [Remote];
|
|
2439
2736
|
_removeGroupMember_decorators = [Remote];
|
|
2440
2737
|
_resumeGroupRebindRecovery_decorators = [Remote];
|
|
2738
|
+
_getConversationPreferences_decorators = [Remote];
|
|
2739
|
+
_updateConversationPreference_decorators = [Remote];
|
|
2441
2740
|
_listConversations_decorators = [Remote];
|
|
2442
2741
|
_getHistory_decorators = [Remote];
|
|
2443
2742
|
_getLocalHistory_decorators = [Remote];
|
|
@@ -2738,6 +3037,28 @@ let AwikiService = (() => {
|
|
|
2738
3037
|
},
|
|
2739
3038
|
metadata: _metadata
|
|
2740
3039
|
}, null, _instanceExtraInitializers);
|
|
3040
|
+
__esDecorate(this, null, _getConversationPreferences_decorators, {
|
|
3041
|
+
kind: "method",
|
|
3042
|
+
name: "getConversationPreferences",
|
|
3043
|
+
static: false,
|
|
3044
|
+
private: false,
|
|
3045
|
+
access: {
|
|
3046
|
+
has: (obj) => "getConversationPreferences" in obj,
|
|
3047
|
+
get: (obj) => obj.getConversationPreferences
|
|
3048
|
+
},
|
|
3049
|
+
metadata: _metadata
|
|
3050
|
+
}, null, _instanceExtraInitializers);
|
|
3051
|
+
__esDecorate(this, null, _updateConversationPreference_decorators, {
|
|
3052
|
+
kind: "method",
|
|
3053
|
+
name: "updateConversationPreference",
|
|
3054
|
+
static: false,
|
|
3055
|
+
private: false,
|
|
3056
|
+
access: {
|
|
3057
|
+
has: (obj) => "updateConversationPreference" in obj,
|
|
3058
|
+
get: (obj) => obj.updateConversationPreference
|
|
3059
|
+
},
|
|
3060
|
+
metadata: _metadata
|
|
3061
|
+
}, null, _instanceExtraInitializers);
|
|
2741
3062
|
__esDecorate(this, null, _listConversations_decorators, {
|
|
2742
3063
|
kind: "method",
|
|
2743
3064
|
name: "listConversations",
|
|
@@ -2905,6 +3226,7 @@ let AwikiService = (() => {
|
|
|
2905
3226
|
sessionStore;
|
|
2906
3227
|
imageAttachmentCache;
|
|
2907
3228
|
sentMailStore;
|
|
3229
|
+
conversationPreferenceStore;
|
|
2908
3230
|
startupUserServiceDomain;
|
|
2909
3231
|
settingsProvider;
|
|
2910
3232
|
provider;
|
|
@@ -2930,6 +3252,7 @@ let AwikiService = (() => {
|
|
|
2930
3252
|
this.sessionStore = new AwikiSessionStore(this.resolved.stateRoot);
|
|
2931
3253
|
this.imageAttachmentCache = new AwikiImageAttachmentCache(this.resolved.stateRoot, this.resolved.attachmentMaxBytes, this.resolved.imageAttachmentCacheMaxBytes);
|
|
2932
3254
|
this.sentMailStore = new AwikiSentMailStore(this.resolved.stateRoot);
|
|
3255
|
+
this.conversationPreferenceStore = new AwikiConversationPreferenceStore(this.resolved.stateRoot);
|
|
2933
3256
|
this.startupUserServiceDomain = this.resolved.userServiceDomain;
|
|
2934
3257
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
2935
3258
|
const provider = settingsCtx.settings;
|
|
@@ -3426,6 +3749,19 @@ let AwikiService = (() => {
|
|
|
3426
3749
|
value: summary
|
|
3427
3750
|
};
|
|
3428
3751
|
}
|
|
3752
|
+
/** Read presentation-only roster preferences for the active identity. */
|
|
3753
|
+
getConversationPreferences() {
|
|
3754
|
+
return this.run(async (client) => this.conversationPreferenceStore.get(await this.ownerDid(client)));
|
|
3755
|
+
}
|
|
3756
|
+
/** Persist one bounded local roster preference without changing Core or remote membership. */
|
|
3757
|
+
updateConversationPreference(request) {
|
|
3758
|
+
const normalized = normalizeConversationPreferenceMutation(request);
|
|
3759
|
+
if (normalized === void 0) return Promise.resolve({
|
|
3760
|
+
ok: false,
|
|
3761
|
+
error: failure("invalid-request")
|
|
3762
|
+
});
|
|
3763
|
+
return this.run(async (client) => this.conversationPreferenceStore.update(await this.ownerDid(client), normalized));
|
|
3764
|
+
}
|
|
3429
3765
|
/**
|
|
3430
3766
|
* List direct and existing group conversations.
|
|
3431
3767
|
* @param request - Optional opaque cursor and page limit.
|
|
@@ -3751,6 +4087,7 @@ let AwikiService = (() => {
|
|
|
3751
4087
|
try {
|
|
3752
4088
|
await this.imageAttachmentCache.clear();
|
|
3753
4089
|
await this.sentMailStore.clear();
|
|
4090
|
+
await this.conversationPreferenceStore.clear();
|
|
3754
4091
|
await this.sessionStore.signIn();
|
|
3755
4092
|
this.signedOut = false;
|
|
3756
4093
|
this.activeIdentityDid = void 0;
|
package/lib/provider.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as RustSdkAdapter } from "./sdk-adapter-
|
|
1
|
+
import { t as RustSdkAdapter } from "./sdk-adapter-BIbsJmRj.mjs";
|
|
2
2
|
import { openImCoreNodeClient } from "@awiki/im-core-node";
|
|
3
3
|
//#region lib/types/provider.js
|
|
4
4
|
/** Production AWiki provider backed by the versioned Rust IM Core Node bridge. */
|