diff --git a/README.md b/README.md index 912ac25eb..95e37f480 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,8 @@ typecell │ ├── server - HocusPocus + Supabase server for storing documents │ ├── shared - TypeCell specific models shared across the codebase │ ├── shared-test - Helper functions shared across the codebase for unit tests -│ └── util - Generic helper functions +│ ├── util - Generic helper functions +│ └── y-penpal - yjs transport for crossdomain / crossframe communication ├── patches - patch-package patches └── test-util - Data for unit tests ``` diff --git a/package-lock.json b/package-lock.json index d8845741c..e48cac188 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "hasInstallScript": true, "license": "AGPL-3.0", "workspaces": [ + "packages/y-penpal", "packages/util", "packages/shared", "packages/shared-test", @@ -4513,6 +4514,10 @@ "resolved": "packages/util", "link": true }, + "node_modules/@typecell-org/y-penpal": { + "resolved": "packages/y-penpal", + "link": true + }, "node_modules/@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -5313,52 +5318,6 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, - "node_modules/abstract-leveldown": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", - "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", - "optional": true, - "dependencies": { - "buffer": "^5.5.0", - "immediate": "^3.2.3", - "level-concat-iterator": "~2.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/abstract-leveldown/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/abstract-leveldown/node_modules/immediate": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", - "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", - "optional": true - }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -5683,12 +5642,6 @@ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "optional": true - }, "node_modules/async-lock": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.0.tgz", @@ -6751,19 +6704,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deferred-leveldown": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", - "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", - "optional": true, - "dependencies": { - "abstract-leveldown": "~6.2.1", - "inherits": "^2.0.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -6896,33 +6836,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/encoding-down": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", - "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", - "optional": true, - "dependencies": { - "abstract-leveldown": "^6.2.1", - "inherits": "^2.0.3", - "level-codec": "^9.0.0", - "level-errors": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "optional": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -8790,7 +8703,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "devOptional": true + "dev": true }, "node_modules/internal-slot": { "version": "1.0.5", @@ -9752,198 +9665,6 @@ "language-subtag-registry": "~0.3.2" } }, - "node_modules/level": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz", - "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==", - "optional": true, - "dependencies": { - "level-js": "^5.0.0", - "level-packager": "^5.1.0", - "leveldown": "^5.4.0" - }, - "engines": { - "node": ">=8.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/level" - } - }, - "node_modules/level-codec": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", - "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", - "optional": true, - "dependencies": { - "buffer": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-codec/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/level-concat-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", - "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", - "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", - "optional": true, - "dependencies": { - "errno": "~0.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-iterator-stream": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", - "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", - "optional": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.4.0", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-js": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz", - "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==", - "optional": true, - "dependencies": { - "abstract-leveldown": "~6.2.3", - "buffer": "^5.5.0", - "inherits": "^2.0.3", - "ltgt": "^2.1.2" - } - }, - "node_modules/level-js/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/level-packager": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz", - "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==", - "optional": true, - "dependencies": { - "encoding-down": "^6.3.0", - "levelup": "^4.3.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/level-supports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", - "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", - "optional": true, - "dependencies": { - "xtend": "^4.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/leveldown": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", - "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "abstract-leveldown": "~6.2.1", - "napi-macros": "~2.0.0", - "node-gyp-build": "~4.1.0" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/leveldown/node_modules/node-gyp-build": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", - "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==", - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/levelup": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", - "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", - "optional": true, - "dependencies": { - "deferred-leveldown": "~5.3.0", - "level-errors": "~2.0.0", - "level-iterator-stream": "~4.0.0", - "level-supports": "~1.0.0", - "xtend": "~4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10125,12 +9846,6 @@ "yallist": "^3.0.2" } }, - "node_modules/ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", - "optional": true - }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -11497,12 +11212,6 @@ "node": "^14 || ^16 || >=18" } }, - "node_modules/napi-macros": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", - "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", - "optional": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12856,12 +12565,6 @@ "prosemirror-transform": "^1.1.0" } }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "optional": true - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -13294,7 +12997,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "devOptional": true, + "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -13764,7 +13467,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -14016,7 +13719,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, + "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -15074,7 +14777,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "devOptional": true + "dev": true }, "node_modules/uuid": { "version": "8.3.2", @@ -15694,15 +15397,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "optional": true, - "engines": { - "node": ">=0.4" - } - }, "node_modules/y-indexeddb": { "version": "9.0.6", "resolved": "https://registry.npmjs.org/y-indexeddb/-/y-indexeddb-9.0.6.tgz", @@ -15718,23 +15412,6 @@ "yjs": "^13.0.0" } }, - "node_modules/y-leveldb": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/y-leveldb/-/y-leveldb-0.1.2.tgz", - "integrity": "sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==", - "optional": true, - "dependencies": { - "level": "^6.0.1", - "lib0": "^0.2.31" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - }, - "peerDependencies": { - "yjs": "^13.0.0" - } - }, "node_modules/y-prosemirror": { "version": "1.0.20", "resolved": "https://registry.npmjs.org/y-prosemirror/-/y-prosemirror-1.0.20.tgz", @@ -15766,40 +15443,6 @@ "url": "https://github.com/sponsors/dmonad" } }, - "node_modules/y-websocket": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-1.5.0.tgz", - "integrity": "sha512-A8AO6XtnQlYwWFytWdkDCeXg4l8ghRTIw5h2YUgUYDmEC9ugWGIwYNW80yadhSFAF7CvuWTEkQNEpevnH6EiZw==", - "dependencies": { - "lib0": "^0.2.52", - "lodash.debounce": "^4.0.8", - "y-protocols": "^1.0.5" - }, - "bin": { - "y-websocket": "bin/server.js", - "y-websocket-server": "bin/server.js" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - }, - "optionalDependencies": { - "ws": "^6.2.1", - "y-leveldb": "^0.1.0" - }, - "peerDependencies": { - "yjs": "^13.5.6" - } - }, - "node_modules/y-websocket/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "optional": true, - "dependencies": { - "async-limiter": "~1.0.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -15959,6 +15602,7 @@ "@typecell-org/parsers": "^0.0.3", "@typecell-org/shared": "^0.0.3", "@typecell-org/util": "^0.0.3", + "@typecell-org/y-penpal": "^0.0.3", "classnames": "^2.3.1", "fractional-indexing": "^2.0.0", "lodash.memoize": "^4.1.2", @@ -15979,7 +15623,6 @@ "web-vitals": "^1.0.1", "y-indexeddb": "9.0.6", "y-protocols": "^1.0.5", - "y-websocket": "^1.5.0", "yjs": "^13.6.4" }, "devDependencies": { @@ -16377,6 +16020,7 @@ "@typecell-org/engine": "^0.0.3", "@typecell-org/shared": "^0.0.3", "@typecell-org/util": "^0.0.3", + "@typecell-org/y-penpal": "^0.0.3", "localforage": "^1.10.0", "lz-string": "^1.4.4", "mobx": "^6.2.0", @@ -16395,7 +16039,6 @@ "typescript": "5.0.4", "vscode-lib": "^0.1.2", "y-protocols": "^1.0.5", - "y-websocket": "^1.5.0", "yjs": "^13.6.4" }, "devDependencies": { @@ -16899,6 +16542,18 @@ "peerDependencies": { "react": "17 || 18" } + }, + "packages/y-penpal": { + "name": "@typecell-org/y-penpal", + "version": "0.0.3", + "dependencies": { + "lib0": "^0.2.82", + "y-protocols": "^1.0.5" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "5.0.4" + } } } } diff --git a/package.json b/package.json index bd93dc075..4c9c36718 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "playwright": "^1.36.0" }, "workspaces": [ + "packages/y-penpal", "packages/util", "packages/shared", "packages/shared-test", diff --git a/packages/editor/package.json b/packages/editor/package.json index cc6246430..741cf0141 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -37,6 +37,7 @@ "@typecell-org/engine": "^0.0.3", "@typecell-org/parsers": "^0.0.3", "@typecell-org/frame": "^0.0.3", + "@typecell-org/y-penpal": "^0.0.3", "speakingurl": "^14.0.1", "classnames": "^2.3.1", "fractional-indexing": "^2.0.0", @@ -56,7 +57,6 @@ "web-vitals": "^1.0.1", "y-indexeddb": "9.0.6", "y-protocols": "^1.0.5", - "y-websocket": "^1.5.0", "yjs": "^13.6.4" }, "scripts": { diff --git a/packages/editor/src/app/documentRenderers/richtext/FrameHost.tsx b/packages/editor/src/app/documentRenderers/richtext/FrameHost.tsx index 793e68762..cb4e90da3 100644 --- a/packages/editor/src/app/documentRenderers/richtext/FrameHost.tsx +++ b/packages/editor/src/app/documentRenderers/richtext/FrameHost.tsx @@ -1,18 +1,63 @@ import { IframeBridgeMethods } from "@typecell-org/shared"; -import { ContainedElement } from "@typecell-org/util"; +import { ContainedElement, useResource } from "@typecell-org/util"; +import { PenPalProvider } from "@typecell-org/y-penpal"; import { AsyncMethodReturns, connectToChild } from "penpal"; -import { useMemo } from "react"; +import { useRef } from "react"; +import * as awarenessProtocol from "y-protocols/awareness"; import { parseIdentifier } from "../../../identifiers"; +import { DocumentResource } from "../../../store/DocumentResource"; import { DocumentResourceModelProvider } from "../../../store/DocumentResourceModelProvider"; import { SessionStore } from "../../../store/local/SessionStore"; import { ModelForwarder } from "./ModelForwarder"; -export function FrameHost(props: { url: string; sessionStore: SessionStore }) { - const frame: HTMLIFrameElement = useMemo(() => { - /** - * Penpal postmessage connection methods exposed by the iframe - */ - let connectionMethods: AsyncMethodReturns | undefined; +export function FrameHost(props: { + url: string; + sessionStore: SessionStore; + document: DocumentResource; +}) { + /** + * Penpal postmessage connection methods exposed by the iframe + */ + const connectionMethods = useRef>(); + + const frame = useResource(() => { + const provider = new PenPalProvider( + props.document.ydoc, + (buf, provider) => { + connectionMethods.current?.processYjsMessage(buf); + }, + props.document.awareness, + ); + const frameClientIds = new Set(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + props.document.awareness?.on("change", (changes: any, origin: any) => { + if (origin !== provider) { + return; + } + if (changes.added.length === 0) { + return; + } + changes.added.forEach((client: number) => { + frameClientIds.add(client); + }); + }); + provider.connect(); + + const removePresence = () => { + // remove cursor from awareness when we navigate away + console.log("remove awareness"); + if (props.document.awareness) { + console.log("remove awareness 2"); + awarenessProtocol.removeAwarenessStates( + props.document.awareness, + [...frameClientIds], + "window unload", + ); + } + }; + + window.addEventListener("beforeunload", removePresence); const moduleManagers = new Map< string, @@ -20,6 +65,9 @@ export function FrameHost(props: { url: string; sessionStore: SessionStore }) { >(); const methods = { + processYjsMessage: async (message: ArrayBuffer) => { + provider.onMessage(message, "penpal"); + }, registerTypeCellModuleCompiler: async (moduleName: string) => { if (moduleManagers.has(moduleName)) { console.warn("already has moduleManager for", moduleName); @@ -39,7 +87,7 @@ export function FrameHost(props: { url: string; sessionStore: SessionStore }) { "modules/" + moduleName, provider, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connectionMethods!, + connectionMethods.current!, ); moduleManagers.set(moduleName, { provider, forwarder }); await forwarder.initialize(); @@ -90,14 +138,27 @@ export function FrameHost(props: { url: string; sessionStore: SessionStore }) { connection.promise.then( (methods) => { console.info("connected to iframe succesfully"); - connectionMethods = methods; + connectionMethods.current = methods; + // connect }, (e) => { console.error("connection to iframe failed", e); }, ); - return iframe; - }, [props.url, props.sessionStore]); + + return [ + iframe, + () => { + connectionMethods.current = undefined; + window.removeEventListener("beforeunload", removePresence); + provider.disconnect(); + provider.destroy(); + removePresence(); + + connection.destroy(); + }, + ]; + }, [props.url, props.sessionStore, props.document]); return ( <> diff --git a/packages/editor/src/app/documentRenderers/richtext/RichTextRenderer.tsx b/packages/editor/src/app/documentRenderers/richtext/RichTextRenderer.tsx index dcabf6415..ac744d8f2 100644 --- a/packages/editor/src/app/documentRenderers/richtext/RichTextRenderer.tsx +++ b/packages/editor/src/app/documentRenderers/richtext/RichTextRenderer.tsx @@ -1,11 +1,9 @@ import { observer } from "mobx-react-lite"; -import React, { useEffect, useMemo } from "react"; +import React, { useMemo } from "react"; // import LocalExecutionHost from "../../../runtime/executor/executionHosts/local/LocalExecutionHost" import "@blocknote/core/style.css"; import { uniqueId } from "@typecell-org/util"; -import * as awarenessProtocol from "y-protocols/awareness"; -import { WebsocketProvider } from "y-websocket"; import { getFrameDomain } from "../../../config/security"; import { DocumentResource } from "../../../store/DocumentResource"; import { SessionStore } from "../../../store/local/SessionStore"; @@ -21,48 +19,6 @@ const RichTextRenderer: React.FC = observer((props) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.document.id]); - useEffect(() => { - const provider = new WebsocketProvider("", roomName, props.document.ydoc, { - connect: false, - awareness: props.document.awareness, - }); - const frameClientIds = new Set(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - props.document.awareness?.on("change", (changes: any, origin: any) => { - if (origin !== provider) { - return; - } - if (changes.added.length === 0) { - return; - } - changes.added.forEach((client: number) => { - frameClientIds.add(client); - }); - }); - provider.connectBc(); - - const removePresence = () => { - // remove cursor from awareness when we navigate away - if (props.document.awareness) { - awarenessProtocol.removeAwarenessStates( - props.document.awareness, - [...frameClientIds], - "window unload", - ); - } - }; - - window.addEventListener("beforeunload", removePresence); - - return () => { - window.removeEventListener("beforeunload", removePresence); - removePresence(); - provider.disconnectBc(); - provider.destroy(); - }; - }, [props.document, roomName]); - const params = new URLSearchParams(); params.append("documentId", props.document.id); params.append("roomName", roomName); @@ -81,7 +37,13 @@ const RichTextRenderer: React.FC = observer((props) => { "#" + params.toString(); - return ; + return ( + + ); }); export default RichTextRenderer; diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json index 1a4fb5bff..1560fa842 100644 --- a/packages/editor/tsconfig.json +++ b/packages/editor/tsconfig.json @@ -37,6 +37,9 @@ }, { "path": "../frame" + }, + { + "path": "../y-penpal" } ] } diff --git a/packages/editor/vite.config.ts b/packages/editor/vite.config.ts index a8c3da84b..374bd5932 100644 --- a/packages/editor/vite.config.ts +++ b/packages/editor/vite.config.ts @@ -69,9 +69,9 @@ export default defineConfig((conf) => ({ rollupOptions: { output: { chunkFileNames: (chunkInfo) => { - if (chunkInfo.name.includes("y-websocket")) { + if (chunkInfo.name.includes("startConnectionTimeout")) { // because rollup uses the last source of the chunk as file name, we end - // up with a file named "y-websocket.xxx.js" which actually has a lot of node_modules + // up with a file named "startConnectionTimeout.xxx.js" which actually has a lot of node_modules // (not just y-websocket). Rename to "chunk" return "assets/chunk-[hash].js"; } diff --git a/packages/frame/package.json b/packages/frame/package.json index b10ca39fc..254fb5d72 100644 --- a/packages/frame/package.json +++ b/packages/frame/package.json @@ -8,6 +8,7 @@ "@typecell-org/util": "^0.0.3", "@typecell-org/shared": "^0.0.3", "@typecell-org/engine": "^0.0.3", + "@typecell-org/y-penpal": "^0.0.3", "@tiptap/core": "^2.0.4", "@tiptap/react": "^2.0.4", "@floating-ui/react": "^0.25.1", @@ -30,7 +31,6 @@ "typescript": "5.0.4", "vscode-lib": "^0.1.2", "y-protocols": "^1.0.5", - "y-websocket": "^1.5.0", "yjs": "^13.6.4" }, "devDependencies": { diff --git a/packages/frame/src/Frame.tsx b/packages/frame/src/Frame.tsx index 6e27cc1a4..782e11bf8 100644 --- a/packages/frame/src/Frame.tsx +++ b/packages/frame/src/Frame.tsx @@ -13,16 +13,6 @@ import { getDefaultReactSlashMenuItems, useBlockNote, } from "@blocknote/react"; -import * as mobx from "mobx"; -import ReactDOM from "react-dom"; -import * as Y from "yjs"; -import { MonacoBlockContent } from "./MonacoBlockContent"; -import { RichTextContext } from "./RichTextContext"; -import SourceModelCompiler from "./runtime/compiler/SourceModelCompiler"; -import { MonacoContext } from "./runtime/editor/MonacoContext"; -import { ExecutionHost } from "./runtime/executor/executionHosts/ExecutionHost"; -import LocalExecutionHost from "./runtime/executor/executionHosts/local/LocalExecutionHost"; - import { enableMobxBindings } from "@syncedstore/yjs-reactive-bindings"; import { ReactiveEngine } from "@typecell-org/engine"; import { @@ -32,10 +22,19 @@ import { ModelReceiver, } from "@typecell-org/shared"; import { useResource } from "@typecell-org/util"; +import { PenPalProvider } from "@typecell-org/y-penpal"; +import * as mobx from "mobx"; import * as monaco from "monaco-editor"; import { AsyncMethodReturns, connectToParent } from "penpal"; -import { WebsocketProvider } from "y-websocket"; +import ReactDOM from "react-dom"; +import * as Y from "yjs"; import styles from "./Frame.module.css"; +import { MonacoBlockContent } from "./MonacoBlockContent"; +import { RichTextContext } from "./RichTextContext"; +import SourceModelCompiler from "./runtime/compiler/SourceModelCompiler"; +import { MonacoContext } from "./runtime/editor/MonacoContext"; +import { ExecutionHost } from "./runtime/executor/executionHosts/ExecutionHost"; +import LocalExecutionHost from "./runtime/executor/executionHosts/local/LocalExecutionHost"; import { setMonacoDefaults } from "./runtime/editor"; @@ -92,8 +91,46 @@ export const Frame: React.FC = observer((props) => { const modelReceivers = useMemo(() => new Map(), []); const connectionMethods = useRef>(); + const document = useResource(() => { + const ydoc = new Y.Doc(); + // ydoc.on("update", () => { + // console.log("frame ydoc", ydoc.toJSON()); + // }); + const provider = new PenPalProvider( + ydoc, + (buf, _provider) => { + connectionMethods.current?.processYjsMessage(buf); + }, + undefined, + false, + ); + + const colorManager = new MonacoColorManager( + provider.awareness, + props.userName, + props.userColor, + monacoStyles.yRemoteSelectionHead, + monacoStyles.yRemoteSelection, + ); + + return [ + { + provider, + awareness: provider.awareness, + ydoc, + }, + () => { + provider.destroy(); + colorManager.dispose(); + }, + ]; + }, []); + useEffect(() => { const methods: IframeBridgeMethods = { + processYjsMessage: async (message: ArrayBuffer) => { + document.provider.onMessage(message, "penpal"); + }, updateModels: async ( bridgeId: string, models: { @@ -140,42 +177,13 @@ export const Frame: React.FC = observer((props) => { (parent) => { console.info("connected to parent window succesfully"); connectionMethods.current = parent; + document.provider.connect(); }, (e) => { console.error("connection to parent window failed", e); }, ); - }, [modelReceivers]); - - const document = useResource(() => { - const ydoc = new Y.Doc(); - // ydoc.on("update", () => { - // console.log("frame ydoc", ydoc.toJSON()); - // }); - const provider = new WebsocketProvider("", props.roomName, ydoc, { - connect: false, - }); - const colorManager = new MonacoColorManager( - provider.awareness, - props.userName, - props.userColor, - monacoStyles.yRemoteSelectionHead, - monacoStyles.yRemoteSelection, - ); - provider.connectBc(); - - return [ - { - provider, - awareness: provider.awareness, - ydoc, - }, - () => { - provider.destroy(); - colorManager.dispose(); - }, - ]; - }, []); + }, [modelReceivers, document]); const tools = useResource( // "compilers", diff --git a/packages/frame/tsconfig.json b/packages/frame/tsconfig.json index d258239f7..0af6d565f 100644 --- a/packages/frame/tsconfig.json +++ b/packages/frame/tsconfig.json @@ -32,6 +32,9 @@ }, { "path": "../engine" + }, + { + "path": "../y-penpal" } ] } diff --git a/packages/shared/src/frameInterop/HostBridgeMethods.ts b/packages/shared/src/frameInterop/HostBridgeMethods.ts index 69dee11dc..0182c17c7 100644 --- a/packages/shared/src/frameInterop/HostBridgeMethods.ts +++ b/packages/shared/src/frameInterop/HostBridgeMethods.ts @@ -12,4 +12,9 @@ export type HostBridgeMethods = { */ registerTypeCellModuleCompiler: (moduleName: string) => Promise; unregisterTypeCellModuleCompiler: (moduleName: string) => Promise; + + /** + * Function for y-penpal + */ + processYjsMessage: (message: Uint8Array) => Promise; }; diff --git a/packages/shared/src/frameInterop/IframeBridgeMethods.ts b/packages/shared/src/frameInterop/IframeBridgeMethods.ts index 7673718cc..b67cacb1c 100644 --- a/packages/shared/src/frameInterop/IframeBridgeMethods.ts +++ b/packages/shared/src/frameInterop/IframeBridgeMethods.ts @@ -2,17 +2,22 @@ * The methods the iframe exposes that are callable by the host (from SandboxedExecutionHost) */ export type IframeBridgeMethods = { + /** + * Function for y-penpal + */ + processYjsMessage: (message: ArrayBuffer) => Promise; + /** * Functions to update javascript code models */ updateModels: ( bridgeId: string, - models: { modelId: string; model: { value: string; language: string } }[] + models: { modelId: string; model: { value: string; language: string } }[], ) => Promise; updateModel: ( bridgeId: string, modelId: string, - model: { value: string; language: string } + model: { value: string; language: string }, ) => Promise; deleteModel: (bridgeId: string, modelId: string) => Promise; diff --git a/packages/y-penpal/README.md b/packages/y-penpal/README.md new file mode 100644 index 000000000..ad27a2d23 --- /dev/null +++ b/packages/y-penpal/README.md @@ -0,0 +1,3 @@ +# packages/y-penpal + +Yjs provider based on y-websocket, but with the websocket stuff stripped out and broadcastchannel replaced with handlers that can be connected to penpa / iframe PostMessage. diff --git a/packages/y-penpal/package.json b/packages/y-penpal/package.json new file mode 100644 index 000000000..e931f1133 --- /dev/null +++ b/packages/y-penpal/package.json @@ -0,0 +1,30 @@ +{ + "name": "@typecell-org/y-penpal", + "version": "0.0.3", + "private": true, + "dependencies": { + "y-protocols": "^1.0.5", + "lib0": "^0.2.82" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "5.0.4" + }, + "peerDependencies": {}, + "source": "src/index.ts", + "types": "types/src/index.d.ts", + "main": "dist/src/index.js", + "type": "module", + "scripts": { + "clean": "rimraf dist && rimraf types", + "build": "npm run clean && tsc -p tsconfig.json", + "watch": "tsc --watch", + "lint": "eslint src", + "test": "echo \"no tests in project\" && exit 0" + }, + "eslintConfig": { + "extends": [ + "../shared/.eslintrc.cjs" + ] + } +} diff --git a/packages/y-penpal/src/index.ts b/packages/y-penpal/src/index.ts new file mode 100644 index 000000000..9844a3235 --- /dev/null +++ b/packages/y-penpal/src/index.ts @@ -0,0 +1,267 @@ +/** + * Based on y-websocket, but with the websocket stuff stripped out and + * broadcastchannel replaced with handlers that can be connected to penpal / iframe PostMessage + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +import * as decoding from "lib0/decoding"; +import * as encoding from "lib0/encoding"; +import { Observable } from "lib0/observable"; +import * as authProtocol from "y-protocols/auth"; +import * as awarenessProtocol from "y-protocols/awareness"; +import * as syncProtocol from "y-protocols/sync"; +export const messageSync = 0; +export const messageQueryAwareness = 3; +export const messageAwareness = 1; +export const messageAuth = 2; + +const messageHandlers: any = []; + +messageHandlers[messageSync] = ( + encoder: encoding.Encoder, + decoder: decoding.Decoder, + provider: PenPalProvider, + emitSynced: boolean, + _messageType: any, +) => { + encoding.writeVarUint(encoder, messageSync); + const syncMessageType = syncProtocol.readSyncMessage( + decoder, + encoder, + provider.doc, + provider, + ); + if ( + emitSynced && + syncMessageType === syncProtocol.messageYjsSyncStep2 && + !provider.synced + ) { + provider.synced = true; + } +}; + +messageHandlers[messageQueryAwareness] = ( + encoder: encoding.Encoder, + decoder: decoding.Decoder, + provider: PenPalProvider, + emitSynced: boolean, + _messageType: any, +) => { + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate( + provider.awareness, + Array.from(provider.awareness.getStates().keys()), + ), + ); +}; + +messageHandlers[messageAwareness] = ( + encoder: encoding.Encoder, + decoder: decoding.Decoder, + provider: PenPalProvider, + emitSynced: boolean, + _messageType: any, +) => { + awarenessProtocol.applyAwarenessUpdate( + provider.awareness, + decoding.readVarUint8Array(decoder), + provider, + ); +}; + +messageHandlers[messageAuth] = ( + encoder: encoding.Encoder, + decoder: decoding.Decoder, + provider: PenPalProvider, + emitSynced: boolean, + _messageType: any, +) => { + authProtocol.readAuthMessage(decoder, provider.doc, (_ydoc, reason) => + console.warn(`Permission denied to access doc.\n${reason}`), + ); +}; + +const readMessage = ( + provider: PenPalProvider, + buf: Uint8Array, + emitSynced: boolean, +) => { + const decoder = decoding.createDecoder(buf); + const encoder = encoding.createEncoder(); + const messageType = decoding.readVarUint(decoder); + const messageHandler = provider.messageHandlers[messageType]; + if (/** @type {any} */ messageHandler) { + messageHandler(encoder, decoder, provider, emitSynced, messageType); + } else { + console.error("Unable to compute message"); + } + return encoder; +}; + +/** + * @param {WebsocketProvider} provider + * @param {ArrayBuffer} buf + */ +const broadcastMessage = (provider: PenPalProvider, buf: Uint8Array) => { + if (provider.connected) { + provider.send(buf, provider); + } +}; + +export class PenPalProvider extends Observable { + public readonly messageHandlers = messageHandlers.slice(); + public connected = false; + + private _synced = false; + + constructor( + public readonly doc: any, + public readonly send: (buf: Uint8Array, provider: PenPalProvider) => void, + public readonly awareness = new awarenessProtocol.Awareness(doc), + connect = true, + ) { + super(); + + this.doc.on("update", this._updateHandler); + + if (typeof window !== "undefined") { + window.addEventListener("unload", this._unloadHandler); + } else if (typeof process !== "undefined") { + process.on("exit", this._unloadHandler); + } + awareness.on("update", this._awarenessUpdateHandler); + + if (connect) { + this.connect(); + } + } + + private _bcSubscriber = (data: ArrayBuffer, origin: any) => { + if (origin !== this) { + const encoder = readMessage(this, new Uint8Array(data), false); + if (encoding.length(encoder) > 1) { + this.send(encoding.toUint8Array(encoder), this); + } + } + }; + + /** + * Listens to Yjs updates and sends them to peers + */ + private _updateHandler = (update: Uint8Array, origin: any) => { + if (origin !== this) { + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageSync); + syncProtocol.writeUpdate(encoder, update); + broadcastMessage(this, encoding.toUint8Array(encoder)); + } + }; + + private _awarenessUpdateHandler = ( + { added, updated, removed }: any, + _origin: any, + ) => { + const changedClients = added.concat(updated).concat(removed); + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients), + ); + broadcastMessage(this, encoding.toUint8Array(encoder)); + }; + + private _unloadHandler = () => { + awarenessProtocol.removeAwarenessStates( + this.awareness, + [this.doc.clientID], + "window unload", + ); + }; + + public onMessage(data: ArrayBuffer, origin: any) { + if (this.connected) { + this._bcSubscriber(data, origin); + } + } + + /** + * @type {boolean} + */ + get synced() { + return this._synced; + } + + set synced(state) { + if (this._synced !== state) { + this._synced = state; + this.emit("synced", [state]); + this.emit("sync", [state]); + } + } + + destroy() { + this.disconnect(); + if (typeof window !== "undefined") { + window.removeEventListener("unload", this._unloadHandler); + } else if (typeof process !== "undefined") { + process.off("exit", this._unloadHandler); + } + this.awareness.off("update", this._awarenessUpdateHandler); + this.doc.off("update", this._updateHandler); + super.destroy(); + } + + connect() { + if (!this.connected) { + // bc.subscribe(this.bcChannel, this._bcSubscriber); + this.connected = true; + } + // send sync step1 to bc + // write sync step 1 + const encoderSync = encoding.createEncoder(); + encoding.writeVarUint(encoderSync, messageSync); + syncProtocol.writeSyncStep1(encoderSync, this.doc); + this.send(encoding.toUint8Array(encoderSync), this); + // broadcast local state + const encoderState = encoding.createEncoder(); + encoding.writeVarUint(encoderState, messageSync); + syncProtocol.writeSyncStep2(encoderState, this.doc); + this.send(encoding.toUint8Array(encoderState), this); + // write queryAwareness + const encoderAwarenessQuery = encoding.createEncoder(); + encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness); + this.send(encoding.toUint8Array(encoderAwarenessQuery), this); + // broadcast local awareness state + const encoderAwarenessState = encoding.createEncoder(); + encoding.writeVarUint(encoderAwarenessState, messageAwareness); + encoding.writeVarUint8Array( + encoderAwarenessState, + awarenessProtocol.encodeAwarenessUpdate(this.awareness, [ + this.doc.clientID, + ]), + ); + this.send(encoding.toUint8Array(encoderAwarenessState), this); + } + + disconnect() { + // broadcast message with local awareness state set to null (indicating disconnect) + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate( + this.awareness, + [this.doc.clientID], + new Map(), + ), + ); + broadcastMessage(this, encoding.toUint8Array(encoder)); + if (this.connected) { + // bc.unsubscribe(this.bcChannel, this._bcSubscriber); + this.connected = false; + } + } +} diff --git a/packages/y-penpal/tsconfig.json b/packages/y-penpal/tsconfig.json new file mode 100644 index 000000000..3474e454c --- /dev/null +++ b/packages/y-penpal/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": false, + "declaration": true, + "declarationDir": "types", + "sourceMap": true, + "downlevelIteration": true, + "outDir": "dist", + "composite": true, + "jsx": "react-jsx" + }, + "include": ["src"] +}