{{ emoji.name }}
{{ emoji.host }}
@@ -83,6 +83,7 @@ import FormSplit from '@/components/form/split.vue';
import { selectFile } from '@/scripts/select-file.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
+import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 3765319b25..da3315cff5 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -118,7 +118,7 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
}, { immediate: true });
-const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null);
+const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? props.emoji.url : null);
async function changeImage(ev: Event) {
file.value = await selectFile(ev.currentTarget ?? ev.target, null);
From 08040924265cb9f8f4016ecc258c656449813c92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
<67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 16 Dec 2024 09:03:46 +0900
Subject: [PATCH 5/6] =?UTF-8?q?fix(frontend):=20serverContext=E3=81=AE?=
=?UTF-8?q?=E5=9E=8B=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E4=BF=AE=E6=AD=A3?=
=?UTF-8?q?=20(#15131)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fix(frontend): serverContextの型エラーを修正
* add comment
---
packages/frontend/src/pages/clip.vue | 5 +++--
packages/frontend/src/pages/note.vue | 6 ++++--
packages/frontend/src/pages/user/index.vue | 5 +++--
packages/frontend/src/server-context.ts | 12 +++++-------
4 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 891d59d605..c925bac4d9 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -46,9 +46,10 @@ import { clipsCache } from '@/cache.js';
import { isSupportShare } from '@/scripts/navigator.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { genEmbedCode } from '@/scripts/get-embed-code.js';
-import { getServerContext } from '@/server-context.js';
+import { assertServerContext, serverContext } from '@/server-context.js';
-const CTX_CLIP = getServerContext('clip');
+// contextは非ログイン状態の情報しかないためログイン時は利用できない
+const CTX_CLIP = $i && assertServerContext(serverContext, 'clip') ? serverContext.clip : null;
const props = defineProps<{
clipId: string,
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 5cf8f56776..6eca04db8d 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -63,9 +63,11 @@ import { dateString } from '@/filters/date.js';
import MkClipPreview from '@/components/MkClipPreview.vue';
import { defaultStore } from '@/store.js';
import { pleaseLogin } from '@/scripts/please-login.js';
-import { getServerContext } from '@/server-context.js';
+import { serverContext, assertServerContext } from '@/server-context.js';
+import { $i } from '@/account.js';
-const CTX_NOTE = getServerContext('note');
+// contextは非ログイン状態の情報しかないためログイン時は利用できない
+const CTX_NOTE = $i && assertServerContext(serverContext, 'note') ? serverContext.note : null;
const props = defineProps<{
noteId: string;
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index d862387401..c5cda79935 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -39,7 +39,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
-import { getServerContext } from '@/server-context.js';
+import { serverContext, assertServerContext } from '@/server-context.js';
const XHome = defineAsyncComponent(() => import('./home.vue'));
const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
@@ -53,7 +53,8 @@ const XFlashs = defineAsyncComponent(() => import('./flashs.vue'));
const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
const XRaw = defineAsyncComponent(() => import('./raw.vue'));
-const CTX_USER = getServerContext('user');
+// contextは非ログイン状態の情報しかないためログイン時は利用できない
+const CTX_USER = $i && assertServerContext(serverContext, 'user') ? serverContext.user : null;
const props = withDefaults(defineProps<{
acct: string;
diff --git a/packages/frontend/src/server-context.ts b/packages/frontend/src/server-context.ts
index aa44a10290..e79d3fa314 100644
--- a/packages/frontend/src/server-context.ts
+++ b/packages/frontend/src/server-context.ts
@@ -2,22 +2,20 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
+
import * as Misskey from 'misskey-js';
-import { $i } from '@/account.js';
const providedContextEl = document.getElementById('misskey_clientCtx');
export type ServerContext = {
clip?: Misskey.entities.Clip;
note?: Misskey.entities.Note;
- user?: Misskey.entities.UserLite;
+ user?: Misskey.entities.UserDetailed;
} | null;
export const serverContext: ServerContext = (providedContextEl && providedContextEl.textContent) ? JSON.parse(providedContextEl.textContent) : null;
-export function getServerContext
>(entity: K): Required, K>> | null {
- // contextは非ログイン状態の情報しかないためログイン時は利用できない
- if ($i) return null;
-
- return serverContext ? (serverContext[entity] ?? null) : null;
+export function assertServerContext>(ctx: ServerContext, entity: K): ctx is Required, K>> {
+ if (ctx == null) return false;
+ return entity in ctx && ctx[entity] != null;
}
From f123be38b93339f405468c8ed1aaa39d340b7791 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
<67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 19 Dec 2024 16:05:33 +0900
Subject: [PATCH 6/6] =?UTF-8?q?enhance(frontend):=20=E7=85=A7=E4=BC=9A?=
=?UTF-8?q?=E3=81=AE=E9=9A=9B=E3=81=AB=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92?=
=?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
=?UTF-8?q?=20(#15147)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* enhance: 照会の失敗理由を表示するように
* Update Changelog
* fix
* fix test
* lookupErrors-> remoteLookupErrors
---
CHANGELOG.md | 1 +
locales/index.d.ts | 59 +++++++++++++++++
locales/ja-JP.yml | 19 ++++++
.../src/core/activitypub/ApResolverService.ts | 25 +++----
.../src/server/api/endpoints/ap/show.ts | 65 ++++++++++++++++++-
.../backend/test-federation/test/note.test.ts | 6 +-
packages/frontend/src/scripts/lookup.ts | 38 ++++++++++-
7 files changed, 193 insertions(+), 20 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee82834de7..fd56700e1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
### Client
- Enhance: PC画面でチャンネルが複数列で表示されるように
(Cherry-picked from https://github.com/Otaku-Social/maniakey/pull/13)
+- Enhance: 照会に失敗した場合、その理由を表示するように
- Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正
- Fix: サーバー情報メニューに区切り線が不足していたのを修正
- Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 0ae188f1f7..63878d3d47 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -10601,6 +10601,65 @@ export interface Locale extends ILocale {
*/
"sent": string;
};
+ "_remoteLookupErrors": {
+ "_federationNotAllowed": {
+ /**
+ * このサーバーとは通信できません
+ */
+ "title": string;
+ /**
+ * このサーバーとの通信が無効化されているか、このサーバーをブロックしている・ブロックされている可能性があります。
+ * サーバー管理者にお問い合わせください。
+ */
+ "description": string;
+ };
+ "_uriInvalid": {
+ /**
+ * URIが不正です
+ */
+ "title": string;
+ /**
+ * 入力されたURIに問題があります。URIに使用できない文字を入力していないか確認してください。
+ */
+ "description": string;
+ };
+ "_requestFailed": {
+ /**
+ * リクエストに失敗しました
+ */
+ "title": string;
+ /**
+ * このサーバーとの通信に失敗しました。相手サーバーがダウンしている可能性があります。また、不正なURIや存在しないURIを入力していないか確認してください。
+ */
+ "description": string;
+ };
+ "_responseInvalid": {
+ /**
+ * レスポンスが不正です
+ */
+ "title": string;
+ /**
+ * このサーバーと通信することはできましたが、得られたデータが不正なものでした。
+ */
+ "description": string;
+ };
+ "_responseInvalidIdHostNotMatch": {
+ /**
+ * 入力されたURIのドメインと最終的に得られたURIのドメインとが異なります。第三者のサーバーを介してリモートのコンテンツを照会している場合は、発信元のサーバーで取得できるURIを使用して照会し直してください。
+ */
+ "description": string;
+ };
+ "_noSuchObject": {
+ /**
+ * 見つかりません
+ */
+ "title": string;
+ /**
+ * 要求されたリソースは見つかりませんでした。URIをもう一度お確かめください。
+ */
+ "description": string;
+ };
+ };
}
declare const locales: {
[lang: string]: Locale;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1b59708d85..d78bd4ee65 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2826,3 +2826,22 @@ _selfXssPrevention:
_followRequest:
recieved: "受け取った申請"
sent: "送った申請"
+
+_remoteLookupErrors:
+ _federationNotAllowed:
+ title: "このサーバーとは通信できません"
+ description: "このサーバーとの通信が無効化されているか、このサーバーをブロックしている・ブロックされている可能性があります。\nサーバー管理者にお問い合わせください。"
+ _uriInvalid:
+ title: "URIが不正です"
+ description: "入力されたURIに問題があります。URIに使用できない文字を入力していないか確認してください。"
+ _requestFailed:
+ title: "リクエストに失敗しました"
+ description: "このサーバーとの通信に失敗しました。相手サーバーがダウンしている可能性があります。また、不正なURIや存在しないURIを入力していないか確認してください。"
+ _responseInvalid:
+ title: "レスポンスが不正です"
+ description: "このサーバーと通信することはできましたが、得られたデータが不正なものでした。"
+ _responseInvalidIdHostNotMatch:
+ description: "入力されたURIのドメインと最終的に得られたURIのドメインとが異なります。第三者のサーバーを介してリモートのコンテンツを照会している場合は、発信元のサーバーで取得できるURIを使用して照会し直してください。"
+ _noSuchObject:
+ title: "見つかりません"
+ description: "要求されたリソースは見つかりませんでした。URIをもう一度お確かめください。"
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index b0b35274ea..52cc569140 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -20,6 +20,7 @@ import { ApDbResolverService } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js';
import type { IObject, ICollection, IOrderedCollection } from './type.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
export class Resolver {
private history: Set;
@@ -66,7 +67,7 @@ export class Resolver {
if (isCollectionOrOrderedCollection(collection)) {
return collection;
} else {
- throw new Error(`unrecognized collection type: ${collection.type}`);
+ throw new IdentifiableError('f100eccf-f347-43fb-9b45-96a0831fb635', `unrecognized collection type: ${collection.type}`);
}
}
@@ -80,15 +81,15 @@ export class Resolver {
// URLs with fragment parts cannot be resolved correctly because
// the fragment part does not get transmitted over HTTP(S).
// Avoid strange behaviour by not trying to resolve these at all.
- throw new Error(`cannot resolve URL with fragment: ${value}`);
+ throw new IdentifiableError('b94fd5b1-0e3b-4678-9df2-dad4cd515ab2', `cannot resolve URL with fragment: ${value}`);
}
if (this.history.has(value)) {
- throw new Error('cannot resolve already resolved one');
+ throw new IdentifiableError('0dc86cf6-7cd6-4e56-b1e6-5903d62d7ea5', 'cannot resolve already resolved one');
}
if (this.history.size > this.recursionLimit) {
- throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`);
+ throw new IdentifiableError('d592da9f-822f-4d91-83d7-4ceefabcf3d2', `hit recursion limit: ${this.utilityService.extractDbHost(value)}`);
}
this.history.add(value);
@@ -99,7 +100,7 @@ export class Resolver {
}
if (!this.utilityService.isFederationAllowedHost(host)) {
- throw new Error('Instance is blocked');
+ throw new IdentifiableError('09d79f9e-64f1-4316-9cfa-e75c4d091574', 'Instance is blocked');
}
if (this.config.signToActivityPubGet && !this.user) {
@@ -115,7 +116,7 @@ export class Resolver {
!(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') :
object['@context'] !== 'https://www.w3.org/ns/activitystreams'
) {
- throw new Error('invalid response');
+ throw new IdentifiableError('72180409-793c-4973-868e-5a118eb5519b', 'invalid response');
}
// HttpRequestService / ApRequestService have already checked that
@@ -123,11 +124,11 @@ export class Resolver {
// object after redirects; here we double-check that no redirects
// bounced between hosts
if (object.id == null) {
- throw new Error('invalid AP object: missing id');
+ throw new IdentifiableError('ad2dc287-75c1-44c4-839d-3d2e64576675', 'invalid AP object: missing id');
}
if (this.utilityService.punyHost(object.id) !== this.utilityService.punyHost(value)) {
- throw new Error(`invalid AP object ${value}: id ${object.id} has different host`);
+ throw new IdentifiableError('fd93c2fa-69a8-440f-880b-bf178e0ec877', `invalid AP object ${value}: id ${object.id} has different host`);
}
return object;
@@ -136,7 +137,7 @@ export class Resolver {
@bindThis
private resolveLocal(url: string): Promise {
const parsed = this.apDbResolverService.parseUri(url);
- if (!parsed.local) throw new Error('resolveLocal: not local');
+ if (!parsed.local) throw new IdentifiableError('02b40cd0-fa92-4b0c-acc9-fb2ada952ab8', 'resolveLocal: not local');
switch (parsed.type) {
case 'notes':
@@ -165,7 +166,7 @@ export class Resolver {
case 'follows':
return this.followRequestsRepository.findOneBy({ id: parsed.id })
.then(async followRequest => {
- if (followRequest == null) throw new Error('resolveLocal: invalid follow request ID');
+ if (followRequest == null) throw new IdentifiableError('a9d946e5-d276-47f8-95fb-f04230289bb0', 'resolveLocal: invalid follow request ID');
const [follower, followee] = await Promise.all([
this.usersRepository.findOneBy({
id: followRequest.followerId,
@@ -177,12 +178,12 @@ export class Resolver {
}),
]);
if (follower == null || followee == null) {
- throw new Error('resolveLocal: follower or followee does not exist');
+ throw new IdentifiableError('06ae3170-1796-4d93-a697-2611ea6d83b6', 'resolveLocal: follower or followee does not exist');
}
return this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url));
});
default:
- throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
+ throw new IdentifiableError('7a5d2fc0-94bc-4db6-b8b8-1bf24a2e23d0', `resolveLocal: type ${parsed.type} unhandled`);
}
}
}
diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts
index 24d5a7b0f1..5c2e82da88 100644
--- a/packages/backend/src/server/api/endpoints/ap/show.ts
+++ b/packages/backend/src/server/api/endpoints/ap/show.ts
@@ -19,6 +19,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import { ApiError } from '../../error.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
export const meta = {
tags: ['federation'],
@@ -32,6 +33,31 @@ export const meta = {
},
errors: {
+ federationNotAllowed: {
+ message: 'Federation for this host is not allowed.',
+ code: 'FEDERATION_NOT_ALLOWED',
+ id: '974b799e-1a29-4889-b706-18d4dd93e266',
+ },
+ uriInvalid: {
+ message: 'URI is invalid.',
+ code: 'URI_INVALID',
+ id: '1a5eab56-e47b-48c2-8d5e-217b897d70db',
+ },
+ requestFailed: {
+ message: 'Request failed.',
+ code: 'REQUEST_FAILED',
+ id: '81b539cf-4f57-4b29-bc98-032c33c0792e',
+ },
+ responseInvalid: {
+ message: 'Response from remote server is invalid.',
+ code: 'RESPONSE_INVALID',
+ id: '70193c39-54f3-4813-82f0-70a680f7495b',
+ },
+ responseInvalidIdHostNotMatch: {
+ message: 'Requested URI and response URI host does not match.',
+ code: 'RESPONSE_INVALID_ID_HOST_NOT_MATCH',
+ id: 'a2c9c61a-cb72-43ab-a964-3ca5fddb410a',
+ },
noSuchObject: {
message: 'No such object.',
code: 'NO_SUCH_OBJECT',
@@ -110,7 +136,9 @@ export default class extends Endpoint { // eslint-
*/
@bindThis
private async fetchAny(uri: string, me: MiLocalUser | null | undefined): Promise | null> {
- if (!this.utilityService.isFederationAllowedUri(uri)) return null;
+ if (!this.utilityService.isFederationAllowedUri(uri)) {
+ throw new ApiError(meta.errors.federationNotAllowed);
+ }
let local = await this.mergePack(me, ...await Promise.all([
this.apDbResolverService.getUserFromApId(uri),
@@ -125,7 +153,40 @@ export default class extends Endpoint { // eslint-
// リモートから一旦オブジェクトフェッチ
const resolver = this.apResolverService.createResolver();
- const object = await resolver.resolve(uri) as any;
+ const object = await resolver.resolve(uri).catch((err) => {
+ if (err instanceof IdentifiableError) {
+ switch (err.id) {
+ // resolve
+ case 'b94fd5b1-0e3b-4678-9df2-dad4cd515ab2':
+ throw new ApiError(meta.errors.uriInvalid);
+ case '0dc86cf6-7cd6-4e56-b1e6-5903d62d7ea5':
+ case 'd592da9f-822f-4d91-83d7-4ceefabcf3d2':
+ throw new ApiError(meta.errors.requestFailed);
+ case '09d79f9e-64f1-4316-9cfa-e75c4d091574':
+ throw new ApiError(meta.errors.federationNotAllowed);
+ case '72180409-793c-4973-868e-5a118eb5519b':
+ case 'ad2dc287-75c1-44c4-839d-3d2e64576675':
+ throw new ApiError(meta.errors.responseInvalid);
+ case 'fd93c2fa-69a8-440f-880b-bf178e0ec877':
+ throw new ApiError(meta.errors.responseInvalidIdHostNotMatch);
+
+ // resolveLocal
+ case '02b40cd0-fa92-4b0c-acc9-fb2ada952ab8':
+ throw new ApiError(meta.errors.uriInvalid);
+ case 'a9d946e5-d276-47f8-95fb-f04230289bb0':
+ case '06ae3170-1796-4d93-a697-2611ea6d83b6':
+ throw new ApiError(meta.errors.noSuchObject);
+ case '7a5d2fc0-94bc-4db6-b8b8-1bf24a2e23d0':
+ throw new ApiError(meta.errors.responseInvalid);
+ }
+ }
+
+ throw new ApiError(meta.errors.requestFailed);
+ });
+
+ if (object.id == null) {
+ throw new ApiError(meta.errors.responseInvalid);
+ }
// /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する
// これはDBに存在する可能性があるため再度DB検索
diff --git a/packages/backend/test-federation/test/note.test.ts b/packages/backend/test-federation/test/note.test.ts
index bacc4cc54f..220c22e198 100644
--- a/packages/backend/test-federation/test/note.test.ts
+++ b/packages/backend/test-federation/test/note.test.ts
@@ -131,11 +131,7 @@ describe('Note', () => {
rejects(
async () => await bob.client.request('ap/show', { uri: `https://a.test/notes/${note.id}` }),
(err: any) => {
- /**
- * FIXME: this error is not handled
- * @see https://github.com/misskey-dev/misskey/issues/12736
- */
- strictEqual(err.code, 'INTERNAL_ERROR');
+ strictEqual(err.code, 'REQUEST_FAILED');
return true;
},
);
diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts
index a261ec0669..ddcbfe1a8d 100644
--- a/packages/frontend/src/scripts/lookup.ts
+++ b/packages/frontend/src/scripts/lookup.ts
@@ -33,7 +33,43 @@ export async function lookup(router?: Router) {
uri: query,
});
- os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
+ os.promiseDialog(promise, null, (err) => {
+ let title = i18n.ts.somethingHappened;
+ let text = err.message + '\n' + err.id;
+
+ switch (err.id) {
+ case '974b799e-1a29-4889-b706-18d4dd93e266':
+ title = i18n.ts._remoteLookupErrors._federationNotAllowed.title;
+ text = i18n.ts._remoteLookupErrors._federationNotAllowed.description;
+ break;
+ case '1a5eab56-e47b-48c2-8d5e-217b897d70db':
+ title = i18n.ts._remoteLookupErrors._uriInvalid.title;
+ text = i18n.ts._remoteLookupErrors._uriInvalid.description;
+ break;
+ case '81b539cf-4f57-4b29-bc98-032c33c0792e':
+ title = i18n.ts._remoteLookupErrors._requestFailed.title;
+ text = i18n.ts._remoteLookupErrors._requestFailed.description;
+ break;
+ case '70193c39-54f3-4813-82f0-70a680f7495b':
+ title = i18n.ts._remoteLookupErrors._responseInvalid.title;
+ text = i18n.ts._remoteLookupErrors._responseInvalid.description;
+ break;
+ case 'a2c9c61a-cb72-43ab-a964-3ca5fddb410a':
+ title = i18n.ts._remoteLookupErrors._responseInvalid.title;
+ text = i18n.ts._remoteLookupErrors._responseInvalidIdHostNotMatch.description;
+ break;
+ case 'dc94d745-1262-4e63-a17d-fecaa57efc82':
+ title = i18n.ts._remoteLookupErrors._noSuchObject.title;
+ text = i18n.ts._remoteLookupErrors._noSuchObject.description;
+ break;
+ }
+
+ os.alert({
+ type: 'error',
+ title,
+ text,
+ });
+ }, i18n.ts.fetchingAsApObject);
const res = await promise;