@librechat/agents 3.2.43 → 3.2.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -23,6 +23,19 @@ function createRemoveAllMessage() {
|
|
|
23
23
|
return new _langchain_core_messages.RemoveMessage({ id: REMOVE_ALL_MESSAGES });
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
+
* Coerce each entry to a {@link BaseMessage} in a single pass, skipping
|
|
27
|
+
* null/undefined entries. Providers can emit empty/partial stream chunks that
|
|
28
|
+
* arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws
|
|
29
|
+
* "Cannot read properties of undefined (reading 'role')" and crashes the run.
|
|
30
|
+
* Folding the null check into the coercion loop avoids a second pass over the
|
|
31
|
+
* array. Refs LibreChat Discussion #12284.
|
|
32
|
+
*/
|
|
33
|
+
function coerceMessages(messages) {
|
|
34
|
+
const coerced = [];
|
|
35
|
+
for (const message of messages) if (message != null) coerced.push((0, _langchain_core_messages.coerceMessageLikeToMessage)(message));
|
|
36
|
+
return coerced;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
26
39
|
* Prebuilt reducer that combines returned messages.
|
|
27
40
|
* Can handle standard messages and special modifiers like {@link RemoveMessage}
|
|
28
41
|
* instances.
|
|
@@ -30,8 +43,8 @@ function createRemoveAllMessage() {
|
|
|
30
43
|
function messagesStateReducer(left, right) {
|
|
31
44
|
const leftArray = Array.isArray(left) ? left : [left];
|
|
32
45
|
const rightArray = Array.isArray(right) ? right : [right];
|
|
33
|
-
const leftMessages = leftArray
|
|
34
|
-
const rightMessages = rightArray
|
|
46
|
+
const leftMessages = coerceMessages(leftArray);
|
|
47
|
+
const rightMessages = coerceMessages(rightArray);
|
|
35
48
|
for (const m of leftMessages) if (m.id === null || m.id === void 0) {
|
|
36
49
|
m.id = (0, uuid.v4)();
|
|
37
50
|
m.lc_kwargs.id = m.id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.cjs","names":["RemoveMessage"
|
|
1
|
+
{"version":3,"file":"reducer.cjs","names":["RemoveMessage"],"sources":["../../../src/messages/reducer.ts"],"sourcesContent":["import { v4 } from 'uuid';\nimport {\n BaseMessage,\n RemoveMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n} from '@langchain/core/messages';\n\nexport const REMOVE_ALL_MESSAGES = '__remove_all__';\n\n/**\n * Creates a message that instructs messagesStateReducer to remove ALL\n * existing messages from state. Messages appearing after this one in\n * the array become the new state.\n *\n * Usage (in a node return value):\n * ```ts\n * return { messages: [createRemoveAllMessage(), ...survivingMessages] };\n * ```\n *\n * This works because the reducer checks for `getType() === 'remove'`\n * with `id === REMOVE_ALL_MESSAGES` and discards everything before it.\n *\n * NOTE: Uses RemoveMessage from @langchain/core with a sentinel id so\n * the reducer can distinguish a \"remove-all\" marker from a single-message\n * removal.\n */\nexport function createRemoveAllMessage(): BaseMessage {\n return new RemoveMessage({ id: REMOVE_ALL_MESSAGES });\n}\n\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Coerce each entry to a {@link BaseMessage} in a single pass, skipping\n * null/undefined entries. Providers can emit empty/partial stream chunks that\n * arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws\n * \"Cannot read properties of undefined (reading 'role')\" and crashes the run.\n * Folding the null check into the coercion loop avoids a second pass over the\n * array. Refs LibreChat Discussion #12284.\n */\nfunction coerceMessages(\n messages: ReadonlyArray<BaseMessageLike | null | undefined>\n): BaseMessage[] {\n const coerced: BaseMessage[] = [];\n for (const message of messages) {\n if (message != null) {\n coerced.push(coerceMessageLikeToMessage(message));\n }\n }\n return coerced;\n}\n\n/**\n * Prebuilt reducer that combines returned messages.\n * Can handle standard messages and special modifiers like {@link RemoveMessage}\n * instances.\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n // coerce to message, skipping null/undefined entries in the same pass\n const leftMessages = coerceMessages(leftArray as BaseMessageLike[]);\n const rightMessages = coerceMessages(rightArray as BaseMessageLike[]);\n // assign missing ids\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n if (m.getType() === 'remove' && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // merge\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n if (m.getType() === 'remove') {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n if (m.getType() === 'remove') {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n"],"mappings":";;;AAQA,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,yBAAsC;CACpD,OAAO,IAAIA,yBAAAA,cAAc,EAAE,IAAI,oBAAoB,CAAC;AACtD;;;;;;;;;AAeA,SAAS,eACP,UACe;CACf,MAAM,UAAyB,CAAC;CAChC,KAAK,MAAM,WAAW,UACpB,IAAI,WAAW,MACb,QAAQ,MAAA,GAAA,yBAAA,2BAAA,CAAgC,OAAO,CAAC;CAGpD,OAAO;AACT;;;;;;AAOA,SAAgB,qBACd,MACA,OACe;CACf,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CACpD,MAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;CAExD,MAAM,eAAe,eAAe,SAA8B;CAClE,MAAM,gBAAgB,eAAe,UAA+B;CAEpE,KAAK,MAAM,KAAK,cACd,IAAI,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAA,GAAW;EACvC,EAAE,MAAA,GAAA,KAAA,GAAA,CAAQ;EACV,EAAE,UAAU,KAAK,EAAE;CACrB;CAGF,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK,GAAG;EAChD,MAAM,IAAI,cAAc;EACxB,IAAI,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAA,GAAW;GACvC,EAAE,MAAA,GAAA,KAAA,GAAA,CAAQ;GACV,EAAE,UAAU,KAAK,EAAE;EACrB;EAEA,IAAI,EAAE,QAAQ,MAAM,YAAY,EAAE,OAAA,kBAChC,eAAe;CAEnB;CAEA,IAAI,gBAAgB,MAAM,OAAO,cAAc,MAAM,eAAe,CAAC;CAGrE,MAAM,SAAS,CAAC,GAAG,YAAY;CAC/B,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC1D,MAAM,8BAAc,IAAI,IAAI;CAC5B,KAAK,MAAM,KAAK,eAAe;EAC7B,MAAM,cAAc,WAAW,IAAI,EAAE,EAAE;EACvC,IAAI,gBAAgB,KAAA,GAClB,IAAI,EAAE,QAAQ,MAAM,UAClB,YAAY,IAAI,EAAE,EAAE;OACf;GACL,YAAY,OAAO,EAAE,EAAE;GACvB,OAAO,eAAe;EACxB;OACK;GACL,IAAI,EAAE,QAAQ,MAAM,UAClB,MAAM,IAAI,MACR,kEAAkE,EAAE,GAAG,GACzE;GAEF,WAAW,IAAI,EAAE,IAAI,OAAO,MAAM;GAClC,OAAO,KAAK,CAAC;EACf;CACF;CACA,OAAO,OAAO,QAAQ,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;AACpD"}
|
|
@@ -23,6 +23,19 @@ function createRemoveAllMessage() {
|
|
|
23
23
|
return new RemoveMessage({ id: REMOVE_ALL_MESSAGES });
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
+
* Coerce each entry to a {@link BaseMessage} in a single pass, skipping
|
|
27
|
+
* null/undefined entries. Providers can emit empty/partial stream chunks that
|
|
28
|
+
* arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws
|
|
29
|
+
* "Cannot read properties of undefined (reading 'role')" and crashes the run.
|
|
30
|
+
* Folding the null check into the coercion loop avoids a second pass over the
|
|
31
|
+
* array. Refs LibreChat Discussion #12284.
|
|
32
|
+
*/
|
|
33
|
+
function coerceMessages(messages) {
|
|
34
|
+
const coerced = [];
|
|
35
|
+
for (const message of messages) if (message != null) coerced.push(coerceMessageLikeToMessage(message));
|
|
36
|
+
return coerced;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
26
39
|
* Prebuilt reducer that combines returned messages.
|
|
27
40
|
* Can handle standard messages and special modifiers like {@link RemoveMessage}
|
|
28
41
|
* instances.
|
|
@@ -30,8 +43,8 @@ function createRemoveAllMessage() {
|
|
|
30
43
|
function messagesStateReducer(left, right) {
|
|
31
44
|
const leftArray = Array.isArray(left) ? left : [left];
|
|
32
45
|
const rightArray = Array.isArray(right) ? right : [right];
|
|
33
|
-
const leftMessages = leftArray
|
|
34
|
-
const rightMessages = rightArray
|
|
46
|
+
const leftMessages = coerceMessages(leftArray);
|
|
47
|
+
const rightMessages = coerceMessages(rightArray);
|
|
35
48
|
for (const m of leftMessages) if (m.id === null || m.id === void 0) {
|
|
36
49
|
m.id = v4();
|
|
37
50
|
m.lc_kwargs.id = m.id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.mjs","names":[],"sources":["../../../src/messages/reducer.ts"],"sourcesContent":["import { v4 } from 'uuid';\nimport {\n BaseMessage,\n RemoveMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n} from '@langchain/core/messages';\n\nexport const REMOVE_ALL_MESSAGES = '__remove_all__';\n\n/**\n * Creates a message that instructs messagesStateReducer to remove ALL\n * existing messages from state. Messages appearing after this one in\n * the array become the new state.\n *\n * Usage (in a node return value):\n * ```ts\n * return { messages: [createRemoveAllMessage(), ...survivingMessages] };\n * ```\n *\n * This works because the reducer checks for `getType() === 'remove'`\n * with `id === REMOVE_ALL_MESSAGES` and discards everything before it.\n *\n * NOTE: Uses RemoveMessage from @langchain/core with a sentinel id so\n * the reducer can distinguish a \"remove-all\" marker from a single-message\n * removal.\n */\nexport function createRemoveAllMessage(): BaseMessage {\n return new RemoveMessage({ id: REMOVE_ALL_MESSAGES });\n}\n\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Prebuilt reducer that combines returned messages.\n * Can handle standard messages and special modifiers like {@link RemoveMessage}\n * instances.\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n // coerce to message\n const leftMessages = (leftArray as BaseMessageLike[])
|
|
1
|
+
{"version":3,"file":"reducer.mjs","names":[],"sources":["../../../src/messages/reducer.ts"],"sourcesContent":["import { v4 } from 'uuid';\nimport {\n BaseMessage,\n RemoveMessage,\n BaseMessageLike,\n coerceMessageLikeToMessage,\n} from '@langchain/core/messages';\n\nexport const REMOVE_ALL_MESSAGES = '__remove_all__';\n\n/**\n * Creates a message that instructs messagesStateReducer to remove ALL\n * existing messages from state. Messages appearing after this one in\n * the array become the new state.\n *\n * Usage (in a node return value):\n * ```ts\n * return { messages: [createRemoveAllMessage(), ...survivingMessages] };\n * ```\n *\n * This works because the reducer checks for `getType() === 'remove'`\n * with `id === REMOVE_ALL_MESSAGES` and discards everything before it.\n *\n * NOTE: Uses RemoveMessage from @langchain/core with a sentinel id so\n * the reducer can distinguish a \"remove-all\" marker from a single-message\n * removal.\n */\nexport function createRemoveAllMessage(): BaseMessage {\n return new RemoveMessage({ id: REMOVE_ALL_MESSAGES });\n}\n\nexport type Messages =\n | Array<BaseMessage | BaseMessageLike>\n | BaseMessage\n | BaseMessageLike;\n\n/**\n * Coerce each entry to a {@link BaseMessage} in a single pass, skipping\n * null/undefined entries. Providers can emit empty/partial stream chunks that\n * arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws\n * \"Cannot read properties of undefined (reading 'role')\" and crashes the run.\n * Folding the null check into the coercion loop avoids a second pass over the\n * array. Refs LibreChat Discussion #12284.\n */\nfunction coerceMessages(\n messages: ReadonlyArray<BaseMessageLike | null | undefined>\n): BaseMessage[] {\n const coerced: BaseMessage[] = [];\n for (const message of messages) {\n if (message != null) {\n coerced.push(coerceMessageLikeToMessage(message));\n }\n }\n return coerced;\n}\n\n/**\n * Prebuilt reducer that combines returned messages.\n * Can handle standard messages and special modifiers like {@link RemoveMessage}\n * instances.\n */\nexport function messagesStateReducer(\n left: Messages,\n right: Messages\n): BaseMessage[] {\n const leftArray = Array.isArray(left) ? left : [left];\n const rightArray = Array.isArray(right) ? right : [right];\n // coerce to message, skipping null/undefined entries in the same pass\n const leftMessages = coerceMessages(leftArray as BaseMessageLike[]);\n const rightMessages = coerceMessages(rightArray as BaseMessageLike[]);\n // assign missing ids\n for (const m of leftMessages) {\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n }\n\n let removeAllIdx: number | undefined;\n for (let i = 0; i < rightMessages.length; i += 1) {\n const m = rightMessages[i];\n if (m.id === null || m.id === undefined) {\n m.id = v4();\n m.lc_kwargs.id = m.id;\n }\n\n if (m.getType() === 'remove' && m.id === REMOVE_ALL_MESSAGES) {\n removeAllIdx = i;\n }\n }\n\n if (removeAllIdx != null) return rightMessages.slice(removeAllIdx + 1);\n\n // merge\n const merged = [...leftMessages];\n const mergedById = new Map(merged.map((m, i) => [m.id, i]));\n const idsToRemove = new Set();\n for (const m of rightMessages) {\n const existingIdx = mergedById.get(m.id);\n if (existingIdx !== undefined) {\n if (m.getType() === 'remove') {\n idsToRemove.add(m.id);\n } else {\n idsToRemove.delete(m.id);\n merged[existingIdx] = m;\n }\n } else {\n if (m.getType() === 'remove') {\n throw new Error(\n `Attempting to delete a message with an ID that doesn't exist ('${m.id}')`\n );\n }\n mergedById.set(m.id, merged.length);\n merged.push(m);\n }\n }\n return merged.filter((m) => !idsToRemove.has(m.id));\n}\n"],"mappings":";;;AAQA,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,yBAAsC;CACpD,OAAO,IAAI,cAAc,EAAE,IAAI,oBAAoB,CAAC;AACtD;;;;;;;;;AAeA,SAAS,eACP,UACe;CACf,MAAM,UAAyB,CAAC;CAChC,KAAK,MAAM,WAAW,UACpB,IAAI,WAAW,MACb,QAAQ,KAAK,2BAA2B,OAAO,CAAC;CAGpD,OAAO;AACT;;;;;;AAOA,SAAgB,qBACd,MACA,OACe;CACf,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CACpD,MAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;CAExD,MAAM,eAAe,eAAe,SAA8B;CAClE,MAAM,gBAAgB,eAAe,UAA+B;CAEpE,KAAK,MAAM,KAAK,cACd,IAAI,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAA,GAAW;EACvC,EAAE,KAAK,GAAG;EACV,EAAE,UAAU,KAAK,EAAE;CACrB;CAGF,IAAI;CACJ,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK,GAAG;EAChD,MAAM,IAAI,cAAc;EACxB,IAAI,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAA,GAAW;GACvC,EAAE,KAAK,GAAG;GACV,EAAE,UAAU,KAAK,EAAE;EACrB;EAEA,IAAI,EAAE,QAAQ,MAAM,YAAY,EAAE,OAAA,kBAChC,eAAe;CAEnB;CAEA,IAAI,gBAAgB,MAAM,OAAO,cAAc,MAAM,eAAe,CAAC;CAGrE,MAAM,SAAS,CAAC,GAAG,YAAY;CAC/B,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC1D,MAAM,8BAAc,IAAI,IAAI;CAC5B,KAAK,MAAM,KAAK,eAAe;EAC7B,MAAM,cAAc,WAAW,IAAI,EAAE,EAAE;EACvC,IAAI,gBAAgB,KAAA,GAClB,IAAI,EAAE,QAAQ,MAAM,UAClB,YAAY,IAAI,EAAE,EAAE;OACf;GACL,YAAY,OAAO,EAAE,EAAE;GACvB,OAAO,eAAe;EACxB;OACK;GACL,IAAI,EAAE,QAAQ,MAAM,UAClB,MAAM,IAAI,MACR,kEAAkE,EAAE,GAAG,GACzE;GAEF,WAAW,IAAI,EAAE,IAAI,OAAO,MAAM;GAClC,OAAO,KAAK,CAAC;EACf;CACF;CACA,OAAO,OAAO,QAAQ,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;AACpD"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
2
|
+
import { messagesStateReducer } from './reducer';
|
|
3
|
+
|
|
4
|
+
describe('messagesStateReducer', () => {
|
|
5
|
+
it('drops null/undefined entries and preserves order', () => {
|
|
6
|
+
const a = new HumanMessage({ id: 'a', content: 'a' });
|
|
7
|
+
const b = new HumanMessage({ id: 'b', content: 'b' });
|
|
8
|
+
const c = new HumanMessage({ id: 'c', content: 'c' });
|
|
9
|
+
|
|
10
|
+
const result = messagesStateReducer(
|
|
11
|
+
[a, null, b] as never,
|
|
12
|
+
[undefined, c] as never
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
expect(result.map((m) => m.id)).toEqual(['a', 'b', 'c']);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('does not throw when every entry is null/undefined', () => {
|
|
19
|
+
expect(() =>
|
|
20
|
+
messagesStateReducer(
|
|
21
|
+
[null, undefined] as never,
|
|
22
|
+
[undefined, null] as never
|
|
23
|
+
)
|
|
24
|
+
).not.toThrow();
|
|
25
|
+
|
|
26
|
+
const result = messagesStateReducer(
|
|
27
|
+
[null, undefined] as never,
|
|
28
|
+
[undefined, null] as never
|
|
29
|
+
);
|
|
30
|
+
expect(result).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
});
|
package/src/messages/reducer.ts
CHANGED
|
@@ -34,6 +34,26 @@ export type Messages =
|
|
|
34
34
|
| BaseMessage
|
|
35
35
|
| BaseMessageLike;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Coerce each entry to a {@link BaseMessage} in a single pass, skipping
|
|
39
|
+
* null/undefined entries. Providers can emit empty/partial stream chunks that
|
|
40
|
+
* arrive as `undefined`; passing those to `coerceMessageLikeToMessage` throws
|
|
41
|
+
* "Cannot read properties of undefined (reading 'role')" and crashes the run.
|
|
42
|
+
* Folding the null check into the coercion loop avoids a second pass over the
|
|
43
|
+
* array. Refs LibreChat Discussion #12284.
|
|
44
|
+
*/
|
|
45
|
+
function coerceMessages(
|
|
46
|
+
messages: ReadonlyArray<BaseMessageLike | null | undefined>
|
|
47
|
+
): BaseMessage[] {
|
|
48
|
+
const coerced: BaseMessage[] = [];
|
|
49
|
+
for (const message of messages) {
|
|
50
|
+
if (message != null) {
|
|
51
|
+
coerced.push(coerceMessageLikeToMessage(message));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return coerced;
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
/**
|
|
38
58
|
* Prebuilt reducer that combines returned messages.
|
|
39
59
|
* Can handle standard messages and special modifiers like {@link RemoveMessage}
|
|
@@ -45,13 +65,9 @@ export function messagesStateReducer(
|
|
|
45
65
|
): BaseMessage[] {
|
|
46
66
|
const leftArray = Array.isArray(left) ? left : [left];
|
|
47
67
|
const rightArray = Array.isArray(right) ? right : [right];
|
|
48
|
-
// coerce to message
|
|
49
|
-
const leftMessages = (leftArray as BaseMessageLike[])
|
|
50
|
-
|
|
51
|
-
);
|
|
52
|
-
const rightMessages = (rightArray as BaseMessageLike[]).map(
|
|
53
|
-
coerceMessageLikeToMessage
|
|
54
|
-
);
|
|
68
|
+
// coerce to message, skipping null/undefined entries in the same pass
|
|
69
|
+
const leftMessages = coerceMessages(leftArray as BaseMessageLike[]);
|
|
70
|
+
const rightMessages = coerceMessages(rightArray as BaseMessageLike[]);
|
|
55
71
|
// assign missing ids
|
|
56
72
|
for (const m of leftMessages) {
|
|
57
73
|
if (m.id === null || m.id === undefined) {
|