diff --git a/README.md b/README.md index 54dfbb00..bc7553fb 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ To instantiate the Spaces SDK, create an [Ably client](https://ably.com/docs/get import Spaces from '@ably/spaces'; import { Realtime } from 'ably'; -const client = new Realtime.Promise({ key: "", clientId: "" }); +const client = new Realtime({ key: "", clientId: "" }); const spaces = new Spaces(client); ``` You can use [basic authentication](https://ably.com/docs/auth/basic) i.e. the API Key directly for testing purposes, however it is strongly recommended that you use [token authentication](https://ably.com/docs/auth/token) in production environments. @@ -95,7 +95,7 @@ You can also use Spaces with a CDN, such as [unpkg](https://www.unpkg.com/): After this, instantiate the SDK in the same way as in the NPM option above: ```ts -const client = new Ably.Realtime.Promise({ key: "", clientId: "" }); +const client = new Ably.Realtime({ key: "", clientId: "" }); const spaces = new Spaces(client); ``` diff --git a/__mocks__/ably/promises/index.ts b/__mocks__/ably/index.ts similarity index 88% rename from __mocks__/ably/promises/index.ts rename to __mocks__/ably/index.ts index 61248461..a3c455ba 100644 --- a/__mocks__/ably/promises/index.ts +++ b/__mocks__/ably/index.ts @@ -1,4 +1,4 @@ -import Ably, { Types } from 'ably/promises'; +import { PresenceMessage, Rest, ErrorInfo } from 'ably'; const MOCK_CLIENT_ID = 'MOCK_CLIENT_ID'; @@ -7,7 +7,7 @@ const methodReturningVoidPromise = () => mockPromisify((() => {})()); function createMockPresence() { return { - get: () => mockPromisify([]), + get: () => mockPromisify([]), update: () => mockPromisify(undefined), enter: methodReturningVoidPromise, leave: methodReturningVoidPromise, @@ -91,8 +91,10 @@ class MockRealtime { } } +class MockErrorInfo extends ErrorInfo {} + // maintain the PresenceMessage class so tests can initialise it directly using // PresenceMessage.fromValues. -MockRealtime.PresenceMessage = Ably.Rest.PresenceMessage; +MockRealtime.PresenceMessage = Rest.PresenceMessage; -export { MockRealtime as Realtime }; +export { MockRealtime as Realtime, MockErrorInfo as ErrorInfo }; diff --git a/demo/api/ably-token-request/index.ts b/demo/api/ably-token-request/index.ts index 56b09990..9e077434 100644 --- a/demo/api/ably-token-request/index.ts +++ b/demo/api/ably-token-request/index.ts @@ -1,5 +1,5 @@ import * as dotenv from 'dotenv'; -import * as Ably from 'ably/promises'; +import * as Ably from 'ably'; import { HandlerEvent } from '@netlify/functions'; dotenv.config(); diff --git a/demo/api/ably-token-request/package-lock.json b/demo/api/ably-token-request/package-lock.json index f9bcb100..51bd49ce 100644 --- a/demo/api/ably-token-request/package-lock.json +++ b/demo/api/ably-token-request/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@netlify/functions": "^1.4.0", "@types/node": "^18.3.0", - "ably": "^1.2.36", + "ably": "^2.3.0", "dotenv": "^16.0.3", "typescript": "^4.9.5" } @@ -102,24 +102,32 @@ } }, "node_modules/ably": { - "version": "1.2.36", - "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.36.tgz", - "integrity": "sha512-6Pf5l+wapM3sBC49JttyuNRoa5m/GPYQigM+ehlUECv/UjEg+j2vpNFdTidSuBKphwPhGMF0Jjf2/sLhXVWoug==", - "license": "Apache-2.0", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ably/-/ably-2.3.0.tgz", + "integrity": "sha512-4QyN2SEUG1Svpg5LIr/K14U+X9e5Wv/dsldPcPZGwaE1iFVZxl7Ccb6/lcBMn+8Uq0+2q9rzTKFnGERQIRIHVg==", "dependencies": { "@ably/msgpack-js": "^0.4.0", - "got": "^11.8.2", - "ws": "^5.1" + "fastestsmallesttextencoderdecoder": "^1.0.22", + "got": "^11.8.5", + "ulid": "^2.3.0", + "ws": "^8.17.1" }, "engines": { - "node": ">=5.10.x" + "node": ">=16" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": 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==" - }, "node_modules/base64-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", @@ -235,6 +243,11 @@ "once": "^1.4.0" } }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==" + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -406,17 +419,37 @@ "node": ">=4.2.0" } }, + "node_modules/ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "bin": { + "ulid": "bin/cli.js" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", - "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", - "dependencies": { - "async-limiter": "~1.0.0" + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "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 + } } } }, @@ -493,20 +526,17 @@ } }, "ably": { - "version": "1.2.36", - "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.36.tgz", - "integrity": "sha512-6Pf5l+wapM3sBC49JttyuNRoa5m/GPYQigM+ehlUECv/UjEg+j2vpNFdTidSuBKphwPhGMF0Jjf2/sLhXVWoug==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ably/-/ably-2.3.0.tgz", + "integrity": "sha512-4QyN2SEUG1Svpg5LIr/K14U+X9e5Wv/dsldPcPZGwaE1iFVZxl7Ccb6/lcBMn+8Uq0+2q9rzTKFnGERQIRIHVg==", "requires": { "@ably/msgpack-js": "^0.4.0", - "got": "^11.8.2", - "ws": "^5.1" + "fastestsmallesttextencoderdecoder": "^1.0.22", + "got": "^11.8.5", + "ulid": "^2.3.0", + "ws": "^8.17.1" } }, - "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==" - }, "base64-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", @@ -590,6 +620,11 @@ "once": "^1.4.0" } }, + "fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==" + }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -714,18 +749,21 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, + "ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==" + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", - "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", - "requires": { - "async-limiter": "~1.0.0" - } + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} } } } diff --git a/demo/api/ably-token-request/package.json b/demo/api/ably-token-request/package.json index f6bd0725..64508514 100644 --- a/demo/api/ably-token-request/package.json +++ b/demo/api/ably-token-request/package.json @@ -16,7 +16,7 @@ "dependencies": { "@netlify/functions": "^1.4.0", "@types/node": "^18.3.0", - "ably": "^1.2.36", + "ably": "^2.3.0", "dotenv": "^16.0.3", "typescript": "^4.9.5" } diff --git a/demo/package-lock.json b/demo/package-lock.json index 4a8208df..e126d4cd 100644 --- a/demo/package-lock.json +++ b/demo/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "dependencies": { "@ably/spaces": "file:..", - "ably": "^1.2.45", + "ably": "^2.3.0", "classnames": "^2.3.2", "dayjs": "^1.11.9", "nanoid": "^3.3.7", @@ -41,7 +41,7 @@ }, "..": { "name": "@ably/spaces", - "version": "0.3.0", + "version": "0.3.1", "license": "ISC", "dependencies": { "nanoid": "^3.3.7" @@ -74,7 +74,7 @@ "vitest": "^0.34.3" }, "peerDependencies": { - "ably": "^1.2.46", + "ably": "^2.3.0", "react": ">=16.8.0", "react-dom": ">=16.8.0" }, @@ -1356,16 +1356,18 @@ } }, "node_modules/ably": { - "version": "1.2.45", - "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.45.tgz", - "integrity": "sha512-8Jk8XT0dSPcLcZ8zSAF+mRtwMzaQR5dWHqGdx9Y859ZLhXiqf4gQb7P+4Elqy1l1IIRoDWgyt0fkyfo7ngUdMA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ably/-/ably-2.3.0.tgz", + "integrity": "sha512-4QyN2SEUG1Svpg5LIr/K14U+X9e5Wv/dsldPcPZGwaE1iFVZxl7Ccb6/lcBMn+8Uq0+2q9rzTKFnGERQIRIHVg==", "dependencies": { "@ably/msgpack-js": "^0.4.0", + "fastestsmallesttextencoderdecoder": "^1.0.22", "got": "^11.8.5", - "ws": "^5.1" + "ulid": "^2.3.0", + "ws": "^8.17.1" }, "engines": { - "node": ">=5.10.x" + "node": ">=16" }, "peerDependencies": { "react": ">=16.8.0", @@ -1478,11 +1480,6 @@ "node": ">=8" } }, - "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==" - }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -2342,6 +2339,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -19864,6 +19866,14 @@ "node": ">=14.17" } }, + "node_modules/ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "bin": { + "ulid": "bin/cli.js" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -20374,11 +20384,23 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", - "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", - "dependencies": { - "async-limiter": "~1.0.0" + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "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 + } } }, "node_modules/yallist": { @@ -21251,13 +21273,15 @@ } }, "ably": { - "version": "1.2.45", - "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.45.tgz", - "integrity": "sha512-8Jk8XT0dSPcLcZ8zSAF+mRtwMzaQR5dWHqGdx9Y859ZLhXiqf4gQb7P+4Elqy1l1IIRoDWgyt0fkyfo7ngUdMA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ably/-/ably-2.3.0.tgz", + "integrity": "sha512-4QyN2SEUG1Svpg5LIr/K14U+X9e5Wv/dsldPcPZGwaE1iFVZxl7Ccb6/lcBMn+8Uq0+2q9rzTKFnGERQIRIHVg==", "requires": { "@ably/msgpack-js": "^0.4.0", + "fastestsmallesttextencoderdecoder": "^1.0.22", "got": "^11.8.5", - "ws": "^5.1" + "ulid": "^2.3.0", + "ws": "^8.17.1" } }, "acorn": { @@ -21334,11 +21358,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "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==" - }, "autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -21946,6 +21965,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==" + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -34923,6 +34947,11 @@ "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true }, + "ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==" + }, "update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -35161,12 +35190,10 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", - "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", - "requires": { - "async-limiter": "~1.0.0" - } + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} }, "yallist": { "version": "3.1.1", diff --git a/demo/package.json b/demo/package.json index 0933a7cc..17ef9cad 100644 --- a/demo/package.json +++ b/demo/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@ably/spaces": "file:..", - "ably": "^1.2.45", + "ably": "^2.3.0", "classnames": "^2.3.2", "dayjs": "^1.11.9", "nanoid": "^3.3.7", diff --git a/demo/src/components/Paragraph.tsx b/demo/src/components/Paragraph.tsx index 02512f06..fd8888dd 100644 --- a/demo/src/components/Paragraph.tsx +++ b/demo/src/components/Paragraph.tsx @@ -1,10 +1,13 @@ import React, { useRef } from 'react'; import cn from 'classnames'; -import { getMemberFirstName, getOutlineClasses } from '../utils'; +import { ChannelProvider } from 'ably/react'; + +import { generateSpaceName, getMemberFirstName, getOutlineClasses, getParamValueFromUrl } from '../utils'; import { StickyLabel } from './StickyLabel'; import { LockFilledSvg } from './svg/LockedFilled.tsx'; import { EditableText } from './EditableText.tsx'; import { useTextComponentLock } from '../hooks/useTextComponentLock.ts'; +import { buildLockId } from '../utils/locking.ts'; interface Props extends React.HTMLAttributes { id: string; @@ -14,18 +17,38 @@ interface Props extends React.HTMLAttributes { maxlength?: number; } -export const Paragraph = ({ +export const Paragraph = (props: Props) => { + const spaceName = getParamValueFromUrl('space', generateSpaceName); + const lockId = buildLockId(props.slide, props.id); + const channelName = `${spaceName}${lockId}`; + + return ( + + + + ); +}; + +const ParagraphChild = ({ variant = 'regular', id, slide, className, children, maxlength = 300, + channelName, ...props -}: Props) => { +}: Props & { channelName: string }) => { const containerRef = useRef(null); const { content, activeMember, locked, lockedByYou, editIsNotAllowed, handleSelect, handleContentUpdate } = useTextComponentLock({ + channelName, id, slide, defaultText: children, diff --git a/demo/src/components/Title.tsx b/demo/src/components/Title.tsx index 2c24d714..47dfa38e 100644 --- a/demo/src/components/Title.tsx +++ b/demo/src/components/Title.tsx @@ -1,11 +1,13 @@ import React, { useRef } from 'react'; import cn from 'classnames'; +import { ChannelProvider } from 'ably/react'; -import { getMemberFirstName, getOutlineClasses } from '../utils'; +import { generateSpaceName, getMemberFirstName, getOutlineClasses, getParamValueFromUrl } from '../utils'; import { LockFilledSvg } from './svg/LockedFilled.tsx'; import { StickyLabel } from './StickyLabel.tsx'; import { EditableText } from './EditableText.tsx'; import { useTextComponentLock } from '../hooks/useTextComponentLock.ts'; +import { buildLockId } from '../utils/locking.ts'; interface Props extends React.HTMLAttributes { id: string; @@ -15,10 +17,38 @@ interface Props extends React.HTMLAttributes { maxlength?: number; } -export const Title = ({ variant = 'h1', className, id, slide, children, maxlength = 70, ...props }: Props) => { +export const Title = (props: Props) => { + const spaceName = getParamValueFromUrl('space', generateSpaceName); + const lockId = buildLockId(props.slide, props.id); + const channelName = `${spaceName}${lockId}`; + + return ( + + + + ); +}; + +const TitleChild = ({ + variant = 'h1', + className, + id, + slide, + children, + maxlength = 70, + channelName, + ...props +}: Props & { channelName: string }) => { const containerRef = useRef(null); const { content, activeMember, locked, lockedByYou, editIsNotAllowed, handleSelect, handleContentUpdate } = useTextComponentLock({ + channelName, id, slide, defaultText: children, diff --git a/demo/src/hooks/useTextComponentLock.ts b/demo/src/hooks/useTextComponentLock.ts index c175d67a..b75761a2 100644 --- a/demo/src/hooks/useTextComponentLock.ts +++ b/demo/src/hooks/useTextComponentLock.ts @@ -2,28 +2,33 @@ import { MutableRefObject, useCallback } from 'react'; import { useChannel } from 'ably/react'; import { useMembers, useLock } from '@ably/spaces/react'; import sanitize from 'sanitize-html'; -import { findActiveMember, generateSpaceName, getParamValueFromUrl } from '../utils'; +import { findActiveMember } from '../utils'; import { buildLockId } from '../utils/locking.ts'; import { usePreview } from '../components/PreviewContext.tsx'; import { useClearOnFailedLock, useClickOutside, useElementSelect } from './useElementSelect.ts'; import { useSlideElementContent } from './useSlideElementContent.ts'; interface UseTextComponentLockArgs { + channelName: string; id: string; slide: string; defaultText: string; containerRef: MutableRefObject; } -export const useTextComponentLock = ({ id, slide, defaultText, containerRef }: UseTextComponentLockArgs) => { - const spaceName = getParamValueFromUrl('space', generateSpaceName); +export const useTextComponentLock = ({ + channelName, + id, + slide, + defaultText, + containerRef, +}: UseTextComponentLockArgs) => { const { members, self } = useMembers(); const activeMember = findActiveMember(id, slide, members); const lockId = buildLockId(slide, id); const { status, member } = useLock(lockId); const locked = status === 'locked'; const lockedByYou = locked && self?.connectionId === member?.connectionId; - const channelName = `[?rewind=1]${spaceName}${lockId}`; const [content, updateContent] = useSlideElementContent(lockId, defaultText); const preview = usePreview(); diff --git a/demo/src/main.tsx b/demo/src/main.tsx index 743cbbff..30c02d54 100644 --- a/demo/src/main.tsx +++ b/demo/src/main.tsx @@ -15,7 +15,7 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) const clientId = nanoid(); -const client = new Realtime.Promise({ +const client = new Realtime({ authUrl: `/api/ably-token-request?clientId=${clientId}`, clientId, }); diff --git a/docs/connection-and-channel-management.md b/docs/connection-and-channel-management.md index d72dd016..1c64aec2 100644 --- a/docs/connection-and-channel-management.md +++ b/docs/connection-and-channel-management.md @@ -9,7 +9,7 @@ This document describes how to access a connection and channels on Spaces, and w When initializing the Spaces SDK, an Ably client is passed as a required argument: ```ts -const client = new Realtime.Promise({ key: "", clientId: "" }); +const client = new Realtime({ key: "", clientId: "" }); const spaces = new Spaces(client); ``` @@ -50,4 +50,4 @@ mySpace.cursors.subscribe('update', () => {}); - [Channels](https://ably.com/docs/channels) - [Channel states](https://ably.com/docs/channels#states) -- [Handle channel failure](https://ably.com/docs/channels#failure) \ No newline at end of file +- [Handle channel failure](https://ably.com/docs/channels#failure) diff --git a/docs/react.md b/docs/react.md index 69b320ee..87b55ac0 100644 --- a/docs/react.md +++ b/docs/react.md @@ -43,7 +43,7 @@ import { Realtime } from "ably"; import Spaces from "@ably/spaces"; import { SpacesProvider, SpaceProvider } from "@ably/spaces/react"; -const ably = new Realtime.Promise({ key: "your-ably-api-key", clientId: 'me' }); +const ably = new Realtime({ key: "your-ably-api-key", clientId: 'me' }); const spaces = new Spaces(ably); root.render( diff --git a/docs/usage.md b/docs/usage.md index 02c47d95..c48c4059 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -39,19 +39,19 @@ You can also use Spaces with a CDN like [unpkg](https://www.unpkg.com/): > **Note** > -> If you do this, then replace the call to `new Realtime.Promise` in the next section with `new Ably.Realtime.Promise`. +> If you do this, then replace the call to `new Realtime` in the next section with `new Ably.Realtime`. ## Authentication and instantiation -Spaces use an [Ably promise-based realtime client](https://github.com/ably/ably-js#using-the-async-api-style). You can pass an Ably client directly to the spaces constructor. +You can pass an Ably client directly to the spaces constructor. The Ably client instantiation accepts client options. You will need at minimum an [Ably API key](#ably-api-key) and a [clientId](https://ably.com/docs/auth/identified-clients?lang=javascripts). A clientId represents an identity of an connection. In most cases this will something like the id of a user: ```ts -import { Realtime } from 'ably/promise'; +import { Realtime } from 'ably'; import Spaces from '@ably/spaces'; -const client = new Realtime.Promise({ key: "", clientId: "" }); +const client = new Realtime({ key: "", clientId: "" }); const spaces = new Spaces(client); ``` diff --git a/examples/avatar-stack.ts b/examples/avatar-stack.ts index d847be67..60c1a615 100644 --- a/examples/avatar-stack.ts +++ b/examples/avatar-stack.ts @@ -4,7 +4,7 @@ import { Realtime } from 'ably'; import { renderAvatars, renderNotification } from './my-application'; // Create Ably client -const client = new Realtime.Promise({ authUrl: '', clientId: '' }); +const client = new Realtime({ authUrl: '', clientId: '' }); // Initialize the Spaces SDK with an Ably client const spaces = new Spaces(client); diff --git a/examples/live-cursors.ts b/examples/live-cursors.ts index a319ff15..3866866c 100644 --- a/examples/live-cursors.ts +++ b/examples/live-cursors.ts @@ -4,7 +4,7 @@ import { Realtime } from 'ably'; import renderCursor from './my-application'; // Create Ably client -const client = new Realtime.Promise({ authUrl: '', clientId: '' }); +const client = new Realtime({ authUrl: '', clientId: '' }); // Initialize the Spaces SDK with an Ably client const spaces = new Spaces(client); diff --git a/examples/location.ts b/examples/location.ts index ba04835c..197caed6 100644 --- a/examples/location.ts +++ b/examples/location.ts @@ -4,7 +4,7 @@ import { Realtime } from 'ably'; import updateLocationsForMember from './my-application'; // Create Ably client -const client = new Realtime.Promise({ authUrl: '', clientId: '' }); +const client = new Realtime({ authUrl: '', clientId: '' }); // Initialize the Spaces SDK with an Ably client const spaces = new Spaces(client); diff --git a/examples/locking.ts b/examples/locking.ts index 13ff339c..41fb8148 100644 --- a/examples/locking.ts +++ b/examples/locking.ts @@ -4,7 +4,7 @@ import { Realtime } from 'ably'; import { enableLocationEditing, lockId } from './my-application'; // Create Ably client -const client = new Realtime.Promise({ authUrl: '', clientId: '' }); +const client = new Realtime({ authUrl: '', clientId: '' }); // Initialize the Spaces SDK with an Ably client const spaces = new Spaces(client); diff --git a/examples/locks.ts b/examples/locks.ts index 81c685df..686f8a5b 100644 --- a/examples/locks.ts +++ b/examples/locks.ts @@ -1,6 +1,6 @@ // An example of members locating at and locking elements in a slides // application. -import Ably from 'ably/promises'; +import Ably from 'ably'; import Spaces from '../dist/cjs/Spaces.js'; import { Lock, LockAttributes } from '../dist/cjs/index.js'; @@ -32,7 +32,7 @@ class SlideElement { // define a main async function since we can't use await at the top-level. const main = async () => { info('initialising Ably client'); - const client = new Ably.Realtime.Promise({ + const client = new Ably.Realtime({ key: process.env.ABLY_API_KEY, clientId: 'Alice', }); diff --git a/package-lock.json b/package-lock.json index d8b908d9..918d8e4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "vitest": "^0.34.3" }, "peerDependencies": { - "ably": "^1.2.46", + "ably": "^2.3.0", "react": ">=16.8.0", "react-dom": ">=16.8.0" }, @@ -1848,17 +1848,19 @@ "dev": true }, "node_modules/ably": { - "version": "1.2.46", - "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.46.tgz", - "integrity": "sha512-N+t2C3bWc8P7dL9sJ/WbfspdshSfdJFhkvlqQKBJjPZCjT0WeyW6PW4nQnJK7TfzsiESWZovZ+Aku+/kgZW2OQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ably/-/ably-2.3.0.tgz", + "integrity": "sha512-4QyN2SEUG1Svpg5LIr/K14U+X9e5Wv/dsldPcPZGwaE1iFVZxl7Ccb6/lcBMn+8Uq0+2q9rzTKFnGERQIRIHVg==", "peer": true, "dependencies": { "@ably/msgpack-js": "^0.4.0", + "fastestsmallesttextencoderdecoder": "^1.0.22", "got": "^11.8.5", - "ws": "^8.14.2" + "ulid": "^2.3.0", + "ws": "^8.17.1" }, "engines": { - "node": ">=5.10.x" + "node": ">=16" }, "peerDependencies": { "react": ">=16.8.0", @@ -3471,6 +3473,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -6583,6 +6591,15 @@ "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", "dev": true }, + "node_modules/ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "peer": true, + "bin": { + "ulid": "bin/cli.js" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -7022,9 +7039,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -8331,14 +8348,16 @@ "dev": true }, "ably": { - "version": "1.2.46", - "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.46.tgz", - "integrity": "sha512-N+t2C3bWc8P7dL9sJ/WbfspdshSfdJFhkvlqQKBJjPZCjT0WeyW6PW4nQnJK7TfzsiESWZovZ+Aku+/kgZW2OQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ably/-/ably-2.3.0.tgz", + "integrity": "sha512-4QyN2SEUG1Svpg5LIr/K14U+X9e5Wv/dsldPcPZGwaE1iFVZxl7Ccb6/lcBMn+8Uq0+2q9rzTKFnGERQIRIHVg==", "peer": true, "requires": { "@ably/msgpack-js": "^0.4.0", + "fastestsmallesttextencoderdecoder": "^1.0.22", "got": "^11.8.5", - "ws": "^8.14.2" + "ulid": "^2.3.0", + "ws": "^8.17.1" } }, "accepts": { @@ -9594,6 +9613,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -11855,6 +11880,12 @@ "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", "dev": true }, + "ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "peer": true + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -12128,9 +12159,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "requires": {} }, "xml-name-validator": { diff --git a/package.json b/package.json index 29706da7..18c90331 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "nanoid": "^3.3.7" }, "peerDependencies": { - "ably": "^1.2.46", + "ably": "^2.3.0", "react": ">=16.8.0", "react-dom": ">=16.8.0" }, diff --git a/src/CursorBatching.ts b/src/CursorBatching.ts index 58e4f507..d782b786 100644 --- a/src/CursorBatching.ts +++ b/src/CursorBatching.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { RealtimeChannel } from 'ably'; import { CURSOR_UPDATE } from './CursorConstants.js'; import type { CursorUpdate } from './types.js'; @@ -27,7 +27,7 @@ export default class CursorBatching { this.batchTime = outboundBatchInterval; } - pushCursorPosition(channel: Types.RealtimeChannelPromise, cursor: Pick) { + pushCursorPosition(channel: RealtimeChannel, cursor: Pick) { // Ignore the cursor update if there is no one listening if (!this.shouldSend) return; @@ -60,14 +60,14 @@ export default class CursorBatching { this.outgoingBuffer.push(value); } - private async publishFromBuffer(channel: Types.RealtimeChannelPromise, eventName: string) { + private async publishFromBuffer(channel: RealtimeChannel, eventName: string) { if (!this.isRunning) { this.isRunning = true; await this.batchToChannel(channel, eventName); } } - private async batchToChannel(channel: Types.RealtimeChannelPromise, eventName: string) { + private async batchToChannel(channel: RealtimeChannel, eventName: string) { if (!this.hasMovement) { this.isRunning = false; return; diff --git a/src/CursorDispensing.ts b/src/CursorDispensing.ts index 3cb215b9..45f0718a 100644 --- a/src/CursorDispensing.ts +++ b/src/CursorDispensing.ts @@ -1,5 +1,5 @@ import { type CursorUpdate } from './types.js'; -import { type RealtimeMessage } from './utilities/types.js'; +import { type RealtimeInboundMessage } from './utilities/types.js'; export default class CursorDispensing { private buffer: Record = {}; @@ -32,11 +32,11 @@ export default class CursorDispensing { ); } - processBatch(message: RealtimeMessage) { + processBatch(message: RealtimeInboundMessage) { const updates: { cursor: CursorUpdate; offset: number }[] = message.data || []; updates.forEach((update: { cursor: CursorUpdate; offset: number }) => { - const enhancedMsg = { + const enhancedMsg: { cursor: CursorUpdate; offset: number } = { cursor: { clientId: message.clientId, connectionId: message.connectionId, diff --git a/src/CursorHistory.ts b/src/CursorHistory.ts index 8a8e532a..580226d1 100644 --- a/src/CursorHistory.ts +++ b/src/CursorHistory.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { InboundMessage, PaginatedResult, RealtimeChannel } from 'ably'; import type { CursorUpdate } from './types.js'; import type { CursorsOptions } from './types.js'; @@ -29,14 +29,14 @@ export default class CursorHistory { private allCursorUpdates( connections: ConnectionsLastPosition, - page: Types.PaginatedResult, + page: PaginatedResult, ): ConnectionsLastPosition { return Object.fromEntries( Object.entries(connections).map(([connectionId, cursors]) => { const lastMessage = page.items.find((item) => item.connectionId === connectionId); if (!lastMessage) return [connectionId, cursors]; - const { data = [], clientId }: { data: OutgoingBuffer[] } & Pick = lastMessage; + const { data = [], clientId }: { data?: OutgoingBuffer[] } & Pick = lastMessage; const lastPositionSet = data[data.length - 1]?.cursor; const lastUpdate = lastPositionSet @@ -54,7 +54,7 @@ export default class CursorHistory { } async getLastCursorUpdate( - channel: Types.RealtimeChannelPromise, + channel: RealtimeChannel, paginationLimit: CursorsOptions['paginationLimit'], ): Promise { const members = await channel.presence.get(); @@ -76,7 +76,8 @@ export default class CursorHistory { pageNo++; while (pageNo <= paginationLimit && this.positionsMissing(connections) && history.hasNext()) { - page = await history.next(); + // assert result of .next() is non null as we've checked .hasNext() before + page = (await history.next())!; connections = this.allCursorUpdates(connections, page); pageNo++; } diff --git a/src/Cursors.test.ts b/src/Cursors.test.ts index 3f1daa60..383f4ae8 100644 --- a/src/Cursors.test.ts +++ b/src/Cursors.test.ts @@ -1,5 +1,5 @@ import { it, describe, expect, vi, beforeEach, vitest, afterEach } from 'vitest'; -import { Realtime, Types } from 'ably/promises'; +import { Realtime, RealtimeClient, RealtimeChannel } from 'ably'; import Space from './Space.js'; import Cursors from './Cursors.js'; @@ -10,22 +10,22 @@ import CursorDispensing from './CursorDispensing.js'; import CursorHistory from './CursorHistory.js'; import type { CursorUpdate, SpaceMember } from './types.js'; -import type { RealtimeMessage } from './utilities/types.js'; +import type { RealtimeInboundMessage } from './utilities/types.js'; interface CursorsTestContext { - client: Types.RealtimePromise; + client: RealtimeClient; space: Space; cursors: Cursors; - channel: Types.RealtimeChannelPromise; + channel: RealtimeChannel; batching: CursorBatching; dispensing: CursorDispensing; history: CursorHistory; selfStub: SpaceMember; lastCursorPositionsStub: Record; - fakeMessageStub: RealtimeMessage; + fakeMessageStub: RealtimeInboundMessage; } -vi.mock('ably/promises'); +vi.mock('ably'); function createPresenceCount(length: number) { return async () => Array.from({ length }, (_, i) => createPresenceMessage('enter', { clientId: '' + i })); @@ -39,7 +39,7 @@ describe('Cursors', () => { context.cursors = context.space.cursors; // This will set the channel context.cursors.subscribe('update', () => {}); - context.channel = context.cursors['channel'] as Types.RealtimeChannelPromise; + context.channel = context.cursors.channel!; context.batching = context.space.cursors['cursorBatching']; context.dispensing = context.space.cursors['cursorDispensing']; context.history = context.space.cursors['cursorHistory']; diff --git a/src/Cursors.ts b/src/Cursors.ts index 5760663b..7548f463 100644 --- a/src/Cursors.ts +++ b/src/Cursors.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { RealtimeChannel } from 'ably'; import Space from './Space.js'; import CursorBatching from './CursorBatching.js'; @@ -8,7 +8,7 @@ import CursorHistory from './CursorHistory.js'; import { CURSOR_UPDATE } from './CursorConstants.js'; import type { CursorsOptions, CursorUpdate } from './types.js'; -import type { RealtimeMessage } from './utilities/types.js'; +import type { RealtimeInboundMessage } from './utilities/types.js'; import { ERR_NOT_ENTERED_SPACE } from './Errors.js'; /** @@ -44,7 +44,7 @@ export default class Cursors extends EventEmitter { /** * The [realtime channel](https://ably.com/docs/channels) instance that this `Cursors` instance uses for transmitting and receiving data. */ - public channel?: Types.RealtimeChannelPromise; + public channel?: RealtimeChannel; /** @internal */ constructor(private space: Space) { @@ -90,11 +90,11 @@ export default class Cursors extends EventEmitter { this.cursorBatching.pushCursorPosition(channel, cursor); } - private getChannel(): Types.RealtimeChannelPromise { + private getChannel(): RealtimeChannel { return this.channel ?? (this.channel = this.initializeCursorsChannel()); } - private initializeCursorsChannel(): Types.RealtimeChannelPromise { + private initializeCursorsChannel(): RealtimeChannel { const channel = this.space.client.channels.get(this.channelName); channel.presence.subscribe(this.onPresenceUpdate.bind(this)); channel.presence.enter(); @@ -111,7 +111,7 @@ export default class Cursors extends EventEmitter { private isUnsubscribed() { const channel = this.getChannel(); - interface ChannelWithSubscriptions extends Types.RealtimeChannelPromise { + interface ChannelWithSubscriptions extends RealtimeChannel { subscriptions: EventEmitter<{}>; } @@ -185,7 +185,7 @@ export default class Cursors extends EventEmitter { const channel = this.getChannel(); channel.subscribe(CURSOR_UPDATE, (message) => { - this.cursorDispensing.processBatch(message as RealtimeMessage); + this.cursorDispensing.processBatch(message as RealtimeInboundMessage); }); } } diff --git a/src/Locations.test.ts b/src/Locations.test.ts index b6c488d3..c5509e3d 100644 --- a/src/Locations.test.ts +++ b/src/Locations.test.ts @@ -1,5 +1,5 @@ import { it, describe, expect, vi, beforeEach } from 'vitest'; -import { Realtime, Types } from 'ably/promises'; +import { PresenceMessage, Realtime, RealtimeClient, RealtimePresence } from 'ably'; import Space from './Space.js'; @@ -11,13 +11,13 @@ import { } from './utilities/test/fakes.js'; interface SpaceTestContext { - client: Types.RealtimePromise; + client: RealtimeClient; space: Space; - presence: Types.RealtimePresencePromise; - presenceMap: Map; + presence: RealtimePresence; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('Locations', () => { diff --git a/src/Locks.test.ts b/src/Locks.test.ts index 6cfaf670..409bf00a 100644 --- a/src/Locks.test.ts +++ b/src/Locks.test.ts @@ -1,18 +1,18 @@ import { it, describe, expect, vi, beforeEach } from 'vitest'; -import { Realtime, Types } from 'ably/promises'; +import { PresenceMessage, Realtime, RealtimeClient, RealtimePresence } from 'ably'; import Space from './Space.js'; import type { SpaceMember, LockStatus } from './types.js'; import { createPresenceMessage } from './utilities/test/fakes.js'; interface SpaceTestContext { - client: Types.RealtimePromise; + client: RealtimeClient; space: Space; - presence: Types.RealtimePresencePromise; - presenceMap: Map; + presence: RealtimePresence; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); describe('Locks', () => { beforeEach((context) => { diff --git a/src/Locks.ts b/src/Locks.ts index 2a20b4f9..8d73a21f 100644 --- a/src/Locks.ts +++ b/src/Locks.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { PresenceMessage } from 'ably'; import Space from './Space.js'; import type { Lock, SpaceMember } from './types.js'; @@ -350,7 +350,7 @@ export default class Locks extends EventEmitter { } /** @internal */ - async processPresenceMessage(message: Types.PresenceMessage) { + async processPresenceMessage(message: PresenceMessage) { const member = await this.space.members.getByConnectionId(message.connectionId); if (!member) return; diff --git a/src/Members.test.ts b/src/Members.test.ts index 08203d23..2cb1b853 100644 --- a/src/Members.test.ts +++ b/src/Members.test.ts @@ -1,18 +1,18 @@ import { it, describe, expect, vi, beforeEach, afterEach } from 'vitest'; -import { Types, Realtime } from 'ably/promises'; +import { PresenceMessage, Realtime, RealtimeClient, RealtimePresence } from 'ably'; import Space from './Space.js'; import { createPresenceEvent, createSpaceMember, createProfileUpdate } from './utilities/test/fakes.js'; interface SpaceTestContext { - client: Types.RealtimePromise; + client: RealtimeClient; space: Space; - presence: Types.RealtimePresencePromise; - presenceMap: Map; + presence: RealtimePresence; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('Members', () => { diff --git a/src/Space.test.ts b/src/Space.test.ts index 7a69ad01..b6adfaa3 100644 --- a/src/Space.test.ts +++ b/src/Space.test.ts @@ -1,5 +1,5 @@ import { it, describe, expect, vi, beforeEach, expectTypeOf } from 'vitest'; -import { Realtime, Types } from 'ably/promises'; +import { PresenceMessage, Realtime, RealtimeClient, RealtimePresence } from 'ably'; import Space from './Space.js'; import Locations from './Locations.js'; @@ -14,13 +14,13 @@ import { } from './utilities/test/fakes.js'; interface SpaceTestContext { - client: Types.RealtimePromise; + client: RealtimeClient; space: Space; - presence: Types.RealtimePresencePromise; - presenceMap: Map; + presence: RealtimePresence; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('Space', () => { @@ -62,7 +62,7 @@ describe('Space', () => { describe('enter', () => { beforeEach(({ presence }) => { vi.spyOn(presence, 'subscribe').mockImplementation( - async (_, listener?: (presenceMessage: Types.PresenceMessage) => void) => { + async (_, listener?: (presenceMessage: PresenceMessage) => void) => { listener!( createPresenceMessage('enter' /* arbitrarily chosen */, { clientId: 'MOCK_CLIENT_ID', connectionId: '1' }), ); @@ -99,7 +99,7 @@ describe('Space', () => { const unsubscribeSpy = vi.spyOn(presence, 'unsubscribe'); vi.spyOn(presence, 'subscribe').mockImplementation( - async (_, listener?: (presenceMessage: Types.PresenceMessage) => void) => { + async (_, listener?: (presenceMessage: PresenceMessage) => void) => { listener!(createPresenceMessage('enter' /* arbitrarily chosen */, presenceMessageData)); }, ); diff --git a/src/Space.ts b/src/Space.ts index 1cd15f59..e3a40fe0 100644 --- a/src/Space.ts +++ b/src/Space.ts @@ -1,4 +1,4 @@ -import Ably, { Types } from 'ably'; +import { PresenceMessage, Realtime, RealtimeClient, RealtimeChannel } from 'ably'; import EventEmitter, { InvalidArgumentError, inspect, type EventListener } from './utilities/EventEmitter.js'; import Locations from './Locations.js'; @@ -85,7 +85,7 @@ class Space extends EventEmitter { /** * @internal */ - readonly client: Types.RealtimePromise; + readonly client: RealtimeClient; private readonly channelName: string; /** * @internal @@ -110,7 +110,7 @@ class Space extends EventEmitter { /** * The [realtime channel instance](https://ably.com/docs/channels) that this `Space` instance uses for transmitting and receiving data. */ - readonly channel: Types.RealtimeChannelPromise; + readonly channel: RealtimeChannel; /** * An instance of {@link Locks}. */ @@ -121,7 +121,7 @@ class Space extends EventEmitter { readonly name: string; /** @internal */ - constructor(name: string, client: Types.RealtimePromise, options?: Subset) { + constructor(name: string, client: RealtimeClient, options?: Subset) { super(); this.client = client; @@ -144,21 +144,21 @@ class Space extends EventEmitter { if (!extras) { return this.channel.presence.update(data); } - return this.channel.presence.update(Ably.Realtime.PresenceMessage.fromValues({ data, extras })); + return this.channel.presence.update(Realtime.PresenceMessage.fromValues({ data, extras })); }; private presenceEnter = ({ data, extras }: SpacePresenceData) => { if (!extras) { return this.channel.presence.enter(data); } - return this.channel.presence.enter(Ably.Realtime.PresenceMessage.fromValues({ data, extras })); + return this.channel.presence.enter(Realtime.PresenceMessage.fromValues({ data, extras })); }; private presenceLeave = ({ data, extras }: SpacePresenceData) => { if (!extras) { return this.channel.presence.leave(data); } - return this.channel.presence.leave(Ably.Realtime.PresenceMessage.fromValues({ data, extras })); + return this.channel.presence.leave(Realtime.PresenceMessage.fromValues({ data, extras })); }; private setOptions(options?: Subset): SpaceOptions { @@ -209,7 +209,7 @@ class Space extends EventEmitter { return new Promise((resolve) => { const presence = this.channel.presence; - const presenceListener = async (presenceMessage: Types.PresenceMessage) => { + const presenceListener = async (presenceMessage: PresenceMessage) => { if ( !( presenceMessage.clientId == this.client.auth.clientId && diff --git a/src/SpaceUpdate.ts b/src/SpaceUpdate.ts index 11cebaec..91e646a8 100644 --- a/src/SpaceUpdate.ts +++ b/src/SpaceUpdate.ts @@ -1,5 +1,5 @@ import { nanoid } from 'nanoid'; -import { Types } from 'ably'; +import { PresenceMessage } from 'ably'; import type { SpaceMember, ProfileData } from './types.js'; import type { PresenceMember } from './utilities/types.js'; @@ -11,9 +11,9 @@ export interface SpacePresenceData { class SpaceUpdate { private self: SpaceMember | null; - private extras: Types.PresenceMessage['extras']; + private extras: PresenceMessage['extras']; - constructor({ self, extras }: { self: SpaceMember | null; extras?: Types.PresenceMessage['extras'] }) { + constructor({ self, extras }: { self: SpaceMember | null; extras?: PresenceMessage['extras'] }) { this.self = self; this.extras = extras; } diff --git a/src/Spaces.test.ts b/src/Spaces.test.ts index 41d80dc3..326fa4c8 100644 --- a/src/Spaces.test.ts +++ b/src/Spaces.test.ts @@ -1,5 +1,5 @@ import { it, describe, expect, expectTypeOf, vi, beforeEach } from 'vitest'; -import { Realtime, Types } from 'ably/promises'; +import { Realtime, RealtimeClient } from 'ably'; import Spaces, { type ClientWithOptions } from './Spaces.js'; @@ -7,16 +7,16 @@ interface SpacesTestContext { client: ClientWithOptions; } -vi.mock('ably/promises'); +vi.mock('ably'); describe('Spaces', () => { beforeEach((context) => { context.client = new Realtime({ key: 'asd' }) as ClientWithOptions; }); - it('expects the injected client to be of the type RealtimePromise', ({ client }) => { + it('expects the injected client to be of the type RealtimeClient', ({ client }) => { const spaces = new Spaces(client); - expectTypeOf(spaces.client).toMatchTypeOf(); + expectTypeOf(spaces.client).toMatchTypeOf(); }); it('creates and retrieves spaces successfully', async ({ client }) => { diff --git a/src/Spaces.ts b/src/Spaces.ts index 6117aff1..1ffe3bef 100644 --- a/src/Spaces.ts +++ b/src/Spaces.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { RealtimeClient, Connection } from 'ably'; import { ERR_SPACE_NAME_MISSING } from './Errors.js'; import Space from './Space.js'; @@ -8,7 +8,7 @@ import type { Subset } from './utilities/types.js'; import { VERSION } from './version.js'; -export interface ClientWithOptions extends Types.RealtimePromise { +export interface ClientWithOptions extends RealtimeClient { options: { agents?: Record; }; @@ -24,11 +24,11 @@ class Spaces { /** * Instance of the [Ably realtime client](https://ably.com/docs/getting-started/setup) client that was passed to the {@link constructor}. */ - client: Types.RealtimePromise; + client: RealtimeClient; /** * Instance of the [Ably realtime client](https://ably.com/docs/getting-started/setup) connection, belonging to the client that was passed to the {@link constructor}. */ - connection: Types.ConnectionPromise; + connection: Connection; /** * Version of the Spaces library. @@ -36,13 +36,13 @@ class Spaces { readonly version = VERSION; /** - * Create a new instance of the Spaces SDK by passing an instance of the [Ably promise-based realtime client](https://ably.com/docs/getting-started/setup). A [`clientId`](https://ably.com/docs/auth/identified-clients) is required. + * Create a new instance of the Spaces SDK by passing an instance of the [Ably realtime client](https://ably.com/docs/getting-started/setup). A [`clientId`](https://ably.com/docs/auth/identified-clients) is required. * * An Ably API key is needed to authenticate. [Basic authentication](https://ably.com/docs/auth/basic) may be used for convenience, however Ably strongly recommends you use [token authentication](https://ably.com/docs/auth/token) in a production environment. * * @param client An instance of the Ably prmise-based realtime client. */ - constructor(client: Types.RealtimePromise) { + constructor(client: RealtimeClient) { this.client = client; this.connection = client.connection; this.addAgent((this.client as ClientWithOptions)['options']); diff --git a/src/react/useChannelState.ts b/src/react/useChannelState.ts index a3a39b12..44c1bdf0 100644 --- a/src/react/useChannelState.ts +++ b/src/react/useChannelState.ts @@ -1,22 +1,22 @@ import { useState } from 'react'; import { useEventListener } from './useEventListener.js'; -import type { Types } from 'ably'; +import type { ChannelState, ChannelStateChange, ErrorInfo, EventEmitter } from 'ably'; -type ChannelStateListener = (stateChange: Types.ChannelStateChange) => void; +type ChannelStateListener = (stateChange: ChannelStateChange) => void; -const failedStateEvents: Types.ChannelState[] = ['suspended', 'failed', 'detached']; -const successStateEvents: Types.ChannelState[] = ['attached']; +const failedStateEvents: ChannelState[] = ['suspended', 'failed', 'detached']; +const successStateEvents: ChannelState[] = ['attached']; /** * todo use `ably/react` hooks instead */ -export const useChannelState = ( - emitter?: Types.EventEmitter, +export const useChannelState = ( + emitter?: EventEmitter, ) => { - const [channelError, setChannelError] = useState(null); + const [channelError, setChannelError] = useState(null); - useEventListener( + useEventListener( emitter, (stateChange) => { if (stateChange.reason) { @@ -26,7 +26,7 @@ export const useChannelState = ( + useEventListener( emitter, () => { setChannelError(null); diff --git a/src/react/useConnectionState.ts b/src/react/useConnectionState.ts index ff8271f6..4b5315b9 100644 --- a/src/react/useConnectionState.ts +++ b/src/react/useConnectionState.ts @@ -1,19 +1,19 @@ import { useState } from 'react'; import { useEventListener } from './useEventListener.js'; -import type { Types } from 'ably'; +import type { ConnectionState, ConnectionStateChange, ErrorInfo, EventEmitter } from 'ably'; -type ConnectionStateListener = (stateChange: Types.ConnectionStateChange) => void; +type ConnectionStateListener = (stateChange: ConnectionStateChange) => void; -const failedStateEvents: Types.ConnectionState[] = ['suspended', 'failed', 'disconnected']; -const successStateEvents: Types.ConnectionState[] = ['connected', 'closed']; +const failedStateEvents: ConnectionState[] = ['suspended', 'failed', 'disconnected']; +const successStateEvents: ConnectionState[] = ['connected', 'closed']; -export const useConnectionState = ( - emitter?: Types.EventEmitter, +export const useConnectionState = ( + emitter?: EventEmitter, ) => { - const [connectionError, setConnectionError] = useState(null); + const [connectionError, setConnectionError] = useState(null); - useEventListener( + useEventListener( emitter, (stateChange) => { if (stateChange.reason) { @@ -23,7 +23,7 @@ export const useConnectionState = ( + useEventListener( emitter, () => { setConnectionError(null); diff --git a/src/react/useCursors.test.tsx b/src/react/useCursors.test.tsx index 8337b7e3..766df2af 100644 --- a/src/react/useCursors.test.tsx +++ b/src/react/useCursors.test.tsx @@ -3,7 +3,7 @@ */ import React from 'react'; -import { Realtime } from 'ably/promises'; +import { Realtime } from 'ably'; import { it, beforeEach, describe, expect, vi } from 'vitest'; import { waitFor, renderHook, act } from '@testing-library/react'; import { SpacesProvider } from './contexts/SpacesContext.js'; @@ -12,15 +12,15 @@ import Spaces from '../index.js'; import Space from '../Space.js'; import { createPresenceEvent } from '../utilities/test/fakes.js'; import { useCursors } from './useCursors.js'; -import type { Types } from 'ably'; +import type { PresenceMessage } from 'ably'; interface SpaceTestContext { spaces: Spaces; space: Space; - presenceMap: Map; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('useCursors', () => { diff --git a/src/react/useEventListener.ts b/src/react/useEventListener.ts index 4b69665b..d999e9b8 100644 --- a/src/react/useEventListener.ts +++ b/src/react/useEventListener.ts @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react'; -import type { Types } from 'ably'; +import type { ChannelState, ChannelStateChange, ConnectionState, ConnectionStateChange, EventEmitter } from 'ably'; type EventListener = (stateChange: T) => void; @@ -8,10 +8,10 @@ type EventListener = (stateChange: T) => void; * todo use `ably/react` hooks instead */ export const useEventListener = < - S extends Types.ConnectionState | Types.ChannelState, - C extends Types.ConnectionStateChange | Types.ChannelStateChange, + S extends ConnectionState | ChannelState, + C extends ConnectionStateChange | ChannelStateChange, >( - emitter?: Types.EventEmitter, C, S>, + emitter?: EventEmitter, C, S>, listener?: EventListener, event?: S | S[], ) => { diff --git a/src/react/useLocations.test.tsx b/src/react/useLocations.test.tsx index a569379c..2167571e 100644 --- a/src/react/useLocations.test.tsx +++ b/src/react/useLocations.test.tsx @@ -3,12 +3,12 @@ */ import React from 'react'; -import { Realtime } from 'ably/promises'; +import { Realtime } from 'ably'; import { it, beforeEach, describe, expect, vi } from 'vitest'; import { waitFor, renderHook } from '@testing-library/react'; import { SpacesProvider } from './contexts/SpacesContext.js'; import { SpaceProvider } from './contexts/SpaceContext.js'; -import type { Types } from 'ably'; +import type { PresenceMessage } from 'ably'; import Spaces from '../index.js'; import { createLocationUpdate, createPresenceEvent } from '../utilities/test/fakes.js'; import Space from '../Space.js'; @@ -17,10 +17,10 @@ import { useLocations } from './useLocations.js'; interface SpaceTestContext { spaces: Spaces; space: Space; - presenceMap: Map; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('useLocations', () => { diff --git a/src/react/useLocks.test.tsx b/src/react/useLocks.test.tsx index 8ad080d7..ba27abfd 100644 --- a/src/react/useLocks.test.tsx +++ b/src/react/useLocks.test.tsx @@ -3,12 +3,12 @@ */ import React from 'react'; -import { Realtime } from 'ably/promises'; +import { Realtime } from 'ably'; import { it, beforeEach, describe, expect, vi } from 'vitest'; import { waitFor, renderHook } from '@testing-library/react'; import { SpacesProvider } from './contexts/SpacesContext.js'; import { SpaceProvider } from './contexts/SpaceContext.js'; -import type { Types } from 'ably'; +import type { PresenceMessage } from 'ably'; import Spaces from '../index.js'; import { createPresenceEvent } from '../utilities/test/fakes.js'; import Space from '../Space.js'; @@ -17,10 +17,10 @@ import { useLocks } from './useLocks.js'; interface SpaceTestContext { spaces: Spaces; space: Space; - presenceMap: Map; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('useLocks', () => { diff --git a/src/react/useMembers.test.tsx b/src/react/useMembers.test.tsx index 00653f4e..44cf7cc7 100644 --- a/src/react/useMembers.test.tsx +++ b/src/react/useMembers.test.tsx @@ -3,12 +3,12 @@ */ import React from 'react'; -import { Realtime } from 'ably/promises'; +import { Realtime } from 'ably'; import { it, beforeEach, describe, expect, vi } from 'vitest'; import { waitFor, renderHook } from '@testing-library/react'; import { SpacesProvider } from './contexts/SpacesContext.js'; import { SpaceProvider } from './contexts/SpaceContext.js'; -import type { Types } from 'ably'; +import type { PresenceMessage } from 'ably'; import Spaces from '../index.js'; import { useMembers } from './useMembers.js'; import { createLocationUpdate, createPresenceEvent } from '../utilities/test/fakes.js'; @@ -17,10 +17,10 @@ import Space from '../Space.js'; interface SpaceTestContext { spaces: Spaces; space: Space; - presenceMap: Map; + presenceMap: Map; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('useMembers', () => { diff --git a/src/react/useSpace.test.tsx b/src/react/useSpace.test.tsx index af6baf93..af12bd6a 100644 --- a/src/react/useSpace.test.tsx +++ b/src/react/useSpace.test.tsx @@ -3,7 +3,7 @@ */ import React from 'react'; -import { Realtime } from 'ably/promises'; +import { Realtime } from 'ably'; import { it, beforeEach, describe, expect, vi } from 'vitest'; import { waitFor, renderHook } from '@testing-library/react'; import { SpacesProvider } from './contexts/SpacesContext.js'; @@ -15,7 +15,7 @@ interface SpaceTestContext { spaces: Spaces; } -vi.mock('ably/promises'); +vi.mock('ably'); vi.mock('nanoid'); describe('useSpace', () => { diff --git a/src/types.ts b/src/types.ts index afdfff5a..e5866a48 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { ErrorInfo, PresenceAction } from 'ably'; import type { LockAttributes } from './Locks.js'; /** @@ -143,7 +143,7 @@ export interface SpaceMember { /** * The most recent event emitted by the member. */ - name: Types.PresenceAction; + name: PresenceAction; /** * The timestamp of the most recently emitted event. */ @@ -201,5 +201,5 @@ export type Lock = { /** * The reason why the lock status is {@link LockStatuses.Unlocked | `unlocked`}. */ - reason?: Types.ErrorInfo; + reason?: ErrorInfo; }; diff --git a/src/utilities/test/fakes.ts b/src/utilities/test/fakes.ts index 5d3d59ba..789ec52d 100644 --- a/src/utilities/test/fakes.ts +++ b/src/utilities/test/fakes.ts @@ -1,4 +1,4 @@ -import { Types } from 'ably'; +import { PresenceMessage } from 'ably'; import Space from '../../Space.js'; @@ -8,7 +8,7 @@ import type { PresenceMember } from '../types.js'; // import { nanoidId } from '../../../__mocks__/nanoid/index.js'; const nanoidId = 'NanoidID'; -const enterPresenceMessage: Types.PresenceMessage = { +const enterPresenceMessage: PresenceMessage = { clientId: '1', data: { profileUpdate: { @@ -29,12 +29,12 @@ const enterPresenceMessage: Types.PresenceMessage = { timestamp: 1, }; -const updatePresenceMessage: Types.PresenceMessage = { +const updatePresenceMessage: PresenceMessage = { ...enterPresenceMessage, action: 'update', }; -const leavePresenceMessage: Types.PresenceMessage = { +const leavePresenceMessage: PresenceMessage = { ...enterPresenceMessage, action: 'leave', }; @@ -60,7 +60,7 @@ const createPresenceMessage = (type: T, override?: P const createPresenceEvent = async ( space: Space, - presenceMap: Map, + presenceMap: Map, type: T, override?: Partial, ) => { diff --git a/src/utilities/types.ts b/src/utilities/types.ts index e3e59768..22eada31 100644 --- a/src/utilities/types.ts +++ b/src/utilities/types.ts @@ -1,4 +1,4 @@ -import type { Types } from 'ably'; +import type { InboundMessage, PresenceMessage } from 'ably'; import type { ProfileData, Lock } from '../types.js'; @@ -17,7 +17,7 @@ export type PresenceMember = { extras?: { locks: Lock[]; }; -} & Omit; +} & Omit; /** * Given an object type `T`, `Subset` represents an object which has the same shape as `T`, but with some keys (at any level of nesting) potentially absent. @@ -28,6 +28,7 @@ export type Subset = { [attr in keyof T]?: T[attr] extends object ? Subset : T[attr]; }; -export type RealtimeMessage = Omit & { +export type RealtimeInboundMessage = Omit & { + clientId: string; connectionId: string; }; diff --git a/test/cdn-bundle/resources/test.html b/test/cdn-bundle/resources/test.html index a2526d08..556d75f5 100644 --- a/test/cdn-bundle/resources/test.html +++ b/test/cdn-bundle/resources/test.html @@ -19,7 +19,7 @@ const key = await createSandboxAblyAPIKey(); // The next two statements are based on the example given in the "Using a CDN" section of the README. - const client = new Ably.Realtime.Promise({ + const client = new Ably.Realtime({ key, environment: 'sandbox', clientId: 'myClientId', diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts index b4072878..d0f1f388 100644 --- a/test/integration/integration.test.ts +++ b/test/integration/integration.test.ts @@ -1,11 +1,12 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createClients } from './utilities/setup.js'; import { LocationsEventMap } from '../../src/Locations.js'; -import { SpaceEventMap } from '../../src/Space.js'; +import Space, { SpaceEventMap } from '../../src/Space.js'; import { MembersEventMap } from '../../src/Members.js'; import { CursorsEventMap } from '../../src/Cursors.js'; import { nanoid } from 'nanoid'; import { LocksEventMap } from '../../src/Locks.js'; +import Spaces from '../../src/Spaces.js'; /* * These tests have one `describe` for each area of functionality, each of these `describe`s then containing multiple `it`s. @@ -146,11 +147,11 @@ describe( }); describe('cursors', () => { - let performerSpaces; - let observerSpaces; - let performerClientId; - let performerSpace; - let observerSpace; + let performerSpaces: Spaces; + let observerSpaces: Spaces; + let performerClientId: string; + let performerSpace: Space; + let observerSpace: Space; beforeAll(async () => { [{ spaces: performerSpaces, clientId: performerClientId }, { spaces: observerSpaces }] = await createClients({ @@ -186,7 +187,7 @@ describe( it('scenario 2.2: set cursor position', async () => { // Before calling `performerSpace.cursors.set()` below, we want to be sure that `performerSpace.cursors` has found out about the presence enter operations triggered by calling `performerSpace.cursors.set()` in scenario 2.1, and by calling `observerSpace.cursors.subscribe()` below, so that it doesn’t drop the cursor updates that we pass to `set()`. So here we set up a promise which will resolve once `performerSpace.cursors.channel` sees two clients (i.e. `performerSpaces` and `observerSpaces`) as present. const performerCursorsChannelObserverPresentPromise = new Promise((resolve) => { - const presence = performerSpace.cursors.channel.presence; + const presence = performerSpace.cursors.channel!.presence; const listener = async () => { const members = await presence.get(); if (members.length === 2) { @@ -214,9 +215,7 @@ describe( // To be sure that the `observerSpace.cursors.subscribe()` listener will receive the cursor positions sent by the calls to `performerSpace.cursors.set()` below, we need to know that the cursors channel attach operation triggered by calling `observerSpace.cursors.subscribe()` has completed. The `cursors.subscribe()` API does not currently provide any direct way for the user to know that this attach operation has completed, so here we do so by directly observing the channel. // // We should consider exposing the completion of the attach operation via the `cursors.subscribe()` API, the same way as ably-js exposes it through `presence.subscribe()`. I’m not convinced of the necessity though — not sure how useful it’d be for an average user, and we can work around it in tests (as I have here). - const observerCursorsChannelAttachedPromise = new Promise((resolve) => { - observerSpace.cursors.channel.whenState('attached', resolve); - }); + const observerCursorsChannelAttachedPromise = observerSpace.cursors.channel!.whenState('attached'); await Promise.all([performerCursorsChannelObserverPresentPromise, observerCursorsChannelAttachedPromise]); diff --git a/test/integration/utilities/setup.ts b/test/integration/utilities/setup.ts index 6400e953..8e8a9fb4 100644 --- a/test/integration/utilities/setup.ts +++ b/test/integration/utilities/setup.ts @@ -24,7 +24,7 @@ export async function createClients({ count }: { count: number }) { return Array.from({ length: count }, () => { const clientId = nanoid(); - const realtime = new Realtime.Promise({ + const realtime = new Realtime({ environment: 'sandbox', key: sandboxKey, clientId: clientId,