refactor(core): standardize frontend error handling (#10667)
This commit is contained in:
70 files changed
+495
-480
No files matched your search
@@ -5,6 +5,7 @@ import { HttpStatus, Logger } from '@nestjs/common';
|
|||||||
import { ClsServiceManager } from 'nestjs-cls';
|
import { ClsServiceManager } from 'nestjs-cls';
|
||||||
|
|
||||||
export type UserFriendlyErrorBaseType =
|
export type UserFriendlyErrorBaseType =
|
||||||
|
| 'network_error'
|
||||||
| 'bad_request'
|
| 'bad_request'
|
||||||
| 'too_many_requests'
|
| 'too_many_requests'
|
||||||
| 'resource_not_found'
|
| 'resource_not_found'
|
||||||
@@ -26,6 +27,7 @@ export type UserFriendlyErrorOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const BaseTypeToHttpStatusMap: Record<UserFriendlyErrorBaseType, HttpStatus> = {
|
const BaseTypeToHttpStatusMap: Record<UserFriendlyErrorBaseType, HttpStatus> = {
|
||||||
|
network_error: HttpStatus.GATEWAY_TIMEOUT,
|
||||||
too_many_requests: HttpStatus.TOO_MANY_REQUESTS,
|
too_many_requests: HttpStatus.TOO_MANY_REQUESTS,
|
||||||
bad_request: HttpStatus.BAD_REQUEST,
|
bad_request: HttpStatus.BAD_REQUEST,
|
||||||
resource_not_found: HttpStatus.NOT_FOUND,
|
resource_not_found: HttpStatus.NOT_FOUND,
|
||||||
@@ -239,6 +241,10 @@ export const USER_FRIENDLY_ERRORS = {
|
|||||||
type: 'internal_server_error',
|
type: 'internal_server_error',
|
||||||
message: 'An internal error occurred.',
|
message: 'An internal error occurred.',
|
||||||
},
|
},
|
||||||
|
network_error: {
|
||||||
|
type: 'network_error',
|
||||||
|
message: 'Network error.',
|
||||||
|
},
|
||||||
too_many_request: {
|
too_many_request: {
|
||||||
type: 'too_many_requests',
|
type: 'too_many_requests',
|
||||||
message: 'Too many requests.',
|
message: 'Too many requests.',
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ export class InternalServerError extends UserFriendlyError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class NetworkError extends UserFriendlyError {
|
||||||
|
constructor(message?: string) {
|
||||||
|
super('network_error', 'network_error', message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class TooManyRequest extends UserFriendlyError {
|
export class TooManyRequest extends UserFriendlyError {
|
||||||
constructor(message?: string) {
|
constructor(message?: string) {
|
||||||
super('too_many_requests', 'too_many_request', message);
|
super('too_many_requests', 'too_many_request', message);
|
||||||
@@ -851,6 +857,7 @@ export class UnsupportedClientVersion extends UserFriendlyError {
|
|||||||
}
|
}
|
||||||
export enum ErrorNames {
|
export enum ErrorNames {
|
||||||
INTERNAL_SERVER_ERROR,
|
INTERNAL_SERVER_ERROR,
|
||||||
|
NETWORK_ERROR,
|
||||||
TOO_MANY_REQUEST,
|
TOO_MANY_REQUEST,
|
||||||
NOT_FOUND,
|
NOT_FOUND,
|
||||||
BAD_REQUEST,
|
BAD_REQUEST,
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# @affine/error
|
||||||
|
|
||||||
|
AFFiNE error handler utilities
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@affine/error",
|
||||||
|
"version": "0.20.0",
|
||||||
|
"private": true,
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@affine/graphql": "workspace:*",
|
||||||
|
"graphql": "^16.9.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^3.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { GraphQLError, UserFriendlyError } from '..';
|
||||||
|
|
||||||
|
describe('user friendly error', () => {
|
||||||
|
it('should create from graphql error', () => {
|
||||||
|
const gqlError = new GraphQLError('test', {
|
||||||
|
extensions: {
|
||||||
|
status: 400,
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
type: 'BAD_REQUEST',
|
||||||
|
name: 'SOME_ERROR_NAME',
|
||||||
|
message: 'test',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const error = UserFriendlyError.fromAny(gqlError);
|
||||||
|
|
||||||
|
expect(error.name).toBe('SOME_ERROR_NAME');
|
||||||
|
expect(error.status).toBe(400);
|
||||||
|
expect(error.code).toBe('BAD_REQUEST');
|
||||||
|
expect(error.type).toBe('BAD_REQUEST');
|
||||||
|
expect(error.message).toBe('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create from any error', () => {
|
||||||
|
const error = UserFriendlyError.fromAny(new Error('test'));
|
||||||
|
expect(error.message).toBe('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create from object', () => {
|
||||||
|
const error = UserFriendlyError.fromAny({
|
||||||
|
name: 'SOME_ERROR_NAME',
|
||||||
|
status: 400,
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
type: 'BAD_REQUEST',
|
||||||
|
message: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(error.message).toBe('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create from string', () => {
|
||||||
|
const error = UserFriendlyError.fromAny('test error');
|
||||||
|
expect(error.message).toBe('test error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create fallback error', () => {
|
||||||
|
const error = UserFriendlyError.fromAny(null);
|
||||||
|
|
||||||
|
expect(error.message).toBe(
|
||||||
|
'Unhandled error raised. Please contact us for help.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test network error', () => {
|
||||||
|
const error = UserFriendlyError.fromAny({
|
||||||
|
name: 'NETWORK_ERROR',
|
||||||
|
status: 500,
|
||||||
|
code: 'INTERNAL_SERVER_ERROR',
|
||||||
|
type: 'INTERNAL_SERVER_ERROR',
|
||||||
|
message: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(error.isNetworkError()).toBe(true);
|
||||||
|
|
||||||
|
const error2 = UserFriendlyError.fromAny({
|
||||||
|
name: 'SOME_ERROR_NAME',
|
||||||
|
status: 400,
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
type: 'BAD_REQUEST',
|
||||||
|
message: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(error2.isNetworkError()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test name', () => {
|
||||||
|
const error = UserFriendlyError.fromAny({
|
||||||
|
name: 'SOME_ERROR_NAME',
|
||||||
|
status: 400,
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
type: 'BAD_REQUEST',
|
||||||
|
message: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error test name
|
||||||
|
expect(error.is('SOME_ERROR_NAME')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test status', () => {
|
||||||
|
const error = UserFriendlyError.fromAny({
|
||||||
|
name: 'SOME_ERROR_NAME',
|
||||||
|
status: 400,
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
type: 'BAD_REQUEST',
|
||||||
|
message: 'test',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(error.isStatus(400)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import type { ErrorDataUnion, ErrorNames } from '@affine/graphql';
|
||||||
|
import { GraphQLError as BaseGraphQLError } from 'graphql';
|
||||||
|
|
||||||
|
export type ErrorName = keyof typeof ErrorNames | 'NETWORK_ERROR';
|
||||||
|
|
||||||
|
export interface UserFriendlyErrorResponse {
|
||||||
|
status: number;
|
||||||
|
code: string;
|
||||||
|
type: string;
|
||||||
|
name: ErrorName;
|
||||||
|
message: string;
|
||||||
|
data?: any;
|
||||||
|
stacktrace?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnknownError(message: string) {
|
||||||
|
return new UserFriendlyError({
|
||||||
|
status: 500,
|
||||||
|
code: 'INTERNAL_SERVER_ERROR',
|
||||||
|
type: 'INTERNAL_SERVER_ERROR',
|
||||||
|
name: 'INTERNAL_SERVER_ERROR',
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToPascalCase<S extends string> = S extends `${infer A}_${infer B}`
|
||||||
|
? `${Capitalize<Lowercase<A>>}${ToPascalCase<B>}`
|
||||||
|
: Capitalize<Lowercase<S>>;
|
||||||
|
|
||||||
|
export type ErrorData = {
|
||||||
|
[K in ErrorNames]: Extract<
|
||||||
|
ErrorDataUnion,
|
||||||
|
{ __typename?: `${ToPascalCase<K>}DataType` }
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class GraphQLError extends BaseGraphQLError {
|
||||||
|
// @ts-expect-error better to be a known type without any type casting
|
||||||
|
override extensions!: UserFriendlyErrorResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserFriendlyError
|
||||||
|
extends Error
|
||||||
|
implements UserFriendlyErrorResponse
|
||||||
|
{
|
||||||
|
readonly status = this.response.status;
|
||||||
|
readonly code = this.response.code;
|
||||||
|
readonly type = this.response.type;
|
||||||
|
override readonly name = this.response.name;
|
||||||
|
override readonly message = this.response.message;
|
||||||
|
readonly data = this.response.data;
|
||||||
|
readonly stacktrace = this.response.stacktrace;
|
||||||
|
|
||||||
|
static fromAny(anything: any) {
|
||||||
|
if (anything instanceof UserFriendlyError) {
|
||||||
|
return anything;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (typeof anything) {
|
||||||
|
case 'string':
|
||||||
|
return UnknownError(anything);
|
||||||
|
case 'object': {
|
||||||
|
if (anything) {
|
||||||
|
if (anything instanceof GraphQLError) {
|
||||||
|
return new UserFriendlyError(anything.extensions);
|
||||||
|
} else if (anything.type && anything.name && anything.message) {
|
||||||
|
return new UserFriendlyError(anything);
|
||||||
|
} else if (anything.message) {
|
||||||
|
return UnknownError(anything.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnknownError('Unhandled error raised. Please contact us for help.');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private readonly response: UserFriendlyErrorResponse) {
|
||||||
|
super(response.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
is(name: ErrorName) {
|
||||||
|
return this.name === name;
|
||||||
|
}
|
||||||
|
|
||||||
|
isStatus(status: number) {
|
||||||
|
return this.status === status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isNetworkError(error: UserFriendlyError) {
|
||||||
|
return error.name === 'NETWORK_ERROR';
|
||||||
|
}
|
||||||
|
|
||||||
|
static notNetworkError(error: UserFriendlyError) {
|
||||||
|
return !UserFriendlyError.isNetworkError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
isNetworkError() {
|
||||||
|
return UserFriendlyError.isNetworkError(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
notNetworkError() {
|
||||||
|
return UserFriendlyError.notNetworkError(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.web.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||||
|
},
|
||||||
|
"include": ["./src"],
|
||||||
|
"references": []
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@affine/env": "workspace:*",
|
"@affine/env": "workspace:*",
|
||||||
|
"@affine/error": "workspace:*",
|
||||||
"@affine/templates": "workspace:*",
|
"@affine/templates": "workspace:*",
|
||||||
"@datastructures-js/binary-search-tree": "^5.3.2",
|
"@datastructures-js/binary-search-tree": "^5.3.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ export {
|
|||||||
mapInto,
|
mapInto,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from './ops';
|
} from './ops';
|
||||||
export { useEnsureLiveData, useLiveData } from './react';
|
export { useEnsureLiveData, useLiveData } from './react';
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
catchError,
|
catchError,
|
||||||
connect,
|
connect,
|
||||||
@@ -144,6 +145,32 @@ export function backoffRetry<T>({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function smartRetry<T>({
|
||||||
|
count = 3,
|
||||||
|
delay = 200,
|
||||||
|
maxDelay = 15000,
|
||||||
|
}: {
|
||||||
|
count?: number;
|
||||||
|
delay?: number;
|
||||||
|
maxDelay?: number;
|
||||||
|
} = {}) {
|
||||||
|
return (obs$: Observable<T>) =>
|
||||||
|
obs$.pipe(
|
||||||
|
backoffRetry({
|
||||||
|
when: UserFriendlyError.isNetworkError,
|
||||||
|
count: Infinity,
|
||||||
|
delay,
|
||||||
|
maxDelay,
|
||||||
|
}),
|
||||||
|
backoffRetry({
|
||||||
|
when: UserFriendlyError.notNetworkError,
|
||||||
|
count,
|
||||||
|
delay,
|
||||||
|
maxDelay,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An operator that combines `exhaustMap` and `switchMap`.
|
* An operator that combines `exhaustMap` and `switchMap`.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -6,5 +6,9 @@
|
|||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||||
},
|
},
|
||||||
"references": [{ "path": "../debug" }, { "path": "../env" }]
|
"references": [
|
||||||
|
{ "path": "../debug" },
|
||||||
|
{ "path": "../env" },
|
||||||
|
{ "path": "../error" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@affine/electron-api": "workspace:*",
|
"@affine/electron-api": "workspace:*",
|
||||||
|
"@affine/error": "workspace:*",
|
||||||
"@affine/graphql": "workspace:*",
|
"@affine/graphql": "workspace:*",
|
||||||
"@affine/i18n": "workspace:*",
|
"@affine/i18n": "workspace:*",
|
||||||
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||||
import {
|
import { UserFriendlyError } from '@affine/error';
|
||||||
ErrorNames,
|
import { ErrorNames, type GetInviteInfoQuery } from '@affine/graphql';
|
||||||
type GetInviteInfoQuery,
|
|
||||||
UserFriendlyError,
|
|
||||||
} from '@affine/graphql';
|
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
|
|
||||||
import { Avatar } from '../../ui/avatar';
|
import { Avatar } from '../../ui/avatar';
|
||||||
@@ -16,7 +13,7 @@ export const JoinFailedPage = ({
|
|||||||
inviteInfo: GetInviteInfoQuery['getInviteInfo'];
|
inviteInfo: GetInviteInfoQuery['getInviteInfo'];
|
||||||
error?: any;
|
error?: any;
|
||||||
}) => {
|
}) => {
|
||||||
const userFriendlyError = UserFriendlyError.fromAnyError(error);
|
const userFriendlyError = UserFriendlyError.fromAny(error);
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
return (
|
return (
|
||||||
<AuthPageContainer
|
<AuthPageContainer
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"references": [
|
"references": [
|
||||||
{ "path": "../../common/debug" },
|
{ "path": "../../common/debug" },
|
||||||
{ "path": "../electron-api" },
|
{ "path": "../electron-api" },
|
||||||
|
{ "path": "../../common/error" },
|
||||||
{ "path": "../graphql" },
|
{ "path": "../graphql" },
|
||||||
{ "path": "../i18n" },
|
{ "path": "../i18n" },
|
||||||
{ "path": "../../../tools/utils" },
|
{ "path": "../../../tools/utils" },
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@affine/electron-api": "workspace:*",
|
"@affine/electron-api": "workspace:*",
|
||||||
"@affine/env": "workspace:*",
|
"@affine/env": "workspace:*",
|
||||||
|
"@affine/error": "workspace:*",
|
||||||
"@affine/graphql": "workspace:*",
|
"@affine/graphql": "workspace:*",
|
||||||
"@affine/i18n": "workspace:*",
|
"@affine/i18n": "workspace:*",
|
||||||
"@affine/nbstore": "workspace:*",
|
"@affine/nbstore": "workspace:*",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
UnauthorizedError,
|
UnauthorizedError,
|
||||||
} from '@affine/core/blocksuite/ai/components/ai-item/types';
|
} from '@affine/core/blocksuite/ai/components/ai-item/types';
|
||||||
import { showAILoginRequiredAtom } from '@affine/core/components/affine/auth/ai-login-required';
|
import { showAILoginRequiredAtom } from '@affine/core/components/affine/auth/ai-login-required';
|
||||||
|
import type { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
addContextDocMutation,
|
addContextDocMutation,
|
||||||
cleanupCopilotSessionMutation,
|
cleanupCopilotSessionMutation,
|
||||||
@@ -14,7 +15,6 @@ import {
|
|||||||
getCopilotHistoriesQuery,
|
getCopilotHistoriesQuery,
|
||||||
getCopilotHistoryIdsQuery,
|
getCopilotHistoryIdsQuery,
|
||||||
getCopilotSessionsQuery,
|
getCopilotSessionsQuery,
|
||||||
GraphQLError,
|
|
||||||
type GraphQLQuery,
|
type GraphQLQuery,
|
||||||
listContextDocsAndFilesQuery,
|
listContextDocsAndFilesQuery,
|
||||||
listContextQuery,
|
listContextQuery,
|
||||||
@@ -23,7 +23,6 @@ import {
|
|||||||
removeContextDocMutation,
|
removeContextDocMutation,
|
||||||
type RequestOptions,
|
type RequestOptions,
|
||||||
updateCopilotSessionMutation,
|
updateCopilotSessionMutation,
|
||||||
UserFriendlyError,
|
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import { getCurrentStore } from '@toeverything/infra';
|
import { getCurrentStore } from '@toeverything/infra';
|
||||||
|
|
||||||
@@ -40,18 +39,13 @@ function codeToError(error: UserFriendlyError) {
|
|||||||
return new GeneralNetworkError(
|
return new GeneralNetworkError(
|
||||||
error.code
|
error.code
|
||||||
? `${error.code}: ${error.message}\nIdentify: ${error.name}`
|
? `${error.code}: ${error.message}\nIdentify: ${error.name}`
|
||||||
: undefined
|
: error.message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveError(err: any) {
|
export function resolveError(err: any) {
|
||||||
const standardError =
|
return codeToError(err);
|
||||||
err instanceof GraphQLError
|
|
||||||
? new UserFriendlyError(err.extensions)
|
|
||||||
: UserFriendlyError.fromAnyError(err);
|
|
||||||
|
|
||||||
return codeToError(standardError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleError(src: any) {
|
export function handleError(src: any) {
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
|||||||
import { OpenInAppService } from '@affine/core/modules/open-in-app';
|
import { OpenInAppService } from '@affine/core/modules/open-in-app';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
import { GuardService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import { UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import {
|
import {
|
||||||
@@ -87,7 +87,7 @@ export function useRegisterBlocksuiteEditorCommands(
|
|||||||
doc.moveToTrash();
|
doc.moveToTrash();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const userFriendlyError = UserFriendlyError.fromAnyError(error);
|
const userFriendlyError = UserFriendlyError.fromAny(error);
|
||||||
toast(t[`error.${userFriendlyError.name}`](userFriendlyError.data));
|
toast(t[`error.${userFriendlyError.name}`](userFriendlyError.data));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { notify } from '@affine/component';
|
import { notify } from '@affine/component';
|
||||||
import { GraphQLError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import type { PropsWithChildren, ReactNode } from 'react';
|
import type { PropsWithChildren, ReactNode } from 'react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import type { SWRConfiguration } from 'swr';
|
import type { SWRConfiguration } from 'swr';
|
||||||
@@ -17,21 +17,13 @@ const swrConfig: SWRConfiguration = {
|
|||||||
const d = fetcher(...args);
|
const d = fetcher(...args);
|
||||||
if (d instanceof Promise) {
|
if (d instanceof Promise) {
|
||||||
return d.catch(e => {
|
return d.catch(e => {
|
||||||
if (
|
const error = UserFriendlyError.fromAny(e);
|
||||||
e instanceof GraphQLError ||
|
|
||||||
(Array.isArray(e) && e[0] instanceof GraphQLError)
|
notify.error({
|
||||||
) {
|
title: error.name,
|
||||||
const graphQLError = e instanceof GraphQLError ? e : e[0];
|
message: error.message,
|
||||||
notify.error({
|
});
|
||||||
title: 'GraphQL Error',
|
|
||||||
message: graphQLError.toString(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
notify.error({
|
|
||||||
title: 'Error',
|
|
||||||
message: e.toString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { DocsService } from '@affine/core/modules/doc';
|
|||||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||||
import { GuardService } from '@affine/core/modules/permissions';
|
import { GuardService } from '@affine/core/modules/permissions';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
|
||||||
@@ -58,8 +58,7 @@ export const TrashButton = () => {
|
|||||||
docRecord.moveToTrash();
|
docRecord.moveToTrash();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
const userFriendlyError =
|
const userFriendlyError = UserFriendlyError.fromAny(error);
|
||||||
UserFriendlyError.fromAnyError(error);
|
|
||||||
toast(
|
toast(
|
||||||
t[`error.${userFriendlyError.name}`](userFriendlyError.data)
|
t[`error.${userFriendlyError.name}`](userFriendlyError.data)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,13 +8,10 @@ import {
|
|||||||
} from '@affine/component/auth-components';
|
} from '@affine/component/auth-components';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import {
|
import { AuthService, CaptchaService } from '@affine/core/modules/cloud';
|
||||||
AuthService,
|
|
||||||
BackendError,
|
|
||||||
CaptchaService,
|
|
||||||
} from '@affine/core/modules/cloud';
|
|
||||||
import type { AuthSessionStatus } from '@affine/core/modules/cloud/entities/session';
|
import type { AuthSessionStatus } from '@affine/core/modules/cloud/entities/session';
|
||||||
import { Unreachable } from '@affine/env/constant';
|
import { Unreachable } from '@affine/env/constant';
|
||||||
|
import type { UserFriendlyError } from '@affine/error';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
@@ -143,12 +140,10 @@ export const SignInWithEmailStep = ({
|
|||||||
try {
|
try {
|
||||||
await authService.signInMagicLink(email, otp, false);
|
await authService.signInMagicLink(email, otp, false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof BackendError) {
|
notify.error({
|
||||||
notify.error({
|
title: (e as UserFriendlyError).message,
|
||||||
title: e.originError.message,
|
});
|
||||||
});
|
setOtpError(t['com.affine.auth.sign.auth.code.invalid']());
|
||||||
setOtpError(t['com.affine.auth.sign.auth.code.invalid']());
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsVerifying(false);
|
setIsVerifying(false);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -15,6 +15,7 @@ import {
|
|||||||
SubscriptionService,
|
SubscriptionService,
|
||||||
} from '@affine/core/modules/cloud';
|
} from '@affine/core/modules/cloud';
|
||||||
import { UrlService } from '@affine/core/modules/url';
|
import { UrlService } from '@affine/core/modules/url';
|
||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import type { InvoicesQuery } from '@affine/graphql';
|
import type { InvoicesQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
createCustomerPortalMutation,
|
createCustomerPortalMutation,
|
||||||
@@ -22,7 +23,6 @@ import {
|
|||||||
SubscriptionPlan,
|
SubscriptionPlan,
|
||||||
SubscriptionRecurring,
|
SubscriptionRecurring,
|
||||||
SubscriptionStatus,
|
SubscriptionStatus,
|
||||||
UserFriendlyError,
|
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import { type I18nString, i18nTime, Trans, useI18n } from '@affine/i18n';
|
import { type I18nString, i18nTime, Trans, useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -539,8 +539,8 @@ const BillingHistory = () => {
|
|||||||
return (
|
return (
|
||||||
<span style={{ color: cssVar('errorColor') }}>
|
<span style={{ color: cssVar('errorColor') }}>
|
||||||
{error
|
{error
|
||||||
? UserFriendlyError.fromAnyError(error).message
|
? UserFriendlyError.fromAny(error).message
|
||||||
: 'Failed to load members'}
|
: 'Failed to load invoices'}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -17,13 +17,13 @@ import {
|
|||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { UrlService } from '@affine/core/modules/url';
|
import { UrlService } from '@affine/core/modules/url';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
createCustomerPortalMutation,
|
createCustomerPortalMutation,
|
||||||
type InvoicesQuery,
|
type InvoicesQuery,
|
||||||
InvoiceStatus,
|
InvoiceStatus,
|
||||||
SubscriptionPlan,
|
SubscriptionPlan,
|
||||||
SubscriptionRecurring,
|
SubscriptionRecurring,
|
||||||
UserFriendlyError,
|
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -321,8 +321,8 @@ const BillingHistory = () => {
|
|||||||
return (
|
return (
|
||||||
<span style={{ color: cssVar('errorColor') }}>
|
<span style={{ color: cssVar('errorColor') }}>
|
||||||
{error
|
{error
|
||||||
? UserFriendlyError.fromAnyError(error).message
|
? UserFriendlyError.fromAny(error).message
|
||||||
: 'Failed to load members'}
|
: 'Failed to load invoices'}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -8,7 +8,7 @@ import {
|
|||||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import { UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
@@ -91,7 +91,7 @@ export const SelfHostTeamCard = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
console.error(e);
|
console.error(e);
|
||||||
const error = UserFriendlyError.fromAnyError(e);
|
const error = UserFriendlyError.fromAny(e);
|
||||||
|
|
||||||
notify.error({
|
notify.error({
|
||||||
title: error.name,
|
title: error.name,
|
||||||
@@ -119,7 +119,7 @@ export const SelfHostTeamCard = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
console.error(e);
|
console.error(e);
|
||||||
const error = UserFriendlyError.fromAnyError(e);
|
const error = UserFriendlyError.fromAny(e);
|
||||||
|
|
||||||
notify.error({
|
notify.error({
|
||||||
title: error.name,
|
title: error.name,
|
||||||
|
|||||||
+4
-7
@@ -20,12 +20,9 @@ import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
|||||||
import { WorkspaceShareSettingService } from '@affine/core/modules/share-setting';
|
import { WorkspaceShareSettingService } from '@affine/core/modules/share-setting';
|
||||||
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
|
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
|
||||||
import { emailRegex } from '@affine/core/utils/email-regex';
|
import { emailRegex } from '@affine/core/utils/email-regex';
|
||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import type { WorkspaceInviteLinkExpireTime } from '@affine/graphql';
|
import type { WorkspaceInviteLinkExpireTime } from '@affine/graphql';
|
||||||
import {
|
import { ServerDeploymentType, SubscriptionPlan } from '@affine/graphql';
|
||||||
ServerDeploymentType,
|
|
||||||
SubscriptionPlan,
|
|
||||||
UserFriendlyError,
|
|
||||||
} from '@affine/graphql';
|
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { ExportIcon } from '@blocksuite/icons/rc';
|
import { ExportIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -129,7 +126,7 @@ export const CloudWorkspaceMembersPanel = ({
|
|||||||
message: t['com.affine.payment.resume.success.team.message'](),
|
message: t['com.affine.payment.resume.success.team.message'](),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = UserFriendlyError.fromAnyError(err);
|
const error = UserFriendlyError.fromAny(err);
|
||||||
notify.error({
|
notify.error({
|
||||||
title: error.name,
|
title: error.name,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
@@ -299,7 +296,7 @@ export const CloudWorkspaceMembersPanel = ({
|
|||||||
return (
|
return (
|
||||||
<span className={styles.errorStyle}>
|
<span className={styles.errorStyle}>
|
||||||
{error
|
{error
|
||||||
? UserFriendlyError.fromAnyError(error).message
|
? UserFriendlyError.fromAny(error).message
|
||||||
: 'Failed to load members'}
|
: 'Failed to load members'}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-6
@@ -6,11 +6,8 @@ import {
|
|||||||
WorkspaceMembersService,
|
WorkspaceMembersService,
|
||||||
} from '@affine/core/modules/permissions';
|
} from '@affine/core/modules/permissions';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import {
|
import { UserFriendlyError } from '@affine/error';
|
||||||
Permission,
|
import { Permission, WorkspaceMemberStatus } from '@affine/graphql';
|
||||||
UserFriendlyError,
|
|
||||||
WorkspaceMemberStatus,
|
|
||||||
} from '@affine/graphql';
|
|
||||||
import { type I18nString, useI18n } from '@affine/i18n';
|
import { type I18nString, useI18n } from '@affine/i18n';
|
||||||
import { MoreVerticalIcon } from '@blocksuite/icons/rc';
|
import { MoreVerticalIcon } from '@blocksuite/icons/rc';
|
||||||
import {
|
import {
|
||||||
@@ -75,7 +72,7 @@ export const MemberList = ({
|
|||||||
) : (
|
) : (
|
||||||
<span className={styles.errorStyle}>
|
<span className={styles.errorStyle}>
|
||||||
{error
|
{error
|
||||||
? UserFriendlyError.fromAnyError(error).message
|
? UserFriendlyError.fromAny(error).message
|
||||||
: 'Failed to load members'}
|
: 'Failed to load members'}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Button } from '@affine/component';
|
import { Button } from '@affine/component';
|
||||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
import { BackendError, GraphQLService } from '@affine/core/modules/cloud';
|
import { GraphQLService } from '@affine/core/modules/cloud';
|
||||||
import { changeEmailMutation, ErrorNames } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
|
import { changeEmailMutation } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useService } from '@toeverything/infra';
|
||||||
import { type FC, useEffect, useState } from 'react';
|
import { type FC, useEffect, useState } from 'react';
|
||||||
@@ -33,11 +34,8 @@ export const ConfirmChangeEmail: FC<{
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err instanceof BackendError) {
|
if (UserFriendlyError.fromAny(err).is('INVALID_EMAIL_TOKEN')) {
|
||||||
const userFriendlyError = err.originError;
|
return navigateHelper.jumpToExpired();
|
||||||
if (userFriendlyError.name === ErrorNames.INVALID_EMAIL_TOKEN) {
|
|
||||||
return navigateHelper.jumpToExpired();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ import { Button } from '@affine/component';
|
|||||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
import { GraphQLService } from '@affine/core/modules/cloud';
|
import { GraphQLService } from '@affine/core/modules/cloud';
|
||||||
import {
|
import { UserFriendlyError } from '@affine/error';
|
||||||
ErrorNames,
|
import { verifyEmailMutation } from '@affine/graphql';
|
||||||
UserFriendlyError,
|
|
||||||
verifyEmailMutation,
|
|
||||||
} from '@affine/graphql';
|
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useService } from '@toeverything/infra';
|
||||||
import { type FC, useEffect, useState } from 'react';
|
import { type FC, useEffect, useState } from 'react';
|
||||||
@@ -35,8 +32,8 @@ export const ConfirmVerifiedEmail: FC<{
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
const userFriendlyError = UserFriendlyError.fromAnyError(error);
|
const userFriendlyError = UserFriendlyError.fromAny(error);
|
||||||
if (userFriendlyError.name === ErrorNames.INVALID_EMAIL_TOKEN) {
|
if (userFriendlyError.is('INVALID_EMAIL_TOKEN')) {
|
||||||
return navigateHelper.jumpToExpired();
|
return navigateHelper.jumpToExpired();
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
ExpiredPage,
|
ExpiredPage,
|
||||||
JoinFailedPage,
|
JoinFailedPage,
|
||||||
} from '@affine/component/member-components';
|
} from '@affine/component/member-components';
|
||||||
import { ErrorNames, UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { Navigate, useParams } from 'react-router-dom';
|
import { Navigate, useParams } from 'react-router-dom';
|
||||||
@@ -43,8 +43,8 @@ const AcceptInvite = ({ inviteId: targetInviteId }: { inviteId: string }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (error && inviteId === targetInviteId) {
|
if (error && inviteId === targetInviteId) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAny(error);
|
||||||
if (err.name === ErrorNames.ALREADY_IN_SPACE) {
|
if (err.is('ALREADY_IN_SPACE')) {
|
||||||
return navigateHelper.jumpToIndex();
|
return navigateHelper.jumpToIndex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Button, Loading } from '@affine/component';
|
import { Button, Loading } from '@affine/component';
|
||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
SubscriptionPlan,
|
SubscriptionPlan,
|
||||||
SubscriptionRecurring,
|
SubscriptionRecurring,
|
||||||
@@ -16,11 +17,7 @@ import {
|
|||||||
RouteLogic,
|
RouteLogic,
|
||||||
useNavigateHelper,
|
useNavigateHelper,
|
||||||
} from '../../../components/hooks/use-navigate-helper';
|
} from '../../../components/hooks/use-navigate-helper';
|
||||||
import {
|
import { AuthService, SubscriptionService } from '../../../modules/cloud';
|
||||||
AuthService,
|
|
||||||
BackendError,
|
|
||||||
SubscriptionService,
|
|
||||||
} from '../../../modules/cloud';
|
|
||||||
import { container } from './subscribe.css';
|
import { container } from './subscribe.css';
|
||||||
|
|
||||||
interface ProductTriple {
|
interface ProductTriple {
|
||||||
@@ -160,12 +157,8 @@ export const Component = () => {
|
|||||||
setMessage('Redirecting...');
|
setMessage('Redirecting...');
|
||||||
location.href = checkout;
|
location.href = checkout;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof BackendError) {
|
const e = UserFriendlyError.fromAny(err);
|
||||||
setMessage(err.originError.message);
|
setMessage(e.message);
|
||||||
} else {
|
|
||||||
console.log(err);
|
|
||||||
setError('Something went wrong, please contact support.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).pipe(mergeMap(() => EMPTY));
|
}).pipe(mergeMap(() => EMPTY));
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GetWorkspacePageMetaByIdQuery } from '@affine/graphql';
|
import type { GetWorkspacePageMetaByIdQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,12 +8,12 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, mergeMap } from 'rxjs';
|
import { EMPTY, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import type { DocService } from '../../doc';
|
import type { DocService } from '../../doc';
|
||||||
import type { GlobalCache } from '../../storage';
|
import type { GlobalCache } from '../../storage';
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { CloudDocMetaStore } from '../stores/cloud-doc-meta';
|
import type { CloudDocMetaStore } from '../stores/cloud-doc-meta';
|
||||||
|
|
||||||
export type CloudDocMetaType =
|
export type CloudDocMetaType =
|
||||||
@@ -47,13 +46,7 @@ export class CloudDocMeta extends Entity {
|
|||||||
return fromPromise(
|
return fromPromise(
|
||||||
this.store.fetchCloudDocMeta(this.workspaceId, this.docId)
|
this.store.fetchCloudDocMeta(this.workspaceId, this.docId)
|
||||||
).pipe(
|
).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(meta => {
|
mergeMap(meta => {
|
||||||
this.cache.set<CloudDocMetaType>(this.cacheKey, meta);
|
this.cache.set<CloudDocMetaType>(this.cacheKey, meta);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { InvoicesQuery } from '@affine/graphql';
|
import type { InvoicesQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,10 +8,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { InvoicesStore } from '../stores/invoices';
|
import type { InvoicesStore } from '../stores/invoices';
|
||||||
|
|
||||||
export type Invoice = NonNullable<
|
export type Invoice = NonNullable<
|
||||||
@@ -50,13 +49,7 @@ export class Invoices extends Entity {
|
|||||||
this.pageInvoices$.setValue(data.invoices);
|
this.pageInvoices$.setValue(data.invoices);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.pageInvoices$.setValue(undefined);
|
this.pageInvoices$.setValue(undefined);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
backoffRetry,
|
||||||
effect,
|
effect,
|
||||||
@@ -12,7 +13,6 @@ import { isEqual } from 'lodash-es';
|
|||||||
import { EMPTY, mergeMap } from 'rxjs';
|
import { EMPTY, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { validateAndReduceImage } from '../../../utils/reduce-image';
|
import { validateAndReduceImage } from '../../../utils/reduce-image';
|
||||||
import { BackendError } from '../error';
|
|
||||||
import type { AccountProfile, AuthStore } from '../stores/auth';
|
import type { AccountProfile, AuthStore } from '../stores/auth';
|
||||||
|
|
||||||
export interface AuthSessionInfo {
|
export interface AuthSessionInfo {
|
||||||
@@ -114,10 +114,7 @@ export class AuthSession extends Entity {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (
|
if (UserFriendlyError.fromAny(e).is('UNSUPPORTED_CLIENT_VERSION')) {
|
||||||
e instanceof BackendError &&
|
|
||||||
e.originError.name === 'UNSUPPORTED_CLIENT_VERSION'
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { PricesQuery } from '@affine/graphql';
|
import type { PricesQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,10 +8,10 @@ import {
|
|||||||
mapInto,
|
mapInto,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { exhaustMap } from 'rxjs';
|
import { exhaustMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { ServerService } from '../services/server';
|
import type { ServerService } from '../services/server';
|
||||||
import type { SubscriptionStore } from '../stores/subscription';
|
import type { SubscriptionStore } from '../stores/subscription';
|
||||||
|
|
||||||
@@ -55,13 +54,7 @@ export class SubscriptionPrices extends Entity {
|
|||||||
}
|
}
|
||||||
return this.store.fetchSubscriptionPrices(signal);
|
return this.store.fetchSubscriptionPrices(signal);
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mapInto(this.prices$),
|
mapInto(this.prices$),
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => this.isRevalidating$.next(true)),
|
onStart(() => this.isRevalidating$.next(true)),
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
SubscriptionVariant,
|
SubscriptionVariant,
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -14,10 +13,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { AuthService } from '../services/auth';
|
import type { AuthService } from '../services/auth';
|
||||||
import type { ServerService } from '../services/server';
|
import type { ServerService } from '../services/server';
|
||||||
import type { SubscriptionStore } from '../stores/subscription';
|
import type { SubscriptionStore } from '../stores/subscription';
|
||||||
@@ -122,13 +121,7 @@ export class Subscription extends Entity {
|
|||||||
subscriptions: subscriptions,
|
subscriptions: subscriptions,
|
||||||
};
|
};
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
this.store.setCachedSubscriptions(
|
this.store.setCachedSubscriptions(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -8,10 +7,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { AuthService } from '../services/auth';
|
import type { AuthService } from '../services/auth';
|
||||||
import type { ServerService } from '../services/server';
|
import type { ServerService } from '../services/server';
|
||||||
import type { UserCopilotQuotaStore } from '../stores/user-copilot-quota';
|
import type { UserCopilotQuotaStore } from '../stores/user-copilot-quota';
|
||||||
@@ -54,13 +53,7 @@ export class UserCopilotQuota extends Entity {
|
|||||||
|
|
||||||
return aiQuota;
|
return aiQuota;
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
const { limit, used } = data;
|
const { limit, used } = data;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { FeatureType } from '@affine/graphql';
|
import { FeatureType } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,10 +8,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { AuthService } from '../services/auth';
|
import type { AuthService } from '../services/auth';
|
||||||
import type { UserFeatureStore } from '../stores/user-feature';
|
import type { UserFeatureStore } from '../stores/user-feature';
|
||||||
|
|
||||||
@@ -59,13 +58,7 @@ export class UserFeature extends Entity {
|
|||||||
features: features,
|
features: features,
|
||||||
};
|
};
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
this.features$.next(data.features);
|
this.features$.next(data.features);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { QuotaQuery } from '@affine/graphql';
|
import type { QuotaQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,12 +8,12 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import bytes from 'bytes';
|
import bytes from 'bytes';
|
||||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { AuthService } from '../services/auth';
|
import type { AuthService } from '../services/auth';
|
||||||
import type { UserQuotaStore } from '../stores/user-quota';
|
import type { UserQuotaStore } from '../stores/user-quota';
|
||||||
|
|
||||||
@@ -79,13 +78,7 @@ export class UserQuota extends Entity {
|
|||||||
|
|
||||||
return { quota, used };
|
return { quota, used };
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
const { quota, used } = data;
|
const { quota, used } = data;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { InvoicesQuery } from '@affine/graphql';
|
import type { InvoicesQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,11 +8,11 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, map, mergeMap } from 'rxjs';
|
import { EMPTY, map, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { WorkspaceServerService } from '../services/workspace-server';
|
import type { WorkspaceServerService } from '../services/workspace-server';
|
||||||
import { InvoicesStore } from '../stores/invoices';
|
import { InvoicesStore } from '../stores/invoices';
|
||||||
|
|
||||||
@@ -61,13 +60,7 @@ export class WorkspaceInvoices extends Entity {
|
|||||||
this.pageInvoices$.setValue(data.invoices);
|
this.pageInvoices$.setValue(data.invoices);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.pageInvoices$.setValue(undefined);
|
this.pageInvoices$.setValue(undefined);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { SubscriptionQuery, SubscriptionRecurring } from '@affine/graphql';
|
import type { SubscriptionQuery, SubscriptionRecurring } from '@affine/graphql';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -10,11 +9,11 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, mergeMap } from 'rxjs';
|
import { EMPTY, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { WorkspaceServerService } from '../services/workspace-server';
|
import type { WorkspaceServerService } from '../services/workspace-server';
|
||||||
import { SubscriptionStore } from '../stores/subscription';
|
import { SubscriptionStore } from '../stores/subscription';
|
||||||
export type SubscriptionType = NonNullable<
|
export type SubscriptionType = NonNullable<
|
||||||
@@ -123,13 +122,7 @@ export class WorkspaceSubscription extends Entity {
|
|||||||
subscription: subscription,
|
subscription: subscription,
|
||||||
};
|
};
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data && data.subscription && data.workspaceId && this.store) {
|
if (data && data.subscription && data.workspaceId && this.store) {
|
||||||
this.store.setCachedWorkspaceSubscription(
|
this.store.setCachedWorkspaceSubscription(
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import type { UserFriendlyError } from '@affine/graphql';
|
|
||||||
|
|
||||||
export class NetworkError extends Error {
|
|
||||||
constructor(
|
|
||||||
public readonly originError: Error,
|
|
||||||
public readonly status?: number
|
|
||||||
) {
|
|
||||||
super(`Network error: ${originError.message}`);
|
|
||||||
this.stack = originError.stack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNetworkError(error: Error): error is NetworkError {
|
|
||||||
return error instanceof NetworkError;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BackendError extends Error {
|
|
||||||
get status() {
|
|
||||||
return this.originError.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(public readonly originError: UserFriendlyError) {
|
|
||||||
super(`Server error: ${originError.message}`);
|
|
||||||
this.stack = originError.stack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isBackendError(error: Error): error is BackendError {
|
|
||||||
return error instanceof BackendError;
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
export type { Invoice } from './entities/invoices';
|
export type { Invoice } from './entities/invoices';
|
||||||
export { Server } from './entities/server';
|
export { Server } from './entities/server';
|
||||||
export type { AuthAccountInfo } from './entities/session';
|
export type { AuthAccountInfo } from './entities/session';
|
||||||
export {
|
|
||||||
BackendError,
|
|
||||||
isBackendError,
|
|
||||||
isNetworkError,
|
|
||||||
NetworkError,
|
|
||||||
} from './error';
|
|
||||||
export { AccountChanged } from './events/account-changed';
|
export { AccountChanged } from './events/account-changed';
|
||||||
export { AccountLoggedIn } from './events/account-logged-in';
|
export { AccountLoggedIn } from './events/account-logged-in';
|
||||||
export { AccountLoggedOut } from './events/account-logged-out';
|
export { AccountLoggedOut } from './events/account-logged-out';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GetInviteInfoQuery } from '@affine/graphql';
|
import type { GetInviteInfoQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
fromPromise,
|
fromPromise,
|
||||||
@@ -8,10 +7,10 @@ import {
|
|||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
Service,
|
Service,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { AcceptInviteStore } from '../stores/accept-invite';
|
import type { AcceptInviteStore } from '../stores/accept-invite';
|
||||||
import type { InviteInfoStore } from '../stores/invite-info';
|
import type { InviteInfoStore } from '../stores/invite-info';
|
||||||
|
|
||||||
@@ -52,14 +51,7 @@ export class AcceptInviteService extends Service {
|
|||||||
this.accepted$.next(res);
|
this.accepted$.next(res);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
count: 3,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.inviteId$.setValue(inviteId);
|
this.inviteId$.setValue(inviteId);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AIProvider } from '@affine/core/blocksuite/ai';
|
import { AIProvider } from '@affine/core/blocksuite/ai';
|
||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import type { OAuthProviderType } from '@affine/graphql';
|
import type { OAuthProviderType } from '@affine/graphql';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { OnEvent, Service } from '@toeverything/infra';
|
import { OnEvent, Service } from '@toeverything/infra';
|
||||||
@@ -7,7 +8,6 @@ import { distinctUntilChanged, map, skip } from 'rxjs';
|
|||||||
import { ApplicationFocused } from '../../lifecycle';
|
import { ApplicationFocused } from '../../lifecycle';
|
||||||
import type { UrlService } from '../../url';
|
import type { UrlService } from '../../url';
|
||||||
import { type AuthAccountInfo, AuthSession } from '../entities/session';
|
import { type AuthAccountInfo, AuthSession } from '../entities/session';
|
||||||
import { BackendError } from '../error';
|
|
||||||
import { AccountChanged } from '../events/account-changed';
|
import { AccountChanged } from '../events/account-changed';
|
||||||
import { AccountLoggedIn } from '../events/account-logged-in';
|
import { AccountLoggedIn } from '../events/account-logged-in';
|
||||||
import { AccountLoggedOut } from '../events/account-logged-out';
|
import { AccountLoggedOut } from '../events/account-logged-out';
|
||||||
@@ -103,7 +103,7 @@ export class AuthService extends Service {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
track.$.$.auth.signInFail({
|
track.$.$.auth.signInFail({
|
||||||
method: 'magic-link',
|
method: 'magic-link',
|
||||||
reason: e instanceof BackendError ? e.originError.name : 'unknown',
|
reason: UserFriendlyError.fromAny(e).name,
|
||||||
});
|
});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ export class AuthService extends Service {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
track.$.$.auth.signInFail({
|
track.$.$.auth.signInFail({
|
||||||
method,
|
method,
|
||||||
reason: e instanceof BackendError ? e.originError.name : 'unknown',
|
reason: UserFriendlyError.fromAny(e).name,
|
||||||
});
|
});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ export class AuthService extends Service {
|
|||||||
track.$.$.auth.signInFail({
|
track.$.$.auth.signInFail({
|
||||||
method: 'oauth',
|
method: 'oauth',
|
||||||
provider,
|
provider,
|
||||||
reason: e instanceof BackendError ? e.originError.name : 'unknown',
|
reason: UserFriendlyError.fromAny(e).name,
|
||||||
});
|
});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ export class AuthService extends Service {
|
|||||||
track.$.$.auth.signInFail({
|
track.$.$.auth.signInFail({
|
||||||
method: 'oauth',
|
method: 'oauth',
|
||||||
provider,
|
provider,
|
||||||
reason: e instanceof BackendError ? e.originError.name : 'unknown',
|
reason: UserFriendlyError.fromAny(e).name,
|
||||||
});
|
});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ export class AuthService extends Service {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
track.$.$.auth.signInFail({
|
track.$.$.auth.signInFail({
|
||||||
method: 'password',
|
method: 'password',
|
||||||
reason: e instanceof BackendError ? e.originError.name : 'unknown',
|
reason: UserFriendlyError.fromAny(e).name,
|
||||||
});
|
});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import { fromPromise, Service } from '@toeverything/infra';
|
import { fromPromise, Service } from '@toeverything/infra';
|
||||||
|
|
||||||
import { BackendError, NetworkError } from '../error';
|
|
||||||
import type { ServerService } from './server';
|
import type { ServerService } from './server';
|
||||||
|
|
||||||
const logger = new DebugLogger('affine:fetch');
|
const logger = new DebugLogger('affine:fetch');
|
||||||
@@ -46,40 +45,54 @@ export class FetchService extends Service {
|
|||||||
abortController.abort('timeout');
|
abortController.abort('timeout');
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
|
||||||
const res = await globalThis
|
let res: Response;
|
||||||
.fetch(new URL(input, this.serverService.server.serverMetadata.baseUrl), {
|
|
||||||
...init,
|
try {
|
||||||
signal: abortController.signal,
|
res = await globalThis.fetch(
|
||||||
headers: {
|
new URL(input, this.serverService.server.serverMetadata.baseUrl),
|
||||||
...init?.headers,
|
{
|
||||||
'x-affine-version': BUILD_CONFIG.appVersion,
|
...init,
|
||||||
},
|
signal: abortController.signal,
|
||||||
})
|
headers: {
|
||||||
.catch(err => {
|
...init?.headers,
|
||||||
logger.debug('network error', err);
|
'x-affine-version': BUILD_CONFIG.appVersion,
|
||||||
throw new NetworkError(err);
|
},
|
||||||
});
|
}
|
||||||
clearTimeout(timeoutId);
|
|
||||||
if (res.status === 504) {
|
|
||||||
const error = new Error('Gateway Timeout');
|
|
||||||
logger.debug('network error', error);
|
|
||||||
throw new NetworkError(error, res.status);
|
|
||||||
}
|
|
||||||
if (!res.ok) {
|
|
||||||
logger.warn(
|
|
||||||
'backend error',
|
|
||||||
new Error(`${res.status} ${res.statusText}`)
|
|
||||||
);
|
);
|
||||||
let reason: string | any = '';
|
} catch (err: any) {
|
||||||
if (res.headers.get('Content-Type')?.includes('application/json')) {
|
throw new UserFriendlyError({
|
||||||
try {
|
status: 504,
|
||||||
reason = await res.json();
|
code: 'NETWORK_ERROR',
|
||||||
} catch {
|
type: 'NETWORK_ERROR',
|
||||||
// ignore
|
name: 'NETWORK_ERROR',
|
||||||
|
message: `Network error: ${err.message}`,
|
||||||
|
stacktrace: err.stack,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
if (res.status === 504) {
|
||||||
|
const error = new Error('Gateway Timeout');
|
||||||
|
logger.debug('network error', error);
|
||||||
|
throw new UserFriendlyError({
|
||||||
|
status: 504,
|
||||||
|
code: 'NETWORK_ERROR',
|
||||||
|
type: 'NETWORK_ERROR',
|
||||||
|
name: 'NETWORK_ERROR',
|
||||||
|
message: 'Gateway Timeout',
|
||||||
|
stacktrace: error.stack,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (res.headers.get('Content-Type') === 'application/json') {
|
||||||
|
throw UserFriendlyError.fromAny(await res.json());
|
||||||
|
} else {
|
||||||
|
throw UserFriendlyError.fromAny(await res.text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new BackendError(UserFriendlyError.fromAnyError(reason));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
gqlFetcherFactory,
|
gqlFetcherFactory,
|
||||||
GraphQLError,
|
|
||||||
type GraphQLQuery,
|
type GraphQLQuery,
|
||||||
type QueryOptions,
|
type QueryOptions,
|
||||||
type QueryResponse,
|
type QueryResponse,
|
||||||
UserFriendlyError,
|
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
import { fromPromise, Service } from '@toeverything/infra';
|
import { fromPromise, Service } from '@toeverything/infra';
|
||||||
import type { Observable } from 'rxjs';
|
import type { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { BackendError } from '../error';
|
|
||||||
import { AuthService } from './auth';
|
import { AuthService } from './auth';
|
||||||
import type { FetchService } from './fetch';
|
import type { FetchService } from './fetch';
|
||||||
|
|
||||||
@@ -40,16 +38,9 @@ export class GraphQLService extends Service {
|
|||||||
try {
|
try {
|
||||||
return await this.rawGql(options);
|
return await this.rawGql(options);
|
||||||
} catch (anyError) {
|
} catch (anyError) {
|
||||||
let error = anyError;
|
const error = UserFriendlyError.fromAny(anyError);
|
||||||
|
|
||||||
// NOTE(@forehalo):
|
if (error.isStatus(401)) {
|
||||||
// GraphQL error is not present by non-200 status code, but by responding `errors` fields in the body
|
|
||||||
// So it will never be `BackendError` originally.
|
|
||||||
if (anyError instanceof GraphQLError) {
|
|
||||||
error = new BackendError(UserFriendlyError.fromAnyError(anyError));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof BackendError && error.status === 403) {
|
|
||||||
this.framework.get(AuthService).session.revalidate();
|
this.framework.get(AuthService).session.revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { UserFriendlyError } from '@affine/graphql';
|
import { type UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
fromPromise,
|
fromPromise,
|
||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
Service,
|
Service,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { catchError, EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { SelfhostGenerateLicenseStore } from '../stores/selfhost-generate-license';
|
import type { SelfhostGenerateLicenseStore } from '../stores/selfhost-generate-license';
|
||||||
|
|
||||||
export class SelfhostGenerateLicenseService extends Service {
|
export class SelfhostGenerateLicenseService extends Service {
|
||||||
@@ -26,22 +26,12 @@ export class SelfhostGenerateLicenseService extends Service {
|
|||||||
return fromPromise(async () => {
|
return fromPromise(async () => {
|
||||||
return await this.store.generateKey(sessionId);
|
return await this.store.generateKey(sessionId);
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(key => {
|
mergeMap(key => {
|
||||||
this.licenseKey$.next(key);
|
this.licenseKey$.next(key);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
catchError(err => {
|
catchErrorInto(this.error$),
|
||||||
this.error$.next(UserFriendlyError.fromAnyError(err));
|
|
||||||
console.error(err);
|
|
||||||
return EMPTY;
|
|
||||||
}),
|
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.isLoading$.next(true);
|
this.isLoading$.next(true);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { License } from '@affine/graphql';
|
import type { License } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
exhaustMapWithTrailing,
|
exhaustMapWithTrailing,
|
||||||
@@ -9,11 +8,11 @@ import {
|
|||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
Service,
|
Service,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, mergeMap } from 'rxjs';
|
import { EMPTY, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import { isBackendError, isNetworkError } from '../error';
|
|
||||||
import type { SelfhostLicenseStore } from '../stores/selfhost-license';
|
import type { SelfhostLicenseStore } from '../stores/selfhost-license';
|
||||||
|
|
||||||
export class SelfhostLicenseService extends Service {
|
export class SelfhostLicenseService extends Service {
|
||||||
@@ -36,13 +35,7 @@ export class SelfhostLicenseService extends Service {
|
|||||||
}
|
}
|
||||||
return await this.store.getLicense(currentWorkspaceId, signal);
|
return await this.store.getLicense(currentWorkspaceId, signal);
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
this.license$.next(data);
|
this.license$.next(data);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -7,10 +6,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, mergeMap, switchMap } from 'rxjs';
|
import { EMPTY, mergeMap, switchMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { TemplateDownloaderStore } from '../store/downloader';
|
import type { TemplateDownloaderStore } from '../store/downloader';
|
||||||
|
|
||||||
export class TemplateDownloader extends Entity {
|
export class TemplateDownloader extends Entity {
|
||||||
@@ -29,13 +28,7 @@ export class TemplateDownloader extends Entity {
|
|||||||
this.data$.next(data);
|
this.data$.next(data);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.isDownloading$.next(true);
|
this.isDownloading$.next(true);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GetMembersByWorkspaceIdQuery } from '@affine/graphql';
|
import type { GetMembersByWorkspaceIdQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -8,10 +7,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, map, mergeMap, switchMap } from 'rxjs';
|
import { EMPTY, map, mergeMap, switchMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { WorkspaceMembersStore } from '../stores/members';
|
import type { WorkspaceMembersStore } from '../stores/members';
|
||||||
|
|
||||||
@@ -51,13 +50,7 @@ export class WorkspaceMembers extends Entity {
|
|||||||
this.pageMembers$.setValue(data.members);
|
this.pageMembers$.setValue(data.members);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.pageMembers$.setValue(undefined);
|
this.pageMembers$.setValue(undefined);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DocRole, type GetPageGrantedUsersListQuery } from '@affine/graphql';
|
import { DocRole, type GetPageGrantedUsersListQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
fromPromise,
|
fromPromise,
|
||||||
@@ -8,10 +7,10 @@ import {
|
|||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
Service,
|
Service,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { DocService } from '../../doc';
|
import type { DocService } from '../../doc';
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { DocGrantedUsersStore } from '../stores/doc-granted-users';
|
import type { DocGrantedUsersStore } from '../stores/doc-granted-users';
|
||||||
@@ -65,13 +64,7 @@ export class DocGrantedUsersService extends Service {
|
|||||||
|
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.isLoading$.setValue(true);
|
this.isLoading$.setValue(true);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
fromPromise,
|
fromPromise,
|
||||||
@@ -7,10 +6,10 @@ import {
|
|||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
Service,
|
Service,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { Member } from '../entities/members';
|
import type { Member } from '../entities/members';
|
||||||
import type { MemberSearchStore } from '../stores/member-search';
|
import type { MemberSearchStore } from '../stores/member-search';
|
||||||
@@ -50,13 +49,7 @@ export class MemberSearchService extends Service {
|
|||||||
|
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => {
|
onStart(() => {
|
||||||
this.isLoading$.setValue(true);
|
this.isLoading$.setValue(true);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import type { WorkspaceQuotaQuery } from '@affine/graphql';
|
import type { WorkspaceQuotaQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -10,12 +9,12 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
import bytes from 'bytes';
|
import bytes from 'bytes';
|
||||||
import { EMPTY, mergeMap } from 'rxjs';
|
import { EMPTY, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { WorkspaceQuotaStore } from '../stores/quota';
|
import type { WorkspaceQuotaStore } from '../stores/quota';
|
||||||
|
|
||||||
@@ -76,14 +75,7 @@ export class WorkspaceQuota extends Entity {
|
|||||||
);
|
);
|
||||||
return { quota: data, used: data.usedStorageQuota };
|
return { quota: data, used: data.usedStorageQuota };
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
count: 3,
|
|
||||||
}),
|
|
||||||
mergeMap(data => {
|
mergeMap(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
const { quota, used } = data;
|
const { quota, used } = data;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import type { GetWorkspacePublicPagesQuery } from '@affine/graphql';
|
import type { GetWorkspacePublicPagesQuery } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -10,10 +9,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, mergeMap } from 'rxjs';
|
import { EMPTY, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { GlobalCache } from '../../storage';
|
import type { GlobalCache } from '../../storage';
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { ShareDocsStore } from '../stores/share-docs';
|
import type { ShareDocsStore } from '../stores/share-docs';
|
||||||
@@ -43,13 +42,7 @@ export class ShareDocsList extends Entity {
|
|||||||
signal
|
signal
|
||||||
);
|
);
|
||||||
}).pipe(
|
}).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mergeMap(list => {
|
mergeMap(list => {
|
||||||
this.cache.set('share-docs', list);
|
this.cache.set('share-docs', list);
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GetWorkspacePageByIdQuery, PublicDocMode } from '@affine/graphql';
|
import type { GetWorkspacePageByIdQuery, PublicDocMode } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,10 +8,10 @@ import {
|
|||||||
mapInto,
|
mapInto,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { switchMap } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { DocService } from '../../doc';
|
import type { DocService } from '../../doc';
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { ShareStore } from '../stores/share';
|
import type { ShareStore } from '../stores/share';
|
||||||
@@ -44,13 +43,7 @@ export class ShareInfo extends Entity {
|
|||||||
signal
|
signal
|
||||||
)
|
)
|
||||||
).pipe(
|
).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
}),
|
|
||||||
mapInto(this.info$),
|
mapInto(this.info$),
|
||||||
catchErrorInto(this.error$),
|
catchErrorInto(this.error$),
|
||||||
onStart(() => this.isRevalidating$.next(true)),
|
onStart(() => this.isRevalidating$.next(true)),
|
||||||
|
|||||||
+3
-2
@@ -8,7 +8,8 @@ import {
|
|||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { DocGrantedUsersService } from '@affine/core/modules/permissions';
|
import { DocGrantedUsersService } from '@affine/core/modules/permissions';
|
||||||
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
||||||
import { DocRole, UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
|
import { DocRole } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { InformationIcon } from '@blocksuite/icons/rc';
|
import { InformationIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -62,7 +63,7 @@ export const MembersPermission = ({
|
|||||||
await docGrantedUsersService.updateDocDefaultRole(docRole);
|
await docGrantedUsersService.updateDocDefaultRole(docRole);
|
||||||
shareInfoService.shareInfo.revalidate();
|
shareInfoService.shareInfo.revalidate();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAny(error);
|
||||||
notify.error({
|
notify.error({
|
||||||
title: err.name,
|
title: err.name,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
import { Menu, MenuItem, MenuTrigger, notify } from '@affine/component';
|
import { Menu, MenuItem, MenuTrigger, notify } from '@affine/component';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
import { ShareInfoService } from '@affine/core/modules/share-doc';
|
||||||
import { PublicDocMode, UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
|
import { PublicDocMode } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import track from '@affine/track';
|
import track from '@affine/track';
|
||||||
import {
|
import {
|
||||||
@@ -74,7 +75,7 @@ export const PublicDoc = ({ disabled }: { disabled?: boolean }) => {
|
|||||||
icon: <SingleSelectCheckSolidIcon color={cssVar('primaryColor')} />,
|
icon: <SingleSelectCheckSolidIcon color={cssVar('primaryColor')} />,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAny(error);
|
||||||
notify.error({
|
notify.error({
|
||||||
title: err.name,
|
title: err.name,
|
||||||
message: err.message,
|
message: err.message,
|
||||||
|
|||||||
+3
-6
@@ -14,11 +14,8 @@ import {
|
|||||||
type Member,
|
type Member,
|
||||||
MemberSearchService,
|
MemberSearchService,
|
||||||
} from '@affine/core/modules/permissions';
|
} from '@affine/core/modules/permissions';
|
||||||
import {
|
import { UserFriendlyError } from '@affine/error';
|
||||||
DocRole,
|
import { DocRole, WorkspaceMemberStatus } from '@affine/graphql';
|
||||||
UserFriendlyError,
|
|
||||||
WorkspaceMemberStatus,
|
|
||||||
} from '@affine/graphql';
|
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { ArrowLeftBigIcon } from '@blocksuite/icons/rc';
|
import { ArrowLeftBigIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -113,7 +110,7 @@ export const InviteMemberEditor = ({
|
|||||||
title: t['Invitation sent'](),
|
title: t['Invitation sent'](),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAny(error);
|
||||||
notify.error({
|
notify.error({
|
||||||
title: t[`error.${err.name}`](err.data),
|
title: t[`error.${err.name}`](err.data),
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-3
@@ -16,7 +16,8 @@ import {
|
|||||||
GuardService,
|
GuardService,
|
||||||
WorkspacePermissionService,
|
WorkspacePermissionService,
|
||||||
} from '@affine/core/modules/permissions';
|
} from '@affine/core/modules/permissions';
|
||||||
import { DocRole, UserFriendlyError } from '@affine/graphql';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
|
import { DocRole } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import track from '@affine/track';
|
import track from '@affine/track';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -161,7 +162,7 @@ const Options = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAny(error);
|
||||||
notify.error({
|
notify.error({
|
||||||
title: t[`error.${err.name}`](err.data),
|
title: t[`error.${err.name}`](err.data),
|
||||||
});
|
});
|
||||||
@@ -219,7 +220,7 @@ const Options = ({
|
|||||||
await docGrantedUsersService.revokeUsersRole(userId);
|
await docGrantedUsersService.revokeUsersRole(userId);
|
||||||
docGrantedUsersService.loadMore();
|
docGrantedUsersService.loadMore();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = UserFriendlyError.fromAnyError(error);
|
const err = UserFriendlyError.fromAny(error);
|
||||||
notify.error({
|
notify.error({
|
||||||
title: t[`error.${err.name}`](err.data),
|
title: t[`error.${err.name}`](err.data),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import type { GetWorkspaceConfigQuery, InviteLink } from '@affine/graphql';
|
import type { GetWorkspaceConfigQuery, InviteLink } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
backoffRetry,
|
|
||||||
catchErrorInto,
|
catchErrorInto,
|
||||||
effect,
|
effect,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -9,10 +8,10 @@ import {
|
|||||||
LiveData,
|
LiveData,
|
||||||
onComplete,
|
onComplete,
|
||||||
onStart,
|
onStart,
|
||||||
|
smartRetry,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||||
|
|
||||||
import { isBackendError, isNetworkError } from '../../cloud';
|
|
||||||
import type { WorkspaceService } from '../../workspace';
|
import type { WorkspaceService } from '../../workspace';
|
||||||
import type { WorkspaceShareSettingStore } from '../stores/share-setting';
|
import type { WorkspaceShareSettingStore } from '../stores/share-setting';
|
||||||
|
|
||||||
@@ -45,14 +44,7 @@ export class WorkspaceShareSetting extends Entity {
|
|||||||
signal
|
signal
|
||||||
)
|
)
|
||||||
).pipe(
|
).pipe(
|
||||||
backoffRetry({
|
smartRetry(),
|
||||||
when: isNetworkError,
|
|
||||||
count: Infinity,
|
|
||||||
}),
|
|
||||||
backoffRetry({
|
|
||||||
when: isBackendError,
|
|
||||||
count: 3,
|
|
||||||
}),
|
|
||||||
mergeMap(value => {
|
mergeMap(value => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.enableAi$.next(value.enableAi);
|
this.enableAi$.next(value.enableAi);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
{ "path": "../../common/debug" },
|
{ "path": "../../common/debug" },
|
||||||
{ "path": "../electron-api" },
|
{ "path": "../electron-api" },
|
||||||
{ "path": "../../common/env" },
|
{ "path": "../../common/env" },
|
||||||
|
{ "path": "../../common/error" },
|
||||||
{ "path": "../graphql" },
|
{ "path": "../graphql" },
|
||||||
{ "path": "../i18n" },
|
{ "path": "../i18n" },
|
||||||
{ "path": "../../common/nbstore" },
|
{ "path": "../../common/nbstore" },
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@affine/env": "workspace:*",
|
"@affine/env": "workspace:*",
|
||||||
|
"@affine/error": "workspace:*",
|
||||||
"graphql": "^16.9.0",
|
"graphql": "^16.9.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21"
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import { GraphQLError as BaseGraphQLError } from 'graphql';
|
|
||||||
|
|
||||||
import { type ErrorDataUnion, ErrorNames } from './schema';
|
|
||||||
|
|
||||||
export interface UserFriendlyErrorResponse {
|
|
||||||
status: number;
|
|
||||||
code: string;
|
|
||||||
type: string;
|
|
||||||
name: ErrorNames;
|
|
||||||
message: string;
|
|
||||||
data?: any;
|
|
||||||
stacktrace?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UserFriendlyError
|
|
||||||
extends Error
|
|
||||||
implements UserFriendlyErrorResponse
|
|
||||||
{
|
|
||||||
readonly status = this.response.status;
|
|
||||||
readonly code = this.response.code;
|
|
||||||
readonly type = this.response.type;
|
|
||||||
readonly rawName = this.response.name;
|
|
||||||
override readonly message = this.response.message;
|
|
||||||
readonly data = this.response.data;
|
|
||||||
readonly stacktrace = this.response.stacktrace;
|
|
||||||
|
|
||||||
override get name() {
|
|
||||||
if (this.rawName in ErrorNames) {
|
|
||||||
return this.rawName;
|
|
||||||
}
|
|
||||||
return ErrorNames.INTERNAL_SERVER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromAnyError(response: any) {
|
|
||||||
if (response instanceof GraphQLError) {
|
|
||||||
return new UserFriendlyError(response.extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
'originError' in response &&
|
|
||||||
response.originError instanceof UserFriendlyError
|
|
||||||
) {
|
|
||||||
return response.originError as UserFriendlyError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
response &&
|
|
||||||
typeof response === 'object' &&
|
|
||||||
response.type &&
|
|
||||||
response.name
|
|
||||||
) {
|
|
||||||
return new UserFriendlyError(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UserFriendlyError({
|
|
||||||
status: 500,
|
|
||||||
code: 'INTERNAL_SERVER_ERROR',
|
|
||||||
type: 'INTERNAL_SERVER_ERROR',
|
|
||||||
name: ErrorNames.INTERNAL_SERVER_ERROR,
|
|
||||||
message: 'Internal server error',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private readonly response: UserFriendlyErrorResponse) {
|
|
||||||
super(response.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GraphQLError extends BaseGraphQLError {
|
|
||||||
// @ts-expect-error better to be a known type without any type casting
|
|
||||||
override extensions!: UserFriendlyErrorResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ToPascalCase<S extends string> = S extends `${infer A}_${infer B}`
|
|
||||||
? `${Capitalize<Lowercase<A>>}${ToPascalCase<B>}`
|
|
||||||
: Capitalize<Lowercase<S>>;
|
|
||||||
|
|
||||||
export type ErrorData = {
|
|
||||||
[K in ErrorNames]: Extract<
|
|
||||||
ErrorDataUnion,
|
|
||||||
{ __typename?: `${ToPascalCase<K>}DataType` }
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
|
import { GraphQLError } from '@affine/error';
|
||||||
import type { ExecutionResult } from 'graphql';
|
import type { ExecutionResult } from 'graphql';
|
||||||
import { isNil, isObject, merge } from 'lodash-es';
|
import { isNil, isObject, merge } from 'lodash-es';
|
||||||
|
|
||||||
import { GraphQLError } from './error';
|
|
||||||
import type { GraphQLQuery } from './graphql';
|
import type { GraphQLQuery } from './graphql';
|
||||||
import type { Mutations, Queries } from './schema';
|
import type { Mutations, Queries } from './schema';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './error';
|
|
||||||
export * from './fetcher';
|
export * from './fetcher';
|
||||||
export * from './graphql';
|
export * from './graphql';
|
||||||
export * from './schema';
|
export * from './schema';
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
},
|
},
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "../../common/debug" },
|
{ "path": "../../common/debug" },
|
||||||
{ "path": "../../common/env" }
|
{ "path": "../../common/env" },
|
||||||
|
{ "path": "../../common/error" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -7067,6 +7067,10 @@ export function useAFFiNEI18N(): {
|
|||||||
* `An internal error occurred.`
|
* `An internal error occurred.`
|
||||||
*/
|
*/
|
||||||
["error.INTERNAL_SERVER_ERROR"](): string;
|
["error.INTERNAL_SERVER_ERROR"](): string;
|
||||||
|
/**
|
||||||
|
* `Network error.`
|
||||||
|
*/
|
||||||
|
["error.NETWORK_ERROR"](): string;
|
||||||
/**
|
/**
|
||||||
* `Too many requests.`
|
* `Too many requests.`
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1758,6 +1758,7 @@
|
|||||||
"com.affine.fail-to-join-workspace.description-1": "Unable to join <1/> <2>{{workspaceName}}</2> due to insufficient seats available.",
|
"com.affine.fail-to-join-workspace.description-1": "Unable to join <1/> <2>{{workspaceName}}</2> due to insufficient seats available.",
|
||||||
"com.affine.fail-to-join-workspace.description-2": "Please contact your workspace owner to add more seats.",
|
"com.affine.fail-to-join-workspace.description-2": "Please contact your workspace owner to add more seats.",
|
||||||
"error.INTERNAL_SERVER_ERROR": "An internal error occurred.",
|
"error.INTERNAL_SERVER_ERROR": "An internal error occurred.",
|
||||||
|
"error.NETWORK_ERROR": "Network error.",
|
||||||
"error.TOO_MANY_REQUEST": "Too many requests.",
|
"error.TOO_MANY_REQUEST": "Too many requests.",
|
||||||
"error.NOT_FOUND": "Resource not found.",
|
"error.NOT_FOUND": "Resource not found.",
|
||||||
"error.BAD_REQUEST": "Bad request.",
|
"error.BAD_REQUEST": "Bad request.",
|
||||||
|
|||||||
@@ -599,12 +599,18 @@ export const PackageList = [
|
|||||||
name: '@affine/env',
|
name: '@affine/env',
|
||||||
workspaceDependencies: [],
|
workspaceDependencies: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
location: 'packages/common/error',
|
||||||
|
name: '@affine/error',
|
||||||
|
workspaceDependencies: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
location: 'packages/common/infra',
|
location: 'packages/common/infra',
|
||||||
name: '@toeverything/infra',
|
name: '@toeverything/infra',
|
||||||
workspaceDependencies: [
|
workspaceDependencies: [
|
||||||
'packages/common/debug',
|
'packages/common/debug',
|
||||||
'packages/common/env',
|
'packages/common/env',
|
||||||
|
'packages/common/error',
|
||||||
'packages/frontend/templates',
|
'packages/frontend/templates',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -704,6 +710,7 @@ export const PackageList = [
|
|||||||
workspaceDependencies: [
|
workspaceDependencies: [
|
||||||
'packages/common/debug',
|
'packages/common/debug',
|
||||||
'packages/frontend/electron-api',
|
'packages/frontend/electron-api',
|
||||||
|
'packages/common/error',
|
||||||
'packages/frontend/graphql',
|
'packages/frontend/graphql',
|
||||||
'packages/frontend/i18n',
|
'packages/frontend/i18n',
|
||||||
'tools/utils',
|
'tools/utils',
|
||||||
@@ -718,6 +725,7 @@ export const PackageList = [
|
|||||||
'packages/common/debug',
|
'packages/common/debug',
|
||||||
'packages/frontend/electron-api',
|
'packages/frontend/electron-api',
|
||||||
'packages/common/env',
|
'packages/common/env',
|
||||||
|
'packages/common/error',
|
||||||
'packages/frontend/graphql',
|
'packages/frontend/graphql',
|
||||||
'packages/frontend/i18n',
|
'packages/frontend/i18n',
|
||||||
'packages/common/nbstore',
|
'packages/common/nbstore',
|
||||||
@@ -735,7 +743,11 @@ export const PackageList = [
|
|||||||
{
|
{
|
||||||
location: 'packages/frontend/graphql',
|
location: 'packages/frontend/graphql',
|
||||||
name: '@affine/graphql',
|
name: '@affine/graphql',
|
||||||
workspaceDependencies: ['packages/common/debug', 'packages/common/env'],
|
workspaceDependencies: [
|
||||||
|
'packages/common/debug',
|
||||||
|
'packages/common/env',
|
||||||
|
'packages/common/error',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
location: 'packages/frontend/i18n',
|
location: 'packages/frontend/i18n',
|
||||||
@@ -890,6 +902,7 @@ export type PackageName =
|
|||||||
| '@affine/server'
|
| '@affine/server'
|
||||||
| '@affine/debug'
|
| '@affine/debug'
|
||||||
| '@affine/env'
|
| '@affine/env'
|
||||||
|
| '@affine/error'
|
||||||
| '@toeverything/infra'
|
| '@toeverything/infra'
|
||||||
| '@affine/nbstore'
|
| '@affine/nbstore'
|
||||||
| '@affine/admin'
|
| '@affine/admin'
|
||||||
|
|||||||
@@ -95,6 +95,7 @@
|
|||||||
{ "path": "./packages/backend/server" },
|
{ "path": "./packages/backend/server" },
|
||||||
{ "path": "./packages/common/debug" },
|
{ "path": "./packages/common/debug" },
|
||||||
{ "path": "./packages/common/env" },
|
{ "path": "./packages/common/env" },
|
||||||
|
{ "path": "./packages/common/error" },
|
||||||
{ "path": "./packages/common/infra" },
|
{ "path": "./packages/common/infra" },
|
||||||
{ "path": "./packages/common/nbstore" },
|
{ "path": "./packages/common/nbstore" },
|
||||||
{ "path": "./packages/frontend/admin" },
|
{ "path": "./packages/frontend/admin" },
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ __metadata:
|
|||||||
"@affine-tools/utils": "workspace:*"
|
"@affine-tools/utils": "workspace:*"
|
||||||
"@affine/debug": "workspace:*"
|
"@affine/debug": "workspace:*"
|
||||||
"@affine/electron-api": "workspace:*"
|
"@affine/electron-api": "workspace:*"
|
||||||
|
"@affine/error": "workspace:*"
|
||||||
"@affine/graphql": "workspace:*"
|
"@affine/graphql": "workspace:*"
|
||||||
"@affine/i18n": "workspace:*"
|
"@affine/i18n": "workspace:*"
|
||||||
"@atlaskit/pragmatic-drag-and-drop": "npm:^1.4.0"
|
"@atlaskit/pragmatic-drag-and-drop": "npm:^1.4.0"
|
||||||
@@ -363,6 +364,7 @@ __metadata:
|
|||||||
"@affine/debug": "workspace:*"
|
"@affine/debug": "workspace:*"
|
||||||
"@affine/electron-api": "workspace:*"
|
"@affine/electron-api": "workspace:*"
|
||||||
"@affine/env": "workspace:*"
|
"@affine/env": "workspace:*"
|
||||||
|
"@affine/error": "workspace:*"
|
||||||
"@affine/graphql": "workspace:*"
|
"@affine/graphql": "workspace:*"
|
||||||
"@affine/i18n": "workspace:*"
|
"@affine/i18n": "workspace:*"
|
||||||
"@affine/nbstore": "workspace:*"
|
"@affine/nbstore": "workspace:*"
|
||||||
@@ -567,12 +569,24 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@affine/error@workspace:*, @affine/error@workspace:packages/common/error":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@affine/error@workspace:packages/common/error"
|
||||||
|
dependencies:
|
||||||
|
vitest: "npm:^3.0.7"
|
||||||
|
peerDependencies:
|
||||||
|
"@affine/graphql": "workspace:*"
|
||||||
|
graphql: ^16.9.0
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@affine/graphql@workspace:*, @affine/graphql@workspace:packages/frontend/graphql":
|
"@affine/graphql@workspace:*, @affine/graphql@workspace:packages/frontend/graphql":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine/graphql@workspace:packages/frontend/graphql"
|
resolution: "@affine/graphql@workspace:packages/frontend/graphql"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@affine/debug": "workspace:*"
|
"@affine/debug": "workspace:*"
|
||||||
"@affine/env": "workspace:*"
|
"@affine/env": "workspace:*"
|
||||||
|
"@affine/error": "workspace:*"
|
||||||
"@graphql-codegen/add": "npm:^5.0.3"
|
"@graphql-codegen/add": "npm:^5.0.3"
|
||||||
"@graphql-codegen/cli": "npm:5.0.5"
|
"@graphql-codegen/cli": "npm:5.0.5"
|
||||||
"@graphql-codegen/typescript": "npm:^4.1.2"
|
"@graphql-codegen/typescript": "npm:^4.1.2"
|
||||||
@@ -13479,6 +13493,7 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@affine/debug": "workspace:*"
|
"@affine/debug": "workspace:*"
|
||||||
"@affine/env": "workspace:*"
|
"@affine/env": "workspace:*"
|
||||||
|
"@affine/error": "workspace:*"
|
||||||
"@affine/templates": "workspace:*"
|
"@affine/templates": "workspace:*"
|
||||||
"@datastructures-js/binary-search-tree": "npm:^5.3.2"
|
"@datastructures-js/binary-search-tree": "npm:^5.3.2"
|
||||||
"@emotion/react": "npm:^11.14.0"
|
"@emotion/react": "npm:^11.14.0"
|
||||||
@@ -32883,7 +32898,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vitest@npm:3.0.7, vitest@npm:^3.0.0":
|
"vitest@npm:3.0.7, vitest@npm:^3.0.0, vitest@npm:^3.0.7":
|
||||||
version: 3.0.7
|
version: 3.0.7
|
||||||
resolution: "vitest@npm:3.0.7"
|
resolution: "vitest@npm:3.0.7"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in new issue
Block a user