Skip to content

Commit 86fc932

Browse files
committed
Refactor decoding/encoding in StateMessage
1 parent 1a828a6 commit 86fc932

File tree

1 file changed

+120
-99
lines changed

1 file changed

+120
-99
lines changed

src/plugins/liveobjects/statemessage.ts

Lines changed: 120 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -144,41 +144,16 @@ export class StateMessage {
144144
): Promise<void> {
145145
// TODO: decide how to handle individual errors from decoding values. currently we throw first ever error we get
146146

147-
const decodeMapEntry = async (
148-
entry: StateMapEntry,
149-
ctx: ChannelOptions,
150-
decode: typeof decodeData,
151-
): Promise<void> => {
152-
const { data, encoding, error } = await decode(entry.data.value, entry.data.encoding, ctx);
153-
entry.data.value = data;
154-
entry.data.encoding = encoding ?? undefined;
155-
156-
if (error) {
157-
throw error;
158-
}
159-
};
160-
161147
if (message.object?.map?.entries) {
162-
for (const entry of Object.values(message.object.map.entries)) {
163-
await decodeMapEntry(entry, inputContext, decodeDataFn);
164-
}
148+
await this._decodeMapEntries(message.object.map.entries, inputContext, decodeDataFn);
165149
}
166150

167151
if (message.operation?.map?.entries) {
168-
for (const entry of Object.values(message.operation.map.entries)) {
169-
await decodeMapEntry(entry, inputContext, decodeDataFn);
170-
}
152+
await this._decodeMapEntries(message.operation.map.entries, inputContext, decodeDataFn);
171153
}
172154

173-
if (message.operation?.mapOp?.data && 'value' in message.operation?.mapOp?.data) {
174-
const mapOpData = message.operation.mapOp.data;
175-
const { data, encoding, error } = await decodeDataFn(mapOpData.value, mapOpData.encoding, inputContext);
176-
mapOpData.value = data;
177-
mapOpData.encoding = encoding ?? undefined;
178-
179-
if (error) {
180-
throw error;
181-
}
155+
if (message.operation?.mapOp?.data && 'value' in message.operation.mapOp.data) {
156+
await this._decodeStateData(message.operation.mapOp.data, inputContext, decodeDataFn);
182157
}
183158
}
184159

@@ -197,6 +172,114 @@ export class StateMessage {
197172
return result;
198173
}
199174

175+
private static async _decodeMapEntries(
176+
mapEntries: Record<string, StateMapEntry>,
177+
inputContext: ChannelOptions,
178+
decodeDataFn: typeof decodeData,
179+
): Promise<void> {
180+
for (const entry of Object.values(mapEntries)) {
181+
await this._decodeStateData(entry.data, inputContext, decodeDataFn);
182+
}
183+
}
184+
185+
private static async _decodeStateData(
186+
stateData: StateData,
187+
inputContext: ChannelOptions,
188+
decodeDataFn: typeof decodeData,
189+
): Promise<void> {
190+
const { data, encoding, error } = await decodeDataFn(stateData.value, stateData.encoding, inputContext);
191+
stateData.value = data;
192+
stateData.encoding = encoding ?? undefined;
193+
194+
if (error) {
195+
throw error;
196+
}
197+
}
198+
199+
private static _encodeStateOperation(
200+
platform: typeof Platform,
201+
stateOperation: StateOperation,
202+
withBase64Encoding: boolean,
203+
): StateOperation {
204+
// deep copy "stateOperation" object so we can modify the copy here.
205+
// buffer values won't be correctly copied, so we will need to set them again explictly.
206+
const stateOperationCopy = JSON.parse(JSON.stringify(stateOperation)) as StateOperation;
207+
208+
if (stateOperationCopy.mapOp?.data && 'value' in stateOperationCopy.mapOp.data) {
209+
// use original "stateOperation" object when encoding values, so we have access to the original buffer values.
210+
stateOperationCopy.mapOp.data = this._encodeStateData(platform, stateOperation.mapOp?.data!, withBase64Encoding);
211+
}
212+
213+
if (stateOperationCopy.map?.entries) {
214+
Object.entries(stateOperationCopy.map.entries).forEach(([key, entry]) => {
215+
// use original "stateOperation" object when encoding values, so we have access to original buffer values.
216+
entry.data = this._encodeStateData(platform, stateOperation?.map?.entries?.[key].data!, withBase64Encoding);
217+
});
218+
}
219+
220+
return stateOperationCopy;
221+
}
222+
223+
private static _encodeStateObject(
224+
platform: typeof Platform,
225+
stateObject: StateObject,
226+
withBase64Encoding: boolean,
227+
): StateObject {
228+
// deep copy "stateObject" object so we can modify the copy here.
229+
// buffer values won't be correctly copied, so we will need to set them again explictly.
230+
const stateObjectCopy = JSON.parse(JSON.stringify(stateObject)) as StateObject;
231+
232+
if (stateObjectCopy.map?.entries) {
233+
Object.entries(stateObjectCopy.map.entries).forEach(([key, entry]) => {
234+
// use original "stateObject" object when encoding values, so we have access to original buffer values.
235+
entry.data = StateMessage._encodeStateData(
236+
platform,
237+
stateObject?.map?.entries?.[key].data!,
238+
withBase64Encoding,
239+
);
240+
});
241+
}
242+
243+
return stateObjectCopy;
244+
}
245+
246+
private static _encodeStateData(platform: typeof Platform, data: StateData, withBase64Encoding: boolean): StateData {
247+
const { value, encoding } = this._encodeStateValue(platform, data?.value, data?.encoding, withBase64Encoding);
248+
return {
249+
...data,
250+
value,
251+
encoding,
252+
};
253+
}
254+
255+
private static _encodeStateValue(
256+
platform: typeof Platform,
257+
value: StateValue | undefined,
258+
encoding: string | undefined,
259+
withBase64Encoding: boolean,
260+
): {
261+
value: StateValue | undefined;
262+
encoding: string | undefined;
263+
} {
264+
if (!value || !platform.BufferUtils.isBuffer(value)) {
265+
return { value, encoding };
266+
}
267+
268+
if (withBase64Encoding) {
269+
return {
270+
value: platform.BufferUtils.base64Encode(value),
271+
encoding: encoding ? encoding + '/base64' : 'base64',
272+
};
273+
}
274+
275+
// toBuffer returns a datatype understandable by
276+
// that platform's msgpack implementation (Buffer in node, Uint8Array in browsers)
277+
return {
278+
value: platform.BufferUtils.toBuffer(value),
279+
encoding,
280+
};
281+
}
282+
200283
/**
201284
* Overload toJSON() to intercept JSON.stringify()
202285
* @return {*}
@@ -215,44 +298,18 @@ export class StateMessage {
215298
// if withBase64Encoding = false - we were called by msgpack
216299
const withBase64Encoding = arguments.length > 0;
217300

218-
let operationCopy: StateOperation | undefined = undefined;
219-
if (this.operation) {
220-
// deep copy "operation" prop so we can modify it here.
221-
// buffer values won't be correctly copied, so we will need to set them again explictly
222-
operationCopy = JSON.parse(JSON.stringify(this.operation)) as StateOperation;
223-
224-
if (operationCopy.mapOp?.data && 'value' in operationCopy.mapOp.data) {
225-
// use original "operation" prop when encoding values, so we have access to original buffer values.
226-
operationCopy.mapOp.data = this._encodeStateData(this.operation.mapOp?.data!, withBase64Encoding);
227-
}
228-
229-
if (operationCopy.map?.entries) {
230-
Object.entries(operationCopy.map.entries).forEach(([key, entry]) => {
231-
// use original "operation" prop when encoding values, so we have access to original buffer values.
232-
entry.data = this._encodeStateData(this.operation?.map?.entries?.[key].data!, withBase64Encoding);
233-
});
234-
}
235-
}
236-
237-
let object: StateObject | undefined = undefined;
238-
if (this.object) {
239-
// deep copy "object" prop so we can modify it here.
240-
// buffer values won't be correctly copied, so we will need to set them again explictly
241-
object = JSON.parse(JSON.stringify(this.object)) as StateObject;
242-
243-
if (object.map?.entries) {
244-
Object.entries(object.map.entries).forEach(([key, entry]) => {
245-
// use original "object" prop when encoding values, so we have access to original buffer values.
246-
entry.data = this._encodeStateData(this.object?.map?.entries?.[key].data!, withBase64Encoding);
247-
});
248-
}
249-
}
301+
const encodedOperation = this.operation
302+
? StateMessage._encodeStateOperation(this._platform, this.operation, withBase64Encoding)
303+
: undefined;
304+
const encodedObject = this.object
305+
? StateMessage._encodeStateObject(this._platform, this.object, withBase64Encoding)
306+
: undefined;
250307

251308
return {
252309
id: this.id,
253310
clientId: this.clientId,
254-
operation: operationCopy,
255-
object: object,
311+
operation: encodedOperation,
312+
object: encodedObject,
256313
extras: this.extras,
257314
};
258315
}
@@ -275,40 +332,4 @@ export class StateMessage {
275332

276333
return result;
277334
}
278-
279-
private _encodeStateData(data: StateData, withBase64Encoding: boolean): StateData {
280-
const { value, encoding } = this._encodeStateValue(data?.value, data?.encoding, withBase64Encoding);
281-
return {
282-
...data,
283-
value,
284-
encoding,
285-
};
286-
}
287-
288-
private _encodeStateValue(
289-
value: StateValue | undefined,
290-
encoding: string | undefined,
291-
withBase64Encoding: boolean,
292-
): {
293-
value: StateValue | undefined;
294-
encoding: string | undefined;
295-
} {
296-
if (!value || !this._platform.BufferUtils.isBuffer(value)) {
297-
return { value, encoding };
298-
}
299-
300-
if (withBase64Encoding) {
301-
return {
302-
value: this._platform.BufferUtils.base64Encode(value),
303-
encoding: encoding ? encoding + '/base64' : 'base64',
304-
};
305-
}
306-
307-
// toBuffer returns a datatype understandable by
308-
// that platform's msgpack implementation (Buffer in node, Uint8Array in browsers)
309-
return {
310-
value: this._platform.BufferUtils.toBuffer(value),
311-
encoding,
312-
};
313-
}
314335
}

0 commit comments

Comments
 (0)