Skip to content

Commit fc3db1b

Browse files
committed
Replace internal usage of "state" term with "objects" for LiveObjects feature
This commit does next changes: - removes `state` mentions in comments / internal API. Uses `objects` instead where possible - normalizes terminology used when referencing "LiveObjects" product and objects on a channel according to LiveObjects product docs PR [1] - `STATE` message action -> `OBJECT` - `STATE_SYNC` message action -> `OBJECT_SYNC` - `HAS_STATE` flag -> `HAS_OBJECT` - `StateMessage` type -> `ObjectMessage` - `StateOperation` type -> `ObjectOperation` - `StateObject` type -> `ObjectState` - `StateData` type -> `ObjectData` - `StateValue` type -> `ObjectValue` This brings ably-js LiveObjects implementation in line with the naming changes introduced in the spec PR [2]. [1] ably/docs#2463 (review) [2] ably/specification#279 (comment)
1 parent e2b6b6f commit fc3db1b

20 files changed

+727
-712
lines changed

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -645,19 +645,19 @@ The authentication token must include corresponding capabilities for the client
645645
646646
#### Getting the Root Object
647647
648-
The root object represents the top-level entry point for Objects within a channel. It gives access to all other nested Live Objects.
648+
The root object represents the top-level entry point for objects within a channel. It gives access to all other nested objects.
649649
650650
```typescript
651651
const root = await objects.getRoot();
652652
```
653653
654654
The root object is a `LiveMap` instance and serves as the starting point for storing and organizing Objects on a channel.
655655
656-
#### Live Object Types
656+
#### Object Types
657657
658658
LiveObjects currently supports two primary data structures; `LiveMap` and `LiveCounter`.
659659
660-
`LiveMap` - A key/value map data structure, similar to a JavaScript `Map`, where all changes are synchronized across clients in realtime. It allows you to store primitive values and other Live Objects, enabling composability.
660+
`LiveMap` - A key/value map data structure, similar to a JavaScript `Map`, where all changes are synchronized across clients in realtime. It enables you to store primitive values and other objects, enabling composability.
661661
662662
You can use `LiveMap` as follows:
663663
@@ -689,7 +689,7 @@ await root.set('foo', 'Alice');
689689
await root.set('bar', 1);
690690
await root.set('baz', true);
691691
await root.set('qux', new Uint8Array([21, 31]));
692-
// as well as other live objects
692+
// as well as other objects
693693
const counter = await objects.createCounter();
694694
await root.set('quux', counter);
695695

@@ -714,11 +714,11 @@ await counter.decrement(2);
714714
715715
#### Subscribing to Updates
716716
717-
Subscribing to updates on Live Objects allows you to receive changes made by other clients in realtime. Since multiple clients may modify the same Live Objects, subscribing ensures that your application reacts to external updates as soon as they are received.
717+
Subscribing to updates on objects enables you to receive changes made by other clients in realtime. Since multiple clients may modify the same objects, subscribing ensures that your application reacts to external updates as soon as they are received.
718718
719719
Additionally, mutation methods such as `LiveMap.set`, `LiveCounter.increment`, and `LiveCounter.decrement` do not directly edit the current state of the object locally. Instead, they send the intended operation to the Ably system, and the change is applied to the local object only when the corresponding realtime operation is echoed back to the client. This means that the state you retrieve immediately after a mutation may not reflect the latest updates yet.
720720
721-
You can subscribe to updates on all Live Objects using subscription listeners as follows:
721+
You can subscribe to updates on all objects using subscription listeners as follows:
722722
723723
```typescript
724724
const root = await objects.getRoot();
@@ -772,14 +772,14 @@ root.unsubscribeAll();
772772
773773
#### Creating New Objects
774774
775-
New `LiveMap` and `LiveCounter` instances can be created as follows:
775+
New `LiveMap` and `LiveCounter` objects can be created as follows:
776776
777777
```typescript
778778
const counter = await objects.createCounter(123); // with optional initial counter value
779779
const map = await objects.createMap({ key: 'value' }); // with optional initial map entries
780780
```
781781
782-
To persist them within the Objects state, they must be assigned to a parent `LiveMap` that is connected to the root object through the object hierarchy:
782+
To persist them on a channel and share them between clients, they must be assigned to a parent `LiveMap` that is connected to the root object through the object hierarchy:
783783
784784
```typescript
785785
const root = await objects.getRoot();
@@ -799,9 +799,9 @@ await root.set('outerMap', outerMap);
799799
800800
#### Batch Operations
801801
802-
Batching allows multiple operations to be grouped into a single message that is sent to the Ably service. This allows batched operations to be applied atomically together.
802+
Batching enables multiple operations to be grouped into a single channel message that is sent to the Ably service. This guarantees that all changes are applied atomically.
803803
804-
Within a batch callback, the `BatchContext` instance provides wrapper objects around regular Live Objects with a synchronous API for storing changes in the batch context.
804+
Within a batch callback, the `BatchContext` instance provides wrapper objects around regular `LiveMap` and `LiveCounter` objects with a synchronous API for storing changes in the batch context.
805805
806806
```typescript
807807
await objects.batch((ctx) => {
@@ -819,9 +819,9 @@ await objects.batch((ctx) => {
819819
820820
#### Lifecycle Events
821821
822-
Live Objects emit lifecycle events to signal critical state changes, such as synchronization progress and object deletions.
822+
LiveObjects emit events that allow you to monitor objects' lifecycle changes, such as synchronization progress and object deletions.
823823
824-
**Synchronization Events** - the `syncing` and `synced` events notify when the Objects state is being synchronized with the Ably service. These events can be useful for displaying loading indicators, preventing user edits during synchronization, or triggering application logic when the data was loaded for the first time.
824+
**Synchronization Events** - the `syncing` and `synced` events notify when the local Objects state on a client is being synchronized with the Ably service. These events can be useful for displaying loading indicators, preventing user edits during synchronization, or triggering application logic when the data was loaded for the first time.
825825
826826
```typescript
827827
objects.on('syncing', () => {
@@ -835,14 +835,14 @@ objects.on('synced', () => {
835835
});
836836
```
837837
838-
**Object Deletion Events** - objects that have been orphaned for a long period (i.e., not connected to the state tree graph by being set as a key in a map accessible from the root map object) will eventually be deleted. Once a Live Object is deleted, it can no longer be interacted with. You should avoid accessing its data or trying to update its value and you should remove all references to the deleted object in your application.
838+
**Object Deletion Events** - objects that have been orphaned for a long period (i.e., not connected to the object tree by being set as a key in a map accessible from the root map object) will eventually be deleted. Once an object is deleted, it can no longer be interacted with. You should avoid accessing its data or trying to update its value and you should remove all references to the deleted object in your application.
839839
840840
```typescript
841841
const root = await objects.getRoot();
842842
const counter = root.get('counter');
843843

844844
counter.on('deleted', () => {
845-
console.log('Live Object has been deleted.');
845+
console.log('Object has been deleted.');
846846
// Example: Remove references to the object from the application
847847
});
848848
```

ably.d.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -872,11 +872,11 @@ declare namespace ChannelModes {
872872
*/
873873
type PRESENCE_SUBSCRIBE = 'PRESENCE_SUBSCRIBE';
874874
/**
875-
* The client can publish Objects messages.
875+
* The client can publish object messages.
876876
*/
877877
type OBJECT_PUBLISH = 'OBJECT_PUBLISH';
878878
/**
879-
* The client can receive Objects messages.
879+
* The client can receive object messages.
880880
*/
881881
type OBJECT_SUBSCRIBE = 'OBJECT_SUBSCRIBE';
882882
/**
@@ -1560,9 +1560,9 @@ export type DeregisterCallback = (device: DeviceDetails, callback: StandardCallb
15601560
export type ErrorCallback = (error: ErrorInfo | null) => void;
15611561

15621562
/**
1563-
* A callback used in {@link LiveObject} to listen for updates to the Live Object.
1563+
* A callback used in {@link LiveObject} to listen for updates to the object.
15641564
*
1565-
* @param update - The update object describing the changes made to the Live Object.
1565+
* @param update - The update object describing the changes made to the object.
15661566
*/
15671567
export type LiveObjectUpdateCallback<T> = (update: T) => void;
15681568

@@ -2181,9 +2181,9 @@ declare global {
21812181

21822182
/**
21832183
* Represents the type of data stored in a {@link LiveMap}.
2184-
* It maps string keys to scalar values ({@link StateValue}), or other Live Objects.
2184+
* It maps string keys to scalar values ({@link ObjectValue}), or other {@link LiveObject | LiveObjects}.
21852185
*/
2186-
export type LiveMapType = { [key: string]: StateValue | LiveMap<LiveMapType> | LiveCounter | undefined };
2186+
export type LiveMapType = { [key: string]: ObjectValue | LiveMap<LiveMapType> | LiveCounter | undefined };
21872187

21882188
/**
21892189
* The default type for the `root` object for Objects on a channel, based on the globally defined {@link ObjectsTypes} interface.
@@ -2236,7 +2236,7 @@ export declare interface BatchContextLiveMap<T extends LiveMapType> {
22362236
get<TKey extends keyof T & string>(key: TKey): T[TKey] | undefined;
22372237

22382238
/**
2239-
* Returns the number of key/value pairs in the map.
2239+
* Returns the number of key-value pairs in the map.
22402240
*/
22412241
size(): number;
22422242

@@ -2293,10 +2293,11 @@ export declare interface BatchContextLiveCounter {
22932293
}
22942294

22952295
/**
2296-
* The `LiveMap` class represents a key/value map data structure, similar to a JavaScript Map, where all changes are synchronized across clients in realtime.
2297-
* 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.
2296+
* The `LiveMap` class represents a key-value map data structure, similar to a JavaScript Map, where all changes are synchronized across clients in realtime.
2297+
* Conflicts in a LiveMap are automatically resolved with last-write-wins (LWW) semantics,
2298+
* meaning that if two clients update the same key in the map, the update with the most recent timestamp wins.
22982299
*
2299-
* Keys must be strings. Values can be another Live Object, or a primitive type, such as a string, number, boolean, or binary data (see {@link StateValue}).
2300+
* Keys must be strings. Values can be another {@link LiveObject}, or a primitive type, such as a string, number, boolean, or binary data (see {@link ObjectValue}).
23002301
*/
23012302
export declare interface LiveMap<T extends LiveMapType> extends LiveObject<LiveMapUpdate> {
23022303
/**
@@ -2310,12 +2311,12 @@ export declare interface LiveMap<T extends LiveMapType> extends LiveObject<LiveM
23102311
get<TKey extends keyof T & string>(key: TKey): T[TKey] | undefined;
23112312

23122313
/**
2313-
* Returns the number of key/value pairs in the map.
2314+
* Returns the number of key-value pairs in the map.
23142315
*/
23152316
size(): number;
23162317

23172318
/**
2318-
* Returns an iterable of key/value pairs for every entry in the map.
2319+
* Returns an iterable of key-value pairs for every entry in the map.
23192320
*/
23202321
entries<TKey extends keyof T & string>(): IterableIterator<[TKey, T[TKey]]>;
23212322

@@ -2372,7 +2373,7 @@ export declare interface LiveMapUpdate extends LiveObjectUpdate {
23722373
*
23732374
* For binary data, the resulting type depends on the platform (`Buffer` in Node.js, `ArrayBuffer` elsewhere).
23742375
*/
2375-
export type StateValue = string | number | boolean | Buffer | ArrayBuffer;
2376+
export type ObjectValue = string | number | boolean | Buffer | ArrayBuffer;
23762377

23772378
/**
23782379
* The `LiveCounter` class represents a counter that can be incremented or decremented and is synchronized across clients in realtime.
@@ -2424,22 +2425,22 @@ export declare interface LiveCounterUpdate extends LiveObjectUpdate {
24242425
*/
24252426
export declare interface LiveObject<TUpdate extends LiveObjectUpdate = LiveObjectUpdate> {
24262427
/**
2427-
* Registers a listener that is called each time this Live Object is updated.
2428+
* Registers a listener that is called each time this LiveObject is updated.
24282429
*
2429-
* @param listener - An event listener function that is called with an update object whenever this Live Object is updated.
2430+
* @param listener - An event listener function that is called with an update object whenever this LiveObject is updated.
24302431
* @returns A {@link SubscribeResponse} object that allows the provided listener to be deregistered from future updates.
24312432
*/
24322433
subscribe(listener: LiveObjectUpdateCallback<TUpdate>): SubscribeResponse;
24332434

24342435
/**
2435-
* Deregisters the given listener from updates for this Live Object.
2436+
* Deregisters the given listener from updates for this LiveObject.
24362437
*
24372438
* @param listener - An event listener function.
24382439
*/
24392440
unsubscribe(listener: LiveObjectUpdateCallback<TUpdate>): void;
24402441

24412442
/**
2442-
* Deregisters all listeners from updates for this Live Object.
2443+
* Deregisters all listeners from updates for this LiveObject.
24432444
*/
24442445
unsubscribeAll(): void;
24452446

@@ -2467,7 +2468,7 @@ export declare interface LiveObject<TUpdate extends LiveObjectUpdate = LiveObjec
24672468
}
24682469

24692470
/**
2470-
* Represents a generic update object describing the changes that occurred on a Live Object.
2471+
* Represents a generic update object describing the changes that occurred on a LiveObject.
24712472
*/
24722473
export declare interface LiveObjectUpdate {
24732474
/**
@@ -2626,7 +2627,7 @@ export declare interface RealtimeChannel extends EventEmitter<channelEventCallba
26262627
*/
26272628
presence: RealtimePresence;
26282629
/**
2629-
* A {@link Objects} object.
2630+
* An {@link Objects} object.
26302631
*/
26312632
objects: Objects;
26322633
/**

src/common/lib/client/realtimechannel.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { ChannelOptions } from '../../types/channel';
3030
import { normaliseChannelOptions } from '../util/defaults';
3131
import { PaginatedResult } from './paginatedresource';
3232
import type { PushChannel } from 'plugins/push';
33-
import type { Objects, StateMessage } from 'plugins/objects';
33+
import type { Objects, ObjectMessage } from 'plugins/objects';
3434

3535
interface RealtimeHistoryParams {
3636
start?: number;
@@ -507,12 +507,12 @@ class RealtimeChannel extends EventEmitter {
507507
this.sendMessage(msg, callback);
508508
}
509509

510-
sendState(state: StateMessage[]): Promise<void> {
510+
sendState(objectMessages: ObjectMessage[]): Promise<void> {
511511
return new Promise((resolve, reject) => {
512512
const msg = protocolMessageFromValues({
513-
action: actions.STATE,
513+
action: actions.OBJECT,
514514
channel: this.name,
515-
state,
515+
state: objectMessages,
516516
});
517517
this.sendMessage(msg, (err) => (err ? reject(err) : resolve()));
518518
});
@@ -524,7 +524,7 @@ class RealtimeChannel extends EventEmitter {
524524
message.action === actions.ATTACHED ||
525525
message.action === actions.MESSAGE ||
526526
message.action === actions.PRESENCE ||
527-
message.action === actions.STATE
527+
message.action === actions.OBJECT
528528
) {
529529
// RTL15b
530530
this.setChannelSerial(message.channelSerial);
@@ -542,17 +542,17 @@ class RealtimeChannel extends EventEmitter {
542542
const resumed = message.hasFlag('RESUMED');
543543
const hasPresence = message.hasFlag('HAS_PRESENCE');
544544
const hasBacklog = message.hasFlag('HAS_BACKLOG');
545-
const hasState = message.hasFlag('HAS_STATE');
545+
const hasObjects = message.hasFlag('HAS_OBJECTS');
546546
if (this.state === 'attached') {
547547
if (!resumed) {
548548
// we have lost continuity.
549549
// the presence set needs to be re-synced
550550
if (this._presence) {
551551
this._presence.onAttached(hasPresence);
552552
}
553-
// the Objects state needs to be re-synced
553+
// the Objects tree needs to be re-synced
554554
if (this._objects) {
555-
this._objects.onAttached(hasState);
555+
this._objects.onAttached(hasObjects);
556556
}
557557
}
558558
const change = new ChannelStateChange(this.state, this.state, resumed, hasBacklog, message.error);
@@ -564,7 +564,7 @@ class RealtimeChannel extends EventEmitter {
564564
/* RTL5i: re-send DETACH and remain in the 'detaching' state */
565565
this.checkPendingState();
566566
} else {
567-
this.notifyState('attached', message.error, resumed, hasPresence, hasBacklog, hasState);
567+
this.notifyState('attached', message.error, resumed, hasPresence, hasBacklog, hasObjects);
568568
}
569569
break;
570570
}
@@ -612,25 +612,25 @@ class RealtimeChannel extends EventEmitter {
612612
break;
613613
}
614614

615-
// STATE and STATE_SYNC message processing share most of the logic, so group them together
616-
case actions.STATE:
617-
case actions.STATE_SYNC: {
615+
// OBJECT and OBJECT_SYNC message processing share most of the logic, so group them together
616+
case actions.OBJECT:
617+
case actions.OBJECT_SYNC: {
618618
if (!this._objects) {
619619
return;
620620
}
621621

622-
const stateMessages = message.state ?? [];
622+
const objectMessages = message.state ?? [];
623623
const options = this.channelOptions;
624-
await this._decodeAndPrepareMessages(message, stateMessages, (msg) =>
624+
await this._decodeAndPrepareMessages(message, objectMessages, (msg) =>
625625
this.client._objectsPlugin
626-
? this.client._objectsPlugin.StateMessage.decode(msg, options, MessageEncoding)
626+
? this.client._objectsPlugin.ObjectMessage.decode(msg, options, MessageEncoding)
627627
: Utils.throwMissingPluginError('Objects'),
628628
);
629629

630-
if (message.action === actions.STATE) {
631-
this._objects.handleStateMessages(stateMessages);
630+
if (message.action === actions.OBJECT) {
631+
this._objects.handleObjectMessages(objectMessages);
632632
} else {
633-
this._objects.handleStateSyncMessages(stateMessages, message.channelSerial);
633+
this._objects.handleObjectSyncMessages(objectMessages, message.channelSerial);
634634
}
635635

636636
break;
@@ -740,7 +740,7 @@ class RealtimeChannel extends EventEmitter {
740740
* @returns `unrecoverableError` flag. If `true` indicates that unrecoverable error was encountered during message decoding
741741
* and any further message processing should be stopped. Always equals to `false` if `decodeErrorRecoveryHandler` was not provided
742742
*/
743-
private async _decodeAndPrepareMessages<T extends Message | PresenceMessage | StateMessage>(
743+
private async _decodeAndPrepareMessages<T extends Message | PresenceMessage | ObjectMessage>(
744744
protocolMessage: ProtocolMessage,
745745
messages: T[],
746746
decodeFn: (msg: T) => Promise<void>,
@@ -809,7 +809,7 @@ class RealtimeChannel extends EventEmitter {
809809
resumed?: boolean,
810810
hasPresence?: boolean,
811811
hasBacklog?: boolean,
812-
hasState?: boolean,
812+
hasObjects?: boolean,
813813
): void {
814814
Logger.logAction(
815815
this.logger,
@@ -831,7 +831,7 @@ class RealtimeChannel extends EventEmitter {
831831
this._presence.actOnChannelState(state, hasPresence, reason);
832832
}
833833
if (this._objects) {
834-
this._objects.actOnChannelState(state, hasState);
834+
this._objects.actOnChannelState(state, hasObjects);
835835
}
836836
if (state === 'suspended' && this.connectionManager.state.sendEvents) {
837837
this.startRetryTimer();

src/common/lib/transport/protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class PendingMessage {
2121
const action = message.action;
2222
this.sendAttempted = false;
2323
this.ackRequired =
24-
typeof action === 'number' && [actions.MESSAGE, actions.PRESENCE, actions.STATE].includes(action);
24+
typeof action === 'number' && [actions.MESSAGE, actions.PRESENCE, actions.OBJECT].includes(action);
2525
}
2626
}
2727

0 commit comments

Comments
 (0)