@msssystems/mss-link-sdk 0.1.7 → 0.1.9
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.
- package/dist/client/drive-file-runtime.contracts.d.ts +39 -0
- package/dist/client/drive-file-runtime.contracts.js +1 -0
- package/dist/client/drive-file-runtime.d.ts +8 -0
- package/dist/client/drive-file-runtime.js +94 -0
- package/dist/client/drive-sharing.contracts.d.ts +64 -0
- package/dist/client/drive-sharing.contracts.js +1 -0
- package/dist/client/drive-sharing.d.ts +5 -0
- package/dist/client/drive-sharing.js +51 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DecryptedMediaBlobResult } from './media-crypto.contracts';
|
|
2
|
+
import type { MediaMessageAttachmentMetadata } from './media-message.contracts';
|
|
3
|
+
export type DriveFilePreviewKind = 'image' | 'pdf' | 'text' | 'unsupported' | 'video';
|
|
4
|
+
export interface DriveFileContentEnvelope {
|
|
5
|
+
algorithm?: 'AES-256-GCM' | string | null;
|
|
6
|
+
keyId?: string | null;
|
|
7
|
+
nonce?: string | null;
|
|
8
|
+
wrappedKey?: string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface DriveFileRuntimeMetadataInput {
|
|
11
|
+
fileName: string;
|
|
12
|
+
mimeType: string;
|
|
13
|
+
size: number | string;
|
|
14
|
+
storageKey: string;
|
|
15
|
+
encryption: DriveFileContentEnvelope;
|
|
16
|
+
}
|
|
17
|
+
export interface DriveFileAttachmentInput {
|
|
18
|
+
decrypted: DecryptedMediaBlobResult;
|
|
19
|
+
fallbackFileName?: string;
|
|
20
|
+
fallbackMimeType?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DriveFilePreviewInput {
|
|
23
|
+
decrypted: DecryptedMediaBlobResult;
|
|
24
|
+
fallbackFileName?: string;
|
|
25
|
+
fallbackMimeType?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface PreparedDriveFileAttachment {
|
|
28
|
+
file: File;
|
|
29
|
+
fileName: string;
|
|
30
|
+
mimeType: string;
|
|
31
|
+
}
|
|
32
|
+
export interface PreparedDriveFilePreview {
|
|
33
|
+
blob: Blob;
|
|
34
|
+
fileName: string;
|
|
35
|
+
kind: DriveFilePreviewKind;
|
|
36
|
+
mimeType: string;
|
|
37
|
+
text?: string;
|
|
38
|
+
}
|
|
39
|
+
export type DriveFileMediaMetadata = MediaMessageAttachmentMetadata;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DecryptedMediaBlobResult } from './media-crypto.contracts';
|
|
2
|
+
import type { DriveFileAttachmentInput, DriveFileMediaMetadata, DriveFilePreviewInput, DriveFilePreviewKind, DriveFileRuntimeMetadataInput, PreparedDriveFileAttachment, PreparedDriveFilePreview } from './drive-file-runtime.contracts';
|
|
3
|
+
export declare function buildDriveFileMediaMetadata(input: DriveFileRuntimeMetadataInput): DriveFileMediaMetadata;
|
|
4
|
+
export declare function getDriveFilePreviewKind(mimeType: string): DriveFilePreviewKind;
|
|
5
|
+
export declare function prepareDriveFileAttachment({ decrypted, fallbackFileName, fallbackMimeType, }: DriveFileAttachmentInput): PreparedDriveFileAttachment;
|
|
6
|
+
export declare function prepareDriveFilePreview({ decrypted, fallbackFileName, fallbackMimeType, }: DriveFilePreviewInput): Promise<PreparedDriveFilePreview>;
|
|
7
|
+
export declare function getDecryptedFileName(decrypted: DecryptedMediaBlobResult, fallbackFileName?: string): string;
|
|
8
|
+
export declare function getDecryptedMimeType(decrypted: DecryptedMediaBlobResult, fallbackMimeType?: string): string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const DEFAULT_FILE_NAME = 'attachment';
|
|
2
|
+
const DEFAULT_MIME_TYPE = 'application/octet-stream';
|
|
3
|
+
export function buildDriveFileMediaMetadata(input) {
|
|
4
|
+
const fileName = normalizeOptionalText(input.fileName) ?? DEFAULT_FILE_NAME;
|
|
5
|
+
const mimeType = normalizeOptionalText(input.mimeType) ?? DEFAULT_MIME_TYPE;
|
|
6
|
+
const nonce = normalizeRequiredText(input.encryption.nonce, 'Drive file encryption nonce is required.');
|
|
7
|
+
const wrappedKey = normalizeRequiredText(input.encryption.wrappedKey, 'Drive file wrapped content key is required.');
|
|
8
|
+
return {
|
|
9
|
+
assetId: normalizeRequiredText(input.storageKey, 'Drive file storage key is required.'),
|
|
10
|
+
mimeType,
|
|
11
|
+
originalName: fileName,
|
|
12
|
+
size: String(input.size),
|
|
13
|
+
encryption: {
|
|
14
|
+
schema: 'mss.link.media-encryption.v1',
|
|
15
|
+
algorithm: normalizeDriveFileContentAlgorithm(input.encryption.algorithm),
|
|
16
|
+
...(input.encryption.keyId ? { keyId: input.encryption.keyId } : {}),
|
|
17
|
+
nonce,
|
|
18
|
+
wrappedKey,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function getDriveFilePreviewKind(mimeType) {
|
|
23
|
+
const normalizedMimeType = normalizeOptionalText(mimeType) ?? DEFAULT_MIME_TYPE;
|
|
24
|
+
if (normalizedMimeType.startsWith('image/')) {
|
|
25
|
+
return 'image';
|
|
26
|
+
}
|
|
27
|
+
if (normalizedMimeType.startsWith('video/')) {
|
|
28
|
+
return 'video';
|
|
29
|
+
}
|
|
30
|
+
if (normalizedMimeType === 'application/pdf') {
|
|
31
|
+
return 'pdf';
|
|
32
|
+
}
|
|
33
|
+
if (normalizedMimeType.startsWith('text/') ||
|
|
34
|
+
normalizedMimeType.includes('json')) {
|
|
35
|
+
return 'text';
|
|
36
|
+
}
|
|
37
|
+
return 'unsupported';
|
|
38
|
+
}
|
|
39
|
+
export function prepareDriveFileAttachment({ decrypted, fallbackFileName, fallbackMimeType, }) {
|
|
40
|
+
const fileName = getDecryptedFileName(decrypted, fallbackFileName);
|
|
41
|
+
const mimeType = getDecryptedMimeType(decrypted, fallbackMimeType);
|
|
42
|
+
return {
|
|
43
|
+
file: new File([decrypted.blob], fileName, { type: mimeType }),
|
|
44
|
+
fileName,
|
|
45
|
+
mimeType,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export async function prepareDriveFilePreview({ decrypted, fallbackFileName, fallbackMimeType, }) {
|
|
49
|
+
const fileName = getDecryptedFileName(decrypted, fallbackFileName);
|
|
50
|
+
const mimeType = getDecryptedMimeType(decrypted, fallbackMimeType);
|
|
51
|
+
const kind = getDriveFilePreviewKind(mimeType);
|
|
52
|
+
if (kind === 'text') {
|
|
53
|
+
return {
|
|
54
|
+
blob: decrypted.blob,
|
|
55
|
+
fileName,
|
|
56
|
+
kind,
|
|
57
|
+
mimeType,
|
|
58
|
+
text: await decrypted.blob.text(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
blob: decrypted.blob,
|
|
63
|
+
fileName,
|
|
64
|
+
kind,
|
|
65
|
+
mimeType,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function getDecryptedFileName(decrypted, fallbackFileName) {
|
|
69
|
+
return (normalizeOptionalText(decrypted.fileName) ??
|
|
70
|
+
normalizeOptionalText(fallbackFileName) ??
|
|
71
|
+
DEFAULT_FILE_NAME);
|
|
72
|
+
}
|
|
73
|
+
export function getDecryptedMimeType(decrypted, fallbackMimeType) {
|
|
74
|
+
return (normalizeOptionalText(decrypted.mimeType) ??
|
|
75
|
+
normalizeOptionalText(fallbackMimeType) ??
|
|
76
|
+
DEFAULT_MIME_TYPE);
|
|
77
|
+
}
|
|
78
|
+
function normalizeDriveFileContentAlgorithm(algorithm) {
|
|
79
|
+
if (!algorithm || algorithm === 'AES-256-GCM') {
|
|
80
|
+
return 'AES-256-GCM';
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Unsupported Drive file encryption algorithm: ${algorithm}`);
|
|
83
|
+
}
|
|
84
|
+
function normalizeRequiredText(value, message) {
|
|
85
|
+
const normalized = normalizeOptionalText(value);
|
|
86
|
+
if (!normalized) {
|
|
87
|
+
throw new Error(message);
|
|
88
|
+
}
|
|
89
|
+
return normalized;
|
|
90
|
+
}
|
|
91
|
+
function normalizeOptionalText(value) {
|
|
92
|
+
const normalized = value?.trim();
|
|
93
|
+
return normalized ? normalized : undefined;
|
|
94
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type DrivePermissionRole = 'editor' | 'owner' | 'viewer';
|
|
2
|
+
export type DriveEditablePermissionRole = Exclude<DrivePermissionRole, 'owner'>;
|
|
3
|
+
export type DriveVisibility = 'private' | 'shared';
|
|
4
|
+
export type DriveItemType = 'file' | 'folder';
|
|
5
|
+
export type DriveShareTargetType = 'user';
|
|
6
|
+
export interface DriveShareTarget {
|
|
7
|
+
type: DriveShareTargetType;
|
|
8
|
+
userId?: string;
|
|
9
|
+
username?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface DriveWrappedItemKey {
|
|
12
|
+
encryptedItemKey: string;
|
|
13
|
+
keyAlgorithm?: string;
|
|
14
|
+
keyNonce?: string;
|
|
15
|
+
keyRecipientId?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CreateDriveShareGrantInput {
|
|
18
|
+
role: DriveEditablePermissionRole;
|
|
19
|
+
target: DriveShareTarget;
|
|
20
|
+
wrappedItemKey: DriveWrappedItemKey;
|
|
21
|
+
}
|
|
22
|
+
export interface DriveShareGrantPayload {
|
|
23
|
+
encryptedItemKey: string;
|
|
24
|
+
keyAlgorithm?: string;
|
|
25
|
+
keyNonce?: string;
|
|
26
|
+
keyRecipientId?: string;
|
|
27
|
+
role: 'EDITOR' | 'VIEWER';
|
|
28
|
+
target: {
|
|
29
|
+
type: 'user';
|
|
30
|
+
userId?: string;
|
|
31
|
+
username?: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface DriveAccessPrincipal {
|
|
35
|
+
accountId: string;
|
|
36
|
+
avatarUrl: string | null;
|
|
37
|
+
displayName: string | null;
|
|
38
|
+
userId: string | null;
|
|
39
|
+
username: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface DriveAccessGrant {
|
|
42
|
+
createdAt: string;
|
|
43
|
+
encryptedItemKey: string;
|
|
44
|
+
fileId: string | null;
|
|
45
|
+
folderId: string | null;
|
|
46
|
+
grantee: DriveAccessPrincipal;
|
|
47
|
+
granteeAccountId: string;
|
|
48
|
+
id: string;
|
|
49
|
+
itemType: 'FILE' | 'FOLDER';
|
|
50
|
+
keyAlgorithm: string;
|
|
51
|
+
keyNonce: string | null;
|
|
52
|
+
keyRecipientId: string | null;
|
|
53
|
+
ownerAccountId: string;
|
|
54
|
+
role: 'EDITOR' | 'OWNER' | 'VIEWER';
|
|
55
|
+
updatedAt: string;
|
|
56
|
+
}
|
|
57
|
+
export interface DriveAccessResponse {
|
|
58
|
+
currentUserRole: 'EDITOR' | 'OWNER' | 'VIEWER';
|
|
59
|
+
grants: DriveAccessGrant[];
|
|
60
|
+
itemId: string;
|
|
61
|
+
itemType: 'FILE' | 'FOLDER';
|
|
62
|
+
owner: DriveAccessPrincipal;
|
|
63
|
+
visibility: 'PRIVATE' | 'SHARED';
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CreateDriveShareGrantInput, DriveEditablePermissionRole, DriveShareGrantPayload, DriveShareTarget, DriveWrappedItemKey } from './drive-sharing.contracts';
|
|
2
|
+
export declare function createDriveShareGrantPayload(input: CreateDriveShareGrantInput): DriveShareGrantPayload;
|
|
3
|
+
export declare function normalizeDriveShareTarget(target: DriveShareTarget): DriveShareGrantPayload['target'];
|
|
4
|
+
export declare function normalizeWrappedItemKey(key: DriveWrappedItemKey): Omit<DriveShareGrantPayload, 'role' | 'target'>;
|
|
5
|
+
export declare function toApiDrivePermissionRole(role: DriveEditablePermissionRole): DriveShareGrantPayload['role'];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const DEFAULT_KEY_ALGORITHM = 'X25519-AES-256-GCM';
|
|
2
|
+
export function createDriveShareGrantPayload(input) {
|
|
3
|
+
return {
|
|
4
|
+
...normalizeWrappedItemKey(input.wrappedItemKey),
|
|
5
|
+
role: toApiDrivePermissionRole(input.role),
|
|
6
|
+
target: normalizeDriveShareTarget(input.target),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function normalizeDriveShareTarget(target) {
|
|
10
|
+
if (target.type !== 'user') {
|
|
11
|
+
throw new Error('Drive sharing supports only selected users in v1.');
|
|
12
|
+
}
|
|
13
|
+
const userId = target.userId?.trim();
|
|
14
|
+
const username = target.username?.trim().replace(/^@/, '').toLowerCase();
|
|
15
|
+
if (!userId && !username) {
|
|
16
|
+
throw new Error('Drive share target must include userId or username.');
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
type: 'user',
|
|
20
|
+
...(userId ? { userId } : {}),
|
|
21
|
+
...(username ? { username } : {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function normalizeWrappedItemKey(key) {
|
|
25
|
+
const encryptedItemKey = key.encryptedItemKey.trim();
|
|
26
|
+
const keyAlgorithm = (key.keyAlgorithm?.trim() || DEFAULT_KEY_ALGORITHM).trim();
|
|
27
|
+
const keyNonce = key.keyNonce?.trim();
|
|
28
|
+
const keyRecipientId = key.keyRecipientId?.trim();
|
|
29
|
+
if (!encryptedItemKey) {
|
|
30
|
+
throw new Error('Drive share requires encrypted wrapped item key.');
|
|
31
|
+
}
|
|
32
|
+
if (!keyAlgorithm) {
|
|
33
|
+
throw new Error('Drive share key algorithm is required.');
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
encryptedItemKey,
|
|
37
|
+
keyAlgorithm,
|
|
38
|
+
...(keyNonce ? { keyNonce } : {}),
|
|
39
|
+
...(keyRecipientId ? { keyRecipientId } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function toApiDrivePermissionRole(role) {
|
|
43
|
+
switch (role) {
|
|
44
|
+
case 'editor':
|
|
45
|
+
return 'EDITOR';
|
|
46
|
+
case 'viewer':
|
|
47
|
+
return 'VIEWER';
|
|
48
|
+
default:
|
|
49
|
+
throw new Error('Drive owner role cannot be granted through sharing.');
|
|
50
|
+
}
|
|
51
|
+
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export * from './api-base-url';
|
|
2
2
|
export * from './client-options';
|
|
3
3
|
export * from './decryption-errors';
|
|
4
|
+
export * from './drive-file-runtime';
|
|
5
|
+
export * from './drive-file-runtime.contracts';
|
|
6
|
+
export * from './drive-sharing';
|
|
7
|
+
export * from './drive-sharing.contracts';
|
|
4
8
|
export * from './local-crypto-health';
|
|
5
9
|
export * from './local-crypto-health.contracts';
|
|
6
10
|
export * from './media-crypto.contracts';
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export * from './api-base-url';
|
|
2
2
|
export * from './client-options';
|
|
3
3
|
export * from './decryption-errors';
|
|
4
|
+
export * from './drive-file-runtime';
|
|
5
|
+
export * from './drive-file-runtime.contracts';
|
|
6
|
+
export * from './drive-sharing';
|
|
7
|
+
export * from './drive-sharing.contracts';
|
|
4
8
|
export * from './local-crypto-health';
|
|
5
9
|
export * from './local-crypto-health.contracts';
|
|
6
10
|
export * from './media-crypto.contracts';
|