@awiki/dsh-plugin 0.2.5 → 0.3.0-rc.1
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 +63 -23
- package/README.zh.md +55 -20
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/cordis.patch.yml +0 -8
- package/lib/client.js +5452 -1849
- package/lib/client.js.map +1 -1
- package/lib/index.js +950 -38
- package/lib/model-proxy-contract.js +8 -1
- package/lib/provider.js +3 -1
- package/lib/{sdk-adapter-DKgkyApB.mjs → sdk-adapter-DLhLrLI6.mjs} +219 -2
- package/lib/typert.host.js +796 -49
- package/lib/typert.remote-client.d.ts +33 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +796 -49
- package/lib/types/client/AwikiGroupDetails.d.ts +16 -0
- package/lib/types/client/AwikiGroupDetails.d.ts.map +1 -0
- package/lib/types/client/AwikiGroupDetails.js +108 -0
- package/lib/types/client/AwikiGroupDetails.js.map +1 -0
- package/lib/types/client/AwikiIdentityAccess.d.ts +21 -0
- package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -0
- package/lib/types/client/AwikiIdentityAccess.js +148 -0
- package/lib/types/client/AwikiIdentityAccess.js.map +1 -0
- package/lib/types/client/AwikiIdentityPage.d.ts +11 -0
- package/lib/types/client/AwikiIdentityPage.d.ts.map +1 -0
- package/lib/types/client/AwikiIdentityPage.js +8 -0
- package/lib/types/client/AwikiIdentityPage.js.map +1 -0
- package/lib/types/client/AwikiMail.d.ts +2 -1
- package/lib/types/client/AwikiMail.d.ts.map +1 -1
- package/lib/types/client/AwikiMail.js +153 -33
- package/lib/types/client/AwikiMail.js.map +1 -1
- package/lib/types/client/AwikiOnboarding.d.ts.map +1 -1
- package/lib/types/client/AwikiOnboarding.js +26 -9
- package/lib/types/client/AwikiOnboarding.js.map +1 -1
- package/lib/types/client/AwikiOverlay.d.ts +0 -5
- package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
- package/lib/types/client/AwikiOverlay.js +136 -124
- package/lib/types/client/AwikiOverlay.js.map +1 -1
- package/lib/types/client/AwikiProfileCard.d.ts +9 -0
- package/lib/types/client/AwikiProfileCard.d.ts.map +1 -0
- package/lib/types/client/AwikiProfileCard.js +90 -0
- package/lib/types/client/AwikiProfileCard.js.map +1 -0
- package/lib/types/client/AwikiRecoveryForm.d.ts +24 -0
- package/lib/types/client/AwikiRecoveryForm.d.ts.map +1 -0
- package/lib/types/client/AwikiRecoveryForm.js +148 -0
- package/lib/types/client/AwikiRecoveryForm.js.map +1 -0
- package/lib/types/client/AwikiSettingsSection.d.ts +0 -2
- package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
- package/lib/types/client/AwikiSettingsSection.js +13 -15
- package/lib/types/client/AwikiSettingsSection.js.map +1 -1
- package/lib/types/client/MentionText.d.ts +7 -0
- package/lib/types/client/MentionText.d.ts.map +1 -0
- package/lib/types/client/MentionText.js +10 -0
- package/lib/types/client/MentionText.js.map +1 -0
- package/lib/types/client/controller.d.ts +94 -2
- package/lib/types/client/controller.d.ts.map +1 -1
- package/lib/types/client/controller.js +466 -13
- package/lib/types/client/controller.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +21 -4
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/mail-list-cache.d.ts +15 -0
- package/lib/types/client/mail-list-cache.d.ts.map +1 -0
- package/lib/types/client/mail-list-cache.js +212 -0
- package/lib/types/client/mail-list-cache.js.map +1 -0
- package/lib/types/client/mentions.d.ts +51 -0
- package/lib/types/client/mentions.d.ts.map +1 -0
- package/lib/types/client/mentions.js +165 -0
- package/lib/types/client/mentions.js.map +1 -0
- package/lib/types/client/model-proxy-controller.d.ts +4 -0
- package/lib/types/client/model-proxy-controller.d.ts.map +1 -1
- package/lib/types/client/model-proxy-controller.js +68 -3
- package/lib/types/client/model-proxy-controller.js.map +1 -1
- package/lib/types/client/settings-locales.js +8 -8
- package/lib/types/client/settings-locales.js.map +1 -1
- package/lib/types/client/slots.d.ts +21 -2
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/index.d.ts +40 -6
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +523 -23
- package/lib/types/index.js.map +1 -1
- package/lib/types/model-proxy-contract.d.ts +6 -0
- package/lib/types/model-proxy-contract.d.ts.map +1 -1
- package/lib/types/model-proxy-contract.js +6 -0
- package/lib/types/model-proxy-contract.js.map +1 -1
- package/lib/types/provider-api.d.ts +29 -1
- package/lib/types/provider-api.d.ts.map +1 -1
- package/lib/types/provider.d.ts.map +1 -1
- package/lib/types/provider.js +2 -0
- package/lib/types/provider.js.map +1 -1
- package/lib/types/sdk-adapter.d.ts +18 -1
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +244 -2
- package/lib/types/sdk-adapter.js.map +1 -1
- package/lib/types/sent-mail-store.d.ts +22 -0
- package/lib/types/sent-mail-store.d.ts.map +1 -0
- package/lib/types/sent-mail-store.js +246 -0
- package/lib/types/sent-mail-store.js.map +1 -0
- package/lib/types/types.d.ts +152 -2
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/package.json +15 -26
- package/lib/model-proxy.js +0 -383
- package/lib/types/client/model-account-settings-availability.d.ts +0 -4
- package/lib/types/client/model-account-settings-availability.d.ts.map +0 -1
- package/lib/types/client/model-account-settings-availability.js +0 -4
- package/lib/types/client/model-account-settings-availability.js.map +0 -1
- package/lib/types/model-proxy.d.ts +0 -14
- package/lib/types/model-proxy.d.ts.map +0 -1
- package/lib/types/model-proxy.js +0 -371
- package/lib/types/model-proxy.js.map +0 -1
package/lib/types/index.js
CHANGED
|
@@ -50,6 +50,7 @@ import { AWIKI_SETTINGS_RPC_CHANNEL } from "./settings-rpc-contract.js";
|
|
|
50
50
|
import { createAwikiSettingsRpcHandler } from "./settings-rpc.js";
|
|
51
51
|
import { AwikiSessionStore } from "./session.js";
|
|
52
52
|
import { AwikiImageAttachmentCache, minimumImageAttachmentCacheMaxBytes } from "./attachment-cache.js";
|
|
53
|
+
import { AwikiSentMailStore, isLocalSentMailId } from "./sent-mail-store.js";
|
|
53
54
|
import { AwikiAgentListener, DshAwikiListenerAgentRuntime, } from "./listener.js";
|
|
54
55
|
export { AWIKI_CLEAR_LOCAL_DATA_CONFIRMATION, AWIKI_LOGOUT_CONFIRMATION } from "./types.js";
|
|
55
56
|
export { AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES, AwikiExternalHttpAuthError, } from "./external-http-auth.js";
|
|
@@ -100,6 +101,8 @@ const FAILURE_CODES = new Set([
|
|
|
100
101
|
'forbidden',
|
|
101
102
|
'conflict',
|
|
102
103
|
'rate-limited',
|
|
104
|
+
'group-membership-required',
|
|
105
|
+
'group-identity-stale',
|
|
103
106
|
'attachment-too-large',
|
|
104
107
|
'summary-unavailable',
|
|
105
108
|
'summary-timeout',
|
|
@@ -126,6 +129,8 @@ const FAILURE_MESSAGES = {
|
|
|
126
129
|
'forbidden': 'The AWiki operation is not permitted.',
|
|
127
130
|
'conflict': 'The AWiki operation conflicts with current state.',
|
|
128
131
|
'rate-limited': 'The AWiki service rate-limited the request.',
|
|
132
|
+
'group-membership-required': 'The active AWiki identity is not a member of this group.',
|
|
133
|
+
'group-identity-stale': 'The AWiki group identity binding is still recovering.',
|
|
129
134
|
'attachment-too-large': 'The attachment exceeds this deployment\'s size limit.',
|
|
130
135
|
'summary-unavailable': 'AI summary is unavailable. Check the current default model configuration.',
|
|
131
136
|
'summary-timeout': 'AI summary timed out. Try again.',
|
|
@@ -294,13 +299,18 @@ function normalizeFailure(error) {
|
|
|
294
299
|
const MAX_GROUP_NAME_CHARACTERS = 100;
|
|
295
300
|
const MAX_GROUP_INITIAL_MEMBERS = 50;
|
|
296
301
|
const MAX_GROUP_MEMBER_CHARACTERS = 512;
|
|
302
|
+
const MAX_PROFILE_DISPLAY_NAME_CHARACTERS = 50;
|
|
303
|
+
const MAX_PROFILE_BIO_CHARACTERS = 100;
|
|
304
|
+
const MAX_PROFILE_TAGS = 5;
|
|
305
|
+
const MAX_PROFILE_TAG_CHARACTERS = 30;
|
|
306
|
+
const MAX_MESSAGE_CHARACTERS = 20_000;
|
|
297
307
|
function normalizeCreateGroupRequest(request) {
|
|
298
308
|
if (typeof request?.name !== 'string' || !Array.isArray(request.members))
|
|
299
309
|
return undefined;
|
|
300
310
|
const name = request.name.trim();
|
|
301
311
|
if (name.length === 0 || Array.from(name).length > MAX_GROUP_NAME_CHARACTERS)
|
|
302
312
|
return undefined;
|
|
303
|
-
if (request.members.length
|
|
313
|
+
if (request.members.length > MAX_GROUP_INITIAL_MEMBERS)
|
|
304
314
|
return undefined;
|
|
305
315
|
const members = [];
|
|
306
316
|
const seen = new Set();
|
|
@@ -315,7 +325,178 @@ function normalizeCreateGroupRequest(request) {
|
|
|
315
325
|
seen.add(member);
|
|
316
326
|
members.push(member);
|
|
317
327
|
}
|
|
318
|
-
return
|
|
328
|
+
return { name, members };
|
|
329
|
+
}
|
|
330
|
+
function normalizeMember(value) {
|
|
331
|
+
if (typeof value !== 'string')
|
|
332
|
+
return undefined;
|
|
333
|
+
const member = value.trim().replace(/^@+/u, '');
|
|
334
|
+
return member.length > 0 && Array.from(member).length <= MAX_GROUP_MEMBER_CHARACTERS
|
|
335
|
+
? member
|
|
336
|
+
: undefined;
|
|
337
|
+
}
|
|
338
|
+
function normalizeGroupDid(value) {
|
|
339
|
+
if (typeof value !== 'string' || !value.startsWith('did:') || value.length > 2_048)
|
|
340
|
+
return undefined;
|
|
341
|
+
return value;
|
|
342
|
+
}
|
|
343
|
+
/** Rebuild the browser-safe recovery DTO instead of trusting provider object shape. */
|
|
344
|
+
function publicGroupRebindRecoverySummary(value) {
|
|
345
|
+
if (typeof value !== 'object' || value === null)
|
|
346
|
+
return undefined;
|
|
347
|
+
const candidate = value;
|
|
348
|
+
const counts = [candidate.processed, candidate.completed, candidate.pending, candidate.blocked];
|
|
349
|
+
if (counts.some(count => !Number.isSafeInteger(count) || count < 0 || count > 0xffff_ffff)) {
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
processed: candidate.processed,
|
|
354
|
+
completed: candidate.completed,
|
|
355
|
+
pending: candidate.pending,
|
|
356
|
+
blocked: candidate.blocked,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function normalizeProfileRequest(request) {
|
|
360
|
+
if (typeof request?.displayName !== 'string' || typeof request.bio !== 'string' || !Array.isArray(request.tags))
|
|
361
|
+
return undefined;
|
|
362
|
+
const displayName = request.displayName.trim();
|
|
363
|
+
const bio = request.bio.trim();
|
|
364
|
+
if (displayName.length === 0
|
|
365
|
+
|| Array.from(displayName).length > MAX_PROFILE_DISPLAY_NAME_CHARACTERS
|
|
366
|
+
|| Array.from(bio).length > MAX_PROFILE_BIO_CHARACTERS
|
|
367
|
+
|| request.tags.length > MAX_PROFILE_TAGS)
|
|
368
|
+
return undefined;
|
|
369
|
+
const tags = [];
|
|
370
|
+
const seen = new Set();
|
|
371
|
+
for (const raw of request.tags) {
|
|
372
|
+
if (typeof raw !== 'string')
|
|
373
|
+
return undefined;
|
|
374
|
+
const tag = raw.trim();
|
|
375
|
+
const key = tag.toLocaleLowerCase();
|
|
376
|
+
if (tag.length === 0 || Array.from(tag).length > MAX_PROFILE_TAG_CHARACTERS || seen.has(key))
|
|
377
|
+
return undefined;
|
|
378
|
+
seen.add(key);
|
|
379
|
+
tags.push(tag);
|
|
380
|
+
}
|
|
381
|
+
return { displayName, bio, tags };
|
|
382
|
+
}
|
|
383
|
+
function normalizeRecoveryOperation(request) {
|
|
384
|
+
if (typeof request?.operationId !== 'string')
|
|
385
|
+
return undefined;
|
|
386
|
+
const operationId = request.operationId.trim();
|
|
387
|
+
return operationId.length > 0 && operationId.length <= 512 ? { operationId } : undefined;
|
|
388
|
+
}
|
|
389
|
+
function normalizeRecoveryOtpRequest(request) {
|
|
390
|
+
if (typeof request?.fullHandle !== 'string' || typeof request.phone !== 'string')
|
|
391
|
+
return undefined;
|
|
392
|
+
const fullHandle = request.fullHandle.trim().replace(/^@+/u, '');
|
|
393
|
+
const phone = request.phone.trim();
|
|
394
|
+
if (fullHandle.length === 0 || fullHandle.length > 512 || phone.length < 5 || phone.length > 32)
|
|
395
|
+
return undefined;
|
|
396
|
+
return { fullHandle, phone };
|
|
397
|
+
}
|
|
398
|
+
function normalizeRecoveryPrepareRequest(request) {
|
|
399
|
+
const operation = normalizeRecoveryOperation(request);
|
|
400
|
+
if (operation === undefined || typeof request.phone !== 'string' || typeof request.otp !== 'string')
|
|
401
|
+
return undefined;
|
|
402
|
+
const phone = request.phone.trim();
|
|
403
|
+
const otp = request.otp.trim();
|
|
404
|
+
if (phone.length < 5 || phone.length > 32 || !/^\d{4,12}$/u.test(otp))
|
|
405
|
+
return undefined;
|
|
406
|
+
return { ...operation, phone, otp };
|
|
407
|
+
}
|
|
408
|
+
const IDENTITY_ACCESS_RESPONSE_MAX_BYTES = 64 * 1024;
|
|
409
|
+
/** Read one untrusted discovery response without buffering beyond the fixed Host limit. */
|
|
410
|
+
async function readBoundedResponseText(response, maxBytes) {
|
|
411
|
+
const declaredLength = response.headers.get('content-length');
|
|
412
|
+
if (declaredLength !== null && /^\d+$/u.test(declaredLength) && Number(declaredLength) > maxBytes)
|
|
413
|
+
return undefined;
|
|
414
|
+
if (response.body === null)
|
|
415
|
+
return '';
|
|
416
|
+
const reader = response.body.getReader();
|
|
417
|
+
const chunks = [];
|
|
418
|
+
let length = 0;
|
|
419
|
+
try {
|
|
420
|
+
while (true) {
|
|
421
|
+
const result = await reader.read();
|
|
422
|
+
if (result.done)
|
|
423
|
+
break;
|
|
424
|
+
length += result.value.byteLength;
|
|
425
|
+
if (length > maxBytes) {
|
|
426
|
+
await reader.cancel();
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
chunks.push(Uint8Array.from(result.value));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
finally {
|
|
436
|
+
reader.releaseLock();
|
|
437
|
+
}
|
|
438
|
+
const bytes = new Uint8Array(length);
|
|
439
|
+
let offset = 0;
|
|
440
|
+
for (const chunk of chunks) {
|
|
441
|
+
bytes.set(chunk, offset);
|
|
442
|
+
offset += chunk.byteLength;
|
|
443
|
+
}
|
|
444
|
+
return Buffer.from(bytes).toString('utf8');
|
|
445
|
+
}
|
|
446
|
+
/** Resolve one configured-domain Handle without widening registration authority. */
|
|
447
|
+
function identityAccessTarget(request, configuredDomain) {
|
|
448
|
+
if (typeof request?.handle !== 'string')
|
|
449
|
+
return undefined;
|
|
450
|
+
const raw = request.handle.trim();
|
|
451
|
+
if (raw.length === 0 || raw.length > 255 || /[\u0000-\u001f\u007f]/u.test(raw))
|
|
452
|
+
return undefined;
|
|
453
|
+
const lowered = raw.replace(/[A-Z]/gu, character => character.toLowerCase());
|
|
454
|
+
const handle = lowered.startsWith('wba://') ? lowered.slice('wba://'.length) : lowered;
|
|
455
|
+
const dot = handle.indexOf('.');
|
|
456
|
+
const localPart = (dot < 0 ? handle : handle.slice(0, dot)).trim();
|
|
457
|
+
const domain = (dot < 0 ? configuredDomain : handle.slice(dot + 1)).trim().replace(/\.$/u, '');
|
|
458
|
+
if (localPart.length === 0 || domain !== configuredDomain)
|
|
459
|
+
return undefined;
|
|
460
|
+
return { localPart, fullHandle: `${localPart}.${configuredDomain}` };
|
|
461
|
+
}
|
|
462
|
+
function normalizeSendTextRequest(request) {
|
|
463
|
+
if (typeof request?.text !== 'string' || typeof request.idempotencyKey !== 'string'
|
|
464
|
+
|| typeof request.target !== 'object' || request.target === null)
|
|
465
|
+
return undefined;
|
|
466
|
+
const text = request.text;
|
|
467
|
+
const codePoints = Array.from(text);
|
|
468
|
+
if (text.trim().length === 0 || codePoints.length > MAX_MESSAGE_CHARACTERS
|
|
469
|
+
|| request.idempotencyKey.length === 0 || request.idempotencyKey.length > 512)
|
|
470
|
+
return undefined;
|
|
471
|
+
if (request.target.kind === 'direct') {
|
|
472
|
+
if (typeof request.target.peer !== 'string' || request.mentions !== undefined)
|
|
473
|
+
return undefined;
|
|
474
|
+
}
|
|
475
|
+
else if (request.target.kind === 'group') {
|
|
476
|
+
if (typeof request.target.group !== 'string')
|
|
477
|
+
return undefined;
|
|
478
|
+
}
|
|
479
|
+
else
|
|
480
|
+
return undefined;
|
|
481
|
+
if (request.mentions === undefined)
|
|
482
|
+
return request;
|
|
483
|
+
if (!Array.isArray(request.mentions) || request.mentions.length === 0 || request.mentions.length > 100)
|
|
484
|
+
return undefined;
|
|
485
|
+
const ids = new Set();
|
|
486
|
+
let previousEnd = 0;
|
|
487
|
+
const mentions = [...request.mentions].sort((left, right) => left.start - right.start || left.end - right.end);
|
|
488
|
+
for (const mention of mentions) {
|
|
489
|
+
if (typeof mention !== 'object' || mention === null
|
|
490
|
+
|| typeof mention.id !== 'string' || mention.id.trim() === '' || ids.has(mention.id)
|
|
491
|
+
|| !Number.isSafeInteger(mention.start) || !Number.isSafeInteger(mention.end)
|
|
492
|
+
|| mention.start < previousEnd || mention.start < 0 || mention.end <= mention.start || mention.end > codePoints.length
|
|
493
|
+
|| typeof mention.did !== 'string' || !mention.did.startsWith('did:')
|
|
494
|
+
|| (mention.displayName !== undefined && typeof mention.displayName !== 'string'))
|
|
495
|
+
return undefined;
|
|
496
|
+
ids.add(mention.id);
|
|
497
|
+
previousEnd = mention.end;
|
|
498
|
+
}
|
|
499
|
+
return { ...request, mentions };
|
|
319
500
|
}
|
|
320
501
|
/** Normalize summary-provider failures without returning prompts, model output, routes, or causes. */
|
|
321
502
|
function normalizeSummaryFailure(error) {
|
|
@@ -450,11 +631,27 @@ let AwikiService = (() => {
|
|
|
450
631
|
let _getSession_decorators;
|
|
451
632
|
let _logout_decorators;
|
|
452
633
|
let _login_decorators;
|
|
634
|
+
let _inspectIdentityAccess_decorators;
|
|
453
635
|
let _sendRegistrationOtp_decorators;
|
|
454
636
|
let _registerIdentity_decorators;
|
|
455
637
|
let _updateDisplayName_decorators;
|
|
638
|
+
let _getProfile_decorators;
|
|
639
|
+
let _updateProfile_decorators;
|
|
640
|
+
let _sendRecoveryOtp_decorators;
|
|
641
|
+
let _prepareRecovery_decorators;
|
|
642
|
+
let _activateRecovery_decorators;
|
|
643
|
+
let _getRecoveryStatus_decorators;
|
|
644
|
+
let _resumeRecovery_decorators;
|
|
645
|
+
let _discardRecovery_decorators;
|
|
456
646
|
let _resolvePeer_decorators;
|
|
457
647
|
let _createGroup_decorators;
|
|
648
|
+
let _getGroup_decorators;
|
|
649
|
+
let _joinGroup_decorators;
|
|
650
|
+
let _leaveGroup_decorators;
|
|
651
|
+
let _listGroupMembers_decorators;
|
|
652
|
+
let _addGroupMember_decorators;
|
|
653
|
+
let _removeGroupMember_decorators;
|
|
654
|
+
let _resumeGroupRebindRecovery_decorators;
|
|
458
655
|
let _listConversations_decorators;
|
|
459
656
|
let _getHistory_decorators;
|
|
460
657
|
let _getLocalHistory_decorators;
|
|
@@ -477,11 +674,27 @@ let AwikiService = (() => {
|
|
|
477
674
|
_getSession_decorators = [Remote];
|
|
478
675
|
_logout_decorators = [Remote];
|
|
479
676
|
_login_decorators = [Remote];
|
|
677
|
+
_inspectIdentityAccess_decorators = [Remote];
|
|
480
678
|
_sendRegistrationOtp_decorators = [Remote];
|
|
481
679
|
_registerIdentity_decorators = [Remote];
|
|
482
680
|
_updateDisplayName_decorators = [Remote];
|
|
681
|
+
_getProfile_decorators = [Remote];
|
|
682
|
+
_updateProfile_decorators = [Remote];
|
|
683
|
+
_sendRecoveryOtp_decorators = [Remote];
|
|
684
|
+
_prepareRecovery_decorators = [Remote];
|
|
685
|
+
_activateRecovery_decorators = [Remote];
|
|
686
|
+
_getRecoveryStatus_decorators = [Remote];
|
|
687
|
+
_resumeRecovery_decorators = [Remote];
|
|
688
|
+
_discardRecovery_decorators = [Remote];
|
|
483
689
|
_resolvePeer_decorators = [Remote];
|
|
484
690
|
_createGroup_decorators = [Remote];
|
|
691
|
+
_getGroup_decorators = [Remote];
|
|
692
|
+
_joinGroup_decorators = [Remote];
|
|
693
|
+
_leaveGroup_decorators = [Remote];
|
|
694
|
+
_listGroupMembers_decorators = [Remote];
|
|
695
|
+
_addGroupMember_decorators = [Remote];
|
|
696
|
+
_removeGroupMember_decorators = [Remote];
|
|
697
|
+
_resumeGroupRebindRecovery_decorators = [Remote];
|
|
485
698
|
_listConversations_decorators = [Remote];
|
|
486
699
|
_getHistory_decorators = [Remote];
|
|
487
700
|
_getLocalHistory_decorators = [Remote];
|
|
@@ -501,11 +714,27 @@ let AwikiService = (() => {
|
|
|
501
714
|
__esDecorate(this, null, _getSession_decorators, { kind: "method", name: "getSession", static: false, private: false, access: { has: obj => "getSession" in obj, get: obj => obj.getSession }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
502
715
|
__esDecorate(this, null, _logout_decorators, { kind: "method", name: "logout", static: false, private: false, access: { has: obj => "logout" in obj, get: obj => obj.logout }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
503
716
|
__esDecorate(this, null, _login_decorators, { kind: "method", name: "login", static: false, private: false, access: { has: obj => "login" in obj, get: obj => obj.login }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
717
|
+
__esDecorate(this, null, _inspectIdentityAccess_decorators, { kind: "method", name: "inspectIdentityAccess", static: false, private: false, access: { has: obj => "inspectIdentityAccess" in obj, get: obj => obj.inspectIdentityAccess }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
504
718
|
__esDecorate(this, null, _sendRegistrationOtp_decorators, { kind: "method", name: "sendRegistrationOtp", static: false, private: false, access: { has: obj => "sendRegistrationOtp" in obj, get: obj => obj.sendRegistrationOtp }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
505
719
|
__esDecorate(this, null, _registerIdentity_decorators, { kind: "method", name: "registerIdentity", static: false, private: false, access: { has: obj => "registerIdentity" in obj, get: obj => obj.registerIdentity }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
506
720
|
__esDecorate(this, null, _updateDisplayName_decorators, { kind: "method", name: "updateDisplayName", static: false, private: false, access: { has: obj => "updateDisplayName" in obj, get: obj => obj.updateDisplayName }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
721
|
+
__esDecorate(this, null, _getProfile_decorators, { kind: "method", name: "getProfile", static: false, private: false, access: { has: obj => "getProfile" in obj, get: obj => obj.getProfile }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
722
|
+
__esDecorate(this, null, _updateProfile_decorators, { kind: "method", name: "updateProfile", static: false, private: false, access: { has: obj => "updateProfile" in obj, get: obj => obj.updateProfile }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
723
|
+
__esDecorate(this, null, _sendRecoveryOtp_decorators, { kind: "method", name: "sendRecoveryOtp", static: false, private: false, access: { has: obj => "sendRecoveryOtp" in obj, get: obj => obj.sendRecoveryOtp }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
724
|
+
__esDecorate(this, null, _prepareRecovery_decorators, { kind: "method", name: "prepareRecovery", static: false, private: false, access: { has: obj => "prepareRecovery" in obj, get: obj => obj.prepareRecovery }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
725
|
+
__esDecorate(this, null, _activateRecovery_decorators, { kind: "method", name: "activateRecovery", static: false, private: false, access: { has: obj => "activateRecovery" in obj, get: obj => obj.activateRecovery }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
726
|
+
__esDecorate(this, null, _getRecoveryStatus_decorators, { kind: "method", name: "getRecoveryStatus", static: false, private: false, access: { has: obj => "getRecoveryStatus" in obj, get: obj => obj.getRecoveryStatus }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
727
|
+
__esDecorate(this, null, _resumeRecovery_decorators, { kind: "method", name: "resumeRecovery", static: false, private: false, access: { has: obj => "resumeRecovery" in obj, get: obj => obj.resumeRecovery }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
728
|
+
__esDecorate(this, null, _discardRecovery_decorators, { kind: "method", name: "discardRecovery", static: false, private: false, access: { has: obj => "discardRecovery" in obj, get: obj => obj.discardRecovery }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
507
729
|
__esDecorate(this, null, _resolvePeer_decorators, { kind: "method", name: "resolvePeer", static: false, private: false, access: { has: obj => "resolvePeer" in obj, get: obj => obj.resolvePeer }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
508
730
|
__esDecorate(this, null, _createGroup_decorators, { kind: "method", name: "createGroup", static: false, private: false, access: { has: obj => "createGroup" in obj, get: obj => obj.createGroup }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
731
|
+
__esDecorate(this, null, _getGroup_decorators, { kind: "method", name: "getGroup", static: false, private: false, access: { has: obj => "getGroup" in obj, get: obj => obj.getGroup }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
732
|
+
__esDecorate(this, null, _joinGroup_decorators, { kind: "method", name: "joinGroup", static: false, private: false, access: { has: obj => "joinGroup" in obj, get: obj => obj.joinGroup }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
733
|
+
__esDecorate(this, null, _leaveGroup_decorators, { kind: "method", name: "leaveGroup", static: false, private: false, access: { has: obj => "leaveGroup" in obj, get: obj => obj.leaveGroup }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
734
|
+
__esDecorate(this, null, _listGroupMembers_decorators, { kind: "method", name: "listGroupMembers", static: false, private: false, access: { has: obj => "listGroupMembers" in obj, get: obj => obj.listGroupMembers }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
735
|
+
__esDecorate(this, null, _addGroupMember_decorators, { kind: "method", name: "addGroupMember", static: false, private: false, access: { has: obj => "addGroupMember" in obj, get: obj => obj.addGroupMember }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
736
|
+
__esDecorate(this, null, _removeGroupMember_decorators, { kind: "method", name: "removeGroupMember", static: false, private: false, access: { has: obj => "removeGroupMember" in obj, get: obj => obj.removeGroupMember }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
737
|
+
__esDecorate(this, null, _resumeGroupRebindRecovery_decorators, { kind: "method", name: "resumeGroupRebindRecovery", static: false, private: false, access: { has: obj => "resumeGroupRebindRecovery" in obj, get: obj => obj.resumeGroupRebindRecovery }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
509
738
|
__esDecorate(this, null, _listConversations_decorators, { kind: "method", name: "listConversations", static: false, private: false, access: { has: obj => "listConversations" in obj, get: obj => obj.listConversations }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
510
739
|
__esDecorate(this, null, _getHistory_decorators, { kind: "method", name: "getHistory", static: false, private: false, access: { has: obj => "getHistory" in obj, get: obj => obj.getHistory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
511
740
|
__esDecorate(this, null, _getLocalHistory_decorators, { kind: "method", name: "getLocalHistory", static: false, private: false, access: { has: obj => "getLocalHistory" in obj, get: obj => obj.getLocalHistory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -527,6 +756,7 @@ let AwikiService = (() => {
|
|
|
527
756
|
resolved = __runInitializers(this, _instanceExtraInitializers);
|
|
528
757
|
sessionStore;
|
|
529
758
|
imageAttachmentCache;
|
|
759
|
+
sentMailStore;
|
|
530
760
|
startupUserServiceDomain;
|
|
531
761
|
settingsProvider;
|
|
532
762
|
provider;
|
|
@@ -551,6 +781,7 @@ let AwikiService = (() => {
|
|
|
551
781
|
this.externalHttpAuth = createAwikiExternalHttpAuth(() => this.acquireExternalHttpAuthSession());
|
|
552
782
|
this.sessionStore = new AwikiSessionStore(this.resolved.stateRoot);
|
|
553
783
|
this.imageAttachmentCache = new AwikiImageAttachmentCache(this.resolved.stateRoot, this.resolved.attachmentMaxBytes, this.resolved.imageAttachmentCacheMaxBytes);
|
|
784
|
+
this.sentMailStore = new AwikiSentMailStore(this.resolved.stateRoot);
|
|
554
785
|
this.startupUserServiceDomain = this.resolved.userServiceDomain;
|
|
555
786
|
ctx.inject(['settings'], (settingsCtx) => {
|
|
556
787
|
const provider = settingsCtx.settings;
|
|
@@ -733,11 +964,56 @@ let AwikiService = (() => {
|
|
|
733
964
|
}
|
|
734
965
|
});
|
|
735
966
|
}
|
|
736
|
-
/**
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
967
|
+
/** Classify one configured-domain Handle before selecting the registration or recovery OTP purpose. */
|
|
968
|
+
async inspectIdentityAccess(request) {
|
|
969
|
+
const target = identityAccessTarget(request, this.startupUserServiceDomain);
|
|
970
|
+
if (target === undefined)
|
|
971
|
+
return { ok: false, error: failure('invalid-request') };
|
|
972
|
+
const endpoint = new URL(`/.well-known/handle/${encodeURIComponent(target.localPart)}`, this.resolved.userServiceUrl);
|
|
973
|
+
const abort = new AbortController();
|
|
974
|
+
const timeout = setTimeout(() => { abort.abort(); }, 10_000);
|
|
975
|
+
let response;
|
|
976
|
+
try {
|
|
977
|
+
response = await fetch(endpoint, {
|
|
978
|
+
method: 'GET',
|
|
979
|
+
headers: { accept: 'application/json', 'cache-control': 'no-store' },
|
|
980
|
+
cache: 'no-store',
|
|
981
|
+
redirect: 'error',
|
|
982
|
+
signal: abort.signal,
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
catch {
|
|
986
|
+
return { ok: false, error: failure('network') };
|
|
987
|
+
}
|
|
988
|
+
finally {
|
|
989
|
+
clearTimeout(timeout);
|
|
990
|
+
}
|
|
991
|
+
if (response.status === 404) {
|
|
992
|
+
return { ok: true, value: { status: 'available', fullHandle: target.fullHandle } };
|
|
993
|
+
}
|
|
994
|
+
if (!response.ok)
|
|
995
|
+
return { ok: false, error: failure('remote') };
|
|
996
|
+
try {
|
|
997
|
+
const text = await readBoundedResponseText(response, IDENTITY_ACCESS_RESPONSE_MAX_BYTES);
|
|
998
|
+
if (text === undefined)
|
|
999
|
+
return { ok: false, error: failure('remote') };
|
|
1000
|
+
const value = JSON.parse(text);
|
|
1001
|
+
if (typeof value !== 'object' || value === null)
|
|
1002
|
+
return { ok: false, error: failure('remote') };
|
|
1003
|
+
const binding = value;
|
|
1004
|
+
if (binding.handle !== target.fullHandle
|
|
1005
|
+
|| typeof binding.did !== 'string'
|
|
1006
|
+
|| !binding.did.startsWith('did:')
|
|
1007
|
+
|| typeof binding.status !== 'string'
|
|
1008
|
+
|| binding.status.length === 0) {
|
|
1009
|
+
return { ok: false, error: failure('remote') };
|
|
1010
|
+
}
|
|
1011
|
+
return { ok: true, value: { status: 'existing', fullHandle: target.fullHandle } };
|
|
1012
|
+
}
|
|
1013
|
+
catch {
|
|
1014
|
+
return { ok: false, error: failure('remote') };
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
741
1017
|
sendRegistrationOtp(request) {
|
|
742
1018
|
return this.run(client => client.sendRegistrationOtp(request));
|
|
743
1019
|
}
|
|
@@ -748,15 +1024,8 @@ let AwikiService = (() => {
|
|
|
748
1024
|
*/
|
|
749
1025
|
async registerIdentity(request) {
|
|
750
1026
|
const result = await this.run(client => client.registerIdentity(request));
|
|
751
|
-
if (result.ok)
|
|
752
|
-
this.
|
|
753
|
-
this.publishSession({ status: 'active', identity: result.value });
|
|
754
|
-
const provider = this.provider;
|
|
755
|
-
if (provider !== undefined) {
|
|
756
|
-
await provider.listenerStartup;
|
|
757
|
-
await this.startListener(provider);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
1027
|
+
if (result.ok)
|
|
1028
|
+
await this.activateRegisteredIdentity(result.value);
|
|
760
1029
|
return result;
|
|
761
1030
|
}
|
|
762
1031
|
/**
|
|
@@ -770,6 +1039,72 @@ let AwikiService = (() => {
|
|
|
770
1039
|
this.activeIdentityDid = result.value.did;
|
|
771
1040
|
return result;
|
|
772
1041
|
}
|
|
1042
|
+
/** Return the public editable profile for the active identity. */
|
|
1043
|
+
getProfile() {
|
|
1044
|
+
return this.run(client => client.getProfile());
|
|
1045
|
+
}
|
|
1046
|
+
/** Update Display Name, bio, and tags after applying product limits in the Host. */
|
|
1047
|
+
updateProfile(request) {
|
|
1048
|
+
const normalized = normalizeProfileRequest(request);
|
|
1049
|
+
if (normalized === undefined)
|
|
1050
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1051
|
+
return this.run(client => client.updateProfile(normalized));
|
|
1052
|
+
}
|
|
1053
|
+
/** Start durable phone recovery for one existing full Handle. */
|
|
1054
|
+
sendRecoveryOtp(request) {
|
|
1055
|
+
const normalized = normalizeRecoveryOtpRequest(request);
|
|
1056
|
+
if (normalized === undefined)
|
|
1057
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1058
|
+
return this.run(client => client.sendRecoveryOtp(normalized), { allowSignedOut: true });
|
|
1059
|
+
}
|
|
1060
|
+
/** Verify a recovery OTP and freeze its exact intent before the remote commit. */
|
|
1061
|
+
prepareRecovery(request) {
|
|
1062
|
+
const normalized = normalizeRecoveryPrepareRequest(request);
|
|
1063
|
+
if (normalized === undefined)
|
|
1064
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1065
|
+
return this.run(client => client.prepareRecovery(normalized), { allowSignedOut: true });
|
|
1066
|
+
}
|
|
1067
|
+
/** Attempt one prepared recovery commit; uncertain outcomes remain durable in Core. */
|
|
1068
|
+
async activateRecovery(request) {
|
|
1069
|
+
const normalized = normalizeRecoveryOperation(request);
|
|
1070
|
+
if (normalized === undefined)
|
|
1071
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1072
|
+
const result = await this.run(client => client.activateRecovery(normalized), { allowSignedOut: true });
|
|
1073
|
+
if (result.ok && !await this.applyRecoveredSession(result.value)) {
|
|
1074
|
+
return { ok: false, error: failure('remote') };
|
|
1075
|
+
}
|
|
1076
|
+
return result;
|
|
1077
|
+
}
|
|
1078
|
+
/** Read durable recovery state before deciding whether a retry is valid. */
|
|
1079
|
+
async getRecoveryStatus(request) {
|
|
1080
|
+
const normalized = normalizeRecoveryOperation(request);
|
|
1081
|
+
if (normalized === undefined)
|
|
1082
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1083
|
+
const result = await this.run(client => client.getRecoveryStatus(normalized), { allowSignedOut: true });
|
|
1084
|
+
if (result.ok && !await this.applyRecoveredSession(result.value)) {
|
|
1085
|
+
return { ok: false, error: failure('remote') };
|
|
1086
|
+
}
|
|
1087
|
+
return result;
|
|
1088
|
+
}
|
|
1089
|
+
/** Resume only the exact Core-owned operation selected by the browser. */
|
|
1090
|
+
async resumeRecovery(request) {
|
|
1091
|
+
const normalized = normalizeRecoveryOperation(request);
|
|
1092
|
+
if (normalized === undefined)
|
|
1093
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1094
|
+
const result = await this.run(client => client.resumeRecovery(normalized), { allowSignedOut: true });
|
|
1095
|
+
if (result.ok && !await this.applyRecoveredSession(result.value)) {
|
|
1096
|
+
return { ok: false, error: failure('remote') };
|
|
1097
|
+
}
|
|
1098
|
+
return result;
|
|
1099
|
+
}
|
|
1100
|
+
/** Discard only a pre-attempt operation; Core rejects post-attempt deletion. */
|
|
1101
|
+
async discardRecovery(request) {
|
|
1102
|
+
const normalized = normalizeRecoveryOperation(request);
|
|
1103
|
+
if (normalized === undefined)
|
|
1104
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1105
|
+
const result = await this.run(client => client.discardRecovery(normalized), { allowSignedOut: true });
|
|
1106
|
+
return result.ok ? { ok: true, value: { completed: true } } : result;
|
|
1107
|
+
}
|
|
773
1108
|
/**
|
|
774
1109
|
* Resolve one Handle or DID before the browser opens a direct chat.
|
|
775
1110
|
* @param request - typed Handle or DID.
|
|
@@ -804,6 +1139,70 @@ let AwikiService = (() => {
|
|
|
804
1139
|
value: { conversation: created.value, addedMembers, failedMembers },
|
|
805
1140
|
};
|
|
806
1141
|
}
|
|
1142
|
+
/** Return one authoritative group snapshot for permission-aware UI. */
|
|
1143
|
+
getGroup(request) {
|
|
1144
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
1145
|
+
if (groupDid === undefined)
|
|
1146
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1147
|
+
return this.run(client => client.getGroup(groupDid));
|
|
1148
|
+
}
|
|
1149
|
+
/** Join one open group and return its authoritative membership state. */
|
|
1150
|
+
joinGroup(request) {
|
|
1151
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
1152
|
+
if (groupDid === undefined)
|
|
1153
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1154
|
+
return this.run(client => client.joinGroup(groupDid));
|
|
1155
|
+
}
|
|
1156
|
+
/** Leave one group. Core rejects owner leave and unsupported security profiles. */
|
|
1157
|
+
async leaveGroup(request) {
|
|
1158
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
1159
|
+
if (groupDid === undefined)
|
|
1160
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1161
|
+
const result = await this.run(client => client.leaveGroup(groupDid));
|
|
1162
|
+
return result.ok ? { ok: true, value: { completed: true } } : result;
|
|
1163
|
+
}
|
|
1164
|
+
/** Read one authoritative versioned member page. */
|
|
1165
|
+
listGroupMembers(request) {
|
|
1166
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
1167
|
+
if (groupDid === undefined
|
|
1168
|
+
|| (request.cursor !== undefined && (typeof request.cursor !== 'string' || request.cursor.length > 4_096))
|
|
1169
|
+
|| (request.limit !== undefined && (!Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > 100))) {
|
|
1170
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1171
|
+
}
|
|
1172
|
+
return this.run(client => client.listGroupMembers({
|
|
1173
|
+
groupDid,
|
|
1174
|
+
...request.cursor === undefined ? {} : { cursor: request.cursor },
|
|
1175
|
+
...request.limit === undefined ? {} : { limit: request.limit },
|
|
1176
|
+
}));
|
|
1177
|
+
}
|
|
1178
|
+
/** Invite one ordinary member after group creation. */
|
|
1179
|
+
addGroupMember(request) {
|
|
1180
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
1181
|
+
const member = normalizeMember(request?.member);
|
|
1182
|
+
if (groupDid === undefined || member === undefined || (request.role !== undefined && request.role !== 'member')) {
|
|
1183
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1184
|
+
}
|
|
1185
|
+
return this.run(client => client.addGroupMember(groupDid, member));
|
|
1186
|
+
}
|
|
1187
|
+
/** Remove one member. The authoritative Core role check remains decisive. */
|
|
1188
|
+
removeGroupMember(request) {
|
|
1189
|
+
const groupDid = normalizeGroupDid(request?.groupDid);
|
|
1190
|
+
const member = normalizeMember(request?.member);
|
|
1191
|
+
if (groupDid === undefined || member === undefined) {
|
|
1192
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1193
|
+
}
|
|
1194
|
+
return this.run(client => client.removeGroupMember(groupDid, member));
|
|
1195
|
+
}
|
|
1196
|
+
/** Resume durable group membership convergence after account sync restores old groups. */
|
|
1197
|
+
async resumeGroupRebindRecovery() {
|
|
1198
|
+
const result = await this.run(client => client.resumeGroupRebindRecovery());
|
|
1199
|
+
if (!result.ok)
|
|
1200
|
+
return result;
|
|
1201
|
+
const summary = publicGroupRebindRecoverySummary(result.value);
|
|
1202
|
+
return summary === undefined
|
|
1203
|
+
? { ok: false, error: failure('remote') }
|
|
1204
|
+
: { ok: true, value: summary };
|
|
1205
|
+
}
|
|
807
1206
|
/**
|
|
808
1207
|
* List direct and existing group conversations.
|
|
809
1208
|
* @param request - Optional opaque cursor and page limit.
|
|
@@ -928,7 +1327,10 @@ let AwikiService = (() => {
|
|
|
928
1327
|
* @returns The accepted public message or a closed failure.
|
|
929
1328
|
*/
|
|
930
1329
|
sendText(request) {
|
|
931
|
-
|
|
1330
|
+
const normalized = normalizeSendTextRequest(request);
|
|
1331
|
+
if (normalized === undefined)
|
|
1332
|
+
return Promise.resolve({ ok: false, error: failure('invalid-request') });
|
|
1333
|
+
return this.run(client => client.sendText(normalized));
|
|
932
1334
|
}
|
|
933
1335
|
/**
|
|
934
1336
|
* Upload and send one attachment after Host validation.
|
|
@@ -1013,20 +1415,68 @@ let AwikiService = (() => {
|
|
|
1013
1415
|
return this.run(client => client.getMailAccount());
|
|
1014
1416
|
}
|
|
1015
1417
|
/** List one bounded mailbox page on explicit browser/tool demand. */
|
|
1016
|
-
listMailInbox(request) {
|
|
1017
|
-
|
|
1418
|
+
async listMailInbox(request) {
|
|
1419
|
+
let normalized;
|
|
1420
|
+
try {
|
|
1421
|
+
normalized = mailInboxRequest(request ?? {});
|
|
1422
|
+
}
|
|
1423
|
+
catch {
|
|
1424
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1425
|
+
}
|
|
1426
|
+
return this.run(async (client) => {
|
|
1427
|
+
if (normalized.folder !== 'sent')
|
|
1428
|
+
return client.listMailInbox(normalized);
|
|
1429
|
+
return this.sentMailStore.list(await this.ownerDid(client), normalized);
|
|
1430
|
+
});
|
|
1018
1431
|
}
|
|
1019
1432
|
/** Read one bounded plain-text mail message. */
|
|
1020
|
-
readMail(request) {
|
|
1021
|
-
|
|
1433
|
+
async readMail(request) {
|
|
1434
|
+
let normalized;
|
|
1435
|
+
try {
|
|
1436
|
+
normalized = mailReadRequest(request);
|
|
1437
|
+
}
|
|
1438
|
+
catch {
|
|
1439
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1440
|
+
}
|
|
1441
|
+
return this.run(async (client) => {
|
|
1442
|
+
if (!isLocalSentMailId(normalized.messageId))
|
|
1443
|
+
return client.readMail(normalized);
|
|
1444
|
+
const local = await this.sentMailStore.read(await this.ownerDid(client), normalized.messageId);
|
|
1445
|
+
if (local === undefined) {
|
|
1446
|
+
throw Object.assign(new Error('sent mail not found'), { name: 'AwikiSdkError', code: 'not-found' });
|
|
1447
|
+
}
|
|
1448
|
+
return local;
|
|
1449
|
+
});
|
|
1022
1450
|
}
|
|
1023
1451
|
/** Mark explicitly selected mail messages read. Browser callers require an explicit click. */
|
|
1024
1452
|
markMailRead(request) {
|
|
1025
1453
|
return this.runValidatedMail(() => mailMarkReadRequest(request), (client, normalized) => client.markMailRead(normalized));
|
|
1026
1454
|
}
|
|
1027
1455
|
/** Send one plain-text mail once. Browser callers require an explicit confirmation. */
|
|
1028
|
-
sendMail(request) {
|
|
1029
|
-
|
|
1456
|
+
async sendMail(request) {
|
|
1457
|
+
let normalized;
|
|
1458
|
+
try {
|
|
1459
|
+
normalized = mailSendRequest(request);
|
|
1460
|
+
}
|
|
1461
|
+
catch {
|
|
1462
|
+
return { ok: false, error: failure('invalid-request') };
|
|
1463
|
+
}
|
|
1464
|
+
return this.run(async (client) => {
|
|
1465
|
+
const result = await client.sendMail(normalized);
|
|
1466
|
+
if (!result.accepted)
|
|
1467
|
+
return result;
|
|
1468
|
+
try {
|
|
1469
|
+
const ownerDid = await this.ownerDid(client);
|
|
1470
|
+
const account = await client.getMailAccount().catch(() => undefined);
|
|
1471
|
+
await this.sentMailStore.append(ownerDid, normalized, result, account);
|
|
1472
|
+
return result;
|
|
1473
|
+
}
|
|
1474
|
+
catch {
|
|
1475
|
+
return result.warnings.length >= 100
|
|
1476
|
+
? result
|
|
1477
|
+
: { ...result, warnings: [...result.warnings, 'Sent history could not be saved locally.'] };
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1030
1480
|
}
|
|
1031
1481
|
/**
|
|
1032
1482
|
* Permanently remove the exact SDK-owned local state after an explicit browser acknowledgement.
|
|
@@ -1050,6 +1500,7 @@ let AwikiService = (() => {
|
|
|
1050
1500
|
}
|
|
1051
1501
|
try {
|
|
1052
1502
|
await this.imageAttachmentCache.clear();
|
|
1503
|
+
await this.sentMailStore.clear();
|
|
1053
1504
|
await this.sessionStore.signIn();
|
|
1054
1505
|
this.signedOut = false;
|
|
1055
1506
|
this.activeIdentityDid = undefined;
|
|
@@ -1064,6 +1515,45 @@ let AwikiService = (() => {
|
|
|
1064
1515
|
}
|
|
1065
1516
|
});
|
|
1066
1517
|
}
|
|
1518
|
+
/** Re-enter only after Core confirms that the exact recovered identity is applied locally. */
|
|
1519
|
+
async applyRecoveredSession(progress) {
|
|
1520
|
+
if (progress.phase !== 'applied')
|
|
1521
|
+
return true;
|
|
1522
|
+
return this.mutateSession(async () => {
|
|
1523
|
+
const provider = this.provider;
|
|
1524
|
+
if (provider === undefined)
|
|
1525
|
+
return false;
|
|
1526
|
+
try {
|
|
1527
|
+
const identity = await provider.client.getIdentity();
|
|
1528
|
+
if (identity === null || identity.did !== progress.currentDid)
|
|
1529
|
+
return false;
|
|
1530
|
+
if (this.signedOut === false && this.activeIdentityDid === identity.did)
|
|
1531
|
+
return true;
|
|
1532
|
+
await this.sessionStore.signIn();
|
|
1533
|
+
this.signedOut = false;
|
|
1534
|
+
this.activeIdentityDid = identity.did;
|
|
1535
|
+
this.invalidateSummaries();
|
|
1536
|
+
const session = { status: 'active', identity };
|
|
1537
|
+
this.publishSession(session);
|
|
1538
|
+
await provider.listenerStartup;
|
|
1539
|
+
await this.startListener(provider);
|
|
1540
|
+
return true;
|
|
1541
|
+
}
|
|
1542
|
+
catch {
|
|
1543
|
+
return false;
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
/** Publish one newly registered identity and start its listener through the existing session path. */
|
|
1548
|
+
async activateRegisteredIdentity(identity) {
|
|
1549
|
+
this.activeIdentityDid = identity.did;
|
|
1550
|
+
this.publishSession({ status: 'active', identity });
|
|
1551
|
+
const provider = this.provider;
|
|
1552
|
+
if (provider !== undefined) {
|
|
1553
|
+
await provider.listenerStartup;
|
|
1554
|
+
await this.startListener(provider);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1067
1557
|
/** Invalidate cached session work and cancel every model request still owned by the old session. */
|
|
1068
1558
|
invalidateSummaries() {
|
|
1069
1559
|
this.sessionRevision += 1;
|
|
@@ -1075,6 +1565,16 @@ let AwikiService = (() => {
|
|
|
1075
1565
|
publishSession(session) {
|
|
1076
1566
|
this.hostContext.emit('awiki/session', session);
|
|
1077
1567
|
}
|
|
1568
|
+
/** Resolve and cache the owner binding required by private Host-side projections. */
|
|
1569
|
+
async ownerDid(client) {
|
|
1570
|
+
const identity = this.activeIdentityDid === undefined ? await client.getIdentity() : undefined;
|
|
1571
|
+
const ownerDid = this.activeIdentityDid ?? identity?.did;
|
|
1572
|
+
if (ownerDid === undefined) {
|
|
1573
|
+
throw Object.assign(new Error('not registered'), { name: 'AwikiSdkError', code: 'not-registered' });
|
|
1574
|
+
}
|
|
1575
|
+
this.activeIdentityDid = ownerDid;
|
|
1576
|
+
return ownerDid;
|
|
1577
|
+
}
|
|
1078
1578
|
/** Invoke the current client and normalize every rejection to a public result. */
|
|
1079
1579
|
async run(operation, options = {}) {
|
|
1080
1580
|
try {
|