favalib 0.0.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 (67) hide show
  1. package/build/Command/BaseCommand.d.mts +65 -0
  2. package/build/Command/BaseCommand.mjs +54 -0
  3. package/build/Command/CommandQueue.d.mts +28 -0
  4. package/build/Command/CommandQueue.mjs +43 -0
  5. package/build/Command/commandConstructors.d.mts +11 -0
  6. package/build/Command/commandConstructors.mjs +11 -0
  7. package/build/Command/commands/AddEntryCommand.d.mts +31 -0
  8. package/build/Command/commands/AddEntryCommand.mjs +43 -0
  9. package/build/Command/commands/AddSyncDeviceCommand.d.mts +36 -0
  10. package/build/Command/commands/AddSyncDeviceCommand.mjs +42 -0
  11. package/build/Command/commands/DeleteEntryCommand.d.mts +35 -0
  12. package/build/Command/commands/DeleteEntryCommand.mjs +50 -0
  13. package/build/Command/commands/UpdateEntryCommand.d.mts +38 -0
  14. package/build/Command/commands/UpdateEntryCommand.mjs +51 -0
  15. package/build/CryptoProviders/browser/index.d.mts +73 -0
  16. package/build/CryptoProviders/browser/index.mjs +209 -0
  17. package/build/CryptoProviders/node/index.d.mts +62 -0
  18. package/build/CryptoProviders/node/index.mjs +189 -0
  19. package/build/TwoFALibError.d.mts +77 -0
  20. package/build/TwoFALibError.mjs +91 -0
  21. package/build/TwoFaLib.d.mts +95 -0
  22. package/build/TwoFaLib.mjs +180 -0
  23. package/build/TwoFaLibEvent.d.mts +8 -0
  24. package/build/TwoFaLibEvent.mjs +9 -0
  25. package/build/TwoFaLibMediator.d.mts +37 -0
  26. package/build/TwoFaLibMediator.mjs +58 -0
  27. package/build/interfaces/CommandTypes.d.mts +20 -0
  28. package/build/interfaces/CommandTypes.mjs +1 -0
  29. package/build/interfaces/CryptoLib.d.mts +113 -0
  30. package/build/interfaces/CryptoLib.mjs +1 -0
  31. package/build/interfaces/Entry.d.mts +33 -0
  32. package/build/interfaces/Entry.mjs +1 -0
  33. package/build/interfaces/Events.d.mts +22 -0
  34. package/build/interfaces/Events.mjs +1 -0
  35. package/build/interfaces/PassphraseExtraDict.d.ts +2 -0
  36. package/build/interfaces/PassphraseExtraDict.js +1 -0
  37. package/build/interfaces/SyncTypes.d.mts +45 -0
  38. package/build/interfaces/SyncTypes.mjs +1 -0
  39. package/build/interfaces/Vault.d.mts +30 -0
  40. package/build/interfaces/Vault.mjs +1 -0
  41. package/build/main.d.mts +12 -0
  42. package/build/main.mjs +5 -0
  43. package/build/subclasses/CommandManager.d.mts +46 -0
  44. package/build/subclasses/CommandManager.mjs +117 -0
  45. package/build/subclasses/ExportImportManager.d.mts +58 -0
  46. package/build/subclasses/ExportImportManager.mjs +105 -0
  47. package/build/subclasses/LibraryLoader.d.mts +56 -0
  48. package/build/subclasses/LibraryLoader.mjs +108 -0
  49. package/build/subclasses/PersistentStorageManager.d.mts +71 -0
  50. package/build/subclasses/PersistentStorageManager.mjs +127 -0
  51. package/build/subclasses/SyncManager.d.mts +161 -0
  52. package/build/subclasses/SyncManager.mjs +567 -0
  53. package/build/subclasses/VaultDataManager.d.mts +68 -0
  54. package/build/subclasses/VaultDataManager.mjs +114 -0
  55. package/build/subclasses/VaultOperationsManager.d.mts +91 -0
  56. package/build/subclasses/VaultOperationsManager.mjs +163 -0
  57. package/build/utils/constants.d.mts +2 -0
  58. package/build/utils/constants.mjs +1 -0
  59. package/build/utils/creationUtils.d.mts +43 -0
  60. package/build/utils/creationUtils.mjs +125 -0
  61. package/build/utils/exportImportUtils.d.mts +53 -0
  62. package/build/utils/exportImportUtils.mjs +185 -0
  63. package/build/utils/qrUtils.d.mts +25 -0
  64. package/build/utils/qrUtils.mjs +84 -0
  65. package/build/utils/syncUtils.d.mts +26 -0
  66. package/build/utils/syncUtils.mjs +78 -0
  67. package/package.json +56 -0
@@ -0,0 +1,185 @@
1
+ import { ExportImportError } from '../TwoFALibError.mjs';
2
+ /**
3
+ * Determines the hashing algorithm based on the input string.
4
+ * @param alg - The algorithm string to parse.
5
+ * @returns The standardized algorithm name or null if unsupported.
6
+ */
7
+ const parseOtpAlgorithm = (alg) => {
8
+ if (!alg) {
9
+ return 'SHA-1'; // default algorithm
10
+ }
11
+ switch (alg.toLowerCase()) {
12
+ case 'sha1':
13
+ case 'sha-1':
14
+ case 'algorithm.sha1':
15
+ return 'SHA-1';
16
+ case 'sha-256':
17
+ return 'SHA-256';
18
+ default:
19
+ return null;
20
+ }
21
+ };
22
+ /**
23
+ * Parses an OTP URI and extracts the relevant information to create a new entry.
24
+ * @param UrlParser - The URL parsing library.
25
+ * @param otpUri - The OTP URI to parse.
26
+ * @returns An object representing the new entry.
27
+ * @throws ExportImportError if the URI is invalid or contains unsupported features.
28
+ */
29
+ export const parseOtpUri = (UrlParser, otpUri) => {
30
+ if (!otpUri.startsWith('otpauth://')) {
31
+ throw new ExportImportError('Invalid OTP URI');
32
+ }
33
+ const parsedUri = UrlParser.parseURL(otpUri);
34
+ if (!parsedUri) {
35
+ throw new ExportImportError('Failed to parse URI');
36
+ }
37
+ const { scheme, host, path, query } = parsedUri;
38
+ if (scheme !== 'otpauth') {
39
+ throw new ExportImportError(`Unsupported protocol "${scheme}"`);
40
+ }
41
+ if (host !== 'totp') {
42
+ throw new ExportImportError(`Unsupported OTP type "${String(host)}"`);
43
+ }
44
+ const searchParams = new URLSearchParams(query ?? '');
45
+ // some use /, some use :
46
+ const splitOn = path[0].includes('/') ? '/' : ':';
47
+ // ente double encodes its exports
48
+ let [issuer, name] = decodeURIComponent(decodeURIComponent(path[0])).split(splitOn);
49
+ const secret = searchParams.get('secret');
50
+ const algorithm = parseOtpAlgorithm(searchParams.get('algorithm'));
51
+ const digits = parseInt(searchParams.get('digits') ?? '6', 10);
52
+ const period = parseInt(searchParams.get('period') ?? '30', 10);
53
+ // if searchParams has an issuer, use that
54
+ if (searchParams.get('issuer')) {
55
+ if (!name) {
56
+ name = issuer;
57
+ }
58
+ issuer = searchParams.get('issuer');
59
+ }
60
+ // validate
61
+ if (!secret) {
62
+ throw new ExportImportError('Invalid OTP URI: missing secret');
63
+ }
64
+ if (!algorithm) {
65
+ throw new ExportImportError(`Unsupported algorithm "${searchParams.get('algorithm')}"`);
66
+ }
67
+ return {
68
+ name: name && name.length > 0 ? name : 'Imported Entry',
69
+ issuer: issuer && issuer.length > 0 ? issuer : 'Unknown Issuer',
70
+ type: 'TOTP',
71
+ payload: {
72
+ secret,
73
+ algorithm,
74
+ digits,
75
+ period,
76
+ },
77
+ };
78
+ };
79
+ const generateOtpUrl = (entry) => {
80
+ const { name, issuer, payload } = entry;
81
+ const { secret, algorithm, digits, period } = payload;
82
+ return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(name)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=${algorithm}&digits=${digits}&period=${period}`;
83
+ };
84
+ /**
85
+ * Generates an HTML page with QR codes for the provided OTP entries.
86
+ * @param qrGeneratorLib - The QR code generation library.
87
+ * @param entries - An array of OTP entries.
88
+ * @returns A promise that resolves to the HTML string.
89
+ */
90
+ export const generateHtmlExport = async (qrGeneratorLib, entries) => {
91
+ const qrPromises = entries.map(async (entry) => {
92
+ const { name, issuer } = entry;
93
+ const otpUrl = generateOtpUrl(entry);
94
+ const qrCode = await qrGeneratorLib.toDataURL(otpUrl);
95
+ return `
96
+ <div class="entry">
97
+ <img src="${qrCode}" alt="QR Code for ${name}">
98
+ <p><strong>${name}</strong></p>
99
+ <p>Issuer: ${issuer}</p>
100
+ </div>
101
+ `;
102
+ });
103
+ const qrCodes = await Promise.all(qrPromises);
104
+ return `
105
+ <html>
106
+ <head>
107
+ <style>
108
+ .container { display: flex; flex-wrap: wrap; }
109
+ .entry { margin: 10px; text-align: center; }
110
+ img { width: 200px; height: 200px; }
111
+ </style>
112
+ </head>
113
+ <body>
114
+ <div class="container">
115
+ ${qrCodes.join('')}
116
+ </div>
117
+ </body>
118
+ </html>
119
+ `;
120
+ };
121
+ /**
122
+ * Generates a text export of OTP URIs for the provided entries.
123
+ * @param entries - An array of OTP entries.
124
+ * @returns A string containing the OTP URIs, one per line.
125
+ */
126
+ export const generateTextExport = (entries) => {
127
+ return entries
128
+ .map((entry) => {
129
+ return generateOtpUrl(entry);
130
+ })
131
+ .join('\n');
132
+ };
133
+ /**
134
+ * Processes the lines of a text file containing OTP URIs and returns an array of objects, each containing the line number,
135
+ * the EntryId or null if it was not a valid entry and the error if there was one.
136
+ * @param lines - An array of strings, each containing an OTP URI.
137
+ * @param importFromUri - A function that takes a URI and returns a promise that resolves to the EntryId.
138
+ * @returns A promise that resolves to an array of objects, each containing the line number,
139
+ * the EntryId or null if it was not a valid entry and the error if there was one.
140
+ */
141
+ export const processImportLines = async (lines, importFromUri) => {
142
+ return Promise.all(lines
143
+ .filter((line) => line !== '')
144
+ .map(async (line, lineNr) => {
145
+ try {
146
+ return {
147
+ lineNr,
148
+ entryId: await importFromUri(line),
149
+ error: null,
150
+ };
151
+ }
152
+ catch (err) {
153
+ return { lineNr, entryId: null, error: err };
154
+ }
155
+ }));
156
+ };
157
+ /**
158
+ * Encrypts the given data using OpenPGP.
159
+ * @param openPgpLib - The OpenPGP library.
160
+ * @param data - The data to encrypt.
161
+ * @param password - The password to use for encryption.
162
+ * @returns A promise that resolves to the encrypted data.
163
+ */
164
+ export const encryptExport = async (openPgpLib, data, password) => {
165
+ const encrypted = await openPgpLib.encrypt({
166
+ message: await openPgpLib.createMessage({ text: data }),
167
+ passwords: [password],
168
+ format: 'armored',
169
+ });
170
+ return encrypted;
171
+ };
172
+ /**
173
+ * Decrypts the given data using OpenPGP.
174
+ * @param openPgpLib - The OpenPGP library.
175
+ * @param data - The data to decrypt.
176
+ * @param password - The password to use for decryption.
177
+ * @returns A promise that resolves to the decrypted data.
178
+ */
179
+ export const decryptExport = async (openPgpLib, data, password) => {
180
+ const decrypted = await openPgpLib.decrypt({
181
+ message: await openPgpLib.readMessage({ armoredMessage: data }),
182
+ passwords: [password],
183
+ });
184
+ return decrypted.data;
185
+ };
@@ -0,0 +1,25 @@
1
+ import type { ImageData } from 'canvas';
2
+ import LibraryLoader from '../subclasses/LibraryLoader.mjs';
3
+ /**
4
+ * Gets the ImageData from an image, for browser environments.
5
+ * This ImageData is then further processed to get QR codes.
6
+ * @param input - The image to get the ImageData from.
7
+ * @returns A promise that resolves to the ImageData.
8
+ */
9
+ export declare const getImageDataBrowser: (input: string | File) => Promise<ImageData>;
10
+ /**
11
+ * Gets the ImageData from an image, for Node.js environments.
12
+ * This ImageData is then further processed to get QR codes.
13
+ * @param canvasLib - The Canvas library.
14
+ * @param inputImage - The image to get the ImageData from.
15
+ * @returns A promise that resolves to the ImageData.
16
+ */
17
+ export declare const getImageDataNode: (canvasLib: typeof import("canvas"), inputImage: Uint8Array | string) => Promise<ImageData>;
18
+ /**
19
+ * Import an entry from a QR code image.
20
+ * @param libraryLoader - An instance of LibraryLoader.
21
+ * @param imageInput - The image containing the QR code
22
+ * @returns A promise that resolves to the qr data.
23
+ * @throws {InvalidInputExportImportError} If the QR code is invalid or doesn't contain a valid OTP URI.
24
+ */
25
+ export declare const getDataFromQRImage: (libraryLoader: LibraryLoader, imageInput: string | File | Uint8Array) => Promise<string>;
@@ -0,0 +1,84 @@
1
+ import { isUint8Array } from 'uint8array-extras';
2
+ import { TwoFALibError } from '../TwoFALibError.mjs';
3
+ /**
4
+ * Gets the ImageData from an image, for browser environments.
5
+ * This ImageData is then further processed to get QR codes.
6
+ * @param input - The image to get the ImageData from.
7
+ * @returns A promise that resolves to the ImageData.
8
+ */
9
+ export const getImageDataBrowser = (input) => {
10
+ return new Promise((resolve, reject) => {
11
+ const img = new Image();
12
+ img.onload = () => {
13
+ const canvas = document.createElement('canvas');
14
+ canvas.width = img.width;
15
+ canvas.height = img.height;
16
+ const ctx = canvas.getContext('2d');
17
+ if (!ctx) {
18
+ reject(new TwoFALibError('Could not create canvas context'));
19
+ return;
20
+ }
21
+ ctx.drawImage(img, 0, 0);
22
+ resolve(ctx.getImageData(0, 0, img.width, img.height));
23
+ };
24
+ img.onerror = () => reject(new TwoFALibError('Failed to load image'));
25
+ if (typeof input === 'string') {
26
+ // URL or Data URL
27
+ img.src = input;
28
+ }
29
+ else {
30
+ // File object
31
+ const reader = new FileReader();
32
+ reader.onload = (e) => {
33
+ img.src = e.target?.result;
34
+ };
35
+ reader.onerror = () => reject(new TwoFALibError('Failed to read file'));
36
+ reader.readAsDataURL(input);
37
+ }
38
+ });
39
+ };
40
+ /**
41
+ * Gets the ImageData from an image, for Node.js environments.
42
+ * This ImageData is then further processed to get QR codes.
43
+ * @param canvasLib - The Canvas library.
44
+ * @param inputImage - The image to get the ImageData from.
45
+ * @returns A promise that resolves to the ImageData.
46
+ */
47
+ export const getImageDataNode = async (canvasLib, inputImage) => {
48
+ const { createCanvas, loadImage } = canvasLib;
49
+ // eslint-disable-next-line no-restricted-globals
50
+ const input = isUint8Array(inputImage) ? Buffer.from(inputImage) : inputImage;
51
+ const image = await loadImage(input); // canvaslib expects a buffer
52
+ const canvas = createCanvas(image.width, image.height);
53
+ const ctx = canvas.getContext('2d');
54
+ ctx.drawImage(image, 0, 0);
55
+ return ctx.getImageData(0, 0, image.width, image.height);
56
+ };
57
+ /**
58
+ * Import an entry from a QR code image.
59
+ * @param libraryLoader - An instance of LibraryLoader.
60
+ * @param imageInput - The image containing the QR code
61
+ * @returns A promise that resolves to the qr data.
62
+ * @throws {InvalidInputExportImportError} If the QR code is invalid or doesn't contain a valid OTP URI.
63
+ */
64
+ export const getDataFromQRImage = async (libraryLoader, imageInput) => {
65
+ const jsQr = await libraryLoader.getJsQrLib();
66
+ let imageData;
67
+ if (typeof window !== 'undefined') {
68
+ // Browser environment
69
+ imageData = await getImageDataBrowser(imageInput);
70
+ }
71
+ else {
72
+ if (imageInput instanceof File) {
73
+ throw new TwoFALibError('Getting data from QR where image type is "File" is not supported in the node environment');
74
+ }
75
+ // Node.js environment
76
+ const canvasLib = await libraryLoader.getCanvasLib();
77
+ imageData = await getImageDataNode(canvasLib, imageInput);
78
+ }
79
+ const qrCodeResult = jsQr(imageData.data, imageData.width, imageData.height);
80
+ if (!qrCodeResult) {
81
+ throw new TwoFALibError("Couldn't read QR code data from image");
82
+ }
83
+ return qrCodeResult.data;
84
+ };
@@ -0,0 +1,26 @@
1
+ import { InitiateAddDeviceFlowResult } from '../interfaces/SyncTypes.mjs';
2
+ /**
3
+ * Decodes the initiator data from a string or QR code.
4
+ * @param initiatorData - The initiator data to decode.
5
+ * @param initiatorDataType The type of the initiatorData, determines how it should be decoded
6
+ * @param jsQr - The QR code decoder.
7
+ * @param getCanvasLib - A function to get the Canvas library.
8
+ * @returns A promise that resolves to the decoded initiator data.
9
+ */
10
+ export declare const decodeInitiatorData: (initiatorData: string | Uint8Array | File, initiatorDataType: "text" | "qr", jsQr: typeof import("jsqr").default, getCanvasLib: () => Promise<typeof import("canvas")>) => Promise<InitiateAddDeviceFlowResult>;
11
+ /**
12
+ * Converts a JSONified Uint8Array to a Uint8Array.
13
+ * A JSONified Uint8Array is the output of JSON.stringify on a Uint8Array.
14
+ * Example:
15
+ * JSON.stringify(new Uint8Array([123, 456])) looks like this:
16
+ * '{"0":123,"1":200}'
17
+ * @param jsonObj - The JSONified Uint8Array to convert.
18
+ * @returns A Uint8Array containing the values of the JSONified Uint8Array.
19
+ */
20
+ export declare const jsonifiedUint8ArraytoUint8Array: (jsonObj: Record<string, number>) => Uint8Array;
21
+ /**
22
+ * Converts a JSON object to a record of Uint8Arrays.
23
+ * @param jsonObj - The JSON object to convert.
24
+ * @returns A record of Uint8Arrays.
25
+ */
26
+ export declare const jsonToUint8Array: (jsonObj: Record<string, Record<string, number>>) => Record<string, Uint8Array>;
@@ -0,0 +1,78 @@
1
+ import { base64ToString } from 'uint8array-extras';
2
+ import { getImageDataBrowser, getImageDataNode } from './qrUtils.mjs';
3
+ import { SyncError } from '../TwoFALibError.mjs';
4
+ /**
5
+ * Decodes the initiator data from a string or QR code.
6
+ * @param initiatorData - The initiator data to decode.
7
+ * @param initiatorDataType The type of the initiatorData, determines how it should be decoded
8
+ * @param jsQr - The QR code decoder.
9
+ * @param getCanvasLib - A function to get the Canvas library.
10
+ * @returns A promise that resolves to the decoded initiator data.
11
+ */
12
+ export const decodeInitiatorData = async (initiatorData, initiatorDataType, jsQr, getCanvasLib) => {
13
+ if (initiatorDataType === 'qr') {
14
+ try {
15
+ let imageData;
16
+ if (typeof window !== 'undefined') {
17
+ if (initiatorData instanceof Uint8Array) {
18
+ throw new SyncError('Invalid initiator data type, should be a string or File in browser environment');
19
+ }
20
+ // Browser environment
21
+ imageData = await getImageDataBrowser(initiatorData);
22
+ }
23
+ else {
24
+ if (!(initiatorData instanceof Uint8Array)) {
25
+ throw new SyncError('Invalid initiator data type, should be a Uint8Array in Node.js environment');
26
+ }
27
+ // Node.js environment
28
+ const canvasLib = await getCanvasLib();
29
+ imageData = await getImageDataNode(canvasLib, initiatorData);
30
+ }
31
+ const qrCodeResult = jsQr(imageData.data, imageData.width, imageData.height);
32
+ if (!qrCodeResult) {
33
+ throw new SyncError('Invalid QR code');
34
+ }
35
+ return JSON.parse(qrCodeResult.data);
36
+ }
37
+ catch (error) {
38
+ // eslint-disable-next-line no-restricted-globals
39
+ if (error instanceof Error) {
40
+ throw new SyncError('Failed to decode QR code: ' + error.message);
41
+ }
42
+ throw new SyncError('Failed to decode QR code: unknown error.');
43
+ }
44
+ }
45
+ else {
46
+ if (typeof initiatorData !== 'string') {
47
+ throw new SyncError('Invalid initiator data');
48
+ }
49
+ const parsedInitiatorData = JSON.parse(base64ToString(initiatorData));
50
+ if (!parsedInitiatorData || typeof parsedInitiatorData !== 'object') {
51
+ throw new SyncError('Invalid initiator data');
52
+ }
53
+ return parsedInitiatorData;
54
+ }
55
+ };
56
+ /**
57
+ * Converts a JSONified Uint8Array to a Uint8Array.
58
+ * A JSONified Uint8Array is the output of JSON.stringify on a Uint8Array.
59
+ * Example:
60
+ * JSON.stringify(new Uint8Array([123, 456])) looks like this:
61
+ * '{"0":123,"1":200}'
62
+ * @param jsonObj - The JSONified Uint8Array to convert.
63
+ * @returns A Uint8Array containing the values of the JSONified Uint8Array.
64
+ */
65
+ export const jsonifiedUint8ArraytoUint8Array = (jsonObj) => {
66
+ return new Uint8Array(Object.values(jsonObj));
67
+ };
68
+ /**
69
+ * Converts a JSON object to a record of Uint8Arrays.
70
+ * @param jsonObj - The JSON object to convert.
71
+ * @returns A record of Uint8Arrays.
72
+ */
73
+ export const jsonToUint8Array = (jsonObj) => {
74
+ return Object.entries(jsonObj).reduce((acc, [key, value]) => ({
75
+ ...acc,
76
+ [key]: jsonifiedUint8ArraytoUint8Array(value),
77
+ }), {});
78
+ };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "favalib",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./build/main.mjs",
9
+ "types": "./build/main.d.mts"
10
+ },
11
+ "./cryptoProviders/browser": {
12
+ "import": "./build/CryptoProviders/browser/index.mjs",
13
+ "types": "./build/CryptoProviders/browser/index.d.mts"
14
+ },
15
+ "./cryptoProviders/node": {
16
+ "import": "./build/CryptoProviders/node/index.mjs",
17
+ "types": "./build/CryptoProviders/node/index.d.mts"
18
+ }
19
+ },
20
+ "types": "./build/main.d.mts",
21
+ "files": [
22
+ "build/**"
23
+ ],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "devDependencies": {
27
+ "@types/node-forge": "^1.3.11",
28
+ "@types/qrcode": "^1.5.5",
29
+ "@types/uuid": "^10.0.0",
30
+ "@types/whatwg-url": "^11.0.5",
31
+ "@vitest/coverage-v8": "^2.1.2",
32
+ "eslint": "^9.12.0",
33
+ "type-fest": "^4.26.1",
34
+ "vitest": "^2.1.2"
35
+ },
36
+ "dependencies": {
37
+ "@zxcvbn-ts/core": "^3.0.4",
38
+ "@zxcvbn-ts/language-common": "^3.0.4",
39
+ "@zxcvbn-ts/language-en": "^3.0.2",
40
+ "canvas": "^2.11.2",
41
+ "hash-wasm": "^4.11.0",
42
+ "isomorphic-ws": "^5.0.0",
43
+ "jpake-ts": "^1.0.1",
44
+ "jsqr": "^1.4.0",
45
+ "node-forge": "^1.3.1",
46
+ "openpgp": "^5.11.2",
47
+ "qrcode": "^1.5.4",
48
+ "totp-generator": "^1.0.0",
49
+ "typescript-event-target": "^1.1.1",
50
+ "uint8array-extras": "^1.4.0",
51
+ "uuid": "^10.0.0",
52
+ "vitest-websocket-mock": "^0.4.0",
53
+ "whatwg-url": "^14.0.0",
54
+ "ws": "^8.18.0"
55
+ }
56
+ }