mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-24 18:56:32 -05:00
feat(packages): add commons package
This is an import of 166ca8da12
with some changes to make it fit into the mono repo.
- TypedEventEmitter has been replaced with EventEmitter2 because EventEmitter2 is faster and TypedEventEmitter had some troubles with the new way of compiling.
- tsc-esm has been replaced with microbundle. The problems that lib0 doesn't export its types correctly has been solved using yarn patch.
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
814d8bc856
commit
7320fe2ac1
49 changed files with 3058 additions and 88 deletions
5
.github/actions/setup-node/action.yml
vendored
5
.github/actions/setup-node/action.yml
vendored
|
@ -36,3 +36,8 @@ runs:
|
||||||
run: yarn install --immutable
|
run: yarn install --immutable
|
||||||
working-directory: .
|
working-directory: .
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build commons lib
|
||||||
|
run: yarn build
|
||||||
|
working-directory: commons
|
||||||
|
shell: bash
|
||||||
|
|
1
.github/workflows/backend-docker.yml
vendored
1
.github/workflows/backend-docker.yml
vendored
|
@ -39,6 +39,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'backend/**'
|
- 'backend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
1
.github/workflows/backend-e2e-tests.yml
vendored
1
.github/workflows/backend-e2e-tests.yml
vendored
|
@ -40,6 +40,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'backend/**'
|
- 'backend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
1
.github/workflows/backend-tests.yml
vendored
1
.github/workflows/backend-tests.yml
vendored
|
@ -41,6 +41,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'backend/**'
|
- 'backend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
60
.github/workflows/commons-lint.yml
vendored
Normal file
60
.github/workflows/commons-lint.yml
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
name: Commons / Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ develop ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ develop ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: 18
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: commons
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changes:
|
||||||
|
name: Check for commons changes
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
outputs:
|
||||||
|
changed: ${{ github.event_name == 'push' || steps.changed.outputs.files }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
if: github.event_name != 'push'
|
||||||
|
|
||||||
|
- name: Check for frontend file changes
|
||||||
|
if: github.event_name != 'push'
|
||||||
|
uses: dorny/paths-filter@v2
|
||||||
|
id: changed
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
files:
|
||||||
|
- 'commons/**'
|
||||||
|
- '.github/**'
|
||||||
|
- '.yarn/**'
|
||||||
|
|
||||||
|
lint:
|
||||||
|
needs: changes
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Lints all .ts and .tsx files
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
NODE_VERSION: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
|
- name: Lint code
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
run: yarn lint
|
68
.github/workflows/commons-tests.yml
vendored
Normal file
68
.github/workflows/commons-tests.yml
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
name: Commons / Run unit tests & build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ develop ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ develop ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: commons
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changes:
|
||||||
|
name: Check for commons changes
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
outputs:
|
||||||
|
changed: ${{ github.event_name == 'push' || steps.changed.outputs.files }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
if: github.event_name != 'push'
|
||||||
|
|
||||||
|
- name: Check for frontend file changes
|
||||||
|
if: github.event_name != 'push'
|
||||||
|
uses: dorny/paths-filter@v2
|
||||||
|
id: changed
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
files:
|
||||||
|
- 'commons/**'
|
||||||
|
- '.github/**'
|
||||||
|
- '.yarn/**'
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: changes
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node: [ '14', '16', '18' ]
|
||||||
|
name: Test and build with NodeJS ${{ matrix.node }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
NODE_VERSION: ${{ matrix.node }}
|
||||||
|
|
||||||
|
- name: Test Project
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
run: yarn test
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
if: needs.changes.outputs.changed == 'true'
|
||||||
|
run: yarn build
|
1
.github/workflows/frontend-docker.yml
vendored
1
.github/workflows/frontend-docker.yml
vendored
|
@ -35,6 +35,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
1
.github/workflows/frontend-e2e-tests.yml
vendored
1
.github/workflows/frontend-e2e-tests.yml
vendored
|
@ -46,6 +46,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
1
.github/workflows/frontend-lint.yml
vendored
1
.github/workflows/frontend-lint.yml
vendored
|
@ -36,6 +36,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
|
@ -39,6 +39,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
|
@ -57,6 +57,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
|
@ -37,6 +37,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
files:
|
files:
|
||||||
|
- 'commons/**'
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- '.yarn/**'
|
- '.yarn/**'
|
||||||
|
|
366
.yarn/patches/lib0-npm-0.2.58-7de44657f5.patch
Normal file
366
.yarn/patches/lib0-npm-0.2.58-7de44657f5.patch
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
diff --git a/package.json b/package.json
|
||||||
|
index 9f2285b2a327935a64443dfff3b4389d2df0620d..b2a923b42f4a3f0fda68a9707cf65ac6007502e6 100644
|
||||||
|
--- a/package.json
|
||||||
|
+++ b/package.json
|
||||||
|
@@ -15,271 +15,316 @@
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": {
|
||||||
|
"import": "./index.js",
|
||||||
|
- "require": "./dist/index.cjs"
|
||||||
|
+ "require": "./dist/index.cjs",
|
||||||
|
+ "types": "./index.d.ts"
|
||||||
|
},
|
||||||
|
"./array.js": "./array.js",
|
||||||
|
"./dist/array.cjs": "./dist/array.cjs",
|
||||||
|
"./array": {
|
||||||
|
"import": "./array.js",
|
||||||
|
- "require": "./dist/array.cjs"
|
||||||
|
+ "require": "./dist/array.cjs",
|
||||||
|
+ "types": "./array.d.ts"
|
||||||
|
},
|
||||||
|
"./binary.js": "./binary.js",
|
||||||
|
"./dist/binary.cjs": "./dist/binary.cjs",
|
||||||
|
"./binary": {
|
||||||
|
"import": "./binary.js",
|
||||||
|
- "require": "./dist/binary.cjs"
|
||||||
|
+ "require": "./dist/binary.cjs",
|
||||||
|
+ "types": "./binary.d.ts"
|
||||||
|
},
|
||||||
|
"./broadcastchannel.js": "./broadcastchannel.js",
|
||||||
|
"./dist/broadcastchannel.cjs": "./dist/broadcastchannel.cjs",
|
||||||
|
"./broadcastchannel": {
|
||||||
|
"import": "./broadcastchannel.js",
|
||||||
|
- "require": "./dist/broadcastchannel.cjs"
|
||||||
|
+ "require": "./dist/broadcastchannel.cjs",
|
||||||
|
+ "types": "./broadcastchannel.d.ts"
|
||||||
|
},
|
||||||
|
"./buffer.js": "./buffer.js",
|
||||||
|
"./dist/buffer.cjs": "./dist/buffer.cjs",
|
||||||
|
"./buffer": {
|
||||||
|
"import": "./buffer.js",
|
||||||
|
- "require": "./dist/buffer.cjs"
|
||||||
|
+ "require": "./dist/buffer.cjs",
|
||||||
|
+ "types": "./buffer.d.ts"
|
||||||
|
},
|
||||||
|
"./cache.js": "./cache.js",
|
||||||
|
"./dist/cache.cjs": "./dist/cache.cjs",
|
||||||
|
"./cache": {
|
||||||
|
"import": "./cache.js",
|
||||||
|
- "require": "./dist/cache.cjs"
|
||||||
|
+ "require": "./dist/cache.cjs",
|
||||||
|
+ "types": "./cache.d.ts"
|
||||||
|
},
|
||||||
|
"./component.js": "./component.js",
|
||||||
|
"./dist/component.cjs": "./dist/component.cjs",
|
||||||
|
"./component": {
|
||||||
|
"import": "./component.js",
|
||||||
|
- "require": "./dist/component.cjs"
|
||||||
|
+ "require": "./dist/component.cjs",
|
||||||
|
+ "types": "./component.d.ts"
|
||||||
|
},
|
||||||
|
"./conditions.js": "./conditions.js",
|
||||||
|
"./dist/conditions.cjs": "./dist/conditions.cjs",
|
||||||
|
"./conditions": {
|
||||||
|
"import": "./condititons.js",
|
||||||
|
- "require": "./dist/conditions.cjs"
|
||||||
|
+ "require": "./dist/conditions.cjs",
|
||||||
|
+ "types": "./conditions.d.ts"
|
||||||
|
},
|
||||||
|
"./decoding.js": "./decoding.js",
|
||||||
|
"./dist/decoding.cjs": "./dist/decoding.cjs",
|
||||||
|
"./decoding": {
|
||||||
|
"import": "./decoding.js",
|
||||||
|
- "require": "./dist/decoding.cjs"
|
||||||
|
+ "require": "./dist/decoding.cjs",
|
||||||
|
+ "types": "./decoding.d.ts"
|
||||||
|
},
|
||||||
|
"./diff.js": "./diff.js",
|
||||||
|
"./dist/diff.cjs": "./dist/diff.cjs",
|
||||||
|
"./diff": {
|
||||||
|
"import": "./diff.js",
|
||||||
|
- "require": "./dist/diff.cjs"
|
||||||
|
+ "require": "./dist/diff.cjs",
|
||||||
|
+ "types": "./diff.d.ts"
|
||||||
|
},
|
||||||
|
"./dom.js": "./dom.js",
|
||||||
|
"./dist/dom.cjs": "./dist/dom.cjs",
|
||||||
|
"./dom": {
|
||||||
|
"import": "./dom.js",
|
||||||
|
- "require": "./dist/dom.cjs"
|
||||||
|
+ "require": "./dist/dom.cjs",
|
||||||
|
+ "types": "./dom.d.ts"
|
||||||
|
},
|
||||||
|
"./encoding.js": "./encoding.js",
|
||||||
|
"./dist/encoding.cjs": "./dist/encoding.cjs",
|
||||||
|
"./encoding": {
|
||||||
|
"import": "./encoding.js",
|
||||||
|
- "require": "./dist/encoding.cjs"
|
||||||
|
+ "require": "./dist/encoding.cjs",
|
||||||
|
+ "types": "./encoding.d.ts"
|
||||||
|
},
|
||||||
|
"./environment.js": "./environment.js",
|
||||||
|
"./dist/environment.cjs": "./dist/environment.cjs",
|
||||||
|
"./environment": {
|
||||||
|
"import": "./environment.js",
|
||||||
|
- "require": "./dist/environment.cjs"
|
||||||
|
+ "require": "./dist/environment.cjs",
|
||||||
|
+ "types": "./environment.d.ts"
|
||||||
|
},
|
||||||
|
"./error.js": "./error.js",
|
||||||
|
"./dist/error.cjs": "./dist/error.cjs",
|
||||||
|
"./error": {
|
||||||
|
"import": "./error.js",
|
||||||
|
- "require": "./dist/error.cjs"
|
||||||
|
+ "require": "./dist/error.cjs",
|
||||||
|
+ "types": "./error.d.ts"
|
||||||
|
},
|
||||||
|
"./eventloop.js": "./eventloop.js",
|
||||||
|
"./dist/eventloop.cjs": "./dist/eventloop.cjs",
|
||||||
|
"./eventloop": {
|
||||||
|
"import": "./eventloop.js",
|
||||||
|
- "require": "./dist/eventloop.cjs"
|
||||||
|
+ "require": "./dist/eventloop.cjs",
|
||||||
|
+ "types": "./eventloop.d.ts"
|
||||||
|
},
|
||||||
|
"./function.js": "./function.js",
|
||||||
|
"./dist/function.cjs": "./dist/function.cjs",
|
||||||
|
"./function": {
|
||||||
|
"import": "./function.js",
|
||||||
|
- "require": "./dist/function.cjs"
|
||||||
|
+ "require": "./dist/function.cjs",
|
||||||
|
+ "types": "./function.d.ts"
|
||||||
|
},
|
||||||
|
"./indexeddb.js": "./indexeddb.js",
|
||||||
|
"./dist/indexeddb.cjs": "./dist/indexeddb.cjs",
|
||||||
|
"./indexeddb": {
|
||||||
|
"import": "./indexeddb.js",
|
||||||
|
- "require": "./dist/indexeddb.cjs"
|
||||||
|
+ "require": "./dist/indexeddb.cjs",
|
||||||
|
+ "types": "./indexeddb.d.ts"
|
||||||
|
},
|
||||||
|
"./isomorphic.js": "./isomorphic.js",
|
||||||
|
"./dist/isomorphic.cjs": "./dist/isomorphic.cjs",
|
||||||
|
"./isomorphic": {
|
||||||
|
"import": "./isomorphic.js",
|
||||||
|
- "require": "./dist/isomorphic.cjs"
|
||||||
|
+ "require": "./dist/isomorphic.cjs",
|
||||||
|
+ "types": "./isomorphic.d.ts"
|
||||||
|
},
|
||||||
|
"./iterator.js": "./iterator.js",
|
||||||
|
"./dist/iterator.cjs": "./dist/iterator.cjs",
|
||||||
|
"./iterator": {
|
||||||
|
"import": "./iterator.js",
|
||||||
|
- "require": "./dist/iterator.cjs"
|
||||||
|
+ "require": "./dist/iterator.cjs",
|
||||||
|
+ "types": "./iterator.d.ts"
|
||||||
|
},
|
||||||
|
"./json.js": "./json.js",
|
||||||
|
"./dist/json.cjs": "./dist/json.cjs",
|
||||||
|
"./json": {
|
||||||
|
"import": "./json.js",
|
||||||
|
- "require": "./dist/json.cjs"
|
||||||
|
+ "require": "./dist/json.cjs",
|
||||||
|
+ "types": "./json.d.ts"
|
||||||
|
},
|
||||||
|
"./list.js": "./list.js",
|
||||||
|
"./dist/list.cjs": "./dist/list.cjs",
|
||||||
|
"./list": {
|
||||||
|
"import": "./list.js",
|
||||||
|
- "require": "./dist/list.cjs"
|
||||||
|
+ "require": "./dist/list.cjs",
|
||||||
|
+ "types": "./list.d.ts"
|
||||||
|
},
|
||||||
|
"./logging.js": "./logging.js",
|
||||||
|
"./dist/logging.cjs": "./dist/logging.cjs",
|
||||||
|
"./logging": {
|
||||||
|
"import": "./logging.js",
|
||||||
|
- "require": "./dist/logging.cjs"
|
||||||
|
+ "require": "./dist/logging.cjs",
|
||||||
|
+ "types": "./logging.d.ts"
|
||||||
|
},
|
||||||
|
"./map.js": "./map.js",
|
||||||
|
"./dist/map.cjs": "./dist/map.cjs",
|
||||||
|
"./map": {
|
||||||
|
"import": "./map.js",
|
||||||
|
- "require": "./dist/map.cjs"
|
||||||
|
+ "require": "./dist/map.cjs",
|
||||||
|
+ "types": "./map.d.ts"
|
||||||
|
},
|
||||||
|
"./math.js": "./math.js",
|
||||||
|
"./dist/math.cjs": "./dist/math.cjs",
|
||||||
|
"./math": {
|
||||||
|
"import": "./math.js",
|
||||||
|
- "require": "./dist/math.cjs"
|
||||||
|
+ "require": "./dist/math.cjs",
|
||||||
|
+ "types": "./math.d.ts"
|
||||||
|
},
|
||||||
|
"./metric.js": "./metric.js",
|
||||||
|
"./dist/metric.cjs": "./dist/metric.cjs",
|
||||||
|
"./metric": {
|
||||||
|
"import": "./metric.js",
|
||||||
|
- "require": "./dist/metric.cjs"
|
||||||
|
+ "require": "./dist/metric.cjs",
|
||||||
|
+ "types": "./metric.d.ts"
|
||||||
|
},
|
||||||
|
"./mutex.js": "./mutex.js",
|
||||||
|
"./dist/mutex.cjs": "./dist/mutex.cjs",
|
||||||
|
"./mutex": {
|
||||||
|
"import": "./mutex.js",
|
||||||
|
- "require": "./dist/mutex.cjs"
|
||||||
|
+ "require": "./dist/mutex.cjs",
|
||||||
|
+ "types": "./mutex.d.ts"
|
||||||
|
},
|
||||||
|
"./number.js": "./number.js",
|
||||||
|
"./dist/number.cjs": "./dist/number.cjs",
|
||||||
|
"./number": {
|
||||||
|
"import": "./number.js",
|
||||||
|
- "require": "./dist/number.cjs"
|
||||||
|
+ "require": "./dist/number.cjs",
|
||||||
|
+ "types": "./number.d.ts"
|
||||||
|
},
|
||||||
|
"./object.js": "./object.js",
|
||||||
|
"./dist/object.cjs": "./dist/object.cjs",
|
||||||
|
"./object": {
|
||||||
|
"import": "./object.js",
|
||||||
|
- "require": "./dist/object.cjs"
|
||||||
|
+ "require": "./dist/object.cjs",
|
||||||
|
+ "types": "./object.d.ts"
|
||||||
|
},
|
||||||
|
"./observable.js": "./observable.js",
|
||||||
|
"./dist/observable.cjs": "./dist/observable.cjs",
|
||||||
|
"./observable": {
|
||||||
|
"import": "./observable.js",
|
||||||
|
- "require": "./dist/observable.cjs"
|
||||||
|
+ "require": "./dist/observable.cjs",
|
||||||
|
+ "types": "./observable.d.ts"
|
||||||
|
},
|
||||||
|
"./pair.js": "./pair.js",
|
||||||
|
"./dist/pair.cjs": "./dist/pair.cjs",
|
||||||
|
"./pair": {
|
||||||
|
"import": "./pair.js",
|
||||||
|
- "require": "./dist/pair.cjs"
|
||||||
|
+ "require": "./dist/pair.cjs",
|
||||||
|
+ "types": "./pair.d.ts"
|
||||||
|
},
|
||||||
|
"./prng.js": "./prng.js",
|
||||||
|
"./dist/prng.cjs": "./dist/prng.cjs",
|
||||||
|
"./prng": {
|
||||||
|
"import": "./prng.js",
|
||||||
|
- "require": "./dist/prng.cjs"
|
||||||
|
+ "require": "./dist/prng.cjs",
|
||||||
|
+ "types": "./prng.d.ts"
|
||||||
|
},
|
||||||
|
"./promise.js": "./promise.js",
|
||||||
|
"./dist/promise.cjs": "./dist/promise.cjs",
|
||||||
|
"./promise": {
|
||||||
|
"import": "./promise.js",
|
||||||
|
- "require": "./dist/promise.cjs"
|
||||||
|
+ "require": "./dist/promise.cjs",
|
||||||
|
+ "types": "./promise.d.ts"
|
||||||
|
},
|
||||||
|
"./queue.js": "./queue.js",
|
||||||
|
"./dist/queue.cjs": "./dist/queue.cjs",
|
||||||
|
"./queue": {
|
||||||
|
"import": "./queue.js",
|
||||||
|
- "require": "./dist/queue.cjs"
|
||||||
|
+ "require": "./dist/queue.cjs",
|
||||||
|
+ "types": "./queue.d.ts"
|
||||||
|
},
|
||||||
|
"./random.js": "./random.js",
|
||||||
|
"./dist/random.cjs": "./dist/random.cjs",
|
||||||
|
"./random": {
|
||||||
|
"import": "./random.js",
|
||||||
|
- "require": "./dist/random.cjs"
|
||||||
|
+ "require": "./dist/random.cjs",
|
||||||
|
+ "types": "./random.d.ts"
|
||||||
|
},
|
||||||
|
"./set.js": "./set.js",
|
||||||
|
"./dist/set.cjs": "./dist/set.cjs",
|
||||||
|
"./set": {
|
||||||
|
"import": "./set.js",
|
||||||
|
- "require": "./dist/set.cjs"
|
||||||
|
+ "require": "./dist/set.cjs",
|
||||||
|
+ "types": "./set.d.ts"
|
||||||
|
},
|
||||||
|
"./sort.js": "./sort.js",
|
||||||
|
"./dist/sort.cjs": "./dist/sort.cjs",
|
||||||
|
"./sort": {
|
||||||
|
"import": "./sort.js",
|
||||||
|
- "require": "./dist/sort.cjs"
|
||||||
|
+ "require": "./dist/sort.cjs",
|
||||||
|
+ "types": "./sort.d.ts"
|
||||||
|
},
|
||||||
|
"./statistics.js": "./statistics.js",
|
||||||
|
"./dist/statistics.cjs": "./dist/statistics.cjs",
|
||||||
|
"./statistics": {
|
||||||
|
"import": "./statistics.js",
|
||||||
|
- "require": "./dist/statistics.cjs"
|
||||||
|
+ "require": "./dist/statistics.cjs",
|
||||||
|
+ "types": "./statistics.d.ts"
|
||||||
|
},
|
||||||
|
"./storage.js": "./storage.js",
|
||||||
|
"./dist/storage.cjs": "./dist/storage.cjs",
|
||||||
|
"./storage": {
|
||||||
|
"import": "./storage.js",
|
||||||
|
- "require": "./dist/storage.cjs"
|
||||||
|
+ "require": "./dist/storage.cjs",
|
||||||
|
+ "types": "./storage.d.ts"
|
||||||
|
},
|
||||||
|
"./string.js": "./string.js",
|
||||||
|
"./dist/string.cjs": "./dist/string.cjs",
|
||||||
|
"./string": {
|
||||||
|
"import": "./string.js",
|
||||||
|
- "require": "./dist/string.cjs"
|
||||||
|
+ "require": "./dist/string.cjs",
|
||||||
|
+ "types": "./string.d.ts"
|
||||||
|
},
|
||||||
|
"./symbol.js": "./symbol.js",
|
||||||
|
"./dist/symbol.cjs": "./dist/symbol.cjs",
|
||||||
|
"./symbol": {
|
||||||
|
"import": "./symbol.js",
|
||||||
|
- "require": "./dist/symbol.cjs"
|
||||||
|
+ "require": "./dist/symbol.cjs",
|
||||||
|
+ "types": "./symbol.d.ts"
|
||||||
|
},
|
||||||
|
"./testing.js": "./testing.js",
|
||||||
|
"./dist/testing.cjs": "./dist/testing.cjs",
|
||||||
|
"./testing": {
|
||||||
|
"import": "./testing.js",
|
||||||
|
- "require": "./dist/testing.cjs"
|
||||||
|
+ "require": "./dist/testing.cjs",
|
||||||
|
+ "types": "./testing.d.ts"
|
||||||
|
},
|
||||||
|
"./time.js": "./time.js",
|
||||||
|
"./dist/time.cjs": "./dist/time.cjs",
|
||||||
|
"./time": {
|
||||||
|
"import": "./time.js",
|
||||||
|
- "require": "./dist/time.cjs"
|
||||||
|
+ "require": "./dist/time.cjs",
|
||||||
|
+ "types": "./time.d.ts"
|
||||||
|
},
|
||||||
|
"./tree.js": "./tree.js",
|
||||||
|
"./dist/tree.cjs": "./dist/tree.cjs",
|
||||||
|
"./tree": {
|
||||||
|
"import": "./tree.js",
|
||||||
|
- "require": "./dist/tree.cjs"
|
||||||
|
+ "require": "./dist/tree.cjs",
|
||||||
|
+ "types": "./tree.d.ts"
|
||||||
|
},
|
||||||
|
"./url.js": "./url.js",
|
||||||
|
"./dist/url.cjs": "./dist/url.cjs",
|
||||||
|
"./url": {
|
||||||
|
"import": "./url.js",
|
||||||
|
- "require": "./dist/url.cjs"
|
||||||
|
+ "require": "./dist/url.cjs",
|
||||||
|
+ "types": "./url.d.ts"
|
||||||
|
},
|
||||||
|
"./websocket.js": "./websocket.js",
|
||||||
|
"./dist/websocket.cjs": "./dist/websocket.cjs",
|
||||||
|
"./websocket": {
|
||||||
|
"import": "./websocket.js",
|
||||||
|
- "require": "./dist/websocket.cjs"
|
||||||
|
+ "require": "./dist/websocket.cjs",
|
||||||
|
+ "types": "./websocket.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
26
.yarn/patches/y-protocols-npm-1.0.5-af6f64b4df.patch
Normal file
26
.yarn/patches/y-protocols-npm-1.0.5-af6f64b4df.patch
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
diff --git a/package.json b/package.json
|
||||||
|
index 5f953f00544710a638dc502b30841d39193f6d3f..6c31784d1b1f32ee8f21106011c4e6ef526f1560 100644
|
||||||
|
--- a/package.json
|
||||||
|
+++ b/package.json
|
||||||
|
@@ -47,18 +47,21 @@
|
||||||
|
"./sync.js": "./sync.js",
|
||||||
|
"./dist/sync.cjs": "./dist/sync.cjs",
|
||||||
|
"./sync": {
|
||||||
|
+ "types": "./sync.d.ts",
|
||||||
|
"import": "./sync.js",
|
||||||
|
"require": "./dist/sync.cjs"
|
||||||
|
},
|
||||||
|
"./awareness.js": "./awareness.js",
|
||||||
|
"./dist/awareness.cjs": "./dist/awareness.cjs",
|
||||||
|
"./awareness": {
|
||||||
|
+ "types": "./awareness.d.ts",
|
||||||
|
"import": "./awareness.js",
|
||||||
|
"require": "./dist/awareness.cjs"
|
||||||
|
},
|
||||||
|
"./auth.js": "./auth.js",
|
||||||
|
"./dist/auth.cjs": "./dist/auth.cjs",
|
||||||
|
"./auth": {
|
||||||
|
+ "types": "./auth.d.ts",
|
||||||
|
"import": "./auth.js",
|
||||||
|
"require": "./dist/auth.cjs"
|
||||||
|
}
|
40
commons/.eslintrc.cjs
Normal file
40
commons/.eslintrc.cjs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": [
|
||||||
|
"./tsconfig-eslint.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"jest",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"jest": true,
|
||||||
|
"jest/globals": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": ["error",
|
||||||
|
require('./.prettierrc.json')
|
||||||
|
],
|
||||||
|
"jest/no-disabled-tests": "warn",
|
||||||
|
"jest/no-focused-tests": "error",
|
||||||
|
"jest/no-identical-title": "error",
|
||||||
|
"jest/prefer-to-have-length": "warn",
|
||||||
|
"jest/valid-expect": "error"
|
||||||
|
}
|
||||||
|
}
|
31
commons/.gitignore
vendored
Normal file
31
commons/.gitignore
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# package manager
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/dist
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
9
commons/.npmignore
Normal file
9
commons/.npmignore
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.idea
|
||||||
|
.babelrc
|
||||||
|
.eslintrc
|
||||||
|
.travis.yml
|
||||||
|
karma.conf.js
|
||||||
|
tests.webpack.js
|
||||||
|
webpack.config.*.js
|
||||||
|
coverage/
|
||||||
|
test/
|
3
commons/.npmignore.license
Normal file
3
commons/.npmignore.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
1
commons/.prettierignore
Normal file
1
commons/.prettierignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
4
commons/.prettierignore.license
Normal file
4
commons/.prettierignore.license
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
11
commons/.prettierrc.json
Normal file
11
commons/.prettierrc.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"parser": "typescript",
|
||||||
|
"singleQuote": true,
|
||||||
|
"jsxSingleQuote": true,
|
||||||
|
"semi": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
3
commons/.prettierrc.json.license
Normal file
3
commons/.prettierrc.json.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
25
commons/jest.config.json
Normal file
25
commons/jest.config.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"testRegex" : "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
|
||||||
|
"testPathIgnorePatterns" : [
|
||||||
|
"/dist/"
|
||||||
|
],
|
||||||
|
"moduleFileExtensions" : [
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"js"
|
||||||
|
],
|
||||||
|
"extensionsToTreatAsEsm" : [
|
||||||
|
".ts"
|
||||||
|
],
|
||||||
|
"moduleNameMapper" : {
|
||||||
|
"^(\\.{1,2}/.*)\\.js$" : "$1"
|
||||||
|
},
|
||||||
|
"transform" : {
|
||||||
|
"^.+\\.tsx?$" : [
|
||||||
|
"ts-jest",
|
||||||
|
{
|
||||||
|
"useESM" : true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
3
commons/jest.config.json.license
Normal file
3
commons/jest.config.json.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
60
commons/package.json
Normal file
60
commons/package.json
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"name": "@hedgedoc/commons",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.3.0",
|
||||||
|
"description": "Common code between frontend and backend",
|
||||||
|
"author": "The HedgeDoc Authors",
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rm -rf dist && microbundle",
|
||||||
|
"build:watch": "rm -rf dist && microbundle -w",
|
||||||
|
"test": "jest",
|
||||||
|
"prepublish": "rm -rf dist && yarn lint && yarn build && yarn test",
|
||||||
|
"lint": "eslint src --ext .ts",
|
||||||
|
"lint:fix": "eslint --fix --ext .ts src"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"main": "dist/index.cjs",
|
||||||
|
"module": "dist/index.mjs",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"source": "src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.mjs",
|
||||||
|
"require": "./dist/index.cjs"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"LICENSES/*",
|
||||||
|
"package.json",
|
||||||
|
"README.md",
|
||||||
|
"dist/**"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/hedgedoc/hedgedoc.git"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter2": "^6.4.9",
|
||||||
|
"isomorphic-ws": "^5.0.0",
|
||||||
|
"lib0": "^0.2.51",
|
||||||
|
"ws": "^8.0.0",
|
||||||
|
"y-protocols": "^1.0.0",
|
||||||
|
"yjs": "13.5.43"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@jest/types": "29.3.1",
|
||||||
|
"@types/ws": "8.5.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "5.44.0",
|
||||||
|
"@typescript-eslint/parser": "5.44.0",
|
||||||
|
"eslint": "8.29.0",
|
||||||
|
"eslint-config-prettier": "8.5.0",
|
||||||
|
"eslint-plugin-jest": "27.1.6",
|
||||||
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
|
"jest": "29.3.1",
|
||||||
|
"microbundle": "0.15.1",
|
||||||
|
"prettier": "2.8.1",
|
||||||
|
"ts-jest": "29.0.3",
|
||||||
|
"typescript": "4.9.3"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@3.3.0"
|
||||||
|
}
|
3
commons/package.json.license
Normal file
3
commons/package.json.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
77
commons/src/connection-keep-alive-handler.ts
Normal file
77
commons/src/connection-keep-alive-handler.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { MessageType } from './messages/message-type.enum.js'
|
||||||
|
import type { YDocMessageTransporter } from './y-doc-message-transporter.js'
|
||||||
|
import { createEncoder, toUint8Array, writeVarUint } from 'lib0/encoding'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a keep alive ping for a given {@link WebSocket websocket} connection by sending a periodic message.
|
||||||
|
*/
|
||||||
|
export class ConnectionKeepAliveHandler {
|
||||||
|
private pongReceived = false
|
||||||
|
private static readonly pingTimeout = 30 * 1000
|
||||||
|
private intervalId: NodeJS.Timer | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the instance and starts the interval.
|
||||||
|
*
|
||||||
|
* @param messageTransporter The websocket to keep alive
|
||||||
|
*/
|
||||||
|
constructor(private messageTransporter: YDocMessageTransporter) {
|
||||||
|
this.messageTransporter.on('disconnected', () => this.stopTimer())
|
||||||
|
this.messageTransporter.on('ready', () => this.startTimer())
|
||||||
|
this.messageTransporter.on(String(MessageType.PING), () => {
|
||||||
|
this.sendPongMessage()
|
||||||
|
})
|
||||||
|
this.messageTransporter.on(
|
||||||
|
String(MessageType.PONG),
|
||||||
|
() => (this.pongReceived = true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the ping timer.
|
||||||
|
*/
|
||||||
|
public startTimer(): void {
|
||||||
|
this.pongReceived = false
|
||||||
|
this.intervalId = setInterval(
|
||||||
|
() => this.check(),
|
||||||
|
ConnectionKeepAliveHandler.pingTimeout
|
||||||
|
)
|
||||||
|
this.sendPingMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
public stopTimer(): void {
|
||||||
|
clearInterval(this.intervalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a pong has been received since the last run. If not, the connection is probably dead and will be terminated.
|
||||||
|
*/
|
||||||
|
private check(): void {
|
||||||
|
if (this.pongReceived) {
|
||||||
|
this.pongReceived = false
|
||||||
|
this.sendPingMessage()
|
||||||
|
} else {
|
||||||
|
this.messageTransporter.disconnect()
|
||||||
|
console.error(
|
||||||
|
`No pong received in the last ${ConnectionKeepAliveHandler.pingTimeout} seconds. Connection seems to be dead.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendPingMessage(): void {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, MessageType.PING)
|
||||||
|
this.messageTransporter.send(toUint8Array(encoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendPongMessage(): void {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, MessageType.PONG)
|
||||||
|
this.messageTransporter.send(toUint8Array(encoder))
|
||||||
|
}
|
||||||
|
}
|
27
commons/src/index.ts
Normal file
27
commons/src/index.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { MessageType } from './messages/message-type.enum.js'
|
||||||
|
export { ConnectionKeepAliveHandler } from './connection-keep-alive-handler.js'
|
||||||
|
export { YDocMessageTransporter } from './y-doc-message-transporter.js'
|
||||||
|
export {
|
||||||
|
applyAwarenessUpdateMessage,
|
||||||
|
encodeAwarenessUpdateMessage
|
||||||
|
} from './messages/awareness-update-message.js'
|
||||||
|
export {
|
||||||
|
applyDocumentUpdateMessage,
|
||||||
|
encodeDocumentUpdateMessage
|
||||||
|
} from './messages/document-update-message.js'
|
||||||
|
export { encodeCompleteAwarenessStateRequestMessage } from './messages/complete-awareness-state-request-message.js'
|
||||||
|
export { encodeCompleteDocumentStateRequestMessage } from './messages/complete-document-state-request-message.js'
|
||||||
|
export { encodeCompleteDocumentStateAnswerMessage } from './messages/complete-document-state-answer-message.js'
|
||||||
|
export { encodeDocumentDeletedMessage } from './messages/document-deleted-message.js'
|
||||||
|
export { encodeMetadataUpdatedMessage } from './messages/metadata-updated-message.js'
|
||||||
|
export { encodeServerVersionUpdatedMessage } from './messages/server-version-updated-message.js'
|
||||||
|
|
||||||
|
export { WebsocketTransporter } from './websocket-transporter.js'
|
||||||
|
|
||||||
|
export type { MessageTransporterEvents } from './y-doc-message-transporter.js'
|
40
commons/src/messages/awareness-update-message.ts
Normal file
40
commons/src/messages/awareness-update-message.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
import type { Decoder } from 'lib0/decoding'
|
||||||
|
import { readVarUint8Array } from 'lib0/decoding'
|
||||||
|
import {
|
||||||
|
createEncoder,
|
||||||
|
toUint8Array,
|
||||||
|
writeVarUint,
|
||||||
|
writeVarUint8Array
|
||||||
|
} from 'lib0/encoding'
|
||||||
|
import type { Awareness } from 'y-protocols/awareness'
|
||||||
|
import {
|
||||||
|
applyAwarenessUpdate,
|
||||||
|
encodeAwarenessUpdate
|
||||||
|
} from 'y-protocols/awareness'
|
||||||
|
|
||||||
|
export function applyAwarenessUpdateMessage(
|
||||||
|
decoder: Decoder,
|
||||||
|
awareness: Awareness,
|
||||||
|
origin: unknown
|
||||||
|
): void {
|
||||||
|
applyAwarenessUpdate(awareness, readVarUint8Array(decoder), origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeAwarenessUpdateMessage(
|
||||||
|
awareness: Awareness,
|
||||||
|
updatedClientIds: number[]
|
||||||
|
): Uint8Array {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, MessageType.AWARENESS_UPDATE)
|
||||||
|
writeVarUint8Array(
|
||||||
|
encoder,
|
||||||
|
encodeAwarenessUpdate(awareness, updatedClientIds)
|
||||||
|
)
|
||||||
|
return toUint8Array(encoder)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeGenericMessage } from './generic-message.js'
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
|
||||||
|
export function encodeCompleteAwarenessStateRequestMessage(): Uint8Array {
|
||||||
|
return encodeGenericMessage(MessageType.COMPLETE_AWARENESS_STATE_REQUEST)
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
import { decoding } from 'lib0'
|
||||||
|
import { Decoder } from 'lib0/decoding'
|
||||||
|
import {
|
||||||
|
createEncoder,
|
||||||
|
toUint8Array,
|
||||||
|
writeVarUint,
|
||||||
|
writeVarUint8Array
|
||||||
|
} from 'lib0/encoding'
|
||||||
|
import type { Doc } from 'yjs'
|
||||||
|
import { encodeStateAsUpdate } from 'yjs'
|
||||||
|
|
||||||
|
export function encodeCompleteDocumentStateAnswerMessage(
|
||||||
|
doc: Doc,
|
||||||
|
decoder: Decoder
|
||||||
|
): Uint8Array {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, MessageType.COMPLETE_DOCUMENT_STATE_ANSWER)
|
||||||
|
writeVarUint8Array(
|
||||||
|
encoder,
|
||||||
|
encodeStateAsUpdate(doc, decoding.readVarUint8Array(decoder))
|
||||||
|
)
|
||||||
|
return toUint8Array(encoder)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
import {
|
||||||
|
createEncoder,
|
||||||
|
toUint8Array,
|
||||||
|
writeVarUint,
|
||||||
|
writeVarUint8Array
|
||||||
|
} from 'lib0/encoding'
|
||||||
|
import type { Doc } from 'yjs'
|
||||||
|
import { encodeStateVector } from 'yjs'
|
||||||
|
|
||||||
|
export function encodeCompleteDocumentStateRequestMessage(
|
||||||
|
doc: Doc
|
||||||
|
): Uint8Array {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, MessageType.COMPLETE_DOCUMENT_STATE_REQUEST)
|
||||||
|
writeVarUint8Array(encoder, encodeStateVector(doc))
|
||||||
|
return toUint8Array(encoder)
|
||||||
|
}
|
11
commons/src/messages/document-deleted-message.ts
Normal file
11
commons/src/messages/document-deleted-message.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeGenericMessage } from './generic-message.js'
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
|
||||||
|
export function encodeDocumentDeletedMessage(): Uint8Array {
|
||||||
|
return encodeGenericMessage(MessageType.DOCUMENT_DELETED)
|
||||||
|
}
|
33
commons/src/messages/document-update-message.ts
Normal file
33
commons/src/messages/document-update-message.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
import { readVarUint8Array } from 'lib0/decoding'
|
||||||
|
import type { Decoder } from 'lib0/decoding.js'
|
||||||
|
import {
|
||||||
|
createEncoder,
|
||||||
|
toUint8Array,
|
||||||
|
writeVarUint,
|
||||||
|
writeVarUint8Array
|
||||||
|
} from 'lib0/encoding'
|
||||||
|
import type { Doc } from 'yjs'
|
||||||
|
import { applyUpdate } from 'yjs'
|
||||||
|
|
||||||
|
export function applyDocumentUpdateMessage(
|
||||||
|
decoder: Decoder,
|
||||||
|
doc: Doc,
|
||||||
|
origin: unknown
|
||||||
|
): void {
|
||||||
|
applyUpdate(doc, readVarUint8Array(decoder), origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeDocumentUpdateMessage(
|
||||||
|
documentUpdate: Uint8Array
|
||||||
|
): Uint8Array {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, MessageType.DOCUMENT_UPDATE)
|
||||||
|
writeVarUint8Array(encoder, documentUpdate)
|
||||||
|
return toUint8Array(encoder)
|
||||||
|
}
|
16
commons/src/messages/generic-message.ts
Normal file
16
commons/src/messages/generic-message.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
import { createEncoder, toUint8Array, writeVarUint } from 'lib0/encoding'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a generic message with a given message type but without content.
|
||||||
|
*/
|
||||||
|
export function encodeGenericMessage(messageType: MessageType): Uint8Array {
|
||||||
|
const encoder = createEncoder()
|
||||||
|
writeVarUint(encoder, messageType)
|
||||||
|
return toUint8Array(encoder)
|
||||||
|
}
|
20
commons/src/messages/message-type.enum.ts
Normal file
20
commons/src/messages/message-type.enum.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum MessageType {
|
||||||
|
COMPLETE_DOCUMENT_STATE_REQUEST = 0,
|
||||||
|
COMPLETE_DOCUMENT_STATE_ANSWER = 1,
|
||||||
|
DOCUMENT_UPDATE = 2,
|
||||||
|
AWARENESS_UPDATE = 3,
|
||||||
|
COMPLETE_AWARENESS_STATE_REQUEST = 4,
|
||||||
|
PING = 5,
|
||||||
|
PONG = 6,
|
||||||
|
READY_REQUEST = 7,
|
||||||
|
READY_ANSWER = 8,
|
||||||
|
METADATA_UPDATED = 9,
|
||||||
|
DOCUMENT_DELETED = 10,
|
||||||
|
SERVER_VERSION_UPDATED = 11
|
||||||
|
}
|
11
commons/src/messages/metadata-updated-message.ts
Normal file
11
commons/src/messages/metadata-updated-message.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeGenericMessage } from './generic-message.js'
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
|
||||||
|
export function encodeMetadataUpdatedMessage(): Uint8Array {
|
||||||
|
return encodeGenericMessage(MessageType.METADATA_UPDATED)
|
||||||
|
}
|
11
commons/src/messages/ready-answer-message.ts
Normal file
11
commons/src/messages/ready-answer-message.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeGenericMessage } from './generic-message.js'
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
|
||||||
|
export function encodeReadyAnswerMessage(): Uint8Array {
|
||||||
|
return encodeGenericMessage(MessageType.READY_ANSWER)
|
||||||
|
}
|
11
commons/src/messages/ready-request-message.ts
Normal file
11
commons/src/messages/ready-request-message.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeGenericMessage } from './generic-message.js'
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
|
||||||
|
export function encodeReadyRequestMessage(): Uint8Array {
|
||||||
|
return encodeGenericMessage(MessageType.READY_REQUEST)
|
||||||
|
}
|
11
commons/src/messages/server-version-updated-message.ts
Normal file
11
commons/src/messages/server-version-updated-message.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeGenericMessage } from './generic-message.js'
|
||||||
|
import { MessageType } from './message-type.enum.js'
|
||||||
|
|
||||||
|
export function encodeServerVersionUpdatedMessage(): Uint8Array {
|
||||||
|
return encodeGenericMessage(MessageType.SERVER_VERSION_UPDATED)
|
||||||
|
}
|
61
commons/src/websocket-transporter.ts
Normal file
61
commons/src/websocket-transporter.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { ConnectionKeepAliveHandler } from './connection-keep-alive-handler.js'
|
||||||
|
import { YDocMessageTransporter } from './y-doc-message-transporter.js'
|
||||||
|
import WebSocket from 'isomorphic-ws'
|
||||||
|
import { Awareness } from 'y-protocols/awareness'
|
||||||
|
import { Doc } from 'yjs'
|
||||||
|
|
||||||
|
export class WebsocketTransporter extends YDocMessageTransporter {
|
||||||
|
private websocket: WebSocket | undefined
|
||||||
|
|
||||||
|
constructor(doc: Doc, awareness: Awareness) {
|
||||||
|
super(doc, awareness)
|
||||||
|
new ConnectionKeepAliveHandler(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
public setupWebsocket(websocket: WebSocket) {
|
||||||
|
if (
|
||||||
|
websocket.readyState === WebSocket.CLOSED ||
|
||||||
|
websocket.readyState === WebSocket.CLOSING
|
||||||
|
) {
|
||||||
|
throw new Error(`Socket is closed`)
|
||||||
|
}
|
||||||
|
this.websocket = websocket
|
||||||
|
websocket.binaryType = 'arraybuffer'
|
||||||
|
websocket.addEventListener('message', (event) =>
|
||||||
|
this.decodeMessage(event.data as ArrayBuffer)
|
||||||
|
)
|
||||||
|
websocket.addEventListener('error', () => this.disconnect())
|
||||||
|
websocket.addEventListener('close', () => this.onClose())
|
||||||
|
if (websocket.readyState === WebSocket.OPEN) {
|
||||||
|
this.onOpen()
|
||||||
|
} else {
|
||||||
|
websocket.addEventListener('open', this.onOpen.bind(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnect(): void {
|
||||||
|
this.websocket?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
public send(content: Uint8Array): void {
|
||||||
|
if (this.websocket?.readyState !== WebSocket.OPEN) {
|
||||||
|
throw new Error("Can't send message over non-open socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.websocket.send(content)
|
||||||
|
} catch (error: unknown) {
|
||||||
|
this.disconnect()
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isWebSocketOpen(): boolean {
|
||||||
|
return this.websocket?.readyState === WebSocket.OPEN
|
||||||
|
}
|
||||||
|
}
|
206
commons/src/y-doc-message-transporter.test.ts
Normal file
206
commons/src/y-doc-message-transporter.test.ts
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { encodeDocumentUpdateMessage } from './messages/document-update-message.js'
|
||||||
|
import { MessageType } from './messages/message-type.enum.js'
|
||||||
|
import { YDocMessageTransporter } from './y-doc-message-transporter.js'
|
||||||
|
import { describe, expect, it } from '@jest/globals'
|
||||||
|
import { Awareness } from 'y-protocols/awareness'
|
||||||
|
import { Doc } from 'yjs'
|
||||||
|
|
||||||
|
class InMemoryMessageTransporter extends YDocMessageTransporter {
|
||||||
|
private otherSide: InMemoryMessageTransporter | undefined
|
||||||
|
|
||||||
|
constructor(private name: string, doc: Doc, awareness: Awareness) {
|
||||||
|
super(doc, awareness)
|
||||||
|
}
|
||||||
|
|
||||||
|
public connect(other: InMemoryMessageTransporter): void {
|
||||||
|
this.setOtherSide(other)
|
||||||
|
other.setOtherSide(this)
|
||||||
|
this.onOpen()
|
||||||
|
other.onOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
private setOtherSide(other: InMemoryMessageTransporter | undefined): void {
|
||||||
|
this.otherSide = other
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnect(): void {
|
||||||
|
this.onClose()
|
||||||
|
this.setOtherSide(undefined)
|
||||||
|
this.otherSide?.onClose()
|
||||||
|
this.otherSide?.setOtherSide(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
send(content: Uint8Array): void {
|
||||||
|
if (this.otherSide === undefined) {
|
||||||
|
throw new Error('Disconnected')
|
||||||
|
}
|
||||||
|
console.debug(`${this.name}`, 'Sending', content)
|
||||||
|
this.otherSide?.decodeMessage(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
public onOpen(): void {
|
||||||
|
super.onOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('message transporter', () =>
|
||||||
|
it('server client communication', () => {
|
||||||
|
const docServer: Doc = new Doc()
|
||||||
|
const docClient1: Doc = new Doc()
|
||||||
|
const docClient2: Doc = new Doc()
|
||||||
|
const dummyAwareness: Awareness = new Awareness(docServer)
|
||||||
|
|
||||||
|
const textServer = docServer.getText('markdownContent')
|
||||||
|
const textClient1 = docClient1.getText('markdownContent')
|
||||||
|
const textClient2 = docClient2.getText('markdownContent')
|
||||||
|
textServer.insert(0, 'This is a test note')
|
||||||
|
|
||||||
|
textServer.observe(() =>
|
||||||
|
console.debug('textServer', new Date(), textServer.toString())
|
||||||
|
)
|
||||||
|
textClient1.observe(() =>
|
||||||
|
console.debug('textClient1', new Date(), textClient1.toString())
|
||||||
|
)
|
||||||
|
textClient2.observe(() =>
|
||||||
|
console.debug('textClient2', new Date(), textClient2.toString())
|
||||||
|
)
|
||||||
|
|
||||||
|
const transporterServerTo1 = new InMemoryMessageTransporter(
|
||||||
|
's>1',
|
||||||
|
docServer,
|
||||||
|
dummyAwareness
|
||||||
|
)
|
||||||
|
const transporterServerTo2 = new InMemoryMessageTransporter(
|
||||||
|
's>2',
|
||||||
|
docServer,
|
||||||
|
dummyAwareness
|
||||||
|
)
|
||||||
|
const transporterClient1 = new InMemoryMessageTransporter(
|
||||||
|
'1>s',
|
||||||
|
docClient1,
|
||||||
|
dummyAwareness
|
||||||
|
)
|
||||||
|
const transporterClient2 = new InMemoryMessageTransporter(
|
||||||
|
'2>s',
|
||||||
|
docClient2,
|
||||||
|
dummyAwareness
|
||||||
|
)
|
||||||
|
|
||||||
|
transporterServerTo1.on(String(MessageType.DOCUMENT_UPDATE), () =>
|
||||||
|
console.debug('Received DOCUMENT_UPDATE from client 1 to server')
|
||||||
|
)
|
||||||
|
transporterServerTo2.on(String(MessageType.DOCUMENT_UPDATE), () =>
|
||||||
|
console.debug('Received DOCUMENT_UPDATE from client 2 to server')
|
||||||
|
)
|
||||||
|
transporterClient1.on(String(MessageType.DOCUMENT_UPDATE), () =>
|
||||||
|
console.debug('Received DOCUMENT_UPDATE from server to client 1')
|
||||||
|
)
|
||||||
|
transporterClient2.on(String(MessageType.DOCUMENT_UPDATE), () =>
|
||||||
|
console.debug('Received DOCUMENT_UPDATE from server to client 2')
|
||||||
|
)
|
||||||
|
|
||||||
|
transporterServerTo1.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_ANSWER),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_ANSWER from client 1 to server'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterServerTo2.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_ANSWER),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_ANSWER from client 2 to server'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterClient1.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_ANSWER),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_ANSWER from server to client 1'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterClient2.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_ANSWER),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_ANSWER from server to client 2'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
transporterServerTo1.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_REQUEST),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_REQUEST from client 1 to server'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterServerTo2.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_REQUEST),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_REQUEST from client 2 to server'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterClient1.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_REQUEST),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_REQUEST from server to client 1'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterClient2.on(
|
||||||
|
String(MessageType.COMPLETE_DOCUMENT_STATE_REQUEST),
|
||||||
|
() =>
|
||||||
|
console.debug(
|
||||||
|
'Received COMPLETE_DOCUMENT_STATE_REQUEST from server to client 2'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
transporterClient1.on('ready', () => console.debug('Client 1 is ready'))
|
||||||
|
transporterClient2.on('ready', () => console.debug('Client 2 is ready'))
|
||||||
|
|
||||||
|
docServer.on('update', (update: Uint8Array, origin: unknown) => {
|
||||||
|
const message = encodeDocumentUpdateMessage(update)
|
||||||
|
if (origin !== transporterServerTo1) {
|
||||||
|
console.debug('YDoc on Server updated. Sending to Client 1')
|
||||||
|
transporterServerTo1.send(message)
|
||||||
|
}
|
||||||
|
if (origin !== transporterServerTo2) {
|
||||||
|
console.debug('YDoc on Server updated. Sending to Client 2')
|
||||||
|
transporterServerTo2.send(message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
docClient1.on('update', (update: Uint8Array, origin: unknown) => {
|
||||||
|
if (origin !== transporterClient1) {
|
||||||
|
console.debug('YDoc on client 1 updated. Sending to Server')
|
||||||
|
transporterClient1.send(encodeDocumentUpdateMessage(update))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
docClient2.on('update', (update: Uint8Array, origin: unknown) => {
|
||||||
|
if (origin !== transporterClient2) {
|
||||||
|
console.debug('YDoc on client 2 updated. Sending to Server')
|
||||||
|
transporterClient2.send(encodeDocumentUpdateMessage(update))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
transporterClient1.connect(transporterServerTo1)
|
||||||
|
transporterClient2.connect(transporterServerTo2)
|
||||||
|
|
||||||
|
textClient1.insert(0, 'test2')
|
||||||
|
textClient1.insert(0, 'test3')
|
||||||
|
textClient2.insert(0, 'test4')
|
||||||
|
|
||||||
|
expect(textServer.toString()).toBe('test4test3test2This is a test note')
|
||||||
|
expect(textClient1.toString()).toBe('test4test3test2This is a test note')
|
||||||
|
expect(textClient2.toString()).toBe('test4test3test2This is a test note')
|
||||||
|
|
||||||
|
dummyAwareness.destroy()
|
||||||
|
docServer.destroy()
|
||||||
|
docClient1.destroy()
|
||||||
|
docClient2.destroy()
|
||||||
|
}))
|
112
commons/src/y-doc-message-transporter.ts
Normal file
112
commons/src/y-doc-message-transporter.ts
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
applyAwarenessUpdateMessage,
|
||||||
|
encodeAwarenessUpdateMessage
|
||||||
|
} from './messages/awareness-update-message.js'
|
||||||
|
import { encodeCompleteDocumentStateAnswerMessage } from './messages/complete-document-state-answer-message.js'
|
||||||
|
import { encodeCompleteDocumentStateRequestMessage } from './messages/complete-document-state-request-message.js'
|
||||||
|
import { applyDocumentUpdateMessage } from './messages/document-update-message.js'
|
||||||
|
import { MessageType } from './messages/message-type.enum.js'
|
||||||
|
import { encodeReadyAnswerMessage } from './messages/ready-answer-message.js'
|
||||||
|
import { encodeReadyRequestMessage } from './messages/ready-request-message.js'
|
||||||
|
import { EventEmitter2 } from 'eventemitter2'
|
||||||
|
import { Decoder, readVarUint } from 'lib0/decoding'
|
||||||
|
import { Awareness } from 'y-protocols/awareness'
|
||||||
|
import { Doc } from 'yjs'
|
||||||
|
|
||||||
|
export type Handler = (decoder: Decoder) => void
|
||||||
|
|
||||||
|
export type MessageTransporterEvents = {
|
||||||
|
disconnected: () => void
|
||||||
|
connected: () => void
|
||||||
|
ready: () => void
|
||||||
|
synced: () => void
|
||||||
|
} & Partial<Record<MessageType, Handler>>
|
||||||
|
|
||||||
|
export abstract class YDocMessageTransporter extends EventEmitter2 {
|
||||||
|
private synced = false
|
||||||
|
|
||||||
|
protected constructor(
|
||||||
|
protected readonly doc: Doc,
|
||||||
|
protected readonly awareness: Awareness
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
this.on(String(MessageType.READY_REQUEST), () => {
|
||||||
|
this.send(encodeReadyAnswerMessage())
|
||||||
|
})
|
||||||
|
this.on(String(MessageType.READY_ANSWER), () => {
|
||||||
|
this.emit('ready')
|
||||||
|
})
|
||||||
|
this.bindDocumentSyncMessageEvents(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSynced(): boolean {
|
||||||
|
return this.synced
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onOpen(): void {
|
||||||
|
this.emit('connected')
|
||||||
|
this.send(encodeReadyRequestMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onClose(): void {
|
||||||
|
this.emit('disconnected')
|
||||||
|
}
|
||||||
|
|
||||||
|
protected markAsSynced(): void {
|
||||||
|
if (!this.synced) {
|
||||||
|
this.synced = true
|
||||||
|
this.emit('synced')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected decodeMessage(buffer: ArrayBuffer): void {
|
||||||
|
const data = new Uint8Array(buffer)
|
||||||
|
const decoder = new Decoder(data)
|
||||||
|
const messageType = readVarUint(decoder) as MessageType
|
||||||
|
|
||||||
|
switch (messageType) {
|
||||||
|
case MessageType.COMPLETE_DOCUMENT_STATE_REQUEST:
|
||||||
|
this.send(encodeCompleteDocumentStateAnswerMessage(this.doc, decoder))
|
||||||
|
break
|
||||||
|
case MessageType.DOCUMENT_UPDATE:
|
||||||
|
applyDocumentUpdateMessage(decoder, this.doc, this)
|
||||||
|
break
|
||||||
|
case MessageType.COMPLETE_DOCUMENT_STATE_ANSWER:
|
||||||
|
applyDocumentUpdateMessage(decoder, this.doc, this)
|
||||||
|
this.markAsSynced()
|
||||||
|
break
|
||||||
|
case MessageType.COMPLETE_AWARENESS_STATE_REQUEST:
|
||||||
|
this.send(
|
||||||
|
encodeAwarenessUpdateMessage(this.awareness, [
|
||||||
|
...this.awareness.getStates().keys()
|
||||||
|
])
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case MessageType.AWARENESS_UPDATE:
|
||||||
|
applyAwarenessUpdateMessage(decoder, this.awareness, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit(String(messageType), decoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindDocumentSyncMessageEvents(doc: Doc) {
|
||||||
|
this.on('ready', () => {
|
||||||
|
this.send(encodeCompleteDocumentStateRequestMessage(doc))
|
||||||
|
})
|
||||||
|
this.on('disconnected', () => (this.synced = false))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends binary data to the client. Closes the connection on errors.
|
||||||
|
*
|
||||||
|
* @param content The binary data to send.
|
||||||
|
*/
|
||||||
|
public abstract send(content: Uint8Array): void
|
||||||
|
|
||||||
|
public abstract disconnect(): void
|
||||||
|
}
|
4
commons/tsconfig-eslint.json
Normal file
4
commons/tsconfig-eslint.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["./dist"]
|
||||||
|
}
|
3
commons/tsconfig-eslint.json.license
Normal file
3
commons/tsconfig-eslint.json.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
24
commons/tsconfig.json
Normal file
24
commons/tsconfig.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"removeComments": true,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"lib": [
|
||||||
|
"es2020",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
|
"declaration": true,
|
||||||
|
"strict": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "dist/",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"allowJs": true,
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"include": ["./src"],
|
||||||
|
"exclude": ["./dist", "**/*.test.*", "**/__mocks__/*", "**/__tests__/*"]
|
||||||
|
}
|
3
commons/tsconfig.json.license
Normal file
3
commons/tsconfig.json.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
13
package.json
13
package.json
|
@ -4,14 +4,21 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"backend",
|
"backend",
|
||||||
"frontend"
|
"frontend",
|
||||||
|
"commons"
|
||||||
],
|
],
|
||||||
"packageManager": "yarn@3.3.0",
|
"packageManager": "yarn@3.3.0",
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"domhandler": "5.0.3",
|
"domhandler": "5.0.3",
|
||||||
"yjs": "13.5.42",
|
|
||||||
"@codemirror/state": "6.1.4",
|
"@codemirror/state": "6.1.4",
|
||||||
"@codemirror/view": "6.7.0",
|
"@codemirror/view": "6.7.0",
|
||||||
"@codemirror/language": "6.3.1"
|
"@codemirror/language": "6.3.1",
|
||||||
|
"@types/react": "18.0.26",
|
||||||
|
"y-protocols@^1.0.0": "patch:y-protocols@npm%3A1.0.5#./.yarn/patches/y-protocols-npm-1.0.5-af6f64b4df.patch",
|
||||||
|
"y-protocols@1.0.5": "patch:y-protocols@npm%3A1.0.5#./.yarn/patches/y-protocols-npm-1.0.5-af6f64b4df.patch",
|
||||||
|
"lib0@^0.2.51": "patch:lib0@npm%3A0.2.58#./.yarn/patches/lib0-npm-0.2.58-7de44657f5.patch",
|
||||||
|
"lib0@^0.2.49": "patch:lib0@npm%3A0.2.58#./.yarn/patches/lib0-npm-0.2.58-7de44657f5.patch",
|
||||||
|
"lib0@^0.2.42": "patch:lib0@npm%3A0.2.58#./.yarn/patches/lib0-npm-0.2.58-7de44657f5.patch",
|
||||||
|
"lib0@0.2.58": "patch:lib0@npm%3A0.2.58#./.yarn/patches/lib0-npm-0.2.58-7de44657f5.patch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue