Convert js/modules/types/message to Typescript
This commit is contained in:
		
					parent
					
						
							
								9975758fde
							
						
					
				
			
			
				commit
				
					
						924c271b13
					
				
			
		
					 18 changed files with 748 additions and 398 deletions
				
			
		| 
						 | 
					@ -110,7 +110,7 @@ const searchSelectors = require('../../ts/state/selectors/search');
 | 
				
			||||||
// Types
 | 
					// Types
 | 
				
			||||||
const AttachmentType = require('../../ts/types/Attachment');
 | 
					const AttachmentType = require('../../ts/types/Attachment');
 | 
				
			||||||
const VisualAttachment = require('../../ts/types/VisualAttachment');
 | 
					const VisualAttachment = require('../../ts/types/VisualAttachment');
 | 
				
			||||||
const MessageType = require('./types/message');
 | 
					const MessageType = require('../../ts/types/Message2');
 | 
				
			||||||
const { UUID } = require('../../ts/types/UUID');
 | 
					const { UUID } = require('../../ts/types/UUID');
 | 
				
			||||||
const { Address } = require('../../ts/types/Address');
 | 
					const { Address } = require('../../ts/types/Address');
 | 
				
			||||||
const { QualifiedAddress } = require('../../ts/types/QualifiedAddress');
 | 
					const { QualifiedAddress } = require('../../ts/types/QualifiedAddress');
 | 
				
			||||||
| 
						 | 
					@ -281,6 +281,8 @@ function initializeMigrations({
 | 
				
			||||||
        makeVideoScreenshot,
 | 
					        makeVideoScreenshot,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
        maxVersion,
 | 
					        maxVersion,
 | 
				
			||||||
 | 
					        getAbsoluteStickerPath,
 | 
				
			||||||
 | 
					        writeNewStickerData,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    writeMessageAttachments: MessageType.createAttachmentDataWriter({
 | 
					    writeMessageAttachments: MessageType.createAttachmentDataWriter({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								js/modules/types/message.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								js/modules/types/message.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,4 +0,0 @@
 | 
				
			||||||
// Copyright 2020 Signal Messenger, LLC
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: AGPL-3.0-only
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const CURRENT_SCHEMA_VERSION: number;
 | 
					 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,7 @@ import type {
 | 
				
			||||||
  GroupLogResponseType,
 | 
					  GroupLogResponseType,
 | 
				
			||||||
} from './textsecure/WebAPI';
 | 
					} from './textsecure/WebAPI';
 | 
				
			||||||
import type MessageSender from './textsecure/SendMessage';
 | 
					import type MessageSender from './textsecure/SendMessage';
 | 
				
			||||||
import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from '../js/modules/types/message';
 | 
					import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from './types/Message2';
 | 
				
			||||||
import type { ConversationModel } from './models/conversations';
 | 
					import type { ConversationModel } from './models/conversations';
 | 
				
			||||||
import { getGroupSizeHardLimit } from './groups/limits';
 | 
					import { getGroupSizeHardLimit } from './groups/limits';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
// SPDX-License-Identifier: AGPL-3.0-only
 | 
					// SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { isFunction, isNumber } from 'lodash';
 | 
					import { isFunction, isNumber } from 'lodash';
 | 
				
			||||||
import * as Message from '../../js/modules/types/message';
 | 
					import { CURRENT_SCHEMA_VERSION } from '../types/Message2';
 | 
				
			||||||
import type { MessageAttributesType } from '../model-types.d';
 | 
					import type { MessageAttributesType } from '../model-types.d';
 | 
				
			||||||
import type { UUIDStringType } from '../types/UUID';
 | 
					import type { UUIDStringType } from '../types/UUID';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ export async function migrateMessageData({
 | 
				
			||||||
  upgradeMessageSchema,
 | 
					  upgradeMessageSchema,
 | 
				
			||||||
  getMessagesNeedingUpgrade,
 | 
					  getMessagesNeedingUpgrade,
 | 
				
			||||||
  saveMessage,
 | 
					  saveMessage,
 | 
				
			||||||
  maxVersion = Message.CURRENT_SCHEMA_VERSION,
 | 
					  maxVersion = CURRENT_SCHEMA_VERSION,
 | 
				
			||||||
}: Readonly<{
 | 
					}: Readonly<{
 | 
				
			||||||
  numMessagesPerBatch: number;
 | 
					  numMessagesPerBatch: number;
 | 
				
			||||||
  upgradeMessageSchema: (
 | 
					  upgradeMessageSchema: (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								ts/model-types.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								ts/model-types.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -61,8 +61,14 @@ export type GroupMigrationType = {
 | 
				
			||||||
  droppedMemberIds: Array<string>;
 | 
					  droppedMemberIds: Array<string>;
 | 
				
			||||||
  invitedMembers: Array<GroupV2PendingMemberType>;
 | 
					  invitedMembers: Array<GroupV2PendingMemberType>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					export type PreviewType = {
 | 
				
			||||||
 | 
					  domain: string;
 | 
				
			||||||
 | 
					  image: AttachmentType;
 | 
				
			||||||
 | 
					  title: string;
 | 
				
			||||||
 | 
					  url: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type PreviewMessageType = Array<WhatIsThis>;
 | 
					export type PreviewMessageType = Array<PreviewType>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type QuotedMessageType = {
 | 
					export type QuotedMessageType = {
 | 
				
			||||||
  attachments: Array<typeof window.WhatIsThis>;
 | 
					  attachments: Array<typeof window.WhatIsThis>;
 | 
				
			||||||
| 
						 | 
					@ -90,6 +96,9 @@ export type StickerMessageType = {
 | 
				
			||||||
  stickerId: number;
 | 
					  stickerId: number;
 | 
				
			||||||
  packKey: string;
 | 
					  packKey: string;
 | 
				
			||||||
  data?: AttachmentType;
 | 
					  data?: AttachmentType;
 | 
				
			||||||
 | 
					  path?: string;
 | 
				
			||||||
 | 
					  width?: number;
 | 
				
			||||||
 | 
					  height?: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type RetryOptions = Readonly<{
 | 
					export type RetryOptions = Readonly<{
 | 
				
			||||||
| 
						 | 
					@ -129,9 +138,9 @@ export type MessageAttributesType = {
 | 
				
			||||||
  expireTimer?: number;
 | 
					  expireTimer?: number;
 | 
				
			||||||
  groupMigration?: GroupMigrationType;
 | 
					  groupMigration?: GroupMigrationType;
 | 
				
			||||||
  group_update?: GroupV1Update;
 | 
					  group_update?: GroupV1Update;
 | 
				
			||||||
  hasAttachments?: boolean;
 | 
					  hasAttachments?: boolean | 0 | 1;
 | 
				
			||||||
  hasFileAttachments?: boolean;
 | 
					  hasFileAttachments?: boolean | 0 | 1;
 | 
				
			||||||
  hasVisualMediaAttachments?: boolean;
 | 
					  hasVisualMediaAttachments?: boolean | 0 | 1;
 | 
				
			||||||
  isErased?: boolean;
 | 
					  isErased?: boolean;
 | 
				
			||||||
  isTapToViewInvalid?: boolean;
 | 
					  isTapToViewInvalid?: boolean;
 | 
				
			||||||
  isViewOnce?: boolean;
 | 
					  isViewOnce?: boolean;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ import { deleteExternalFiles } from '../types/Conversation';
 | 
				
			||||||
import { expiringMessagesDeletionService } from '../services/expiringMessagesDeletion';
 | 
					import { expiringMessagesDeletionService } from '../services/expiringMessagesDeletion';
 | 
				
			||||||
import { tapToViewMessagesDeletionService } from '../services/tapToViewMessagesDeletionService';
 | 
					import { tapToViewMessagesDeletionService } from '../services/tapToViewMessagesDeletionService';
 | 
				
			||||||
import * as Bytes from '../Bytes';
 | 
					import * as Bytes from '../Bytes';
 | 
				
			||||||
import { CURRENT_SCHEMA_VERSION } from '../../js/modules/types/message';
 | 
					import { CURRENT_SCHEMA_VERSION } from '../types/Message2';
 | 
				
			||||||
import { createBatcher } from '../util/batcher';
 | 
					import { createBatcher } from '../util/batcher';
 | 
				
			||||||
import { assert, strictAssert } from '../util/assert';
 | 
					import { assert, strictAssert } from '../util/assert';
 | 
				
			||||||
import { cleanDataForIpc } from './cleanDataForIpc';
 | 
					import { cleanDataForIpc } from './cleanDataForIpc';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1491,7 +1491,7 @@ export function getPropsForAttachment(
 | 
				
			||||||
    url: path
 | 
					    url: path
 | 
				
			||||||
      ? window.Signal.Migrations.getAbsoluteAttachmentPath(path)
 | 
					      ? window.Signal.Migrations.getAbsoluteAttachmentPath(path)
 | 
				
			||||||
      : undefined,
 | 
					      : undefined,
 | 
				
			||||||
    screenshot: screenshot
 | 
					    screenshot: screenshot?.path
 | 
				
			||||||
      ? {
 | 
					      ? {
 | 
				
			||||||
          ...screenshot,
 | 
					          ...screenshot,
 | 
				
			||||||
          url: window.Signal.Migrations.getAbsoluteAttachmentPath(
 | 
					          url: window.Signal.Migrations.getAbsoluteAttachmentPath(
 | 
				
			||||||
| 
						 | 
					@ -1499,7 +1499,7 @@ export function getPropsForAttachment(
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      : undefined,
 | 
					      : undefined,
 | 
				
			||||||
    thumbnail: thumbnail
 | 
					    thumbnail: thumbnail?.path
 | 
				
			||||||
      ? {
 | 
					      ? {
 | 
				
			||||||
          ...thumbnail,
 | 
					          ...thumbnail,
 | 
				
			||||||
          url: window.Signal.Migrations.getAbsoluteAttachmentPath(
 | 
					          url: window.Signal.Migrations.getAbsoluteAttachmentPath(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,7 +270,7 @@ describe('Contact', () => {
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      assert.deepEqual(result, message.contact[0]);
 | 
					      assert.deepEqual(result, message.contact[0]);
 | 
				
			||||||
| 
						 | 
					@ -311,7 +311,7 @@ describe('Contact', () => {
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        regionCode: 'US',
 | 
					        getRegionCode: () => 'US',
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
| 
						 | 
					@ -355,7 +355,7 @@ describe('Contact', () => {
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
| 
						 | 
					@ -440,7 +440,7 @@ describe('Contact', () => {
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
| 
						 | 
					@ -487,7 +487,7 @@ describe('Contact', () => {
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
| 
						 | 
					@ -534,7 +534,7 @@ describe('Contact', () => {
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
| 
						 | 
					@ -577,7 +577,7 @@ describe('Contact', () => {
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
| 
						 | 
					@ -606,7 +606,7 @@ describe('Contact', () => {
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      const result = await upgradeVersion(message.contact[0], {
 | 
					      const result = await upgradeVersion(message.contact[0], {
 | 
				
			||||||
        regionCode: '1',
 | 
					        getRegionCode: () => '1',
 | 
				
			||||||
        writeNewAttachmentData,
 | 
					        writeNewAttachmentData,
 | 
				
			||||||
        message,
 | 
					        message,
 | 
				
			||||||
        logger,
 | 
					        logger,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,30 +1,97 @@
 | 
				
			||||||
// Copyright 2018-2020 Signal Messenger, LLC
 | 
					// Copyright 2018-2020 Signal Messenger, LLC
 | 
				
			||||||
// SPDX-License-Identifier: AGPL-3.0-only
 | 
					// SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { assert } = require('chai');
 | 
					import { assert } from 'chai';
 | 
				
			||||||
const sinon = require('sinon');
 | 
					import * as sinon from 'sinon';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Message = require('../../../js/modules/types/message');
 | 
					import * as Message from '../../types/Message2';
 | 
				
			||||||
const { SignalService } = require('../../../ts/protobuf');
 | 
					import { SignalService } from '../../protobuf';
 | 
				
			||||||
const Bytes = require('../../../ts/Bytes');
 | 
					import * as Bytes from '../../Bytes';
 | 
				
			||||||
 | 
					import * as MIME from '../../types/MIME';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { EmbeddedContactType } from '../../types/EmbeddedContact';
 | 
				
			||||||
 | 
					import type {
 | 
				
			||||||
 | 
					  MessageAttributesType,
 | 
				
			||||||
 | 
					  StickerMessageType,
 | 
				
			||||||
 | 
					} from '../../model-types.d';
 | 
				
			||||||
 | 
					import type { AttachmentType } from '../../types/Attachment';
 | 
				
			||||||
 | 
					import type { LoggerType } from '../../types/Logging';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Message', () => {
 | 
					describe('Message', () => {
 | 
				
			||||||
  const logger = {
 | 
					  const logger: LoggerType = {
 | 
				
			||||||
    warn: () => null,
 | 
					    warn: () => null,
 | 
				
			||||||
    error: () => null,
 | 
					    error: () => null,
 | 
				
			||||||
 | 
					    fatal: () => null,
 | 
				
			||||||
 | 
					    info: () => null,
 | 
				
			||||||
 | 
					    debug: () => null,
 | 
				
			||||||
 | 
					    trace: () => null,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function getDefaultMessage(
 | 
				
			||||||
 | 
					    props?: Partial<MessageAttributesType>
 | 
				
			||||||
 | 
					  ): MessageAttributesType {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      id: 'some-id',
 | 
				
			||||||
 | 
					      type: 'incoming',
 | 
				
			||||||
 | 
					      sent_at: 45,
 | 
				
			||||||
 | 
					      received_at: 45,
 | 
				
			||||||
 | 
					      timestamp: 45,
 | 
				
			||||||
 | 
					      conversationId: 'some-conversation-id',
 | 
				
			||||||
 | 
					      ...props,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function getDefaultContext(
 | 
				
			||||||
 | 
					    props?: Partial<Message.ContextType>
 | 
				
			||||||
 | 
					  ): Message.ContextType {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      getAbsoluteAttachmentPath: (_path: string) =>
 | 
				
			||||||
 | 
					        'fake-absolute-attachment-path',
 | 
				
			||||||
 | 
					      getAbsoluteStickerPath: (_path: string) => 'fake-absolute-sticker-path',
 | 
				
			||||||
 | 
					      getImageDimensions: async (_params: {
 | 
				
			||||||
 | 
					        objectUrl: string;
 | 
				
			||||||
 | 
					        logger: LoggerType;
 | 
				
			||||||
 | 
					      }) => ({
 | 
				
			||||||
 | 
					        width: 10,
 | 
				
			||||||
 | 
					        height: 20,
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      getRegionCode: () => 'region-code',
 | 
				
			||||||
 | 
					      logger,
 | 
				
			||||||
 | 
					      makeImageThumbnail: async (_params: {
 | 
				
			||||||
 | 
					        size: number;
 | 
				
			||||||
 | 
					        objectUrl: string;
 | 
				
			||||||
 | 
					        contentType: MIME.MIMEType;
 | 
				
			||||||
 | 
					        logger: LoggerType;
 | 
				
			||||||
 | 
					      }) => new Blob(),
 | 
				
			||||||
 | 
					      makeObjectUrl: (
 | 
				
			||||||
 | 
					        _data: Uint8Array | ArrayBuffer,
 | 
				
			||||||
 | 
					        _contentType: MIME.MIMEType
 | 
				
			||||||
 | 
					      ) => 'fake-object-url',
 | 
				
			||||||
 | 
					      makeVideoScreenshot: async (_params: {
 | 
				
			||||||
 | 
					        objectUrl: string;
 | 
				
			||||||
 | 
					        contentType: MIME.MIMEType;
 | 
				
			||||||
 | 
					        logger: LoggerType;
 | 
				
			||||||
 | 
					      }) => new Blob(),
 | 
				
			||||||
 | 
					      revokeObjectUrl: (_objectUrl: string) => undefined,
 | 
				
			||||||
 | 
					      writeNewAttachmentData: async (_data: Uint8Array) =>
 | 
				
			||||||
 | 
					        'fake-attachment-path',
 | 
				
			||||||
 | 
					      writeNewStickerData: async (_sticker: StickerMessageType) =>
 | 
				
			||||||
 | 
					        'fake-sticker-path',
 | 
				
			||||||
 | 
					      ...props,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const writeExistingAttachmentData = () => Promise.resolve();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('createAttachmentDataWriter', () => {
 | 
					  describe('createAttachmentDataWriter', () => {
 | 
				
			||||||
    it('should ignore messages that didn’t go through attachment migration', async () => {
 | 
					    it('should ignore messages that didn’t go through attachment migration', async () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 2,
 | 
					        schemaVersion: 2,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 2,
 | 
					        schemaVersion: 2,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const writeExistingAttachmentData = () => {};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.createAttachmentDataWriter({
 | 
					      const actual = await Message.createAttachmentDataWriter({
 | 
				
			||||||
        writeExistingAttachmentData,
 | 
					        writeExistingAttachmentData,
 | 
				
			||||||
| 
						 | 
					@ -34,17 +101,16 @@ describe('Message', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should ignore messages without attachments', async () => {
 | 
					    it('should ignore messages without attachments', async () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const writeExistingAttachmentData = () => {};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.createAttachmentDataWriter({
 | 
					      const actual = await Message.createAttachmentDataWriter({
 | 
				
			||||||
        writeExistingAttachmentData,
 | 
					        writeExistingAttachmentData,
 | 
				
			||||||
| 
						 | 
					@ -54,32 +120,39 @@ describe('Message', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should write attachments to file system on original path', async () => {
 | 
					    it('should write attachments to file system on original path', async () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [
 | 
					        attachments: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
 | 
					            contentType: MIME.IMAGE_GIF,
 | 
				
			||||||
 | 
					            size: 3534,
 | 
				
			||||||
            path: 'ab/abcdefghi',
 | 
					            path: 'ab/abcdefghi',
 | 
				
			||||||
            data: Bytes.fromString('It’s easy if you try'),
 | 
					            data: Bytes.fromString('It’s easy if you try'),
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [
 | 
					        attachments: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
 | 
					            contentType: MIME.IMAGE_GIF,
 | 
				
			||||||
 | 
					            size: 3534,
 | 
				
			||||||
            path: 'ab/abcdefghi',
 | 
					            path: 'ab/abcdefghi',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        contact: [],
 | 
					        contact: [],
 | 
				
			||||||
        preview: [],
 | 
					        preview: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const writeExistingAttachmentData = attachment => {
 | 
					      // eslint-disable-next-line @typescript-eslint/no-shadow
 | 
				
			||||||
 | 
					      const writeExistingAttachmentData = async (
 | 
				
			||||||
 | 
					        attachment: Pick<AttachmentType, 'data' | 'path'>
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
        assert.equal(attachment.path, 'ab/abcdefghi');
 | 
					        assert.equal(attachment.path, 'ab/abcdefghi');
 | 
				
			||||||
        assert.strictEqual(
 | 
					        assert.strictEqual(
 | 
				
			||||||
          Bytes.toString(attachment.data),
 | 
					          Bytes.toString(attachment.data || new Uint8Array()),
 | 
				
			||||||
          'It’s easy if you try'
 | 
					          'It’s easy if you try'
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
| 
						 | 
					@ -92,11 +165,15 @@ describe('Message', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should process quote attachment thumbnails', async () => {
 | 
					    it('should process quote attachment thumbnails', async () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
 | 
					          id: 3523,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'some-message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              thumbnail: {
 | 
					              thumbnail: {
 | 
				
			||||||
| 
						 | 
					@ -106,12 +183,16 @@ describe('Message', () => {
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
 | 
					          id: 3523,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'some-message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              thumbnail: {
 | 
					              thumbnail: {
 | 
				
			||||||
| 
						 | 
					@ -122,12 +203,15 @@ describe('Message', () => {
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        contact: [],
 | 
					        contact: [],
 | 
				
			||||||
        preview: [],
 | 
					        preview: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const writeExistingAttachmentData = attachment => {
 | 
					      // eslint-disable-next-line @typescript-eslint/no-shadow
 | 
				
			||||||
 | 
					      const writeExistingAttachmentData = async (
 | 
				
			||||||
 | 
					        attachment: Pick<AttachmentType, 'data' | 'path'>
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
        assert.equal(attachment.path, 'ab/abcdefghi');
 | 
					        assert.equal(attachment.path, 'ab/abcdefghi');
 | 
				
			||||||
        assert.strictEqual(
 | 
					        assert.strictEqual(
 | 
				
			||||||
          Bytes.toString(attachment.data),
 | 
					          Bytes.toString(attachment.data || new Uint8Array()),
 | 
				
			||||||
          'It’s easy if you try'
 | 
					          'It’s easy if you try'
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
| 
						 | 
					@ -140,45 +224,52 @@ describe('Message', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should process contact avatars', async () => {
 | 
					    it('should process contact avatars', async () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
        contact: [
 | 
					        contact: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            name: 'john',
 | 
					            name: { givenName: 'john' },
 | 
				
			||||||
            avatar: {
 | 
					            avatar: {
 | 
				
			||||||
              isProfile: false,
 | 
					              isProfile: false,
 | 
				
			||||||
              avatar: {
 | 
					              avatar: {
 | 
				
			||||||
 | 
					                contentType: MIME.IMAGE_PNG,
 | 
				
			||||||
 | 
					                size: 47,
 | 
				
			||||||
                path: 'ab/abcdefghi',
 | 
					                path: 'ab/abcdefghi',
 | 
				
			||||||
                data: Bytes.fromString('It’s easy if you try'),
 | 
					                data: Bytes.fromString('It’s easy if you try'),
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
        contact: [
 | 
					        contact: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            name: 'john',
 | 
					            name: { givenName: 'john' },
 | 
				
			||||||
            avatar: {
 | 
					            avatar: {
 | 
				
			||||||
              isProfile: false,
 | 
					              isProfile: false,
 | 
				
			||||||
              avatar: {
 | 
					              avatar: {
 | 
				
			||||||
 | 
					                contentType: MIME.IMAGE_PNG,
 | 
				
			||||||
 | 
					                size: 47,
 | 
				
			||||||
                path: 'ab/abcdefghi',
 | 
					                path: 'ab/abcdefghi',
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        preview: [],
 | 
					        preview: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const writeExistingAttachmentData = attachment => {
 | 
					      // eslint-disable-next-line @typescript-eslint/no-shadow
 | 
				
			||||||
 | 
					      const writeExistingAttachmentData = async (
 | 
				
			||||||
 | 
					        attachment: Pick<AttachmentType, 'data' | 'path'>
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
        assert.equal(attachment.path, 'ab/abcdefghi');
 | 
					        assert.equal(attachment.path, 'ab/abcdefghi');
 | 
				
			||||||
        assert.strictEqual(
 | 
					        assert.strictEqual(
 | 
				
			||||||
          Bytes.toString(attachment.data),
 | 
					          Bytes.toString(attachment.data || new Uint8Array()),
 | 
				
			||||||
          'It’s easy if you try'
 | 
					          'It’s easy if you try'
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
| 
						 | 
					@ -193,14 +284,14 @@ describe('Message', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('initializeSchemaVersion', () => {
 | 
					  describe('initializeSchemaVersion', () => {
 | 
				
			||||||
    it('should ignore messages with previously inherited schema', () => {
 | 
					    it('should ignore messages with previously inherited schema', () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 2,
 | 
					        schemaVersion: 2,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'Imagine there is no heaven…',
 | 
					        body: 'Imagine there is no heaven…',
 | 
				
			||||||
        schemaVersion: 2,
 | 
					        schemaVersion: 2,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = Message.initializeSchemaVersion({
 | 
					      const actual = Message.initializeSchemaVersion({
 | 
				
			||||||
        message: input,
 | 
					        message: input,
 | 
				
			||||||
| 
						 | 
					@ -211,15 +302,15 @@ describe('Message', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context('for message without attachments', () => {
 | 
					    context('for message without attachments', () => {
 | 
				
			||||||
      it('should initialize schema version to zero', () => {
 | 
					      it('should initialize schema version to zero', () => {
 | 
				
			||||||
        const input = {
 | 
					        const input = getDefaultMessage({
 | 
				
			||||||
          body: 'Imagine there is no heaven…',
 | 
					          body: 'Imagine there is no heaven…',
 | 
				
			||||||
          attachments: [],
 | 
					          attachments: [],
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
        const expected = {
 | 
					        const expected = getDefaultMessage({
 | 
				
			||||||
          body: 'Imagine there is no heaven…',
 | 
					          body: 'Imagine there is no heaven…',
 | 
				
			||||||
          attachments: [],
 | 
					          attachments: [],
 | 
				
			||||||
          schemaVersion: 0,
 | 
					          schemaVersion: 0,
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const actual = Message.initializeSchemaVersion({
 | 
					        const actual = Message.initializeSchemaVersion({
 | 
				
			||||||
          message: input,
 | 
					          message: input,
 | 
				
			||||||
| 
						 | 
					@ -231,26 +322,28 @@ describe('Message', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context('for message with attachments', () => {
 | 
					    context('for message with attachments', () => {
 | 
				
			||||||
      it('should inherit existing attachment schema version', () => {
 | 
					      it('should inherit existing attachment schema version', () => {
 | 
				
			||||||
        const input = {
 | 
					        const input = getDefaultMessage({
 | 
				
			||||||
          body: 'Imagine there is no heaven…',
 | 
					          body: 'Imagine there is no heaven…',
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              contentType: 'image/jpeg',
 | 
					              contentType: MIME.IMAGE_JPEG,
 | 
				
			||||||
 | 
					              size: 45,
 | 
				
			||||||
              fileName: 'lennon.jpg',
 | 
					              fileName: 'lennon.jpg',
 | 
				
			||||||
              schemaVersion: 7,
 | 
					              schemaVersion: 7,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
        const expected = {
 | 
					        const expected = getDefaultMessage({
 | 
				
			||||||
          body: 'Imagine there is no heaven…',
 | 
					          body: 'Imagine there is no heaven…',
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              contentType: 'image/jpeg',
 | 
					              contentType: MIME.IMAGE_JPEG,
 | 
				
			||||||
 | 
					              size: 45,
 | 
				
			||||||
              fileName: 'lennon.jpg',
 | 
					              fileName: 'lennon.jpg',
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
          schemaVersion: 7,
 | 
					          schemaVersion: 7,
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const actual = Message.initializeSchemaVersion({
 | 
					        const actual = Message.initializeSchemaVersion({
 | 
				
			||||||
          message: input,
 | 
					          message: input,
 | 
				
			||||||
| 
						 | 
					@ -263,10 +356,10 @@ describe('Message', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('upgradeSchema', () => {
 | 
					  describe('upgradeSchema', () => {
 | 
				
			||||||
    it('should upgrade an unversioned message to the latest version', async () => {
 | 
					    it('should upgrade an unversioned message to the latest version', async () => {
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        attachments: [
 | 
					        attachments: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            contentType: 'audio/aac',
 | 
					            contentType: MIME.AUDIO_AAC,
 | 
				
			||||||
            flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
 | 
					            flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
 | 
				
			||||||
            data: Bytes.fromString('It’s easy if you try'),
 | 
					            data: Bytes.fromString('It’s easy if you try'),
 | 
				
			||||||
            fileName: 'test\u202Dfig.exe',
 | 
					            fileName: 'test\u202Dfig.exe',
 | 
				
			||||||
| 
						 | 
					@ -274,11 +367,11 @@ describe('Message', () => {
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        schemaVersion: 0,
 | 
					        schemaVersion: 0,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        attachments: [
 | 
					        attachments: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            contentType: 'audio/aac',
 | 
					            contentType: MIME.AUDIO_AAC,
 | 
				
			||||||
            flags: 1,
 | 
					            flags: 1,
 | 
				
			||||||
            path: 'abc/abcdefg',
 | 
					            path: 'abc/abcdefg',
 | 
				
			||||||
            fileName: 'test\uFFFDfig.exe',
 | 
					            fileName: 'test\uFFFDfig.exe',
 | 
				
			||||||
| 
						 | 
					@ -290,10 +383,10 @@ describe('Message', () => {
 | 
				
			||||||
        hasFileAttachments: undefined,
 | 
					        hasFileAttachments: undefined,
 | 
				
			||||||
        schemaVersion: Message.CURRENT_SCHEMA_VERSION,
 | 
					        schemaVersion: Message.CURRENT_SCHEMA_VERSION,
 | 
				
			||||||
        contact: [],
 | 
					        contact: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const expectedAttachmentData = 'It’s easy if you try';
 | 
					      const expectedAttachmentData = 'It’s easy if you try';
 | 
				
			||||||
      const context = {
 | 
					      const context = getDefaultContext({
 | 
				
			||||||
        writeNewAttachmentData: async attachmentData => {
 | 
					        writeNewAttachmentData: async attachmentData => {
 | 
				
			||||||
          assert.strictEqual(
 | 
					          assert.strictEqual(
 | 
				
			||||||
            Bytes.toString(attachmentData),
 | 
					            Bytes.toString(attachmentData),
 | 
				
			||||||
| 
						 | 
					@ -301,58 +394,46 @@ describe('Message', () => {
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          return 'abc/abcdefg';
 | 
					          return 'abc/abcdefg';
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        getRegionCode: () => 'US',
 | 
					      });
 | 
				
			||||||
        getAbsoluteAttachmentPath: () => 'some/path/on/disk',
 | 
					 | 
				
			||||||
        makeObjectUrl: () => 'blob://FAKE',
 | 
					 | 
				
			||||||
        revokeObjectUrl: () => null,
 | 
					 | 
				
			||||||
        getImageDimensions: () => ({ height: 10, width: 15 }),
 | 
					 | 
				
			||||||
        makeImageThumbnail: () => new Blob(),
 | 
					 | 
				
			||||||
        makeVideoScreenshot: () => new Blob(),
 | 
					 | 
				
			||||||
        logger: {
 | 
					 | 
				
			||||||
          warn: () => null,
 | 
					 | 
				
			||||||
          error: () => null,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      const actual = await Message.upgradeSchema(input, context);
 | 
					      const actual = await Message.upgradeSchema(input, context);
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context('with multiple upgrade steps', () => {
 | 
					    context('with multiple upgrade steps', () => {
 | 
				
			||||||
      it('should return last valid message when any upgrade step fails', async () => {
 | 
					      it('should return last valid message when any upgrade step fails', async () => {
 | 
				
			||||||
        const input = {
 | 
					        const input = getDefaultMessage({
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              contentType: 'application/json',
 | 
					              contentType: MIME.APPLICATION_JSON,
 | 
				
			||||||
              data: null,
 | 
					 | 
				
			||||||
              fileName: 'test\u202Dfig.exe',
 | 
					              fileName: 'test\u202Dfig.exe',
 | 
				
			||||||
              size: 1111,
 | 
					              size: 1111,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          body: 'start',
 | 
				
			||||||
          schemaVersion: 0,
 | 
					          schemaVersion: 0,
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
        const expected = {
 | 
					        const expected = getDefaultMessage({
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              contentType: 'application/json',
 | 
					              contentType: MIME.APPLICATION_JSON,
 | 
				
			||||||
              data: null,
 | 
					 | 
				
			||||||
              fileName: 'test\u202Dfig.exe',
 | 
					              fileName: 'test\u202Dfig.exe',
 | 
				
			||||||
              size: 1111,
 | 
					              size: 1111,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
          hasUpgradedToVersion1: true,
 | 
					          body: 'start +1',
 | 
				
			||||||
          schemaVersion: 1,
 | 
					          schemaVersion: 1,
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const v1 = async message => ({
 | 
					        const v1 = async (message: MessageAttributesType) => ({
 | 
				
			||||||
          ...message,
 | 
					          ...message,
 | 
				
			||||||
          hasUpgradedToVersion1: true,
 | 
					          body: `${message.body} +1`,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        const v2 = async () => {
 | 
					        const v2 = async () => {
 | 
				
			||||||
          throw new Error('boom');
 | 
					          throw new Error('boom');
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        const v3 = async message => ({
 | 
					        const v3 = async (message: MessageAttributesType) => ({
 | 
				
			||||||
          ...message,
 | 
					          ...message,
 | 
				
			||||||
          hasUpgradedToVersion3: true,
 | 
					          body: `${message.body} +3`,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const toVersion1 = Message._withSchemaVersion({
 | 
					        const toVersion1 = Message._withSchemaVersion({
 | 
				
			||||||
| 
						 | 
					@ -368,8 +449,8 @@ describe('Message', () => {
 | 
				
			||||||
          upgrade: v3,
 | 
					          upgrade: v3,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const context = { logger };
 | 
					        const context = getDefaultContext({ logger });
 | 
				
			||||||
        const upgradeSchema = async message =>
 | 
					        const upgradeSchema = async (message: MessageAttributesType) =>
 | 
				
			||||||
          toVersion3(
 | 
					          toVersion3(
 | 
				
			||||||
            await toVersion2(await toVersion1(message, context), context),
 | 
					            await toVersion2(await toVersion1(message, context), context),
 | 
				
			||||||
            context
 | 
					            context
 | 
				
			||||||
| 
						 | 
					@ -380,42 +461,40 @@ describe('Message', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('should skip out-of-order upgrade steps', async () => {
 | 
					      it('should skip out-of-order upgrade steps', async () => {
 | 
				
			||||||
        const input = {
 | 
					        const input = getDefaultMessage({
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              contentType: 'application/json',
 | 
					              contentType: MIME.APPLICATION_JSON,
 | 
				
			||||||
              data: null,
 | 
					 | 
				
			||||||
              fileName: 'test\u202Dfig.exe',
 | 
					              fileName: 'test\u202Dfig.exe',
 | 
				
			||||||
              size: 1111,
 | 
					              size: 1111,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          body: 'start',
 | 
				
			||||||
          schemaVersion: 0,
 | 
					          schemaVersion: 0,
 | 
				
			||||||
        };
 | 
					        });
 | 
				
			||||||
        const expected = {
 | 
					        const expected = getDefaultMessage({
 | 
				
			||||||
          attachments: [
 | 
					          attachments: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              contentType: 'application/json',
 | 
					              contentType: MIME.APPLICATION_JSON,
 | 
				
			||||||
              data: null,
 | 
					 | 
				
			||||||
              fileName: 'test\u202Dfig.exe',
 | 
					              fileName: 'test\u202Dfig.exe',
 | 
				
			||||||
              size: 1111,
 | 
					              size: 1111,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          body: 'start +1 +2',
 | 
				
			||||||
          schemaVersion: 2,
 | 
					          schemaVersion: 2,
 | 
				
			||||||
          hasUpgradedToVersion1: true,
 | 
					        });
 | 
				
			||||||
          hasUpgradedToVersion2: true,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const v1 = async attachment => ({
 | 
					        const v1 = async (message: MessageAttributesType) => ({
 | 
				
			||||||
          ...attachment,
 | 
					          ...message,
 | 
				
			||||||
          hasUpgradedToVersion1: true,
 | 
					          body: `${message.body} +1`,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        const v2 = async attachment => ({
 | 
					        const v2 = async (message: MessageAttributesType) => ({
 | 
				
			||||||
          ...attachment,
 | 
					          ...message,
 | 
				
			||||||
          hasUpgradedToVersion2: true,
 | 
					          body: `${message.body} +2`,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        const v3 = async attachment => ({
 | 
					        const v3 = async (message: MessageAttributesType) => ({
 | 
				
			||||||
          ...attachment,
 | 
					          ...message,
 | 
				
			||||||
          hasUpgradedToVersion3: true,
 | 
					          body: `${message.body} +3`,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const toVersion1 = Message._withSchemaVersion({
 | 
					        const toVersion1 = Message._withSchemaVersion({
 | 
				
			||||||
| 
						 | 
					@ -431,15 +510,13 @@ describe('Message', () => {
 | 
				
			||||||
          upgrade: v3,
 | 
					          upgrade: v3,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const context = { logger };
 | 
					        const context = getDefaultContext({ logger });
 | 
				
			||||||
        // NOTE: We upgrade to 3 before 2, i.e. the pipeline should abort:
 | 
					        const atVersion1 = await toVersion1(input, context);
 | 
				
			||||||
        const upgradeSchema = async attachment =>
 | 
					 | 
				
			||||||
          toVersion2(
 | 
					 | 
				
			||||||
            await toVersion3(await toVersion1(attachment, context), context),
 | 
					 | 
				
			||||||
            context
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const actual = await upgradeSchema(input);
 | 
					        // Note: this will fail to apply and log, since it's jumping two versions up
 | 
				
			||||||
 | 
					        const atVersion3 = await toVersion3(atVersion1, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const actual = await toVersion2(atVersion3, context);
 | 
				
			||||||
        assert.deepEqual(actual, expected);
 | 
					        assert.deepEqual(actual, expected);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -447,37 +524,49 @@ describe('Message', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('_withSchemaVersion', () => {
 | 
					  describe('_withSchemaVersion', () => {
 | 
				
			||||||
    it('should require a version number', () => {
 | 
					    it('should require a version number', () => {
 | 
				
			||||||
      const toVersionX = () => {};
 | 
					      const toVersionX = () => null;
 | 
				
			||||||
      assert.throws(
 | 
					      assert.throws(
 | 
				
			||||||
        () =>
 | 
					        () =>
 | 
				
			||||||
          Message._withSchemaVersion({ schemaVersion: toVersionX, upgrade: 2 }),
 | 
					          Message._withSchemaVersion({
 | 
				
			||||||
 | 
					            // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
				
			||||||
 | 
					            schemaVersion: toVersionX as any,
 | 
				
			||||||
 | 
					            upgrade: () => Promise.resolve(getDefaultMessage()),
 | 
				
			||||||
 | 
					          }),
 | 
				
			||||||
        '_withSchemaVersion: schemaVersion is invalid'
 | 
					        '_withSchemaVersion: schemaVersion is invalid'
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should require an upgrade function', () => {
 | 
					    it('should require an upgrade function', () => {
 | 
				
			||||||
      assert.throws(
 | 
					      assert.throws(
 | 
				
			||||||
        () => Message._withSchemaVersion({ schemaVersion: 2, upgrade: 3 }),
 | 
					        () =>
 | 
				
			||||||
 | 
					          // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
				
			||||||
 | 
					          Message._withSchemaVersion({ schemaVersion: 2, upgrade: 3 as any }),
 | 
				
			||||||
        '_withSchemaVersion: upgrade must be a function'
 | 
					        '_withSchemaVersion: upgrade must be a function'
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should skip upgrading if message has already been upgraded', async () => {
 | 
					    it('should skip upgrading if message has already been upgraded', async () => {
 | 
				
			||||||
      const upgrade = async message => ({ ...message, foo: true });
 | 
					      const upgrade = async (message: MessageAttributesType) => ({
 | 
				
			||||||
 | 
					        ...message,
 | 
				
			||||||
 | 
					        foo: true,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
      const upgradeWithVersion = Message._withSchemaVersion({
 | 
					      const upgradeWithVersion = Message._withSchemaVersion({
 | 
				
			||||||
        schemaVersion: 3,
 | 
					        schemaVersion: 3,
 | 
				
			||||||
        upgrade,
 | 
					        upgrade,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        id: 'guid-guid-guid-guid',
 | 
					        id: 'guid-guid-guid-guid',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        id: 'guid-guid-guid-guid',
 | 
					        id: 'guid-guid-guid-guid',
 | 
				
			||||||
        schemaVersion: 4,
 | 
					        schemaVersion: 4,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const actual = await upgradeWithVersion(input, { logger });
 | 
					      const actual = await upgradeWithVersion(
 | 
				
			||||||
 | 
					        input,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -490,15 +579,18 @@ describe('Message', () => {
 | 
				
			||||||
        upgrade,
 | 
					        upgrade,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        id: 'guid-guid-guid-guid',
 | 
					        id: 'guid-guid-guid-guid',
 | 
				
			||||||
        schemaVersion: 0,
 | 
					        schemaVersion: 0,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        id: 'guid-guid-guid-guid',
 | 
					        id: 'guid-guid-guid-guid',
 | 
				
			||||||
        schemaVersion: 0,
 | 
					        schemaVersion: 0,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const actual = await upgradeWithVersion(input, { logger });
 | 
					      const actual = await upgradeWithVersion(
 | 
				
			||||||
 | 
					        input,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -506,18 +598,22 @@ describe('Message', () => {
 | 
				
			||||||
      const upgrade = async () => null;
 | 
					      const upgrade = async () => null;
 | 
				
			||||||
      const upgradeWithVersion = Message._withSchemaVersion({
 | 
					      const upgradeWithVersion = Message._withSchemaVersion({
 | 
				
			||||||
        schemaVersion: 3,
 | 
					        schemaVersion: 3,
 | 
				
			||||||
        upgrade,
 | 
					        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
				
			||||||
 | 
					        upgrade: upgrade as any,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const input = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        id: 'guid-guid-guid-guid',
 | 
					        id: 'guid-guid-guid-guid',
 | 
				
			||||||
        schemaVersion: 0,
 | 
					        schemaVersion: 0,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        id: 'guid-guid-guid-guid',
 | 
					        id: 'guid-guid-guid-guid',
 | 
				
			||||||
        schemaVersion: 0,
 | 
					        schemaVersion: 0,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const actual = await upgradeWithVersion(input, { logger });
 | 
					      const actual = await upgradeWithVersion(
 | 
				
			||||||
 | 
					        input,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
| 
						 | 
					@ -529,10 +625,10 @@ describe('Message', () => {
 | 
				
			||||||
        .throws(new Error("Shouldn't be called"));
 | 
					        .throws(new Error("Shouldn't be called"));
 | 
				
			||||||
      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
					      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message);
 | 
					      const result = await upgradeVersion(message, getDefaultContext());
 | 
				
			||||||
      assert.deepEqual(result, message);
 | 
					      assert.deepEqual(result, message);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -542,20 +638,32 @@ describe('Message', () => {
 | 
				
			||||||
        .throws(new Error("Shouldn't be called"));
 | 
					        .throws(new Error("Shouldn't be called"));
 | 
				
			||||||
      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
					      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
        },
 | 
					          id: 34233,
 | 
				
			||||||
      };
 | 
					          isViewOnce: false,
 | 
				
			||||||
      const expected = {
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
 | 
					          // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
				
			||||||
 | 
					        } as any,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
          attachments: [],
 | 
					          attachments: [],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message, { logger });
 | 
					      const result = await upgradeVersion(
 | 
				
			||||||
 | 
					        message,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(result, expected);
 | 
					      assert.deepEqual(result, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -565,14 +673,21 @@ describe('Message', () => {
 | 
				
			||||||
        .throws(new Error("Shouldn't be called"));
 | 
					        .throws(new Error("Shouldn't be called"));
 | 
				
			||||||
      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
					      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
          attachments: [],
 | 
					          attachments: [],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message, { logger });
 | 
					      const result = await upgradeVersion(
 | 
				
			||||||
 | 
					        message,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(result, message);
 | 
					      assert.deepEqual(result, message);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -582,7 +697,7 @@ describe('Message', () => {
 | 
				
			||||||
        .throws(new Error("Shouldn't be called"));
 | 
					        .throws(new Error("Shouldn't be called"));
 | 
				
			||||||
      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
					      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
| 
						 | 
					@ -592,9 +707,16 @@ describe('Message', () => {
 | 
				
			||||||
              contentType: 'text/plain',
 | 
					              contentType: 'text/plain',
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message, { logger });
 | 
					      const result = await upgradeVersion(
 | 
				
			||||||
 | 
					        message,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(result, message);
 | 
					      assert.deepEqual(result, message);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -604,7 +726,7 @@ describe('Message', () => {
 | 
				
			||||||
        .returns({ fileName: 'processed!' });
 | 
					        .returns({ fileName: 'processed!' });
 | 
				
			||||||
      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
					      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
| 
						 | 
					@ -617,9 +739,13 @@ describe('Message', () => {
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
| 
						 | 
					@ -632,9 +758,16 @@ describe('Message', () => {
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message, { logger });
 | 
					      const result = await upgradeVersion(
 | 
				
			||||||
 | 
					        message,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(result, expected);
 | 
					      assert.deepEqual(result, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -644,7 +777,7 @@ describe('Message', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
					      const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
| 
						 | 
					@ -655,9 +788,13 @@ describe('Message', () => {
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        quote: {
 | 
					        quote: {
 | 
				
			||||||
          text: 'hey!',
 | 
					          text: 'hey!',
 | 
				
			||||||
| 
						 | 
					@ -668,9 +805,16 @@ describe('Message', () => {
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
					          id: 34233,
 | 
				
			||||||
 | 
					          isViewOnce: false,
 | 
				
			||||||
 | 
					          messageId: 'message-id',
 | 
				
			||||||
 | 
					          referencedMessageNotFound: false,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message, { logger });
 | 
					      const result = await upgradeVersion(
 | 
				
			||||||
 | 
					        message,
 | 
				
			||||||
 | 
					        getDefaultContext({ logger })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      assert.deepEqual(result, expected);
 | 
					      assert.deepEqual(result, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
| 
						 | 
					@ -682,22 +826,23 @@ describe('Message', () => {
 | 
				
			||||||
        .throws(new Error("Shouldn't be called"));
 | 
					        .throws(new Error("Shouldn't be called"));
 | 
				
			||||||
      const upgradeVersion = Message._mapContact(upgradeContact);
 | 
					      const upgradeVersion = Message._mapContact(upgradeContact);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        contact: [],
 | 
					        contact: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message);
 | 
					      const result = await upgradeVersion(message, getDefaultContext());
 | 
				
			||||||
      assert.deepEqual(result, expected);
 | 
					      assert.deepEqual(result, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('handles one contact', async () => {
 | 
					    it('handles one contact', async () => {
 | 
				
			||||||
      const upgradeContact = contact => Promise.resolve(contact);
 | 
					      const upgradeContact = (contact: EmbeddedContactType) =>
 | 
				
			||||||
 | 
					        Promise.resolve(contact);
 | 
				
			||||||
      const upgradeVersion = Message._mapContact(upgradeContact);
 | 
					      const upgradeVersion = Message._mapContact(upgradeContact);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const message = {
 | 
					      const message = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        contact: [
 | 
					        contact: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
| 
						 | 
					@ -706,8 +851,8 @@ describe('Message', () => {
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        body: 'hey there!',
 | 
					        body: 'hey there!',
 | 
				
			||||||
        contact: [
 | 
					        contact: [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
| 
						 | 
					@ -716,8 +861,8 @@ describe('Message', () => {
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const result = await upgradeVersion(message);
 | 
					      const result = await upgradeVersion(message, getDefaultContext());
 | 
				
			||||||
      assert.deepEqual(result, expected);
 | 
					      assert.deepEqual(result, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
| 
						 | 
					@ -4,15 +4,29 @@
 | 
				
			||||||
import { assert } from 'chai';
 | 
					import { assert } from 'chai';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as Message from '../../../types/message/initializeAttachmentMetadata';
 | 
					import * as Message from '../../../types/message/initializeAttachmentMetadata';
 | 
				
			||||||
import type { IncomingMessage } from '../../../types/Message';
 | 
					 | 
				
			||||||
import { SignalService } from '../../../protobuf';
 | 
					import { SignalService } from '../../../protobuf';
 | 
				
			||||||
import * as MIME from '../../../types/MIME';
 | 
					import * as MIME from '../../../types/MIME';
 | 
				
			||||||
import * as Bytes from '../../../Bytes';
 | 
					import * as Bytes from '../../../Bytes';
 | 
				
			||||||
 | 
					import type { MessageAttributesType } from '../../../model-types.d';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getDefaultMessage(
 | 
				
			||||||
 | 
					  props?: Partial<MessageAttributesType>
 | 
				
			||||||
 | 
					): MessageAttributesType {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    id: 'some-id',
 | 
				
			||||||
 | 
					    type: 'incoming',
 | 
				
			||||||
 | 
					    sent_at: 45,
 | 
				
			||||||
 | 
					    received_at: 45,
 | 
				
			||||||
 | 
					    timestamp: 45,
 | 
				
			||||||
 | 
					    conversationId: 'some-conversation-id',
 | 
				
			||||||
 | 
					    ...props,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Message', () => {
 | 
					describe('Message', () => {
 | 
				
			||||||
  describe('initializeAttachmentMetadata', () => {
 | 
					  describe('initializeAttachmentMetadata', () => {
 | 
				
			||||||
    it('should classify visual media attachments', async () => {
 | 
					    it('should classify visual media attachments', async () => {
 | 
				
			||||||
      const input: IncomingMessage = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -27,8 +41,8 @@ describe('Message', () => {
 | 
				
			||||||
            size: 1111,
 | 
					            size: 1111,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected: IncomingMessage = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -46,14 +60,14 @@ describe('Message', () => {
 | 
				
			||||||
        hasAttachments: 1,
 | 
					        hasAttachments: 1,
 | 
				
			||||||
        hasVisualMediaAttachments: 1,
 | 
					        hasVisualMediaAttachments: 1,
 | 
				
			||||||
        hasFileAttachments: undefined,
 | 
					        hasFileAttachments: undefined,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.initializeAttachmentMetadata(input);
 | 
					      const actual = await Message.initializeAttachmentMetadata(input);
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should classify file attachments', async () => {
 | 
					    it('should classify file attachments', async () => {
 | 
				
			||||||
      const input: IncomingMessage = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -68,8 +82,8 @@ describe('Message', () => {
 | 
				
			||||||
            size: 1111,
 | 
					            size: 1111,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected: IncomingMessage = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -87,14 +101,14 @@ describe('Message', () => {
 | 
				
			||||||
        hasAttachments: 1,
 | 
					        hasAttachments: 1,
 | 
				
			||||||
        hasVisualMediaAttachments: undefined,
 | 
					        hasVisualMediaAttachments: undefined,
 | 
				
			||||||
        hasFileAttachments: 1,
 | 
					        hasFileAttachments: 1,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.initializeAttachmentMetadata(input);
 | 
					      const actual = await Message.initializeAttachmentMetadata(input);
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should classify voice message attachments', async () => {
 | 
					    it('should classify voice message attachments', async () => {
 | 
				
			||||||
      const input: IncomingMessage = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -110,8 +124,8 @@ describe('Message', () => {
 | 
				
			||||||
            size: 1111,
 | 
					            size: 1111,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected: IncomingMessage = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -130,14 +144,14 @@ describe('Message', () => {
 | 
				
			||||||
        hasAttachments: 1,
 | 
					        hasAttachments: 1,
 | 
				
			||||||
        hasVisualMediaAttachments: undefined,
 | 
					        hasVisualMediaAttachments: undefined,
 | 
				
			||||||
        hasFileAttachments: undefined,
 | 
					        hasFileAttachments: undefined,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.initializeAttachmentMetadata(input);
 | 
					      const actual = await Message.initializeAttachmentMetadata(input);
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('does not include long message attachments', async () => {
 | 
					    it('does not include long message attachments', async () => {
 | 
				
			||||||
      const input: IncomingMessage = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -152,8 +166,8 @@ describe('Message', () => {
 | 
				
			||||||
            size: 1111,
 | 
					            size: 1111,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected: IncomingMessage = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -171,14 +185,14 @@ describe('Message', () => {
 | 
				
			||||||
        hasAttachments: 0,
 | 
					        hasAttachments: 0,
 | 
				
			||||||
        hasVisualMediaAttachments: undefined,
 | 
					        hasVisualMediaAttachments: undefined,
 | 
				
			||||||
        hasFileAttachments: undefined,
 | 
					        hasFileAttachments: undefined,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.initializeAttachmentMetadata(input);
 | 
					      const actual = await Message.initializeAttachmentMetadata(input);
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('handles not attachments', async () => {
 | 
					    it('handles not attachments', async () => {
 | 
				
			||||||
      const input: IncomingMessage = {
 | 
					      const input = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -186,8 +200,8 @@ describe('Message', () => {
 | 
				
			||||||
        received_at: 1523317140899,
 | 
					        received_at: 1523317140899,
 | 
				
			||||||
        sent_at: 1523317140800,
 | 
					        sent_at: 1523317140800,
 | 
				
			||||||
        attachments: [],
 | 
					        attachments: [],
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
      const expected: IncomingMessage = {
 | 
					      const expected = getDefaultMessage({
 | 
				
			||||||
        type: 'incoming',
 | 
					        type: 'incoming',
 | 
				
			||||||
        conversationId: 'foo',
 | 
					        conversationId: 'foo',
 | 
				
			||||||
        id: '11111111-1111-1111-1111-111111111111',
 | 
					        id: '11111111-1111-1111-1111-111111111111',
 | 
				
			||||||
| 
						 | 
					@ -198,7 +212,7 @@ describe('Message', () => {
 | 
				
			||||||
        hasAttachments: 0,
 | 
					        hasAttachments: 0,
 | 
				
			||||||
        hasVisualMediaAttachments: undefined,
 | 
					        hasVisualMediaAttachments: undefined,
 | 
				
			||||||
        hasFileAttachments: undefined,
 | 
					        hasFileAttachments: undefined,
 | 
				
			||||||
      };
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const actual = await Message.initializeAttachmentMetadata(input);
 | 
					      const actual = await Message.initializeAttachmentMetadata(input);
 | 
				
			||||||
      assert.deepEqual(actual, expected);
 | 
					      assert.deepEqual(actual, expected);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,8 +24,8 @@ import {
 | 
				
			||||||
} from '../util/GoogleChrome';
 | 
					} from '../util/GoogleChrome';
 | 
				
			||||||
import type { LocalizerType } from './Util';
 | 
					import type { LocalizerType } from './Util';
 | 
				
			||||||
import { ThemeType } from './Util';
 | 
					import { ThemeType } from './Util';
 | 
				
			||||||
import * as GoogleChrome from '../util/GoogleChrome';
 | 
					 | 
				
			||||||
import { scaleImageToLevel } from '../util/scaleImageToLevel';
 | 
					import { scaleImageToLevel } from '../util/scaleImageToLevel';
 | 
				
			||||||
 | 
					import * as GoogleChrome from '../util/GoogleChrome';
 | 
				
			||||||
import { parseIntOrThrow } from '../util/parseIntOrThrow';
 | 
					import { parseIntOrThrow } from '../util/parseIntOrThrow';
 | 
				
			||||||
import { getValue } from '../RemoteConfig';
 | 
					import { getValue } from '../RemoteConfig';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,7 @@ export type AttachmentType = {
 | 
				
			||||||
    url?: string;
 | 
					    url?: string;
 | 
				
			||||||
    contentType: MIME.MIMEType;
 | 
					    contentType: MIME.MIMEType;
 | 
				
			||||||
    path: string;
 | 
					    path: string;
 | 
				
			||||||
 | 
					    data?: Uint8Array;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  screenshotData?: Uint8Array;
 | 
					  screenshotData?: Uint8Array;
 | 
				
			||||||
  screenshotPath?: string;
 | 
					  screenshotPath?: string;
 | 
				
			||||||
| 
						 | 
					@ -74,6 +75,9 @@ export type AttachmentType = {
 | 
				
			||||||
  /** Legacy field. Used only for downloading old attachments */
 | 
					  /** Legacy field. Used only for downloading old attachments */
 | 
				
			||||||
  id?: number;
 | 
					  id?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Legacy field, used long ago for migrating attachments to disk. */
 | 
				
			||||||
 | 
					  schemaVersion?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Removed once we download the attachment */
 | 
					  /** Removed once we download the attachment */
 | 
				
			||||||
  digest?: string;
 | 
					  digest?: string;
 | 
				
			||||||
  key?: string;
 | 
					  key?: string;
 | 
				
			||||||
| 
						 | 
					@ -159,11 +163,12 @@ export type AttachmentDraftType =
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ThumbnailType = {
 | 
					export type ThumbnailType = {
 | 
				
			||||||
  height: number;
 | 
					  height?: number;
 | 
				
			||||||
  width: number;
 | 
					  width?: number;
 | 
				
			||||||
  url?: string;
 | 
					  url?: string;
 | 
				
			||||||
  contentType: MIME.MIMEType;
 | 
					  contentType: MIME.MIMEType;
 | 
				
			||||||
  path: string;
 | 
					  path?: string;
 | 
				
			||||||
 | 
					  data?: Uint8Array;
 | 
				
			||||||
  // Only used when quote needed to make an in-memory thumbnail
 | 
					  // Only used when quote needed to make an in-memory thumbnail
 | 
				
			||||||
  objectUrl?: string;
 | 
					  objectUrl?: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -432,16 +437,19 @@ export async function captureDimensionsAndScreenshot(
 | 
				
			||||||
  attachment: AttachmentType,
 | 
					  attachment: AttachmentType,
 | 
				
			||||||
  params: {
 | 
					  params: {
 | 
				
			||||||
    writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
 | 
					    writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
 | 
				
			||||||
    getAbsoluteAttachmentPath: (path: string) => Promise<string>;
 | 
					    getAbsoluteAttachmentPath: (path: string) => string;
 | 
				
			||||||
    makeObjectUrl: (
 | 
					    makeObjectUrl: (
 | 
				
			||||||
      data: Uint8Array | ArrayBuffer,
 | 
					      data: Uint8Array | ArrayBuffer,
 | 
				
			||||||
      contentType: MIME.MIMEType
 | 
					      contentType: MIME.MIMEType
 | 
				
			||||||
    ) => string;
 | 
					    ) => string;
 | 
				
			||||||
    revokeObjectUrl: (path: string) => void;
 | 
					    revokeObjectUrl: (path: string) => void;
 | 
				
			||||||
    getImageDimensions: (params: { objectUrl: string; logger: LoggerType }) => {
 | 
					    getImageDimensions: (params: {
 | 
				
			||||||
 | 
					      objectUrl: string;
 | 
				
			||||||
 | 
					      logger: LoggerType;
 | 
				
			||||||
 | 
					    }) => Promise<{
 | 
				
			||||||
      width: number;
 | 
					      width: number;
 | 
				
			||||||
      height: number;
 | 
					      height: number;
 | 
				
			||||||
    };
 | 
					    }>;
 | 
				
			||||||
    makeImageThumbnail: (params: {
 | 
					    makeImageThumbnail: (params: {
 | 
				
			||||||
      size: number;
 | 
					      size: number;
 | 
				
			||||||
      objectUrl: string;
 | 
					      objectUrl: string;
 | 
				
			||||||
| 
						 | 
					@ -481,7 +489,7 @@ export async function captureDimensionsAndScreenshot(
 | 
				
			||||||
    return attachment;
 | 
					    return attachment;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const absolutePath = await getAbsoluteAttachmentPath(attachment.path);
 | 
					  const absolutePath = getAbsoluteAttachmentPath(attachment.path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (GoogleChrome.isImageTypeSupported(contentType)) {
 | 
					  if (GoogleChrome.isImageTypeSupported(contentType)) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -191,12 +191,12 @@ export function parseAndWriteAvatar(
 | 
				
			||||||
    contact: EmbeddedContactType,
 | 
					    contact: EmbeddedContactType,
 | 
				
			||||||
    context: {
 | 
					    context: {
 | 
				
			||||||
      message: MessageAttributesType;
 | 
					      message: MessageAttributesType;
 | 
				
			||||||
      regionCode: string;
 | 
					      getRegionCode: () => string | undefined;
 | 
				
			||||||
      logger: Pick<LoggerType, 'error'>;
 | 
					      logger: Pick<LoggerType, 'error'>;
 | 
				
			||||||
      writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
 | 
					      writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ): Promise<EmbeddedContactType> => {
 | 
					  ): Promise<EmbeddedContactType> => {
 | 
				
			||||||
    const { message, regionCode, logger } = context;
 | 
					    const { message, getRegionCode, logger } = context;
 | 
				
			||||||
    const { avatar } = contact;
 | 
					    const { avatar } = contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const contactWithUpdatedAvatar =
 | 
					    const contactWithUpdatedAvatar =
 | 
				
			||||||
| 
						 | 
					@ -212,7 +212,7 @@ export function parseAndWriteAvatar(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // eliminates empty numbers, emails, and addresses; adds type if not provided
 | 
					    // eliminates empty numbers, emails, and addresses; adds type if not provided
 | 
				
			||||||
    const parsedContact = parseContact(contactWithUpdatedAvatar, {
 | 
					    const parsedContact = parseContact(contactWithUpdatedAvatar, {
 | 
				
			||||||
      regionCode,
 | 
					      regionCode: getRegionCode(),
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const error = _validate(parsedContact, {
 | 
					    const error = _validate(parsedContact, {
 | 
				
			||||||
| 
						 | 
					@ -231,7 +231,7 @@ export function parseAndWriteAvatar(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseContact(
 | 
					function parseContact(
 | 
				
			||||||
  contact: EmbeddedContactType,
 | 
					  contact: EmbeddedContactType,
 | 
				
			||||||
  { regionCode }: { regionCode: string }
 | 
					  { regionCode }: { regionCode: string | undefined }
 | 
				
			||||||
): EmbeddedContactType {
 | 
					): EmbeddedContactType {
 | 
				
			||||||
  const boundParsePhone = (phoneNumber: Phone): Phone | undefined =>
 | 
					  const boundParsePhone = (phoneNumber: Phone): Phone | undefined =>
 | 
				
			||||||
    parsePhoneItem(phoneNumber, { regionCode });
 | 
					    parsePhoneItem(phoneNumber, { regionCode });
 | 
				
			||||||
| 
						 | 
					@ -294,7 +294,7 @@ export function _validate(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parsePhoneItem(
 | 
					function parsePhoneItem(
 | 
				
			||||||
  item: Phone,
 | 
					  item: Phone,
 | 
				
			||||||
  { regionCode }: { regionCode: string }
 | 
					  { regionCode }: { regionCode: string | undefined }
 | 
				
			||||||
): Phone | undefined {
 | 
					): Phone | undefined {
 | 
				
			||||||
  if (!item.value) {
 | 
					  if (!item.value) {
 | 
				
			||||||
    return undefined;
 | 
					    return undefined;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,13 +75,13 @@ export type ProfileChangeNotificationMessage = Readonly<
 | 
				
			||||||
    ExpirationTimerUpdate
 | 
					    ExpirationTimerUpdate
 | 
				
			||||||
>;
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SharedMessageProperties = Readonly<{
 | 
					export type SharedMessageProperties = Readonly<{
 | 
				
			||||||
  conversationId: string;
 | 
					  conversationId: string;
 | 
				
			||||||
  sent_at: number;
 | 
					  sent_at: number;
 | 
				
			||||||
  timestamp: number;
 | 
					  timestamp: number;
 | 
				
			||||||
}>;
 | 
					}>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ExpirationTimerUpdate = Partial<
 | 
					export type ExpirationTimerUpdate = Partial<
 | 
				
			||||||
  Readonly<{
 | 
					  Readonly<{
 | 
				
			||||||
    expirationTimerUpdate: Readonly<{
 | 
					    expirationTimerUpdate: Readonly<{
 | 
				
			||||||
      expireTimer: number;
 | 
					      expireTimer: number;
 | 
				
			||||||
| 
						 | 
					@ -91,7 +91,7 @@ type ExpirationTimerUpdate = Partial<
 | 
				
			||||||
  }>
 | 
					  }>
 | 
				
			||||||
>;
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MessageSchemaVersion5 = Partial<
 | 
					export type MessageSchemaVersion5 = Partial<
 | 
				
			||||||
  Readonly<{
 | 
					  Readonly<{
 | 
				
			||||||
    hasAttachments: IndexableBoolean;
 | 
					    hasAttachments: IndexableBoolean;
 | 
				
			||||||
    hasVisualMediaAttachments: IndexablePresence;
 | 
					    hasVisualMediaAttachments: IndexablePresence;
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ type MessageSchemaVersion5 = Partial<
 | 
				
			||||||
  }>
 | 
					  }>
 | 
				
			||||||
>;
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MessageSchemaVersion6 = Partial<
 | 
					export type MessageSchemaVersion6 = Partial<
 | 
				
			||||||
  Readonly<{
 | 
					  Readonly<{
 | 
				
			||||||
    contact: Array<EmbeddedContactType>;
 | 
					    contact: Array<EmbeddedContactType>;
 | 
				
			||||||
  }>
 | 
					  }>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,80 @@
 | 
				
			||||||
// Copyright 2018-2021 Signal Messenger, LLC
 | 
					// Copyright 2018-2021 Signal Messenger, LLC
 | 
				
			||||||
// SPDX-License-Identifier: AGPL-3.0-only
 | 
					// SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { isFunction, isObject, isString, omit } = require('lodash');
 | 
					import { isFunction, isObject, isString, omit } from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Contact = require('../../../ts/types/EmbeddedContact');
 | 
					import * as Contact from './EmbeddedContact';
 | 
				
			||||||
const Attachment = require('../../../ts/types/Attachment');
 | 
					import type { AttachmentType } from './Attachment';
 | 
				
			||||||
const Errors = require('../../../ts/types/errors');
 | 
					import {
 | 
				
			||||||
const SchemaVersion = require('../../../ts/types/SchemaVersion');
 | 
					  autoOrientJPEG,
 | 
				
			||||||
const {
 | 
					  captureDimensionsAndScreenshot,
 | 
				
			||||||
  initializeAttachmentMetadata,
 | 
					  hasData,
 | 
				
			||||||
} = require('../../../ts/types/message/initializeAttachmentMetadata');
 | 
					  migrateDataToFileSystem,
 | 
				
			||||||
const MessageTS = require('../../../ts/types/Message');
 | 
					  removeSchemaVersion,
 | 
				
			||||||
 | 
					  replaceUnicodeOrderOverrides,
 | 
				
			||||||
 | 
					  replaceUnicodeV2,
 | 
				
			||||||
 | 
					} from './Attachment';
 | 
				
			||||||
 | 
					import * as Errors from './errors';
 | 
				
			||||||
 | 
					import * as SchemaVersion from './SchemaVersion';
 | 
				
			||||||
 | 
					import { initializeAttachmentMetadata } from './message/initializeAttachmentMetadata';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const GROUP = 'group';
 | 
					import type * as MIME from './MIME';
 | 
				
			||||||
const PRIVATE = 'private';
 | 
					import type { LoggerType } from './Logging';
 | 
				
			||||||
 | 
					import type { EmbeddedContactType } from './EmbeddedContact';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type {
 | 
				
			||||||
 | 
					  MessageAttributesType,
 | 
				
			||||||
 | 
					  PreviewMessageType,
 | 
				
			||||||
 | 
					  PreviewType,
 | 
				
			||||||
 | 
					  QuotedMessageType,
 | 
				
			||||||
 | 
					  StickerMessageType,
 | 
				
			||||||
 | 
					} from '../model-types.d';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { hasExpiration } from './Message';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const GROUP = 'group';
 | 
				
			||||||
 | 
					export const PRIVATE = 'private';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ContextType = {
 | 
				
			||||||
 | 
					  getAbsoluteAttachmentPath: (path: string) => string;
 | 
				
			||||||
 | 
					  getAbsoluteStickerPath: (path: string) => string;
 | 
				
			||||||
 | 
					  getImageDimensions: (params: {
 | 
				
			||||||
 | 
					    objectUrl: string;
 | 
				
			||||||
 | 
					    logger: LoggerType;
 | 
				
			||||||
 | 
					  }) => Promise<{
 | 
				
			||||||
 | 
					    width: number;
 | 
				
			||||||
 | 
					    height: number;
 | 
				
			||||||
 | 
					  }>;
 | 
				
			||||||
 | 
					  getRegionCode: () => string;
 | 
				
			||||||
 | 
					  logger: LoggerType;
 | 
				
			||||||
 | 
					  makeImageThumbnail: (params: {
 | 
				
			||||||
 | 
					    size: number;
 | 
				
			||||||
 | 
					    objectUrl: string;
 | 
				
			||||||
 | 
					    contentType: MIME.MIMEType;
 | 
				
			||||||
 | 
					    logger: LoggerType;
 | 
				
			||||||
 | 
					  }) => Promise<Blob>;
 | 
				
			||||||
 | 
					  makeObjectUrl: (
 | 
				
			||||||
 | 
					    data: Uint8Array | ArrayBuffer,
 | 
				
			||||||
 | 
					    contentType: MIME.MIMEType
 | 
				
			||||||
 | 
					  ) => string;
 | 
				
			||||||
 | 
					  makeVideoScreenshot: (params: {
 | 
				
			||||||
 | 
					    objectUrl: string;
 | 
				
			||||||
 | 
					    contentType: MIME.MIMEType;
 | 
				
			||||||
 | 
					    logger: LoggerType;
 | 
				
			||||||
 | 
					  }) => Promise<Blob>;
 | 
				
			||||||
 | 
					  maxVersion?: number;
 | 
				
			||||||
 | 
					  revokeObjectUrl: (objectUrl: string) => void;
 | 
				
			||||||
 | 
					  writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
 | 
				
			||||||
 | 
					  writeNewStickerData: (sticker: StickerMessageType) => Promise<string>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WriteExistingAttachmentDataType = (
 | 
				
			||||||
 | 
					  attachment: Pick<AttachmentType, 'data' | 'path'>
 | 
				
			||||||
 | 
					) => Promise<void>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ContextWithMessageType = ContextType & {
 | 
				
			||||||
 | 
					  message: MessageAttributesType;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Schema version history
 | 
					// Schema version history
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
| 
						 | 
					@ -55,32 +116,30 @@ const PRIVATE = 'private';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const INITIAL_SCHEMA_VERSION = 0;
 | 
					const INITIAL_SCHEMA_VERSION = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Public API
 | 
					 | 
				
			||||||
exports.GROUP = GROUP;
 | 
					 | 
				
			||||||
exports.PRIVATE = PRIVATE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Placeholder until we have stronger preconditions:
 | 
					// Placeholder until we have stronger preconditions:
 | 
				
			||||||
exports.isValid = () => true;
 | 
					export const isValid = (_message: MessageAttributesType): boolean => true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Schema
 | 
					// Schema
 | 
				
			||||||
exports.initializeSchemaVersion = ({ message, logger }) => {
 | 
					export const initializeSchemaVersion = ({
 | 
				
			||||||
 | 
					  message,
 | 
				
			||||||
 | 
					  logger,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  message: MessageAttributesType;
 | 
				
			||||||
 | 
					  logger: LoggerType;
 | 
				
			||||||
 | 
					}): MessageAttributesType => {
 | 
				
			||||||
  const isInitialized =
 | 
					  const isInitialized =
 | 
				
			||||||
    SchemaVersion.isValid(message.schemaVersion) && message.schemaVersion >= 1;
 | 
					    SchemaVersion.isValid(message.schemaVersion) && message.schemaVersion >= 1;
 | 
				
			||||||
  if (isInitialized) {
 | 
					  if (isInitialized) {
 | 
				
			||||||
    return message;
 | 
					    return message;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const numAttachments = Array.isArray(message.attachments)
 | 
					  const firstAttachment = message?.attachments?.[0];
 | 
				
			||||||
    ? message.attachments.length
 | 
					  if (!firstAttachment) {
 | 
				
			||||||
    : 0;
 | 
					 | 
				
			||||||
  const hasAttachments = numAttachments > 0;
 | 
					 | 
				
			||||||
  if (!hasAttachments) {
 | 
					 | 
				
			||||||
    return { ...message, schemaVersion: INITIAL_SCHEMA_VERSION };
 | 
					    return { ...message, schemaVersion: INITIAL_SCHEMA_VERSION };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // All attachments should have the same schema version, so we just pick
 | 
					  // All attachments should have the same schema version, so we just pick
 | 
				
			||||||
  // the first one:
 | 
					  // the first one:
 | 
				
			||||||
  const firstAttachment = message.attachments[0];
 | 
					 | 
				
			||||||
  const inheritedSchemaVersion = SchemaVersion.isValid(
 | 
					  const inheritedSchemaVersion = SchemaVersion.isValid(
 | 
				
			||||||
    firstAttachment.schemaVersion
 | 
					    firstAttachment.schemaVersion
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					@ -89,9 +148,10 @@ exports.initializeSchemaVersion = ({ message, logger }) => {
 | 
				
			||||||
  const messageWithInitialSchema = {
 | 
					  const messageWithInitialSchema = {
 | 
				
			||||||
    ...message,
 | 
					    ...message,
 | 
				
			||||||
    schemaVersion: inheritedSchemaVersion,
 | 
					    schemaVersion: inheritedSchemaVersion,
 | 
				
			||||||
    attachments: message.attachments.map(attachment =>
 | 
					    attachments:
 | 
				
			||||||
      Attachment.removeSchemaVersion({ attachment, logger })
 | 
					      message?.attachments?.map(attachment =>
 | 
				
			||||||
    ),
 | 
					        removeSchemaVersion({ attachment, logger })
 | 
				
			||||||
 | 
					      ) || [],
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return messageWithInitialSchema;
 | 
					  return messageWithInitialSchema;
 | 
				
			||||||
| 
						 | 
					@ -101,7 +161,19 @@ exports.initializeSchemaVersion = ({ message, logger }) => {
 | 
				
			||||||
// type UpgradeStep = (Message, Context) -> Promise Message
 | 
					// type UpgradeStep = (Message, Context) -> Promise Message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SchemaVersion -> UpgradeStep -> UpgradeStep
 | 
					// SchemaVersion -> UpgradeStep -> UpgradeStep
 | 
				
			||||||
exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
 | 
					export const _withSchemaVersion = ({
 | 
				
			||||||
 | 
					  schemaVersion,
 | 
				
			||||||
 | 
					  upgrade,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  schemaVersion: number;
 | 
				
			||||||
 | 
					  upgrade: (
 | 
				
			||||||
 | 
					    message: MessageAttributesType,
 | 
				
			||||||
 | 
					    context: ContextType
 | 
				
			||||||
 | 
					  ) => Promise<MessageAttributesType>;
 | 
				
			||||||
 | 
					}): ((
 | 
				
			||||||
 | 
					  message: MessageAttributesType,
 | 
				
			||||||
 | 
					  context: ContextType
 | 
				
			||||||
 | 
					) => Promise<MessageAttributesType>) => {
 | 
				
			||||||
  if (!SchemaVersion.isValid(schemaVersion)) {
 | 
					  if (!SchemaVersion.isValid(schemaVersion)) {
 | 
				
			||||||
    throw new TypeError('_withSchemaVersion: schemaVersion is invalid');
 | 
					    throw new TypeError('_withSchemaVersion: schemaVersion is invalid');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -109,7 +181,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
 | 
				
			||||||
    throw new TypeError('_withSchemaVersion: upgrade must be a function');
 | 
					    throw new TypeError('_withSchemaVersion: upgrade must be a function');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async (message, context) => {
 | 
					  return async (message: MessageAttributesType, context: ContextType) => {
 | 
				
			||||||
    if (!context || !isObject(context.logger)) {
 | 
					    if (!context || !isObject(context.logger)) {
 | 
				
			||||||
      throw new TypeError(
 | 
					      throw new TypeError(
 | 
				
			||||||
        '_withSchemaVersion: context must have logger object'
 | 
					        '_withSchemaVersion: context must have logger object'
 | 
				
			||||||
| 
						 | 
					@ -117,7 +189,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const { logger } = context;
 | 
					    const { logger } = context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!exports.isValid(message)) {
 | 
					    if (!isValid(message)) {
 | 
				
			||||||
      logger.error(
 | 
					      logger.error(
 | 
				
			||||||
        'Message._withSchemaVersion: Invalid input message:',
 | 
					        'Message._withSchemaVersion: Invalid input message:',
 | 
				
			||||||
        message
 | 
					        message
 | 
				
			||||||
| 
						 | 
					@ -125,7 +197,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
 | 
				
			||||||
      return message;
 | 
					      return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const isAlreadyUpgraded = message.schemaVersion >= schemaVersion;
 | 
					    const isAlreadyUpgraded = (message.schemaVersion || 0) >= schemaVersion;
 | 
				
			||||||
    if (isAlreadyUpgraded) {
 | 
					    if (isAlreadyUpgraded) {
 | 
				
			||||||
      return message;
 | 
					      return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -152,7 +224,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
 | 
				
			||||||
      return message;
 | 
					      return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!exports.isValid(upgradedMessage)) {
 | 
					    if (!isValid(upgradedMessage)) {
 | 
				
			||||||
      logger.error(
 | 
					      logger.error(
 | 
				
			||||||
        'Message._withSchemaVersion: Invalid upgraded message:',
 | 
					        'Message._withSchemaVersion: Invalid upgraded message:',
 | 
				
			||||||
        upgradedMessage
 | 
					        upgradedMessage
 | 
				
			||||||
| 
						 | 
					@ -168,34 +240,59 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
 | 
				
			||||||
//      _mapAttachments :: (Attachment -> Promise Attachment) ->
 | 
					//      _mapAttachments :: (Attachment -> Promise Attachment) ->
 | 
				
			||||||
//                         (Message, Context) ->
 | 
					//                         (Message, Context) ->
 | 
				
			||||||
//                         Promise Message
 | 
					//                         Promise Message
 | 
				
			||||||
exports._mapAttachments = upgradeAttachment => async (message, context) => {
 | 
					export type UpgradeAttachmentType = (
 | 
				
			||||||
  const upgradeWithContext = attachment =>
 | 
					  attachment: AttachmentType,
 | 
				
			||||||
    upgradeAttachment(attachment, context, message);
 | 
					  context: ContextType,
 | 
				
			||||||
  const attachments = await Promise.all(
 | 
					  message: MessageAttributesType
 | 
				
			||||||
    (message.attachments || []).map(upgradeWithContext)
 | 
					) => Promise<AttachmentType>;
 | 
				
			||||||
  );
 | 
					
 | 
				
			||||||
  return { ...message, attachments };
 | 
					export const _mapAttachments =
 | 
				
			||||||
};
 | 
					  (upgradeAttachment: UpgradeAttachmentType) =>
 | 
				
			||||||
 | 
					  async (
 | 
				
			||||||
 | 
					    message: MessageAttributesType,
 | 
				
			||||||
 | 
					    context: ContextType
 | 
				
			||||||
 | 
					  ): Promise<MessageAttributesType> => {
 | 
				
			||||||
 | 
					    const upgradeWithContext = (attachment: AttachmentType) =>
 | 
				
			||||||
 | 
					      upgradeAttachment(attachment, context, message);
 | 
				
			||||||
 | 
					    const attachments = await Promise.all(
 | 
				
			||||||
 | 
					      (message.attachments || []).map(upgradeWithContext)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    return { ...message, attachments };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Public API
 | 
					// Public API
 | 
				
			||||||
//      _mapContact :: (Contact -> Promise Contact) ->
 | 
					//      _mapContact :: (Contact -> Promise Contact) ->
 | 
				
			||||||
//                     (Message, Context) ->
 | 
					//                     (Message, Context) ->
 | 
				
			||||||
//                     Promise Message
 | 
					//                     Promise Message
 | 
				
			||||||
exports._mapContact = upgradeContact => async (message, context) => {
 | 
					
 | 
				
			||||||
  const contextWithMessage = { ...context, message };
 | 
					export type UpgradeContactType = (
 | 
				
			||||||
  const upgradeWithContext = contact =>
 | 
					  contact: EmbeddedContactType,
 | 
				
			||||||
    upgradeContact(contact, contextWithMessage);
 | 
					  contextWithMessage: ContextWithMessageType
 | 
				
			||||||
  const contact = await Promise.all(
 | 
					) => Promise<EmbeddedContactType>;
 | 
				
			||||||
    (message.contact || []).map(upgradeWithContext)
 | 
					export const _mapContact =
 | 
				
			||||||
  );
 | 
					  (upgradeContact: UpgradeContactType) =>
 | 
				
			||||||
  return { ...message, contact };
 | 
					  async (
 | 
				
			||||||
};
 | 
					    message: MessageAttributesType,
 | 
				
			||||||
 | 
					    context: ContextType
 | 
				
			||||||
 | 
					  ): Promise<MessageAttributesType> => {
 | 
				
			||||||
 | 
					    const contextWithMessage = { ...context, message };
 | 
				
			||||||
 | 
					    const upgradeWithContext = (contact: EmbeddedContactType) =>
 | 
				
			||||||
 | 
					      upgradeContact(contact, contextWithMessage);
 | 
				
			||||||
 | 
					    const contact = await Promise.all(
 | 
				
			||||||
 | 
					      (message.contact || []).map(upgradeWithContext)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    return { ...message, contact };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//      _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
 | 
					//      _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
 | 
				
			||||||
//                               (Message, Context) ->
 | 
					//                               (Message, Context) ->
 | 
				
			||||||
//                               Promise Message
 | 
					//                               Promise Message
 | 
				
			||||||
exports._mapQuotedAttachments =
 | 
					export const _mapQuotedAttachments =
 | 
				
			||||||
  upgradeAttachment => async (message, context) => {
 | 
					  (upgradeAttachment: UpgradeAttachmentType) =>
 | 
				
			||||||
 | 
					  async (
 | 
				
			||||||
 | 
					    message: MessageAttributesType,
 | 
				
			||||||
 | 
					    context: ContextType
 | 
				
			||||||
 | 
					  ): Promise<MessageAttributesType> => {
 | 
				
			||||||
    if (!message.quote) {
 | 
					    if (!message.quote) {
 | 
				
			||||||
      return message;
 | 
					      return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -203,13 +300,19 @@ exports._mapQuotedAttachments =
 | 
				
			||||||
      throw new Error('_mapQuotedAttachments: context must have logger object');
 | 
					      throw new Error('_mapQuotedAttachments: context must have logger object');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const upgradeWithContext = async attachment => {
 | 
					    const upgradeWithContext = async (
 | 
				
			||||||
 | 
					      attachment: AttachmentType
 | 
				
			||||||
 | 
					    ): Promise<AttachmentType> => {
 | 
				
			||||||
      const { thumbnail } = attachment;
 | 
					      const { thumbnail } = attachment;
 | 
				
			||||||
      if (!thumbnail) {
 | 
					      if (!thumbnail) {
 | 
				
			||||||
        return attachment;
 | 
					        return attachment;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const upgradedThumbnail = await upgradeAttachment(thumbnail, context);
 | 
					      const upgradedThumbnail = await upgradeAttachment(
 | 
				
			||||||
 | 
					        thumbnail as AttachmentType,
 | 
				
			||||||
 | 
					        context,
 | 
				
			||||||
 | 
					        message
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      return { ...attachment, thumbnail: upgradedThumbnail };
 | 
					      return { ...attachment, thumbnail: upgradedThumbnail };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,8 +328,12 @@ exports._mapQuotedAttachments =
 | 
				
			||||||
//      _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) ->
 | 
					//      _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) ->
 | 
				
			||||||
//                               (Message, Context) ->
 | 
					//                               (Message, Context) ->
 | 
				
			||||||
//                               Promise Message
 | 
					//                               Promise Message
 | 
				
			||||||
exports._mapPreviewAttachments =
 | 
					export const _mapPreviewAttachments =
 | 
				
			||||||
  upgradeAttachment => async (message, context) => {
 | 
					  (upgradeAttachment: UpgradeAttachmentType) =>
 | 
				
			||||||
 | 
					  async (
 | 
				
			||||||
 | 
					    message: MessageAttributesType,
 | 
				
			||||||
 | 
					    context: ContextType
 | 
				
			||||||
 | 
					  ): Promise<MessageAttributesType> => {
 | 
				
			||||||
    if (!message.preview) {
 | 
					    if (!message.preview) {
 | 
				
			||||||
      return message;
 | 
					      return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -236,13 +343,13 @@ exports._mapPreviewAttachments =
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const upgradeWithContext = async preview => {
 | 
					    const upgradeWithContext = async (preview: PreviewType) => {
 | 
				
			||||||
      const { image } = preview;
 | 
					      const { image } = preview;
 | 
				
			||||||
      if (!image) {
 | 
					      if (!image) {
 | 
				
			||||||
        return preview;
 | 
					        return preview;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const upgradedImage = await upgradeAttachment(image, context);
 | 
					      const upgradedImage = await upgradeAttachment(image, context, message);
 | 
				
			||||||
      return { ...preview, image: upgradedImage };
 | 
					      return { ...preview, image: upgradedImage };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -252,58 +359,59 @@ exports._mapPreviewAttachments =
 | 
				
			||||||
    return { ...message, preview };
 | 
					    return { ...message, preview };
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toVersion0 = async (message, context) =>
 | 
					const toVersion0 = async (
 | 
				
			||||||
  exports.initializeSchemaVersion({ message, logger: context.logger });
 | 
					  message: MessageAttributesType,
 | 
				
			||||||
const toVersion1 = exports._withSchemaVersion({
 | 
					  context: ContextType
 | 
				
			||||||
 | 
					) => initializeSchemaVersion({ message, logger: context.logger });
 | 
				
			||||||
 | 
					const toVersion1 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 1,
 | 
					  schemaVersion: 1,
 | 
				
			||||||
  upgrade: exports._mapAttachments(Attachment.autoOrientJPEG),
 | 
					  upgrade: _mapAttachments(autoOrientJPEG),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const toVersion2 = exports._withSchemaVersion({
 | 
					const toVersion2 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 2,
 | 
					  schemaVersion: 2,
 | 
				
			||||||
  upgrade: exports._mapAttachments(Attachment.replaceUnicodeOrderOverrides),
 | 
					  upgrade: _mapAttachments(replaceUnicodeOrderOverrides),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const toVersion3 = exports._withSchemaVersion({
 | 
					const toVersion3 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 3,
 | 
					  schemaVersion: 3,
 | 
				
			||||||
  upgrade: exports._mapAttachments(Attachment.migrateDataToFileSystem),
 | 
					  upgrade: _mapAttachments(migrateDataToFileSystem),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const toVersion4 = exports._withSchemaVersion({
 | 
					const toVersion4 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 4,
 | 
					  schemaVersion: 4,
 | 
				
			||||||
  upgrade: exports._mapQuotedAttachments(Attachment.migrateDataToFileSystem),
 | 
					  upgrade: _mapQuotedAttachments(migrateDataToFileSystem),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const toVersion5 = exports._withSchemaVersion({
 | 
					const toVersion5 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 5,
 | 
					  schemaVersion: 5,
 | 
				
			||||||
  upgrade: initializeAttachmentMetadata,
 | 
					  upgrade: initializeAttachmentMetadata,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const toVersion6 = exports._withSchemaVersion({
 | 
					const toVersion6 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 6,
 | 
					  schemaVersion: 6,
 | 
				
			||||||
  upgrade: exports._mapContact(
 | 
					  upgrade: _mapContact(Contact.parseAndWriteAvatar(migrateDataToFileSystem)),
 | 
				
			||||||
    Contact.parseAndWriteAvatar(Attachment.migrateDataToFileSystem)
 | 
					 | 
				
			||||||
  ),
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
// IMPORTANT: We’ve updated our definition of `initializeAttachmentMetadata`, so
 | 
					// IMPORTANT: We’ve updated our definition of `initializeAttachmentMetadata`, so
 | 
				
			||||||
// we need to run it again on existing items that have previously been incorrectly
 | 
					// we need to run it again on existing items that have previously been incorrectly
 | 
				
			||||||
// classified:
 | 
					// classified:
 | 
				
			||||||
const toVersion7 = exports._withSchemaVersion({
 | 
					const toVersion7 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 7,
 | 
					  schemaVersion: 7,
 | 
				
			||||||
  upgrade: initializeAttachmentMetadata,
 | 
					  upgrade: initializeAttachmentMetadata,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toVersion8 = exports._withSchemaVersion({
 | 
					const toVersion8 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 8,
 | 
					  schemaVersion: 8,
 | 
				
			||||||
  upgrade: exports._mapAttachments(Attachment.captureDimensionsAndScreenshot),
 | 
					  upgrade: _mapAttachments(captureDimensionsAndScreenshot),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toVersion9 = exports._withSchemaVersion({
 | 
					const toVersion9 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 9,
 | 
					  schemaVersion: 9,
 | 
				
			||||||
  upgrade: exports._mapAttachments(Attachment.replaceUnicodeV2),
 | 
					  upgrade: _mapAttachments(replaceUnicodeV2),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const toVersion10 = exports._withSchemaVersion({
 | 
					const toVersion10 = _withSchemaVersion({
 | 
				
			||||||
  schemaVersion: 10,
 | 
					  schemaVersion: 10,
 | 
				
			||||||
  upgrade: async (message, context) => {
 | 
					  upgrade: async (message, context) => {
 | 
				
			||||||
    const processPreviews = exports._mapPreviewAttachments(
 | 
					    const processPreviews = _mapPreviewAttachments(migrateDataToFileSystem);
 | 
				
			||||||
      Attachment.migrateDataToFileSystem
 | 
					    const processSticker = async (
 | 
				
			||||||
    );
 | 
					      stickerMessage: MessageAttributesType,
 | 
				
			||||||
    const processSticker = async (stickerMessage, stickerContext) => {
 | 
					      stickerContext: ContextType
 | 
				
			||||||
 | 
					    ): Promise<MessageAttributesType> => {
 | 
				
			||||||
      const { sticker } = stickerMessage;
 | 
					      const { sticker } = stickerMessage;
 | 
				
			||||||
      if (!sticker || !sticker.data || !sticker.data.data) {
 | 
					      if (!sticker || !sticker.data || !sticker.data.data) {
 | 
				
			||||||
        return stickerMessage;
 | 
					        return stickerMessage;
 | 
				
			||||||
| 
						 | 
					@ -313,10 +421,7 @@ const toVersion10 = exports._withSchemaVersion({
 | 
				
			||||||
        ...stickerMessage,
 | 
					        ...stickerMessage,
 | 
				
			||||||
        sticker: {
 | 
					        sticker: {
 | 
				
			||||||
          ...sticker,
 | 
					          ...sticker,
 | 
				
			||||||
          data: await Attachment.migrateDataToFileSystem(
 | 
					          data: await migrateDataToFileSystem(sticker.data, stickerContext),
 | 
				
			||||||
            sticker.data,
 | 
					 | 
				
			||||||
            stickerContext
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					@ -341,27 +446,29 @@ const VERSIONS = [
 | 
				
			||||||
  toVersion9,
 | 
					  toVersion9,
 | 
				
			||||||
  toVersion10,
 | 
					  toVersion10,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
exports.CURRENT_SCHEMA_VERSION = VERSIONS.length - 1;
 | 
					export const CURRENT_SCHEMA_VERSION = VERSIONS.length - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// We need dimensions and screenshots for images for proper display
 | 
					// We need dimensions and screenshots for images for proper display
 | 
				
			||||||
exports.VERSION_NEEDED_FOR_DISPLAY = 9;
 | 
					export const VERSION_NEEDED_FOR_DISPLAY = 9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpgradeStep
 | 
					// UpgradeStep
 | 
				
			||||||
exports.upgradeSchema = async (
 | 
					export const upgradeSchema = async (
 | 
				
			||||||
  rawMessage,
 | 
					  rawMessage: MessageAttributesType,
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    writeNewAttachmentData,
 | 
					    writeNewAttachmentData,
 | 
				
			||||||
    getRegionCode,
 | 
					    getRegionCode,
 | 
				
			||||||
    getAbsoluteAttachmentPath,
 | 
					    getAbsoluteAttachmentPath,
 | 
				
			||||||
 | 
					    getAbsoluteStickerPath,
 | 
				
			||||||
    makeObjectUrl,
 | 
					    makeObjectUrl,
 | 
				
			||||||
    revokeObjectUrl,
 | 
					    revokeObjectUrl,
 | 
				
			||||||
    getImageDimensions,
 | 
					    getImageDimensions,
 | 
				
			||||||
    makeImageThumbnail,
 | 
					    makeImageThumbnail,
 | 
				
			||||||
    makeVideoScreenshot,
 | 
					    makeVideoScreenshot,
 | 
				
			||||||
 | 
					    writeNewStickerData,
 | 
				
			||||||
    logger,
 | 
					    logger,
 | 
				
			||||||
    maxVersion = exports.CURRENT_SCHEMA_VERSION,
 | 
					    maxVersion = CURRENT_SCHEMA_VERSION,
 | 
				
			||||||
  } = {}
 | 
					  }: ContextType
 | 
				
			||||||
) => {
 | 
					): Promise<MessageAttributesType> => {
 | 
				
			||||||
  if (!isFunction(writeNewAttachmentData)) {
 | 
					  if (!isFunction(writeNewAttachmentData)) {
 | 
				
			||||||
    throw new TypeError('context.writeNewAttachmentData is required');
 | 
					    throw new TypeError('context.writeNewAttachmentData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -389,6 +496,12 @@ exports.upgradeSchema = async (
 | 
				
			||||||
  if (!isObject(logger)) {
 | 
					  if (!isObject(logger)) {
 | 
				
			||||||
    throw new TypeError('context.logger is required');
 | 
					    throw new TypeError('context.logger is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (!isFunction(getAbsoluteStickerPath)) {
 | 
				
			||||||
 | 
					    throw new TypeError('context.getAbsoluteStickerPath is required');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!isFunction(writeNewStickerData)) {
 | 
				
			||||||
 | 
					    throw new TypeError('context.writeNewStickerData is required');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let message = rawMessage;
 | 
					  let message = rawMessage;
 | 
				
			||||||
  for (let index = 0, max = VERSIONS.length; index < max; index += 1) {
 | 
					  for (let index = 0, max = VERSIONS.length; index < max; index += 1) {
 | 
				
			||||||
| 
						 | 
					@ -402,7 +515,6 @@ exports.upgradeSchema = async (
 | 
				
			||||||
    // eslint-disable-next-line no-await-in-loop
 | 
					    // eslint-disable-next-line no-await-in-loop
 | 
				
			||||||
    message = await currentVersion(message, {
 | 
					    message = await currentVersion(message, {
 | 
				
			||||||
      writeNewAttachmentData,
 | 
					      writeNewAttachmentData,
 | 
				
			||||||
      regionCode: getRegionCode(),
 | 
					 | 
				
			||||||
      getAbsoluteAttachmentPath,
 | 
					      getAbsoluteAttachmentPath,
 | 
				
			||||||
      makeObjectUrl,
 | 
					      makeObjectUrl,
 | 
				
			||||||
      revokeObjectUrl,
 | 
					      revokeObjectUrl,
 | 
				
			||||||
| 
						 | 
					@ -410,6 +522,9 @@ exports.upgradeSchema = async (
 | 
				
			||||||
      makeImageThumbnail,
 | 
					      makeImageThumbnail,
 | 
				
			||||||
      makeVideoScreenshot,
 | 
					      makeVideoScreenshot,
 | 
				
			||||||
      logger,
 | 
					      logger,
 | 
				
			||||||
 | 
					      getAbsoluteStickerPath,
 | 
				
			||||||
 | 
					      getRegionCode,
 | 
				
			||||||
 | 
					      writeNewStickerData,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -418,8 +533,8 @@ exports.upgradeSchema = async (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Runs on attachments outside of the schema upgrade process, since attachments are
 | 
					// Runs on attachments outside of the schema upgrade process, since attachments are
 | 
				
			||||||
//   downloaded out of band.
 | 
					//   downloaded out of band.
 | 
				
			||||||
exports.processNewAttachment = async (
 | 
					export const processNewAttachment = async (
 | 
				
			||||||
  attachment,
 | 
					  attachment: AttachmentType,
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    writeNewAttachmentData,
 | 
					    writeNewAttachmentData,
 | 
				
			||||||
    getAbsoluteAttachmentPath,
 | 
					    getAbsoluteAttachmentPath,
 | 
				
			||||||
| 
						 | 
					@ -429,8 +544,8 @@ exports.processNewAttachment = async (
 | 
				
			||||||
    makeImageThumbnail,
 | 
					    makeImageThumbnail,
 | 
				
			||||||
    makeVideoScreenshot,
 | 
					    makeVideoScreenshot,
 | 
				
			||||||
    logger,
 | 
					    logger,
 | 
				
			||||||
  } = {}
 | 
					  }: ContextType
 | 
				
			||||||
) => {
 | 
					): Promise<AttachmentType> => {
 | 
				
			||||||
  if (!isFunction(writeNewAttachmentData)) {
 | 
					  if (!isFunction(writeNewAttachmentData)) {
 | 
				
			||||||
    throw new TypeError('context.writeNewAttachmentData is required');
 | 
					    throw new TypeError('context.writeNewAttachmentData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -456,16 +571,13 @@ exports.processNewAttachment = async (
 | 
				
			||||||
    throw new TypeError('context.logger is required');
 | 
					    throw new TypeError('context.logger is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const rotatedAttachment = await Attachment.autoOrientJPEG(
 | 
					  const rotatedAttachment = await autoOrientJPEG(attachment, undefined, {
 | 
				
			||||||
    attachment,
 | 
					    isIncoming: true,
 | 
				
			||||||
    undefined,
 | 
					  });
 | 
				
			||||||
    { isIncoming: true }
 | 
					  const onDiskAttachment = await migrateDataToFileSystem(rotatedAttachment, {
 | 
				
			||||||
  );
 | 
					    writeNewAttachmentData,
 | 
				
			||||||
  const onDiskAttachment = await Attachment.migrateDataToFileSystem(
 | 
					  });
 | 
				
			||||||
    rotatedAttachment,
 | 
					  const finalAttachment = await captureDimensionsAndScreenshot(
 | 
				
			||||||
    { writeNewAttachmentData }
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  const finalAttachment = await Attachment.captureDimensionsAndScreenshot(
 | 
					 | 
				
			||||||
    onDiskAttachment,
 | 
					    onDiskAttachment,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      writeNewAttachmentData,
 | 
					      writeNewAttachmentData,
 | 
				
			||||||
| 
						 | 
					@ -482,15 +594,15 @@ exports.processNewAttachment = async (
 | 
				
			||||||
  return finalAttachment;
 | 
					  return finalAttachment;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.processNewSticker = async (
 | 
					export const processNewSticker = async (
 | 
				
			||||||
  stickerData,
 | 
					  stickerData: StickerMessageType,
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    writeNewStickerData,
 | 
					    writeNewStickerData,
 | 
				
			||||||
    getAbsoluteStickerPath,
 | 
					    getAbsoluteStickerPath,
 | 
				
			||||||
    getImageDimensions,
 | 
					    getImageDimensions,
 | 
				
			||||||
    logger,
 | 
					    logger,
 | 
				
			||||||
  } = {}
 | 
					  }: ContextType
 | 
				
			||||||
) => {
 | 
					): Promise<{ path: string; width: number; height: number }> => {
 | 
				
			||||||
  if (!isFunction(writeNewStickerData)) {
 | 
					  if (!isFunction(writeNewStickerData)) {
 | 
				
			||||||
    throw new TypeError('context.writeNewStickerData is required');
 | 
					    throw new TypeError('context.writeNewStickerData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -519,25 +631,41 @@ exports.processNewSticker = async (
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.createAttachmentLoader = loadAttachmentData => {
 | 
					type LoadAttachmentType = (
 | 
				
			||||||
 | 
					  attachment: AttachmentType
 | 
				
			||||||
 | 
					) => Promise<AttachmentType>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const createAttachmentLoader = (
 | 
				
			||||||
 | 
					  loadAttachmentData: LoadAttachmentType
 | 
				
			||||||
 | 
					): ((message: MessageAttributesType) => Promise<MessageAttributesType>) => {
 | 
				
			||||||
  if (!isFunction(loadAttachmentData)) {
 | 
					  if (!isFunction(loadAttachmentData)) {
 | 
				
			||||||
    throw new TypeError(
 | 
					    throw new TypeError(
 | 
				
			||||||
      'createAttachmentLoader: loadAttachmentData is required'
 | 
					      'createAttachmentLoader: loadAttachmentData is required'
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async message => ({
 | 
					  return async (
 | 
				
			||||||
 | 
					    message: MessageAttributesType
 | 
				
			||||||
 | 
					  ): Promise<MessageAttributesType> => ({
 | 
				
			||||||
    ...message,
 | 
					    ...message,
 | 
				
			||||||
    attachments: await Promise.all(message.attachments.map(loadAttachmentData)),
 | 
					    attachments: await Promise.all(
 | 
				
			||||||
 | 
					      (message.attachments || []).map(loadAttachmentData)
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.loadQuoteData = loadAttachmentData => {
 | 
					export const loadQuoteData = (
 | 
				
			||||||
 | 
					  loadAttachmentData: LoadAttachmentType
 | 
				
			||||||
 | 
					): ((
 | 
				
			||||||
 | 
					  quote: QuotedMessageType | undefined | null
 | 
				
			||||||
 | 
					) => Promise<QuotedMessageType | null>) => {
 | 
				
			||||||
  if (!isFunction(loadAttachmentData)) {
 | 
					  if (!isFunction(loadAttachmentData)) {
 | 
				
			||||||
    throw new TypeError('loadQuoteData: loadAttachmentData is required');
 | 
					    throw new TypeError('loadQuoteData: loadAttachmentData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async quote => {
 | 
					  return async (
 | 
				
			||||||
 | 
					    quote: QuotedMessageType | undefined | null
 | 
				
			||||||
 | 
					  ): Promise<QuotedMessageType | null> => {
 | 
				
			||||||
    if (!quote) {
 | 
					    if (!quote) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -562,48 +690,58 @@ exports.loadQuoteData = loadAttachmentData => {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.loadContactData = loadAttachmentData => {
 | 
					export const loadContactData = (
 | 
				
			||||||
 | 
					  loadAttachmentData: LoadAttachmentType
 | 
				
			||||||
 | 
					): ((
 | 
				
			||||||
 | 
					  contact: Array<EmbeddedContactType> | undefined
 | 
				
			||||||
 | 
					) => Promise<Array<EmbeddedContactType> | null>) => {
 | 
				
			||||||
  if (!isFunction(loadAttachmentData)) {
 | 
					  if (!isFunction(loadAttachmentData)) {
 | 
				
			||||||
    throw new TypeError('loadContactData: loadAttachmentData is required');
 | 
					    throw new TypeError('loadContactData: loadAttachmentData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async contact => {
 | 
					  return async (
 | 
				
			||||||
 | 
					    contact: Array<EmbeddedContactType> | undefined
 | 
				
			||||||
 | 
					  ): Promise<Array<EmbeddedContactType> | null> => {
 | 
				
			||||||
    if (!contact) {
 | 
					    if (!contact) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Promise.all(
 | 
					    return Promise.all(
 | 
				
			||||||
      contact.map(async item => {
 | 
					      contact.map(
 | 
				
			||||||
        if (
 | 
					        async (item: EmbeddedContactType): Promise<EmbeddedContactType> => {
 | 
				
			||||||
          !item ||
 | 
					          if (
 | 
				
			||||||
          !item.avatar ||
 | 
					            !item ||
 | 
				
			||||||
          !item.avatar.avatar ||
 | 
					            !item.avatar ||
 | 
				
			||||||
          !item.avatar.avatar.path
 | 
					            !item.avatar.avatar ||
 | 
				
			||||||
        ) {
 | 
					            !item.avatar.avatar.path
 | 
				
			||||||
          return item;
 | 
					          ) {
 | 
				
			||||||
        }
 | 
					            return item;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					          return {
 | 
				
			||||||
          ...item,
 | 
					            ...item,
 | 
				
			||||||
          avatar: {
 | 
					 | 
				
			||||||
            ...item.avatar,
 | 
					 | 
				
			||||||
            avatar: {
 | 
					            avatar: {
 | 
				
			||||||
              ...item.avatar.avatar,
 | 
					              ...item.avatar,
 | 
				
			||||||
              ...(await loadAttachmentData(item.avatar.avatar)),
 | 
					              avatar: {
 | 
				
			||||||
 | 
					                ...item.avatar.avatar,
 | 
				
			||||||
 | 
					                ...(await loadAttachmentData(item.avatar.avatar)),
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          };
 | 
				
			||||||
        };
 | 
					        }
 | 
				
			||||||
      })
 | 
					      )
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.loadPreviewData = loadAttachmentData => {
 | 
					export const loadPreviewData = (
 | 
				
			||||||
 | 
					  loadAttachmentData: LoadAttachmentType
 | 
				
			||||||
 | 
					): ((preview: PreviewMessageType) => Promise<PreviewMessageType>) => {
 | 
				
			||||||
  if (!isFunction(loadAttachmentData)) {
 | 
					  if (!isFunction(loadAttachmentData)) {
 | 
				
			||||||
    throw new TypeError('loadPreviewData: loadAttachmentData is required');
 | 
					    throw new TypeError('loadPreviewData: loadAttachmentData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async preview => {
 | 
					  return async (preview: PreviewMessageType) => {
 | 
				
			||||||
    if (!preview || !preview.length) {
 | 
					    if (!preview || !preview.length) {
 | 
				
			||||||
      return [];
 | 
					      return [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -623,12 +761,14 @@ exports.loadPreviewData = loadAttachmentData => {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.loadStickerData = loadAttachmentData => {
 | 
					export const loadStickerData = (
 | 
				
			||||||
 | 
					  loadAttachmentData: LoadAttachmentType
 | 
				
			||||||
 | 
					): ((sticker: StickerMessageType) => Promise<StickerMessageType | null>) => {
 | 
				
			||||||
  if (!isFunction(loadAttachmentData)) {
 | 
					  if (!isFunction(loadAttachmentData)) {
 | 
				
			||||||
    throw new TypeError('loadStickerData: loadAttachmentData is required');
 | 
					    throw new TypeError('loadStickerData: loadAttachmentData is required');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async sticker => {
 | 
					  return async (sticker: StickerMessageType) => {
 | 
				
			||||||
    if (!sticker || !sticker.data) {
 | 
					    if (!sticker || !sticker.data) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -640,7 +780,13 @@ exports.loadStickerData = loadAttachmentData => {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
 | 
					export const deleteAllExternalFiles = ({
 | 
				
			||||||
 | 
					  deleteAttachmentData,
 | 
				
			||||||
 | 
					  deleteOnDisk,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  deleteAttachmentData: (attachment: AttachmentType) => Promise<void>;
 | 
				
			||||||
 | 
					  deleteOnDisk: (path: string) => Promise<void>;
 | 
				
			||||||
 | 
					}): ((message: MessageAttributesType) => Promise<void>) => {
 | 
				
			||||||
  if (!isFunction(deleteAttachmentData)) {
 | 
					  if (!isFunction(deleteAttachmentData)) {
 | 
				
			||||||
    throw new TypeError(
 | 
					    throw new TypeError(
 | 
				
			||||||
      'deleteAllExternalFiles: deleteAttachmentData must be a function'
 | 
					      'deleteAllExternalFiles: deleteAttachmentData must be a function'
 | 
				
			||||||
| 
						 | 
					@ -653,7 +799,7 @@ exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async message => {
 | 
					  return async (message: MessageAttributesType) => {
 | 
				
			||||||
    const { attachments, quote, contact, preview, sticker } = message;
 | 
					    const { attachments, quote, contact, preview, sticker } = message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (attachments && attachments.length) {
 | 
					    if (attachments && attachments.length) {
 | 
				
			||||||
| 
						 | 
					@ -712,10 +858,13 @@ exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
 | 
				
			||||||
//      createAttachmentDataWriter :: (RelativePath -> IO Unit)
 | 
					//      createAttachmentDataWriter :: (RelativePath -> IO Unit)
 | 
				
			||||||
//                                    Message ->
 | 
					//                                    Message ->
 | 
				
			||||||
//                                    IO (Promise Message)
 | 
					//                                    IO (Promise Message)
 | 
				
			||||||
exports.createAttachmentDataWriter = ({
 | 
					export const createAttachmentDataWriter = ({
 | 
				
			||||||
  writeExistingAttachmentData,
 | 
					  writeExistingAttachmentData,
 | 
				
			||||||
  logger,
 | 
					  logger,
 | 
				
			||||||
}) => {
 | 
					}: {
 | 
				
			||||||
 | 
					  writeExistingAttachmentData: WriteExistingAttachmentDataType;
 | 
				
			||||||
 | 
					  logger: LoggerType;
 | 
				
			||||||
 | 
					}): ((message: MessageAttributesType) => Promise<MessageAttributesType>) => {
 | 
				
			||||||
  if (!isFunction(writeExistingAttachmentData)) {
 | 
					  if (!isFunction(writeExistingAttachmentData)) {
 | 
				
			||||||
    throw new TypeError(
 | 
					    throw new TypeError(
 | 
				
			||||||
      'createAttachmentDataWriter: writeExistingAttachmentData must be a function'
 | 
					      'createAttachmentDataWriter: writeExistingAttachmentData must be a function'
 | 
				
			||||||
| 
						 | 
					@ -725,12 +874,14 @@ exports.createAttachmentDataWriter = ({
 | 
				
			||||||
    throw new TypeError('createAttachmentDataWriter: logger must be an object');
 | 
					    throw new TypeError('createAttachmentDataWriter: logger must be an object');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return async rawMessage => {
 | 
					  return async (
 | 
				
			||||||
    if (!exports.isValid(rawMessage)) {
 | 
					    rawMessage: MessageAttributesType
 | 
				
			||||||
 | 
					  ): Promise<MessageAttributesType> => {
 | 
				
			||||||
 | 
					    if (!isValid(rawMessage)) {
 | 
				
			||||||
      throw new TypeError("'rawMessage' is not valid");
 | 
					      throw new TypeError("'rawMessage' is not valid");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const message = exports.initializeSchemaVersion({
 | 
					    const message = initializeSchemaVersion({
 | 
				
			||||||
      message: rawMessage,
 | 
					      message: rawMessage,
 | 
				
			||||||
      logger,
 | 
					      logger,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -748,13 +899,13 @@ exports.createAttachmentDataWriter = ({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const lastVersionWithAttachmentDataInMemory = 2;
 | 
					    const lastVersionWithAttachmentDataInMemory = 2;
 | 
				
			||||||
    const willAttachmentsGoToFileSystemOnUpgrade =
 | 
					    const willAttachmentsGoToFileSystemOnUpgrade =
 | 
				
			||||||
      message.schemaVersion <= lastVersionWithAttachmentDataInMemory;
 | 
					      (message.schemaVersion || 0) <= lastVersionWithAttachmentDataInMemory;
 | 
				
			||||||
    if (willAttachmentsGoToFileSystemOnUpgrade) {
 | 
					    if (willAttachmentsGoToFileSystemOnUpgrade) {
 | 
				
			||||||
      return message;
 | 
					      return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (attachments || []).forEach(attachment => {
 | 
					    (attachments || []).forEach(attachment => {
 | 
				
			||||||
      if (!Attachment.hasData(attachment)) {
 | 
					      if (!hasData(attachment)) {
 | 
				
			||||||
        throw new TypeError(
 | 
					        throw new TypeError(
 | 
				
			||||||
          "'attachment.data' is required during message import"
 | 
					          "'attachment.data' is required during message import"
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
| 
						 | 
					@ -767,27 +918,41 @@ exports.createAttachmentDataWriter = ({
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const writeThumbnails = exports._mapQuotedAttachments(async thumbnail => {
 | 
					    const writeQuoteAttachment = async (attachment: AttachmentType) => {
 | 
				
			||||||
 | 
					      const { thumbnail } = attachment;
 | 
				
			||||||
 | 
					      if (!thumbnail) {
 | 
				
			||||||
 | 
					        return attachment;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const { data, path } = thumbnail;
 | 
					      const { data, path } = thumbnail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // we want to be bulletproof to thumbnails without data
 | 
					      // we want to be bulletproof to attachments without data
 | 
				
			||||||
      if (!data || !path) {
 | 
					      if (!data || !path) {
 | 
				
			||||||
        logger.warn(
 | 
					        logger.warn(
 | 
				
			||||||
          'Thumbnail had neither data nor path.',
 | 
					          'quote attachment had neither data nor path.',
 | 
				
			||||||
          'id:',
 | 
					          'id:',
 | 
				
			||||||
          message.id,
 | 
					          message.id,
 | 
				
			||||||
          'source:',
 | 
					          'source:',
 | 
				
			||||||
          message.source
 | 
					          message.source
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        return thumbnail;
 | 
					        return attachment;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await writeExistingAttachmentData(thumbnail);
 | 
					      await writeExistingAttachmentData(thumbnail);
 | 
				
			||||||
      return omit(thumbnail, ['data']);
 | 
					      return {
 | 
				
			||||||
    });
 | 
					        ...attachment,
 | 
				
			||||||
 | 
					        thumbnail: omit(thumbnail, ['data']),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const writeContactAvatar = async messageContact => {
 | 
					    const writeContactAvatar = async (
 | 
				
			||||||
 | 
					      messageContact: EmbeddedContactType
 | 
				
			||||||
 | 
					    ): Promise<EmbeddedContactType> => {
 | 
				
			||||||
      const { avatar } = messageContact;
 | 
					      const { avatar } = messageContact;
 | 
				
			||||||
 | 
					      if (!avatar) {
 | 
				
			||||||
 | 
					        return messageContact;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (avatar && !avatar.avatar) {
 | 
					      if (avatar && !avatar.avatar) {
 | 
				
			||||||
        return omit(messageContact, ['avatar']);
 | 
					        return omit(messageContact, ['avatar']);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -800,7 +965,9 @@ exports.createAttachmentDataWriter = ({
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const writePreviewImage = async item => {
 | 
					    const writePreviewImage = async (
 | 
				
			||||||
 | 
					      item: PreviewType
 | 
				
			||||||
 | 
					    ): Promise<PreviewType> => {
 | 
				
			||||||
      const { image } = item;
 | 
					      const { image } = item;
 | 
				
			||||||
      if (!image) {
 | 
					      if (!image) {
 | 
				
			||||||
        return omit(item, ['image']);
 | 
					        return omit(item, ['image']);
 | 
				
			||||||
| 
						 | 
					@ -812,7 +979,17 @@ exports.createAttachmentDataWriter = ({
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const messageWithoutAttachmentData = {
 | 
					    const messageWithoutAttachmentData = {
 | 
				
			||||||
      ...(await writeThumbnails(message, { logger })),
 | 
					      ...message,
 | 
				
			||||||
 | 
					      ...(quote
 | 
				
			||||||
 | 
					        ? {
 | 
				
			||||||
 | 
					            quote: {
 | 
				
			||||||
 | 
					              ...quote,
 | 
				
			||||||
 | 
					              attachments: await Promise.all(
 | 
				
			||||||
 | 
					                (quote?.attachments || []).map(writeQuoteAttachment)
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        : undefined),
 | 
				
			||||||
      contact: await Promise.all((contact || []).map(writeContactAvatar)),
 | 
					      contact: await Promise.all((contact || []).map(writeContactAvatar)),
 | 
				
			||||||
      preview: await Promise.all((preview || []).map(writePreviewImage)),
 | 
					      preview: await Promise.all((preview || []).map(writePreviewImage)),
 | 
				
			||||||
      attachments: await Promise.all(
 | 
					      attachments: await Promise.all(
 | 
				
			||||||
| 
						 | 
					@ -842,5 +1019,3 @@ exports.createAttachmentDataWriter = ({
 | 
				
			||||||
    return messageWithoutAttachmentData;
 | 
					    return messageWithoutAttachmentData;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
exports.hasExpiration = MessageTS.hasExpiration;
 | 
					 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ export const format = memoizee(_format, {
 | 
				
			||||||
export function parse(
 | 
					export function parse(
 | 
				
			||||||
  phoneNumber: string,
 | 
					  phoneNumber: string,
 | 
				
			||||||
  options: {
 | 
					  options: {
 | 
				
			||||||
    regionCode: string;
 | 
					    regionCode: string | undefined;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
): string {
 | 
					): string {
 | 
				
			||||||
  const { regionCode } = options;
 | 
					  const { regionCode } = options;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { isNumber } from 'lodash';
 | 
					import { isNumber } from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const isValid = (value: unknown): boolean => {
 | 
					export const isValid = (value: unknown): value is number => {
 | 
				
			||||||
  return Boolean(isNumber(value) && value >= 0);
 | 
					  return Boolean(isNumber(value) && value >= 0);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,19 +3,20 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as Attachment from '../Attachment';
 | 
					import * as Attachment from '../Attachment';
 | 
				
			||||||
import * as IndexedDB from '../IndexedDB';
 | 
					import * as IndexedDB from '../IndexedDB';
 | 
				
			||||||
import type { Message, UserMessage } from '../Message';
 | 
					
 | 
				
			||||||
 | 
					import type { MessageAttributesType } from '../../model-types.d';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const hasAttachment =
 | 
					const hasAttachment =
 | 
				
			||||||
  (predicate: (value: Attachment.AttachmentType) => boolean) =>
 | 
					  (predicate: (value: Attachment.AttachmentType) => boolean) =>
 | 
				
			||||||
  (message: UserMessage): IndexedDB.IndexablePresence =>
 | 
					  (message: MessageAttributesType): IndexedDB.IndexablePresence =>
 | 
				
			||||||
    IndexedDB.toIndexablePresence(message.attachments.some(predicate));
 | 
					    IndexedDB.toIndexablePresence((message.attachments || []).some(predicate));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const hasFileAttachment = hasAttachment(Attachment.isFile);
 | 
					const hasFileAttachment = hasAttachment(Attachment.isFile);
 | 
				
			||||||
const hasVisualMediaAttachment = hasAttachment(Attachment.isVisualMedia);
 | 
					const hasVisualMediaAttachment = hasAttachment(Attachment.isVisualMedia);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const initializeAttachmentMetadata = async (
 | 
					export const initializeAttachmentMetadata = async (
 | 
				
			||||||
  message: Message
 | 
					  message: MessageAttributesType
 | 
				
			||||||
): Promise<Message> => {
 | 
					): Promise<MessageAttributesType> => {
 | 
				
			||||||
  if (message.type === 'verified-change') {
 | 
					  if (message.type === 'verified-change') {
 | 
				
			||||||
    return message;
 | 
					    return message;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -26,7 +27,7 @@ export const initializeAttachmentMetadata = async (
 | 
				
			||||||
    return message;
 | 
					    return message;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const attachments = message.attachments.filter(
 | 
					  const attachments = (message.attachments || []).filter(
 | 
				
			||||||
    (attachment: Attachment.AttachmentType) =>
 | 
					    (attachment: Attachment.AttachmentType) =>
 | 
				
			||||||
      attachment.contentType !== 'text/x-signal-plain'
 | 
					      attachment.contentType !== 'text/x-signal-plain'
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1598,7 +1598,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
 | 
				
			||||||
              return {
 | 
					              return {
 | 
				
			||||||
                path: attachment.path,
 | 
					                path: attachment.path,
 | 
				
			||||||
                objectURL: getAbsoluteAttachmentPath(attachment.path),
 | 
					                objectURL: getAbsoluteAttachmentPath(attachment.path),
 | 
				
			||||||
                thumbnailObjectUrl: thumbnail
 | 
					                thumbnailObjectUrl: thumbnail?.path
 | 
				
			||||||
                  ? getAbsoluteAttachmentPath(thumbnail.path)
 | 
					                  ? getAbsoluteAttachmentPath(thumbnail.path)
 | 
				
			||||||
                  : undefined,
 | 
					                  : undefined,
 | 
				
			||||||
                contentType: attachment.contentType,
 | 
					                contentType: attachment.contentType,
 | 
				
			||||||
| 
						 | 
					@ -2566,7 +2566,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              return {
 | 
					              return {
 | 
				
			||||||
                objectURL: getAbsoluteAttachmentPath(attachment.path || ''),
 | 
					                objectURL: getAbsoluteAttachmentPath(attachment.path || ''),
 | 
				
			||||||
                thumbnailObjectUrl: thumbnail
 | 
					                thumbnailObjectUrl: thumbnail?.path
 | 
				
			||||||
                  ? getAbsoluteAttachmentPath(thumbnail.path)
 | 
					                  ? getAbsoluteAttachmentPath(thumbnail.path)
 | 
				
			||||||
                  : '',
 | 
					                  : '',
 | 
				
			||||||
                contentType: attachment.contentType,
 | 
					                contentType: attachment.contentType,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue