@msssystems/mss-link-sdk 0.1.12 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/client/browser-client.d.ts +16 -0
  2. package/dist/client/browser-client.js +35 -0
  3. package/dist/client/index.d.ts +3 -20
  4. package/dist/client/index.js +3 -20
  5. package/dist/client/mss-link-client.d.ts +12 -0
  6. package/dist/client/mss-link-client.js +17 -0
  7. package/dist/client/wasm-client.types.d.ts +1 -1
  8. package/dist/contracts/chat.contract.d.ts +189 -0
  9. package/dist/contracts/user-search.contract.d.ts +9 -0
  10. package/dist/core/http-client.d.ts +24 -0
  11. package/dist/core/http-client.js +80 -0
  12. package/dist/{client → core}/local-crypto-health.contracts.d.ts +1 -1
  13. package/dist/{client → core}/local-crypto-health.js +1 -1
  14. package/dist/core/wasm-crypto-engine.d.ts +34 -0
  15. package/dist/core/wasm-crypto-engine.js +75 -0
  16. package/dist/errors/api-error.d.ts +6 -0
  17. package/dist/errors/api-error.js +11 -0
  18. package/dist/errors/sdk-error.d.ts +3 -0
  19. package/dist/errors/sdk-error.js +6 -0
  20. package/dist/modules/chats/chats.client.d.ts +11 -0
  21. package/dist/modules/chats/chats.client.js +25 -0
  22. package/dist/modules/media/media-crypto.client.d.ts +10 -0
  23. package/dist/modules/media/media-crypto.client.js +90 -0
  24. package/dist/{client → modules/messages}/message-decryption-state.js +1 -1
  25. package/dist/{client → modules/messages}/message-decryption.contracts.d.ts +1 -1
  26. package/dist/modules/messages/messages-crypto.client.d.ts +14 -0
  27. package/dist/modules/messages/messages-crypto.client.js +34 -0
  28. package/dist/modules/messages/messages.client.d.ts +8 -0
  29. package/dist/modules/messages/messages.client.js +24 -0
  30. package/dist/{client → modules/storage}/drive-file-runtime.contracts.d.ts +2 -2
  31. package/dist/{client → modules/storage}/drive-file-runtime.d.ts +1 -1
  32. package/dist/modules/storage/drive-sharing.contracts.js +1 -0
  33. package/dist/modules/storage/storage-crypto.client.d.ts +13 -0
  34. package/dist/modules/storage/storage-crypto.client.js +73 -0
  35. package/dist/modules/storage/storage-metadata-crypto.contracts.js +1 -0
  36. package/package.json +1 -1
  37. package/dist/client/mss-link-browser-client.d.ts +0 -46
  38. package/dist/client/mss-link-browser-client.js +0 -259
  39. /package/dist/{client/drive-file-runtime.contracts.js → contracts/chat.contract.js} +0 -0
  40. /package/dist/{client/drive-key-wrapping.contracts.js → contracts/user-search.contract.js} +0 -0
  41. /package/dist/{client → core}/api-base-url.d.ts +0 -0
  42. /package/dist/{client → core}/api-base-url.js +0 -0
  43. /package/dist/{client → core}/local-crypto-health.contracts.js +0 -0
  44. /package/dist/{client → core}/local-crypto-health.d.ts +0 -0
  45. /package/dist/{client → core}/wasm-call-queue.d.ts +0 -0
  46. /package/dist/{client → core}/wasm-call-queue.js +0 -0
  47. /package/dist/{client → errors}/decryption-errors.d.ts +0 -0
  48. /package/dist/{client → errors}/decryption-errors.js +0 -0
  49. /package/dist/{client → modules/media}/media-crypto.contracts.d.ts +0 -0
  50. /package/dist/{client → modules/media}/media-crypto.contracts.js +0 -0
  51. /package/dist/{client → modules/media}/media-message-metadata.d.ts +0 -0
  52. /package/dist/{client → modules/media}/media-message-metadata.js +0 -0
  53. /package/dist/{client → modules/media}/media-message.contracts.d.ts +0 -0
  54. /package/dist/{client → modules/media}/media-message.contracts.js +0 -0
  55. /package/dist/{client → modules/messages}/message-decryption-state.d.ts +0 -0
  56. /package/dist/{client → modules/messages}/message-decryption.contracts.js +0 -0
  57. /package/dist/{client → modules/messages}/message.contracts.d.ts +0 -0
  58. /package/dist/{client → modules/messages}/message.contracts.js +0 -0
  59. /package/dist/{client → modules/messages}/message.mapper.d.ts +0 -0
  60. /package/dist/{client → modules/messages}/message.mapper.js +0 -0
  61. /package/dist/{client/drive-sharing.contracts.js → modules/storage/drive-file-runtime.contracts.js} +0 -0
  62. /package/dist/{client → modules/storage}/drive-file-runtime.js +0 -0
  63. /package/dist/{client → modules/storage}/drive-key-wrapping.contracts.d.ts +0 -0
  64. /package/dist/{client/storage-metadata-crypto.contracts.js → modules/storage/drive-key-wrapping.contracts.js} +0 -0
  65. /package/dist/{client → modules/storage}/drive-key-wrapping.d.ts +0 -0
  66. /package/dist/{client → modules/storage}/drive-key-wrapping.js +0 -0
  67. /package/dist/{client → modules/storage}/drive-sharing.contracts.d.ts +0 -0
  68. /package/dist/{client → modules/storage}/drive-sharing.d.ts +0 -0
  69. /package/dist/{client → modules/storage}/drive-sharing.js +0 -0
  70. /package/dist/{client → modules/storage}/storage-metadata-crypto.contracts.d.ts +0 -0
  71. /package/dist/{client → modules/storage}/storage-metadata-crypto.d.ts +0 -0
  72. /package/dist/{client → modules/storage}/storage-metadata-crypto.js +0 -0
@@ -0,0 +1,90 @@
1
+ import { normalizeLocalCryptoError } from '../../errors/decryption-errors';
2
+ import { buildMediaMessageMetadataPayload, extractMediaAssetIds, serializeMediaMessageMetadata, } from './media-message-metadata';
3
+ export class MediaCryptoClient {
4
+ engine;
5
+ constructor(engine) {
6
+ this.engine = engine;
7
+ }
8
+ async sendMediaMessage(params) {
9
+ try {
10
+ await this.engine.ensureReady();
11
+ const payload = buildMediaMessageMetadataPayload(params.attachments, params.caption);
12
+ const metadataJson = serializeMediaMessageMetadata(payload);
13
+ const mediaAssetIds = extractMediaAssetIds(params.attachments);
14
+ return this.engine.runWithClient((client) => client.sendMediaMessage(params.chatId, params.targetUserId, metadataJson, mediaAssetIds));
15
+ }
16
+ catch (error) {
17
+ throw normalizeLocalCryptoError(error);
18
+ }
19
+ }
20
+ async encryptMediaBlob(input) {
21
+ try {
22
+ const plaintext = new Uint8Array(await input.blob.arrayBuffer());
23
+ const encrypted = await this.engine.queue.run(async () => {
24
+ const module = await this.engine.loadWasmModule();
25
+ return module.encryptMediaBytes(plaintext);
26
+ });
27
+ const encryptedBytes = new Uint8Array(encrypted.ciphertext);
28
+ return {
29
+ encryptedBlob: new Blob([encryptedBytes], {
30
+ type: 'application/octet-stream',
31
+ }),
32
+ encryption: {
33
+ schema: 'mss.link.media-encryption.v1',
34
+ algorithm: 'AES-256-GCM',
35
+ keyId: input.assetId,
36
+ nonce: encrypted.nonce,
37
+ wrappedKey: encrypted.key,
38
+ },
39
+ attachment: {
40
+ assetId: input.assetId,
41
+ mimeType: input.mimeType,
42
+ size: input.size,
43
+ ...(input.originalName ? { originalName: input.originalName } : {}),
44
+ ...(input.uploadedAt ? { uploadedAt: input.uploadedAt } : {}),
45
+ encryption: {
46
+ schema: 'mss.link.media-encryption.v1',
47
+ algorithm: 'AES-256-GCM',
48
+ keyId: input.assetId,
49
+ nonce: encrypted.nonce,
50
+ wrappedKey: encrypted.key,
51
+ },
52
+ },
53
+ };
54
+ }
55
+ catch (error) {
56
+ throw normalizeLocalCryptoError(error);
57
+ }
58
+ }
59
+ async decryptMediaBlob(input) {
60
+ const encryption = input.metadata.encryption;
61
+ if (!encryption) {
62
+ return {
63
+ blob: input.encryptedBlob,
64
+ mimeType: input.metadata.mimeType,
65
+ ...(input.metadata.originalName
66
+ ? { fileName: input.metadata.originalName }
67
+ : {}),
68
+ };
69
+ }
70
+ try {
71
+ const ciphertext = new Uint8Array(await input.encryptedBlob.arrayBuffer());
72
+ const plaintext = await this.engine.queue.run(async () => {
73
+ const module = await this.engine.loadWasmModule();
74
+ return module.decryptMediaBytes(ciphertext, encryption.wrappedKey, encryption.nonce);
75
+ });
76
+ return {
77
+ blob: new Blob([new Uint8Array(plaintext)], {
78
+ type: input.metadata.mimeType,
79
+ }),
80
+ mimeType: input.metadata.mimeType,
81
+ ...(input.metadata.originalName
82
+ ? { fileName: input.metadata.originalName }
83
+ : {}),
84
+ };
85
+ }
86
+ catch (error) {
87
+ throw normalizeLocalCryptoError(error);
88
+ }
89
+ }
90
+ }
@@ -1,4 +1,4 @@
1
- import { normalizeLocalCryptoError } from './decryption-errors';
1
+ import { normalizeLocalCryptoError } from '../../errors/decryption-errors';
2
2
  export function createMessageDecryptionStateIndex({ messages, decryptedMessages, isDecrypting, decryptError, }) {
3
3
  const decryptedById = createDecryptedMessageIndex(decryptedMessages);
4
4
  const index = new Map();
@@ -1,4 +1,4 @@
1
- import type { LocalCryptoErrorCode } from './decryption-errors';
1
+ import type { LocalCryptoErrorCode } from '../../errors/decryption-errors';
2
2
  import type { DecryptedMessage } from './message.contracts';
3
3
  export type MessageDecryptStatus = 'decrypted' | 'pending' | 'failed' | 'recovery_required';
4
4
  export type MessageDecryptReason = 'local_sync_in_progress' | 'local_message_missing' | 'local_crypto_error';
@@ -0,0 +1,14 @@
1
+ import { WasmCryptoEngine } from '../../core/wasm-crypto-engine';
2
+ import type { DecryptedMessage } from './message.contracts';
3
+ export declare class MessagesCryptoClient {
4
+ private readonly engine;
5
+ constructor(engine: WasmCryptoEngine);
6
+ sendMessage(params: {
7
+ chatId: string;
8
+ targetUserId: string;
9
+ text: string;
10
+ kind?: string;
11
+ }): Promise<string>;
12
+ syncMessages(): Promise<void>;
13
+ getLocalMessages(chatId: string): Promise<DecryptedMessage[]>;
14
+ }
@@ -0,0 +1,34 @@
1
+ import { normalizeLocalCryptoError } from '../../errors/decryption-errors';
2
+ import { mapLocalSdkMessage } from './message.mapper';
3
+ export class MessagesCryptoClient {
4
+ engine;
5
+ constructor(engine) {
6
+ this.engine = engine;
7
+ }
8
+ async sendMessage(params) {
9
+ try {
10
+ await this.engine.ensureReady();
11
+ return this.engine.runWithClient((client) => client.sendMessage(params.chatId, params.targetUserId, params.text, params.kind ?? 'TEXT'));
12
+ }
13
+ catch (error) {
14
+ throw normalizeLocalCryptoError(error);
15
+ }
16
+ }
17
+ async syncMessages() {
18
+ try {
19
+ await this.engine.runWithClient((client) => client.syncMessages());
20
+ }
21
+ catch (error) {
22
+ throw normalizeLocalCryptoError(error);
23
+ }
24
+ }
25
+ async getLocalMessages(chatId) {
26
+ try {
27
+ const rawMessages = await this.engine.runWithClient((client) => client.getLocalMessages(chatId));
28
+ return rawMessages.map((message) => mapLocalSdkMessage(message));
29
+ }
30
+ catch (error) {
31
+ throw normalizeLocalCryptoError(error);
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ import { HttpClient } from '../../core/http-client';
2
+ export declare class MessagesClient {
3
+ private readonly http;
4
+ constructor(http: HttpClient);
5
+ list(chatId: string, limit?: number, before?: string, after?: string): Promise<any>;
6
+ markDelivered(chatId: string, throughMessageId: string, limit?: number): Promise<any>;
7
+ markRead(chatId: string, throughMessageId: string, limit?: number): Promise<any>;
8
+ }
@@ -0,0 +1,24 @@
1
+ // TODO: Import proper types from contracts
2
+ export class MessagesClient {
3
+ http;
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+ async list(chatId, limit = 30, before, after) {
8
+ return this.http.get(`/link/v1/chats/${chatId}/messages`, {
9
+ params: { limit, before, after }
10
+ });
11
+ }
12
+ async markDelivered(chatId, throughMessageId, limit) {
13
+ return this.http.post(`/link/v1/chats/${chatId}/messages/delivered`, {
14
+ throughMessageId,
15
+ limit
16
+ });
17
+ }
18
+ async markRead(chatId, throughMessageId, limit) {
19
+ return this.http.post(`/link/v1/chats/${chatId}/messages/read`, {
20
+ throughMessageId,
21
+ limit
22
+ });
23
+ }
24
+ }
@@ -1,5 +1,5 @@
1
- import type { DecryptedMediaBlobResult } from './media-crypto.contracts';
2
- import type { MediaMessageAttachmentMetadata } from './media-message.contracts';
1
+ import type { DecryptedMediaBlobResult } from '../media/media-crypto.contracts';
2
+ import type { MediaMessageAttachmentMetadata } from '../media/media-message.contracts';
3
3
  export type DriveFilePreviewKind = 'image' | 'pdf' | 'text' | 'unsupported' | 'video';
4
4
  export interface DriveFileContentEnvelope {
5
5
  algorithm?: 'AES-256-GCM' | string | null;
@@ -1,4 +1,4 @@
1
- import type { DecryptedMediaBlobResult } from './media-crypto.contracts';
1
+ import type { DecryptedMediaBlobResult } from '../media/media-crypto.contracts';
2
2
  import type { DriveFileAttachmentInput, DriveFileMediaMetadata, DriveFilePreviewInput, DriveFilePreviewKind, DriveFileRuntimeMetadataInput, PreparedDriveFileAttachment, PreparedDriveFilePreview } from './drive-file-runtime.contracts';
3
3
  export declare function buildDriveFileMediaMetadata(input: DriveFileRuntimeMetadataInput): DriveFileMediaMetadata;
4
4
  export declare function getDriveFilePreviewKind(mimeType: string): DriveFilePreviewKind;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { WasmCryptoEngine } from '../../core/wasm-crypto-engine';
2
+ import type { DecryptDriveFileMetadataInput, DecryptDriveFolderMetadataInput, DecryptedDriveFileMetadata, DecryptedDriveFolderMetadata, EncryptedDriveFileMetadata, EncryptedDriveFolderMetadata, EncryptDriveFileMetadataInput, EncryptDriveFolderMetadataInput } from './storage-metadata-crypto.contracts';
3
+ import type { CreateDriveWrappedFolderKeyForRecipientInput, CreateDriveWrappedItemKeyForRecipientInput, DriveWrappedItemKeyForRecipientResult } from './drive-key-wrapping.contracts';
4
+ export declare class StorageCryptoClient {
5
+ private readonly engine;
6
+ constructor(engine: WasmCryptoEngine);
7
+ encryptDriveFolderMetadata(input: EncryptDriveFolderMetadataInput): Promise<EncryptedDriveFolderMetadata>;
8
+ decryptDriveFolderMetadata(input: DecryptDriveFolderMetadataInput): Promise<DecryptedDriveFolderMetadata>;
9
+ encryptDriveFileMetadata(input: EncryptDriveFileMetadataInput): Promise<EncryptedDriveFileMetadata>;
10
+ decryptDriveFileMetadata(input: DecryptDriveFileMetadataInput): Promise<DecryptedDriveFileMetadata>;
11
+ createDriveWrappedItemKeyForRecipient(input: CreateDriveWrappedItemKeyForRecipientInput): Promise<DriveWrappedItemKeyForRecipientResult>;
12
+ createDriveWrappedFolderKeyForRecipient(input: CreateDriveWrappedFolderKeyForRecipientInput): Promise<DriveWrappedItemKeyForRecipientResult>;
13
+ }
@@ -0,0 +1,73 @@
1
+ import { normalizeLocalCryptoError } from '../../errors/decryption-errors';
2
+ import { buildDecryptedDriveFileMetadata, buildDecryptedDriveFolderMetadata, buildEncryptedDriveFileMetadata, buildEncryptedDriveFolderMetadata, normalizeDriveFileMetadataInput, normalizeDriveFolderMetadataInput, normalizeEncryptedDriveFileMetadataInput, normalizeEncryptedDriveFolderMetadataInput, } from './storage-metadata-crypto';
3
+ import { createDriveWrappedFolderKeyForRecipient, createDriveWrappedItemKeyForRecipient, } from './drive-key-wrapping';
4
+ export class StorageCryptoClient {
5
+ engine;
6
+ constructor(engine) {
7
+ this.engine = engine;
8
+ }
9
+ async encryptDriveFolderMetadata(input) {
10
+ try {
11
+ const metadata = normalizeDriveFolderMetadataInput(input);
12
+ const encryptedName = await this.engine.queue.run(async () => {
13
+ const module = await this.engine.loadWasmModule();
14
+ return module.encryptStorageMetadata(metadata.name);
15
+ });
16
+ return buildEncryptedDriveFolderMetadata(encryptedName);
17
+ }
18
+ catch (error) {
19
+ throw normalizeLocalCryptoError(error);
20
+ }
21
+ }
22
+ async decryptDriveFolderMetadata(input) {
23
+ try {
24
+ const metadata = normalizeEncryptedDriveFolderMetadataInput(input);
25
+ const name = await this.engine.queue.run(async () => {
26
+ const module = await this.engine.loadWasmModule();
27
+ return module.decryptStorageMetadata(metadata.encryptedName);
28
+ });
29
+ return buildDecryptedDriveFolderMetadata(name);
30
+ }
31
+ catch (error) {
32
+ throw normalizeLocalCryptoError(error);
33
+ }
34
+ }
35
+ async encryptDriveFileMetadata(input) {
36
+ try {
37
+ const metadata = normalizeDriveFileMetadataInput(input);
38
+ const [encryptedName, encryptedMimeType] = await this.engine.queue.run(async () => {
39
+ const module = await this.engine.loadWasmModule();
40
+ return [
41
+ module.encryptStorageMetadata(metadata.name),
42
+ module.encryptStorageMetadata(metadata.mimeType),
43
+ ];
44
+ });
45
+ return buildEncryptedDriveFileMetadata(encryptedName, encryptedMimeType);
46
+ }
47
+ catch (error) {
48
+ throw normalizeLocalCryptoError(error);
49
+ }
50
+ }
51
+ async decryptDriveFileMetadata(input) {
52
+ try {
53
+ const metadata = normalizeEncryptedDriveFileMetadataInput(input);
54
+ const [name, mimeType] = await this.engine.queue.run(async () => {
55
+ const module = await this.engine.loadWasmModule();
56
+ return [
57
+ module.decryptStorageMetadata(metadata.encryptedName),
58
+ module.decryptStorageMetadata(metadata.encryptedMimeType),
59
+ ];
60
+ });
61
+ return buildDecryptedDriveFileMetadata(name, mimeType);
62
+ }
63
+ catch (error) {
64
+ throw normalizeLocalCryptoError(error);
65
+ }
66
+ }
67
+ createDriveWrappedItemKeyForRecipient(input) {
68
+ return createDriveWrappedItemKeyForRecipient(input);
69
+ }
70
+ createDriveWrappedFolderKeyForRecipient(input) {
71
+ return createDriveWrappedFolderKeyForRecipient(input);
72
+ }
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@msssystems/mss-link-sdk",
3
- "version": "0.1.12",
3
+ "version": "0.2.1",
4
4
  "description": "Official managed application SDK for MSS ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,46 +0,0 @@
1
- import type { MssLinkBrowserClientOptions } from './client-options';
2
- import type { LocalCryptoHealth } from './local-crypto-health.contracts';
3
- import type { DecryptMediaBlobInput, DecryptedMediaBlobResult, EncryptedMediaBlobResult, EncryptMediaBlobInput } from './media-crypto.contracts';
4
- import type { SendMediaMessageParams } from './media-message.contracts';
5
- import type { DecryptedMessage } from './message.contracts';
6
- import type { DecryptDriveFileMetadataInput, DecryptDriveFolderMetadataInput, DecryptedDriveFileMetadata, DecryptedDriveFolderMetadata, EncryptedDriveFileMetadata, EncryptedDriveFolderMetadata, EncryptDriveFileMetadataInput, EncryptDriveFolderMetadataInput } from './storage-metadata-crypto.contracts';
7
- import type { CreateDriveWrappedFolderKeyForRecipientInput, CreateDriveWrappedItemKeyForRecipientInput, DriveWrappedItemKeyForRecipientResult } from './drive-key-wrapping.contracts';
8
- export declare class MssLinkBrowserClient {
9
- private readonly userId;
10
- private readonly apiBaseUrl;
11
- private readonly deviceId;
12
- private readonly registrationId;
13
- private readonly signedPrekeyId;
14
- private readonly initialOneTimePrekeyCount;
15
- private readonly minOneTimePrekeys;
16
- private readonly replenishOneTimePrekeys;
17
- private readonly queue;
18
- private wasmClient;
19
- private wasmModulePromise;
20
- private accessToken;
21
- constructor(options: MssLinkBrowserClientOptions);
22
- setAccessToken(accessToken: string | null): void;
23
- ensureReady(): Promise<void>;
24
- getLocalCryptoHealth(): Promise<LocalCryptoHealth>;
25
- sendMessage(params: {
26
- chatId: string;
27
- targetUserId: string;
28
- text: string;
29
- kind?: string;
30
- }): Promise<string>;
31
- sendMediaMessage(params: SendMediaMessageParams): Promise<string>;
32
- encryptMediaBlob(input: EncryptMediaBlobInput): Promise<EncryptedMediaBlobResult>;
33
- decryptMediaBlob(input: DecryptMediaBlobInput): Promise<DecryptedMediaBlobResult>;
34
- encryptDriveFolderMetadata(input: EncryptDriveFolderMetadataInput): Promise<EncryptedDriveFolderMetadata>;
35
- decryptDriveFolderMetadata(input: DecryptDriveFolderMetadataInput): Promise<DecryptedDriveFolderMetadata>;
36
- encryptDriveFileMetadata(input: EncryptDriveFileMetadataInput): Promise<EncryptedDriveFileMetadata>;
37
- decryptDriveFileMetadata(input: DecryptDriveFileMetadataInput): Promise<DecryptedDriveFileMetadata>;
38
- createDriveWrappedItemKeyForRecipient(input: CreateDriveWrappedItemKeyForRecipientInput): Promise<DriveWrappedItemKeyForRecipientResult>;
39
- createDriveWrappedFolderKeyForRecipient(input: CreateDriveWrappedFolderKeyForRecipientInput): Promise<DriveWrappedItemKeyForRecipientResult>;
40
- syncMessages(): Promise<void>;
41
- getLocalMessages(chatId: string): Promise<DecryptedMessage[]>;
42
- reset(): void;
43
- private runWithClient;
44
- private getClient;
45
- private loadWasmModule;
46
- }
@@ -1,259 +0,0 @@
1
- import { normalizeProductApiBaseUrl } from './api-base-url';
2
- import { normalizeLocalCryptoError } from './decryption-errors';
3
- import { checkLocalCryptoHealth } from './local-crypto-health';
4
- import { buildMediaMessageMetadataPayload, extractMediaAssetIds, serializeMediaMessageMetadata, } from './media-message-metadata';
5
- import { mapLocalSdkMessage } from './message.mapper';
6
- import { buildDecryptedDriveFileMetadata, buildDecryptedDriveFolderMetadata, buildEncryptedDriveFileMetadata, buildEncryptedDriveFolderMetadata, normalizeDriveFileMetadataInput, normalizeDriveFolderMetadataInput, normalizeEncryptedDriveFileMetadataInput, normalizeEncryptedDriveFolderMetadataInput, } from './storage-metadata-crypto';
7
- import { createDriveWrappedFolderKeyForRecipient, createDriveWrappedItemKeyForRecipient, } from './drive-key-wrapping';
8
- import { WasmCallQueue } from './wasm-call-queue';
9
- const DEFAULT_DEVICE_ID = 1;
10
- const DEFAULT_REGISTRATION_ID = 1;
11
- const DEFAULT_SIGNED_PREKEY_ID = 1;
12
- const DEFAULT_ONE_TIME_PREKEY_COUNT = 10;
13
- const DEFAULT_MIN_ONE_TIME_PREKEYS = 5;
14
- const DEFAULT_REPLENISH_ONE_TIME_PREKEYS = 10;
15
- export class MssLinkBrowserClient {
16
- userId;
17
- apiBaseUrl;
18
- deviceId;
19
- registrationId;
20
- signedPrekeyId;
21
- initialOneTimePrekeyCount;
22
- minOneTimePrekeys;
23
- replenishOneTimePrekeys;
24
- queue = new WasmCallQueue();
25
- wasmClient = null;
26
- wasmModulePromise = null;
27
- accessToken;
28
- constructor(options) {
29
- this.userId = options.userId;
30
- this.apiBaseUrl = normalizeProductApiBaseUrl(options.apiBaseUrl);
31
- this.accessToken = options.accessToken ?? null;
32
- this.deviceId = options.deviceId ?? DEFAULT_DEVICE_ID;
33
- this.registrationId =
34
- options.registrationId ?? DEFAULT_REGISTRATION_ID;
35
- this.signedPrekeyId =
36
- options.signedPrekeyId ?? DEFAULT_SIGNED_PREKEY_ID;
37
- this.initialOneTimePrekeyCount =
38
- options.initialOneTimePrekeyCount ??
39
- DEFAULT_ONE_TIME_PREKEY_COUNT;
40
- this.minOneTimePrekeys =
41
- options.minOneTimePrekeys ?? DEFAULT_MIN_ONE_TIME_PREKEYS;
42
- this.replenishOneTimePrekeys =
43
- options.replenishOneTimePrekeys ??
44
- DEFAULT_REPLENISH_ONE_TIME_PREKEYS;
45
- }
46
- setAccessToken(accessToken) {
47
- this.accessToken = accessToken;
48
- this.wasmClient?.setAccessToken(accessToken);
49
- }
50
- async ensureReady() {
51
- try {
52
- await this.runWithClient(async (client) => {
53
- await client.ensureDeviceRegistered(this.registrationId, this.signedPrekeyId, this.initialOneTimePrekeyCount);
54
- await client.replenishOneTimePreKeys(this.minOneTimePrekeys, this.replenishOneTimePrekeys);
55
- });
56
- }
57
- catch (error) {
58
- throw normalizeLocalCryptoError(error);
59
- }
60
- }
61
- async getLocalCryptoHealth() {
62
- return checkLocalCryptoHealth(() => this.ensureReady());
63
- }
64
- async sendMessage(params) {
65
- try {
66
- await this.ensureReady();
67
- return this.runWithClient((client) => client.sendMessage(params.chatId, params.targetUserId, params.text, params.kind ?? 'TEXT'));
68
- }
69
- catch (error) {
70
- throw normalizeLocalCryptoError(error);
71
- }
72
- }
73
- async sendMediaMessage(params) {
74
- try {
75
- await this.ensureReady();
76
- const payload = buildMediaMessageMetadataPayload(params.attachments, params.caption);
77
- const metadataJson = serializeMediaMessageMetadata(payload);
78
- const mediaAssetIds = extractMediaAssetIds(params.attachments);
79
- return this.runWithClient((client) => client.sendMediaMessage(params.chatId, params.targetUserId, metadataJson, mediaAssetIds));
80
- }
81
- catch (error) {
82
- throw normalizeLocalCryptoError(error);
83
- }
84
- }
85
- async encryptMediaBlob(input) {
86
- try {
87
- const plaintext = new Uint8Array(await input.blob.arrayBuffer());
88
- const encrypted = await this.queue.run(async () => {
89
- const module = await this.loadWasmModule();
90
- return module.encryptMediaBytes(plaintext);
91
- });
92
- const encryptedBytes = new Uint8Array(encrypted.ciphertext);
93
- return {
94
- encryptedBlob: new Blob([encryptedBytes], {
95
- type: 'application/octet-stream',
96
- }),
97
- encryption: {
98
- schema: 'mss.link.media-encryption.v1',
99
- algorithm: 'AES-256-GCM',
100
- keyId: input.assetId,
101
- nonce: encrypted.nonce,
102
- wrappedKey: encrypted.key,
103
- },
104
- attachment: {
105
- assetId: input.assetId,
106
- mimeType: input.mimeType,
107
- size: input.size,
108
- ...(input.originalName ? { originalName: input.originalName } : {}),
109
- ...(input.uploadedAt ? { uploadedAt: input.uploadedAt } : {}),
110
- encryption: {
111
- schema: 'mss.link.media-encryption.v1',
112
- algorithm: 'AES-256-GCM',
113
- keyId: input.assetId,
114
- nonce: encrypted.nonce,
115
- wrappedKey: encrypted.key,
116
- },
117
- },
118
- };
119
- }
120
- catch (error) {
121
- throw normalizeLocalCryptoError(error);
122
- }
123
- }
124
- async decryptMediaBlob(input) {
125
- const encryption = input.metadata.encryption;
126
- if (!encryption) {
127
- return {
128
- blob: input.encryptedBlob,
129
- mimeType: input.metadata.mimeType,
130
- ...(input.metadata.originalName
131
- ? { fileName: input.metadata.originalName }
132
- : {}),
133
- };
134
- }
135
- try {
136
- const ciphertext = new Uint8Array(await input.encryptedBlob.arrayBuffer());
137
- const plaintext = await this.queue.run(async () => {
138
- const module = await this.loadWasmModule();
139
- return module.decryptMediaBytes(ciphertext, encryption.wrappedKey, encryption.nonce);
140
- });
141
- return {
142
- blob: new Blob([new Uint8Array(plaintext)], {
143
- type: input.metadata.mimeType,
144
- }),
145
- mimeType: input.metadata.mimeType,
146
- ...(input.metadata.originalName
147
- ? { fileName: input.metadata.originalName }
148
- : {}),
149
- };
150
- }
151
- catch (error) {
152
- throw normalizeLocalCryptoError(error);
153
- }
154
- }
155
- async encryptDriveFolderMetadata(input) {
156
- try {
157
- const metadata = normalizeDriveFolderMetadataInput(input);
158
- const encryptedName = await this.queue.run(async () => {
159
- const module = await this.loadWasmModule();
160
- return module.encryptStorageMetadata(metadata.name);
161
- });
162
- return buildEncryptedDriveFolderMetadata(encryptedName);
163
- }
164
- catch (error) {
165
- throw normalizeLocalCryptoError(error);
166
- }
167
- }
168
- async decryptDriveFolderMetadata(input) {
169
- try {
170
- const metadata = normalizeEncryptedDriveFolderMetadataInput(input);
171
- const name = await this.queue.run(async () => {
172
- const module = await this.loadWasmModule();
173
- return module.decryptStorageMetadata(metadata.encryptedName);
174
- });
175
- return buildDecryptedDriveFolderMetadata(name);
176
- }
177
- catch (error) {
178
- throw normalizeLocalCryptoError(error);
179
- }
180
- }
181
- async encryptDriveFileMetadata(input) {
182
- try {
183
- const metadata = normalizeDriveFileMetadataInput(input);
184
- const [encryptedName, encryptedMimeType] = await this.queue.run(async () => {
185
- const module = await this.loadWasmModule();
186
- return [
187
- module.encryptStorageMetadata(metadata.name),
188
- module.encryptStorageMetadata(metadata.mimeType),
189
- ];
190
- });
191
- return buildEncryptedDriveFileMetadata(encryptedName, encryptedMimeType);
192
- }
193
- catch (error) {
194
- throw normalizeLocalCryptoError(error);
195
- }
196
- }
197
- async decryptDriveFileMetadata(input) {
198
- try {
199
- const metadata = normalizeEncryptedDriveFileMetadataInput(input);
200
- const [name, mimeType] = await this.queue.run(async () => {
201
- const module = await this.loadWasmModule();
202
- return [
203
- module.decryptStorageMetadata(metadata.encryptedName),
204
- module.decryptStorageMetadata(metadata.encryptedMimeType),
205
- ];
206
- });
207
- return buildDecryptedDriveFileMetadata(name, mimeType);
208
- }
209
- catch (error) {
210
- throw normalizeLocalCryptoError(error);
211
- }
212
- }
213
- createDriveWrappedItemKeyForRecipient(input) {
214
- return createDriveWrappedItemKeyForRecipient(input);
215
- }
216
- createDriveWrappedFolderKeyForRecipient(input) {
217
- return createDriveWrappedFolderKeyForRecipient(input);
218
- }
219
- async syncMessages() {
220
- try {
221
- await this.runWithClient((client) => client.syncMessages());
222
- }
223
- catch (error) {
224
- throw normalizeLocalCryptoError(error);
225
- }
226
- }
227
- async getLocalMessages(chatId) {
228
- try {
229
- const rawMessages = await this.runWithClient((client) => client.getLocalMessages(chatId));
230
- return rawMessages.map((message) => mapLocalSdkMessage(message));
231
- }
232
- catch (error) {
233
- throw normalizeLocalCryptoError(error);
234
- }
235
- }
236
- reset() {
237
- this.wasmClient?.free();
238
- this.wasmClient = null;
239
- this.wasmModulePromise = null;
240
- }
241
- async runWithClient(callback) {
242
- return this.queue.run(async () => callback(await this.getClient()));
243
- }
244
- async getClient() {
245
- if (typeof window === 'undefined') {
246
- throw new Error('MSS Link browser client is available only in browser context.');
247
- }
248
- if (!this.wasmClient) {
249
- const sdkModule = await this.loadWasmModule();
250
- this.wasmClient = new sdkModule.WasmMssClient(this.userId, this.deviceId, this.apiBaseUrl);
251
- this.wasmClient.setAccessToken(this.accessToken);
252
- }
253
- return this.wasmClient;
254
- }
255
- loadWasmModule() {
256
- this.wasmModulePromise ??= import('@msssystems/mss-crypto-wasm').then((module) => module);
257
- return this.wasmModulePromise;
258
- }
259
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes