@welshare/react 0.0.1-alpha.2 → 0.1.0

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 (38) hide show
  1. package/README.md +44 -0
  2. package/dist/esm/hooks/use-welshare.d.ts +5 -0
  3. package/dist/esm/hooks/use-welshare.d.ts.map +1 -1
  4. package/dist/esm/hooks/use-welshare.js +113 -4
  5. package/dist/esm/index.d.ts +3 -0
  6. package/dist/esm/index.d.ts.map +1 -1
  7. package/dist/esm/index.js +4 -1
  8. package/dist/esm/lib/encryption.d.ts +19 -0
  9. package/dist/esm/lib/encryption.d.ts.map +1 -0
  10. package/dist/esm/lib/encryption.js +61 -0
  11. package/dist/esm/lib/uploads.d.ts +3 -0
  12. package/dist/esm/lib/uploads.d.ts.map +1 -0
  13. package/dist/esm/lib/uploads.js +17 -0
  14. package/dist/esm/types.d.ts +31 -1
  15. package/dist/esm/types.d.ts.map +1 -1
  16. package/dist/node_modules/@welshare/react/.DS_Store +0 -0
  17. package/dist/node_modules/@welshare/react/README.md +44 -0
  18. package/dist/node_modules/@welshare/react/dist/esm/hooks/use-welshare.d.ts +5 -0
  19. package/dist/node_modules/@welshare/react/dist/esm/hooks/use-welshare.d.ts.map +1 -1
  20. package/dist/node_modules/@welshare/react/dist/esm/hooks/use-welshare.js +113 -4
  21. package/dist/node_modules/@welshare/react/dist/esm/index.d.ts +3 -0
  22. package/dist/node_modules/@welshare/react/dist/esm/index.d.ts.map +1 -1
  23. package/dist/node_modules/@welshare/react/dist/esm/index.js +4 -1
  24. package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.d.ts +19 -0
  25. package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.d.ts.map +1 -0
  26. package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.js +61 -0
  27. package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.d.ts +3 -0
  28. package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.d.ts.map +1 -0
  29. package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.js +17 -0
  30. package/dist/node_modules/@welshare/react/dist/esm/types.d.ts +31 -1
  31. package/dist/node_modules/@welshare/react/dist/esm/types.d.ts.map +1 -1
  32. package/dist/node_modules/@welshare/react/package.json +1 -1
  33. package/dist/node_modules/@welshare/react/src/hooks/use-welshare.ts +153 -4
  34. package/dist/node_modules/@welshare/react/src/index.ts +13 -5
  35. package/dist/node_modules/@welshare/react/src/lib/encryption.ts +110 -0
  36. package/dist/node_modules/@welshare/react/src/lib/uploads.ts +29 -0
  37. package/dist/node_modules/@welshare/react/src/types.ts +37 -1
  38. package/package.json +1 -1
@@ -0,0 +1,110 @@
1
+ export const ALGORITHM = "AES-GCM";
2
+ export type Algorithm = "AES-GCM";
3
+
4
+ export const generateRandomAESKey = async (): Promise<CryptoKey> => {
5
+ // Generate a 256-bit AES-GCM key for file encryption
6
+ return window.crypto.subtle.generateKey(
7
+ {
8
+ name: ALGORITHM,
9
+ length: 256, // 256-bit key
10
+ },
11
+ true, // Key is extractable (needed for storage/transmission)
12
+ ["encrypt", "decrypt"] // Key usage
13
+ );
14
+ };
15
+
16
+ /// also Generates random IV (12 bytes for AES-GCM)
17
+ /// @return {arraybuffer ciphertext, uint8array iv}
18
+ export const encryptFile = async (
19
+ file: File,
20
+ key: CryptoKey
21
+ ): Promise<{ encryptedData: ArrayBuffer; iv: Uint8Array }> => {
22
+ // Read file as ArrayBuffer
23
+ const fileData = await file.arrayBuffer();
24
+ const iv = window.crypto.getRandomValues(new Uint8Array(12));
25
+
26
+ // Encrypt the file data
27
+ const encryptedData = await window.crypto.subtle.encrypt(
28
+ {
29
+ name: ALGORITHM,
30
+ iv: iv,
31
+ },
32
+ key,
33
+ fileData
34
+ );
35
+
36
+ return { encryptedData, iv };
37
+ };
38
+
39
+ export type EncryptionKey = {
40
+ algorithm: Algorithm;
41
+ key: string;
42
+ iv: string;
43
+ };
44
+
45
+ export const encodeEncryptionKey = async (
46
+ key: CryptoKey,
47
+ iv: Uint8Array
48
+ ): Promise<EncryptionKey> => {
49
+ // Export the key as raw bytes
50
+ const exportedKey = await window.crypto.subtle.exportKey("raw", key);
51
+ const keyHex = Array.from(new Uint8Array(exportedKey))
52
+ .map((b) => b.toString(16).padStart(2, "0"))
53
+ .join("");
54
+
55
+ const ivHex = Array.from(iv)
56
+ .map((b) => b.toString(16).padStart(2, "0"))
57
+ .join("");
58
+
59
+ return {
60
+ algorithm: ALGORITHM,
61
+ key: keyHex,
62
+ iv: ivHex,
63
+ };
64
+ };
65
+
66
+ export const decodeEncryptionKey = (
67
+ encryptionKey: EncryptionKey
68
+ ): { key: Uint8Array<ArrayBuffer>; iv: Uint8Array<ArrayBuffer> } => {
69
+ const keyBytes = new Uint8Array(
70
+ encryptionKey.key
71
+ .match(/.{1,2}/g)!
72
+ .map((byte: string) => parseInt(byte, 16))
73
+ );
74
+ const ivBytes = new Uint8Array(
75
+ encryptionKey.iv.match(/.{1,2}/g)!.map((byte: string) => parseInt(byte, 16))
76
+ );
77
+ return { key: keyBytes, iv: ivBytes };
78
+ };
79
+
80
+ // Helper function to decrypt a file using encoded encryption key
81
+ export const decrypt = async (
82
+ encryptedData: ArrayBuffer,
83
+ encryptionKey: EncryptionKey
84
+ ): Promise<ArrayBuffer | null> => {
85
+ try {
86
+ const { key: keyBytes, iv } = decodeEncryptionKey(encryptionKey);
87
+
88
+ const key = await window.crypto.subtle.importKey(
89
+ "raw",
90
+ keyBytes,
91
+ { name: ALGORITHM },
92
+ false,
93
+ ["decrypt"]
94
+ );
95
+
96
+ const decryptedData = await window.crypto.subtle.decrypt(
97
+ {
98
+ name: ALGORITHM,
99
+ iv,
100
+ },
101
+ key,
102
+ encryptedData
103
+ );
104
+
105
+ return decryptedData;
106
+ } catch (error) {
107
+ console.error("Failed to decrypt file:", error);
108
+ return null;
109
+ }
110
+ };
@@ -0,0 +1,29 @@
1
+ import {
2
+ encodeEncryptionKey,
3
+ encryptFile,
4
+ EncryptionKey,
5
+ generateRandomAESKey,
6
+ } from "./encryption.js";
7
+
8
+ export const encryptAndUploadFile = async (
9
+ file: File,
10
+ presignedUrl: string
11
+ ): Promise<EncryptionKey> => {
12
+ const encryptionKey = await generateRandomAESKey();
13
+ const { encryptedData, iv } = await encryptFile(file, encryptionKey);
14
+
15
+ // Upload encrypted file to S3
16
+ const uploadResponse = await fetch(presignedUrl, {
17
+ method: "PUT",
18
+ body: encryptedData,
19
+ headers: {
20
+ "Content-Type": file.type,
21
+ },
22
+ });
23
+
24
+ if (!uploadResponse.ok) {
25
+ throw new Error(`Failed to upload file ${uploadResponse.status}`);
26
+ }
27
+
28
+ return encodeEncryptionKey(encryptionKey, iv);
29
+ };
@@ -1,3 +1,5 @@
1
+ import { EncryptionKey } from "./lib/encryption.js";
2
+
1
3
  export interface DialogMessage {
2
4
  type: string;
3
5
  payload?: any;
@@ -18,8 +20,41 @@ export interface SubmissionPayload<T> {
18
20
  submission: T;
19
21
  }
20
22
 
23
+ export type UploadCredentials = {
24
+ presignedUrl: string;
25
+ uploadKey: string;
26
+ }
27
+
28
+ export interface RequestUploadCredentialsPayload {
29
+ timestamp?: Date;
30
+ applicationId: string;
31
+ reference: string;
32
+ fileName: string;
33
+ fileType: string;
34
+ }
35
+
36
+ export interface BinaryFileSubmissionPayload extends RequestUploadCredentialsPayload {
37
+ encryptionKey: EncryptionKey;
38
+ /// in bytes
39
+ fileSize: number;
40
+ url: string;
41
+ }
42
+
43
+ export interface RunningFileUpload extends RequestUploadCredentialsPayload {
44
+ credentials?: UploadCredentials;
45
+ file: File;
46
+ reference: string;
47
+ uploadPromise: {
48
+ resolve: (result: { url: string; binaryFileUid: string }) => void;
49
+ reject: (error: Error) => void;
50
+ };
51
+ }
52
+
21
53
  export interface DataSubmissionDialogMessage extends DialogMessage {
22
- payload: SubmissionPayload<unknown>;
54
+ payload:
55
+ | SubmissionPayload<unknown>
56
+ | BinaryFileSubmissionPayload
57
+ | RequestUploadCredentialsPayload;
23
58
  }
24
59
 
25
60
  export interface WelshareConnectionOptions {
@@ -28,6 +63,7 @@ export interface WelshareConnectionOptions {
28
63
  apiBaseUrl?: string;
29
64
  environment?: WelshareEnvironment;
30
65
  callbacks: {
66
+ onFileUploaded?: (insertedUid: string, url: string) => void;
31
67
  onUploaded?: (payload: SubmissionPayload<unknown>) => void;
32
68
  onError?: (error: string) => void;
33
69
  onSessionReady?: (sessionPubKey: string) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@welshare/react",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "0.1.0",
4
4
  "description": "React library for integrating with Welshare's sovereign data sharing platform",
5
5
  "keywords": [
6
6
  "react",