From 922d0a9eb2709fd560b2ad78a6c19b853c01bc50 Mon Sep 17 00:00:00 2001 From: Andrew Bulat Date: Wed, 12 Feb 2025 07:57:10 +0000 Subject: [PATCH 1/2] Fix LiveObjects docstrings --- src/plugins/liveobjects/livecounter.ts | 2 +- src/plugins/liveobjects/liveobjects.ts | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/plugins/liveobjects/livecounter.ts b/src/plugins/liveobjects/livecounter.ts index c5a7be1bb..9871d235e 100644 --- a/src/plugins/liveobjects/livecounter.ts +++ b/src/plugins/liveobjects/livecounter.ts @@ -143,7 +143,7 @@ export class LiveCounter extends LiveObject } /** - * Alias for calling {@link LiveCounter.increment | LiveCounter.increment(-amount)} + * An alias for calling {@link LiveCounter.increment | LiveCounter.increment(-amount)} */ async decrement(amount: number): Promise { this._liveObjects.throwIfMissingStatePublishMode(); diff --git a/src/plugins/liveobjects/liveobjects.ts b/src/plugins/liveobjects/liveobjects.ts index 7458e3d64..181514e81 100644 --- a/src/plugins/liveobjects/liveobjects.ts +++ b/src/plugins/liveobjects/liveobjects.ts @@ -101,11 +101,10 @@ export class LiveObjects { /** * Send a MAP_CREATE operation to the realtime system to create a new map object in the pool. * - * Locally on the client it creates a zero-value object with the corresponding id and returns it. - * The object initialization with the initial value is expected to happen when the corresponding MAP_CREATE operation is echoed - * back to the client and applied to the object following the regular operation application procedure. + * Once the ACK message is received, the method returns the object from the local pool if it got created due to + * the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally using the provided data and returns it. * - * @returns A promise which resolves upon receiving the ACK message for the published operation message. A promise is resolved with a zero-value object created in the local pool. + * @returns A promise which resolves upon receiving the ACK message for the published operation message. A promise is resolved with an object containing provided data. */ async createMap(entries?: T): Promise> { this.throwIfMissingStatePublishMode(); @@ -134,11 +133,10 @@ export class LiveObjects { /** * Send a COUNTER_CREATE operation to the realtime system to create a new counter object in the pool. * - * Locally on the client it creates a zero-value object with the corresponding id and returns it. - * The object initialization with the initial value is expected to happen when the corresponding COUNTER_CREATE operation is echoed - * back to the client and applied to the object following the regular operation application procedure. + * Once the ACK message is received, the method returns the object from the local pool if it got created due to + * the echoed COUNTER_CREATE operation, or if it wasn't received yet, the method creates a new object locally using the provided data and returns it. * - * @returns A promise which resolves upon receiving the ACK message for the published operation message. A promise is resolved with a zero-value object created in the local pool. + * @returns A promise which resolves upon receiving the ACK message for the published operation message. A promise is resolved with an object containing provided data. */ async createCounter(count?: number): Promise { this.throwIfMissingStatePublishMode(); From f2dcf8a08c30c3bb4d6ec6dc7560f6a82648feac Mon Sep 17 00:00:00 2001 From: Andrew Bulat Date: Wed, 12 Feb 2025 08:21:36 +0000 Subject: [PATCH 2/2] Add type declarations to `ably.d.ts` for Live Objects write, batch and lifecycle events APIs --- ably.d.ts | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/ably.d.ts b/ably.d.ts index eaccb3ccb..4ed3c0811 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -1566,6 +1566,25 @@ export type ErrorCallback = (error: ErrorInfo | null) => void; */ export type LiveObjectUpdateCallback = (update: T) => void; +/** + * The callback used for the events emitted by {@link LiveObjects}. + */ +export type LiveObjectsEventCallback = () => void; + +/** + * The callback used for the lifecycle events emitted by {@link LiveObject}. + */ +export type LiveObjectLifecycleEventCallback = () => void; + +/** + * A function passed to {@link LiveObjects.batch} to group multiple Live Object operations into a single channel message. + * + * Must not be `async`. + * + * @param batchContext - A {@link BatchContext} object that allows grouping operations on Live Objects for this batch. + */ +export type BatchCallback = (batchContext: BatchContext) => void; + // Internal Interfaces // To allow a uniform (callback) interface between on and once even in the @@ -2032,6 +2051,40 @@ export declare interface PushChannel { listSubscriptions(params?: Record): Promise>; } +/** + * The `LiveObjectsEvents` namespace describes the possible values of the {@link LiveObjectsEvent} type. + */ +declare namespace LiveObjectsEvents { + /** + * The local Live Objects state is currently being synchronized with the Ably service. + */ + type SYNCING = 'syncing'; + /** + * The local Live Objects state has been synchronized with the Ably service. + */ + type SYNCED = 'synced'; +} + +/** + * Describes the events emitted by a {@link LiveObjects} object. + */ +export type LiveObjectsEvent = LiveObjectsEvents.SYNCED | LiveObjectsEvents.SYNCING; + +/** + * The `LiveObjectLifecycleEvents` namespace describes the possible values of the {@link LiveObjectLifecycleEvent} type. + */ +declare namespace LiveObjectLifecycleEvents { + /** + * Indicates that the object has been deleted from the Live Objects pool and should no longer be interacted with. + */ + type DELETED = 'deleted'; +} + +/** + * Describes the events emitted by a {@link LiveObject} object. + */ +export type LiveObjectLifecycleEvent = LiveObjectLifecycleEvents.DELETED; + /** * Enables the LiveObjects state to be subscribed to for a channel. */ @@ -2062,6 +2115,59 @@ export declare interface LiveObjects { * @returns A promise which, upon success, will be fulfilled with a {@link LiveMap} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. */ getRoot(): Promise>; + + /** + * Creates a new {@link LiveMap} object instance with the provided entries. + * + * @param entries - The initial entries for the new {@link LiveMap} object. + * @returns A promise which, upon success, will be fulfilled with a {@link LiveMap} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + createMap(entries?: T): Promise>; + + /** + * Creates a new {@link LiveCounter} object instance with the provided `count` value. + * + * @param count - The initial value for the new {@link LiveCounter} object. + * @returns A promise which, upon success, will be fulfilled with a {@link LiveCounter} object. Upon failure, the promise will be rejected with an {@link ErrorInfo} object which explains the error. + */ + createCounter(count?: number): Promise; + + /** + * Allows you to group multiple operations together and send them to the Ably service in a single channel message. + * As a result, other clients will receive the changes as a single channel message after the batch function has completed. + * + * This method accepts a synchronous callback, which is provided with a {@link BatchContext} object. + * Use the context object to access Live Object instances in your state and batch operations for them. + * + * The objects' data is not modified inside the callback function. Instead, the objects will be updated + * when the batched operations are applied by the Ably service and echoed back to the client. + * + * @param callback - A batch callback function used to group operations together. Cannot be an `async` function. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + batch(callback: BatchCallback): Promise; + + /** + * Registers the provided listener for the specified event. If `on()` is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using `on()`, and an event is emitted once, the listener would be invoked twice. + * + * @param event - The named event to listen for. + * @param callback - The event listener. + * @returns A {@link OnLiveObjectsEventResponse} object that allows the provided listener to be deregistered from future updates. + */ + on(event: LiveObjectsEvent, callback: LiveObjectsEventCallback): OnLiveObjectsEventResponse; + + /** + * Removes all registrations that match both the specified listener and the specified event. + * + * @param event - The named event. + * @param callback - The event listener. + */ + off(event: LiveObjectsEvent, callback: LiveObjectsEventCallback): void; + + /** + * Deregisters all registrations, for all events and listeners. + */ + offAll(): void; } declare global { @@ -2095,6 +2201,97 @@ export type DefaultRoot = ? LiveObjectsTypes['root'] // "root" property exists, and it is of an expected type, we can use this interface for the root object in LiveObjects. : `Provided type definition for the "root" object in LiveObjectsTypes is not of an expected LiveMapType`; +/** + * Object returned from an `on` call, allowing the listener provided in that call to be deregistered. + */ +export declare interface OnLiveObjectsEventResponse { + /** + * Deregisters the listener passed to the `on` call. + */ + off(): void; +} + +/** + * Enables grouping multiple Live Object operations together by providing `BatchContext*` wrapper objects. + */ +export declare interface BatchContext { + /** + * Mirrors the {@link LiveObjects.getRoot} method and returns a {@link BatchContextLiveMap} wrapper for the root object on a channel. + * + * @returns A {@link BatchContextLiveMap} object. + */ + getRoot(): BatchContextLiveMap; +} + +/** + * A wrapper around the {@link LiveMap} object that enables batching operations inside a {@link BatchCallback}. + */ +export declare interface BatchContextLiveMap { + /** + * Mirrors the {@link LiveMap.get} method and returns the value associated with a key in the map. + * + * @param key - The key to retrieve the value for. + * @returns A {@link LiveObject}, a primitive type (string, number, boolean, or binary data) or `undefined` if the key doesn't exist in a map or the associated {@link LiveObject} has been deleted. Always `undefined` if this map object is deleted. + */ + get(key: TKey): T[TKey] | undefined; + + /** + * Returns the number of key/value pairs in the map. + */ + size(): number; + + /** + * Similar to the {@link LiveMap.set} method, but instead, it adds an operation to set a key in the map with the provided value to the current batch, to be sent in a single message to the Ably service. + * + * This does not modify the underlying data of this object. Instead, the change is applied when + * the published operation is echoed back to the client and applied to the object. + * To get notified when object gets updated, use the {@link LiveObject.subscribe} method. + * + * @param key - The key to set the value for. + * @param value - The value to assign to the key. + */ + set(key: TKey, value: T[TKey]): void; + + /** + * Similar to the {@link LiveMap.remove} method, but instead, it adds an operation to remove a key from the map to the current batch, to be sent in a single message to the Ably service. + * + * This does not modify the underlying data of this object. Instead, the change is applied when + * the published operation is echoed back to the client and applied to the object. + * To get notified when object gets updated, use the {@link LiveObject.subscribe} method. + * + * @param key - The key to set the value for. + */ + remove(key: TKey): void; +} + +/** + * A wrapper around the {@link LiveCounter} object that enables batching operations inside a {@link BatchCallback}. + */ +export declare interface BatchContextLiveCounter { + /** + * Returns the current value of the counter. + */ + value(): number; + + /** + * Similar to the {@link LiveCounter.increment} method, but instead, it adds an operation to increment the counter value to the current batch, to be sent in a single message to the Ably service. + * + * This does not modify the underlying data of this object. Instead, the change is applied when + * the published operation is echoed back to the client and applied to the object. + * To get notified when object gets updated, use the {@link LiveObject.subscribe} method. + * + * @param amount - The amount by which to increase the counter value. + */ + increment(amount: number): void; + + /** + * An alias for calling {@link BatchContextLiveCounter.increment | BatchContextLiveCounter.increment(-amount)} + * + * @param amount - The amount by which to decrease the counter value. + */ + decrement(amount: number): void; +} + /** * The `LiveMap` class represents a key/value map data structure, similar to a JavaScript Map, where all changes are synchronized across clients in realtime. * Conflict-free resolution for updates follows Last Write Wins (LWW) semantics, meaning that if two clients update the same key in the map, the update with the most recent timestamp wins. @@ -2116,6 +2313,31 @@ export declare interface LiveMap extends LiveObject(key: TKey, value: T[TKey]): Promise; + + /** + * Sends an operation to the Ably system to remove a key from this `LiveMap` object. + * + * This does not modify the underlying data of this object. Instead, the change is applied when + * the published operation is echoed back to the client and applied to the object. + * To get notified when object gets updated, use the {@link LiveObject.subscribe} method. + * + * @param key - The key to remove. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + remove(key: TKey): Promise; } /** @@ -2145,6 +2367,26 @@ export declare interface LiveCounter extends LiveObject { * Returns the current value of the counter. */ value(): number; + + /** + * Sends an operation to the Ably system to increment the value of this `LiveCounter` object. + * + * This does not modify the underlying data of this object. Instead, the change is applied when + * the published operation is echoed back to the client and applied to the object. + * To get notified when object gets updated, use the {@link LiveObject.subscribe} method. + * + * @param amount - The amount by which to increase the counter value. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + increment(amount: number): Promise; + + /** + * An alias for calling {@link LiveCounter.increment | LiveCounter.increment(-amount)} + * + * @param amount - The amount by which to decrease the counter value. + * @returns A promise which resolves upon success of the operation and rejects with an {@link ErrorInfo} object upon its failure. + */ + decrement(amount: number): Promise; } /** @@ -2185,6 +2427,28 @@ export declare interface LiveObject