-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
basic implementation of the Operation and CollaborationManager (#76)
* basic implementation of the Operation and CollaborationManager * use switch/case and upgrade ts * use index builder + format fixes * Apply suggestions from code review Co-authored-by: Peter <[email protected]> * fix ci --------- Co-authored-by: Peter <[email protected]>
- Loading branch information
1 parent
1c40976
commit 927cafb
Showing
15 changed files
with
671 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
extends: | ||
- codex/ts | ||
|
||
ignorePatterns: | ||
- node_modules | ||
- dist | ||
|
||
# Eslint seems to not recognize WeakMap as a global | ||
globals: | ||
WeakMap: readonly | ||
|
||
plugins: | ||
- import | ||
|
||
rules: | ||
import/extensions: | ||
- error | ||
- always | ||
'@typescript-eslint/no-unsafe-declaration-merging': | ||
- 0 | ||
|
||
overrides: | ||
- files: | ||
- '**/*.test.ts' | ||
- '**/*.spec.ts' | ||
env: | ||
jest: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
.yarn/* | ||
!.yarn/patches | ||
!.yarn/plugins | ||
!.yarn/releases | ||
!.yarn/sdks | ||
!.yarn/versions | ||
|
||
# Swap the comments on the following lines if you don't wish to use zero-installs | ||
# Documentation here: https://yarnpkg.com/features/zero-installs | ||
#!.yarn/cache | ||
#.pnp.* | ||
|
||
# IDE | ||
.idea/* | ||
|
||
node_modules/* | ||
dist/* | ||
|
||
# tests | ||
coverage/ | ||
reports/ | ||
|
||
# stryker temp files | ||
.stryker-tmp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { type JestConfigWithTsJest, createDefaultEsmPreset } from 'ts-jest'; | ||
|
||
export default { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
testMatch: [ '<rootDir>/src/**/*.spec.ts' ], | ||
modulePathIgnorePatterns: [ '<rootDir>/.*/__mocks__', '<rootDir>/.*/mocks' ], | ||
extensionsToTreatAsEsm: ['.ts'], | ||
moduleNameMapper: { | ||
'^(\\.{1,2}/.*)\\.js$': '$1', | ||
}, | ||
transform: { | ||
...createDefaultEsmPreset().transform, | ||
}, | ||
} as JestConfigWithTsJest; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "@editorjs/collaboration-manager", | ||
"version": "0.0.0", | ||
"packageManager": "[email protected]", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"type": "module", | ||
"scripts": { | ||
"build": "tsc --project tsconfig.build.json", | ||
"dev": "tsc --project tsconfig.build.json --watch", | ||
"lint": "eslint ./src", | ||
"lint:ci": "yarn lint --max-warnings 0", | ||
"lint:fix": "yarn lint --fix", | ||
"test": "node --experimental-vm-modules $(yarn bin jest)", | ||
"test:coverage": "yarn test --coverage=true", | ||
"test:mutations": "stryker run", | ||
"clear": "rm -rf ./dist && rm -rf ./tsconfig.build.tsbuildinfo" | ||
}, | ||
"dependencies": { | ||
"@editorjs/model": "workspace:^" | ||
}, | ||
"devDependencies": { | ||
"@jest/globals": "^29.7.0", | ||
"@stryker-mutator/core": "^7.0.2", | ||
"@stryker-mutator/jest-runner": "^7.0.2", | ||
"@stryker-mutator/typescript-checker": "^7.0.2", | ||
"@types/eslint": "^8", | ||
"@types/jest": "^29.5.12", | ||
"eslint": "^8.38.0", | ||
"eslint-config-codex": "^1.7.2", | ||
"eslint-plugin-import": "^2.29.0", | ||
"jest": "^29.7.0", | ||
"stryker-cli": "^1.0.2", | ||
"ts-jest": "^29.2.5", | ||
"ts-node": "^10.9.2", | ||
"typescript": "^5.5.4" | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
packages/collaboration-manager/src/CollaborationManager.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* eslint-disable @typescript-eslint/no-magic-numbers */ | ||
import { createDataKey, IndexBuilder } from '@editorjs/model'; | ||
import { EditorJSModel } from '@editorjs/model'; | ||
import { CollaborationManager } from './CollaborationManager.js'; | ||
import { Operation, OperationType } from './Operation.js'; | ||
|
||
describe('CollaborationManager', () => { | ||
it('should add text on apply Insert Operation', () => { | ||
const model = new EditorJSModel({ | ||
blocks: [ { | ||
name: 'paragraph', | ||
data: { | ||
text: { | ||
value: '', | ||
$t: 't', | ||
}, | ||
}, | ||
} ], | ||
}); | ||
const collaborationManager = new CollaborationManager(model); | ||
const index = new IndexBuilder().addBlockIndex(0) | ||
.addDataKey(createDataKey('text')) | ||
.addTextRange([0, 4]) | ||
.build(); | ||
const operation = new Operation(OperationType.Insert, index, { | ||
prevValue: '', | ||
newValue: 'test', | ||
}); | ||
|
||
collaborationManager.applyOperation(operation); | ||
expect(model.serialized).toStrictEqual({ | ||
blocks: [ { | ||
name: 'paragraph', | ||
tunes: {}, | ||
data: { | ||
text: { | ||
$t: 't', | ||
value: 'test', | ||
fragments: [], | ||
}, | ||
}, | ||
} ], | ||
properties: {}, | ||
}); | ||
}); | ||
|
||
|
||
it('should remove text on apply Remove Operation', () => { | ||
const model = new EditorJSModel({ | ||
blocks: [ { | ||
name: 'paragraph', | ||
data: { | ||
text: { | ||
value: 'hel11lo', | ||
$t: 't', | ||
}, | ||
}, | ||
} ], | ||
}); | ||
const collaborationManager = new CollaborationManager(model); | ||
const index = new IndexBuilder().addBlockIndex(0) | ||
.addDataKey(createDataKey('text')) | ||
.addTextRange([ | ||
3, 5]) | ||
.build(); | ||
const operation = new Operation(OperationType.Delete, index, { | ||
prevValue: '11', | ||
newValue: '', | ||
}); | ||
|
||
collaborationManager.applyOperation(operation); | ||
expect(model.serialized).toStrictEqual({ | ||
blocks: [ { | ||
name: 'paragraph', | ||
tunes: {}, | ||
data: { | ||
text: { | ||
$t: 't', | ||
value: 'hello', | ||
fragments: [], | ||
}, | ||
}, | ||
} ], | ||
properties: {}, | ||
}); | ||
}); | ||
}); |
78 changes: 78 additions & 0 deletions
78
packages/collaboration-manager/src/CollaborationManager.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import type { EditorJSModel, ModelEvents } from '@editorjs/model'; | ||
import { EventType, TextAddedEvent, TextRemovedEvent } from '@editorjs/model'; | ||
import { Operation, OperationType } from './Operation.js'; | ||
|
||
/** | ||
* CollaborationManager listens to EditorJSModel events and applies operations | ||
*/ | ||
export class CollaborationManager { | ||
/** | ||
* EditorJSModel instance to listen to and apply operations | ||
*/ | ||
#model: EditorJSModel; | ||
|
||
/** | ||
* Creates an instance of CollaborationManager | ||
* | ||
* @param model - EditorJSModel instance to listen to and apply operations | ||
*/ | ||
constructor(model: EditorJSModel) { | ||
this.#model = model; | ||
model.addEventListener(EventType.Changed, this.#handleEvent.bind(this)); | ||
} | ||
|
||
/** | ||
* Applies operation to the model | ||
* | ||
* @param operation - operation to apply | ||
*/ | ||
public applyOperation(operation: Operation): void { | ||
const { blockIndex, dataKey, textRange } = operation.index; | ||
|
||
if (blockIndex == undefined || dataKey == undefined || textRange == undefined) { | ||
throw new Error('Unsupported index'); | ||
} | ||
|
||
switch (operation.type) { | ||
case OperationType.Insert: | ||
this.#model.insertText(blockIndex, dataKey, operation.data.newValue); | ||
break; | ||
case OperationType.Delete: | ||
this.#model.removeText(blockIndex, dataKey, textRange[0], textRange[1]); | ||
break; | ||
case OperationType.Modify: | ||
console.log('modify operation is not implemented yet'); | ||
// this.#model.insertText(blockIndex, dataKey, operation.data.newValue); | ||
break; | ||
default: | ||
throw new Error('Unknown operation type'); | ||
} | ||
} | ||
|
||
/** | ||
* Handles EditorJSModel events | ||
* | ||
* @param e - event to handle | ||
*/ | ||
#handleEvent(e: ModelEvents): void { | ||
let operation: Operation | null = null; | ||
|
||
switch (true) { | ||
case (e instanceof TextAddedEvent): | ||
operation = new Operation(OperationType.Insert, e.detail.index, { | ||
prevValue: '', | ||
newValue: e.detail.data, | ||
}); | ||
break; | ||
case (e instanceof TextRemovedEvent): | ||
operation = new Operation(OperationType.Delete, e.detail.index, { | ||
prevValue: e.detail.data, | ||
newValue: '', | ||
}); | ||
break; | ||
default: | ||
console.error('Unknown event type', e); | ||
} | ||
console.log('operation', operation); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import type { Index } from '@editorjs/model'; | ||
|
||
/** | ||
* Type of the operation | ||
*/ | ||
export enum OperationType { | ||
Insert = 'insert', | ||
Delete = 'delete', | ||
Modify = 'modify' | ||
} | ||
|
||
/** | ||
* Data for the operation | ||
*/ | ||
export interface OperationData { | ||
/** | ||
* Value before the operation | ||
*/ | ||
prevValue: string; | ||
|
||
/** | ||
* Value after the operation | ||
*/ | ||
newValue: string; | ||
} | ||
|
||
|
||
/** | ||
* Class representing operation on the document model tree | ||
*/ | ||
export class Operation { | ||
/** | ||
* Operation type | ||
*/ | ||
public type: OperationType; | ||
|
||
/** | ||
* Index in the document model tree | ||
*/ | ||
public index: Index; | ||
|
||
/** | ||
* Operation data | ||
*/ | ||
public data: OperationData; | ||
|
||
/** | ||
* Creates an instance of Operation | ||
* | ||
* @param type - operation type | ||
* @param index - index in the document model tree | ||
* @param data - operation data | ||
*/ | ||
constructor(type: OperationType, index: Index, data: OperationData) { | ||
this.type = type; | ||
this.index = index; | ||
this.data = data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './CollaborationManager.js'; | ||
export * from './Operation.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// @ts-check | ||
/** @type {import('@stryker-mutator/api/core').PartialStrykerOptions} */ | ||
const config = { | ||
_comment: | ||
"This config was generated using 'stryker init'. Please take a look at: https://stryker-mutator.io/docs/stryker-js/configuration/ for more information.", | ||
packageManager: "yarn", | ||
thresholds: { | ||
break: 75, | ||
}, | ||
thresholds_comment: "Minimum required coverage. Increase once we're closer to 100%.", | ||
clearTextReporter: { | ||
allowEmojis: true, | ||
}, | ||
reporters: [ | ||
"html", | ||
"clear-text", | ||
"progress", | ||
"dashboard", | ||
], | ||
testRunner: "jest", | ||
testRunner_comment: | ||
"Take a look at https://stryker-mutator.io/docs/stryker-js/jest-runner for information about the jest plugin.", | ||
coverageAnalysis: "perTest", | ||
tsconfigFile: "tsconfig.json", | ||
checkers: ["typescript"], | ||
timeoutMS: 10000, | ||
mutate: ["./src/**/*.ts", "!./src/**/mocks/*.ts", "!./src/**/__mocks__/*.ts", "!./src/**/*.spec.ts"], | ||
/* | ||
* In some cases PRs might not have any unit-tests | ||
*/ | ||
allowEmpty: true, | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"extends": "./tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "dist", | ||
"declaration": true | ||
}, | ||
"include": [ | ||
"src/**/*.ts" | ||
], | ||
"exclude": [ | ||
"src/**/*.spec.ts" | ||
] | ||
} |
Oops, something went wrong.
927cafb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage report for
./packages/model
Test suite run success
390 tests passing in 24 suites.
Report generated by 🧪jest coverage report action from 927cafb