@secrecy/lib 1.62.0-feat-storage-providers.4 → 1.62.0-feat-node-sharing.2

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/lib/cache.js CHANGED
@@ -2,6 +2,7 @@ import { LRUCache } from 'lru-cache';
2
2
  import { gigaToBytes } from './utils.js';
3
3
  export const dataCache = new Map();
4
4
  export const nodesCache = new Map();
5
+ export const nodesEncryptionCache = new Map();
5
6
  export const usersCache = new Map();
6
7
  export const publicKeysCache = new Map();
7
8
  export const dataContentCache = new LRUCache({
@@ -63,17 +63,32 @@ export class SecrecyAppClient {
63
63
  async notifications() {
64
64
  return await this.#apiClient.application.notification.query();
65
65
  }
66
- async userPublicKey(userId) {
67
- const key = `userPublicKey:${userId}-${this.jwtDecoded.aud?.toString()}`;
68
- const cachedPublicKey = publicKeysCache.get(key);
69
- if (cachedPublicKey !== undefined) {
70
- return cachedPublicKey;
71
- }
72
- const { publicKey } = await this.#apiClient.application.userPublicKey.query({
66
+ async userPublicKey(input) {
67
+ const appId = this.jwtDecoded.aud?.toString();
68
+ const userIds = Array.isArray(input) ? input : [input];
69
+ const publicKeys = Object.fromEntries(userIds
70
+ .map((userId) => [
73
71
  userId,
74
- });
75
- publicKeysCache.set(key, publicKey);
76
- return publicKey;
72
+ publicKeysCache.get(`userPublicKey:${userId}-${appId}`),
73
+ ])
74
+ .filter(([_, key]) => !!key));
75
+ const missingKeys = [
76
+ ...new Set(userIds.filter((userId) => publicKeys[userId] === undefined)),
77
+ ];
78
+ if (missingKeys.length > 0) {
79
+ const userKeysMap = await this.#apiClient.application.userPublicKey.query(userIds);
80
+ if ('publicKey' in userKeysMap) {
81
+ throw Error('Should not happen!');
82
+ }
83
+ if (Object.keys(userKeysMap).length !== missingKeys.length) {
84
+ throw new Error("Unable to load some user's public keys!");
85
+ }
86
+ for (const userId in userKeysMap) {
87
+ publicKeys[userId] = userKeysMap[userId];
88
+ publicKeysCache.set(`userPublicKey:${userId}-${appId}`, userKeysMap[userId]);
89
+ }
90
+ }
91
+ return Array.isArray(input) ? publicKeys : publicKeys[input];
77
92
  }
78
93
  async settings() {
79
94
  const settings = await this.#apiClient.application.settings.query({});
@@ -1,7 +1,7 @@
1
1
  import axios from 'axios';
2
2
  import ky from 'ky';
3
3
  import { encryptName } from '../index.js';
4
- import { nodesCache, dataCache, dataContentCache } from '../cache.js';
4
+ import { nodesCache, dataCache, dataContentCache, nodesEncryptionCache, } from '../cache.js';
5
5
  import { secretStreamKeygen } from '../crypto/data.js';
6
6
  import { decryptCryptoBox, encryptCryptoBox } from '../crypto/index.js';
7
7
  import { compress, decompress } from '../minify/index.js';
@@ -13,8 +13,6 @@ import { apiDataToExternal, apiDataToInternal } from './convert/data.js';
13
13
  import { apiNodeFullToInternalFull, apiNodeToExternal, apiNodeToExternalNodeFull, internalNodeFullToNodeFull, } from './convert/node.js';
14
14
  import { promiseAllLimit } from '../utils/promise.js';
15
15
  import { kiloToBytes } from '../utils.js';
16
- // import { md5 } from "../worker/index.js";
17
- // import { firstValueFrom, of } from "rxjs";
18
16
  import { fileTypeFromBuffer } from 'file-type';
19
17
  export class SecrecyCloudClient {
20
18
  #client;
@@ -50,7 +48,7 @@ export class SecrecyCloudClient {
50
48
  }
51
49
  return internalNodeFullToNodeFull(node);
52
50
  }
53
- async uploadData({ storage, data, encrypted = true, encryptProgress, uploadProgress, signal, meta, }) {
51
+ async uploadData({ storageType, data, encrypted = true, encryptProgress, uploadProgress, signal, meta, }) {
54
52
  const dataBuffer = data instanceof File ? new Uint8Array(await data.arrayBuffer()) : data;
55
53
  let filetype;
56
54
  if (meta === true) {
@@ -62,8 +60,7 @@ export class SecrecyCloudClient {
62
60
  else if (!encrypted) {
63
61
  filetype = await fileTypeFromBuffer(dataBuffer);
64
62
  }
65
- if (storage.protocol === 'mongo' &&
66
- dataBuffer.byteLength > kiloToBytes(1024)) {
63
+ if (storageType === 'lite' && dataBuffer.byteLength > kiloToBytes(1024)) {
67
64
  throw new Error('The data is too big for lite upload!');
68
65
  }
69
66
  const compressed = encrypted ? compress(dataBuffer) : dataBuffer;
@@ -82,11 +79,11 @@ export class SecrecyCloudClient {
82
79
  current: 0,
83
80
  percent: 0,
84
81
  });
85
- if (storage.protocol === 'mongo') {
82
+ if (storageType === 'lite') {
86
83
  const uploadDataArgs = encryptedDataKey && md5Encrypted
87
84
  ? {
88
85
  type: 'encrypted',
89
- bytes: Buffer.from(encryptedData),
86
+ content: Buffer.from(encryptedData),
90
87
  sizeEncrypted: BigInt(encryptedData.byteLength),
91
88
  size: BigInt(dataBuffer.byteLength),
92
89
  key: sodium.to_hex(encryptedDataKey),
@@ -96,13 +93,13 @@ export class SecrecyCloudClient {
96
93
  }
97
94
  : {
98
95
  type: 'unencrypted',
99
- bytes: Buffer.from(encryptedData),
96
+ content: Buffer.from(encryptedData),
100
97
  md5: md5Data,
101
98
  sizeEncrypted: undefined,
102
99
  size: BigInt(dataBuffer.byteLength),
103
100
  ...filetype,
104
101
  };
105
- const uploadData = await this.#apiClient.cloud.uploadMongoData.mutate(uploadDataArgs, { signal });
102
+ const uploadData = await this.#apiClient.cloud.uploadLiteData.mutate(uploadDataArgs, { signal });
106
103
  await uploadProgress?.({
107
104
  total: encryptedData.byteLength,
108
105
  current: encryptedData.byteLength,
@@ -110,7 +107,7 @@ export class SecrecyCloudClient {
110
107
  });
111
108
  const localData = {
112
109
  id: uploadData.id,
113
- storage: storage,
110
+ storageType: 'lite',
114
111
  size: uploadDataArgs.size,
115
112
  sizeEncrypted: uploadDataArgs.sizeEncrypted ?? null,
116
113
  data: dataBuffer,
@@ -119,7 +116,7 @@ export class SecrecyCloudClient {
119
116
  dataContentCache.set(uploadData.id, localData);
120
117
  return localData;
121
118
  }
122
- if (storage.protocol === 's3') {
119
+ if (storageType === 's3' || storageType === 'cold') {
123
120
  const uploadDataArgs = encryptedDataKey && md5Encrypted
124
121
  ? {
125
122
  type: 'encrypted',
@@ -137,7 +134,7 @@ export class SecrecyCloudClient {
137
134
  sizeEncrypted: undefined,
138
135
  ...filetype,
139
136
  };
140
- const uploadDataCaller = storage.mode === 'standard'
137
+ const uploadDataCaller = storageType === 's3'
141
138
  ? this.#apiClient.cloud.uploadData
142
139
  : this.#apiClient.cloud.uploadColdData;
143
140
  const uploadData = await uploadDataCaller.mutate(uploadDataArgs, {
@@ -154,7 +151,7 @@ export class SecrecyCloudClient {
154
151
  });
155
152
  const localData = {
156
153
  id: uploadData.id,
157
- storage: storage,
154
+ storageType: storageType,
158
155
  size: uploadDataArgs.size,
159
156
  sizeEncrypted: uploadDataArgs.sizeEncrypted ?? null,
160
157
  data: dataBuffer,
@@ -211,7 +208,7 @@ export class SecrecyCloudClient {
211
208
  }));
212
209
  const localData = {
213
210
  id: uploadData.id,
214
- storage: storage,
211
+ storageType: storageType,
215
212
  size: uploadDataArgs.size,
216
213
  sizeEncrypted: uploadDataArgs.sizeEncrypted ?? null,
217
214
  data: dataBuffer,
@@ -220,11 +217,11 @@ export class SecrecyCloudClient {
220
217
  dataContentCache.set(uploadData.id, localData);
221
218
  return localData;
222
219
  }
223
- throw new Error(`Not implemented yet!`);
220
+ throw new Error(`The "${storageType}" is not implemented yet!`);
224
221
  }
225
- async uploadDataInCloud({ data, name, nodeId, encryptProgress, uploadProgress, storage, signal, }) {
222
+ async uploadDataInCloud({ data, name, nodeId, encryptProgress, uploadProgress, storageType = 's3', signal, }) {
226
223
  const uploadedData = await this.uploadData({
227
- storage,
224
+ storageType,
228
225
  data,
229
226
  encryptProgress,
230
227
  uploadProgress,
@@ -299,11 +296,10 @@ export class SecrecyCloudClient {
299
296
  const users = folder.parent?.users?.filter(([u]) => u.id !== this.#client.app.userId) ??
300
297
  [];
301
298
  if (users.length > 0) {
302
- await Promise.all(users.map(async ([u, rights]) => await this.shareNode({
299
+ await this.shareNode({
303
300
  nodeId: folder.id,
304
- rights,
305
- userId: u.id,
306
- })));
301
+ users: users.map(([user, rights]) => ({ id: user.id, rights })),
302
+ });
307
303
  }
308
304
  return folder;
309
305
  }
@@ -320,25 +316,35 @@ export class SecrecyCloudClient {
320
316
  });
321
317
  return apiDataToExternal(data, this.#keys);
322
318
  }
323
- async shareNode({ nodeId, userId, rights, }) {
324
- const publicKey = await this.#client.app.userPublicKey(userId);
325
- const { ids } = await this.#apiClient.cloud.shareNode.mutate({
326
- nodeId,
327
- userId,
328
- });
329
- const shareNodes = [];
330
- for (const id of ids) {
331
- const shareNode = await this.perNode(id, publicKey);
332
- if (shareNode !== null) {
333
- shareNodes.push(shareNode);
334
- }
335
- }
336
- const { isFinished } = await this.#apiClient.cloud.shareNodeFinish.mutate({
337
- rights,
338
- userId,
339
- nodes: shareNodes,
340
- });
341
- return isFinished;
319
+ async shareNode(input) {
320
+ const userIds = 'rights' in input
321
+ ? Array.isArray(input.nodes)
322
+ ? input.nodes.map(({ userId }) => userId)
323
+ : 'userIds' in input.nodes
324
+ ? input.nodes.userIds
325
+ : [input.nodes.userId]
326
+ : input.users.map(({ id }) => id);
327
+ const nodesToShare = 'nodes' in input
328
+ ? input.nodes
329
+ : {
330
+ nodeId: input.nodeId,
331
+ userIds: input.users.map(({ id }) => id),
332
+ };
333
+ const [publicKeysMap, nodesIdsMap] = await Promise.all([
334
+ this.#client.app.userPublicKey(userIds),
335
+ this.#apiClient.cloud.shareNode.mutate(nodesToShare),
336
+ ]);
337
+ const nodesInfos = await this.encryptNodesForUsers(nodesIdsMap, publicKeysMap);
338
+ const sharingState = await this.#apiClient.cloud.shareNodeFinish.mutate(Object.entries(nodesInfos).map('rights' in input
339
+ ? ([userId, nodes]) => ({ userId, nodes, rights: input.rights })
340
+ : ([userId, nodes]) => {
341
+ const user = input.users.find((user) => user.id === userId);
342
+ if (!user) {
343
+ throw new Error(`Unable to find rights for user: ${userId}`);
344
+ }
345
+ return { userId, nodes, rights: user.rights };
346
+ }));
347
+ return sharingState;
342
348
  }
343
349
  async updateNode({ nodeId, name, isFavorite, deletedAt, }) {
344
350
  let node = nodesCache.get(nodeId);
@@ -398,13 +404,13 @@ export class SecrecyCloudClient {
398
404
  id: data.id,
399
405
  size: data.size,
400
406
  sizeEncrypted: data.sizeEncrypted,
401
- storage: data.storage,
407
+ storageType: data.storageType,
402
408
  })),
403
409
  ...cachedData.map((data) => ({
404
410
  id: data.id,
405
411
  size: data.size,
406
412
  sizeEncrypted: data.sizeEncrypted,
407
- storage: data.storage,
413
+ storageType: data.storageType,
408
414
  })),
409
415
  ];
410
416
  const allDataContentsBytes = Number(allDataContents.reduce((curr, next) => curr + (next.sizeEncrypted ?? next.size), 0n));
@@ -523,42 +529,102 @@ export class SecrecyCloudClient {
523
529
  if (me !== undefined && ['admin', 'write'].includes(me[1])) {
524
530
  const others = node.parent?.users.filter(([u]) => u.id !== this.#client.app.userId) ??
525
531
  [];
526
- await Promise.all(others.map(async ([u, rights]) => await this.shareNode({
527
- nodeId: node.id,
528
- rights,
529
- userId: u.id,
530
- })));
532
+ if (others.length > 0) {
533
+ await this.shareNode({
534
+ nodeId: node.id,
535
+ users: others.map(([user, rights]) => ({ id: user.id, rights })),
536
+ });
537
+ }
531
538
  }
532
539
  return node;
533
540
  }
534
- perNode = async (nodeId, publicKey) => {
535
- let node = nodesCache.get(nodeId);
536
- if (node === undefined || (node.type === 'FILE' && !('history' in node))) {
537
- await this.node({ id: nodeId });
538
- node = nodesCache.get(nodeId);
539
- if (node === undefined) {
540
- return null;
541
+ encryptNodesForUsers = async (userNodes, // { [userId]: nodeIds[] }
542
+ userPublicKeys) => {
543
+ const userIds = Object.keys(userNodes).map((userId) => userId);
544
+ const nodeIds = Object.values(userNodes).flatMap((nodeIds) => nodeIds);
545
+ const nodes = [];
546
+ // Pre check to ensure we get all public keys for users!
547
+ for (const userId of userIds) {
548
+ if (userPublicKeys[userId] === undefined) {
549
+ throw new Error(`Unable to retreive some user public keys!`);
541
550
  }
542
551
  }
543
- if (node.type === 'FILE' &&
544
- (!('history' in node) || node.history.length === 0)) {
545
- throw new Error("Can't share a node without data!");
552
+ // Retreive and format nodes.
553
+ for (const nodeId of nodeIds) {
554
+ let node;
555
+ node ??= nodesEncryptionCache.get(nodeId);
556
+ node ??= nodesCache.get(nodeId);
557
+ if (node &&
558
+ 'history' in node &&
559
+ node.history.length > 0 &&
560
+ node.access.nameKey) {
561
+ nodes.push({
562
+ id: nodeId,
563
+ name: node.name,
564
+ type: node.type,
565
+ access: { nameKey: node.access.nameKey },
566
+ history: node.history.map((data) => {
567
+ if (!data.key) {
568
+ throw new Error('Unable to retreive data key!');
569
+ }
570
+ return { id: data.id, key: data.key };
571
+ }),
572
+ });
573
+ }
546
574
  }
547
- const nameKey = node.access?.nameKey;
548
- return {
549
- id: node.id,
550
- nameKey: nameKey !== null
551
- ? sodium.to_hex(encryptCryptoBox(sodium.from_hex(nameKey), publicKey, this.#keys.privateKey))
552
- : null,
553
- data: 'history' in node
554
- ? node.history.map((f) => ({
555
- id: f.id,
556
- key: f.key
557
- ? sodium.to_hex(encryptCryptoBox(sodium.from_hex(f.key), publicKey, this.#keys.privateKey))
575
+ // Retreive all missing nodes from cache to api.
576
+ const missingNodeIds = nodeIds.filter((nodeId) => !nodes.some((node) => node.id === nodeId));
577
+ const fetchedNodes = await this.#apiClient.cloud.nodesForEncryption.query({
578
+ ids: missingNodeIds,
579
+ });
580
+ if (fetchedNodes.length !== missingNodeIds.length) {
581
+ const diff = missingNodeIds.filter((id) => !fetchedNodes.some((node) => node.id === id));
582
+ throw new Error(`Unable to fetch some node infos (${diff.join(', ')})`);
583
+ }
584
+ nodes.push(...fetchedNodes.map((node) => {
585
+ return {
586
+ id: node.id,
587
+ type: node.type,
588
+ name: node.name,
589
+ access: { nameKey: node.access.nameKey },
590
+ history: node.history.map((data) => ({
591
+ id: data.id,
592
+ key: data.access.key,
593
+ })),
594
+ };
595
+ }));
596
+ const nodesMappedUsers = {};
597
+ for (const userId in userNodes) {
598
+ nodesMappedUsers[userId] ??= [];
599
+ for (const nodeId of userNodes[userId]) {
600
+ const node = nodes.find((node) => node.id === nodeId);
601
+ if (!node) {
602
+ throw new Error('Unable to retreive node from ram');
603
+ }
604
+ if (node.type === 'FILE' &&
605
+ (!('history' in node) || node.history.length === 0)) {
606
+ throw new Error(`Can't share a node without data (${node.id})!`);
607
+ }
608
+ const nameKey = node.access?.nameKey;
609
+ const publicKey = userPublicKeys[userId];
610
+ nodesEncryptionCache.set(node.id, node);
611
+ nodesMappedUsers[userId].push({
612
+ id: node.id,
613
+ nameKey: nameKey !== null
614
+ ? sodium.to_hex(encryptCryptoBox(sodium.from_hex(nameKey), publicKey, this.#keys.privateKey))
558
615
  : null,
559
- }))
560
- : [],
561
- };
616
+ data: 'history' in node
617
+ ? node.history.map((f) => ({
618
+ id: f.id,
619
+ key: f.key
620
+ ? sodium.to_hex(encryptCryptoBox(sodium.from_hex(f.key), publicKey, this.#keys.privateKey))
621
+ : null,
622
+ }))
623
+ : [],
624
+ });
625
+ }
626
+ }
627
+ return nodesMappedUsers;
562
628
  };
563
629
  async reportData({ id, reasons, }) {
564
630
  const [rawData, secrecy] = await Promise.all([
@@ -574,18 +640,13 @@ export class SecrecyCloudClient {
574
640
  : null,
575
641
  });
576
642
  }
577
- async updateDataStorage(input) {
643
+ async updateDataStorageType(input) {
578
644
  const data = dataContentCache.get(input.dataId);
579
645
  if (data) {
580
- if (data.storage.protocol === input.protocol &&
581
- data.storage.provider === input.provider &&
582
- data.storage.mode === input.mode &&
583
- data.storage.region === input.region) {
584
- throw new Error(`The data is already on "${data.storage}`);
646
+ if (data.storageType === input.storageType) {
647
+ throw new Error(`The data is already on "${data.storageType}`);
585
648
  }
586
- if (data.storage.protocol === 's3' &&
587
- data.storage.mode === 'glacier' &&
588
- input.protocol === 'mongo') {
649
+ if (data.storageType === 'cold' && input.storageType === 'lite') {
589
650
  throw new Error("It's not possible to transfer a cold stored data to a lite storage!");
590
651
  }
591
652
  }
@@ -597,6 +658,9 @@ export class SecrecyCloudClient {
597
658
  getPublicDataLinks(input) {
598
659
  return this.#apiClient.cloud.dataLinks.query(input);
599
660
  }
661
+ checkAccesses(input) {
662
+ return this.#apiClient.cloud.checkAccesses.query(input);
663
+ }
600
664
  createPublicDataLink(input) {
601
665
  if (input.expireAt && input.expireAt <= new Date()) {
602
666
  throw new Error('Unable to create public link using a past expireAt date!');
@@ -647,25 +711,19 @@ export class SecrecyCloudClient {
647
711
  .sort((a, b) => a.order - b.order)
648
712
  .map((p) => p.data));
649
713
  };
650
- const encryptedContent = dataContent.type === 'mongo'
651
- ? new Uint8Array(dataContent.bytes)
652
- : dataContent.type === 's3'
714
+ const encryptedContent = dataContent.type === 'lite'
715
+ ? new Uint8Array(dataContent.content)
716
+ : dataContent.type === 'cloud'
653
717
  ? await encryptedContentFromParts({
654
718
  dataId: dataContent.id,
655
- dataParts: dataContent.parts.map(({ url, ...part }) => ({
656
- ...part,
657
- contentUrl: url,
658
- })),
719
+ dataParts: dataContent.parts,
659
720
  })
660
- : dataContent.maybeBytes !== null
661
- ? new Uint8Array(dataContent.maybeBytes)
721
+ : dataContent.maybeContent !== null
722
+ ? new Uint8Array(dataContent.maybeContent)
662
723
  : dataContent.maybeParts !== null
663
724
  ? await encryptedContentFromParts({
664
725
  dataId: dataContent.id,
665
- dataParts: dataContent.maybeParts.map(({ url, ...part }) => ({
666
- ...part,
667
- contentUrl: url,
668
- })),
726
+ dataParts: dataContent.maybeParts,
669
727
  })
670
728
  : null;
671
729
  if (encryptedContent === null) {
@@ -678,7 +736,7 @@ export class SecrecyCloudClient {
678
736
  const key = dataContent.key
679
737
  ? decryptCryptoBox(sodium.from_hex(dataContent.key), dataContent.type === 'received_mail'
680
738
  ? dataContent.senderPublicKey
681
- : dataContent.type === 's3'
739
+ : dataContent.type === 'cloud' || dataContent.type === 'lite'
682
740
  ? dataContent.publicKey
683
741
  : this.#keys.publicKey, this.#keys.privateKey)
684
742
  : null;
@@ -690,20 +748,11 @@ export class SecrecyCloudClient {
690
748
  throw new Error(`Content does not match`);
691
749
  }
692
750
  const decompressed = decompress(src);
693
- const storageOptions = dataContent.storage.protocol === 'mongo'
694
- ? dataContent.storage.mongoStorageOptions
695
- : dataContent.storage.s3StorageOptions;
696
- const storage = {
697
- protocol: dataContent.storage.protocol,
698
- provider: dataContent.storage.provider,
699
- mode: storageOptions.mode,
700
- region: storageOptions.region,
701
- };
702
751
  dataContentCache.set(dataContent.id, {
703
752
  id: dataContent.id,
704
753
  size: dataContent.size,
705
754
  sizeEncrypted: dataContent.sizeEncrypted,
706
- storage: storage,
755
+ storageType: dataContent.storageType,
707
756
  data: decompressed,
708
757
  mime: dataContent.mime ?? undefined,
709
758
  ext: dataContent.ext ?? undefined,
@@ -712,7 +761,7 @@ export class SecrecyCloudClient {
712
761
  id: dataContent.id,
713
762
  size: dataContent.size,
714
763
  sizeEncrypted: dataContent.sizeEncrypted,
715
- storage: storage,
764
+ storageType: dataContent.storageType,
716
765
  data: decompressed,
717
766
  mime: dataContent.mime ?? undefined,
718
767
  ext: dataContent.ext ?? undefined,
@@ -3,7 +3,7 @@ import { decryptCryptoBox } from '../../crypto/index.js';
3
3
  import { nodesCache } from '../../cache.js';
4
4
  import { decryptSecretStream } from '../../crypto/data.js';
5
5
  import { apiDataToInternal, internalDataToExternalData } from './data.js';
6
- export async function apiNodeToInternal(apiNode, keyPair) {
6
+ async function apiNodeToInternal(apiNode, keyPair) {
7
7
  const internal = {
8
8
  id: apiNode.id,
9
9
  type: apiNode.type,
@@ -58,7 +58,7 @@ export async function apiNodeFullToInternalFull(apiNodeFull, keyPair) {
58
58
  nodesCache.set(f.id, nodeFull);
59
59
  return nodeFull;
60
60
  }
61
- export function internalNodeToNode(internal) {
61
+ function internalNodeToNode(internal) {
62
62
  const node = {
63
63
  ...internal,
64
64
  breadcrumb: internal.breadcrumb.map((b) => ({
@@ -1,7 +1,8 @@
1
- import type { InternalNode, InternalData, InternalNodeFull, LocalData } from './client/types/index.js';
1
+ import type { InternalNode, InternalData, InternalNodeFull, LocalData, InternalMinimalNodeForEncryption } from './client/types/index.js';
2
2
  import { LRUCache } from 'lru-cache';
3
3
  export declare const dataCache: Map<string, InternalData>;
4
4
  export declare const nodesCache: Map<string, InternalNode | InternalNodeFull>;
5
+ export declare const nodesEncryptionCache: Map<string, InternalMinimalNodeForEncryption>;
5
6
  export declare const usersCache: Map<string, {
6
7
  id: string;
7
8
  lastname: string;
@@ -16,6 +16,7 @@ export declare class SecrecyAppClient {
16
16
  updateNotifications(notifications: Partial<UserAppNotifications>): Promise<UserAppNotifications>;
17
17
  notifications(): Promise<UserAppNotifications>;
18
18
  userPublicKey(userId: string): Promise<string>;
19
+ userPublicKey(userId: string[]): Promise<Record<string, string>>;
19
20
  settings(): Promise<UserAppSettings>;
20
21
  updateSettings(settings: Partial<UserAppSettings>): Promise<UserAppSettings>;
21
22
  }
@@ -1,5 +1,5 @@
1
1
  import type { ProgressCallback, SecrecyClient } from '../index.js';
2
- import type { DataMetadata, DataStorageInput, KeyPair, LocalData, Node, NodeFull, NodeType, Rights } from './types/index.js';
2
+ import type { DataMetadata, DataStorageType, KeyPair, LocalData, Node, NodeFull, NodeType, Rights } from './types/index.js';
3
3
  import { type RouterInputs, type ApiClient, type RouterOutputs } from '../client.js';
4
4
  import { type DownloadProgress } from '../types.js';
5
5
  import { FileTypeResult } from 'file-type';
@@ -10,8 +10,8 @@ export declare class SecrecyCloudClient {
10
10
  dataId: string;
11
11
  nodeId: string;
12
12
  }): Promise<NodeFull>;
13
- uploadData({ storage, data, encrypted, encryptProgress, uploadProgress, signal, meta, }: {
14
- storage: DataStorageInput;
13
+ uploadData({ storageType, data, encrypted, encryptProgress, uploadProgress, signal, meta, }: {
14
+ storageType: DataStorageType;
15
15
  data: globalThis.File | Uint8Array;
16
16
  encrypted?: boolean;
17
17
  encryptProgress?: ProgressCallback;
@@ -19,14 +19,14 @@ export declare class SecrecyCloudClient {
19
19
  signal?: AbortSignal;
20
20
  meta?: FileTypeResult | true;
21
21
  }): Promise<LocalData>;
22
- uploadDataInCloud({ data, name, nodeId, encryptProgress, uploadProgress, storage, signal, }: {
22
+ uploadDataInCloud({ data, name, nodeId, encryptProgress, uploadProgress, storageType, signal, }: {
23
23
  data: globalThis.File | Uint8Array;
24
24
  name: string;
25
25
  nodeId?: string;
26
26
  encryptProgress?: ProgressCallback;
27
27
  uploadProgress?: ProgressCallback;
28
28
  signal?: AbortSignal;
29
- storage: DataStorageInput;
29
+ storageType?: DataStorageType;
30
30
  }): Promise<NodeFull>;
31
31
  deletedNodes(): Promise<Node[]>;
32
32
  sharedNodes(): Promise<Node[]>;
@@ -54,11 +54,28 @@ export declare class SecrecyCloudClient {
54
54
  dataMetadata({ id }: {
55
55
  id: string;
56
56
  }): Promise<DataMetadata>;
57
- shareNode({ nodeId, userId, rights, }: {
58
- nodeId: string;
59
- userId: string;
57
+ shareNode(input: {
60
58
  rights: Rights;
61
- }): Promise<boolean>;
59
+ nodes: {
60
+ nodeId: string;
61
+ userId: string;
62
+ }[] | {
63
+ nodeId: string;
64
+ userIds: string[];
65
+ } | {
66
+ nodeIds: string[];
67
+ userId: string;
68
+ } | {
69
+ nodeIds: string[];
70
+ userIds: string[];
71
+ };
72
+ } | {
73
+ nodeId: string;
74
+ users: {
75
+ id: string;
76
+ rights: Rights;
77
+ }[];
78
+ }): Promise<RouterOutputs['cloud']['shareNodeFinish']>;
62
79
  updateNode({ nodeId, name, isFavorite, deletedAt, }: {
63
80
  nodeId: string;
64
81
  name?: string | null | undefined;
@@ -100,16 +117,12 @@ export declare class SecrecyCloudClient {
100
117
  name: string;
101
118
  nodeId?: string;
102
119
  }): Promise<NodeFull>;
103
- private readonly perNode;
120
+ private readonly encryptNodesForUsers;
104
121
  reportData({ id, reasons, }: Omit<RouterInputs['cloud']['reportData'], 'encryptedDataKey'>): Promise<RouterOutputs['cloud']['reportData']>;
105
- updateDataStorage(input: RouterInputs['cloud']['moveToStorageType']): Promise<{
122
+ updateDataStorageType(input: RouterInputs['cloud']['moveToStorageType']): Promise<{
106
123
  isMoved: boolean;
107
- fromProtocol?: "s3" | "mongo" | undefined;
108
- fromMode?: "standard" | "glacier" | undefined;
109
- fromRegion?: string | undefined;
110
- toProtocol?: "s3" | "mongo" | undefined;
111
- toMode?: "standard" | "glacier" | undefined;
112
- toRegion?: string | undefined;
124
+ fromType: "s3" | "cold" | "lite";
125
+ toType: "s3" | "cold" | "lite";
113
126
  }>;
114
127
  getPublicDataLink(input: RouterInputs['cloud']['dataLink']): Promise<{
115
128
  name: string;
@@ -125,6 +138,28 @@ export declare class SecrecyCloudClient {
125
138
  expireAt: Date | null;
126
139
  slug: string;
127
140
  }[]>;
141
+ checkAccesses(input: RouterInputs['cloud']['checkAccesses']): Promise<{
142
+ isMatching: true;
143
+ } | {
144
+ details: {
145
+ missingNodeAccesses: {
146
+ userId: string;
147
+ nodeId: string;
148
+ }[];
149
+ missingDataAccesses: {
150
+ userId: string;
151
+ dataId: string;
152
+ nodeId: string;
153
+ }[];
154
+ invalidRightsAccesses: {
155
+ userId: string;
156
+ current: "admin" | "write" | "read";
157
+ nodeId: string;
158
+ expect: "admin" | "write" | "read";
159
+ }[];
160
+ };
161
+ isMatching: false;
162
+ }>;
128
163
  createPublicDataLink(input: RouterInputs['cloud']['createDataLink']): Promise<{
129
164
  name: string;
130
165
  id: string;