diff --git a/src/architecture.md b/src/architecture.md index cabf74b..23c9f19 100644 --- a/src/architecture.md +++ b/src/architecture.md @@ -6,22 +6,31 @@ ```mermaid classDiagram -class PermissionDescriptor { +class IPermissionDescriptor { +canRead(path: string): boolean +canWrite(path: string): boolean +canCustom(path: string, options: any): boolean + +toJSON(): any } -<> PermissionDescriptor -PermissionDescriptor <|.. PermissionImpl +<> IPermissionDescriptor +IPermissionDescriptor <|.. AdminPermission +class AdminPermission { + +canRead(): boolean + +canWrite(): boolean + +canCustom(): boolean + +toJSON(): Inline +} +IPermissionDescriptor <|.. PermissionImpl class PermissionImpl { +basePath: string +writable: boolean + +toJSON(): Inline +canRead(path: string): boolean +canWrite(path: string): boolean +canCustom(_path: string, _options: any): boolean } SessionStore o-- UserSession -UserSession *-- PermissionDescriptor +UserSession *-- IUser class SessionStore~T~ { +sessions: Record +get(id: string): T @@ -34,9 +43,64 @@ class UserSession { +id: string +superuser: boolean +expiredAt: number - +permissionSet: PermissionDescriptor + +permissionSet: IPermissionDescriptor } <> UserSession +IPermissionDescriptor <|.. IUser +class IUser { + +id: string + +expiredAt: number + +basepath: string + +joinPath(path: string): string + +relativePath(path: string): string + +setExpired(seconds: number): void + +isExpired(): boolean + +toJSON(): any +} +<> IUser +IUser <|.. UserSessionImpl +class UserSessionImpl { + +id: string + +expiredAt: number + +permissionSet: IPermissionDescriptor + +basepath: string + +toJSON(): UserSessionImplJSON + +joinPath(path: string): string + +relativePath(path: string): string + +setExpired(seconds: number): void + +isExpired(): boolean + +canRead(path: string): boolean + +canWrite(path: string): boolean + +canCustom(path: string, options: any): boolean +} +``` +```mermaid +classDiagram +class DocumentContent { + +chunks: RPC.Chunk[] + +tags: string[] + +version: number +} +<> DocumentContent +class DocReadWriter { + +read(path: string): Promise + +save(path: string, doc: DocumentContent): Promise +} +<> DocReadWriter +DocReadWriter <|.. MemoryDocReadWriterType +class MemoryDocReadWriterType { + -store: Map + +read(path: string): Promise + +save(path: string, doc: DocumentContent): Promise + +clear(): void +} +DocReadWriter <|.. DocFileReadWriterType +class DocFileReadWriterType { + +rw: IReadWriter + +read(path: string): Promise + +save(path: string, doc: DocumentContent): Promise +} +DocumentContent ..> DocReadWriter ``` ```mermaid classDiagram @@ -80,11 +144,20 @@ Router <|.. FsRouter classDiagram class ResponseBuilder { +status: Status - +headers: Record + +headers: Headers +body?: BodyInit + +#response: Response + +#resolved: boolean + +resolved: boolean +setStatus(status: Status): this +setHeader(key: string, value: string): this + +setHeaders(headers: Record): this +setBody(body: BodyInit): this + +setResponse(response: Response, resolved?: boolean): this + +setCors(origin: string, credentials: boolean): this + +setCorsMethods(methods: string[]): this + +setContentType(contentType: string): this + +setJson(json: unknown): this +redirect(location: string): this +build(): Response } @@ -133,8 +206,10 @@ class ChunkMoveAction { classDiagram class Participant { +id: string - +user: UserSession + +user: IUser +send(data: string): void + +sendNotification(notification: RPC.RPCNotification): void + +responseWith(data: RPC.RPCResponse): void +addEventListener(type: T, listener: (this: WebSocket, event: WebSocketEventMap[T]) => void): void +removeEventListener(type: T, listener: (this: WebSocket, event: WebSocketEventMap[T]) => void): void +close(): void @@ -143,8 +218,10 @@ class Participant { Participant <|.. Connection class Connection { +id: string - +user: UserSession + +user: IUser +socket: WebSocket + +sendNotification(notification: RPC.RPCNotification): void + +responseWith(res: RPC.RPCResponse): void +send(data: string): void +addEventListener(type: T, listener: (this: WebSocket, event: WebSocketEventMap[T]) => void): void +removeEventListener(type: T, listener: (this: WebSocket, event: WebSocketEventMap[T]) => void): void @@ -156,33 +233,50 @@ class ParticipantList { +get(id: string): any +remove(id: string): void +unicast(id: string, message: string): void - +broadcast(message: string): void + +unicastNotification(id: string, notification: RPC.RPCNotification): void + +broadcastNotification(notification: RPC.RPCNotification): void } ParticipantList o-- Participant ``` ```mermaid classDiagram -DocumentObject <|.. FileDocumentObject -class FileDocumentObject { - +docPath: string - +chunks: Chunk[] - +tags: string[] - +updatedAt: number - +tagsUpdatedAt: number - +open(): Promise - +parse(content: unknown[]): void - +save(): Promise +class ISubscriptable { + +join(participant: Participant): void + +leave(participant: Participant): void + +broadcastChunkMethod(method: ChunkNotificationParam, updatedAt: number, exclude?: Participant): void + +participantsCount: number + +participants: Participant[] } -FileDocumentObject <|-- ActiveDocumentObject +<> ISubscriptable +ISubscriptable <|.. ActiveDocumentObject class ActiveDocumentObject { - +conns: Set + -conns: RefCountSet + -disposeHandlers: (() => void)[] +history: DocHistory[] +maxHistory: number + +docPath: string + +chunks: RPC.Chunk[] + +updatedAt: number + +seq: number + +#tags: string[] + +tagsUpdatedAt: number + +readWriter: DocReadWriter + +dispose(): void + +save(): Promise + +setTags(tags: string[]): void + +tags: string[] + +participantsCount: any + +participants: Participant[] +join(conn: Participant): void + +joined(conn: Participant): boolean +leave(conn: Participant): void + +open(): Promise +updateDocHistory(method: ChunkMethodHistory): void - +broadcastMethod(method: ChunkMethod, updatedAt: number, exclude?: Participant): void + +broadcastChunkMethod(method: ChunkNotificationParam, updatedAt: number, exclude?: Participant): void + +broadcastTagsNotification(exclude?: Participant): void } +IDisposable <|.. ActiveDocumentObject +DocumentObject <|.. ActiveDocumentObject class DocumentStore { +documents: Inline +open(conn: Participant, docPath: string): Promise @@ -193,16 +287,91 @@ DocumentStore o-- ActiveDocumentObject ``` ```mermaid classDiagram -class IFsWatcher{ - addEventListener(): void - onNofity(e: FileWatchEvent): void +class IDisposable { + +dispose(): void } -<> IFsWatcher -class FsWatcherImpl{ - onNotify(e: FileWatchEvent): void +<> IDisposable +IDisposable <|.. RefCountDisposable +class RefCountDisposable { + -refCount: number + -handlers: (() => void)[] + +dispose(): void + +disposeForced(): void + +addRef(): void + +addDisposeHandler(handler: () => void): void } - +class RefCountSet~T~ { + -items: Map + +add(item: T, disposeHandler?: () => void): void + +addDisposeHandler(item: T, handler: () => void): boolean + +delete(item: T): void + +deleteForced(item: T): void + +clear(): void + +size: number + +has(item: T): boolean + +values(): IterableIterator +} +RefCountDisposable --o RefCountSet~T~ ``` +```mermaid +classDiagram +Event <|-- FsWatcherEvent +class FsWatcherEvent { + +paths: string[] +} +EventTarget <|-- FsWatcher +class FsWatcher { + -path: string + -watcher?: Deno.FsWatcher + -filterFns: ((path: string, kind: FsWatchEventType) => boolean)[] + +addFilter(fn: (path: string, kind: FsWatchEventType) => boolean): void + +removeFilter(fn: (path: string, kind: FsWatchEventType) => boolean): boolean + +startWatching(): void + +stopWatching(): void + +addEventListener(type: FsWatchEventType, handler: (e: FsWatcherEvent) => void): void +} +``` +```mermaid +classDiagram +class IReadWriter { + +read(path: string): Promise + +write(path: string, content: string): Promise +} +<> IReadWriter +class MemoryReadWriter { + -data: Record + +read(path: string): Promise + +write(path: string, content: string): Promise +} +IReadWriter <|.. MemoryReadWriter +IReadWriter <|.. AtomicReadWriter +class AtomicReadWriter { + +read(path: string): Promise + +write(path: string, content: string): Promise +} +IReadWriter <|.. WatchFilteredReadWriter +class WatchFilteredReadWriter { + -raw: AtomicReadWriter + -fsWatcher: FsWatcher + +read(path: string): Promise + +write(path: string, content: string): Promise +} +IReadWriter <|.. QueueReadWriter +class QueueReadWriter { + -queue: Command[] + +started: boolean + -waitedResolve: () => void + +delayCount: number + +baseReadWriter: IReadWriter + +read(path: string): Promise + +write(path: string, content: string): Promise + +save(path: string, content: string): void + +startTimer(): void + +flush(): Promise + +wait(): Promise +} +``` + ### 5.1.2 Client Side UML ```mermaid diff --git a/src/index.md b/src/index.md index df6d531..d9a6a46 100644 --- a/src/index.md +++ b/src/index.md @@ -47,6 +47,9 @@ 1. [Server Side UML](./architecture.md#511-server-side-uml) 2. [Client Side UML](./architecture.md#512-client-side-uml) 2. [의사코드(Pseudo Code)](./architecture.md#52-의사코드pseudo-code) + 1. [서버 RPC 메세지 처리](./architecture.md#521-서버-rpc-메세지-처리) + 2. [클라이언트의 메세지 처리 동기화](./architecture.md#522-클라이언트의-메세지-처리-동기화) + 3. [다른 작업들](./architecture.md#523-다른-작업들) 6. [시험(Testing)](./testing.md) 1. [유닛 테스트(Unit test)](./testing.md#61-유닛-테스트unit-test) 2. [기능 테스트(Functional Test)](./testing.md#62-기능-테스트functional-test) \ No newline at end of file