Files
moeka-project/packages/server-shared/src/errors.ts
T
2026-08-26 20:13:10 +08:00

180 lines
5.5 KiB
TypeScript

export const ServerErrorMessages = {
invalidEventFormat: 'invalid event format',
invalidToken: 'invalid token',
mustAuthenticateBeforeAnnouncing: 'must authenticate before announcing',
moduleAnnounceIdentityInvalid: 'extension module identity must include an extension id for event \'extension:module:announce\'',
moduleAnnounceIndexInvalid: 'the field \'index\' must be a non-negative integer for event \'extension:module:announce\'',
moduleAnnounceNameInvalid: 'the field \'name\' must be a non-empty string for event \'extension:module:announce\'',
moduleConsumerEventInvalid: 'the field \'event\' must be a non-empty string for event consumer registration',
moduleNotFound: 'module not found, it hasn\'t announced itself or the name is incorrect',
noConsumerRegistered: 'no consumer registered for requested event delivery',
notAuthenticated: 'not authenticated',
uiConfigureModuleIndexInvalid: 'the field \'moduleIndex\' must be a non-negative integer for event \'ui:configure\'',
uiConfigureModuleNameInvalid: 'the field \'moduleName\' can\'t be empty for event \'ui:configure\'',
} as const
export type ServerErrorCode
= | 'invalid-event-format'
| 'invalid-json'
| 'invalid-token'
| 'module-announce-identity-invalid'
| 'module-announce-index-invalid'
| 'module-announce-name-invalid'
| 'module-consumer-event-invalid'
| 'module-not-found'
| 'must-authenticate-before-announcing'
| 'no-consumer-registered'
| 'not-authenticated'
| 'ui-configure-module-index-invalid'
| 'ui-configure-module-name-invalid'
| 'unknown'
export interface ParsedServerErrorMessage {
authentication: boolean
code: ServerErrorCode
message: string
recoverable: boolean
terminal: boolean
}
export function createInvalidJsonServerErrorMessage(errorMessage: string) {
return `invalid JSON, error: ${errorMessage}`
}
/**
* Error metadata registry for predictable error code classification.
* Maps error messages to their error code and classification properties.
* @internal
*/
const errorMetadataRegistry: Record<string, Omit<ParsedServerErrorMessage, 'message'>> = {
[ServerErrorMessages.invalidToken]: {
authentication: true,
code: 'invalid-token',
recoverable: false,
terminal: true,
},
[ServerErrorMessages.notAuthenticated]: {
authentication: true,
code: 'not-authenticated',
recoverable: true,
terminal: false,
},
[ServerErrorMessages.mustAuthenticateBeforeAnnouncing]: {
authentication: true,
code: 'must-authenticate-before-announcing',
recoverable: true,
terminal: false,
},
[ServerErrorMessages.invalidEventFormat]: {
authentication: false,
code: 'invalid-event-format',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.moduleAnnounceNameInvalid]: {
authentication: false,
code: 'module-announce-name-invalid',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.moduleAnnounceIndexInvalid]: {
authentication: false,
code: 'module-announce-index-invalid',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.moduleAnnounceIdentityInvalid]: {
authentication: false,
code: 'module-announce-identity-invalid',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.moduleNotFound]: {
authentication: false,
code: 'module-not-found',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.moduleConsumerEventInvalid]: {
authentication: false,
code: 'module-consumer-event-invalid',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.noConsumerRegistered]: {
authentication: false,
code: 'no-consumer-registered',
recoverable: true,
terminal: false,
},
[ServerErrorMessages.uiConfigureModuleNameInvalid]: {
authentication: false,
code: 'ui-configure-module-name-invalid',
recoverable: false,
terminal: false,
},
[ServerErrorMessages.uiConfigureModuleIndexInvalid]: {
authentication: false,
code: 'ui-configure-module-index-invalid',
recoverable: false,
terminal: false,
},
}
/**
* Parses a server error message and classifies it.
*
* Use when:
* - Receiving error messages from the server
* - Determining whether to retry or give up
* - Checking if the error is authentication-related
*
* Expects:
* - Message string that matches one of ServerErrorMessages or starts with 'invalid JSON, error: '
*
* Returns:
* - Parsed error with classification (code, authentication, recoverable, terminal)
*/
export function parseServerErrorMessage(message: string): ParsedServerErrorMessage {
const metadata = Object.hasOwn(errorMetadataRegistry, message)
? errorMetadataRegistry[message]
: undefined
if (metadata) {
return { ...metadata, message }
}
if (message.startsWith('invalid JSON, error: ')) {
return {
authentication: false,
code: 'invalid-json',
message,
recoverable: false,
terminal: false,
}
}
return {
authentication: false,
code: 'unknown',
message,
recoverable: false,
terminal: false,
}
}
/**
* Checks if a server error message is authentication-related.
*/
export function isAuthenticationServerErrorMessage(message: string) {
return parseServerErrorMessage(message).authentication
}
/**
* Checks if a server error message is a terminal authentication error.
* Terminal errors should not be retried.
*/
export function isTerminalAuthenticationServerErrorMessage(message: string) {
const parsed = parseServerErrorMessage(message)
return parsed.authentication && parsed.terminal
}