Add `common` subproject containing shared files
Move classes in `model` to their own `common` package.
Now, the client and server can import `common` as a library.
This is the first step to eventually migrate all the source code at the root of the
project to an `old-server` package.
GitLab: #55
Change-Id: I4b7a52e80171d9c3399416ab524bcdd6915ac540
diff --git a/common/package-lock.json b/common/package-lock.json
new file mode 100644
index 0000000..e6d0fce
--- /dev/null
+++ b/common/package-lock.json
@@ -0,0 +1,607 @@
+{
+ "name": "jami-web-common",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "jami-web-common",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "typescript": "^4.8.4"
+ },
+ "devDependencies": {
+ "@types/express-session": "^1.17.5",
+ "@types/socket.io": "^3.0.2"
+ }
+ },
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
+ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
+ "dev": true
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dev": true,
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
+ "dev": true
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.12",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
+ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
+ "dev": true
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.14",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
+ "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
+ "dev": true,
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.31",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
+ "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "node_modules/@types/express-session": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz",
+ "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==",
+ "dev": true,
+ "dependencies": {
+ "@types/express": "*"
+ }
+ },
+ "node_modules/@types/mime": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
+ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "18.8.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
+ "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==",
+ "dev": true
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "dev": true,
+ "dependencies": {
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/socket.io": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz",
+ "integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==",
+ "deprecated": "This is a stub types definition. socket.io provides its own type definitions, so you do not need this installed.",
+ "dev": true,
+ "dependencies": {
+ "socket.io": "*"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true,
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz",
+ "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==",
+ "dev": true,
+ "dependencies": {
+ "@types/cookie": "^0.4.1",
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.0.3",
+ "ws": "~8.2.3"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
+ "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/socket.io": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz",
+ "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "debug": "~4.3.2",
+ "engine.io": "~6.2.0",
+ "socket.io-adapter": "~2.4.0",
+ "socket.io-parser": "~4.2.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-adapter": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
+ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
+ "dev": true
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
+ "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
+ "dev": true,
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+ "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "@socket.io/component-emitter": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
+ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
+ "dev": true
+ },
+ "@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dev": true,
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
+ "dev": true
+ },
+ "@types/cors": {
+ "version": "2.8.12",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
+ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
+ "dev": true
+ },
+ "@types/express": {
+ "version": "4.17.14",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
+ "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
+ "dev": true,
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.31",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
+ "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/express-session": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz",
+ "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
+ "@types/mime": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
+ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "18.8.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
+ "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==",
+ "dev": true
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "@types/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "dev": true,
+ "requires": {
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/socket.io": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz",
+ "integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==",
+ "dev": true,
+ "requires": {
+ "socket.io": "*"
+ }
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true
+ },
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "engine.io": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz",
+ "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==",
+ "dev": true,
+ "requires": {
+ "@types/cookie": "^0.4.1",
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.0.3",
+ "ws": "~8.2.3"
+ }
+ },
+ "engine.io-parser": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
+ "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true
+ },
+ "socket.io": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz",
+ "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "debug": "~4.3.2",
+ "engine.io": "~6.2.0",
+ "socket.io-adapter": "~2.4.0",
+ "socket.io-parser": "~4.2.0"
+ }
+ },
+ "socket.io-adapter": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
+ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
+ "dev": true
+ },
+ "socket.io-parser": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
+ "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
+ "dev": true,
+ "requires": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ }
+ },
+ "typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ=="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true
+ },
+ "ws": {
+ "version": "8.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
+ "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+}
diff --git a/common/package.json b/common/package.json
new file mode 100644
index 0000000..78e2bb3
--- /dev/null
+++ b/common/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "jami-web-common",
+ "version": "1.0.0",
+ "license": "ISC",
+ "type": "module",
+ "main": "dist/index.js",
+ "module": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "files": [
+ "dist/index.js"
+ ],
+ "scripts": {
+ "build": "tsc --build",
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
+ "lint": "eslint src",
+ "lint:fix": "npm run lint -- --fix",
+ "format": "prettier --write src",
+ "format:check": "prettier --check src"
+ },
+ "dependencies": {
+ "@types/express-session": "^1.17.5",
+ "@types/socket.io": "^3.0.2",
+ "typescript": "^4.8.4"
+ }
+}
diff --git a/common/src/Account.ts b/common/src/Account.ts
new file mode 100644
index 0000000..70acb52
--- /dev/null
+++ b/common/src/Account.ts
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { AccountDetails, VolatileDetails } from './AccountDetails.js';
+import { Contact } from './Contact.js';
+import { Conversation } from './Conversation.js';
+import { Lookup, PromiseExecutor } from './util.js';
+
+type Devices = Record<string, string>;
+
+export type RegistrationState =
+ | 'UNREGISTERED'
+ | 'TRYING'
+ | 'REGISTERED'
+ | 'ERROR_GENERIC'
+ | 'ERROR_AUTH'
+ | 'ERROR_NETWORK'
+ | 'ERROR_HOST'
+ | 'ERROR_SERVICE_UNAVAILABLE'
+ | 'ERROR_NEED_MIGRATION'
+ | 'INITIALIZING';
+
+interface AccountRegisteringName extends PromiseExecutor<number> {
+ name: string;
+}
+
+export class Account {
+ private readonly id: string;
+ private _details: AccountDetails;
+ private _volatileDetails: VolatileDetails;
+ private readonly contactCache: Record<string, Contact>;
+ private _contacts: Contact[];
+ private readonly conversations: Record<string, Conversation>;
+ private defaultModerators: Contact[];
+ private _lookups: Lookup[];
+ private devices: Devices;
+ private _registrationState: RegistrationState | undefined;
+ private _registeringName: AccountRegisteringName | undefined;
+
+ static TYPE_JAMI: string;
+ static TYPE_SIP: string;
+ static BOOL_TRUE: string;
+ static BOOL_FALSE: string;
+
+ constructor(id: string, details: AccountDetails, volatileDetails: VolatileDetails) {
+ this.id = id;
+ this._details = details || {};
+ this._volatileDetails = volatileDetails || {};
+ this.contactCache = {};
+ this._contacts = [];
+ this.conversations = {};
+ this.defaultModerators = [];
+ this._lookups = [];
+ this.devices = {};
+ this.registrationState = undefined;
+ this._registeringName = undefined;
+ }
+
+ static from(object: any) {
+ const account = new Account(object.id, object.details, object.volatileDetails);
+ if (object.defaultModerators) account.defaultModerators = object.defaultModerators.map((m: any) => Contact.from(m));
+ return account;
+ }
+
+ update(data: Account) {
+ this._details = data._details;
+ this._volatileDetails = data._volatileDetails;
+ }
+
+ async getObject() {
+ const hasModerators = this.defaultModerators && this.defaultModerators.length;
+ return {
+ id: this.id,
+ details: this._details,
+ defaultModerators: hasModerators
+ ? await Promise.all(this.defaultModerators.map(async (c) => await c.getObject()))
+ : undefined,
+ volatileDetails: this._volatileDetails,
+ };
+ }
+
+ getId() {
+ return this.id;
+ }
+
+ getType() {
+ return this._details['Account.type'];
+ }
+
+ getUri() {
+ return this._details['Account.username'];
+ }
+
+ getRegisteredName() {
+ return this._volatileDetails['Account.registeredName'];
+ }
+
+ isRendezVous() {
+ return this._details['Account.rendezVous'] === Account.BOOL_TRUE;
+ }
+
+ isPublicIn() {
+ return this._details['DHT.PublicInCalls'] === Account.BOOL_TRUE;
+ }
+
+ setDetail(detail: keyof AccountDetails, value: string) {
+ this._details[detail] = value;
+ }
+
+ updateDetails(details: Partial<AccountDetails>) {
+ return Object.assign(this._details, details);
+ }
+
+ getDetails() {
+ return this._details;
+ }
+
+ getSummary() {
+ return this.getObject();
+ }
+
+ getDisplayName() {
+ return this._details['Account.displayName'] || this.getDisplayUri();
+ }
+
+ getDisplayUri() {
+ return this.getRegisteredName() || this.getUri();
+ }
+
+ getDisplayNameNoFallback() {
+ return this._details['Account.displayName'] || this.getRegisteredName();
+ }
+
+ getConversationIds() {
+ return Object.keys(this.conversations);
+ }
+
+ getConversations() {
+ return this.conversations;
+ }
+
+ getConversation(conversationId: string) {
+ return this.conversations[conversationId];
+ }
+
+ addConversation(conversation: Conversation) {
+ const conversationId = conversation.getId();
+ if (conversationId != null) {
+ this.conversations[conversationId] = conversation;
+ } else {
+ throw new Error('Conversation ID cannot be undefined');
+ }
+ }
+
+ removeConversation(conversationId: string) {
+ delete this.conversations[conversationId];
+ }
+
+ getContactFromCache(uri: string) {
+ let contact = this.contactCache[uri];
+ if (!contact) {
+ contact = new Contact(uri);
+ this.contactCache[uri] = contact;
+ }
+ return contact;
+ }
+
+ getContacts() {
+ return this._contacts;
+ }
+
+ set contacts(contacts: Contact[]) {
+ this._contacts = contacts;
+ }
+
+ getDefaultModerators() {
+ return this.defaultModerators;
+ }
+
+ set details(value: AccountDetails) {
+ this._details = value;
+ }
+
+ set volatileDetails(value: VolatileDetails) {
+ this._volatileDetails = value;
+ }
+
+ get lookups(): Lookup[] {
+ return this._lookups;
+ }
+
+ set lookups(lookups: Lookup[]) {
+ this._lookups = lookups;
+ }
+
+ setDevices(devices: Devices) {
+ this.devices = { ...devices };
+ }
+
+ getDevices() {
+ return this.devices;
+ }
+
+ get registrationState(): RegistrationState | undefined {
+ return this._registrationState;
+ }
+
+ set registrationState(registrationState: RegistrationState | undefined) {
+ this._registrationState = registrationState;
+ }
+
+ get registeringName(): AccountRegisteringName | undefined {
+ return this._registeringName;
+ }
+
+ set registeringName(registeringName: AccountRegisteringName | undefined) {
+ this._registeringName = registeringName;
+ }
+}
+
+Account.TYPE_JAMI = 'RING';
+Account.TYPE_SIP = 'SIP';
+
+Account.BOOL_TRUE = 'true';
+Account.BOOL_FALSE = 'false';
diff --git a/common/src/AccountDetails.ts b/common/src/AccountDetails.ts
new file mode 100644
index 0000000..69968c0
--- /dev/null
+++ b/common/src/AccountDetails.ts
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Account parameters
+ *
+ * See `jami-daemon/src/account_schema.h`
+ */
+export interface AccountDetails {
+ // Common account parameters
+ 'Account.type': string;
+ 'Account.alias': string;
+ 'Account.displayName': string;
+ 'Account.mailbox': string;
+ 'Account.enable': string;
+ 'Account.autoAnswer': string;
+ 'Account.sendReadReceipt': string;
+ 'Account.rendezVous': string;
+ 'Account.registrationExpire': string;
+ 'Account.dtmfType': string;
+ 'Account.ringtonePath': string;
+ 'Account.ringtoneEnabled': string;
+ 'Account.videoEnabled': string;
+ 'Account.keepAliveEnabled': string;
+ 'Account.presenceEnabled': string;
+ 'Account.presencePublishSupported': string;
+ 'Account.presenceSubscribeSupported': string;
+ 'Account.presenceStatus': string;
+ 'Account.presenceNote': string;
+
+ 'Account.hostname': string;
+ 'Account.username': string;
+ 'Account.routeset': string;
+ 'Account.allowIPAutoRewrite': string;
+ 'Account.password': string;
+ 'Account.realm': string;
+ 'Account.useragent': string;
+ 'Account.hasCustomUserAgent': string;
+ 'Account.audioPortMin': string;
+ 'Account.audioPortMax': string;
+ 'Account.videoPortMin': string;
+ 'Account.videoPortMax': string;
+
+ 'Account.bindAddress': string;
+ 'Account.localInterface': string;
+ 'Account.publishedSameAsLocal': string;
+ 'Account.localPort': string;
+ 'Account.publishedPort': string;
+ 'Account.publishedAddress': string;
+ 'Account.upnpEnabled': string;
+ 'Account.defaultModerators': string;
+ 'Account.localModeratorsEnabled': string;
+ 'Account.allModeratorEnabled': string;
+
+ // SIP specific parameters
+ 'STUN.server': string;
+ 'STUN.enable': string;
+ 'TURN.server': string;
+ 'TURN.enable': string;
+ 'TURN.username': string;
+ 'TURN.password': string;
+ 'TURN.realm': string;
+
+ // SRTP specific parameters
+ 'SRTP.enable': string;
+ 'SRTP.keyExchange': string;
+ 'SRTP.rtpFallback': string;
+
+ 'TLS.listenerPort': string;
+ 'TLS.enable': string;
+ 'TLS.certificateListFile': string;
+ 'TLS.certificateFile': string;
+ 'TLS.privateKeyFile': string;
+ 'TLS.password': string;
+ 'TLS.method': string;
+ 'TLS.ciphers': string;
+ 'TLS.serverName': string;
+ 'TLS.verifyServer': string;
+ 'TLS.verifyClient': string;
+ 'TLS.requireClientCertificate': string;
+ 'TLS.negotiationTimeoutSec': string;
+
+ // DHT specific parameters
+ 'DHT.port': string;
+ 'DHT.PublicInCalls': string;
+
+ // Volatile parameters
+ 'Account.registrationStatus': string;
+ 'Account.registrationCode': string;
+ 'Account.registrationDescription': string;
+ 'Transport.statusCode': string;
+ 'Transport.statusDescription': string;
+}
+
+/**
+ * Volatile properties
+ *
+ * See `jami-daemon/src/jami/account_const.h`
+ */
+export interface VolatileDetails {
+ 'Account.active': string;
+ 'Account.deviceAnnounced': string;
+ 'Account.registeredName': string;
+}
+
+/**
+ * See `ConfProperties` in `jami-daemon/src/jami/account_const.h
+ */
+export interface AccountConfig {
+ id?: string;
+ type?: string;
+ alias?: string;
+ displayName?: string;
+ enable?: boolean;
+ mailbox?: string;
+ dtmfType?: string;
+ autoAnswer?: boolean;
+ sendReadReceipt?: string;
+ rendezVous?: boolean;
+ activeCallLimit?: string;
+ hostname?: string;
+ username?: string;
+ bindAddress?: string;
+ routeset?: string;
+ password?: string;
+ realm?: string;
+ localInterface?: string;
+ publishedSameAsLocal?: boolean;
+ localPort?: string;
+ publishedPort?: string;
+ publishedAddress?: string;
+ useragent?: string;
+ upnpEnabled?: boolean;
+ hasCustomUserAgent?: string;
+ allowCertFromHistory?: string;
+ allowCertFromContact?: string;
+ allowCertFromTrusted?: string;
+ archivePassword?: string;
+ archiveHasPassword?: string;
+ archivePath?: string;
+ archivePIN?: string;
+ deviceID?: string;
+ deviceName?: string;
+ proxyEnabled?: boolean;
+ proxyServer?: string;
+ proxyPushToken?: string;
+ keepAliveEnabled?: boolean;
+ peerDiscovery?: string;
+ accountDiscovery?: string;
+ accountPublish?: string;
+ managerUri?: string;
+ managerUsername?: string;
+ bootstrapListUrl?: string;
+ dhtProxyListUrl?: string;
+ defaultModerators?: string;
+ localModeratorsEnabled?: boolean;
+ allModeratorsEnabled?: boolean;
+ allowIPAutoRewrite?: string;
+
+ // Audio
+ audioPortMax?: string;
+ audioPortMin?: string;
+
+ // Video
+ videoEnabled?: boolean;
+ videoPortMax?: boolean;
+ videoPortMin?: string;
+
+ // Ringtone
+ ringtonePath?: string;
+ ringtoneEnabled?: boolean;
+}
diff --git a/common/src/Contact.ts b/common/src/Contact.ts
new file mode 100644
index 0000000..88e1916
--- /dev/null
+++ b/common/src/Contact.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+export class Contact {
+ private readonly uri: string;
+ private readonly displayName: string | undefined;
+ private registeredName: string | undefined;
+
+ constructor(uri: string) {
+ this.uri = uri;
+ this.displayName = undefined;
+ this.registeredName = undefined;
+ }
+
+ static from(object: any) {
+ const contact = new Contact(object.uri);
+ if (object.registeredName) contact.setRegisteredName(object.registeredName);
+ return contact;
+ }
+
+ getUri() {
+ return this.uri;
+ }
+
+ getRegisteredName() {
+ return this.registeredName;
+ }
+
+ setRegisteredName(name: string | undefined) {
+ this.registeredName = name;
+ }
+
+ isRegisteredNameResolved() {
+ return this.registeredName !== undefined;
+ }
+
+ getDisplayName() {
+ return this.getDisplayNameNoFallback() || this.getUri();
+ }
+
+ getDisplayNameNoFallback() {
+ return this.displayName || this.getRegisteredName();
+ }
+
+ async getObject() {
+ return {
+ uri: this.uri,
+ registeredName: await this.registeredName,
+ };
+ }
+}
diff --git a/common/src/Conversation.ts b/common/src/Conversation.ts
new file mode 100644
index 0000000..01b70cd
--- /dev/null
+++ b/common/src/Conversation.ts
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { Socket } from 'socket.io';
+
+import { Contact } from './Contact.js';
+import { PromiseExecutor, Session } from './util.js';
+
+export interface ConversationMember {
+ contact: Contact;
+ role?: 'admin' | 'member' | 'invited' | 'banned' | 'left';
+}
+
+type ConversationInfos = Record<string, unknown>;
+
+export type Message = Record<string, string>;
+type ConversationRequest = PromiseExecutor<Message[]>;
+
+type ConversationListeners = Record<
+ string,
+ {
+ socket: Socket;
+ session: Session;
+ }
+>;
+
+export class Conversation {
+ private readonly id: string | undefined;
+ private readonly accountId: string;
+ private readonly members: ConversationMember[];
+ private messages: Message[];
+ private _infos: ConversationInfos;
+ private _requests: Record<string, ConversationRequest>;
+ private _listeners: ConversationListeners;
+
+ constructor(id: string | undefined, accountId: string, members?: ConversationMember[]) {
+ this.id = id;
+ this.accountId = accountId;
+ this.members = members || [];
+
+ this.messages = [];
+ this._infos = {};
+ this._requests = {};
+ this._listeners = {};
+ }
+
+ static from(accountId: string, object: any) {
+ const conversation = new Conversation(
+ object.id,
+ accountId,
+ object.members.map((member: any) => {
+ member.contact = Contact.from(member.contact);
+ return member;
+ })
+ );
+ conversation.messages = object.messages;
+ return conversation;
+ }
+ static fromSingleContact(accountId: string, contact: Contact) {
+ return new Conversation(undefined, accountId, [{ contact }]);
+ }
+
+ getId() {
+ return this.id;
+ }
+
+ getAccountId() {
+ return this.accountId;
+ }
+
+ getDisplayName() {
+ if (this.members.length !== 0) {
+ return this.members[0].contact.getDisplayName();
+ }
+ return this.getDisplayUri();
+ }
+
+ getDisplayNameNoFallback() {
+ if (this.members.length !== 0) {
+ return this.members[0].contact.getDisplayNameNoFallback();
+ }
+ }
+
+ async getObject(params?: {
+ memberFilter: (value: ConversationMember, index: number, array: ConversationMember[]) => boolean;
+ }) {
+ const members = params?.memberFilter ? this.members.filter(params.memberFilter) : this.members;
+ return {
+ id: this.id,
+ messages: this.messages,
+ members: await Promise.all(
+ members.map(async (member) => {
+ //Object.assign({}, member);
+ return {
+ role: member.role,
+ contact: await member.contact.getObject(),
+ };
+ })
+ ),
+ };
+ }
+
+ getSummary() {
+ return this.getObject();
+ }
+
+ getDisplayUri() {
+ return this.getId() || this.getFirstMember().contact.getUri();
+ }
+
+ getFirstMember() {
+ return this.members[0];
+ }
+
+ getMembers() {
+ return this.members;
+ }
+
+ addMessage(message: Message) {
+ if (this.messages.length === 0) this.messages.push(message);
+ else if (message.id === this.messages[this.messages.length - 1].linearizedParent) {
+ this.messages.push(message);
+ } else if (message.linearizedParent === this.messages[0].id) {
+ this.messages.unshift(message);
+ } else {
+ console.log("Can't insert message " + message.id);
+ }
+ }
+
+ addLoadedMessages(messages: Message[]) {
+ messages.forEach((message) => this.addMessage(message));
+ }
+
+ getMessages() {
+ return this.messages;
+ }
+
+ set infos(infos: ConversationInfos) {
+ this._infos = infos;
+ }
+
+ get requests(): Record<string, ConversationRequest> {
+ return this._requests;
+ }
+
+ set requests(value: Record<string, ConversationRequest>) {
+ this._requests = value;
+ }
+
+ get listeners(): ConversationListeners {
+ return this._listeners;
+ }
+
+ set listeners(listeners: ConversationListeners) {
+ this._listeners = listeners;
+ }
+}
diff --git a/common/src/index.ts b/common/src/index.ts
new file mode 100644
index 0000000..e58fd49
--- /dev/null
+++ b/common/src/index.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+export * from './Account.js';
+export * from './AccountDetails.js';
+export * from './Contact.js';
+export * from './Conversation.js';
+export * from './util.js';
diff --git a/common/src/util.ts b/common/src/util.ts
new file mode 100644
index 0000000..c814ba1
--- /dev/null
+++ b/common/src/util.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { Session as ISession } from 'express-session';
+
+export interface PromiseExecutor<T> {
+ resolve: (value: T) => void;
+ reject: (reason?: any) => void;
+}
+
+export interface LookupResolveValue {
+ address: string;
+ name: string;
+ state: number;
+}
+
+export interface Lookup extends PromiseExecutor<LookupResolveValue> {
+ name?: string;
+ address?: string;
+}
+
+export interface Session extends ISession {
+ socketId: string;
+ conversation: any;
+}
diff --git a/common/tsconfig.json b/common/tsconfig.json
new file mode 100644
index 0000000..b03c254
--- /dev/null
+++ b/common/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "declaration": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src"]
+}