From d66efe7a11f9deb5e7ddb5f5345c9f13e024b08e Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:25:19 +0100 Subject: [PATCH 1/3] docs: update SuperDoc user prop with email, name, and image (#3503) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Chris Nicholas --- docs/pages/get-started/yjs-superdoc-javascript.mdx | 8 ++++++-- docs/pages/get-started/yjs-superdoc-nextjs.mdx | 6 +++++- docs/pages/get-started/yjs-superdoc-react.mdx | 6 +++++- docs/pages/get-started/yjs-superdoc-svelte.mdx | 8 ++++++-- docs/pages/get-started/yjs-superdoc-vuejs.mdx | 8 ++++++-- .../src/components/CollaborativeEditor.tsx | 6 +++++- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/docs/pages/get-started/yjs-superdoc-javascript.mdx b/docs/pages/get-started/yjs-superdoc-javascript.mdx index e005c9b8cc..a981c2b752 100644 --- a/docs/pages/get-started/yjs-superdoc-javascript.mdx +++ b/docs/pages/get-started/yjs-superdoc-javascript.mdx @@ -84,13 +84,17 @@ collaboration to your JavaScript application using the APIs from the const yDoc = yProvider.getYDoc(); function initSuperDoc() { + // Get the current user's info from Liveblocks + const { id, info } = room.getSelf(); + new SuperDoc({ selector: "#superdoc", toolbar: "#superdoc-toolbar", documentMode: "editing", user: { - name: "User " + Math.floor(Math.random() * 1000), - email: "user@example.com", + email: id, + name: info?.name ?? "Anonymous", + image: info?.avatar ?? undefined, }, modules: { // The collaboration contract is the same for every Yjs provider: diff --git a/docs/pages/get-started/yjs-superdoc-nextjs.mdx b/docs/pages/get-started/yjs-superdoc-nextjs.mdx index 42e462d6cf..31d9dc0b5d 100644 --- a/docs/pages/get-started/yjs-superdoc-nextjs.mdx +++ b/docs/pages/get-started/yjs-superdoc-nextjs.mdx @@ -151,7 +151,11 @@ collaboration to your Next.js application using the APIs from the diff --git a/docs/pages/get-started/yjs-superdoc-react.mdx b/docs/pages/get-started/yjs-superdoc-react.mdx index a906cc8660..3f12d29212 100644 --- a/docs/pages/get-started/yjs-superdoc-react.mdx +++ b/docs/pages/get-started/yjs-superdoc-react.mdx @@ -151,7 +151,11 @@ collaboration to your React application using the APIs from the diff --git a/docs/pages/get-started/yjs-superdoc-svelte.mdx b/docs/pages/get-started/yjs-superdoc-svelte.mdx index bc5e208d20..be01ca343b 100644 --- a/docs/pages/get-started/yjs-superdoc-svelte.mdx +++ b/docs/pages/get-started/yjs-superdoc-svelte.mdx @@ -76,13 +76,17 @@ collaboration to your Svelte application using the APIs from the let superdoc; function initSuperDoc() { + // Get the current user's info from Liveblocks + const { id, info } = room.getSelf(); + superdoc = new SuperDoc({ selector: "#superdoc", toolbar: "#superdoc-toolbar", documentMode: "editing", user: { - name: "User " + Math.floor(Math.random() * 1000), - email: "user@example.com", + email: id, + name: info?.name ?? "Anonymous", + image: info?.avatar ?? undefined, }, modules: { // The collaboration contract is the same for every Yjs provider: diff --git a/docs/pages/get-started/yjs-superdoc-vuejs.mdx b/docs/pages/get-started/yjs-superdoc-vuejs.mdx index 9aee151783..39722887c7 100644 --- a/docs/pages/get-started/yjs-superdoc-vuejs.mdx +++ b/docs/pages/get-started/yjs-superdoc-vuejs.mdx @@ -75,13 +75,17 @@ collaboration to your Vue.js application using the APIs from the let superdoc = null; function initSuperDoc() { + // Get the current user's info from Liveblocks + const { id, info } = room.getSelf(); + superdoc = new SuperDoc({ selector: "#superdoc", toolbar: "#superdoc-toolbar", documentMode: "editing", user: { - name: "User " + Math.floor(Math.random() * 1000), - email: "user@example.com", + email: id, + name: info?.name ?? "Anonymous", + image: info?.avatar ?? undefined, }, modules: { // The collaboration contract is the same for every Yjs provider: diff --git a/examples/nextjs-yjs-superdoc/src/components/CollaborativeEditor.tsx b/examples/nextjs-yjs-superdoc/src/components/CollaborativeEditor.tsx index ff4d4416ac..ef49f7d1e2 100644 --- a/examples/nextjs-yjs-superdoc/src/components/CollaborativeEditor.tsx +++ b/examples/nextjs-yjs-superdoc/src/components/CollaborativeEditor.tsx @@ -36,7 +36,11 @@ export function CollaborativeEditor() { From bad144c6724eb6bf5524a960785f7c888ba5443d Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:07:30 +0100 Subject: [PATCH 2/3] fix: style SuperDoc remote cursor label in example (#3504) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Chris Nicholas --- examples/nextjs-yjs-superdoc/src/globals.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/nextjs-yjs-superdoc/src/globals.css b/examples/nextjs-yjs-superdoc/src/globals.css index a3faa0acf8..902712f0f8 100644 --- a/examples/nextjs-yjs-superdoc/src/globals.css +++ b/examples/nextjs-yjs-superdoc/src/globals.css @@ -34,3 +34,8 @@ main { padding: 16px; background: #e8eaee; } + +.presentation-editor__remote-label { + margin: -5px 0 0 -1px; + border-bottom-left-radius: 0 !important; +} From 08e19177b525ef7803f06473202ae145b2fe20c8 Mon Sep 17 00:00:00 2001 From: Vincent Driessen Date: Tue, 2 Jun 2026 17:10:01 +0200 Subject: [PATCH 3/3] Make all IStorageDriver APIs synchronous (PR 1770) Original commit: 98670db33636ff738bc8e9514d162b0e84dfaa10 --- packages/liveblocks-server/src/MetadataDB.ts | 26 +- packages/liveblocks-server/src/Room.ts | 130 ++- packages/liveblocks-server/src/Storage.ts | 94 +- packages/liveblocks-server/src/YjsStorage.ts | 56 +- .../src/interfaces/IStorageDriver.ts | 66 +- .../src/plugins/InMemoryDriver.ts | 55 +- .../liveblocks-server/test-d/server.test-d.ts | 8 +- .../liveblocks-server/test/MetadataDB.test.ts | 98 +- .../test/plugins/_generateFullTestSuite.ts | 861 ++++++++---------- packages/liveblocks-server/test/room.test.ts | 28 +- .../storage/model-based/index.model.test.ts | 11 +- .../test/storage/model-based/storage-model.ts | 8 +- .../test/storage/push.test.ts | 16 +- .../liveblocks-server/test/storage/utils.ts | 2 +- tools/liveblocks-cli/src/dev-server/index.ts | 2 +- .../src/dev-server/routes/rest-api.ts | 8 +- .../test/devserver/rest-api/ydoc.test.ts | 2 +- .../test/plugins/_generateFullTestSuite.ts | 861 ++++++++---------- 18 files changed, 1077 insertions(+), 1255 deletions(-) diff --git a/packages/liveblocks-server/src/MetadataDB.ts b/packages/liveblocks-server/src/MetadataDB.ts index 8b60202c7e..42162cf9ef 100644 --- a/packages/liveblocks-server/src/MetadataDB.ts +++ b/packages/liveblocks-server/src/MetadataDB.ts @@ -15,18 +15,18 @@ * along with this program. If not, see . */ -import type { Awaitable, Json } from "@liveblocks/core"; +import type { Json } from "@liveblocks/core"; import type { Decoder } from "decoders"; import type { IStorageDriver } from "~/interfaces"; export interface MetadataDB { // Getter supports optional decoder - get(key: string): Promise; - get(decoder: Decoder, key: string): Promise; + get(key: string): Json | undefined; + get(decoder: Decoder, key: string): T | undefined; - put(key: string, value: Json): Awaitable; - delete(key: string): Awaitable; + put(key: string, value: Json): void; + delete(key: string): void; } /** @@ -34,19 +34,13 @@ export interface MetadataDB { * functionality, including type-safe reads. */ export function makeMetadataDB(driver: IStorageDriver): MetadataDB { - async function get(key: string): Promise; - async function get( - decoder: Decoder, - key: string - ): Promise; - async function get( - a1: string | Decoder, - a2?: string - ): Promise { + function get(key: string): Json | undefined; + function get(decoder: Decoder, key: string): T | undefined; + function get(a1: string | Decoder, a2?: string): T | Json | undefined { if (a2 === undefined) { - return await driver.get_meta(a1 as string); + return driver.get_meta(a1 as string); } else { - return (a1 as Decoder).value(await driver.get_meta(a2)); + return (a1 as Decoder).value(driver.get_meta(a2)); } } diff --git a/packages/liveblocks-server/src/Room.ts b/packages/liveblocks-server/src/Room.ts index 15e8dc8e47..4cdf335aa8 100644 --- a/packages/liveblocks-server/src/Room.ts +++ b/packages/liveblocks-server/src/Room.ts @@ -617,6 +617,7 @@ export class Room { * to call multiple times. After awaiting `room.load()` the Room is ready to * be used. */ + // XXXX We should now be able to remove this asyncness here too public async load(ctx?: C): Promise { if (this._loadData$ === null) { this._data = null; @@ -657,15 +658,13 @@ export class Room { * connection is established. If the socket is never established, this * unused Ticket will simply get garbage collected. */ - public async createTicket( - options?: CreateTicketOptions - ): Promise> { - const actor$ = options?.actor ?? this.getNextActor(); + public createTicket(options?: CreateTicketOptions): Ticket { + const actor = options?.actor ?? this.getNextActor(); const sessionKey = nanoid() as SessionKey; const info = options?.info; const ticket: Ticket = { version: options?.version ?? HIGHEST_PROTOCOL_VERSION, - actor: await actor$, + actor, sessionKey, meta: options?.meta, publicMeta: options?.publicMeta, @@ -680,10 +679,11 @@ export class Room { return ticket; } - public async createBackendSession_experimental(): Promise< - [session: BackendSession, outgoingMessages: jstring[]] - > { - const ticket = (await this.createTicket()) as Ticket; + public createBackendSession_experimental(): [ + session: BackendSession, + outgoingMessages: jstring[], + ] { + const ticket = this.createTicket() as Ticket; const capturedServerMsgs: jstring[] = []; const stub = { send: (data) => { @@ -1069,7 +1069,7 @@ export class Room { * Upsert a leased session. Creates a new session if it doesn't exist (or is expired), * or updates an existing session with merged presence. */ - public async upsertLeasedSession( + public upsertLeasedSession( sessionId: string, presence: JsonObject, ttl: number, @@ -1081,18 +1081,18 @@ export class Room { "Pass a `defer` callback to upsertLeasedSession() to collect async side effects." ); } - ): Promise { - const existingSession = await this.driver.get_leased_session(sessionId); + ): void { + const existingSession = this.driver.get_leased_session(sessionId); const isExpired = existingSession !== undefined && isLeasedSessionExpired(existingSession); if (isExpired) { - await this.deleteLeasedSession(existingSession, ctx, defer); + this.deleteLeasedSession(existingSession, ctx, defer); } if (existingSession === undefined || isExpired) { // Creating new session (or was expired) - const actorId = await this.getNextActor(); + const actorId = this.getNextActor(); const now = Date.now(); const session: LeasedSession = { sessionId, @@ -1103,7 +1103,7 @@ export class Room { actorId, }; - await this.driver.put_leased_session(session); + this.driver.put_leased_session(session); // Broadcast USER_JOINED to all existing sessions this.sendToAll( @@ -1143,7 +1143,7 @@ export class Room { ttl, }; - await this.driver.put_leased_session(updatedSession); + this.driver.put_leased_session(updatedSession); // Broadcast UPDATE_PRESENCE WITHOUT targetActor to all sessions (patch) this.sendToAll( @@ -1172,7 +1172,7 @@ export class Room { } ): Promise { await this.load(ctx); - const sessions = await this.driver.list_leased_sessions(); + const sessions = this.driver.list_leased_sessions(); const validSessions: LeasedSession[] = []; const toDelete: LeasedSession[] = []; for (const [_, session] of sessions) { @@ -1184,7 +1184,7 @@ export class Room { } for (const session of toDelete) { - await this.deleteLeasedSession(session, ctx, defer); + this.deleteLeasedSession(session, ctx, defer); } return validSessions; @@ -1193,7 +1193,7 @@ export class Room { /** * Delete a server session and broadcast USER_LEFT to all sessions. */ - public async deleteLeasedSession( + public deleteLeasedSession( session: LeasedSession, ctx?: C, defer: (promise: Promise) => void = () => { @@ -1202,7 +1202,7 @@ export class Room { "Pass a `defer` callback to deleteLeasedSession() to collect async side effects." ); } - ): Promise { + ): void { // Broadcast USER_LEFT to all sessions this.sendToAll( { @@ -1212,7 +1212,7 @@ export class Room { ctx, defer ); - await this.driver.delete_leased_session(session.sessionId); + this.driver.delete_leased_session(session.sessionId); } /** @@ -1228,9 +1228,9 @@ export class Room { } ): Promise { await this.load(ctx); - const sessions = await this.driver.list_leased_sessions(); + const sessions = this.driver.list_leased_sessions(); for (const [_, session] of sessions) { - await this.deleteLeasedSession(session, ctx, defer); + this.deleteLeasedSession(session, ctx, defer); } } @@ -1241,59 +1241,56 @@ export class Room { /** * List feeds with pagination and filtering. */ - public async listFeeds(options?: ListFeedsOptions): Promise { - return await this.driver.list_feeds(options); + public listFeeds(options?: ListFeedsOptions): ListFeedsResult { + return this.driver.list_feeds(options); } /** * Get a specific feed by feed ID. */ - public async getFeed(feedId: string): Promise { - return await this.driver.get_feed(feedId); + public getFeed(feedId: string): Feed | undefined { + return this.driver.get_feed(feedId); } /** * Create a new feed. * If timestamp is not provided, current server time is used. */ - public async createFeed( + public createFeed( feed: Omit & { timestamp?: number } - ): Promise { + ): Feed { const now = feed.timestamp ?? Date.now(); const fullFeed: Feed = { ...feed, createdAt: now, updatedAt: now, }; - await this.driver.create_feed(fullFeed); + this.driver.create_feed(fullFeed); return fullFeed; } /** * Update a feed's metadata. */ - public async updateFeedMetadata( - feedId: string, - metadata: Json - ): Promise { - await this.driver.update_feed_metadata(feedId, metadata); + public updateFeedMetadata(feedId: string, metadata: Json): void { + this.driver.update_feed_metadata(feedId, metadata); } /** * Delete a feed. */ - public async deleteFeed(feedId: string): Promise { - await this.driver.delete_feed(feedId); + public deleteFeed(feedId: string): void { + this.driver.delete_feed(feedId); } /** * List feed messages for a feed with pagination. */ - public async listFeedMessages( + public listFeedMessages( feedId: string, options?: ListFeedMessagesOptions - ): Promise { - return await this.driver.list_feed_messages(feedId, options); + ): ListFeedMessagesResult { + return this.driver.list_feed_messages(feedId, options); } /** @@ -1301,11 +1298,11 @@ export class Room { * If message id is not provided, a unique ID is automatically generated. * If timestamp is not provided, current server time is used. */ - public async addFeedMessage( + public addFeedMessage( feedId: string, message: Omit & Partial> & { timestamp?: number } - ): Promise { + ): FeedMessage { const now = message.timestamp ?? Date.now(); const fullMessage: FeedMessage = { id: message.id ?? nanoid(), @@ -1313,7 +1310,7 @@ export class Room { updatedAt: now, data: message.data, }; - await this.driver.add_feed_message(feedId, fullMessage); + this.driver.add_feed_message(feedId, fullMessage); return fullMessage; } @@ -1321,13 +1318,13 @@ export class Room { * Update a feed message's data. * Returns the updated message. */ - public async updateFeedMessage( + public updateFeedMessage( feedId: string, messageId: string, data: Json, timestamp?: number - ): Promise { - return await this.driver.update_feed_message( + ): FeedMessage { + return this.driver.update_feed_message( feedId, messageId, data, @@ -1338,11 +1335,8 @@ export class Room { /** * Delete a feed message. */ - public async deleteFeedMessage( - feedId: string, - messageId: string - ): Promise { - await this.driver.delete_feed_message(feedId, messageId); + public deleteFeedMessage(feedId: string, messageId: string): void { + this.driver.delete_feed_message(feedId, messageId); } /** @@ -1409,6 +1403,13 @@ export class Room { } } + /** + * Returns a new, unique, actor ID. + */ + public getNextActor(): ActorID { + return this.driver.next_actor() as ActorID; + } + // ------------------------------------------------------------------------------------ // Private APIs // ------------------------------------------------------------------------------------ @@ -1442,13 +1443,6 @@ export class Room { await this.hooks.onRoomDidLoad?.(ctx); } - /** - * Returns a new, unique, actor ID. - */ - private async getNextActor(): Promise { - return (await this.driver.next_actor()) as ActorID; - } - /** * Iterates over all *other* Sessions and their session keys. */ @@ -1694,7 +1688,7 @@ export class Room { // semantics to provide snapshot isolation. this.driver.bump_storage_version?.(); - const result = await this.storage.applyOps(msg.ops); + const result = this.storage.applyOps(msg.ops); const opsToForward: ServerWireOp[] = result.flatMap((r) => r.action === "accepted" ? [r.op] : [] @@ -1807,7 +1801,7 @@ export class Room { // Feed messages case FeedMsgCode.FETCH_FEEDS: { const fetchMsg = msg; - const [result, err] = await tryCatch( + const [result, err] = await tryCatch(() => this.listFeeds({ cursor: fetchMsg.cursor, since: fetchMsg.since, @@ -1830,7 +1824,7 @@ export class Room { case FeedMsgCode.FETCH_FEED_MESSAGES: { const fetchMsg = msg; - const [result, err] = await tryCatch( + const [result, err] = await tryCatch(() => this.listFeedMessages(fetchMsg.feedId, { cursor: fetchMsg.cursor, since: fetchMsg.since, @@ -1853,7 +1847,7 @@ export class Room { case FeedMsgCode.ADD_FEED: { const addMsg = msg; - const [feed, err] = await tryCatch( + const [feed, err] = await tryCatch(() => this.createFeed({ feedId: addMsg.feedId, metadata: (addMsg.metadata as Json) ?? {}, @@ -1874,14 +1868,14 @@ export class Room { case FeedMsgCode.UPDATE_FEED: { const updateMsg = msg; - const [, metaErr] = await tryCatch( + const [, metaErr] = await tryCatch(() => this.updateFeedMetadata(updateMsg.feedId, updateMsg.metadata as Json) ); if (metaErr) { replyImmediately(feedFailureServerMsg(updateMsg.requestId, metaErr)); break; } - const feed = await this.getFeed(updateMsg.feedId); + const feed = this.getFeed(updateMsg.feedId); if (!feed) { replyImmediately( feedRequestFailed( @@ -1902,7 +1896,7 @@ export class Room { case FeedMsgCode.DELETE_FEED: { const deleteMsg = msg; - const [, err] = await tryCatch(this.deleteFeed(deleteMsg.feedId)); + const [, err] = await tryCatch(() => this.deleteFeed(deleteMsg.feedId)); if (err) { replyImmediately(feedFailureServerMsg(deleteMsg.requestId, err)); break; @@ -1918,7 +1912,7 @@ export class Room { case FeedMsgCode.ADD_FEED_MESSAGE: { const addMsg = msg; - const [message, err] = await tryCatch( + const [message, err] = await tryCatch(() => this.addFeedMessage(addMsg.feedId, { data: addMsg.data as Json, id: addMsg.id, @@ -1940,7 +1934,7 @@ export class Room { case FeedMsgCode.UPDATE_FEED_MESSAGE: { const updateMsg = msg; - const [message, err] = await tryCatch( + const [message, err] = await tryCatch(() => this.updateFeedMessage( updateMsg.feedId, updateMsg.messageId, @@ -1963,7 +1957,7 @@ export class Room { case FeedMsgCode.DELETE_FEED_MESSAGE: { const deleteMsg = msg; - const [, err] = await tryCatch( + const [, err] = await tryCatch(() => this.deleteFeedMessage(deleteMsg.feedId, deleteMsg.messageId) ); if (err) { diff --git a/packages/liveblocks-server/src/Storage.ts b/packages/liveblocks-server/src/Storage.ts index 19990c26f0..bd98e17f0b 100644 --- a/packages/liveblocks-server/src/Storage.ts +++ b/packages/liveblocks-server/src/Storage.ts @@ -15,11 +15,7 @@ * along with this program. If not, see . */ -import type { - Awaitable, - SerializedChild, - SerializedCrdt, -} from "@liveblocks/core"; +import type { SerializedChild, SerializedCrdt } from "@liveblocks/core"; import { asPos, assertNever, @@ -123,7 +119,7 @@ export class Storage { } // REFACTOR NOTE: Eventually raw_iter_nodes has to be removed here - raw_iter_nodes(): Awaitable> { + raw_iter_nodes(): Iterable<[string, SerializedCrdt]> { return this.coreDriver.raw_iter_nodes(); } @@ -133,8 +129,11 @@ export class Storage { * Storage tree, and special keys where we store usage metrics, or room * metadata. */ + // XXXX Now that the driver has become fully sync, we no longer need this + // .load() construct. We should be able to refactor it away. + // eslint-disable-next-line @typescript-eslint/require-await async load(logger: Logger): Promise { - this._loadedDriver = await this.coreDriver.load_nodes_api(logger); + this._loadedDriver = this.coreDriver.load_nodes_api(logger); } unload(): void { @@ -144,10 +143,10 @@ export class Storage { /** * Applies a batch of Ops. */ - async applyOps(ops: ClientWireOp[]): Promise { + applyOps(ops: ClientWireOp[]): ApplyOpResult[] { const results: ApplyOpResult[] = []; for (const op of ops) { - results.push(await this.applyOp(op)); + results.push(this.applyOp(op)); } return results; } @@ -159,25 +158,25 @@ export class Storage { /** * Applies a single Op. */ - private async applyOp(op: ClientWireOp): Promise { + private applyOp(op: ClientWireOp): ApplyOpResult { switch (op.type) { case OpCode.CREATE_LIST: case OpCode.CREATE_MAP: case OpCode.CREATE_REGISTER: case OpCode.CREATE_OBJECT: - return await this.applyCreateOp(op); + return this.applyCreateOp(op); case OpCode.UPDATE_OBJECT: - return await this.applyUpdateObjectOp(op); + return this.applyUpdateObjectOp(op); case OpCode.SET_PARENT_KEY: - return await this.applySetParentKeyOp(op); + return this.applySetParentKeyOp(op); case OpCode.DELETE_OBJECT_KEY: - return await this.applyDeleteObjectKeyOp(op); + return this.applyDeleteObjectKeyOp(op); case OpCode.DELETE_CRDT: - return await this.applyDeleteCrdtOp(op); + return this.applyDeleteCrdtOp(op); // istanbul ignore next default: @@ -189,7 +188,7 @@ export class Storage { } } - private async applyCreateOp(op: CreateOp & HasOpId): Promise { + private applyCreateOp(op: CreateOp & HasOpId): ApplyOpResult { if (this.loadedDriver.has_node(op.id)) { // Node already exists, the operation is ignored return ignore(op); @@ -217,7 +216,7 @@ export class Storage { case CrdtType.MAP: // Children of maps and objects require no special needs - await this.loadedDriver.set_child(op.id, node, true); + this.loadedDriver.set_child(op.id, node, true); return accept(op); case CrdtType.LIST: @@ -235,10 +234,10 @@ export class Storage { } } - private async createChildAsListItem( + private createChildAsListItem( op: CreateOp & HasOpId, node: SerializedChild - ): Promise { + ): ApplyOpResult { // The default intent, when not explicitly provided, is to insert, not set, // into the list. const intent: "insert" | "set" | "push" = op.intent ?? "insert"; @@ -247,15 +246,11 @@ export class Storage { if (intent === "insert") { // Insert at the client's preferred position, resolving any collision to a // nearby free slot. - return this.acceptAndFix( - op, - node, - await this.insertIntoList(op.id, node) - ); + return this.acceptAndFix(op, node, this.insertIntoList(op.id, node)); } else if (intent === "push") { // Server-authoritative append: place the node after the authoritative // end of the list (see `appendToList`), regardless of the client's preference. - return this.acceptAndFix(op, node, await this.appendToList(op.id, node)); + return this.acceptAndFix(op, node, this.appendToList(op.id, node)); } else if (intent === "set") { let fix: FixOp | undefined; @@ -277,7 +272,7 @@ export class Storage { : undefined; if (deletedId !== undefined) { - await this.loadedDriver.delete_node(deletedId); + this.loadedDriver.delete_node(deletedId); } const prevItemId = this.loadedDriver.get_child_at( @@ -294,7 +289,7 @@ export class Storage { }; } - await this.loadedDriver.set_child(op.id, node, true); + this.loadedDriver.set_child(op.id, node, true); return accept(op, fix); } else { @@ -321,31 +316,25 @@ export class Storage { return accept(op); } - private async applyDeleteObjectKeyOp( + private applyDeleteObjectKeyOp( op: DeleteObjectKeyOp & HasOpId - ): Promise { - await this.loadedDriver.delete_child_key(op.id, op.key); + ): ApplyOpResult { + this.loadedDriver.delete_child_key(op.id, op.key); return accept(op); } - private async applyUpdateObjectOp( - op: UpdateObjectOp & HasOpId - ): Promise { - await this.loadedDriver.set_object_data(op.id, op.data, true); + private applyUpdateObjectOp(op: UpdateObjectOp & HasOpId): ApplyOpResult { + this.loadedDriver.set_object_data(op.id, op.data, true); return accept(op); } - private async applyDeleteCrdtOp( - op: DeleteCrdtOp & HasOpId - ): Promise { - await this.loadedDriver.delete_node(op.id); + private applyDeleteCrdtOp(op: DeleteCrdtOp & HasOpId): ApplyOpResult { + this.loadedDriver.delete_node(op.id); return accept(op); } - private async applySetParentKeyOp( - op: SetParentKeyOp & HasOpId - ): Promise { - const newPosition = await this.moveToPosInList(op.id, op.parentKey); + private applySetParentKeyOp(op: SetParentKeyOp & HasOpId): ApplyOpResult { + const newPosition = this.moveToPosInList(op.id, op.parentKey); if (newPosition === undefined) { // The operation got rejected because it didn't make sense, ignore it return ignore(op); @@ -376,16 +365,13 @@ export class Storage { * * Returns the key that was used for the insertion. */ - private async insertIntoList( - id: string, - node: SerializedChild - ): Promise { + private insertIntoList(id: string, node: SerializedChild): string { // First, compute the key to use to insert this node const key = this.findFreeListPosition(node.parentId, asPos(node.parentKey)); if (key !== node.parentKey) { node = { ...node, parentKey: key }; } - await this.loadedDriver.set_child(id, node); + this.loadedDriver.set_child(id, node); return node.parentKey; } @@ -399,17 +385,14 @@ export class Storage { * * Returns the final key that was used for the insertion. */ - private async appendToList( - id: string, - node: SerializedChild - ): Promise { + private appendToList(id: string, node: SerializedChild): string { const lastPos = this.loadedDriver.get_last_sibling(node.parentId); const preferredPos = asPos(node.parentKey); const finalKey = lastPos === undefined || preferredPos > lastPos ? preferredPos : makePosition(lastPos); - await this.loadedDriver.set_child( + this.loadedDriver.set_child( id, finalKey !== node.parentKey ? { ...node, parentKey: finalKey } : node ); @@ -428,10 +411,7 @@ export class Storage { * Will return `undefined` if this action could not be interpreted. Will be * a no-op for non-list items. */ - private async moveToPosInList( - id: string, - targetKey: string - ): Promise { + private moveToPosInList(id: string, targetKey: string): string | undefined { const node = this.loadedDriver.get_node(id); if (node?.parentId === undefined) { return; /* reject */ @@ -450,7 +430,7 @@ export class Storage { // First, compute the key to use to insert this node const key = this.findFreeListPosition(node.parentId, asPos(targetKey)); if (key !== node.parentKey) { - await this.loadedDriver.move_sibling(id, key); + this.loadedDriver.move_sibling(id, key); } return key; } diff --git a/packages/liveblocks-server/src/YjsStorage.ts b/packages/liveblocks-server/src/YjsStorage.ts index 57067d6d37..b8a48b9cf9 100644 --- a/packages/liveblocks-server/src/YjsStorage.ts +++ b/packages/liveblocks-server/src/YjsStorage.ts @@ -36,7 +36,7 @@ export class YjsStorage { private readonly doc: Y.Doc = new Y.Doc(); // the root document private readonly lastSnapshotById = new Map(); - private readonly initPromisesById: Map> = new Map(); + private readonly initPromisesById: Map = new Map(); private readonly storedKeysById: Map = new Map(); constructor( @@ -56,9 +56,9 @@ export class YjsStorage { // Public API // ------------------------------------------------------------------------------------ + // eslint-disable-next-line @typescript-eslint/require-await public async getYDoc(logger: Logger, docId: YDocId): Promise { - const doc = await this.loadDocByIdIfNotAlreadyLoaded(logger, docId); - return doc; + return this.loadDocByIdIfNotAlreadyLoaded(logger, docId); } /** @@ -170,7 +170,7 @@ export class YjsStorage { // Check the snapshot before/after to see if the update had an effect const updated = !Y.equalSnapshots(beforeSnapshot, afterSnapshot); if (updated) { - await this.handleYDocUpdate(doc, updateAsU8, isV2); + this.handleYDocUpdate(doc, updateAsU8, isV2); } return { @@ -186,25 +186,24 @@ export class YjsStorage { } } - public loadDocByIdIfNotAlreadyLoaded( - logger: Logger, - docId: YDocId - ): Promise { - let loaded$ = this.initPromisesById.get(docId); + public loadDocByIdIfNotAlreadyLoaded(logger: Logger, docId: YDocId): Y.Doc { + let loaded = this.initPromisesById.get(docId); let doc = docId === ROOT_YDOC_ID ? this.doc : this.findYSubdocByGuid(docId); if (!doc) { // An API call can load a subdoc without the root doc (this._doc) being loaded, we account for that by just instantiating a doc here. doc = new Y.Doc(); } - if (loaded$ === undefined) { - loaded$ = this._loadYDocFromDurableStorage(logger, doc, docId); - this.initPromisesById.set(docId, loaded$); + if (loaded === undefined) { + loaded = this._loadYDocFromDurableStorage(logger, doc, docId); + this.initPromisesById.set(docId, loaded); } - return loaded$; + return loaded; } + // XXXX We should now be able to remove this asyncness here too + // eslint-disable-next-line @typescript-eslint/require-await public async load(logger: Logger): Promise { - await this.loadDocByIdIfNotAlreadyLoaded(logger, ROOT_YDOC_ID); + this.loadDocByIdIfNotAlreadyLoaded(logger, ROOT_YDOC_ID); } /** @@ -246,27 +245,25 @@ export class YjsStorage { } // compact the updates into a single update and write it to the durable storage - private _compactYJSUpdates = async ( + private _compactYJSUpdates = ( doc: Y.Doc, docId: YDocId, storedKeys: string[] - ): Promise => { + ): void => { const compactedUpdate = Y.encodeStateAsUpdate(doc); const newKey = nanoid(); - await this.driver.write_y_updates(docId, newKey, compactedUpdate); + this.driver.write_y_updates(docId, newKey, compactedUpdate); // Todo: after we kill the kv driver, we should have an overwrite method in the driverso we don't need to delete and write - await this.driver.delete_y_updates(docId, storedKeys); + this.driver.delete_y_updates(docId, storedKeys); this.storedKeysById.set(docId, [newKey]); }; - private _loadYDocFromDurableStorage = async ( + private _loadYDocFromDurableStorage = ( _logger: Logger, doc: Y.Doc, docId: YDocId - ): Promise => { - const docUpdates = Object.fromEntries( - await this.driver.iter_y_updates(docId) - ); + ): Y.Doc => { + const docUpdates = Object.fromEntries(this.driver.iter_y_updates(docId)); const updates = Object.values(docUpdates); const beforeSize = updates.reduce((acc, update) => acc + update.length, 0); const newupdate = Y.mergeUpdates(updates); @@ -277,7 +274,7 @@ export class YjsStorage { this.shouldCompactByKeyCount(storedKeys) || this.shouldCompactBySize(beforeSize, newupdate.length) ) { - await this._compactYJSUpdates(doc, docId, storedKeys); + this._compactYJSUpdates(doc, docId, storedKeys); } else { this.storedKeysById.set(docId, storedKeys); } @@ -310,21 +307,22 @@ export class YjsStorage { } // gets a subdoc, it will be loaded if not already loaded + // eslint-disable-next-line @typescript-eslint/require-await private async getYSubdoc(logger: Logger, guid: Guid): Promise { const subdoc = this.findYSubdocByGuid(guid); if (!subdoc) { return null; } - await this.loadDocByIdIfNotAlreadyLoaded(logger, guid); + this.loadDocByIdIfNotAlreadyLoaded(logger, guid); return subdoc; } // When the YJS doc changes, update it in durable storage - private async handleYDocUpdate( + private handleYDocUpdate( doc: Y.Doc, update: Uint8Array, isV2: boolean | undefined - ): Promise { + ): void { // Todo: in the future, we should pass this detail to the driver so it can store the version as metadata // this will be easy for sqlite drivers, but not for the KV driver const v1update = isV2 ? Y.convertUpdateFormatV2ToV1(update) : update; @@ -335,13 +333,13 @@ export class YjsStorage { // Every UPDATE_COUNT_THRESHOLD updates, we compact the updates if (this.shouldCompactByKeyCount(storedKeys)) { - await this._compactYJSUpdates(doc, docId, storedKeys || []); + this._compactYJSUpdates(doc, docId, storedKeys || []); return; } // the whole concept of storing keys is not needed when we kill the kv driver, all of this stuff is trivial in sqlite const newKey = nanoid(); - await this.driver.write_y_updates(docId, newKey, v1update); + this.driver.write_y_updates(docId, newKey, v1update); // update the stored keys, which we'll need for compaction. if (!storedKeys) { diff --git a/packages/liveblocks-server/src/interfaces/IStorageDriver.ts b/packages/liveblocks-server/src/interfaces/IStorageDriver.ts index e5a195881b..6031f10c2f 100644 --- a/packages/liveblocks-server/src/interfaces/IStorageDriver.ts +++ b/packages/liveblocks-server/src/interfaces/IStorageDriver.ts @@ -16,7 +16,6 @@ */ import type { - Awaitable, CompactNode, Json, JsonObject, @@ -189,20 +188,20 @@ export interface IStorageDriverNodeAPI { * If allowOverwrite=true: replace any existing node at this id, deleting its * entire subtree if it has children. */ - set_child(id: string, node: SerializedChild, allowOverwrite?: boolean): Awaitable; // prettier-ignore + set_child(id: string, node: SerializedChild, allowOverwrite?: boolean): void; /** * Change a node's parentKey, effectively repositioning the node within its * parent. The new position must be free. * Throw if another node already occupies (parentId, newPos). */ - move_sibling(id: string, newPos: Pos): Awaitable; + move_sibling(id: string, newPos: Pos): void; /** * Delete a node and its entire subtree recursively. * Ignore if id="root" (root is immortal). */ - delete_node(id: string): Awaitable; + delete_node(id: string): void; /** * Delete a key from node `id`. Handle two cases: @@ -213,7 +212,7 @@ export interface IStorageDriverNodeAPI { * * No-op if neither applies or if the node doesn't exist. */ - delete_child_key(id: string, key: string): Awaitable; + delete_child_key(id: string, key: string): void; /** * Replace the data object of an OBJECT node. @@ -223,7 +222,7 @@ export interface IStorageDriverNodeAPI { * If allowOverwrite=true: first delete any conflicting children (and their * entire subtrees), then set the data. */ - set_object_data(id: string, data: JsonObject, allowOverwrite?: boolean): Awaitable; // prettier-ignore + set_object_data(id: string, data: JsonObject, allowOverwrite?: boolean): void; /** * Return a readable snapshot of the storage tree. @@ -251,7 +250,12 @@ export interface IStorageDriver { * After DANGEROUSLY_reset_nodes(), any previously-loaded instance is * invalid—must call this again to get a fresh one. */ - load_nodes_api(logger: Logger): Awaitable; + // XXXX Now that this method has become sync, we could rip it out entirely and + // just move the methods from the IStorageDriverNodeAPI interface up to + // IStorageDriver. The main reason this interface existed was to support the + // KV case which was asynchronous. None of our remaining implementations are + // async any longer, so... YAGNI! + load_nodes_api(logger: Logger): IStorageDriverNodeAPI; /** * Delete all CRDT nodes and replace them with the given document. @@ -260,14 +264,14 @@ export interface IStorageDriver { * * Pass `{ liveblocksType: "LiveObject", data: {} }` to reset to an empty root. */ - DANGEROUSLY_reset_nodes(doc: PlainLsonObject): Awaitable; + DANGEROUSLY_reset_nodes(doc: PlainLsonObject): void; /** * Read raw node data directly from storage. * * @internal Test-only API */ - raw_iter_nodes(): Awaitable>; + raw_iter_nodes(): Iterable<[string, SerializedCrdt]>; // --------------------------------------------------------------------------- // Metadata APIs (key-value store, isolated from nodes) @@ -276,17 +280,17 @@ export interface IStorageDriver { /** * Return the value for `key`, or undefined if not set. */ - get_meta(key: string): Awaitable; + get_meta(key: string): Json | undefined; /** * Store `value` under `key`. Overwrite any existing value. */ - put_meta(key: string, value: Json): Awaitable; + put_meta(key: string, value: Json): void; /** * Delete the value under `key`. No-op if not set. */ - delete_meta(key: string): Awaitable; + delete_meta(key: string): void; // --------------------------------------------------------------------------- // System APIs @@ -296,7 +300,7 @@ export interface IStorageDriver { * Return a unique actor ID. Each call must return a distinct integer ≥ 0. * Concurrent calls must never return duplicates. */ - next_actor(): Awaitable; + next_actor(): number; /** * If defined, called once before each storage mutation batch. Can be used by @@ -311,23 +315,23 @@ export interface IStorageDriver { /** * Return all Yjs updates for docId as [key, data] pairs. Return empty if none. */ - iter_y_updates(docId: YDocId): Awaitable>; // prettier-ignore + iter_y_updates(docId: YDocId): Iterable<[string, Uint8Array]>; /** * Store a Yjs update under (docId, key). Overwrite if key exists. */ - write_y_updates(docId: YDocId, key: string, data: Uint8Array): Awaitable; // prettier-ignore + write_y_updates(docId: YDocId, key: string, data: Uint8Array): void; /** * Delete the specified keys for docId. */ - delete_y_updates(docId: YDocId, keys: string[]): Awaitable; // prettier-ignore + delete_y_updates(docId: YDocId, keys: string[]): void; /** * Delete ALL Yjs updates across ALL documents. * @private Test-only: never use in production. */ - DANGEROUSLY_wipe_all_y_updates(): Awaitable; + DANGEROUSLY_wipe_all_y_updates(): void; // --------------------------------------------------------------------------- // Leased Session APIs (key-value store for leased sessions) @@ -338,28 +342,26 @@ export interface IStorageDriver { * Note: Does NOT filter by expiration - returns all stored sessions. * Expiration logic is handled at the Room.ts level. */ - list_leased_sessions(): Awaitable< - Iterable<[sessionId: string, session: LeasedSession]> - >; + list_leased_sessions(): Iterable<[sessionId: string, session: LeasedSession]>; /** * Get a specific leased session by session ID. * Note: Does NOT check expiration - returns the stored session if it exists. * Expiration logic is handled at the Room.ts level. */ - get_leased_session(sessionId: string): Awaitable; + get_leased_session(sessionId: string): LeasedSession | undefined; /** * Create or update a leased session. * Note: This is a full replace operation - the caller is responsible for * merging/patching presence if needed. */ - put_leased_session(session: LeasedSession): Awaitable; + put_leased_session(session: LeasedSession): void; /** * Delete a leased session by session ID. */ - delete_leased_session(sessionId: string): Awaitable; + delete_leased_session(sessionId: string): void; /** * Return the number of storage rows written since last call to this method, @@ -375,7 +377,7 @@ export interface IStorageDriver { * List feeds with pagination, filtering, and metadata querying. * Feeds are sorted by createdAt descending (newest first). */ - list_feeds(options?: ListFeedsOptions): Awaitable; + list_feeds(options?: ListFeedsOptions): ListFeedsResult; /** * Get a specific feed by feed ID. @@ -383,26 +385,26 @@ export interface IStorageDriver { * Use list_feed_messages to retrieve messages for this feed. * Returns undefined if the feed doesn't exist. */ - get_feed(feedId: string): Awaitable; + get_feed(feedId: string): Feed | undefined; /** * Create a new feed. * If feedId already exists, throws an error. */ - create_feed(feed: Feed): Awaitable; + create_feed(feed: Feed): void; /** * Update a feed's metadata. * The feed must exist, otherwise throws an error. */ - update_feed_metadata(feedId: string, metadata: Json): Awaitable; + update_feed_metadata(feedId: string, metadata: Json): void; /** * Delete a feed by feed ID. * Also deletes all messages associated with the feed (via CASCADE). * No-op if feed doesn't exist. */ - delete_feed(feedId: string): Awaitable; + delete_feed(feedId: string): void; /** * List feed messages for a feed with pagination. @@ -411,14 +413,14 @@ export interface IStorageDriver { list_feed_messages( feedId: string, options?: ListFeedMessagesOptions - ): Awaitable; + ): ListFeedMessagesResult; /** * Add a message to a feed. * The message must have id, createdAt, and updatedAt already set (handled by Room layer). * The feed must exist, otherwise throws an error. */ - add_feed_message(feedId: string, message: FeedMessage): Awaitable; + add_feed_message(feedId: string, message: FeedMessage): void; /** * Update a feed message's data. @@ -432,11 +434,11 @@ export interface IStorageDriver { messageId: string, data: Json, timestamp?: number - ): Awaitable; + ): FeedMessage; /** * Delete a feed message. * The feed and message must exist, otherwise throws an error. */ - delete_feed_message(feedId: string, messageId: string): Awaitable; + delete_feed_message(feedId: string, messageId: string): void; } diff --git a/packages/liveblocks-server/src/plugins/InMemoryDriver.ts b/packages/liveblocks-server/src/plugins/InMemoryDriver.ts index a3da51221e..de6ea7cac0 100644 --- a/packages/liveblocks-server/src/plugins/InMemoryDriver.ts +++ b/packages/liveblocks-server/src/plugins/InMemoryDriver.ts @@ -16,7 +16,6 @@ */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/require-await */ import type { CompactNode, Json, @@ -170,29 +169,29 @@ export class InMemoryDriver implements IStorageDriver { } } - async get_meta(key: string) { + get_meta(key: string) { return this._metadb.get(key); } - async put_meta(key: string, value: Json) { + put_meta(key: string, value: Json) { this._metadb.set(key, value); } - async delete_meta(key: string) { + delete_meta(key: string) { this._metadb.delete(key); } - async list_leased_sessions() { + list_leased_sessions() { return this._leasedSessions.entries(); } - async get_leased_session(sessionId: string) { + get_leased_session(sessionId: string) { return this._leasedSessions.get(sessionId); } - async put_leased_session(session: LeasedSession) { + put_leased_session(session: LeasedSession) { this._leasedSessions.set(session.sessionId, session); } - async delete_leased_session(sessionId: string) { + delete_leased_session(sessionId: string) { this._leasedSessions.delete(sessionId); } @@ -204,7 +203,7 @@ export class InMemoryDriver implements IStorageDriver { // Feed APIs // --------------------------------------------------------------------------- - async list_feeds(options?: ListFeedsOptions): Promise { + list_feeds(options?: ListFeedsOptions): ListFeedsResult { const limit = Math.min(options?.limit ?? 20, 100); const since = options?.since; const cursor = options?.cursor; @@ -285,7 +284,7 @@ export class InMemoryDriver implements IStorageDriver { return { feeds, nextCursor }; } - async get_feed(feedId: string): Promise { + get_feed(feedId: string): Feed | undefined { const feed = this._feeds.get(feedId); if (feed === undefined) { return undefined; @@ -294,7 +293,7 @@ export class InMemoryDriver implements IStorageDriver { return feed; } - async create_feed(feed: Feed): Promise { + create_feed(feed: Feed): void { // Check if feed already exists if (this._feeds.has(feed.feedId)) { throw new Error(`Feed ${feed.feedId} already exists`); @@ -309,7 +308,7 @@ export class InMemoryDriver implements IStorageDriver { }); } - async update_feed_metadata(feedId: string, metadata: Json): Promise { + update_feed_metadata(feedId: string, metadata: Json): void { const existing = this._feeds.get(feedId); if (existing === undefined) { throw new Error(`Feed ${feedId} not found`); @@ -321,7 +320,7 @@ export class InMemoryDriver implements IStorageDriver { }); } - async delete_feed(feedId: string): Promise { + delete_feed(feedId: string): void { // Delete all messages for this feed const messageKeys: string[] = []; for (const [key] of this._feedMessages.entries()) { @@ -337,10 +336,10 @@ export class InMemoryDriver implements IStorageDriver { this._feeds.delete(feedId); } - async list_feed_messages( + list_feed_messages( feedId: string, options?: ListFeedMessagesOptions - ): Promise { + ): ListFeedMessagesResult { const limit = Math.min(options?.limit ?? 20, 100); const since = options?.since; const cursor = options?.cursor; @@ -408,7 +407,7 @@ export class InMemoryDriver implements IStorageDriver { return { messages, nextCursor }; } - async add_feed_message(feedId: string, message: FeedMessage): Promise { + add_feed_message(feedId: string, message: FeedMessage): void { // Verify feed exists const feed = this._feeds.get(feedId); if (feed === undefined) { @@ -418,12 +417,12 @@ export class InMemoryDriver implements IStorageDriver { this._feedMessages.set(`${feedId}:${message.id}`, message); } - async update_feed_message( + update_feed_message( feedId: string, messageId: string, data: Json, timestamp?: number - ): Promise { + ): FeedMessage { const key = `${feedId}:${messageId}`; const message = this._feedMessages.get(key); @@ -447,7 +446,7 @@ export class InMemoryDriver implements IStorageDriver { return updatedMessage; } - async delete_feed_message(feedId: string, messageId: string): Promise { + delete_feed_message(feedId: string, messageId: string): void { this._feedMessages.delete(`${feedId}:${messageId}`); } @@ -455,24 +454,24 @@ export class InMemoryDriver implements IStorageDriver { return ++this._nextActor; } - async iter_y_updates(docId: YDocId) { + iter_y_updates(docId: YDocId) { const prefix = `${docId}@|@`; return imap( ifilter(this._ydb.entries(), ([k]) => k.startsWith(prefix)), ([k, v]) => [k.slice(prefix.length), v] as [string, Uint8Array] ); } - async write_y_updates(docId: YDocId, key: string, data: Uint8Array) { + write_y_updates(docId: YDocId, key: string, data: Uint8Array) { this._ydb.set(`${docId}@|@${key}`, data); } - async delete_y_updates(docId: YDocId, keys: string[]) { + delete_y_updates(docId: YDocId, keys: string[]) { for (const key of keys) { this._ydb.delete(`${docId}@|@${key}`); } } /** @private Only use this in unit tests, never in production. */ - async DANGEROUSLY_wipe_all_y_updates() { + DANGEROUSLY_wipe_all_y_updates() { this._ydb.clear(); } @@ -518,11 +517,11 @@ export class InMemoryDriver implements IStorageDriver { * Inserts a node in the storage tree, deleting any nodes that already exist * under this key (including all of its children), if any. */ - async function set_child( + function set_child( id: string, node: SerializedChild, allowOverwrite = false - ): Promise { + ): void { const parentNode = nodes.get(node.parentId); // Reject orphans - parent must exist if (parentNode === undefined) { @@ -566,7 +565,7 @@ export class InMemoryDriver implements IStorageDriver { * delete-then-insert would would immediately destroy all (grand)children * when it's deleted. */ - async function move_sibling(id: string, newPos: Pos): Promise { + function move_sibling(id: string, newPos: Pos): void { const node = nodes.get(id); if (node?.parentId === undefined) { return; @@ -591,11 +590,11 @@ export class InMemoryDriver implements IStorageDriver { * But if `allowOverwrite` is set to true, the conflicting child node (and * its entire subtree) will be deleted to make room for the new static data. */ - async function set_object_data( + function set_object_data( id: string, data: JsonObject, allowOverwrite = false - ): Promise { + ): void { const node = nodes.get(id); if (node?.type !== CrdtType.OBJECT) { // Nothing to do diff --git a/packages/liveblocks-server/test-d/server.test-d.ts b/packages/liveblocks-server/test-d/server.test-d.ts index 75f012f44f..6603ac286b 100644 --- a/packages/liveblocks-server/test-d/server.test-d.ts +++ b/packages/liveblocks-server/test-d/server.test-d.ts @@ -37,12 +37,12 @@ async () => { // Room.createTicket() API expectType>( - await room.createTicket({ version: ProtocolVersion.V8 }) + room.createTicket({ version: ProtocolVersion.V8 }) ); - expectType>(await room.createTicket()); - expectType>(await room.createTicket()); + expectType>(room.createTicket()); + expectType>(room.createTicket()); - const ticket = await room.createTicket({ meta: { my: "session-meta" } }); + const ticket = room.createTicket({ meta: { my: "session-meta" } }); // Room.startBrowserSession() API expectType(await room.startBrowserSession(ticket, socket)); diff --git a/packages/liveblocks-server/test/MetadataDB.test.ts b/packages/liveblocks-server/test/MetadataDB.test.ts index 3f25f09a7f..a131626fbb 100644 --- a/packages/liveblocks-server/test/MetadataDB.test.ts +++ b/packages/liveblocks-server/test/MetadataDB.test.ts @@ -40,68 +40,68 @@ class VoidTarget extends LogTarget { const blackHole = new LoggerImpl(new VoidTarget()); describe("test in-memory driver", () => { - test("basic", async () => { + test("basic", () => { const driver = makeNewInMemoryDriver(); const metadataDB = makeMetadataDB(driver); - expect(await metadataDB.get("foo")).toEqual(undefined); + expect(metadataDB.get("foo")).toEqual(undefined); // Before loading, root node should not exist - expect(new Map(await driver.raw_iter_nodes())).toEqual(new Map()); + expect(new Map(driver.raw_iter_nodes())).toEqual(new Map()); // After loading, root node should exist - const nodesApi = await driver.load_nodes_api(blackHole); + const nodesApi = driver.load_nodes_api(blackHole); expect(new Map(nodesApi.iter_nodes())).toEqual( new Map([["root", { type: CrdtType.OBJECT, data: {} }]]) ); }); - test("put and get are async", async () => { + test("put and get are async", () => { const driver = makeNewInMemoryDriver(); const metadataDB = makeMetadataDB(driver); - expect(await metadataDB.get("foo")).toEqual(undefined); - await metadataDB.put("foo", "bar"); - expect(await metadataDB.get("foo")).toEqual("bar"); + expect(metadataDB.get("foo")).toEqual(undefined); + metadataDB.put("foo", "bar"); + expect(metadataDB.get("foo")).toEqual("bar"); }); - test("getters with decoders", async () => { + test("getters with decoders", () => { const driver = makeNewInMemoryDriver(); const metadataDB = makeMetadataDB(driver); - await metadataDB.put("foo", "hi"); - expect(await metadataDB.get("foo")).toEqual("hi"); - expect(await metadataDB.get(number, "foo")).toEqual(undefined); - expect(await metadataDB.get(rounded, "foo")).toEqual(undefined); - expect(await metadataDB.get(string, "foo")).toEqual("hi"); + metadataDB.put("foo", "hi"); + expect(metadataDB.get("foo")).toEqual("hi"); + expect(metadataDB.get(number, "foo")).toEqual(undefined); + expect(metadataDB.get(rounded, "foo")).toEqual(undefined); + expect(metadataDB.get(string, "foo")).toEqual("hi"); // Override - await metadataDB.put("foo", 123); - expect(await metadataDB.get("foo")).toEqual(123); - expect(await metadataDB.get(number, "foo")).toEqual(123); - expect(await metadataDB.get(integer, "foo")).toEqual(123); - expect(await metadataDB.get(rounded, "foo")).toEqual(123); - expect(await metadataDB.get(string, "foo")).toEqual(undefined); - - await metadataDB.put("foo", 3.999); - expect(await metadataDB.get("foo")).toEqual(3.999); - expect(await metadataDB.get(number, "foo")).toEqual(3.999); - expect(await metadataDB.get(integer, "foo")).toEqual(undefined); - expect(await metadataDB.get(rounded, "foo")).toEqual(4); - expect(await metadataDB.get(string, "foo")).toEqual(undefined); - - await metadataDB.put("foo", -3.999); - expect(await metadataDB.get("foo")).toEqual(-3.999); - expect(await metadataDB.get(number, "foo")).toEqual(-3.999); - expect(await metadataDB.get(integer, "foo")).toEqual(undefined); - expect(await metadataDB.get(rounded, "foo")).toEqual(-4); - expect(await metadataDB.get(string, "foo")).toEqual(undefined); + metadataDB.put("foo", 123); + expect(metadataDB.get("foo")).toEqual(123); + expect(metadataDB.get(number, "foo")).toEqual(123); + expect(metadataDB.get(integer, "foo")).toEqual(123); + expect(metadataDB.get(rounded, "foo")).toEqual(123); + expect(metadataDB.get(string, "foo")).toEqual(undefined); + + metadataDB.put("foo", 3.999); + expect(metadataDB.get("foo")).toEqual(3.999); + expect(metadataDB.get(number, "foo")).toEqual(3.999); + expect(metadataDB.get(integer, "foo")).toEqual(undefined); + expect(metadataDB.get(rounded, "foo")).toEqual(4); + expect(metadataDB.get(string, "foo")).toEqual(undefined); + + metadataDB.put("foo", -3.999); + expect(metadataDB.get("foo")).toEqual(-3.999); + expect(metadataDB.get(number, "foo")).toEqual(-3.999); + expect(metadataDB.get(integer, "foo")).toEqual(undefined); + expect(metadataDB.get(rounded, "foo")).toEqual(-4); + expect(metadataDB.get(string, "foo")).toEqual(undefined); }); - test("two namespaces of the same type don't conflict", async () => { + test("two namespaces of the same type don't conflict", () => { const driver = makeNewInMemoryDriver(); const metadataDB = makeMetadataDB(driver); - const nodeDriver = await driver.load_nodes_api(blackHole); + const nodeDriver = driver.load_nodes_api(blackHole); - await metadataDB.put("foo", null); - await metadataDB.put("foo", 123); - await metadataDB.put("foo", "bar"); - await metadataDB.put("bar", [42, 1337]); + metadataDB.put("foo", null); + metadataDB.put("foo", 123); + metadataDB.put("foo", "bar"); + metadataDB.put("bar", [42, 1337]); const node1 = { type: CrdtType.OBJECT, @@ -115,14 +115,14 @@ describe("test in-memory driver", () => { parentId: "root", parentKey: "b", }; - await nodeDriver.set_child("foo", node1); - await nodeDriver.set_child("bar", node2); + nodeDriver.set_child("foo", node1); + nodeDriver.set_child("bar", node2); // Namespaces don't conflict - expect(await metadataDB.get("foo")).toEqual("bar"); - expect(await metadataDB.get("bar")).toEqual([42, 1337]); + expect(metadataDB.get("foo")).toEqual("bar"); + expect(metadataDB.get("bar")).toEqual([42, 1337]); // Both raw_iter_nodes and iter_nodes include root - expect(new Map(await driver.raw_iter_nodes())).toEqual( + expect(new Map(driver.raw_iter_nodes())).toEqual( new Map([ ["root", { type: CrdtType.OBJECT, data: {} }], ["foo", node1], @@ -138,12 +138,12 @@ describe("test in-memory driver", () => { ); // Deleting foo from metadata namespace has no effect on other namespaces - await metadataDB.delete("foo"); - expect(await metadataDB.get("foo")).toEqual(undefined); - expect(new Map(await driver.raw_iter_nodes()).get("foo")).toEqual(node1); + metadataDB.delete("foo"); + expect(metadataDB.get("foo")).toEqual(undefined); + expect(new Map(driver.raw_iter_nodes()).get("foo")).toEqual(node1); expect( new Map( - (await driver.load_nodes_api(blackHole)).iter_nodes() + driver.load_nodes_api(blackHole).iter_nodes() ).get("foo") ).toEqual(node1); }); diff --git a/packages/liveblocks-server/test/plugins/_generateFullTestSuite.ts b/packages/liveblocks-server/test/plugins/_generateFullTestSuite.ts index 0b501457ab..cd09f99278 100644 --- a/packages/liveblocks-server/test/plugins/_generateFullTestSuite.ts +++ b/packages/liveblocks-server/test/plugins/_generateFullTestSuite.ts @@ -156,8 +156,8 @@ function wouldNotOverwriteDefaultDoc(n: { * exist, in order to express tests more succinctly. */ async function withDefaultDocument(driver: IStorageDriver) { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, [ list("0:dl", "root", "l"), @@ -193,7 +193,7 @@ function assert( * (in-memory and on-disk), and will throw if _anything_ is in an inconsistent * state. */ -export async function selfCheck(storage: Storage): Promise { +export function selfCheck(storage: Storage): void { const driver: IStorageDriverNodeAPI = storage.loadedDriver; { @@ -255,7 +255,7 @@ export async function selfCheck(storage: Storage): Promise { // Loading the persisted data back in from storage now, it should match // whatever we still have in memory (ignoring the metadata and usage // metrics keys) - const onDiskNodes = new Map(await storage.raw_iter_nodes()); + const onDiskNodes = new Map(storage.raw_iter_nodes()); for (const [id, inMemoryNode] of driver.iter_nodes()) { if (inMemoryNode.parentId === undefined) { // This is the root node, which needs special treatment. Root nodes are @@ -1023,7 +1023,7 @@ async function delete_nodes(db: IStorageDriverNodeAPI, ids: Iterable) { /** A black hole, where logs disappear beyond the event horizon */ const blackHole = new LoggerImpl(new VoidTarget()); -type TestFn = (driver: TDriver) => Promise; +type TestFn = (driver: TDriver) => Awaitable; type RunTestOptions = { initialNodes?: NodeMap }; export function generateFullTestSuite(config: { @@ -1072,8 +1072,8 @@ export function generateFullTestSuite(config: { describe("nodes API impl", () => { test("get_node and iter_nodes with empty store contains root", () => - runTest(async (driver) => { - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + const db = driver.load_nodes_api(blackHole); expect(db.get_node("non-existing")).toEqual(undefined); // Root node always exists, even in an empty store @@ -1084,15 +1084,15 @@ export function generateFullTestSuite(config: { })); test("get_node and iter_nodes with empty store contains root", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), - async (key) => { + (key) => { fc.pre(key !== "root"); - const db = await driver.load_nodes_api(blackHole); + const db = driver.load_nodes_api(blackHole); expect(db.get_node(key)).toEqual(undefined); expect(Array.from(db.iter_nodes())).toEqual([ ["root", { type: CrdtType.OBJECT, data: {} }], @@ -1135,16 +1135,16 @@ export function generateFullTestSuite(config: { )); test("has_node: root always exists", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_node("root")).toBe(true); })); test("has_node: non-existing node returns false", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_node("non-existing")).toBe(false); })); @@ -1174,8 +1174,8 @@ export function generateFullTestSuite(config: { arb.nodeStream(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); // has_node should be true iff get_node returns a value @@ -1188,23 +1188,23 @@ export function generateFullTestSuite(config: { )); test("get_child_at: returns undefined for empty store", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.get_child_at("root", "someKey")).toBe(undefined); expect(db.get_child_at("non-existing", "someKey")).toBe(undefined); })); test("get_child_at: returns undefined for empty store (property)", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), arb.key(), - async (parentId, parentKey) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + (parentId, parentKey) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.get_child_at(parentId, parentKey)).toBe(undefined); } ) @@ -1213,8 +1213,8 @@ export function generateFullTestSuite(config: { test("set_child: throws if parent does not exist (orphan)", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Trying to add a node with a non-existent parentId should throw await expectToThrow( @@ -1230,8 +1230,8 @@ export function generateFullTestSuite(config: { test("set_child: throws if parent does not exist (ref cycle)", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Trying to add a node with a non-existent parentId should throw await expectToThrow( @@ -1247,8 +1247,8 @@ export function generateFullTestSuite(config: { test("set_child: orphan node is not added after throw", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Try to add orphan, catch the error try { @@ -1312,8 +1312,8 @@ export function generateFullTestSuite(config: { test("get_child_at: returns child id after set", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1327,8 +1327,8 @@ export function generateFullTestSuite(config: { test("get_child_at: returns undefined after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1343,8 +1343,8 @@ export function generateFullTestSuite(config: { test("get_child_at: returns undefined after set_object_data would overwrite it", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1359,8 +1359,8 @@ export function generateFullTestSuite(config: { test("get_child_at: tracks nested children", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // root -> list -> register await db.set_child("0:0", { @@ -1387,8 +1387,8 @@ export function generateFullTestSuite(config: { arb.nodeStream(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); // For each node with a parent, get_child_at(parentId, parentKey) should return the node's id @@ -1406,23 +1406,23 @@ export function generateFullTestSuite(config: { )); test("has_child_at: returns false for empty store", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_child_at("root", "someKey")).toBe(false); expect(db.has_child_at("non-existing", "someKey")).toBe(false); })); test("has_child_at: returns false for empty store (property)", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), arb.key(), - async (parentId, parentKey) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + (parentId, parentKey) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_child_at(parentId, parentKey)).toBe(false); } ) @@ -1431,8 +1431,8 @@ export function generateFullTestSuite(config: { test("has_child_at: returns true after set, false after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_child_at("root", "myList")).toBe(false); @@ -1458,8 +1458,8 @@ export function generateFullTestSuite(config: { arb.key(), async (entries, parentId, parentKey) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); // has_child_at should be true iff get_child_at returns a value @@ -1472,9 +1472,9 @@ export function generateFullTestSuite(config: { )); test("get_next_sibling: returns undefined for empty parent", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // No children under root, so no next position expect(db.get_next_sibling("root", FIRST_POSITION)).toBe(undefined); @@ -1485,8 +1485,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: returns undefined when no positions after", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Add a list with one item at FIRST_POSITION await db.set_child("0:0", { @@ -1511,8 +1511,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: finds next position in ordered list", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create list with items at FIRST, SECOND, THIRD positions await db.set_child("0:0", { @@ -1551,8 +1551,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: updates after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1590,9 +1590,9 @@ export function generateFullTestSuite(config: { })); test("get_last_sibling: returns undefined for empty parent", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.get_last_sibling("root")).toBe(undefined); expect(db.get_last_sibling("non-existing")).toBe(undefined); @@ -1600,8 +1600,8 @@ export function generateFullTestSuite(config: { test("get_last_sibling: returns the rightmost position", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1634,8 +1634,8 @@ export function generateFullTestSuite(config: { test("get_last_sibling: updates after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1666,8 +1666,8 @@ export function generateFullTestSuite(config: { test("move: changes parentKey of node", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1701,8 +1701,8 @@ export function generateFullTestSuite(config: { test("move: updates get_next_sibling correctly", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1739,8 +1739,8 @@ export function generateFullTestSuite(config: { test("move: multiple moves on same node", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1768,8 +1768,8 @@ export function generateFullTestSuite(config: { test("move: preserves other siblings", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1811,8 +1811,8 @@ export function generateFullTestSuite(config: { test("move: throws when target position is occupied", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1847,8 +1847,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: removes static data key from object", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2, c: 3 }); @@ -1867,8 +1867,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: removes child node", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1887,8 +1887,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: removes child node and its descendants recursively", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // root -> list -> register await db.set_child("0:0", { @@ -1922,8 +1922,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: no-op for non-existing key", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1 }); @@ -1939,8 +1939,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: preserves sibling data and nodes", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2 }); await db.set_child("0:0", { @@ -1975,8 +1975,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries as NodeStream); @@ -2069,8 +2069,8 @@ export function generateFullTestSuite(config: { test("deleting the root is a no-op", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { foo: 42 }); // Try to delete the root node @@ -2090,8 +2090,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(Array.from(db.iter_nodes())).toEqual([ ["root", { type: CrdtType.OBJECT, data: {} }], ]); @@ -2116,8 +2116,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(Array.from(db.iter_nodes())).toEqual([ ["root", { type: CrdtType.OBJECT, data: {} }], ]); @@ -2150,8 +2150,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries as NodeStream); // Parse the wire tuples back into CompactNodes and compare @@ -2181,8 +2181,8 @@ export function generateFullTestSuite(config: { arb.nodeStream(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); await delete_nodes( @@ -2355,8 +2355,8 @@ export function generateFullTestSuite(config: { test("get_snapshot: delete_child_key on static data does not affect snapshot", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2, c: 3 }); // Take snapshot before deleting a key @@ -2380,8 +2380,8 @@ export function generateFullTestSuite(config: { test("get_snapshot: set_object_data after snapshot does not affect snapshot", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2 }); // Take snapshot before mutating @@ -2463,9 +2463,9 @@ export function generateFullTestSuite(config: { })); test("get_snapshot: iter_all on empty room returns only root", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); const snapshot = db.get_snapshot(); const nodes = [...snapshot.iter_all()]; expect(nodes).toEqual([["root", { type: CrdtType.OBJECT, data: {} }]]); @@ -2479,8 +2479,8 @@ export function generateFullTestSuite(config: { test("set: read sees new node before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); const node = { type: CrdtType.LIST, @@ -2507,8 +2507,8 @@ export function generateFullTestSuite(config: { test("delete: read sees deletion before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // First create a node await db.set_child("0:0", { @@ -2537,8 +2537,8 @@ export function generateFullTestSuite(config: { test("move: read sees new position before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a list with an item await db.set_child("0:0", { @@ -2582,8 +2582,8 @@ export function generateFullTestSuite(config: { test("set_object_data: read sees new data before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Start with empty root expect(db.get_node("root")).toEqual({ @@ -2635,8 +2635,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: read sees deletion before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Set up root with static data and a child node await db.set_object_data("root", { a: 1, b: 2 }); @@ -2680,8 +2680,8 @@ export function generateFullTestSuite(config: { test("multiple writes: all visible before any await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Perform multiple writes without awaiting any of them const p1$ = db.set_child("0:0", { @@ -2726,8 +2726,8 @@ export function generateFullTestSuite(config: { test("interleaved write and delete: reads see correct state", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a node const p1$ = db.set_child("0:0", { @@ -2771,8 +2771,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: updated before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a list with two items await db.set_child("0:0", { @@ -2823,8 +2823,8 @@ export function generateFullTestSuite(config: { test("set with overwrite: read sees update before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a list to hold registers await db.set_child("0:0", { @@ -2881,45 +2881,45 @@ export function generateFullTestSuite(config: { describe("meta API impl", () => { test("get_meta with empty store is undefined", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), - async (key) => { - expect(await driver.get_meta(key)).toEqual(undefined); + (key) => { + expect(driver.get_meta(key)).toEqual(undefined); } ) ) )); test("put_meta + get_meta", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.metaPair(), - async ([key, value]) => { - await driver.put_meta(key, value); - expect(await driver.get_meta(key)).toEqual(value); + ([key, value]) => { + driver.put_meta(key, value); + expect(driver.get_meta(key)).toEqual(value); } ) ) )); test("put_meta + get_meta loop", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc.array(arb.metaPair()).map((x) => new Map(x)), - async (entries) => { + (entries) => { for (const [key, value] of entries) { - await driver.put_meta(key, value); + driver.put_meta(key, value); } for (const [key, value] of entries) { - expect(await driver.get_meta(key)).toEqual(value); + expect(driver.get_meta(key)).toEqual(value); } } ) @@ -2927,20 +2927,20 @@ export function generateFullTestSuite(config: { )); test("put_meta + delete_meta + get_meta", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), arb.json(), arb.json(), - async (key, value1, value2) => { - await driver.put_meta(key, value1); - await driver.put_meta(key, value2); // override - expect(await driver.get_meta(key)).toEqual(value2); + (key, value1, value2) => { + driver.put_meta(key, value1); + driver.put_meta(key, value2); // override + expect(driver.get_meta(key)).toEqual(value2); - await driver.delete_meta(key); - expect(await driver.get_meta(key)).toEqual(undefined); + driver.delete_meta(key); + expect(driver.get_meta(key)).toEqual(undefined); } ) ) @@ -2963,7 +2963,7 @@ export function generateFullTestSuite(config: { // Check that readone will also have the same results const map = new Map(entries); // de-dupe for (const [key, expected] of map) { - expect(await driver.get_meta(key)).toEqual(expected); + expect(driver.get_meta(key)).toEqual(expected); } } ) @@ -2971,21 +2971,21 @@ export function generateFullTestSuite(config: { )); test("put_meta + get_meta loop", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.metaEntries(), - async (entries) => { + (entries) => { // Write all the entries (can have dupes) for (const [key, value] of entries) { - await driver.put_meta(key, value); + driver.put_meta(key, value); } // Check that readone will also have the same results const map = new Map(entries); // de-dupe for (const [key, expected] of map) { - expect(await driver.get_meta(key)).toEqual(expected); + expect(driver.get_meta(key)).toEqual(expected); } } ) @@ -2993,21 +2993,21 @@ export function generateFullTestSuite(config: { )); test("delete_meta (all keys)", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.metaEntries(), - async (entries) => { + (entries) => { for (const [key, value] of entries) { - await driver.put_meta(key, value); + driver.put_meta(key, value); } for (const key of new Map(entries).keys()) { - await driver.delete_meta(key); + driver.delete_meta(key); } for (const [key] of entries) { - expect(await driver.get_meta(key)).toEqual(undefined); + expect(driver.get_meta(key)).toEqual(undefined); } } ) @@ -3037,19 +3037,19 @@ export function generateFullTestSuite(config: { const key2 = entry2[0]; const key3 = entry3[0]; - expect(await driver.get_meta(key1)).not.toEqual(undefined); - expect(await driver.get_meta(key2)).not.toEqual(undefined); - expect(await driver.get_meta(key3)).not.toEqual(undefined); - await driver.delete_meta(key1); - await driver.delete_meta(key1); - expect(await driver.get_meta(key1)).toEqual(undefined); - expect(await driver.get_meta(key2)).not.toEqual(undefined); - expect(await driver.get_meta(key3)).not.toEqual(undefined); - await driver.delete_meta(key2); - await driver.delete_meta(key2); - expect(await driver.get_meta(key1)).toEqual(undefined); - expect(await driver.get_meta(key2)).toEqual(undefined); - expect(await driver.get_meta(key3)).not.toEqual(undefined); + expect(driver.get_meta(key1)).not.toEqual(undefined); + expect(driver.get_meta(key2)).not.toEqual(undefined); + expect(driver.get_meta(key3)).not.toEqual(undefined); + driver.delete_meta(key1); + driver.delete_meta(key1); + expect(driver.get_meta(key1)).toEqual(undefined); + expect(driver.get_meta(key2)).not.toEqual(undefined); + expect(driver.get_meta(key3)).not.toEqual(undefined); + driver.delete_meta(key2); + driver.delete_meta(key2); + expect(driver.get_meta(key1)).toEqual(undefined); + expect(driver.get_meta(key2)).toEqual(undefined); + expect(driver.get_meta(key3)).not.toEqual(undefined); } ) ) @@ -3058,41 +3058,37 @@ export function generateFullTestSuite(config: { describe("ydoc API impl", () => { test("iter_y_updates on an empty store is empty", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.docId(), - async (docId) => { - expect(Array.from(await driver.iter_y_updates(docId))).toEqual( - [] - ); + (docId) => { + expect(Array.from(driver.iter_y_updates(docId))).toEqual([]); } ) ) )); test("write_y_updates + iter_y_updates", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc.tuple(arb.docId(), arb.docId()).filter(([a, b]) => a !== b), fc .array(fc.tuple(arb.key(), fc.uint8Array())) .map((x) => new Map(x)), - async ([docId, anotherDocId], entries) => { - await driver.DANGEROUSLY_wipe_all_y_updates(); + ([docId, anotherDocId], entries) => { + driver.DANGEROUSLY_wipe_all_y_updates(); for (const [key, data] of entries) { - await driver.write_y_updates(docId, key, data); + driver.write_y_updates(docId, key, data); } - expect(new Map(await driver.iter_y_updates(docId))).toEqual( - entries + expect(new Map(driver.iter_y_updates(docId))).toEqual(entries); + expect(new Map(driver.iter_y_updates(anotherDocId))).toEqual( + new Map() ); - expect( - new Map(await driver.iter_y_updates(anotherDocId)) - ).toEqual(new Map()); } ) ) @@ -3101,132 +3097,118 @@ export function generateFullTestSuite(config: { describe("leased session API impl", () => { test("list_leased_sessions on empty store is empty", () => - runTest(async (driver) => { - const sessions = Array.from(await driver.list_leased_sessions()); + runTest((driver) => { + const sessions = Array.from(driver.list_leased_sessions()); expect(sessions).toEqual([]); })); test("get_leased_session on empty store is undefined", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty(arb.sessionId(), async (sessionId) => { - expect(await driver.get_leased_session(sessionId)).toEqual( - undefined - ); + fc.property(arb.sessionId(), (sessionId) => { + expect(driver.get_leased_session(sessionId)).toEqual(undefined); }) ) )); test("put_leased_session + get_leased_session", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.sessionId(), arb.leasedSession(), - async (sessionId, session) => { + (sessionId, session) => { session.sessionId = sessionId; - await driver.put_leased_session(session); - expect(await driver.get_leased_session(sessionId)).toEqual( - session - ); + driver.put_leased_session(session); + expect(driver.get_leased_session(sessionId)).toEqual(session); // Cleanup: delete the session added in this iteration - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } ) ) )); test("put_leased_session (overwrite) + get_leased_session", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.sessionId(), arb.leasedSession(), arb.leasedSession(), - async (sessionId, session1, session2) => { + (sessionId, session1, session2) => { // Put first session session1.sessionId = sessionId; - await driver.put_leased_session(session1); - expect(await driver.get_leased_session(sessionId)).toEqual( - session1 - ); + driver.put_leased_session(session1); + expect(driver.get_leased_session(sessionId)).toEqual(session1); // Overwrite with second session session2.sessionId = sessionId; - await driver.put_leased_session(session2); - expect(await driver.get_leased_session(sessionId)).toEqual( - session2 - ); + driver.put_leased_session(session2); + expect(driver.get_leased_session(sessionId)).toEqual(session2); // Cleanup: delete the session added in this iteration - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } ) ) )); test("put_leased_session + delete_leased_session + get_leased_session", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.sessionId(), arb.leasedSession(), - async (sessionId, session) => { + (sessionId, session) => { session.sessionId = sessionId; - await driver.put_leased_session(session); - expect(await driver.get_leased_session(sessionId)).toEqual( - session - ); + driver.put_leased_session(session); + expect(driver.get_leased_session(sessionId)).toEqual(session); - await driver.delete_leased_session(sessionId); - expect(await driver.get_leased_session(sessionId)).toEqual( - undefined - ); + driver.delete_leased_session(sessionId); + expect(driver.get_leased_session(sessionId)).toEqual(undefined); } ) ) )); test("delete_leased_session on non-existent session is no-op", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty(arb.sessionId(), async (sessionId) => { + fc.property(arb.sessionId(), (sessionId) => { // Deleting a session that doesn't exist should not throw - await driver.delete_leased_session(sessionId); - expect(await driver.get_leased_session(sessionId)).toEqual( - undefined - ); + driver.delete_leased_session(sessionId); + expect(driver.get_leased_session(sessionId)).toEqual(undefined); }) ) )); test("put_leased_session + list_leased_sessions", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc .array(arb.leasedSessionPair()) .map((x) => new Map(x)) .filter((m) => m.size > 0), // At least one session - async (entries) => { + (entries) => { // Put all sessions for (const [sessionId, session] of entries) { session.sessionId = sessionId; - await driver.put_leased_session(session); + driver.put_leased_session(session); } // List should return all sessions - const listed = new Map(await driver.list_leased_sessions()); + const listed = new Map(driver.list_leased_sessions()); expect(listed).toEqual(entries); // Cleanup: delete all sessions added in this iteration for (const [sessionId] of entries) { - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } } ) @@ -3234,28 +3216,28 @@ export function generateFullTestSuite(config: { )); test("put multiple sessions + get each individually", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc.array(arb.leasedSessionPair()).map((x) => new Map(x)), - async (entries) => { + (entries) => { // Put all sessions for (const [sessionId, session] of entries) { session.sessionId = sessionId; - await driver.put_leased_session(session); + driver.put_leased_session(session); } // Get each session individually and verify for (const [sessionId, expectedSession] of entries) { - expect(await driver.get_leased_session(sessionId)).toEqual( + expect(driver.get_leased_session(sessionId)).toEqual( expectedSession ); } // Cleanup: delete all sessions added in this iteration for (const [sessionId] of entries) { - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } } ) @@ -3291,79 +3273,53 @@ export function generateFullTestSuite(config: { ]); // Verify all exist - expect(await driver.get_leased_session(sessionId1)).toEqual( - session1 - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - session2 - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - session3 - ); + expect(driver.get_leased_session(sessionId1)).toEqual(session1); + expect(driver.get_leased_session(sessionId2)).toEqual(session2); + expect(driver.get_leased_session(sessionId3)).toEqual(session3); // Delete first session - await driver.delete_leased_session(sessionId1); - expect(await driver.get_leased_session(sessionId1)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - session2 - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - session3 - ); + driver.delete_leased_session(sessionId1); + expect(driver.get_leased_session(sessionId1)).toEqual(undefined); + expect(driver.get_leased_session(sessionId2)).toEqual(session2); + expect(driver.get_leased_session(sessionId3)).toEqual(session3); // Delete second session - await driver.delete_leased_session(sessionId2); - expect(await driver.get_leased_session(sessionId1)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - session3 - ); + driver.delete_leased_session(sessionId2); + expect(driver.get_leased_session(sessionId1)).toEqual(undefined); + expect(driver.get_leased_session(sessionId2)).toEqual(undefined); + expect(driver.get_leased_session(sessionId3)).toEqual(session3); // Delete third session - await driver.delete_leased_session(sessionId3); - expect(await driver.get_leased_session(sessionId1)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - undefined - ); + driver.delete_leased_session(sessionId3); + expect(driver.get_leased_session(sessionId1)).toEqual(undefined); + expect(driver.get_leased_session(sessionId2)).toEqual(undefined); + expect(driver.get_leased_session(sessionId3)).toEqual(undefined); // List should be empty - expect(Array.from(await driver.list_leased_sessions())).toEqual( - [] - ); + expect(Array.from(driver.list_leased_sessions())).toEqual([]); } ) ) )); test("list_leased_sessions returns all sessions in any order", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc .array(arb.leasedSessionPair()) .map((x) => new Map(x)) .filter((m) => m.size > 0), - async (entries) => { + (entries) => { // Put all sessions for (const [sessionId, session] of entries) { session.sessionId = sessionId; - await driver.put_leased_session(session); + driver.put_leased_session(session); } // Get listed sessions and convert to a map - const listed = new Map(await driver.list_leased_sessions()); + const listed = new Map(driver.list_leased_sessions()); // Both should have the same size expect(listed.size).toEqual(entries.size); @@ -3375,7 +3331,7 @@ export function generateFullTestSuite(config: { // Cleanup: delete all sessions added in this iteration for (const [sessionId] of entries) { - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } } ) @@ -3427,107 +3383,103 @@ export function generateFullTestSuite(config: { describe("feed API impl", () => { test("list_feeds on empty store is empty", () => - runTest(async (driver) => { - const result = await driver.list_feeds(); + runTest((driver) => { + const result = driver.list_feeds(); expect(result.feeds).toEqual([]); expect(result.nextCursor).toBeUndefined(); })); test("get_feed on empty store is undefined", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feedId(), async (feedId) => { - expect(await driver.get_feed(feedId)).toEqual(undefined); + fc.property(arb.feedId(), (feedId) => { + expect(driver.get_feed(feedId)).toEqual(undefined); }) ); })); test("create_feed + get_feed", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feed(), async (feed) => { - await driver.create_feed(feed); - expect(await driver.get_feed(feed.feedId)).toEqual(feed); + fc.property(arb.feed(), (feed) => { + driver.create_feed(feed); + expect(driver.get_feed(feed.feedId)).toEqual(feed); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); }) ); })); test("update_feed_metadata", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.json(), - async (feed, newMetadata) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.json(), (feed, newMetadata) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - await driver.update_feed_metadata(feed.feedId, newMetadata); + driver.update_feed_metadata(feed.feedId, newMetadata); - const updated = await driver.get_feed(feed.feedId); - expect(updated?.metadata).toEqual(newMetadata); - expect(updated?.feedId).toEqual(feed.feedId); + const updated = driver.get_feed(feed.feedId); + expect(updated?.metadata).toEqual(newMetadata); + expect(updated?.feedId).toEqual(feed.feedId); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("delete_feed", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feed(), async (feed) => { + fc.property(arb.feed(), (feed) => { // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); - expect(await driver.get_feed(feed.feedId)).toEqual(feed); + driver.create_feed(feed); + expect(driver.get_feed(feed.feedId)).toEqual(feed); - await driver.delete_feed(feed.feedId); - expect(await driver.get_feed(feed.feedId)).toBeUndefined(); + driver.delete_feed(feed.feedId); + expect(driver.get_feed(feed.feedId)).toBeUndefined(); }) ); })); test("delete_feed on non-existent feed is no-op", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feedId(), async (feedId) => { - await driver.delete_feed(feedId); - expect(await driver.get_feed(feedId)).toBeUndefined(); + fc.property(arb.feedId(), (feedId) => { + driver.delete_feed(feedId); + expect(driver.get_feed(feedId)).toBeUndefined(); }) ); })); test("list_feeds returns all feeds", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( + fc.property( fc .array(arb.feedPair()) .map((x) => new Map(x)) .filter((m) => m.size > 0 && m.size <= 5), - async (entries) => { + (entries) => { // Ensure feeds don't already exist for (const [feedId] of entries) { - await driver.delete_feed(feedId); + driver.delete_feed(feedId); } // Create all feeds for (const [, feed] of entries) { - await driver.create_feed(feed); + driver.create_feed(feed); } // List all feeds - const result = await driver.list_feeds(); + const result = driver.list_feeds(); // Should return all feeds expect(result.feeds.length).toEqual(entries.size); @@ -3538,7 +3490,7 @@ export function generateFullTestSuite(config: { // Cleanup for (const [feedId] of entries) { - await driver.delete_feed(feedId); + driver.delete_feed(feedId); } } ) @@ -3546,7 +3498,7 @@ export function generateFullTestSuite(config: { })); test("list_feeds with pagination", () => - runTest(async (driver) => { + runTest((driver) => { // Create 3 feeds with different createdAt timestamps const feeds: Feed[] = [ { @@ -3570,11 +3522,11 @@ export function generateFullTestSuite(config: { ]; for (const feed of feeds) { - await driver.create_feed(feed); + driver.create_feed(feed); } // List with limit - const page1 = await driver.list_feeds({ limit: 2 }); + const page1 = driver.list_feeds({ limit: 2 }); expect(page1.feeds.length).toBe(2); expect(page1.nextCursor).toBeDefined(); @@ -3583,7 +3535,7 @@ export function generateFullTestSuite(config: { expect(page1.feeds[1]?.feedId).toBe("feed-2"); // Get next page - const page2 = await driver.list_feeds({ + const page2 = driver.list_feeds({ cursor: page1.nextCursor, }); expect(page2.feeds.length).toBe(1); @@ -3592,12 +3544,12 @@ export function generateFullTestSuite(config: { // Cleanup for (const feed of feeds) { - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); } })); test("list_feeds with since filter", () => - runTest(async (driver) => { + runTest((driver) => { const feeds: Feed[] = [ { feedId: "feed-old", @@ -3614,68 +3566,64 @@ export function generateFullTestSuite(config: { ]; for (const feed of feeds) { - await driver.create_feed(feed); + driver.create_feed(feed); } // List feeds since 2000 - const result = await driver.list_feeds({ since: 2000 }); + const result = driver.list_feeds({ since: 2000 }); expect(result.feeds.length).toBe(1); expect(result.feeds[0]?.feedId).toBe("feed-new"); // Cleanup for (const feed of feeds) { - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); } })); test("add_feed_message", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - // Create feed - await driver.create_feed(feed); + // Create feed + driver.create_feed(feed); - // Add message - await driver.add_feed_message(feed.feedId, message); + // Add message + driver.add_feed_message(feed.feedId, message); - // List messages and verify message was added - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages).toHaveLength(1); - expect(result.messages[0]).toEqual(message); + // List messages and verify message was added + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages).toHaveLength(1); + expect(result.messages[0]).toEqual(message); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("list_feed_messages", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feed(), async (feed) => { + fc.property(arb.feed(), (feed) => { // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - const result = await driver.list_feed_messages(feed.feedId); + const result = driver.list_feed_messages(feed.feedId); expect(result.messages.length).toBe(0); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); }) ); })); test("list_feed_messages with pagination", () => - runTest(async (driver) => { + runTest((driver) => { const messages: FeedMessage[] = [ { id: "msg-1", @@ -3704,15 +3652,15 @@ export function generateFullTestSuite(config: { updatedAt: Date.now(), }; - await driver.create_feed(feed); + driver.create_feed(feed); // Add messages one by one for (const message of messages) { - await driver.add_feed_message(feed.feedId, message); + driver.add_feed_message(feed.feedId, message); } // List with limit - const page1 = await driver.list_feed_messages(feed.feedId, { + const page1 = driver.list_feed_messages(feed.feedId, { limit: 2, }); expect(page1.messages.length).toBe(2); @@ -3723,7 +3671,7 @@ export function generateFullTestSuite(config: { expect(page1.messages[1]?.id).toBe("msg-2"); // Get next page - const page2 = await driver.list_feed_messages(feed.feedId, { + const page2 = driver.list_feed_messages(feed.feedId, { cursor: page1.nextCursor, }); expect(page2.messages.length).toBe(1); @@ -3731,46 +3679,42 @@ export function generateFullTestSuite(config: { expect(page2.nextCursor).toBeUndefined(); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); })); test("update_feed_message", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( + fc.property( arb.feed(), arb.feedMessage(), arb.json(), - async (feed, message, newData) => { + (feed, message, newData) => { // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); // Add message to the feed - await driver.add_feed_message(feed.feedId, message); + driver.add_feed_message(feed.feedId, message); - await driver.update_feed_message( - feed.feedId, - message.id, - newData - ); + driver.update_feed_message(feed.feedId, message.id, newData); - const result = await driver.list_feed_messages(feed.feedId); + const result = driver.list_feed_messages(feed.feedId); const updatedMessage = result.messages.find( (m) => m.id === message.id ); expect(updatedMessage?.data).toEqual(newData); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); } ) ); })); test("update_feed_message ignores stale timestamped updates for same message", () => - runTest(async (driver) => { + runTest((driver) => { const feed: Feed = { feedId: "test-feed", metadata: {}, @@ -3784,11 +3728,11 @@ export function generateFullTestSuite(config: { data: { value: "original" }, }; - await driver.delete_feed(feed.feedId); - await driver.create_feed(feed); - await driver.add_feed_message(feed.feedId, message); + driver.delete_feed(feed.feedId); + driver.create_feed(feed); + driver.add_feed_message(feed.feedId, message); - const newer = await driver.update_feed_message( + const newer = driver.update_feed_message( feed.feedId, message.id, { value: "newer-update" }, @@ -3797,7 +3741,7 @@ export function generateFullTestSuite(config: { expect(newer.updatedAt).toBe(2000); expect(newer.data).toEqual({ value: "newer-update" }); - const stale = await driver.update_feed_message( + const stale = driver.update_feed_message( feed.feedId, message.id, { value: "stale-update" }, @@ -3806,16 +3750,16 @@ export function generateFullTestSuite(config: { expect(stale.updatedAt).toBe(2000); expect(stale.data).toEqual({ value: "newer-update" }); - const result = await driver.list_feed_messages(feed.feedId); + const result = driver.list_feed_messages(feed.feedId); const latest = result.messages.find((m) => m.id === message.id); expect(latest?.updatedAt).toBe(2000); expect(latest?.data).toEqual({ value: "newer-update" }); - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); })); test("list_feed_messages order remains by createdAt after update", () => - runTest(async (driver) => { + runTest((driver) => { const feed: Feed = { feedId: "test-feed", metadata: {}, @@ -3837,24 +3781,24 @@ export function generateFullTestSuite(config: { }, ]; - await driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.delete_feed(feed.feedId); + driver.create_feed(feed); for (const m of messages) { - await driver.add_feed_message(feed.feedId, m); + driver.add_feed_message(feed.feedId, m); } - const beforeUpdate = await driver.list_feed_messages(feed.feedId); + const beforeUpdate = driver.list_feed_messages(feed.feedId); expect(beforeUpdate.messages[0]?.id).toBe("msg-second"); expect(beforeUpdate.messages[1]?.id).toBe("msg-first"); - await driver.update_feed_message( + driver.update_feed_message( feed.feedId, "msg-first", { value: "first-updated" }, 9999 ); - const afterUpdate = await driver.list_feed_messages(feed.feedId); + const afterUpdate = driver.list_feed_messages(feed.feedId); expect(afterUpdate.messages[0]?.id).toBe("msg-second"); expect(afterUpdate.messages[1]?.id).toBe("msg-first"); expect(afterUpdate.messages[1]?.data).toEqual({ @@ -3862,92 +3806,77 @@ export function generateFullTestSuite(config: { }); expect(afterUpdate.messages[1]?.updatedAt).toBe(9999); - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); })); test("delete_feed_message", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - // Add message to the feed - await driver.add_feed_message(feed.feedId, message); + // Add message to the feed + driver.add_feed_message(feed.feedId, message); - await driver.delete_feed_message(feed.feedId, message.id); + driver.delete_feed_message(feed.feedId, message.id); - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages.length).toBe(0); - expect( - result.messages.find((m) => m.id === message.id) - ).toBeUndefined(); + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages.length).toBe(0); + expect( + result.messages.find((m) => m.id === message.id) + ).toBeUndefined(); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("delete_feed_message on non-existent message is no-op", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - // Add message to the feed - await driver.add_feed_message(feed.feedId, message); + // Add message to the feed + driver.add_feed_message(feed.feedId, message); - await driver.delete_feed_message( - feed.feedId, - "non-existent-message-id" - ); + driver.delete_feed_message(feed.feedId, "non-existent-message-id"); - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages.length).toBe(1); + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages.length).toBe(1); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("delete_feed deletes all messages", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); - await driver.add_feed_message(feed.feedId, message); + driver.create_feed(feed); + driver.add_feed_message(feed.feedId, message); - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - // Feed should be gone - expect(await driver.get_feed(feed.feedId)).toBeUndefined(); + // Feed should be gone + expect(driver.get_feed(feed.feedId)).toBeUndefined(); - // Messages should be gone too - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages).toEqual([]); - } - ) + // Messages should be gone too + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages).toEqual([]); + }) ); })); }); @@ -5570,7 +5499,7 @@ export function generateFullTestSuite(config: { // Pre-populate with yjs updates if provided if (yjsUpdates.length > 0) { for (const update of yjsUpdates) { - await driver.write_y_updates(guid, nanoid(), update); + driver.write_y_updates(guid, nanoid(), update); } } diff --git a/packages/liveblocks-server/test/room.test.ts b/packages/liveblocks-server/test/room.test.ts index 2963fe71e1..8de34d2d3b 100644 --- a/packages/liveblocks-server/test/room.test.ts +++ b/packages/liveblocks-server/test/room.test.ts @@ -116,7 +116,7 @@ describe("room", () => { test.skip('room will throw "no such session" errors when used before the session is started', async () => { const room = new Room("my-room"); await room.load(); - const ticket = await room.createTicket(); + const ticket = room.createTicket(); const key = ticket.sessionKey; // Errors will happen if used before .startBrowserSession() is used @@ -127,11 +127,11 @@ describe("room", () => { test("starting multiple sessions", async () => { const room = new Room("my-room"); await room.load(); - const ticket1 = await room.createTicket(); - const ticket2 = await room.createTicket(); - const ticket3 = await room.createTicket(); - const ticket4 = await room.createTicket(); - const ticket5 = await room.createTicket(); + const ticket1 = room.createTicket(); + const ticket2 = room.createTicket(); + const ticket3 = room.createTicket(); + const ticket4 = room.createTicket(); + const ticket5 = room.createTicket(); await room.startBrowserSession(ticket1, new MockServerWebSocket()); await room.startBrowserSession(ticket2, new MockServerWebSocket()); await room.startBrowserSession(ticket3, new MockServerWebSocket()); @@ -143,9 +143,9 @@ describe("room", () => { test("starting a second session for an actor kicks the first one", async () => { const room = new Room("my-room"); await room.load(); - const ticket1 = await room.createTicket({ actor: 13 as ActorID }); - const ticket2 = await room.createTicket(); - const ticket3 = await room.createTicket({ actor: 13 as ActorID }); + const ticket1 = room.createTicket({ actor: 13 as ActorID }); + const ticket2 = room.createTicket(); + const ticket3 = room.createTicket({ actor: 13 as ActorID }); expect(room.numSessions).toEqual(0); await room.startBrowserSession(ticket1, new MockServerWebSocket()); expect(room.numSessions).toEqual(1); @@ -158,7 +158,7 @@ describe("room", () => { test("enter + leave", async () => { const room = new Room("my-room"); await room.load(); - const ticket = await room.createTicket(); + const ticket = room.createTicket(); await room.startBrowserSession(ticket, new MockServerWebSocket()); room.endBrowserSession(ticket.sessionKey, 1001, "bleh"); }); @@ -264,11 +264,9 @@ describe("room (w/ last actor ID)", () => { // Create 1000 tickets const actors = new Set( - ( - await Promise.all( - Array.from({ length: 1000 }).map(() => room.createTicket()) - ) - ).map((t) => t.actor) + Array.from({ length: 1000 }) + .map(() => room.createTicket()) + .map((t) => t.actor) ); // The set contains 1000 unique elements diff --git a/packages/liveblocks-server/test/storage/model-based/index.model.test.ts b/packages/liveblocks-server/test/storage/model-based/index.model.test.ts index a86425bc1d..7c00a44b99 100644 --- a/packages/liveblocks-server/test/storage/model-based/index.model.test.ts +++ b/packages/liveblocks-server/test/storage/model-based/index.model.test.ts @@ -43,21 +43,20 @@ describe("Storage (model-based test)", () => { }), }), - async ({ initialNodes, commands }) => { + ({ initialNodes, commands }) => // Set up real and reference model // In this case, there is no reference model, because the real model // will perform a self-check - await runWithStorage(initialNodes, async ({ storage: real }) => { - await selfCheck(real); + runWithStorage(initialNodes, ({ storage: real }) => { + selfCheck(real); const model = new Model(real.loadedDriver.iter_nodes()); // Tries running randomized sequences of commands (think calling // "applyOp()" a million times) - await fc.asyncModelRun(() => ({ model, real }), commands); - }); - } + fc.modelRun(() => ({ model, real }), commands); + }) ), { numRuns: 200, // Stop after 200 iterations, or... diff --git a/packages/liveblocks-server/test/storage/model-based/storage-model.ts b/packages/liveblocks-server/test/storage/model-based/storage-model.ts index d83fd0a6e2..2f51abcf8f 100644 --- a/packages/liveblocks-server/test/storage/model-based/storage-model.ts +++ b/packages/liveblocks-server/test/storage/model-based/storage-model.ts @@ -52,7 +52,7 @@ export class Model { } } -class ApplyOpCommand implements fc.AsyncCommand { +class ApplyOpCommand implements fc.Command { constructor( readonly op: ClientWireOp, readonly version: ProtocolVersion @@ -94,8 +94,8 @@ class ApplyOpCommand implements fc.AsyncCommand { * deleting nodes that don't exist are much less likely to be produced. (See * the `check` method above.) */ - async run(model: Model, real: RealStorage): Promise { - await real.applyOps([this.op]); + run(model: Model, real: RealStorage): void { + real.applyOps([this.op]); switch (this.op.type) { case OpCode.CREATE_OBJECT: @@ -132,7 +132,7 @@ class ApplyOpCommand implements fc.AsyncCommand { return assertNever(this.op, "Unhandled case"); } - await selfCheck(real); + selfCheck(real); } /** diff --git a/packages/liveblocks-server/test/storage/push.test.ts b/packages/liveblocks-server/test/storage/push.test.ts index f89783da94..3afcb7eaa4 100644 --- a/packages/liveblocks-server/test/storage/push.test.ts +++ b/packages/liveblocks-server/test/storage/push.test.ts @@ -33,11 +33,11 @@ describe("push intent — server-authoritative append", () => { test("keeps the guessed position when it already sorts after the tail (no fix)", () => runWithStorage( [rootObj(), list("0:1", "root", "list")], - async ({ storage, loadedDriver }) => { + ({ storage, loadedDriver }) => { // Empty list; the client guessed THIRD. It already sorts after every // existing sibling (there are none), so the server keeps it as-is // rather than pointlessly relocating it to the canonical first slot. - const [res] = await storage.applyOps([ + const [res] = storage.applyOps([ createRegisterOp("1:0", "0:1", THIRD, "a", "push"), ]); @@ -56,10 +56,10 @@ describe("push intent — server-authoritative append", () => { list("0:1", "root", "list"), register("0:2", "0:1", FIRST, "a"), ], - async ({ storage, loadedDriver }) => { + ({ storage, loadedDriver }) => { // Client knows about "a" and guesses SECOND, which is also where the // server appends — no correction needed. - const [res] = await storage.applyOps([ + const [res] = storage.applyOps([ createRegisterOp("1:0", "0:1", SECOND, "b", "push"), ]); @@ -79,9 +79,9 @@ describe("push intent — server-authoritative append", () => { register("0:2", "0:1", FIRST, "a"), register("0:3", "0:1", SECOND, "b"), ], - async ({ storage, loadedDriver }) => { + ({ storage, loadedDriver }) => { // Stale guess of the head position; the server appends after "b". - const [res] = await storage.applyOps([ + const [res] = storage.applyOps([ createRegisterOp("1:0", "0:1", FIRST, "c", "push"), ]); @@ -100,11 +100,11 @@ describe("push intent — server-authoritative append", () => { test("concurrent pushes all guessing the head settle in arrival order", () => runWithStorage( [rootObj(), list("0:1", "root", "list")], - async ({ storage, loadedDriver }) => { + ({ storage, loadedDriver }) => { // Three independent clients each guess the head position. Applied // serially (as the room mutex guarantees), each appends after the // previous one — strictly increasing keys, no wedge. - await storage.applyOps([ + storage.applyOps([ createRegisterOp("1:0", "0:1", FIRST, "a", "push"), createRegisterOp("2:0", "0:1", FIRST, "b", "push"), createRegisterOp("3:0", "0:1", FIRST, "c", "push"), diff --git a/packages/liveblocks-server/test/storage/utils.ts b/packages/liveblocks-server/test/storage/utils.ts index 0ffd4e20cf..a5fce341dd 100644 --- a/packages/liveblocks-server/test/storage/utils.ts +++ b/packages/liveblocks-server/test/storage/utils.ts @@ -97,7 +97,7 @@ export async function runWithStorage( // Also run an integrity check after initializing _corrupted_ storage. // Because the Storage class ignores any such corruptions, even when loading // corruptions the in-memory nodemap should be consistent. - await selfCheck(storage); + selfCheck(storage); return callback({ storage, loadedDriver: storage.loadedDriver }); } diff --git a/tools/liveblocks-cli/src/dev-server/index.ts b/tools/liveblocks-cli/src/dev-server/index.ts index da23d1d97a..0f23b3e848 100644 --- a/tools/liveblocks-cli/src/dev-server/index.ts +++ b/tools/liveblocks-cli/src/dev-server/index.ts @@ -267,7 +267,7 @@ const dev: SubCommand = { const room = Rooms.getRoomInstance(roomId); await room.load(); - const ticket = await room.createTicket(ticketData); + const ticket = room.createTicket(ticketData); const sessionKey = ticket.sessionKey; const success = server.upgrade(req, { data: { room, ticket, sessionKey }, diff --git a/tools/liveblocks-cli/src/dev-server/routes/rest-api.ts b/tools/liveblocks-cli/src/dev-server/routes/rest-api.ts index d55286cc95..cee0a1a98b 100644 --- a/tools/liveblocks-cli/src/dev-server/routes/rest-api.ts +++ b/tools/liveblocks-cli/src/dev-server/routes/rest-api.ts @@ -335,7 +335,7 @@ zen.route( } // Initialize storage - await room.driver.DANGEROUSLY_reset_nodes(body); + room.driver.DANGEROUSLY_reset_nodes(body); room.unload(); return new Response(JSON.stringify(body), { @@ -360,7 +360,7 @@ zen.route("DELETE /v2/rooms//storage", async ({ p }) => { liveblocksType: "LiveObject", data: {}, }; - await room.driver.DANGEROUSLY_reset_nodes(emptyStorage); + room.driver.DANGEROUSLY_reset_nodes(emptyStorage); room.unload(); return new Response(null, { status: 204 }); @@ -641,7 +641,7 @@ zen.route( const room = Rooms.getRoomInstance(p.roomId); await room.load(); - const actor = await room.driver.next_actor(); + const actor = room.getNextActor(); const snapshot = room.storage.loadedDriver.get_snapshot(false); return ndjsonStream( chain([{ actor }], snapshotToNodeStream(snapshot)) @@ -663,7 +663,7 @@ zen.route( await room.load(); const [session, capturedServerMsgs] = - await room.createBackendSession_experimental(); + room.createBackendSession_experimental(); await room.processClientMsgFromBackendSession(session, body.messages); diff --git a/tools/liveblocks-cli/test/devserver/rest-api/ydoc.test.ts b/tools/liveblocks-cli/test/devserver/rest-api/ydoc.test.ts index a312c1faed..59d5f34626 100644 --- a/tools/liveblocks-cli/test/devserver/rest-api/ydoc.test.ts +++ b/tools/liveblocks-cli/test/devserver/rest-api/ydoc.test.ts @@ -76,7 +76,7 @@ describe("PUT /v2/rooms//ydoc", () => { const room = Rooms.getRoomInstance(roomId); await room.load(); const { received, socket } = makeFakeSocket(); - const ticket = await room.createTicket({}); + const ticket = room.createTicket({}); await room.startBrowserSession(ticket, socket); // Mark how much was sent during session bootstrap so we only inspect diff --git a/tools/liveblocks-cli/test/plugins/_generateFullTestSuite.ts b/tools/liveblocks-cli/test/plugins/_generateFullTestSuite.ts index 9802f7eeed..2790f84d45 100644 --- a/tools/liveblocks-cli/test/plugins/_generateFullTestSuite.ts +++ b/tools/liveblocks-cli/test/plugins/_generateFullTestSuite.ts @@ -162,8 +162,8 @@ function wouldNotOverwriteDefaultDoc(n: { * exist, in order to express tests more succinctly. */ async function withDefaultDocument(driver: IStorageDriver) { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, [ list("0:dl", "root", "l"), @@ -199,7 +199,7 @@ function assert( * (in-memory and on-disk), and will throw if _anything_ is in an inconsistent * state. */ -export async function selfCheck(storage: Storage): Promise { +export function selfCheck(storage: Storage): void { const driver: IStorageDriverNodeAPI = storage.loadedDriver; { @@ -261,7 +261,7 @@ export async function selfCheck(storage: Storage): Promise { // Loading the persisted data back in from storage now, it should match // whatever we still have in memory (ignoring the metadata and usage // metrics keys) - const onDiskNodes = new Map(await storage.raw_iter_nodes()); + const onDiskNodes = new Map(storage.raw_iter_nodes()); for (const [id, inMemoryNode] of driver.iter_nodes()) { if (inMemoryNode.parentId === undefined) { // This is the root node, which needs special treatment. Root nodes are @@ -1029,7 +1029,7 @@ async function delete_nodes(db: IStorageDriverNodeAPI, ids: Iterable) { /** A black hole, where logs disappear beyond the event horizon */ const blackHole = new LoggerImpl(new VoidTarget()); -type TestFn = (driver: TDriver) => Promise; +type TestFn = (driver: TDriver) => Awaitable; type RunTestOptions = { initialNodes?: NodeMap }; export function generateFullTestSuite(config: { @@ -1078,8 +1078,8 @@ export function generateFullTestSuite(config: { describe("nodes API impl", () => { test("get_node and iter_nodes with empty store contains root", () => - runTest(async (driver) => { - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + const db = driver.load_nodes_api(blackHole); expect(db.get_node("non-existing")).toEqual(undefined); // Root node always exists, even in an empty store @@ -1090,15 +1090,15 @@ export function generateFullTestSuite(config: { })); test("get_node and iter_nodes with empty store contains root", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), - async (key) => { + (key) => { fc.pre(key !== "root"); - const db = await driver.load_nodes_api(blackHole); + const db = driver.load_nodes_api(blackHole); expect(db.get_node(key)).toEqual(undefined); expect(Array.from(db.iter_nodes())).toEqual([ ["root", { type: CrdtType.OBJECT, data: {} }], @@ -1141,16 +1141,16 @@ export function generateFullTestSuite(config: { )); test("has_node: root always exists", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_node("root")).toBe(true); })); test("has_node: non-existing node returns false", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_node("non-existing")).toBe(false); })); @@ -1180,8 +1180,8 @@ export function generateFullTestSuite(config: { arb.nodeStream(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); // has_node should be true iff get_node returns a value @@ -1194,23 +1194,23 @@ export function generateFullTestSuite(config: { )); test("get_child_at: returns undefined for empty store", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.get_child_at("root", "someKey")).toBe(undefined); expect(db.get_child_at("non-existing", "someKey")).toBe(undefined); })); test("get_child_at: returns undefined for empty store (property)", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), arb.key(), - async (parentId, parentKey) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + (parentId, parentKey) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.get_child_at(parentId, parentKey)).toBe(undefined); } ) @@ -1219,8 +1219,8 @@ export function generateFullTestSuite(config: { test("set_child: throws if parent does not exist (orphan)", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Trying to add a node with a non-existent parentId should throw await expectToThrow( @@ -1236,8 +1236,8 @@ export function generateFullTestSuite(config: { test("set_child: throws if parent does not exist (ref cycle)", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Trying to add a node with a non-existent parentId should throw await expectToThrow( @@ -1253,8 +1253,8 @@ export function generateFullTestSuite(config: { test("set_child: orphan node is not added after throw", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Try to add orphan, catch the error try { @@ -1318,8 +1318,8 @@ export function generateFullTestSuite(config: { test("get_child_at: returns child id after set", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1333,8 +1333,8 @@ export function generateFullTestSuite(config: { test("get_child_at: returns undefined after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1349,8 +1349,8 @@ export function generateFullTestSuite(config: { test("get_child_at: returns undefined after set_object_data would overwrite it", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1365,8 +1365,8 @@ export function generateFullTestSuite(config: { test("get_child_at: tracks nested children", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // root -> list -> register await db.set_child("0:0", { @@ -1393,8 +1393,8 @@ export function generateFullTestSuite(config: { arb.nodeStream(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); // For each node with a parent, get_child_at(parentId, parentKey) should return the node's id @@ -1412,23 +1412,23 @@ export function generateFullTestSuite(config: { )); test("has_child_at: returns false for empty store", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_child_at("root", "someKey")).toBe(false); expect(db.has_child_at("non-existing", "someKey")).toBe(false); })); test("has_child_at: returns false for empty store (property)", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), arb.key(), - async (parentId, parentKey) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + (parentId, parentKey) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_child_at(parentId, parentKey)).toBe(false); } ) @@ -1437,8 +1437,8 @@ export function generateFullTestSuite(config: { test("has_child_at: returns true after set, false after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.has_child_at("root", "myList")).toBe(false); @@ -1464,8 +1464,8 @@ export function generateFullTestSuite(config: { arb.key(), async (entries, parentId, parentKey) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); // has_child_at should be true iff get_child_at returns a value @@ -1478,9 +1478,9 @@ export function generateFullTestSuite(config: { )); test("get_next_sibling: returns undefined for empty parent", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // No children under root, so no next position expect(db.get_next_sibling("root", FIRST_POSITION)).toBe(undefined); @@ -1491,8 +1491,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: returns undefined when no positions after", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Add a list with one item at FIRST_POSITION await db.set_child("0:0", { @@ -1517,8 +1517,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: finds next position in ordered list", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create list with items at FIRST, SECOND, THIRD positions await db.set_child("0:0", { @@ -1557,8 +1557,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: updates after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1596,9 +1596,9 @@ export function generateFullTestSuite(config: { })); test("get_last_sibling: returns undefined for empty parent", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(db.get_last_sibling("root")).toBe(undefined); expect(db.get_last_sibling("non-existing")).toBe(undefined); @@ -1606,8 +1606,8 @@ export function generateFullTestSuite(config: { test("get_last_sibling: returns the rightmost position", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1640,8 +1640,8 @@ export function generateFullTestSuite(config: { test("get_last_sibling: updates after delete", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1672,8 +1672,8 @@ export function generateFullTestSuite(config: { test("move: changes parentKey of node", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1707,8 +1707,8 @@ export function generateFullTestSuite(config: { test("move: updates get_next_sibling correctly", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1745,8 +1745,8 @@ export function generateFullTestSuite(config: { test("move: multiple moves on same node", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1774,8 +1774,8 @@ export function generateFullTestSuite(config: { test("move: preserves other siblings", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1817,8 +1817,8 @@ export function generateFullTestSuite(config: { test("move: throws when target position is occupied", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1853,8 +1853,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: removes static data key from object", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2, c: 3 }); @@ -1873,8 +1873,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: removes child node", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_child("0:0", { type: CrdtType.LIST, @@ -1893,8 +1893,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: removes child node and its descendants recursively", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // root -> list -> register await db.set_child("0:0", { @@ -1928,8 +1928,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: no-op for non-existing key", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1 }); @@ -1945,8 +1945,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: preserves sibling data and nodes", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2 }); await db.set_child("0:0", { @@ -1981,8 +1981,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries as NodeStream); @@ -2075,8 +2075,8 @@ export function generateFullTestSuite(config: { test("deleting the root is a no-op", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { foo: 42 }); // Try to delete the root node @@ -2096,8 +2096,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(Array.from(db.iter_nodes())).toEqual([ ["root", { type: CrdtType.OBJECT, data: {} }], ]); @@ -2122,8 +2122,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); expect(Array.from(db.iter_nodes())).toEqual([ ["root", { type: CrdtType.OBJECT, data: {} }], ]); @@ -2156,8 +2156,8 @@ export function generateFullTestSuite(config: { arb.nodeMap(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries as NodeStream); // Parse the wire tuples back into CompactNodes and compare @@ -2187,8 +2187,8 @@ export function generateFullTestSuite(config: { arb.nodeStream(), async (entries) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await write_nodes(db, entries); await delete_nodes( @@ -2361,8 +2361,8 @@ export function generateFullTestSuite(config: { test("get_snapshot: delete_child_key on static data does not affect snapshot", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2, c: 3 }); // Take snapshot before deleting a key @@ -2386,8 +2386,8 @@ export function generateFullTestSuite(config: { test("get_snapshot: set_object_data after snapshot does not affect snapshot", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); await db.set_object_data("root", { a: 1, b: 2 }); // Take snapshot before mutating @@ -2469,9 +2469,9 @@ export function generateFullTestSuite(config: { })); test("get_snapshot: iter_all on empty room returns only root", () => - runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + runTest((driver) => { + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); const snapshot = db.get_snapshot(); const nodes = [...snapshot.iter_all()]; expect(nodes).toEqual([["root", { type: CrdtType.OBJECT, data: {} }]]); @@ -2485,8 +2485,8 @@ export function generateFullTestSuite(config: { test("set: read sees new node before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); const node = { type: CrdtType.LIST, @@ -2513,8 +2513,8 @@ export function generateFullTestSuite(config: { test("delete: read sees deletion before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // First create a node await db.set_child("0:0", { @@ -2543,8 +2543,8 @@ export function generateFullTestSuite(config: { test("move: read sees new position before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a list with an item await db.set_child("0:0", { @@ -2588,8 +2588,8 @@ export function generateFullTestSuite(config: { test("set_object_data: read sees new data before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Start with empty root expect(db.get_node("root")).toEqual({ @@ -2641,8 +2641,8 @@ export function generateFullTestSuite(config: { test("delete_child_key: read sees deletion before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Set up root with static data and a child node await db.set_object_data("root", { a: 1, b: 2 }); @@ -2686,8 +2686,8 @@ export function generateFullTestSuite(config: { test("multiple writes: all visible before any await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Perform multiple writes without awaiting any of them const p1$ = db.set_child("0:0", { @@ -2732,8 +2732,8 @@ export function generateFullTestSuite(config: { test("interleaved write and delete: reads see correct state", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a node const p1$ = db.set_child("0:0", { @@ -2777,8 +2777,8 @@ export function generateFullTestSuite(config: { test("get_next_sibling: updated before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a list with two items await db.set_child("0:0", { @@ -2829,8 +2829,8 @@ export function generateFullTestSuite(config: { test("set with overwrite: read sees update before await", () => runTest(async (driver) => { - await driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); - const db = await driver.load_nodes_api(blackHole); + driver.DANGEROUSLY_reset_nodes(EMPTY_DOC); + const db = driver.load_nodes_api(blackHole); // Create a list to hold registers await db.set_child("0:0", { @@ -2887,45 +2887,45 @@ export function generateFullTestSuite(config: { describe("meta API impl", () => { test("get_meta with empty store is undefined", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), - async (key) => { - expect(await driver.get_meta(key)).toEqual(undefined); + (key) => { + expect(driver.get_meta(key)).toEqual(undefined); } ) ) )); test("put_meta + get_meta", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.metaPair(), - async ([key, value]) => { - await driver.put_meta(key, value); - expect(await driver.get_meta(key)).toEqual(value); + ([key, value]) => { + driver.put_meta(key, value); + expect(driver.get_meta(key)).toEqual(value); } ) ) )); test("put_meta + get_meta loop", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc.array(arb.metaPair()).map((x) => new Map(x)), - async (entries) => { + (entries) => { for (const [key, value] of entries) { - await driver.put_meta(key, value); + driver.put_meta(key, value); } for (const [key, value] of entries) { - expect(await driver.get_meta(key)).toEqual(value); + expect(driver.get_meta(key)).toEqual(value); } } ) @@ -2933,20 +2933,20 @@ export function generateFullTestSuite(config: { )); test("put_meta + delete_meta + get_meta", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.key(), arb.json(), arb.json(), - async (key, value1, value2) => { - await driver.put_meta(key, value1); - await driver.put_meta(key, value2); // override - expect(await driver.get_meta(key)).toEqual(value2); + (key, value1, value2) => { + driver.put_meta(key, value1); + driver.put_meta(key, value2); // override + expect(driver.get_meta(key)).toEqual(value2); - await driver.delete_meta(key); - expect(await driver.get_meta(key)).toEqual(undefined); + driver.delete_meta(key); + expect(driver.get_meta(key)).toEqual(undefined); } ) ) @@ -2969,7 +2969,7 @@ export function generateFullTestSuite(config: { // Check that readone will also have the same results const map = new Map(entries); // de-dupe for (const [key, expected] of map) { - expect(await driver.get_meta(key)).toEqual(expected); + expect(driver.get_meta(key)).toEqual(expected); } } ) @@ -2977,21 +2977,21 @@ export function generateFullTestSuite(config: { )); test("put_meta + get_meta loop", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.metaEntries(), - async (entries) => { + (entries) => { // Write all the entries (can have dupes) for (const [key, value] of entries) { - await driver.put_meta(key, value); + driver.put_meta(key, value); } // Check that readone will also have the same results const map = new Map(entries); // de-dupe for (const [key, expected] of map) { - expect(await driver.get_meta(key)).toEqual(expected); + expect(driver.get_meta(key)).toEqual(expected); } } ) @@ -2999,21 +2999,21 @@ export function generateFullTestSuite(config: { )); test("delete_meta (all keys)", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.metaEntries(), - async (entries) => { + (entries) => { for (const [key, value] of entries) { - await driver.put_meta(key, value); + driver.put_meta(key, value); } for (const key of new Map(entries).keys()) { - await driver.delete_meta(key); + driver.delete_meta(key); } for (const [key] of entries) { - expect(await driver.get_meta(key)).toEqual(undefined); + expect(driver.get_meta(key)).toEqual(undefined); } } ) @@ -3043,19 +3043,19 @@ export function generateFullTestSuite(config: { const key2 = entry2[0]; const key3 = entry3[0]; - expect(await driver.get_meta(key1)).not.toEqual(undefined); - expect(await driver.get_meta(key2)).not.toEqual(undefined); - expect(await driver.get_meta(key3)).not.toEqual(undefined); - await driver.delete_meta(key1); - await driver.delete_meta(key1); - expect(await driver.get_meta(key1)).toEqual(undefined); - expect(await driver.get_meta(key2)).not.toEqual(undefined); - expect(await driver.get_meta(key3)).not.toEqual(undefined); - await driver.delete_meta(key2); - await driver.delete_meta(key2); - expect(await driver.get_meta(key1)).toEqual(undefined); - expect(await driver.get_meta(key2)).toEqual(undefined); - expect(await driver.get_meta(key3)).not.toEqual(undefined); + expect(driver.get_meta(key1)).not.toEqual(undefined); + expect(driver.get_meta(key2)).not.toEqual(undefined); + expect(driver.get_meta(key3)).not.toEqual(undefined); + driver.delete_meta(key1); + driver.delete_meta(key1); + expect(driver.get_meta(key1)).toEqual(undefined); + expect(driver.get_meta(key2)).not.toEqual(undefined); + expect(driver.get_meta(key3)).not.toEqual(undefined); + driver.delete_meta(key2); + driver.delete_meta(key2); + expect(driver.get_meta(key1)).toEqual(undefined); + expect(driver.get_meta(key2)).toEqual(undefined); + expect(driver.get_meta(key3)).not.toEqual(undefined); } ) ) @@ -3064,41 +3064,37 @@ export function generateFullTestSuite(config: { describe("ydoc API impl", () => { test("iter_y_updates on an empty store is empty", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.docId(), - async (docId) => { - expect(Array.from(await driver.iter_y_updates(docId))).toEqual( - [] - ); + (docId) => { + expect(Array.from(driver.iter_y_updates(docId))).toEqual([]); } ) ) )); test("write_y_updates + iter_y_updates", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc.tuple(arb.docId(), arb.docId()).filter(([a, b]) => a !== b), fc .array(fc.tuple(arb.key(), fc.uint8Array())) .map((x) => new Map(x)), - async ([docId, anotherDocId], entries) => { - await driver.DANGEROUSLY_wipe_all_y_updates(); + ([docId, anotherDocId], entries) => { + driver.DANGEROUSLY_wipe_all_y_updates(); for (const [key, data] of entries) { - await driver.write_y_updates(docId, key, data); + driver.write_y_updates(docId, key, data); } - expect(new Map(await driver.iter_y_updates(docId))).toEqual( - entries + expect(new Map(driver.iter_y_updates(docId))).toEqual(entries); + expect(new Map(driver.iter_y_updates(anotherDocId))).toEqual( + new Map() ); - expect( - new Map(await driver.iter_y_updates(anotherDocId)) - ).toEqual(new Map()); } ) ) @@ -3107,132 +3103,118 @@ export function generateFullTestSuite(config: { describe("leased session API impl", () => { test("list_leased_sessions on empty store is empty", () => - runTest(async (driver) => { - const sessions = Array.from(await driver.list_leased_sessions()); + runTest((driver) => { + const sessions = Array.from(driver.list_leased_sessions()); expect(sessions).toEqual([]); })); test("get_leased_session on empty store is undefined", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty(arb.sessionId(), async (sessionId) => { - expect(await driver.get_leased_session(sessionId)).toEqual( - undefined - ); + fc.property(arb.sessionId(), (sessionId) => { + expect(driver.get_leased_session(sessionId)).toEqual(undefined); }) ) )); test("put_leased_session + get_leased_session", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.sessionId(), arb.leasedSession(), - async (sessionId, session) => { + (sessionId, session) => { session.sessionId = sessionId; - await driver.put_leased_session(session); - expect(await driver.get_leased_session(sessionId)).toEqual( - session - ); + driver.put_leased_session(session); + expect(driver.get_leased_session(sessionId)).toEqual(session); // Cleanup: delete the session added in this iteration - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } ) ) )); test("put_leased_session (overwrite) + get_leased_session", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.sessionId(), arb.leasedSession(), arb.leasedSession(), - async (sessionId, session1, session2) => { + (sessionId, session1, session2) => { // Put first session session1.sessionId = sessionId; - await driver.put_leased_session(session1); - expect(await driver.get_leased_session(sessionId)).toEqual( - session1 - ); + driver.put_leased_session(session1); + expect(driver.get_leased_session(sessionId)).toEqual(session1); // Overwrite with second session session2.sessionId = sessionId; - await driver.put_leased_session(session2); - expect(await driver.get_leased_session(sessionId)).toEqual( - session2 - ); + driver.put_leased_session(session2); + expect(driver.get_leased_session(sessionId)).toEqual(session2); // Cleanup: delete the session added in this iteration - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } ) ) )); test("put_leased_session + delete_leased_session + get_leased_session", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( arb.sessionId(), arb.leasedSession(), - async (sessionId, session) => { + (sessionId, session) => { session.sessionId = sessionId; - await driver.put_leased_session(session); - expect(await driver.get_leased_session(sessionId)).toEqual( - session - ); + driver.put_leased_session(session); + expect(driver.get_leased_session(sessionId)).toEqual(session); - await driver.delete_leased_session(sessionId); - expect(await driver.get_leased_session(sessionId)).toEqual( - undefined - ); + driver.delete_leased_session(sessionId); + expect(driver.get_leased_session(sessionId)).toEqual(undefined); } ) ) )); test("delete_leased_session on non-existent session is no-op", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty(arb.sessionId(), async (sessionId) => { + fc.property(arb.sessionId(), (sessionId) => { // Deleting a session that doesn't exist should not throw - await driver.delete_leased_session(sessionId); - expect(await driver.get_leased_session(sessionId)).toEqual( - undefined - ); + driver.delete_leased_session(sessionId); + expect(driver.get_leased_session(sessionId)).toEqual(undefined); }) ) )); test("put_leased_session + list_leased_sessions", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc .array(arb.leasedSessionPair()) .map((x) => new Map(x)) .filter((m) => m.size > 0), // At least one session - async (entries) => { + (entries) => { // Put all sessions for (const [sessionId, session] of entries) { session.sessionId = sessionId; - await driver.put_leased_session(session); + driver.put_leased_session(session); } // List should return all sessions - const listed = new Map(await driver.list_leased_sessions()); + const listed = new Map(driver.list_leased_sessions()); expect(listed).toEqual(entries); // Cleanup: delete all sessions added in this iteration for (const [sessionId] of entries) { - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } } ) @@ -3240,28 +3222,28 @@ export function generateFullTestSuite(config: { )); test("put multiple sessions + get each individually", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc.array(arb.leasedSessionPair()).map((x) => new Map(x)), - async (entries) => { + (entries) => { // Put all sessions for (const [sessionId, session] of entries) { session.sessionId = sessionId; - await driver.put_leased_session(session); + driver.put_leased_session(session); } // Get each session individually and verify for (const [sessionId, expectedSession] of entries) { - expect(await driver.get_leased_session(sessionId)).toEqual( + expect(driver.get_leased_session(sessionId)).toEqual( expectedSession ); } // Cleanup: delete all sessions added in this iteration for (const [sessionId] of entries) { - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } } ) @@ -3297,79 +3279,53 @@ export function generateFullTestSuite(config: { ]); // Verify all exist - expect(await driver.get_leased_session(sessionId1)).toEqual( - session1 - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - session2 - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - session3 - ); + expect(driver.get_leased_session(sessionId1)).toEqual(session1); + expect(driver.get_leased_session(sessionId2)).toEqual(session2); + expect(driver.get_leased_session(sessionId3)).toEqual(session3); // Delete first session - await driver.delete_leased_session(sessionId1); - expect(await driver.get_leased_session(sessionId1)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - session2 - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - session3 - ); + driver.delete_leased_session(sessionId1); + expect(driver.get_leased_session(sessionId1)).toEqual(undefined); + expect(driver.get_leased_session(sessionId2)).toEqual(session2); + expect(driver.get_leased_session(sessionId3)).toEqual(session3); // Delete second session - await driver.delete_leased_session(sessionId2); - expect(await driver.get_leased_session(sessionId1)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - session3 - ); + driver.delete_leased_session(sessionId2); + expect(driver.get_leased_session(sessionId1)).toEqual(undefined); + expect(driver.get_leased_session(sessionId2)).toEqual(undefined); + expect(driver.get_leased_session(sessionId3)).toEqual(session3); // Delete third session - await driver.delete_leased_session(sessionId3); - expect(await driver.get_leased_session(sessionId1)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId2)).toEqual( - undefined - ); - expect(await driver.get_leased_session(sessionId3)).toEqual( - undefined - ); + driver.delete_leased_session(sessionId3); + expect(driver.get_leased_session(sessionId1)).toEqual(undefined); + expect(driver.get_leased_session(sessionId2)).toEqual(undefined); + expect(driver.get_leased_session(sessionId3)).toEqual(undefined); // List should be empty - expect(Array.from(await driver.list_leased_sessions())).toEqual( - [] - ); + expect(Array.from(driver.list_leased_sessions())).toEqual([]); } ) ) )); test("list_leased_sessions returns all sessions in any order", () => - runTest(async (driver) => + runTest((driver) => fc.assert( - fc.asyncProperty( + fc.property( fc .array(arb.leasedSessionPair()) .map((x) => new Map(x)) .filter((m) => m.size > 0), - async (entries) => { + (entries) => { // Put all sessions for (const [sessionId, session] of entries) { session.sessionId = sessionId; - await driver.put_leased_session(session); + driver.put_leased_session(session); } // Get listed sessions and convert to a map - const listed = new Map(await driver.list_leased_sessions()); + const listed = new Map(driver.list_leased_sessions()); // Both should have the same size expect(listed.size).toEqual(entries.size); @@ -3381,7 +3337,7 @@ export function generateFullTestSuite(config: { // Cleanup: delete all sessions added in this iteration for (const [sessionId] of entries) { - await driver.delete_leased_session(sessionId); + driver.delete_leased_session(sessionId); } } ) @@ -3433,107 +3389,103 @@ export function generateFullTestSuite(config: { describe("feed API impl", () => { test("list_feeds on empty store is empty", () => - runTest(async (driver) => { - const result = await driver.list_feeds(); + runTest((driver) => { + const result = driver.list_feeds(); expect(result.feeds).toEqual([]); expect(result.nextCursor).toBeUndefined(); })); test("get_feed on empty store is undefined", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feedId(), async (feedId) => { - expect(await driver.get_feed(feedId)).toEqual(undefined); + fc.property(arb.feedId(), (feedId) => { + expect(driver.get_feed(feedId)).toEqual(undefined); }) ); })); test("create_feed + get_feed", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feed(), async (feed) => { - await driver.create_feed(feed); - expect(await driver.get_feed(feed.feedId)).toEqual(feed); + fc.property(arb.feed(), (feed) => { + driver.create_feed(feed); + expect(driver.get_feed(feed.feedId)).toEqual(feed); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); }) ); })); test("update_feed_metadata", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.json(), - async (feed, newMetadata) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.json(), (feed, newMetadata) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - await driver.update_feed_metadata(feed.feedId, newMetadata); + driver.update_feed_metadata(feed.feedId, newMetadata); - const updated = await driver.get_feed(feed.feedId); - expect(updated?.metadata).toEqual(newMetadata); - expect(updated?.feedId).toEqual(feed.feedId); + const updated = driver.get_feed(feed.feedId); + expect(updated?.metadata).toEqual(newMetadata); + expect(updated?.feedId).toEqual(feed.feedId); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("delete_feed", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feed(), async (feed) => { + fc.property(arb.feed(), (feed) => { // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); - expect(await driver.get_feed(feed.feedId)).toEqual(feed); + driver.create_feed(feed); + expect(driver.get_feed(feed.feedId)).toEqual(feed); - await driver.delete_feed(feed.feedId); - expect(await driver.get_feed(feed.feedId)).toBeUndefined(); + driver.delete_feed(feed.feedId); + expect(driver.get_feed(feed.feedId)).toBeUndefined(); }) ); })); test("delete_feed on non-existent feed is no-op", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feedId(), async (feedId) => { - await driver.delete_feed(feedId); - expect(await driver.get_feed(feedId)).toBeUndefined(); + fc.property(arb.feedId(), (feedId) => { + driver.delete_feed(feedId); + expect(driver.get_feed(feedId)).toBeUndefined(); }) ); })); test("list_feeds returns all feeds", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( + fc.property( fc .array(arb.feedPair()) .map((x) => new Map(x)) .filter((m) => m.size > 0 && m.size <= 5), - async (entries) => { + (entries) => { // Ensure feeds don't already exist for (const [feedId] of entries) { - await driver.delete_feed(feedId); + driver.delete_feed(feedId); } // Create all feeds for (const [, feed] of entries) { - await driver.create_feed(feed); + driver.create_feed(feed); } // List all feeds - const result = await driver.list_feeds(); + const result = driver.list_feeds(); // Should return all feeds expect(result.feeds.length).toEqual(entries.size); @@ -3544,7 +3496,7 @@ export function generateFullTestSuite(config: { // Cleanup for (const [feedId] of entries) { - await driver.delete_feed(feedId); + driver.delete_feed(feedId); } } ) @@ -3552,7 +3504,7 @@ export function generateFullTestSuite(config: { })); test("list_feeds with pagination", () => - runTest(async (driver) => { + runTest((driver) => { // Create 3 feeds with different createdAt timestamps const feeds: Feed[] = [ { @@ -3576,11 +3528,11 @@ export function generateFullTestSuite(config: { ]; for (const feed of feeds) { - await driver.create_feed(feed); + driver.create_feed(feed); } // List with limit - const page1 = await driver.list_feeds({ limit: 2 }); + const page1 = driver.list_feeds({ limit: 2 }); expect(page1.feeds.length).toBe(2); expect(page1.nextCursor).toBeDefined(); @@ -3589,7 +3541,7 @@ export function generateFullTestSuite(config: { expect(page1.feeds[1]?.feedId).toBe("feed-2"); // Get next page - const page2 = await driver.list_feeds({ + const page2 = driver.list_feeds({ cursor: page1.nextCursor, }); expect(page2.feeds.length).toBe(1); @@ -3598,12 +3550,12 @@ export function generateFullTestSuite(config: { // Cleanup for (const feed of feeds) { - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); } })); test("list_feeds with since filter", () => - runTest(async (driver) => { + runTest((driver) => { const feeds: Feed[] = [ { feedId: "feed-old", @@ -3620,68 +3572,64 @@ export function generateFullTestSuite(config: { ]; for (const feed of feeds) { - await driver.create_feed(feed); + driver.create_feed(feed); } // List feeds since 2000 - const result = await driver.list_feeds({ since: 2000 }); + const result = driver.list_feeds({ since: 2000 }); expect(result.feeds.length).toBe(1); expect(result.feeds[0]?.feedId).toBe("feed-new"); // Cleanup for (const feed of feeds) { - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); } })); test("add_feed_message", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - // Create feed - await driver.create_feed(feed); + // Create feed + driver.create_feed(feed); - // Add message - await driver.add_feed_message(feed.feedId, message); + // Add message + driver.add_feed_message(feed.feedId, message); - // List messages and verify message was added - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages).toHaveLength(1); - expect(result.messages[0]).toEqual(message); + // List messages and verify message was added + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages).toHaveLength(1); + expect(result.messages[0]).toEqual(message); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("list_feed_messages", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty(arb.feed(), async (feed) => { + fc.property(arb.feed(), (feed) => { // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - const result = await driver.list_feed_messages(feed.feedId); + const result = driver.list_feed_messages(feed.feedId); expect(result.messages.length).toBe(0); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); }) ); })); test("list_feed_messages with pagination", () => - runTest(async (driver) => { + runTest((driver) => { const messages: FeedMessage[] = [ { id: "msg-1", @@ -3710,15 +3658,15 @@ export function generateFullTestSuite(config: { updatedAt: Date.now(), }; - await driver.create_feed(feed); + driver.create_feed(feed); // Add messages one by one for (const message of messages) { - await driver.add_feed_message(feed.feedId, message); + driver.add_feed_message(feed.feedId, message); } // List with limit - const page1 = await driver.list_feed_messages(feed.feedId, { + const page1 = driver.list_feed_messages(feed.feedId, { limit: 2, }); expect(page1.messages.length).toBe(2); @@ -3729,7 +3677,7 @@ export function generateFullTestSuite(config: { expect(page1.messages[1]?.id).toBe("msg-2"); // Get next page - const page2 = await driver.list_feed_messages(feed.feedId, { + const page2 = driver.list_feed_messages(feed.feedId, { cursor: page1.nextCursor, }); expect(page2.messages.length).toBe(1); @@ -3737,46 +3685,42 @@ export function generateFullTestSuite(config: { expect(page2.nextCursor).toBeUndefined(); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); })); test("update_feed_message", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( + fc.property( arb.feed(), arb.feedMessage(), arb.json(), - async (feed, message, newData) => { + (feed, message, newData) => { // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); // Add message to the feed - await driver.add_feed_message(feed.feedId, message); + driver.add_feed_message(feed.feedId, message); - await driver.update_feed_message( - feed.feedId, - message.id, - newData - ); + driver.update_feed_message(feed.feedId, message.id, newData); - const result = await driver.list_feed_messages(feed.feedId); + const result = driver.list_feed_messages(feed.feedId); const updatedMessage = result.messages.find( (m) => m.id === message.id ); expect(updatedMessage?.data).toEqual(newData); // Cleanup - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); } ) ); })); test("update_feed_message ignores stale timestamped updates for same message", () => - runTest(async (driver) => { + runTest((driver) => { const feed: Feed = { feedId: "test-feed", metadata: {}, @@ -3790,11 +3734,11 @@ export function generateFullTestSuite(config: { data: { value: "original" }, }; - await driver.delete_feed(feed.feedId); - await driver.create_feed(feed); - await driver.add_feed_message(feed.feedId, message); + driver.delete_feed(feed.feedId); + driver.create_feed(feed); + driver.add_feed_message(feed.feedId, message); - const newer = await driver.update_feed_message( + const newer = driver.update_feed_message( feed.feedId, message.id, { value: "newer-update" }, @@ -3803,7 +3747,7 @@ export function generateFullTestSuite(config: { expect(newer.updatedAt).toBe(2000); expect(newer.data).toEqual({ value: "newer-update" }); - const stale = await driver.update_feed_message( + const stale = driver.update_feed_message( feed.feedId, message.id, { value: "stale-update" }, @@ -3812,16 +3756,16 @@ export function generateFullTestSuite(config: { expect(stale.updatedAt).toBe(2000); expect(stale.data).toEqual({ value: "newer-update" }); - const result = await driver.list_feed_messages(feed.feedId); + const result = driver.list_feed_messages(feed.feedId); const latest = result.messages.find((m) => m.id === message.id); expect(latest?.updatedAt).toBe(2000); expect(latest?.data).toEqual({ value: "newer-update" }); - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); })); test("list_feed_messages order remains by createdAt after update", () => - runTest(async (driver) => { + runTest((driver) => { const feed: Feed = { feedId: "test-feed", metadata: {}, @@ -3843,24 +3787,24 @@ export function generateFullTestSuite(config: { }, ]; - await driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.delete_feed(feed.feedId); + driver.create_feed(feed); for (const m of messages) { - await driver.add_feed_message(feed.feedId, m); + driver.add_feed_message(feed.feedId, m); } - const beforeUpdate = await driver.list_feed_messages(feed.feedId); + const beforeUpdate = driver.list_feed_messages(feed.feedId); expect(beforeUpdate.messages[0]?.id).toBe("msg-second"); expect(beforeUpdate.messages[1]?.id).toBe("msg-first"); - await driver.update_feed_message( + driver.update_feed_message( feed.feedId, "msg-first", { value: "first-updated" }, 9999 ); - const afterUpdate = await driver.list_feed_messages(feed.feedId); + const afterUpdate = driver.list_feed_messages(feed.feedId); expect(afterUpdate.messages[0]?.id).toBe("msg-second"); expect(afterUpdate.messages[1]?.id).toBe("msg-first"); expect(afterUpdate.messages[1]?.data).toEqual({ @@ -3868,92 +3812,77 @@ export function generateFullTestSuite(config: { }); expect(afterUpdate.messages[1]?.updatedAt).toBe(9999); - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); })); test("delete_feed_message", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - // Add message to the feed - await driver.add_feed_message(feed.feedId, message); + // Add message to the feed + driver.add_feed_message(feed.feedId, message); - await driver.delete_feed_message(feed.feedId, message.id); + driver.delete_feed_message(feed.feedId, message.id); - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages.length).toBe(0); - expect( - result.messages.find((m) => m.id === message.id) - ).toBeUndefined(); + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages.length).toBe(0); + expect( + result.messages.find((m) => m.id === message.id) + ).toBeUndefined(); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("delete_feed_message on non-existent message is no-op", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); + driver.create_feed(feed); - // Add message to the feed - await driver.add_feed_message(feed.feedId, message); + // Add message to the feed + driver.add_feed_message(feed.feedId, message); - await driver.delete_feed_message( - feed.feedId, - "non-existent-message-id" - ); + driver.delete_feed_message(feed.feedId, "non-existent-message-id"); - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages.length).toBe(1); + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages.length).toBe(1); - // Cleanup - await driver.delete_feed(feed.feedId); - } - ) + // Cleanup + driver.delete_feed(feed.feedId); + }) ); })); test("delete_feed deletes all messages", () => - runTest(async (driver) => { + runTest((driver) => { return fc.assert( - fc.asyncProperty( - arb.feed(), - arb.feedMessage(), - async (feed, message) => { - // Ensure feed doesn't already exist - await driver.delete_feed(feed.feedId); + fc.property(arb.feed(), arb.feedMessage(), (feed, message) => { + // Ensure feed doesn't already exist + driver.delete_feed(feed.feedId); - await driver.create_feed(feed); - await driver.add_feed_message(feed.feedId, message); + driver.create_feed(feed); + driver.add_feed_message(feed.feedId, message); - await driver.delete_feed(feed.feedId); + driver.delete_feed(feed.feedId); - // Feed should be gone - expect(await driver.get_feed(feed.feedId)).toBeUndefined(); + // Feed should be gone + expect(driver.get_feed(feed.feedId)).toBeUndefined(); - // Messages should be gone too - const result = await driver.list_feed_messages(feed.feedId); - expect(result.messages).toEqual([]); - } - ) + // Messages should be gone too + const result = driver.list_feed_messages(feed.feedId); + expect(result.messages).toEqual([]); + }) ); })); }); @@ -5576,7 +5505,7 @@ export function generateFullTestSuite(config: { // Pre-populate with yjs updates if provided if (yjsUpdates.length > 0) { for (const update of yjsUpdates) { - await driver.write_y_updates(guid, nanoid(), update); + driver.write_y_updates(guid, nanoid(), update); } }