From 267878f5500dbed46fd46dc1989c0050d27cf935 Mon Sep 17 00:00:00 2001 From: Gunwoo Baik Date: Wed, 7 Aug 2024 15:55:06 +0900 Subject: [PATCH] Unify error throwing methods (#878) This commit unifies error throwing methods and removes throw error in the logger. Previously, implementation of the logger includes both logging and error throwing, which can lead to confusion and type problems. By separating these concerns, we can ensure that the logger is only responsible for logging messages, while errors are thrown explicitly using `YorkieError`. --- src/api/converter.ts | 18 +- src/document/crdt/element_rht.ts | 13 +- src/document/crdt/rga_tree_list.ts | 20 +- src/document/crdt/rga_tree_split.ts | 13 +- src/document/crdt/root.ts | 7 +- src/document/crdt/tree.ts | 19 +- src/document/document.ts | 29 ++- src/document/json/counter.ts | 14 +- src/document/json/text.ts | 76 +++++--- src/document/json/tree.ts | 175 +++++++++++++----- src/document/operation/add_operation.ts | 12 +- src/document/operation/edit_operation.ts | 12 +- src/document/operation/increase_operation.ts | 12 +- src/document/operation/move_operation.ts | 12 +- src/document/operation/operation.ts | 3 +- src/document/operation/remove_operation.ts | 12 +- src/document/operation/set_operation.ts | 12 +- src/document/operation/style_operation.ts | 12 +- src/document/operation/tree_edit_operation.ts | 12 +- .../operation/tree_style_operation.ts | 12 +- src/util/error.ts | 19 +- src/util/index_tree.ts | 67 ++++--- src/util/logger.ts | 4 +- src/util/observable.ts | 8 +- src/util/splay_tree.ts | 5 +- test/bench/tree.bench.ts | 2 +- test/helper/helper.ts | 10 +- test/integration/client_test.ts | 6 +- test/integration/object_test.ts | 4 +- test/integration/primitive_test.ts | 6 +- 30 files changed, 443 insertions(+), 183 deletions(-) diff --git a/src/api/converter.ts b/src/api/converter.ts index b1ba92408..e5a1e1752 100644 --- a/src/api/converter.ts +++ b/src/api/converter.ts @@ -202,7 +202,10 @@ function toValueType(valueType: PrimitiveType): PbValueType { case PrimitiveType.Date: return PbValueType.DATE; default: - throw new YorkieError(Code.Unsupported, `unsupported type: ${valueType}`); + throw new YorkieError( + Code.ErrInvalidType, + `unsupported type: ${valueType}`, + ); } } @@ -216,7 +219,10 @@ function toCounterType(valueType: CounterType): PbValueType { case CounterType.LongCnt: return PbValueType.LONG_CNT; default: - throw new YorkieError(Code.Unsupported, `unsupported type: ${valueType}`); + throw new YorkieError( + Code.ErrInvalidType, + `unsupported type: ${valueType}`, + ); } } @@ -859,7 +865,7 @@ function fromPresenceChange

( }; } - throw new YorkieError(Code.Unsupported, `unsupported type: ${type}`); + throw new YorkieError(Code.ErrInvalidType, `unsupported type: ${type}`); } /** @@ -1476,7 +1482,7 @@ function bytesToSnapshot

( */ function bytesToObject(bytes?: Uint8Array): CRDTObject { if (!bytes) { - throw new Error('bytes is empty'); + throw new YorkieError(Code.ErrInvalidArgument, 'bytes is empty'); } const pbElement = PbJSONElement.fromBinary(bytes); @@ -1495,7 +1501,7 @@ function objectToBytes(obj: CRDTObject): Uint8Array { */ function bytesToArray(bytes?: Uint8Array): CRDTArray { if (!bytes) { - throw new Error('bytes is empty'); + throw new YorkieError(Code.ErrInvalidArgument, 'bytes is empty'); } const pbElement = PbJSONElement.fromBinary(bytes); @@ -1514,7 +1520,7 @@ function arrayToBytes(array: CRDTArray): Uint8Array { */ function bytesToTree(bytes?: Uint8Array): CRDTTree { if (!bytes) { - throw new Error('bytes is empty'); + throw new YorkieError(Code.ErrInvalidArgument, 'bytes is empty'); } const pbElement = PbJSONElement.fromBinary(bytes); diff --git a/src/document/crdt/element_rht.ts b/src/document/crdt/element_rht.ts index d9d3bdadd..83700a549 100644 --- a/src/document/crdt/element_rht.ts +++ b/src/document/crdt/element_rht.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element'; +import { YorkieError, Code } from '@yorkie-js-sdk/src/util/error'; /** * `ElementRHTNode` is a node of ElementRHT. @@ -114,7 +114,10 @@ export class ElementRHT { */ public delete(createdAt: TimeTicket, executedAt: TimeTicket): CRDTElement { if (!this.nodeMapByCreatedAt.has(createdAt.toIDString())) { - logger.fatal(`fail to find ${createdAt.toIDString()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${createdAt.toIDString()}`, + ); } const node = this.nodeMapByCreatedAt.get(createdAt.toIDString())!; @@ -142,8 +145,10 @@ export class ElementRHT { element.getCreatedAt().toIDString(), ); if (!node) { - logger.fatal(`fail to find ${element.getCreatedAt().toIDString()}`); - return; + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${element.getCreatedAt().toIDString()}`, + ); } const nodeByKey = this.nodeMapByKey.get(node.getStrKey()); diff --git a/src/document/crdt/rga_tree_list.ts b/src/document/crdt/rga_tree_list.ts index df7bd0854..c4901baf0 100644 --- a/src/document/crdt/rga_tree_list.ts +++ b/src/document/crdt/rga_tree_list.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { SplayNode, SplayTree } from '@yorkie-js-sdk/src/util/splay_tree'; import { InitialTimeTicket, @@ -22,6 +21,7 @@ import { } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element'; import { Primitive } from '@yorkie-js-sdk/src/document/crdt/primitive'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `RGATreeListNode` is a node of RGATreeList. @@ -180,7 +180,10 @@ export class RGATreeList { ): RGATreeListNode { let node = this.nodeMapByCreatedAt.get(createdAt.toIDString()); if (!node) { - logger.fatal(`cant find the given node: ${createdAt.toIDString()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `cant find the given node: ${createdAt.toIDString()}`, + ); } while ( @@ -232,12 +235,18 @@ export class RGATreeList { ): void { const prevNode = this.nodeMapByCreatedAt.get(prevCreatedAt.toIDString()); if (!prevNode) { - logger.fatal(`cant find the given node: ${prevCreatedAt.toIDString()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `cant find the given node: ${prevCreatedAt.toIDString()}`, + ); } const node = this.nodeMapByCreatedAt.get(createdAt.toIDString()); if (!node) { - logger.fatal(`cant find the given node: ${createdAt.toIDString()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `cant find the given node: ${createdAt.toIDString()}`, + ); } if ( @@ -284,7 +293,8 @@ export class RGATreeList { element.getCreatedAt().toIDString(), ); if (!node) { - logger.fatal( + throw new YorkieError( + Code.ErrInvalidArgument, `fail to find the given createdAt: ${element .getCreatedAt() .toIDString()}`, diff --git a/src/document/crdt/rga_tree_split.ts b/src/document/crdt/rga_tree_split.ts index 0d28d0895..45d8fd34d 100644 --- a/src/document/crdt/rga_tree_split.ts +++ b/src/document/crdt/rga_tree_split.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { ActorID } from '@yorkie-js-sdk/src/document/time/actor_id'; import { Comparator } from '@yorkie-js-sdk/src/util/comparator'; import { SplayNode, SplayTree } from '@yorkie-js-sdk/src/util/splay_tree'; @@ -26,6 +25,7 @@ import { TimeTicketStruct, } from '@yorkie-js-sdk/src/document/time/ticket'; import { GCChild, GCPair, GCParent } from '@yorkie-js-sdk/src/document/crdt/gc'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; export interface ValueChange { actor: ActorID; @@ -634,7 +634,8 @@ export class RGATreeSplit implements GCParent { ? this.findFloorNodePreferToLeft(absoluteID) : this.findFloorNode(absoluteID); if (!node) { - logger.fatal( + throw new YorkieError( + Code.ErrInvalidArgument, `the node of the given id should be found: ${absoluteID.toTestString()}`, ); } @@ -793,7 +794,8 @@ export class RGATreeSplit implements GCParent { ): RGATreeSplitNode { let node = this.findFloorNode(id); if (!node) { - logger.fatal( + throw new YorkieError( + Code.ErrInvalidArgument, `the node of the given id should be found: ${id.toTestString()}`, ); } @@ -847,7 +849,10 @@ export class RGATreeSplit implements GCParent { offset: number, ): RGATreeSplitNode | undefined { if (offset > node.getContentLength()) { - logger.fatal('offset should be less than or equal to length'); + throw new YorkieError( + Code.ErrInvalidArgument, + `offset should be less than or equal to length`, + ); } if (offset === 0) { diff --git a/src/document/crdt/root.ts b/src/document/crdt/root.ts index efe2305af..fe962f4b4 100644 --- a/src/document/crdt/root.ts +++ b/src/document/crdt/root.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { InitialTimeTicket, TimeTicket, @@ -27,6 +26,7 @@ import { CRDTObject } from '@yorkie-js-sdk/src/document/crdt/object'; import { GCPair } from '@yorkie-js-sdk/src/document/crdt/gc'; import { CRDTText } from '@yorkie-js-sdk/src/document/crdt/text'; import { CRDTTree } from '@yorkie-js-sdk/src/document/crdt/tree'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `CRDTElementPair` is a structure that represents a pair of element and its @@ -134,7 +134,10 @@ export class CRDTRoot { const createdAt = pair.element.getCreatedAt(); const subPath = pair.parent.subPathOf(createdAt); if (subPath === undefined) { - logger.fatal(`cant find the given element: ${createdAt.toIDString()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `cant find the given element: ${createdAt.toIDString()}`, + ); } subPaths.unshift(subPath!); diff --git a/src/document/crdt/tree.ts b/src/document/crdt/tree.ts index 33624631f..7c5800205 100644 --- a/src/document/crdt/tree.ts +++ b/src/document/crdt/tree.ts @@ -43,6 +43,7 @@ import { Indexable } from '@yorkie-js-sdk/src/document/document'; import type * as Devtools from '@yorkie-js-sdk/src/devtools/types'; import { escapeString } from '@yorkie-js-sdk/src/document/json/strings'; import { GCChild, GCPair, GCParent } from '@yorkie-js-sdk/src/document/crdt/gc'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `TreeNode` represents a node in the tree. @@ -223,7 +224,8 @@ export class CRDTTreePos { const parentNode = tree.findFloorNode(parentID); let leftNode = tree.findFloorNode(leftSiblingID); if (!parentNode || !leftNode) { - throw new Error( + throw new YorkieError( + Code.ErrRefused, `cannot find node of CRDTTreePos(${parentID.toTestString()}, ${leftSiblingID.toTestString()})`, ); } @@ -514,7 +516,10 @@ export class CRDTTreeNode */ get value() { if (!this.isText) { - throw new Error(`cannot get value of element node: ${this.type}`); + throw new YorkieError( + Code.ErrInvalidType, + `cannot get value of element node: ${this.type}`, + ); } return this._value; @@ -525,7 +530,10 @@ export class CRDTTreeNode */ set value(v: string) { if (!this.isText) { - throw new Error(`cannot set value of element node: ${this.type}`); + throw new YorkieError( + Code.ErrInvalidType, + `cannot set value of element node: ${this.type}`, + ); } this._value = v; @@ -1217,7 +1225,10 @@ export class CRDTTree extends CRDTElement implements GCParent { ticket: TimeTicket, ): void { // TODO(hackerwins, easylogic): Implement this with keeping references of the nodes. - throw new Error(`not implemented: ${target}, ${source}, ${ticket}`); + throw new YorkieError( + Code.ErrUnimplemented, + `not implemented: ${target}, ${source}, ${ticket}`, + ); } /** diff --git a/src/document/document.ts b/src/document/document.ts index 0ebf79ece..9343b6353 100644 --- a/src/document/document.ts +++ b/src/document/document.ts @@ -660,7 +660,7 @@ export class Document { } catch (err) { // drop clone because it is contaminated. this.clone = undefined; - logger.error(err); + throw err; } finally { this.isUpdating = false; @@ -847,7 +847,10 @@ export class Document { ): Unsubscribe { if (typeof arg1 === 'string') { if (typeof arg2 !== 'function') { - throw new Error('Second argument must be a callback function'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'Second argument must be a callback function', + ); } if (arg1 === 'presence') { const callback = arg2 as DocEventCallbackMap

['presence']; @@ -1021,7 +1024,7 @@ export class Document { complete, ); } - throw new Error(`"${arg1}" is not a valid`); + throw new YorkieError(Code.ErrInvalidArgument, `"${arg1}" is not a valid`); } /** @@ -1761,11 +1764,17 @@ export class Document { */ private undo(): void { if (this.isUpdating) { - throw new Error('Undo is not allowed during an update'); + throw new YorkieError( + Code.ErrRefused, + 'Undo is not allowed during an update', + ); } const undoOps = this.internalHistory.popUndo(); if (undoOps === undefined) { - throw new Error('There is no operation to be undone'); + throw new YorkieError( + Code.ErrRefused, + 'There is no operation to be undone', + ); } this.ensureClone(); @@ -1855,12 +1864,18 @@ export class Document { */ private redo(): void { if (this.isUpdating) { - throw new Error('Redo is not allowed during an update'); + throw new YorkieError( + Code.ErrRefused, + 'Redo is not allowed during an update', + ); } const redoOps = this.internalHistory.popRedo(); if (redoOps === undefined) { - throw new Error('There is no operation to be redone'); + throw new YorkieError( + Code.ErrRefused, + 'There is no operation to be redone', + ); } this.ensureClone(); diff --git a/src/document/json/counter.ts b/src/document/json/counter.ts index 6e1f9013a..fdffc975d 100644 --- a/src/document/json/counter.ts +++ b/src/document/json/counter.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { ChangeContext } from '@yorkie-js-sdk/src/document/change/context'; import { Primitive } from '@yorkie-js-sdk/src/document/crdt/primitive'; @@ -25,6 +24,7 @@ import { CRDTCounter, } from '@yorkie-js-sdk/src/document/crdt/counter'; import type * as Devtools from '@yorkie-js-sdk/src/devtools/types'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `Counter` is a custom data type that is used to counter. @@ -78,9 +78,10 @@ export class Counter { */ public increase(v: number | Long): Counter { if (!this.context || !this.counter) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Counter is not initialized yet', + ); } const ticket = this.context.issueTimeTicket(); @@ -105,7 +106,10 @@ export class Counter { */ public toJSForTest(): Devtools.JSONElement { if (!this.context || !this.counter) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Counter is not initialized yet', + ); } return this.counter.toJSForTest(); diff --git a/src/document/json/text.ts b/src/document/json/text.ts index cebe776f8..28e8dba77 100644 --- a/src/document/json/text.ts +++ b/src/document/json/text.ts @@ -38,6 +38,7 @@ import { stringifyObjectValues } from '@yorkie-js-sdk/src/util/object'; import type * as Devtools from '@yorkie-js-sdk/src/devtools/types'; import { SplayTree } from '@yorkie-js-sdk/src/util/splay_tree'; import { LLRBTree } from '@yorkie-js-sdk/src/util/llrb_tree'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `TextPosStruct` represents the structure of RGATreeSplitPos. @@ -92,13 +93,17 @@ export class Text { attributes?: A, ): [number, number] | undefined { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } if (fromIdx > toIdx) { - logger.fatal('from should be less than or equal to to'); - return; + throw new YorkieError( + Code.ErrInvalidArgument, + 'from should be less than or equal to to', + ); } const range = this.text.indexRangeToPosRange(fromIdx, toIdx); @@ -154,13 +159,17 @@ export class Text { */ setStyle(fromIdx: number, toIdx: number, attributes: A): boolean { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - return false; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } if (fromIdx > toIdx) { - logger.fatal('from should be less than or equal to to'); - return false; + throw new YorkieError( + Code.ErrInvalidArgument, + 'from should be less than or equal to to', + ); } const range = this.text.indexRangeToPosRange(fromIdx, toIdx); @@ -203,9 +212,10 @@ export class Text { */ indexRangeToPosRange(range: [number, number]): TextPosStructRange { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } const textRange = this.text.indexRangeToPosRange(range[0], range[1]); @@ -217,9 +227,10 @@ export class Text { */ posRangeToIndexRange(range: TextPosStructRange): [number, number] { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } const textRange = this.text.findIndexesFromRange([ @@ -235,9 +246,10 @@ export class Text { */ toTestString(): string { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } return this.text.toTestString(); @@ -248,9 +260,10 @@ export class Text { */ values(): Array> { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } return this.text.values(); @@ -285,8 +298,10 @@ export class Text { */ toString(): string { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - return ''; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } return this.text.toString(); @@ -297,7 +312,10 @@ export class Text { */ public toJSON(): string { if (!this.context || !this.text) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } return this.text.toJSON(); @@ -309,7 +327,10 @@ export class Text { */ public toJSForTest(): Devtools.JSONElement { if (!this.context || !this.text) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } return this.text.toJSForTest(); @@ -321,9 +342,10 @@ export class Text { */ createRangeForTest(fromIdx: number, toIdx: number): RGATreeSplitPosRange { if (!this.context || !this.text) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Text is not initialized yet', + ); } return this.text.indexRangeToPosRange(fromIdx, toIdx); diff --git a/src/document/json/tree.ts b/src/document/json/tree.ts index 94c6ce06d..b99161875 100644 --- a/src/document/json/tree.ts +++ b/src/document/json/tree.ts @@ -32,7 +32,6 @@ import { TreeEditOperation } from '@yorkie-js-sdk/src/document/operation/tree_ed import { isEmpty, stringifyObjectValues } from '@yorkie-js-sdk/src/util/object'; import { RHT } from '../crdt/rht'; import { TreeStyleOperation } from '../operation/tree_style_operation'; -import { logger } from '@yorkie-js-sdk/src/util/logger'; import type { ElementNode, TextNode, @@ -43,6 +42,7 @@ import type { CRDTTreeNodeIDStruct, } from '@yorkie-js-sdk/src/document/crdt/tree'; import type * as Devtools from '@yorkie-js-sdk/src/devtools/types'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * NOTE(hackerwins): In normal case, we should define the following types in @@ -153,7 +153,10 @@ function createCRDTTreeNode(context: ChangeContext, content: TreeNode) { */ function validateTextNode(textNode: TextNode): boolean { if (!textNode.value.length) { - throw new Error('text node cannot have empty value'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'text node cannot have empty value', + ); } return true; @@ -172,7 +175,10 @@ function validateTreeNodes(treeNodes: Array): boolean { for (const treeNode of treeNodes) { const { type } = treeNode; if (type !== DefaultTextType) { - throw new Error('element node and text node cannot be passed together'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'element node and text node cannot be passed together', + ); } validateTextNode(treeNode as TextNode); } @@ -180,7 +186,10 @@ function validateTreeNodes(treeNodes: Array): boolean { for (const treeNode of treeNodes) { const { type } = treeNode; if (type === DefaultTextType) { - throw new Error('element node and text node cannot be passed together'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'element node and text node cannot be passed together', + ); } } } @@ -247,7 +256,10 @@ export class Tree { */ public getSize(): number { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.getSize(); @@ -258,7 +270,10 @@ export class Tree { */ public getNodeSize(): number { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.getNodeSize(); @@ -269,7 +284,10 @@ export class Tree { */ public getIndexTree(): IndexTree { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.getIndexTree(); @@ -280,11 +298,17 @@ export class Tree { */ public styleByPath(path: Array, attributes: { [key: string]: any }) { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (!path.length) { - throw new Error('path should not be empty'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'path should not be empty', + ); } const [fromPos, toPos] = this.tree.pathToPosRange(path); const ticket = this.context.issueTimeTicket(); @@ -317,11 +341,17 @@ export class Tree { attributes: { [key: string]: any }, ) { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (fromIdx > toIdx) { - throw new Error('from should be less than or equal to to'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'from should be less than or equal to to', + ); } const fromPos = this.tree.findPos(fromIdx); @@ -360,11 +390,17 @@ export class Tree { attributesToRemove: Array, ) { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (fromIdx > toIdx) { - throw new Error('from should be less than or equal to to'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'from should be less than or equal to to', + ); } const fromPos = this.tree.findPos(fromIdx); @@ -470,13 +506,22 @@ export class Tree { splitLevel = 0, ): boolean { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (fromPath.length !== toPath.length) { - throw new Error('path length should be equal'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'path length should be equal', + ); } if (!fromPath.length || !toPath.length) { - throw new Error('path should not be empty'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'path should not be empty', + ); } const fromPos = this.tree.pathToPos(fromPath); @@ -500,13 +545,22 @@ export class Tree { splitLevel = 0, ): boolean { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (fromPath.length !== toPath.length) { - throw new Error('path length should be equal'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'path length should be equal', + ); } if (!fromPath.length || !toPath.length) { - throw new Error('path should not be empty'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'path should not be empty', + ); } const fromPos = this.tree.pathToPos(fromPath); @@ -525,10 +579,16 @@ export class Tree { splitLevel = 0, ): boolean { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (fromIdx > toIdx) { - throw new Error('from should be less than or equal to to'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'from should be less than or equal to to', + ); } const fromPos = this.tree.findPos(fromIdx); @@ -552,10 +612,16 @@ export class Tree { splitLevel = 0, ): boolean { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } if (fromIdx > toIdx) { - throw new Error('from should be less than or equal to to'); + throw new YorkieError( + Code.ErrInvalidArgument, + 'from should be less than or equal to to', + ); } const fromPos = this.tree.findPos(fromIdx); @@ -569,7 +635,10 @@ export class Tree { */ public toXML(): string { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.toXML(); @@ -580,7 +649,10 @@ export class Tree { */ public toJSON(): string { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.toJSON(); @@ -592,7 +664,10 @@ export class Tree { */ public toJSForTest(): Devtools.JSONElement { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.toJSForTest(); @@ -605,7 +680,10 @@ export class Tree { */ public toJSInfoForTest(): Devtools.TreeNodeInfo { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.toJSInfoForTest(); @@ -616,7 +694,10 @@ export class Tree { */ public getRootTreeNode() { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.getRootTreeNode(); @@ -627,7 +708,10 @@ export class Tree { */ public indexToPath(index: number): Array { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.indexToPath(index); @@ -638,7 +722,10 @@ export class Tree { */ public pathToIndex(path: Array): number { if (!this.context || !this.tree) { - throw new Error('it is not initialized yet'); + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.pathToIndex(path); @@ -651,9 +738,10 @@ export class Tree { range: [Array, Array], ): TreePosStructRange { if (!this.context || !this.tree) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } const indexRange: [number, number] = [ @@ -669,9 +757,10 @@ export class Tree { */ indexRangeToPosRange(range: [number, number]): TreePosStructRange { if (!this.context || !this.tree) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } return this.tree.indexRangeToPosStructRange(range); @@ -682,9 +771,10 @@ export class Tree { */ posRangeToIndexRange(range: TreePosStructRange): [number, number] { if (!this.context || !this.tree) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } const posRange: [CRDTTreePos, CRDTTreePos] = [ @@ -702,9 +792,10 @@ export class Tree { range: TreePosStructRange, ): [Array, Array] { if (!this.context || !this.tree) { - logger.fatal('it is not initialized yet'); - // @ts-ignore - return; + throw new YorkieError( + Code.ErrNotInitialized, + 'Tree is not initialized yet', + ); } const posRange: [CRDTTreePos, CRDTTreePos] = [ diff --git a/src/document/operation/add_operation.ts b/src/document/operation/add_operation.ts index ec7208677..478f46c26 100644 --- a/src/document/operation/add_operation.ts +++ b/src/document/operation/add_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; @@ -23,6 +22,7 @@ import { Operation, ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `AddOperation` is an operation representing adding an element to an Array. @@ -60,10 +60,16 @@ export class AddOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTArray)) { - logger.fatal(`fail to execute, only array can execute add`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only array can execute add`, + ); } const array = parentObject as CRDTArray; const value = this.value.deepcopy(); diff --git a/src/document/operation/edit_operation.ts b/src/document/operation/edit_operation.ts index 6175f9d77..290d6a819 100644 --- a/src/document/operation/edit_operation.ts +++ b/src/document/operation/edit_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { RGATreeSplitPos } from '@yorkie-js-sdk/src/document/crdt/rga_tree_split'; @@ -25,6 +24,7 @@ import { ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; import { Indexable } from '../document'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `EditOperation` is an operation representing editing Text. Most of the same as @@ -83,10 +83,16 @@ export class EditOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTText)) { - logger.fatal(`fail to execute, only Text can execute edit`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only Text can execute edit`, + ); } const text = parentObject as CRDTText; diff --git a/src/document/operation/increase_operation.ts b/src/document/operation/increase_operation.ts index d606f271b..287087935 100644 --- a/src/document/operation/increase_operation.ts +++ b/src/document/operation/increase_operation.ts @@ -26,8 +26,8 @@ import { Primitive, PrimitiveType, } from '@yorkie-js-sdk/src/document/crdt/primitive'; -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { CRDTCounter } from '@yorkie-js-sdk/src/document/crdt/counter'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `IncreaseOperation` represents an operation that increments a numeric value to Counter. @@ -62,10 +62,16 @@ export class IncreaseOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTCounter)) { - logger.fatal(`fail to execute, only Counter can execute increase`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only Counter can execute increase`, + ); } const counter = parentObject as CRDTCounter; const value = this.value.deepcopy() as Primitive; diff --git a/src/document/operation/move_operation.ts b/src/document/operation/move_operation.ts index 6a26b3e65..864d0372f 100644 --- a/src/document/operation/move_operation.ts +++ b/src/document/operation/move_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { CRDTArray } from '@yorkie-js-sdk/src/document/crdt/array'; @@ -22,6 +21,7 @@ import { Operation, ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `MoveOperation` is an operation representing moving an element to an Array. @@ -64,10 +64,16 @@ export class MoveOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTArray)) { - logger.fatal(`fail to execute, only array can execute move`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only array can execute move`, + ); } const array = parentObject as CRDTArray; const previousIndex = Number(array.subPathOf(this.createdAt)); diff --git a/src/document/operation/operation.ts b/src/document/operation/operation.ts index bde2aca29..18057b00a 100644 --- a/src/document/operation/operation.ts +++ b/src/document/operation/operation.ts @@ -19,6 +19,7 @@ import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { TreeNode } from '@yorkie-js-sdk/src/document/crdt/tree'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { Indexable } from '@yorkie-js-sdk/src/document/document'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `OpSource` represents the source of the operation. It is used to handle @@ -210,7 +211,7 @@ export abstract class Operation { // it doesn't have an executedAt yet. The executedAt is set when // the operation is executed through undo or redo. if (!this.executedAt) { - throw new Error(`executedAt has not been set yet`); + throw new YorkieError(Code.ErrNotReady, 'executedAt is not set yet'); } return this.executedAt; } diff --git a/src/document/operation/remove_operation.ts b/src/document/operation/remove_operation.ts index 5e88da410..f1ad7b453 100644 --- a/src/document/operation/remove_operation.ts +++ b/src/document/operation/remove_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { @@ -30,6 +29,7 @@ import { import { CRDTObject } from '@yorkie-js-sdk/src/document/crdt/object'; import { CRDTArray } from '@yorkie-js-sdk/src/document/crdt/array'; import { SetOperation } from '@yorkie-js-sdk/src/document/operation/set_operation'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `RemoveOperation` is an operation that removes an element from `CRDTContainer`. @@ -68,10 +68,16 @@ export class RemoveOperation extends Operation { this.getParentCreatedAt(), ) as CRDTContainer; if (!container) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(container instanceof CRDTContainer)) { - logger.fatal(`only object and array can execute remove: ${container}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `only object and array can execute remove: ${container}`, + ); } // NOTE(chacha912): Handle cases where operation cannot be executed during undo and redo. diff --git a/src/document/operation/set_operation.ts b/src/document/operation/set_operation.ts index 8d01db463..ca30b6730 100644 --- a/src/document/operation/set_operation.ts +++ b/src/document/operation/set_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; @@ -25,6 +24,7 @@ import { ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; import { RemoveOperation } from '@yorkie-js-sdk/src/document/operation/remove_operation'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `SetOperation` represents an operation that stores the value corresponding to the @@ -66,10 +66,16 @@ export class SetOperation extends Operation { ): ExecutionResult | undefined { const obj = root.findByCreatedAt(this.getParentCreatedAt()) as CRDTObject; if (!obj) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(obj instanceof CRDTObject)) { - logger.fatal(`fail to execute, only object can execute set`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only object can execute set`, + ); } // NOTE(chacha912): Handle cases where operation cannot be executed during undo and redo. diff --git a/src/document/operation/style_operation.ts b/src/document/operation/style_operation.ts index b0a248998..b7b4af721 100644 --- a/src/document/operation/style_operation.ts +++ b/src/document/operation/style_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { RGATreeSplitPos } from '@yorkie-js-sdk/src/document/crdt/rga_tree_split'; @@ -25,6 +24,7 @@ import { ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; import { Indexable } from '../document'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `StyleOperation` is an operation applies the style of the given range to Text. @@ -77,10 +77,16 @@ export class StyleOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTText)) { - logger.fatal(`fail to execute, only Text can execute edit`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only Text can execute edit`, + ); } const text = parentObject as CRDTText; const [, pairs, changes] = text.setStyle( diff --git a/src/document/operation/tree_edit_operation.ts b/src/document/operation/tree_edit_operation.ts index 6a2bb6f82..3557a765a 100644 --- a/src/document/operation/tree_edit_operation.ts +++ b/src/document/operation/tree_edit_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { @@ -28,6 +27,7 @@ import { OperationInfo, ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `TreeEditOperation` is an operation representing Tree editing. @@ -85,10 +85,16 @@ export class TreeEditOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTTree)) { - logger.fatal(`fail to execute, only Tree can execute edit`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only Tree can execute edit`, + ); } const editedAt = this.getExecutedAt(); const tree = parentObject as CRDTTree; diff --git a/src/document/operation/tree_style_operation.ts b/src/document/operation/tree_style_operation.ts index 5f16f92a4..ecaad81da 100644 --- a/src/document/operation/tree_style_operation.ts +++ b/src/document/operation/tree_style_operation.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; import { TimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { @@ -28,6 +27,7 @@ import { ExecutionResult, } from '@yorkie-js-sdk/src/document/operation/operation'; import { GCPair } from '@yorkie-js-sdk/src/document/crdt/gc'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; /** * `TreeStyleOperation` represents an operation that modifies the style of the @@ -107,10 +107,16 @@ export class TreeStyleOperation extends Operation { public execute(root: CRDTRoot): ExecutionResult { const parentObject = root.findByCreatedAt(this.getParentCreatedAt()); if (!parentObject) { - logger.fatal(`fail to find ${this.getParentCreatedAt()}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to find ${this.getParentCreatedAt()}`, + ); } if (!(parentObject instanceof CRDTTree)) { - logger.fatal(`fail to execute, only Tree can execute edit`); + throw new YorkieError( + Code.ErrInvalidArgument, + `fail to execute, only Tree can execute edit`, + ); } const tree = parentObject as CRDTTree; let changes: Array; diff --git a/src/util/error.ts b/src/util/error.ts index 981da64b5..3abd06324 100644 --- a/src/util/error.ts +++ b/src/util/error.ts @@ -27,8 +27,11 @@ export enum Code { // ErrUnimplemented is returned when the operation is not implemented. ErrUnimplemented = 'ErrUnimplemented', - // Unsupported is returned when the operation is not supported. - Unsupported = 'unsupported', + // ErrInvalidType is returned when the type is invalid. + ErrInvalidType = 'ErrInvalidType', + + // ErrDummy is used to verify errors for testing purposes. + ErrDummy = 'ErrDummy', // ErrDocumentNotAttached is returned when the document is not attached. ErrDocumentNotAttached = 'ErrDocumentNotAttached', @@ -44,6 +47,15 @@ export enum Code { // ErrInvalidArgument is returned when the argument is invalid. ErrInvalidArgument = 'ErrInvalidArgument', + + // ErrNotInitialized is returned when required initialization has not been completed. + ErrNotInitialized = 'ErrNotInitialized', + + // ErrNotReady is returned when execution of following actions is not ready. + ErrNotReady = 'ErrNotReady', + + // ErrRefused is returned when the execution is rejected. + ErrRefused = 'ErrRefused', } /** @@ -55,7 +67,6 @@ export class YorkieError extends Error { constructor(readonly code: Code, readonly message: string) { super(message); - this.toString = (): string => - `${this.name}: [code=${this.code}]: ${this.message}`; + this.toString = (): string => `[code=${this.code}]: ${this.message}`; } } diff --git a/src/util/index_tree.ts b/src/util/index_tree.ts index 9f7c856dd..97c960615 100644 --- a/src/util/index_tree.ts +++ b/src/util/index_tree.ts @@ -15,6 +15,7 @@ */ import { TimeTicket } from '../yorkie'; +import { Code, YorkieError } from './error'; /** * About `index`, `path`, `size` and `TreePos` in crdt.IndexTree. @@ -129,7 +130,7 @@ export abstract class IndexTreeNode> { this._children = children; if (this.isText && this._children.length > 0) { - throw new Error(`Text node cannot have children: ${this.type}`); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } } @@ -303,7 +304,7 @@ export abstract class IndexTreeNode> { */ append(...newNode: Array): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } this._children.push(...newNode); @@ -319,7 +320,7 @@ export abstract class IndexTreeNode> { */ prepend(...newNode: Array): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } this._children.unshift(...newNode); @@ -333,12 +334,12 @@ export abstract class IndexTreeNode> { */ insertBefore(newNode: T, referenceNode: T): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } const offset = this._children.indexOf(referenceNode); if (offset === -1) { - throw new Error('child not found'); + throw new YorkieError(Code.ErrInvalidArgument, 'child not found'); } this.insertAtInternal(newNode, offset); @@ -350,12 +351,12 @@ export abstract class IndexTreeNode> { */ insertAfter(newNode: T, referenceNode: T): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } const offset = this._children.indexOf(referenceNode); if (offset === -1) { - throw new Error('child not found'); + throw new YorkieError(Code.ErrInvalidArgument, 'child not found'); } this.insertAtInternal(newNode, offset + 1); @@ -367,7 +368,7 @@ export abstract class IndexTreeNode> { */ insertAt(newNode: T, offset: number): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } this.insertAtInternal(newNode, offset); @@ -379,12 +380,12 @@ export abstract class IndexTreeNode> { */ removeChild(child: T) { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } const offset = this._children.indexOf(child); if (offset === -1) { - throw new Error('child not found'); + throw new YorkieError(Code.ErrInvalidArgument, 'child not found'); } this._children.splice(offset, 1); @@ -435,12 +436,12 @@ export abstract class IndexTreeNode> { */ insertAfterInternal(newNode: T, referenceNode: T): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } const offset = this._children.indexOf(referenceNode); if (offset === -1) { - throw new Error('child not found'); + throw new YorkieError(Code.ErrInvalidArgument, 'child not found'); } this.insertAtInternal(newNode, offset + 1); @@ -452,7 +453,7 @@ export abstract class IndexTreeNode> { */ insertAtInternal(newNode: T, offset: number): void { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } this._children.splice(offset, 0, newNode); @@ -465,7 +466,7 @@ export abstract class IndexTreeNode> { */ findOffset(node: T): number { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } if (node.isRemoved) { @@ -489,7 +490,7 @@ export abstract class IndexTreeNode> { */ findBranchOffset(node: T): number { if (this.isText) { - throw new Error('Text node cannot have children'); + throw new YorkieError(Code.ErrRefused, 'Text node cannot have children'); } let current: T | undefined = node; @@ -577,15 +578,24 @@ function tokensBetween>( callback: (token: TreeToken, ended: boolean) => void, ) { if (from > to) { - throw new Error(`from is greater than to: ${from} > ${to}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `from is greater than to: ${from} > ${to}`, + ); } if (from > root.size) { - throw new Error(`from is out of range: ${from} > ${root.size}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `from is out of range: ${from} > ${root.size}`, + ); } if (to > root.size) { - throw new Error(`to is out of range: ${to} > ${root.size}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `to is out of range: ${to} > ${root.size}`, + ); } if (from === to) { @@ -663,7 +673,10 @@ function findTreePos>( preferText = true, ): TreePos { if (index > node.size) { - throw new Error(`index is out of range: ${index} > ${node.size}`); + throw new YorkieError( + Code.ErrInvalidArgument, + `index is out of range: ${index} > ${node.size}`, + ); } if (node.isText) { @@ -764,7 +777,7 @@ export function findLeftmost>(node: T): T { */ function findTextPos>(node: T, pathElement: number) { if (node.size < pathElement) { - throw new Error('unacceptable path'); + throw new YorkieError(Code.ErrInvalidArgument, 'unacceptable path'); } for (let i = 0; i < node.children.length; i++) { @@ -834,7 +847,7 @@ export class IndexTree> { if (node.isText) { const offset = node.parent!.findOffset(node); if (offset === -1) { - throw new Error('invalid treePos'); + throw new YorkieError(Code.ErrInvalidArgument, 'invalid treePos'); } const sizeOfLeftSiblings = addSizeOfLeftSiblings( @@ -858,7 +871,7 @@ export class IndexTree> { while (node.parent) { const offset = node.parent.findOffset(node); if (offset === -1) { - throw new Error('invalid treePos'); + throw new YorkieError(Code.ErrInvalidArgument, 'invalid treePos'); } path.push(offset); @@ -882,7 +895,7 @@ export class IndexTree> { */ public pathToTreePos(path: Array): TreePos { if (!path.length) { - throw new Error('unacceptable path'); + throw new YorkieError(Code.ErrInvalidArgument, 'unacceptable path'); } let node = this.root; @@ -891,7 +904,7 @@ export class IndexTree> { node = node.children[pathElement]; if (!node) { - throw new Error('unacceptable path'); + throw new YorkieError(Code.ErrInvalidArgument, 'unacceptable path'); } } @@ -900,7 +913,7 @@ export class IndexTree> { } if (node.children.length < path[path.length - 1]) { - throw new Error('unacceptable path'); + throw new YorkieError(Code.ErrInvalidArgument, 'unacceptable path'); } return { @@ -965,7 +978,7 @@ export class IndexTree> { const parent = node.parent! as T; const offsetOfNode = parent.findOffset(node); if (offsetOfNode === -1) { - throw new Error('invalid pos'); + throw new YorkieError(Code.ErrInvalidArgument, 'invalid pos'); } size += addSizeOfLeftSiblings(parent, offsetOfNode); @@ -979,7 +992,7 @@ export class IndexTree> { const parent = node.parent; const offsetOfNode = parent.findOffset(node); if (offsetOfNode === -1) { - throw new Error('invalid pos'); + throw new YorkieError(Code.ErrInvalidArgument, 'invalid pos'); } size += addSizeOfLeftSiblings(parent, offsetOfNode); diff --git a/src/util/logger.ts b/src/util/logger.ts index fd33c5a56..15d2a9334 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -91,7 +91,7 @@ export const logger = { } }, - fatal: (message: string, ...messages: Array): void => { + fatal: (...messages: Array): void => { if (typeof console != 'undefined') { if (typeof console.error !== 'undefined') { console.error('YORKIE F:', ...messages); @@ -99,8 +99,6 @@ export const logger = { console.log('YORKIE F:', ...messages); } } - - throw new Error(`YORKIE F: ${message}`); }, isEnabled: (l: LogLevel): boolean => { diff --git a/src/util/observable.ts b/src/util/observable.ts index fc36ef15c..c80f1efb9 100644 --- a/src/util/observable.ts +++ b/src/util/observable.ts @@ -16,6 +16,7 @@ import { logger } from '@yorkie-js-sdk/src/util/logger'; import { uuid } from '@yorkie-js-sdk/src/util/uuid'; +import { Code, YorkieError } from './error'; export type NextFn = (value: T) => void; @@ -106,11 +107,14 @@ class ObserverProxy implements Observer { let observer: Observer; if (!nextOrObserver) { - logger.fatal('missing observer'); + throw new YorkieError(Code.ErrInvalidArgument, 'missing observer'); } if (this.finalized) { - logger.fatal('observable is finalized due to previous error'); + throw new YorkieError( + Code.ErrRefused, + 'observable is finalized due to previous error', + ); } if (typeof nextOrObserver === 'object') { diff --git a/src/util/splay_tree.ts b/src/util/splay_tree.ts index 9dc0ce4b4..e10060494 100644 --- a/src/util/splay_tree.ts +++ b/src/util/splay_tree.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@yorkie-js-sdk/src/util/logger'; +import { Code, YorkieError } from './error'; /** * `SplayNode` is a node of SplayTree. @@ -206,7 +206,8 @@ export class SplayTree { } } if (pos > node.getLength()) { - logger.fatal( + throw new YorkieError( + Code.ErrInvalidArgument, `out of index range: pos: ${pos} > node.length: ${node.getLength()}`, ); } diff --git a/test/bench/tree.bench.ts b/test/bench/tree.bench.ts index cec67a367..b8bb2f8ce 100644 --- a/test/bench/tree.bench.ts +++ b/test/bench/tree.bench.ts @@ -98,7 +98,7 @@ const benchmarkTreeConvert = (size: number) => { root.tree = new Tree({ type: 'doc', - children: [{ type: 'p', children: children }], + children: [{ type: 'p', children }], }); }); diff --git a/test/helper/helper.ts b/test/helper/helper.ts index bb736b7a8..dbc6e6470 100644 --- a/test/helper/helper.ts +++ b/test/helper/helper.ts @@ -36,6 +36,7 @@ import { InitialChangeID } from '@yorkie-js-sdk/src/document/change/change_id'; import { CRDTRoot } from '@yorkie-js-sdk/src/document/crdt/root'; import { CRDTObject } from '@yorkie-js-sdk/src/document/crdt/object'; import { ElementRHT } from '@yorkie-js-sdk/src/document/crdt/element_rht'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; export type Indexable = Record; @@ -66,9 +67,12 @@ export class EventCollector { resolve(); } else { reject( - new Error(`event is not equal ${count}- - expected: ${JSON.stringify(event)}, - actual: ${JSON.stringify(this.events[count - 1])}`), + new YorkieError( + Code.ErrInvalidArgument, + `event is not equal ${count}- + expected: ${JSON.stringify(event)}, + actual: ${JSON.stringify(this.events[count - 1])}`, + ), ); } return; diff --git a/test/integration/client_test.ts b/test/integration/client_test.ts index c56997f10..b315408f4 100644 --- a/test/integration/client_test.ts +++ b/test/integration/client_test.ts @@ -32,7 +32,7 @@ import { withTwoClientsAndDocuments, } from '@yorkie-js-sdk/test/integration/integration_helper'; import { ConnectError, Code as ConnectCode } from '@connectrpc/connect'; -import { Code } from '@yorkie-js-sdk/src/util/error'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; describe.sequential('Client', function () { afterEach(() => { @@ -133,7 +133,7 @@ describe.sequential('Client', function () { // Simulate network error with fetch vi.stubGlobal('fetch', () => { return Promise.resolve().then(() => { - throw new Error('Failed to fetch'); + throw new YorkieError(Code.ErrDummy, 'Failed to fetch'); }); }); @@ -211,7 +211,7 @@ describe.sequential('Client', function () { // Simulate network error vi.stubGlobal('fetch', () => { return Promise.resolve().then(() => { - throw new Error('Failed to fetch'); + throw new YorkieError(Code.ErrDummy, 'Failed to fetch'); }); }); diff --git a/test/integration/object_test.ts b/test/integration/object_test.ts index c4b982367..b033e100e 100644 --- a/test/integration/object_test.ts +++ b/test/integration/object_test.ts @@ -8,7 +8,7 @@ import { testRPCAddr, } from '@yorkie-js-sdk/test/integration/integration_helper'; import { toStringHistoryOp } from '@yorkie-js-sdk/test/helper/helper'; -import { YorkieError } from '@yorkie-js-sdk/src/util/error'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; describe('Object', function () { it('valid key test', function () { @@ -63,7 +63,7 @@ describe('Object', function () { assert.throws(() => { doc.update((root) => { root['k2'].push('4'); - throw new Error('dummy error'); + throw new YorkieError(Code.ErrDummy, 'dummy error'); }, 'push "4"'); }, 'dummy error'); assert.equal( diff --git a/test/integration/primitive_test.ts b/test/integration/primitive_test.ts index f102cb23f..c7bac18fd 100644 --- a/test/integration/primitive_test.ts +++ b/test/integration/primitive_test.ts @@ -3,6 +3,7 @@ import Long from 'long'; import { Document } from '@yorkie-js-sdk/src/document/document'; import { InitialCheckpoint } from '@yorkie-js-sdk/src/document/change/checkpoint'; import { withTwoClientsAndDocuments } from '@yorkie-js-sdk/test/integration/integration_helper'; +import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; describe('Primitive', function () { it('should apply updates of string', function () { @@ -35,10 +36,11 @@ describe('Primitive', function () { }); assert.equal('{"k1":{"k1-1":1,"k1-2":2}}', doc.toSortedJSON()); assert.throws(() => { + const errorMsg = 'dummy error'; doc.update((root) => { delete root['k1']['k1-1']; - throw Error('dummy error'); - }, 'dummy error'); + throw new YorkieError(Code.ErrDummy, errorMsg); + }, errorMsg); }); assert.equal('{"k1":{"k1-1":1,"k1-2":2}}', doc.toSortedJSON()); });