@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.
Files changed (91) hide show
  1. package/README.md +17 -16
  2. package/README.zh.md +15 -11
  3. package/THIRD_PARTY_NOTICES.md +0 -7
  4. package/lib/client.js +5572 -8534
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +373 -36
  7. package/lib/provider.js +1 -1
  8. package/lib/{sdk-adapter-DLhLrLI6.mjs → sdk-adapter-BIbsJmRj.mjs} +28 -4
  9. package/lib/typert.host.js +221 -81
  10. package/lib/typert.remote-client.d.ts +5 -1
  11. package/lib/typert.remote-client.d.ts.map +1 -1
  12. package/lib/typert.remote-client.js +221 -81
  13. package/lib/types/client/AwikiGroupAccessNotice.d.ts +12 -0
  14. package/lib/types/client/AwikiGroupAccessNotice.d.ts.map +1 -0
  15. package/lib/types/client/AwikiGroupAccessNotice.js +31 -0
  16. package/lib/types/client/AwikiGroupAccessNotice.js.map +1 -0
  17. package/lib/types/client/AwikiGroupDetails.d.ts +8 -1
  18. package/lib/types/client/AwikiGroupDetails.d.ts.map +1 -1
  19. package/lib/types/client/AwikiGroupDetails.js +40 -4
  20. package/lib/types/client/AwikiGroupDetails.js.map +1 -1
  21. package/lib/types/client/AwikiIdentityAccess.d.ts +2 -1
  22. package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -1
  23. package/lib/types/client/AwikiIdentityAccess.js +10 -2
  24. package/lib/types/client/AwikiIdentityAccess.js.map +1 -1
  25. package/lib/types/client/AwikiMail.d.ts.map +1 -1
  26. package/lib/types/client/AwikiMail.js +5 -1
  27. package/lib/types/client/AwikiMail.js.map +1 -1
  28. package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
  29. package/lib/types/client/AwikiOverlay.js +72 -11
  30. package/lib/types/client/AwikiOverlay.js.map +1 -1
  31. package/lib/types/client/AwikiRecoveryForm.d.ts +3 -0
  32. package/lib/types/client/AwikiRecoveryForm.d.ts.map +1 -1
  33. package/lib/types/client/AwikiRecoveryForm.js +10 -6
  34. package/lib/types/client/AwikiRecoveryForm.js.map +1 -1
  35. package/lib/types/client/AwikiSettingsSection.d.ts +4 -18
  36. package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
  37. package/lib/types/client/AwikiSettingsSection.js +7 -271
  38. package/lib/types/client/AwikiSettingsSection.js.map +1 -1
  39. package/lib/types/client/awiki-client-bridge.d.ts +18 -0
  40. package/lib/types/client/awiki-client-bridge.d.ts.map +1 -0
  41. package/lib/types/client/awiki-client-bridge.js +18 -0
  42. package/lib/types/client/awiki-client-bridge.js.map +1 -0
  43. package/lib/types/client/controller.d.ts +38 -2
  44. package/lib/types/client/controller.d.ts.map +1 -1
  45. package/lib/types/client/controller.js +473 -47
  46. package/lib/types/client/controller.js.map +1 -1
  47. package/lib/types/client/index.d.ts +3 -3
  48. package/lib/types/client/index.d.ts.map +1 -1
  49. package/lib/types/client/index.js +7 -46
  50. package/lib/types/client/index.js.map +1 -1
  51. package/lib/types/client/settings-locales.d.ts +2 -5
  52. package/lib/types/client/settings-locales.d.ts.map +1 -1
  53. package/lib/types/client/settings-locales.js +3 -183
  54. package/lib/types/client/settings-locales.js.map +1 -1
  55. package/lib/types/client/slots.d.ts +6 -0
  56. package/lib/types/client/slots.d.ts.map +1 -1
  57. package/lib/types/conversation-preferences.d.ts +20 -0
  58. package/lib/types/conversation-preferences.d.ts.map +1 -0
  59. package/lib/types/conversation-preferences.js +294 -0
  60. package/lib/types/conversation-preferences.js.map +1 -0
  61. package/lib/types/index.d.ts +6 -1
  62. package/lib/types/index.d.ts.map +1 -1
  63. package/lib/types/index.js +39 -0
  64. package/lib/types/index.js.map +1 -1
  65. package/lib/types/sdk-adapter.d.ts.map +1 -1
  66. package/lib/types/sdk-adapter.js +35 -4
  67. package/lib/types/sdk-adapter.js.map +1 -1
  68. package/lib/types/types.d.ts +32 -1
  69. package/lib/types/types.d.ts.map +1 -1
  70. package/lib/types/types.js.map +1 -1
  71. package/package.json +57 -55
  72. package/lib/types/client/AwikiOnboarding.d.ts +0 -22
  73. package/lib/types/client/AwikiOnboarding.d.ts.map +0 -1
  74. package/lib/types/client/AwikiOnboarding.js +0 -104
  75. package/lib/types/client/AwikiOnboarding.js.map +0 -1
  76. package/lib/types/client/RechargeComingSoonDialog.d.ts +0 -11
  77. package/lib/types/client/RechargeComingSoonDialog.d.ts.map +0 -1
  78. package/lib/types/client/RechargeComingSoonDialog.js +0 -8
  79. package/lib/types/client/RechargeComingSoonDialog.js.map +0 -1
  80. package/lib/types/client/model-availability-controller.d.ts +0 -27
  81. package/lib/types/client/model-availability-controller.d.ts.map +0 -1
  82. package/lib/types/client/model-availability-controller.js +0 -111
  83. package/lib/types/client/model-availability-controller.js.map +0 -1
  84. package/lib/types/client/model-proxy-controller.d.ts +0 -45
  85. package/lib/types/client/model-proxy-controller.d.ts.map +0 -1
  86. package/lib/types/client/model-proxy-controller.js +0 -306
  87. package/lib/types/client/model-proxy-controller.js.map +0 -1
  88. package/lib/types/client/recharge-availability.d.ts +0 -6
  89. package/lib/types/client/recharge-availability.d.ts.map +0 -1
  90. package/lib/types/client/recharge-availability.js +0 -6
  91. 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-DLhLrLI6.mjs";
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$1(value) {
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$1(payload) || !Number.isSafeInteger(payload.expectedRevision) || payload.expectedRevision < 0) return;
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$1(value)) return void 0;
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$1(descriptor.value) || typeof descriptor.value.domain !== "string") return;
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$1(payload)) return badRequest();
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$1(payload) || typeof payload.domain !== "string") return badRequest();
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$3(error)) return false;
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$2(error) || !await this.isSignedOut()) throw error;
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$3(error)) throw error;
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$3(error)) return false;
779
+ if (isMissing$4(error)) return false;
780
780
  throw error;
781
781
  }
782
782
  }
783
783
  };
784
- function isMissing$3(error) {
784
+ function isMissing$4(error) {
785
785
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
786
786
  }
787
- function isFileExists$2(error) {
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$2(error)) throw error;
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$1(error)) throw error;
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$2(error)) return false;
944
+ if (isMissing$3(error)) return false;
945
945
  throw error;
946
946
  }
947
947
  }
948
948
  };
949
- function isMissing$2(error) {
949
+ function isMissing$3(error) {
950
950
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
951
951
  }
952
- function isFileExists$1(error) {
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$1(error) {
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$1(error)) throw error;
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$1(error)) return [];
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-DLhLrLI6.mjs";
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. */