Standalone Protocol Buffers (#2347)
This change introduces a standalone module for our protocol buffers as CommonJS
module incl. TypeScript type definitions.
**Rationale:** In order to exclude voice messages from the media gallery,
I needed to get a reference of `AttachmentPointer.Flags.VOICE_MESSAGE`.
Currently, the only way is to use `textsecure.protobuf` which is only accessible
as a global.
* [x] Add `Attachment.isVoiceMessage` as a way to test standalone
Protocol Buffers.
* [x] Add latest version of `protobufjs`. Leave existing version in place to
keep this change less disruptive and since it’s been stable. Hopefully we
can move over to standalone protobufs over time to improve modularity and
maybe even startup performance.
* [x] Add `yarn build-protobuf` command to compile `SignalService.proto` into
standalone CommonJS module and accompanying TypeScript definitions.
~~Included compiled output for ease of use for other developers.
Can revisit if changes become more frequent.~~
Now built as part of `yarn grunt`.
* [x] Update style guide references and make sure they work!
* [x] ⚠️ Change type definition for `Attachment::file` to include `null` as
that’s apparently a valid value for legacy Android voice messages.
This commit is contained in:
commit
c7a502e2e1
22 changed files with 200 additions and 86 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -24,3 +24,4 @@ test/test.js
|
|||
|
||||
# React / TypeScript
|
||||
ts/**/*.js
|
||||
ts/protobuf/*.d.ts
|
||||
|
|
|
@ -2,14 +2,19 @@
|
|||
# supports `.gitignore`: https://github.com/prettier/prettier/issues/2294
|
||||
|
||||
# Generated files
|
||||
config/local-*.json
|
||||
config/local.json
|
||||
dist/**
|
||||
js/components.js
|
||||
js/libsignal-protocol-worker.js
|
||||
js/libtextsecure.js
|
||||
libtextsecure/components.js
|
||||
libtextsecure/test/test.js
|
||||
stylesheets/*.css
|
||||
test/test.js
|
||||
ts/**/*.js
|
||||
ts/protobuf/*.d.ts
|
||||
ts/protobuf/*.js
|
||||
|
||||
# Third-party files
|
||||
components/**
|
||||
|
|
|
@ -7,11 +7,10 @@ dist: trusty
|
|||
install:
|
||||
- yarn install --frozen-lockfile
|
||||
script:
|
||||
- yarn transpile
|
||||
- yarn generate
|
||||
- yarn lint
|
||||
- yarn test-node
|
||||
- yarn nsp check
|
||||
- yarn generate
|
||||
- yarn prepare-beta-build
|
||||
- $(yarn bin)/build --config.extraMetadata.environment=$SIGNAL_ENV --config.mac.bundleVersion='$TRAVIS_BUILD_NUMBER' --publish=never
|
||||
- ./travis.sh
|
||||
|
|
24
Gruntfile.js
24
Gruntfile.js
|
@ -186,17 +186,21 @@ module.exports = function(grunt) {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
sass: {
|
||||
files: ['./stylesheets/*.scss'],
|
||||
tasks: ['sass'],
|
||||
dist: {
|
||||
files: ['<%= dist.src %>', '<%= dist.res %>'],
|
||||
tasks: ['copy_dist'],
|
||||
},
|
||||
libtextsecure: {
|
||||
files: ['./libtextsecure/*.js', './libtextsecure/storage/*.js'],
|
||||
tasks: ['concat:libtextsecure'],
|
||||
},
|
||||
dist: {
|
||||
files: ['<%= dist.src %>', '<%= dist.res %>'],
|
||||
tasks: ['copy_dist'],
|
||||
protobuf: {
|
||||
files: ['./protos/SignalService.proto'],
|
||||
tasks: ['exec:build-protobuf'],
|
||||
},
|
||||
sass: {
|
||||
files: ['./stylesheets/*.scss'],
|
||||
tasks: ['sass'],
|
||||
},
|
||||
scripts: {
|
||||
files: ['<%= jshint.files %>'],
|
||||
|
@ -216,7 +220,10 @@ module.exports = function(grunt) {
|
|||
cmd: 'tx pull',
|
||||
},
|
||||
transpile: {
|
||||
cmd: 'npm run transpile',
|
||||
cmd: 'yarn transpile',
|
||||
},
|
||||
'build-protobuf': {
|
||||
cmd: 'yarn build-protobuf',
|
||||
},
|
||||
},
|
||||
'test-release': {
|
||||
|
@ -499,10 +506,11 @@ module.exports = function(grunt) {
|
|||
grunt.registerTask('copy_dist', ['gitinfo', 'copy:res', 'copy:src']);
|
||||
grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
|
||||
grunt.registerTask('default', [
|
||||
'exec:build-protobuf',
|
||||
'exec:transpile',
|
||||
'concat',
|
||||
'copy:deps',
|
||||
'sass',
|
||||
'date',
|
||||
'exec:transpile',
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -12,11 +12,10 @@ install:
|
|||
- yarn install --frozen-lockfile
|
||||
|
||||
build_script:
|
||||
- yarn transpile
|
||||
- yarn generate
|
||||
- yarn lint-windows
|
||||
- yarn test-node
|
||||
- yarn nsp check
|
||||
- yarn generate
|
||||
- node build\grunt.js
|
||||
- type package.json | findstr /v certificateSubjectName > temp.json
|
||||
- move temp.json package.json
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
/* global _: false */
|
||||
/* global Backbone: false */
|
||||
/* global Whisper: false */
|
||||
/* global textsecure: false */
|
||||
|
||||
/* global ConversationController: false */
|
||||
/* global i18n: false */
|
||||
/* global getAccountManager: false */
|
||||
/* global i18n: false */
|
||||
/* global Signal: false */
|
||||
/* global textsecure: false */
|
||||
/* global Whisper: false */
|
||||
|
||||
/* eslint-disable more/no-then */
|
||||
|
||||
|
@ -14,8 +16,8 @@
|
|||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
const { Message: TypedMessage } = window.Signal.Types;
|
||||
const { deleteAttachmentData } = window.Signal.Migrations;
|
||||
const { Message: TypedMessage } = Signal.Types;
|
||||
const { deleteAttachmentData } = Signal.Migrations;
|
||||
|
||||
window.Whisper.Message = Backbone.Model.extend({
|
||||
database: Whisper.Database,
|
||||
|
@ -31,9 +33,6 @@
|
|||
this.on('change:expireTimer', this.setToExpire);
|
||||
this.on('unload', this.unload);
|
||||
this.setToExpire();
|
||||
|
||||
this.VOICE_FLAG =
|
||||
textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE;
|
||||
},
|
||||
idForLogging() {
|
||||
return `${this.get('source')}.${this.get('sourceDevice')} ${this.get(
|
||||
|
@ -246,8 +245,7 @@
|
|||
});
|
||||
|
||||
return Object.assign({}, attachment, {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
isVoiceMessage: Boolean(attachment.flags & this.VOICE_FLAG),
|
||||
isVoiceMessage: Signal.Types.Attachment.isVoiceMessage(attachment),
|
||||
thumbnail: thumbnailWithObjectUrl,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -179,4 +179,5 @@ exports.deleteData = deleteAttachmentData => {
|
|||
};
|
||||
};
|
||||
|
||||
exports.isVoiceMessage = AttachmentTS.isVoiceMessage;
|
||||
exports.save = AttachmentTS.save;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
/* global i18n: false */
|
||||
/* global Signal: false */
|
||||
/* global textsecure: false */
|
||||
/* global Whisper: false */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
|
@ -119,20 +118,7 @@
|
|||
Signal.Backbone.Views.Lightbox.show(this.lightboxView.el);
|
||||
},
|
||||
isVoiceMessage() {
|
||||
if (
|
||||
// eslint-disable-next-line no-bitwise
|
||||
this.model.flags &
|
||||
textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Support for android legacy voice messages
|
||||
if (this.isAudio() && this.model.fileName === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Signal.Types.Attachment.isVoiceMessage(this.model);
|
||||
},
|
||||
isAudio() {
|
||||
const { contentType } = this.model;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<title>libTextSecure test runner</title>
|
||||
<title>libtextsecure test runner</title>
|
||||
<link rel="stylesheet" href="../../components/mocha/mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -15,9 +15,13 @@
|
|||
"start": "electron .",
|
||||
"grunt": "grunt",
|
||||
"icon-gen": "electron-icon-maker --input=images/icon_1024.png --output=./build",
|
||||
"generate": "npm run icon-gen && grunt",
|
||||
"generate": "yarn icon-gen && yarn grunt",
|
||||
"build": "build --config.extraMetadata.environment=$SIGNAL_ENV",
|
||||
"build-release": "SIGNAL_ENV=production npm run build -- --config.directories.output=release",
|
||||
"build-module-protobuf": "pbjs --target static-module --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
||||
"clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
||||
"build-protobuf": "yarn build-module-protobuf",
|
||||
"clean-protobuf": "yarn clean-module-protobuf",
|
||||
"prepare-beta-build": "node prepare_beta_build.js",
|
||||
"prepare-import-build": "node prepare_import_build.js",
|
||||
"publish-to-apt": "NAME=$npm_package_name VERSION=$npm_package_version ./aptly.sh",
|
||||
|
@ -69,6 +73,7 @@
|
|||
"node-fetch": "https://github.com/scottnonnenberg/node-fetch.git#3e5f51e08c647ee5f20c43b15cf2d352d61c36b4",
|
||||
"os-locale": "^2.1.0",
|
||||
"pify": "^3.0.0",
|
||||
"protobufjs": "^6.8.6",
|
||||
"proxy-agent": "^2.1.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
package signalservice;
|
||||
|
||||
option java_package = "org.whispersystems.libsignal.protocol";
|
||||
option java_outer_classname = "WhisperProtos";
|
||||
|
||||
message WhisperMessage {
|
||||
optional bytes ephemeralKey = 1;
|
||||
optional uint32 counter = 2;
|
||||
optional uint32 previousCounter = 3;
|
||||
optional bytes ciphertext = 4; // PushMessageContent
|
||||
}
|
||||
|
||||
message PreKeyWhisperMessage {
|
||||
optional uint32 registrationId = 5;
|
||||
optional uint32 preKeyId = 1;
|
||||
optional uint32 signedPreKeyId = 6;
|
||||
optional bytes baseKey = 2;
|
||||
optional bytes identityKey = 3;
|
||||
optional bytes message = 4; // WhisperMessage
|
||||
}
|
||||
|
||||
message KeyExchangeMessage {
|
||||
optional uint32 id = 1;
|
||||
optional bytes baseKey = 2;
|
||||
optional bytes ephemeralKey = 3;
|
||||
optional bytes identityKey = 4;
|
||||
optional bytes baseKeySignature = 5;
|
||||
}
|
|
@ -597,7 +597,7 @@ const outgoing = new Whisper.Message({
|
|||
sent_at: Date.now() - 15000,
|
||||
attachments: [
|
||||
{
|
||||
flags: textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||
flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||
data: util.mp3,
|
||||
fileName: 'agnus_dei.mp3',
|
||||
contentType: 'audio/mp3',
|
||||
|
|
|
@ -572,7 +572,7 @@ const outgoing = new Whisper.Message({
|
|||
attachments: [
|
||||
{
|
||||
// proposed as of afternoon of 4/6 in Quoted Replies group
|
||||
flags: textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||
flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||
contentType: 'audio/mp3',
|
||||
fileName: 'agnus_dei.mp4',
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@ import moment from 'moment';
|
|||
import formatFileSize from 'filesize';
|
||||
|
||||
interface Props {
|
||||
fileName?: string;
|
||||
fileName?: string | null;
|
||||
fileSize?: number;
|
||||
i18n: (key: string, values?: Array<string>) => string;
|
||||
onClick?: () => void;
|
||||
|
|
3
ts/protobuf/README.md
Normal file
3
ts/protobuf/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Protocol Buffers
|
||||
|
||||
Placeholder directory for Protocol Buffers compiled to JavaScript / TypeScript.
|
3
ts/protobuf/index.ts
Normal file
3
ts/protobuf/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { signalservice as SignalService } from './compiled';
|
||||
|
||||
export { SignalService };
|
|
@ -19,7 +19,9 @@ export { BackboneWrapper } from '../components/utility/BackboneWrapper';
|
|||
import { Quote } from '../components/conversation/Quote';
|
||||
import * as HTML from '../html';
|
||||
|
||||
import * as Attachment from '../../ts/types/Attachment';
|
||||
import * as MIME from '../../ts/types/MIME';
|
||||
import { SignalService } from '../../ts/protobuf';
|
||||
|
||||
// TypeScript wants two things when you import:
|
||||
// 1) a normal typescript file
|
||||
|
@ -125,10 +127,12 @@ parent.ReactDOM = ReactDOM;
|
|||
|
||||
parent.Signal.HTML = HTML;
|
||||
parent.Signal.Types.MIME = MIME;
|
||||
parent.Signal.Types.Attachment = Attachment;
|
||||
parent.Signal.Components = {
|
||||
Quote,
|
||||
};
|
||||
parent.Signal.Util = Util;
|
||||
parent.SignalService = SignalService;
|
||||
parent.filesize = filesize;
|
||||
|
||||
parent.ConversationController._initialFetchComplete = true;
|
||||
|
|
|
@ -5,7 +5,8 @@ import 'mocha';
|
|||
import { assert } from 'chai';
|
||||
|
||||
import * as Attachment from '../../types/Attachment';
|
||||
import { MIMEType } from '../../types/MIME';
|
||||
import * as MIME from '../../types/MIME';
|
||||
import { SignalService } from '../../protobuf';
|
||||
// @ts-ignore
|
||||
import { stringToArrayBuffer } from '../../../js/modules/string_to_array_buffer';
|
||||
|
||||
|
@ -14,7 +15,7 @@ describe('Attachment', () => {
|
|||
it('should return file extension from content type', () => {
|
||||
const input: Attachment.Attachment = {
|
||||
data: stringToArrayBuffer('foo'),
|
||||
contentType: 'image/gif' as MIMEType,
|
||||
contentType: MIME.IMAGE_GIF,
|
||||
};
|
||||
assert.strictEqual(Attachment.getFileExtension(input), 'gif');
|
||||
});
|
||||
|
@ -22,7 +23,7 @@ describe('Attachment', () => {
|
|||
it('should return file extension for QuickTime videos', () => {
|
||||
const input: Attachment.Attachment = {
|
||||
data: stringToArrayBuffer('foo'),
|
||||
contentType: 'video/quicktime' as MIMEType,
|
||||
contentType: MIME.VIDEO_QUICKTIME,
|
||||
};
|
||||
assert.strictEqual(Attachment.getFileExtension(input), 'mov');
|
||||
});
|
||||
|
@ -34,7 +35,7 @@ describe('Attachment', () => {
|
|||
const attachment: Attachment.Attachment = {
|
||||
fileName: 'funny-cat.mov',
|
||||
data: stringToArrayBuffer('foo'),
|
||||
contentType: 'video/quicktime' as MIMEType,
|
||||
contentType: MIME.VIDEO_QUICKTIME,
|
||||
};
|
||||
const actual = Attachment.getSuggestedFilename({ attachment });
|
||||
const expected = 'funny-cat.mov';
|
||||
|
@ -45,7 +46,7 @@ describe('Attachment', () => {
|
|||
it('should generate a filename based on timestamp', () => {
|
||||
const attachment: Attachment.Attachment = {
|
||||
data: stringToArrayBuffer('foo'),
|
||||
contentType: 'video/quicktime' as MIMEType,
|
||||
contentType: MIME.VIDEO_QUICKTIME,
|
||||
};
|
||||
const timestamp = new Date(new Date(0).getTimezoneOffset() * 60 * 1000);
|
||||
const actual = Attachment.getSuggestedFilename({
|
||||
|
@ -57,4 +58,34 @@ describe('Attachment', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isVoiceMessage', () => {
|
||||
it('should return true for voice message attachment', () => {
|
||||
const attachment: Attachment.Attachment = {
|
||||
fileName: 'Voice Message.aac',
|
||||
flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||
data: stringToArrayBuffer('voice message'),
|
||||
contentType: MIME.AUDIO_AAC,
|
||||
};
|
||||
assert.isTrue(Attachment.isVoiceMessage(attachment));
|
||||
});
|
||||
|
||||
it('should return true for legacy Android voice message attachment', () => {
|
||||
const attachment: Attachment.Attachment = {
|
||||
fileName: null,
|
||||
data: stringToArrayBuffer('voice message'),
|
||||
contentType: MIME.AUDIO_MP3,
|
||||
};
|
||||
assert.isTrue(Attachment.isVoiceMessage(attachment));
|
||||
});
|
||||
|
||||
it('should return false for other attachments', () => {
|
||||
const attachment: Attachment.Attachment = {
|
||||
fileName: 'foo.gif',
|
||||
data: stringToArrayBuffer('foo'),
|
||||
contentType: MIME.IMAGE_GIF,
|
||||
};
|
||||
assert.isFalse(Attachment.isVoiceMessage(attachment));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import { assert } from 'chai';
|
|||
|
||||
import * as Message from '../../../../ts/types/message/initializeAttachmentMetadata';
|
||||
import { IncomingMessage } from '../../../../ts/types/Message';
|
||||
import { MIMEType } from '../../../../ts/types/MIME';
|
||||
import * as MIME from '../../../../ts/types/MIME';
|
||||
// @ts-ignore
|
||||
import { stringToArrayBuffer } from '../../../../js/modules/string_to_array_buffer';
|
||||
|
||||
|
@ -19,7 +19,7 @@ describe('Message', () => {
|
|||
sent_at: 1523317140800,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'image/jpeg' as MIMEType,
|
||||
contentType: MIME.IMAGE_JPEG,
|
||||
data: stringToArrayBuffer('foo'),
|
||||
fileName: 'foo.jpg',
|
||||
size: 1111,
|
||||
|
@ -35,7 +35,7 @@ describe('Message', () => {
|
|||
sent_at: 1523317140800,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'image/jpeg' as MIMEType,
|
||||
contentType: MIME.IMAGE_JPEG,
|
||||
data: stringToArrayBuffer('foo'),
|
||||
fileName: 'foo.jpg',
|
||||
size: 1111,
|
||||
|
|
|
@ -2,13 +2,15 @@ import is from '@sindresorhus/is';
|
|||
import moment from 'moment';
|
||||
|
||||
import * as GoogleChrome from '../util/GoogleChrome';
|
||||
import { saveURLAsFile } from '../util/saveURLAsFile';
|
||||
import * as MIME from './MIME';
|
||||
import { arrayBufferToObjectURL } from '../util/arrayBufferToObjectURL';
|
||||
import { MIMEType } from './MIME';
|
||||
import { saveURLAsFile } from '../util/saveURLAsFile';
|
||||
import { SignalService } from '../protobuf';
|
||||
|
||||
export type Attachment = {
|
||||
fileName?: string;
|
||||
contentType?: MIMEType;
|
||||
fileName?: string | null;
|
||||
flags?: SignalService.AttachmentPointer.Flags;
|
||||
contentType?: MIME.MIMEType;
|
||||
size?: number;
|
||||
data: ArrayBuffer;
|
||||
|
||||
|
@ -20,15 +22,12 @@ export type Attachment = {
|
|||
// thumbnail?: ArrayBuffer;
|
||||
// key?: ArrayBuffer;
|
||||
// digest?: ArrayBuffer;
|
||||
// flags?: number;
|
||||
} & Partial<AttachmentSchemaVersion3>;
|
||||
|
||||
interface AttachmentSchemaVersion3 {
|
||||
path: string;
|
||||
}
|
||||
|
||||
const SAVE_CONTENT_TYPE = 'application/octet-stream' as MIMEType;
|
||||
|
||||
export const isVisualMedia = (attachment: Attachment): boolean => {
|
||||
const { contentType } = attachment;
|
||||
|
||||
|
@ -41,6 +40,26 @@ export const isVisualMedia = (attachment: Attachment): boolean => {
|
|||
return isSupportedImageType || isSupportedVideoType;
|
||||
};
|
||||
|
||||
export const isVoiceMessage = (attachment: Attachment): boolean => {
|
||||
const flag = SignalService.AttachmentPointer.Flags.VOICE_MESSAGE;
|
||||
const hasFlag =
|
||||
// tslint:disable-next-line no-bitwise
|
||||
!is.undefined(attachment.flags) && (attachment.flags & flag) === flag;
|
||||
if (hasFlag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isLegacyAndroidVoiceMessage =
|
||||
!is.undefined(attachment.contentType) &&
|
||||
MIME.isAudio(attachment.contentType) &&
|
||||
attachment.fileName === null;
|
||||
if (isLegacyAndroidVoiceMessage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const save = ({
|
||||
attachment,
|
||||
document,
|
||||
|
@ -57,7 +76,7 @@ export const save = ({
|
|||
? getAbsolutePath(attachment.path)
|
||||
: arrayBufferToObjectURL({
|
||||
data: attachment.data,
|
||||
type: SAVE_CONTENT_TYPE,
|
||||
type: MIME.APPLICATION_OCTET_STREAM,
|
||||
});
|
||||
const filename = getSuggestedFilename({ attachment, timestamp });
|
||||
saveURLAsFile({ url, filename, document });
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
export type MIMEType = string & { _mimeTypeBrand: any };
|
||||
|
||||
export const APPLICATION_OCTET_STREAM = 'application/octet-stream' as MIMEType;
|
||||
export const AUDIO_AAC = 'audio/aac' as MIMEType;
|
||||
export const AUDIO_MP3 = 'audio/mp3' as MIMEType;
|
||||
export const IMAGE_GIF = 'image/gif' as MIMEType;
|
||||
export const IMAGE_JPEG = 'image/jpeg' as MIMEType;
|
||||
export const VIDEO_QUICKTIME = 'video/quicktime' as MIMEType;
|
||||
|
||||
export const isJPEG = (value: MIMEType): boolean => value === 'image/jpeg';
|
||||
export const isImage = (value: MIMEType): boolean => value.startsWith('image/');
|
||||
export const isVideo = (value: MIMEType): boolean => value.startsWith('video/');
|
||||
|
|
73
yarn.lock
73
yarn.lock
|
@ -22,6 +22,49 @@
|
|||
"7zip-bin-mac" "~1.0.1"
|
||||
"7zip-bin-win" "~2.2.0"
|
||||
|
||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
|
||||
|
||||
"@protobufjs/base64@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
|
||||
|
||||
"@protobufjs/codegen@^2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
|
||||
|
||||
"@protobufjs/eventemitter@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
|
||||
|
||||
"@protobufjs/fetch@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
|
||||
dependencies:
|
||||
"@protobufjs/aspromise" "^1.1.1"
|
||||
"@protobufjs/inquire" "^1.1.0"
|
||||
|
||||
"@protobufjs/float@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
|
||||
|
||||
"@protobufjs/inquire@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
|
||||
|
||||
"@protobufjs/path@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
|
||||
|
||||
"@protobufjs/pool@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
|
||||
|
||||
"@protobufjs/utf8@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
|
||||
"@sindresorhus/is@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||
|
@ -56,6 +99,10 @@
|
|||
version "4.14.106"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.106.tgz#6093e9a02aa567ddecfe9afadca89e53e5dce4dd"
|
||||
|
||||
"@types/long@^3.0.32":
|
||||
version "3.0.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69"
|
||||
|
||||
"@types/mocha@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.0.0.tgz#a3014921991066193f6c8e47290d4d598dfd19e6"
|
||||
|
@ -68,6 +115,10 @@
|
|||
version "8.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.9.4.tgz#dfd327582a06c114eb6e0441fa3d6fab35edad48"
|
||||
|
||||
"@types/node@^8.9.4":
|
||||
version "8.10.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.12.tgz#dcb66f6de39074a296534bd1a256a3c6a1c8f5b5"
|
||||
|
||||
"@types/qs@^6.5.1":
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.5.1.tgz#a38f69c62528d56ba7bd1f91335a8004988d72f7"
|
||||
|
@ -5326,6 +5377,10 @@ lolex@^2.2.0, lolex@^2.3.2:
|
|||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.2.tgz#85f9450425103bf9e7a60668ea25dc43274ca807"
|
||||
|
||||
long@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
|
||||
|
||||
longest-streak@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e"
|
||||
|
@ -6947,6 +7002,24 @@ prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1:
|
|||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
protobufjs@^6.8.6:
|
||||
version "6.8.6"
|
||||
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.6.tgz#ce3cf4fff9625b62966c455fc4c15e4331a11ca2"
|
||||
dependencies:
|
||||
"@protobufjs/aspromise" "^1.1.2"
|
||||
"@protobufjs/base64" "^1.1.2"
|
||||
"@protobufjs/codegen" "^2.0.4"
|
||||
"@protobufjs/eventemitter" "^1.1.0"
|
||||
"@protobufjs/fetch" "^1.1.0"
|
||||
"@protobufjs/float" "^1.0.2"
|
||||
"@protobufjs/inquire" "^1.1.0"
|
||||
"@protobufjs/path" "^1.1.2"
|
||||
"@protobufjs/pool" "^1.1.0"
|
||||
"@protobufjs/utf8" "^1.1.0"
|
||||
"@types/long" "^3.0.32"
|
||||
"@types/node" "^8.9.4"
|
||||
long "^4.0.0"
|
||||
|
||||
proxy-addr@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue