Skip to content

Commit 3b0a73e

Browse files
Exclude PresenceMessage from BaseRealtime bundle
Now PresenceMessage only gets included if you provide the RealtimePresence module.
1 parent c09cee7 commit 3b0a73e

File tree

16 files changed

+128
-32
lines changed

16 files changed

+128
-32
lines changed

src/common/lib/client/baserealtime.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@ import ProtocolMessage from '../types/protocolmessage';
99
import { ChannelOptions } from '../../types/channel';
1010
import ClientOptions from '../../types/ClientOptions';
1111
import * as API from '../../../../ably';
12-
import { ModulesMap } from './modulesmap';
13-
import RealtimePresence from './realtimepresence';
12+
import { ModulesMap, RealtimePresenceModule } from './modulesmap';
1413
import { TransportNames } from 'common/constants/TransportName';
1514
import { TransportImplementations } from 'common/platform';
1615

1716
/**
1817
`BaseRealtime` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRealtime` class exported by the non tree-shakable version.
1918
*/
2019
class BaseRealtime extends BaseClient {
21-
readonly _RealtimePresence: typeof RealtimePresence | null;
20+
readonly _RealtimePresence: RealtimePresenceModule | null;
2221
// Extra transport implementations available to this client, in addition to those in Platform.Transports.bundledImplementations
2322
readonly _additionalTransportImplementations: TransportImplementations;
2423
_channels: any;

src/common/lib/client/defaultrealtime.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import RealtimePresence from './realtimepresence';
1111
import { DefaultPresenceMessage } from '../types/defaultpresencemessage';
1212
import initialiseWebSocketTransport from '../transport/websockettransport';
1313
import { FilteredSubscriptions } from './filteredsubscriptions';
14+
import {
15+
fromValues as presenceMessageFromValues,
16+
fromValuesArray as presenceMessagesFromValuesArray,
17+
} from '../types/presencemessage';
1418

1519
/**
1620
`DefaultRealtime` is the class that the non tree-shakable version of the SDK exports as `Realtime`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version.
@@ -26,7 +30,11 @@ export class DefaultRealtime extends BaseRealtime {
2630
...allCommonModules,
2731
Crypto: DefaultRealtime.Crypto ?? undefined,
2832
MsgPack,
29-
RealtimePresence,
33+
RealtimePresence: {
34+
RealtimePresence,
35+
presenceMessageFromValues,
36+
presenceMessagesFromValuesArray,
37+
},
3038
WebSocketTransport: initialiseWebSocketTransport,
3139
MessageInteractions: FilteredSubscriptions,
3240
});

src/common/lib/client/modulesmap.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,25 @@ import { TransportInitialiser } from '../transport/connectionmanager';
66
import XHRRequest from 'platform/web/lib/http/request/xhrrequest';
77
import fetchRequest from 'platform/web/lib/http/request/fetchrequest';
88
import { FilteredSubscriptions } from './filteredsubscriptions';
9+
import {
10+
fromValues as presenceMessageFromValues,
11+
fromValuesArray as presenceMessagesFromValuesArray,
12+
} from '../types/presencemessage';
13+
14+
export interface PresenceMessageModule {
15+
presenceMessageFromValues: typeof presenceMessageFromValues;
16+
presenceMessagesFromValuesArray: typeof presenceMessagesFromValuesArray;
17+
}
18+
19+
export type RealtimePresenceModule = PresenceMessageModule & {
20+
RealtimePresence: typeof RealtimePresence;
21+
};
922

1023
export interface ModulesMap {
1124
Rest?: typeof Rest;
1225
Crypto?: IUntypedCryptoStatic;
1326
MsgPack?: MsgPack;
14-
RealtimePresence?: typeof RealtimePresence;
27+
RealtimePresence?: RealtimePresenceModule;
1528
WebSocketTransport?: TransportInitialiser;
1629
XHRPolling?: TransportInitialiser;
1730
XHRStreaming?: TransportInitialiser;

src/common/lib/client/realtimechannel.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ import Message, {
1717
} from '../types/message';
1818
import ChannelStateChange from './channelstatechange';
1919
import ErrorInfo, { IPartialErrorInfo, PartialErrorInfo } from '../types/errorinfo';
20-
import PresenceMessage, {
21-
fromValues as presenceMessageFromValues,
22-
fromValuesArray as presenceMessagesFromValuesArray,
23-
decode as decodePresenceMessage,
24-
} from '../types/presencemessage';
20+
import PresenceMessage, { decode as decodePresenceMessage } from '../types/presencemessage';
2521
import ConnectionErrors from '../transport/connectionerrors';
2622
import * as API from '../../../../ably';
2723
import ConnectionManager from '../transport/connectionmanager';
@@ -109,7 +105,7 @@ class RealtimeChannel extends EventEmitter {
109105
this.name = name;
110106
this.channelOptions = normaliseChannelOptions(client._Crypto ?? null, options);
111107
this.client = client;
112-
this._presence = client._RealtimePresence ? new client._RealtimePresence(this) : null;
108+
this._presence = client._RealtimePresence ? new client._RealtimePresence.RealtimePresence(this) : null;
113109
this.connectionManager = client.connection.connectionManager;
114110
this.state = 'initialized';
115111
this.subscriptions = new EventEmitter();
@@ -509,8 +505,8 @@ class RealtimeChannel extends EventEmitter {
509505
action: actions.PRESENCE,
510506
channel: this.name,
511507
presence: Utils.isArray(presence)
512-
? presenceMessagesFromValuesArray(presence)
513-
: [presenceMessageFromValues(presence)],
508+
? this.client._RealtimePresence!.presenceMessagesFromValuesArray(presence)
509+
: [this.client._RealtimePresence!.presenceMessageFromValues(presence)],
514510
});
515511
this.sendMessage(msg, callback);
516512
}
@@ -585,7 +581,12 @@ class RealtimeChannel extends EventEmitter {
585581
if (!message.presence) break;
586582
// eslint-disable-next-line no-fallthrough
587583
case actions.PRESENCE: {
588-
const presence = message.presence as Array<PresenceMessage>;
584+
const presence = message.presence;
585+
586+
if (!presence) {
587+
break;
588+
}
589+
589590
const { id, connectionId, timestamp } = message;
590591

591592
const options = this.channelOptions;

src/common/lib/transport/comettransport.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,10 @@ abstract class CometTransport extends Transport {
348348
try {
349349
const items = this.decodeResponse(responseData);
350350
if (items && items.length)
351-
for (let i = 0; i < items.length; i++) this.onProtocolMessage(protocolMessageFromDeserialized(items[i]));
351+
for (let i = 0; i < items.length; i++)
352+
this.onProtocolMessage(
353+
protocolMessageFromDeserialized(items[i], this.connectionManager.realtime._RealtimePresence)
354+
);
352355
} catch (e) {
353356
Logger.logAction(
354357
Logger.LOG_ERROR,

src/common/lib/transport/connectionmanager.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1914,7 +1914,11 @@ class ConnectionManager extends EventEmitter {
19141914
return;
19151915
}
19161916
if (Logger.shouldLog(Logger.LOG_MICRO)) {
1917-
Logger.logAction(Logger.LOG_MICRO, 'ConnectionManager.send()', 'queueing msg; ' + stringifyProtocolMessage(msg));
1917+
Logger.logAction(
1918+
Logger.LOG_MICRO,
1919+
'ConnectionManager.send()',
1920+
'queueing msg; ' + stringifyProtocolMessage(msg, this.realtime._RealtimePresence)
1921+
);
19181922
}
19191923
this.queue(msg, callback);
19201924
}

src/common/lib/transport/protocol.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ class Protocol extends EventEmitter {
7474
Logger.logAction(
7575
Logger.LOG_MICRO,
7676
'Protocol.send()',
77-
'sending msg; ' + stringifyProtocolMessage(pendingMessage.message)
77+
'sending msg; ' +
78+
stringifyProtocolMessage(pendingMessage.message, this.transport.connectionManager.realtime._RealtimePresence)
7879
);
7980
}
8081
pendingMessage.sendAttempted = true;

src/common/lib/transport/transport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ abstract class Transport extends EventEmitter {
123123
'received on ' +
124124
this.shortName +
125125
': ' +
126-
stringifyProtocolMessage(message) +
126+
stringifyProtocolMessage(message, this.connectionManager.realtime._RealtimePresence) +
127127
'; connectionId = ' +
128128
this.connectionManager.connectionId
129129
);

src/common/lib/transport/websockettransport.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,14 @@ class WebSocketTransport extends Transport {
125125
'data received; length = ' + data.length + '; type = ' + typeof data
126126
);
127127
try {
128-
this.onProtocolMessage(deserializeProtocolMessage(data, this.connectionManager.realtime._MsgPack, this.format));
128+
this.onProtocolMessage(
129+
deserializeProtocolMessage(
130+
data,
131+
this.connectionManager.realtime._MsgPack,
132+
this.connectionManager.realtime._RealtimePresence,
133+
this.format
134+
)
135+
);
129136
} catch (e) {
130137
Logger.logAction(
131138
Logger.LOG_ERROR,

src/common/lib/types/protocolmessage.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { MsgPack } from 'common/types/msgpack';
22
import { Types } from '../../../../ably';
3+
import { PresenceMessageModule } from '../client/modulesmap';
34
import * as Utils from '../util/utils';
45
import ErrorInfo from './errorinfo';
56
import Message, { fromValues as messageFromValues, fromValuesArray as messagesFromValuesArray } from './message';
@@ -65,26 +66,46 @@ export const channelModes = ['PRESENCE', 'PUBLISH', 'SUBSCRIBE', 'PRESENCE_SUBSC
6566

6667
export const serialize = Utils.encodeBody;
6768

68-
export function deserialize(serialized: unknown, MsgPack: MsgPack | null, format?: Utils.Format): ProtocolMessage {
69+
export function deserialize(
70+
serialized: unknown,
71+
MsgPack: MsgPack | null,
72+
presenceMessageModule: PresenceMessageModule | null,
73+
format?: Utils.Format
74+
): ProtocolMessage {
6975
const deserialized = Utils.decodeBody<Record<string, unknown>>(serialized, MsgPack, format);
70-
return fromDeserialized(deserialized);
76+
return fromDeserialized(deserialized, presenceMessageModule);
7177
}
7278

73-
export function fromDeserialized(deserialized: Record<string, unknown>): ProtocolMessage {
79+
export function fromDeserialized(
80+
deserialized: Record<string, unknown>,
81+
presenceMessageModule: PresenceMessageModule | null
82+
): ProtocolMessage {
7483
const error = deserialized.error;
7584
if (error) deserialized.error = ErrorInfo.fromValues(error as ErrorInfo);
7685
const messages = deserialized.messages as Message[];
7786
if (messages) for (let i = 0; i < messages.length; i++) messages[i] = messageFromValues(messages[i]);
78-
const presence = deserialized.presence as PresenceMessage[];
79-
if (presence) for (let i = 0; i < presence.length; i++) presence[i] = presenceMessageFromValues(presence[i], true);
80-
return Object.assign(new ProtocolMessage(), deserialized);
87+
88+
const presence = presenceMessageModule ? (deserialized.presence as PresenceMessage[]) : undefined;
89+
if (presenceMessageModule) {
90+
if (presence && presenceMessageModule)
91+
for (let i = 0; i < presence.length; i++)
92+
presence[i] = presenceMessageModule.presenceMessageFromValues(presence[i], true);
93+
}
94+
return Object.assign(new ProtocolMessage(), { ...deserialized, presence });
95+
}
96+
97+
/**
98+
* Used by the tests.
99+
*/
100+
export function fromDeserializedIncludingDependencies(deserialized: Record<string, unknown>): ProtocolMessage {
101+
return fromDeserialized(deserialized, { presenceMessageFromValues, presenceMessagesFromValuesArray });
81102
}
82103

83104
export function fromValues(values: unknown): ProtocolMessage {
84105
return Object.assign(new ProtocolMessage(), values);
85106
}
86107

87-
export function stringify(msg: any): string {
108+
export function stringify(msg: any, presenceMessageModule: PresenceMessageModule | null): string {
88109
let result = '[ProtocolMessage';
89110
if (msg.action !== undefined) result += '; action=' + ActionName[msg.action] || msg.action;
90111

@@ -96,7 +117,8 @@ export function stringify(msg: any): string {
96117
}
97118

98119
if (msg.messages) result += '; messages=' + toStringArray(messagesFromValuesArray(msg.messages));
99-
if (msg.presence) result += '; presence=' + toStringArray(presenceMessagesFromValuesArray(msg.presence));
120+
if (msg.presence && presenceMessageModule)
121+
result += '; presence=' + toStringArray(presenceMessageModule.presenceMessagesFromValuesArray(msg.presence));
100122
if (msg.error) result += '; error=' + ErrorInfo.fromValues(msg.error).toString();
101123
if (msg.auth && msg.auth.accessToken) result += '; token=' + msg.auth.accessToken;
102124
if (msg.flags) result += '; flags=' + flagNames.filter(msg.hasFlag).join(',');
@@ -128,6 +150,7 @@ class ProtocolMessage {
128150
channelSerial?: string | null;
129151
msgSerial?: number;
130152
messages?: Message[];
153+
// This will be undefined if we skipped decoding this property due to user not requesting presence functionality — see `fromDeserialized`
131154
presence?: PresenceMessage[];
132155
auth?: unknown;
133156
connectionDetails?: Record<string, unknown>;

src/platform/nativescript/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
33
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
44
import Platform from '../../common/platform';
55
import ErrorInfo from '../../common/lib/types/errorinfo';
6-
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
6+
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
77

88
// Platform Specific
99
import BufferUtils from '../web/lib/util/bufferutils';

src/platform/nodejs/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
33
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
44
import Platform from '../../common/platform';
55
import ErrorInfo from '../../common/lib/types/errorinfo';
6-
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
6+
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
77

88
// Platform Specific
99
import BufferUtils from './lib/util/bufferutils';

src/platform/react-native/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
33
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
44
import Platform from '../../common/platform';
55
import ErrorInfo from '../../common/lib/types/errorinfo';
6-
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
6+
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
77

88
// Platform Specific
99
import BufferUtils from '../web/lib/util/bufferutils';

src/platform/web/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest';
33
import { DefaultRealtime } from '../../common/lib/client/defaultrealtime';
44
import Platform from '../../common/platform';
55
import ErrorInfo from '../../common/lib/types/errorinfo';
6-
import { fromDeserialized as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
6+
import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage';
77

88
// Platform Specific
99
import BufferUtils from './lib/util/bufferutils';
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,14 @@
1-
export { default as RealtimePresence } from '../../../common/lib/client/realtimepresence';
1+
import { RealtimePresenceModule } from 'common/lib/client/modulesmap';
2+
import { default as realtimePresenceClass } from '../../../common/lib/client/realtimepresence';
3+
import {
4+
fromValues as presenceMessageFromValues,
5+
fromValuesArray as presenceMessagesFromValuesArray,
6+
} from '../../../common/lib/types/presencemessage';
7+
8+
const RealtimePresence: RealtimePresenceModule = {
9+
RealtimePresence: realtimePresenceClass,
10+
presenceMessageFromValues,
11+
presenceMessagesFromValuesArray,
12+
};
13+
14+
export { RealtimePresence };

test/browser/modules.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,30 @@ describe('browser/modules', function () {
465465

466466
expect(() => channel.presence).to.throw('RealtimePresence module not provided');
467467
});
468+
469+
it('doesn’t break when it receives a PRESENCE ProtocolMessage', async () => {
470+
const rxClient = new BaseRealtime(ablyClientOptions(), { WebSocketTransport, FetchRequest });
471+
const rxChannel = rxClient.channels.get('channel');
472+
473+
await rxChannel.attach();
474+
475+
const receivedMessagePromise = new Promise((resolve) => rxChannel.subscribe(resolve));
476+
477+
const txClient = new BaseRealtime(ablyClientOptions({ clientId: randomString() }), {
478+
WebSocketTransport,
479+
FetchRequest,
480+
RealtimePublishing,
481+
RealtimePresence,
482+
});
483+
const txChannel = txClient.channels.get('channel');
484+
485+
await txChannel.publish('message', 'body');
486+
await txChannel.presence.enter();
487+
488+
// The idea being here that in order for receivedMessagePromise to resolve, rxClient must have first processed the PRESENCE ProtocolMessage that resulted from txChannel.presence.enter()
489+
490+
await receivedMessagePromise;
491+
});
468492
});
469493

470494
describe('BaseRealtime with RealtimePresence', () => {

0 commit comments

Comments
 (0)