diff --git a/README.md b/README.md index 942d1b6c..ab97dad2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This package can be used in both browser and Node.js environments. - [Quick Start](#quick-start) - [Examples](#examples) - [Modules](#modules) - - [Auth](#auth) + - [Auth](#authentication) - [Vault](#vault) - [Membership](#membership) - [Memo](#memo) @@ -38,13 +38,10 @@ const { Akord } = require("@akord/akord-js"); ### Quick start #### Init Akord -##### with email & password ```js -const { akord, wallet, jwtToken } = await Akord.auth.signIn(email, password); -``` -##### with Akord Wallet & JWT -```js -const akord = await Akord.init(wallet, jwtToken); +import { Akord, Auth } from "@akord/akord-js"; +const { wallet } = await Auth.signIn(email, password); +const akord = await Akord.init(wallet); ``` #### Create vault @@ -64,29 +61,59 @@ const { data: fileBuffer, name: fileName } = await akord.stack.getVersion(stackI #### Query user vaults ```js -const vaults = await akord.vault.list(); +const vaults = await akord.vault.listAll(); ``` ### Examples -See our [demo app tutorial](https://akord-js-tutorial.akord.com) and learn how to create, -contribute and access an Akord Vault.\ -We also have some example flows in our [tests](src/__tests__) repository. +- See our [demo app tutorial](https://akord-js-tutorial.akord.com) and learn how to create, +contribute and access an Akord Vault from .\ -## Modules +- See example flows in our [tests repo](src/__tests__). + +- See different setups on [recipes repo](https://github.com/Akord-com/recipes). + +## Authentication +Use `Auth` module to handle authentication. -### auth +```js +import { Auth } from "@akord/akord-js"; +``` + +- By default `Auth` is using SRP authentication +- `Auth` stores tokens in `Storage` implementation +- `Storage` defaults to localStorage on web & memoryStorage on nodeJs +- `Storage` implementation can be configured with `Auth.configure({ storage: window.sessionStorage })` +- `Auth` is automatically refreshing tokens in SRP mode +- On server side it is recommended to use API keys: `Auth.configure({ apiKey: 'your_api_key' })` +- API key: can be generated over web app & over CLI + +##### use short living token with refresh +```js +import { Auth } from "@akord/akord-js"; +Auth.configure({ storage: window.sessionStorage }); // optionally - configure tokens store +``` +##### use API key +```js +import { Auth } from "@akord/akord-js"; +Auth.configure({ apiKey: "api_key" }); +``` +##### use self-managed auth token +```js +import { Akord, Auth } from "@akord/akord-js"; +Auth.configure({ authToken: "auth_token" }); +``` #### `signIn(email, password)` - `email` (`string`, required) - `password` (`string`, required) -- returns `Promise<{ akord, wallet, jwtToken }>` - Promise with Akord Client instance, JWT token & Akord Wallet +- returns `Promise<{ wallet, jwt }>` - Promise with JWT token & Akord Wallet
example ```js -const { akord, wallet, jwtToken } = await Akord.auth.signIn("winston@gmail.com", "1984"); +const { wallet } = await Auth.signIn("winston@gmail.com", "1984"); ```
@@ -95,13 +122,13 @@ const { akord, wallet, jwtToken } = await Akord.auth.signIn("winston@gmail.com", - `email` (`string`, required) - `password` (`string`, required) - `clientMetadata` (`any`, optional) - JSON client metadata, ex: { clientType: "CLI" } -- returns `Promise` - Promise with Akord Wallet +- returns `Promise<{ wallet }>` - Promise with Akord Wallet
example ```js -const wallet = await Akord.auth.signUp("winston@gmail.com", "1984"); +const { wallet } = await Auth.signUp("winston@gmail.com", "1984"); ```
@@ -115,24 +142,33 @@ const wallet = await Akord.auth.signUp("winston@gmail.com", "1984"); example ```js -await Akord.auth.verifyAccount("winston@gmail.com", 123456); +await Auth.verifyAccount("winston@gmail.com", 123456); ``` + +## Modules + ### vault -#### `create(name, termsOfAccess, isPublic)` +#### `create(name, options)` - `name` (`string`, required) - new vault name -- `termsOfAccess` (`string`, optional) - if the vault is intended for professional or legal use, you can add terms of access and they must be digitally signed before accessing the vault -- `isPublic` (`boolean`, optional) +- `options` (`VaultCreateOptions`, optional) - public/private, terms of access, etc. - returns `Promise<{ vaultId, membershipId, transactionId }>` - Promise with new vault id, owner membership id & corresponding transaction id
example ```js -const { vaultId, membershipId } = await akord.vault.create("my first vault", "terms of access"); +// create a private vault +const { vaultId, membershipId } = await akord.vault.create("my first private vault"); + +// create a public vault with terms of access +const { vaultId, membershipId } = await akord.vault.create( + "my first public vault", + { public: true, termsOfAccess: "terms of access here - if the vault is intended for professional or legal use, you can add terms of access and they must be digitally signed before accessing the vault" } +); ```
@@ -189,10 +225,10 @@ const { transactionId } = await akord.vault.delete(vaultId); ``` -#### `get(vaultId, shouldDecrypt)` +#### `get(vaultId, options)` - `vaultId` (`string`, required) -- `shouldDecrypt` (`boolean`, optional) - default to true +- `options` ([`VaultGetOptions`][vault-get-options], optional) - returns `Promise` - Promise with the vault object
@@ -203,16 +239,42 @@ const vault = await akord.vault.get(vaultId); ```
-#### `list(listOptions)` +#### `listAll(options)` -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise>` - Promise with currently authenticated user vaults
example ```js -const vaults = await akord.vault.list(); +const vaults = await akord.vault.listAll(); +``` +
+ +#### `list(listOptions)` + +- `options` ([`ListOptions`][list-options], optional) +- returns `Promise<{ items, nextToken }>` - Promise with paginated user vaults + +
+ example + +```js +// retrieve first 100 user vaults +const { items } = await akord.vault.list(); + +// retrieve first 20 user vaults +const { items } = await akord.vault.list({ limit: 20 }); + +// iterate through all user vaults +let token = null; +let vaults = []; +do { + const { items, nextToken } = await akord.vault.list({ nextToken: token }); + vaults = vaults.concat(items); + token = nextToken; +} while (token); ```
@@ -224,7 +286,8 @@ Invite user with an Akord account - `vaultId` (`string`, required) - `email` (`string`, required) - invitee's email -- `role` (`string`, required) - CONTRIBUTOR or VIEWER +- `role` ([`RoleType`][role-type], required) - CONTRIBUTOR or VIEWER +- `options` (`MembershipCreateOptions`, optional) - invitation email message, etc. - returns `Promise<{ membershipId, transactionId }>` - Promise with new membership id & corresponding transaction id
@@ -241,7 +304,8 @@ Invite user without an Akord account - `vaultId` (`string`, required) - `email` (`string`, required) - invitee's email -- `role` (`string`, required) - CONTRIBUTOR or VIEWER +- `role` ([`RoleType`][role-type], required) - CONTRIBUTOR or VIEWER +- `options` (`MembershipCreateOptions`, optional) - invitation email message, etc. - returns `Promise<{ transactionId }>` - Promise with new membership id & corresponding transaction id
@@ -326,7 +390,7 @@ const { transactionId } = await akord.membership.revoke(membershipId); #### `changeRole(membershipId, role)` - `membershipId` (`string`, required) -- `role` (`string`, required) - CONTRIBUTOR or VIEWER +- `role` ([`RoleType`][role-type], required) - CONTRIBUTOR or VIEWER - returns `Promise<{ transactionId }>` - Promise with corresponding transaction id
@@ -352,10 +416,10 @@ const { transactionId } = await akord.membership.inviteResend(membershipId); ```
-#### `get(membershipId, shouldDecrypt)` +#### `get(membershipId, options)` - `membershipId` (`string`, required) -- `shouldDecrypt` (`boolean`, optional) - default to true +- `options` ([`GetOptions`][get-options], optional) - returns `Promise` - Promise with the membership object
@@ -366,10 +430,10 @@ const membership = await akord.membership.get(membershipId); ```
-#### `listAll(vaultId, listOptions)` +#### `listAll(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise>` - Promise with all memberships within given vault
@@ -380,10 +444,10 @@ const memberships = await akord.membership.listAll(vaultId); ```
-#### `list(vaultId, listOptions)` +#### `list(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise<{ items, nextToken }>` - Promise with paginated memberships within given vault
@@ -413,7 +477,7 @@ do { - `vaultId` (`string`, required) - `message` (`string`, required) - memo content -- `parentId` (`string`, optional) - parent folder id +- `options` (`NodeCreateOptions`, optional) - parent id, etc. - returns `Promise<{ memoId, transactionId }>` - Promise with new memo id & corresponding transaction id
@@ -456,10 +520,10 @@ const { transactionId } = await akord.memo.removeReaction(memoId, Akord.reaction ```
-#### `get(memoId, shouldDecrypt)` +#### `get(memoId, options)` - `memoId` (`string`, required) -- `shouldDecrypt` (`boolean`, optional) - default to true +- `options` ([`GetOptions`][get-options], optional) - returns `Promise` - Promise with the memo object
@@ -470,10 +534,10 @@ const memo = await akord.memo.get(memoId); ```
-#### `listAll(vaultId, listOptions)` +#### `listAll(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise>` - Promise with all memos within given vault
@@ -484,10 +548,10 @@ const memos = await akord.memo.listAll(vaultId); ```
-#### `list(vaultId, listOptions)` +#### `list(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise<{ items, nextToken }>` - Promise with paginated memos within given vault
@@ -513,31 +577,32 @@ do { ### stack -#### `create(vaultId, file, name, parentId, progressHook, cancelHook)` +#### `create(vaultId, file, name)` - `vaultId` (`string`, required) -- `file` ([`FileLike`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/file.ts#L8), required) - file object - web: File, node: NodeJs.File (Blob implementation; web like File) +- `file` ([`FileLike`][file-like], required) - file object - web: File, node: NodeJs.File (Blob implementation; web like File) - `name` (`string`, required) - stack name -- `parentId` (`string`, optional) - parent folder id -- `progressHook` (`(progress:number)=>void`, optional) -- `cancelHook` (`AbortController`, optional) +- `options` (`StackCreateOptions`, optional) - returns `Promise<{ stackId, transactionId }>` - Promise with new stack id & corresponding transaction id
example ```js +import { NodeJs } from "@akord/akord-js/lib/types/file"; +const file = await NodeJs.File.fromPath("path to your file"); const { stackId } = await akord.stack.create(vaultId, file, "your stack name"); ``` +> [See Next.js file upload showcase here][file-upload-example]
-#### `import(vaultId, fileTxId, parentId)` +#### `import(vaultId, fileTxId)` Create new stack from an existing arweave file transaction - `vaultId` (`string`, required) - `fileTxId` (`string`, required) - arweave file transaction id reference -- `parentId` (`string`, optional) - parent folder id +- `options` (`NodeCreateOptions`, optional) - parent id, etc. - returns `Promise<{ stackId, transactionId }>` - Promise with new stack id & corresponding transaction id
@@ -562,17 +627,19 @@ const { transactionId } = await akord.stack.rename(stackId, "new name for your s ```
-#### `uploadRevision(stackId, file, progressHook)` +#### `uploadRevision(stackId, file)` - `stackId` (`string`, required) -- `file` ([`FileLike`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/file.ts#L8), required) - file object -- `progressHook` (`(progress:number)=>void`, optional) +- `file` ([`FileLike`][file-like], required) - file object +- `options` (`FileUploadOptions`, optional) - returns `Promise<{ transactionId }>` - Promise with corresponding transaction id
example ```js +import { NodeJs } from "@akord/akord-js/lib/types/file"; +const file = await NodeJs.File.fromPath("path to your file"); const { transactionId } = await akord.stack.uploadRevision(stackId, file); ```
@@ -633,10 +700,10 @@ const { transactionId } = await akord.stack.delete(stackId); ```
-#### `get(stackId, shouldDecrypt)` +#### `get(stackId, options)` - `stackId` (`string`, required) -- `shouldDecrypt` (`boolean`, optional) - default to true +- `options` ([`GetOptions`][get-options], optional) - returns `Promise` - Promise with the stack object
@@ -647,10 +714,10 @@ const stack = await akord.stack.get(stackId); ```
-#### `listAll(vaultId, listOptions)` +#### `listAll(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise>` - Promise with all stacks within given vault
@@ -661,10 +728,10 @@ const stacks = await akord.stack.listAll(vaultId); ```
-#### `list(vaultId, listOptions)` +#### `list(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise<{ items, nextToken }>` - Promise with paginated stacks within given vault
@@ -713,7 +780,7 @@ const { name: fileName, data: fileBuffer } = await akord.stack.getVersion(stackI Get stack file uri by index, return the latest arweave uri by default - `stackId` (`string`, required) -- `type` ([`StorageType`](https://github.com/Akord-com/akord-js/blob/26d1945bee727a1af45f0f9cc44c7fa9b68c5d75/src/types/node.ts#L149), optional) - storage type, default to arweave +- `type` ([`StorageType`][storage-type], optional) - storage type, default to arweave - `index` (`number`, optional) - file version index, default to latest - returns `Promise` - Promise with stack file uri @@ -760,11 +827,11 @@ See: https://github.com/jimmywarting/StreamSaver.js#configuration ### folder -#### `create(vaultId, name, parentId)` +#### `create(vaultId, name)` - `vaultId` (`string`, required) - `name` (`string`, required) - folder name -- `parentId` (`string`, optional) - parent folder id +- `options` (`NodeCreateOptions`, optional) - parent id, etc. - returns `Promise<{ folderId, transactionId }>` - Promise with new folder id & corresponding transaction id
@@ -853,10 +920,10 @@ const { transactionId } = await akord.folder.delete(folderId); ```
-#### `get(folderId, shouldDecrypt)` +#### `get(folderId, options)` - `folderId` (`string`, required) -- `shouldDecrypt` (`boolean`, optional) - default to true +- `options` ([`GetOptions`][get-options], optional) - returns `Promise` - Promise with the folder object
@@ -867,10 +934,10 @@ const folder = await akord.folder.get(folderId); ```
-#### `listAll(vaultId, listOptions)` +#### `listAll(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise>` - Promise with all folders within given vault
@@ -881,10 +948,10 @@ const folders = await akord.folder.listAll(vaultId); ```
-#### `list(vaultId, listOptions)` +#### `list(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise<{ items, nextToken }>` - Promise with paginated folders within given vault
@@ -910,13 +977,12 @@ do { ### note -#### `create(vaultId, content, name, parentId)` +#### `create(vaultId, content, name)` - `vaultId` (`string`, required) - `content` (`string`, required) - note text content, ex: stringified JSON - `name` (`string`, required) - note name -- `parentId` (`string`, optional) - parent folder id -- `mimeType` (`string`, optional) - MIME type for the note text file, default: text/markdown +- `options` (`NoteCreateOptions`, optional) - parent id, mime type, etc. - returns `Promise<{ noteId, transactionId }>` - Promise with new note id & corresponding transaction id
@@ -929,8 +995,7 @@ const { noteId } = await akord.note.create( vaultId, JSON.stringify({ name: "My first JSON note" }), "My first JSON note", - parentId, - "application/json" + { parentId: parentId, mimeType: "application/json" } ); ```
@@ -940,14 +1005,14 @@ const { noteId } = await akord.note.create( - `noteId` (`string`, required) - `content` (`string`, required) - note text content, ex: stringified JSON - `name` (`string`, required) - note name -- `mimeType` (`string`, optional) - MIME type for the note text file, default: text/markdown +- `options` (`NoteOptions`, optional) - mime type, etc. - returns `Promise<{ transactionId }>` - Promise with corresponding transaction id
example ```js -const { transactionId } = await akord.note.uploadRevision(vaultId, "# Hello World bis", "Hello World note bis"); +const { transactionId } = await akord.note.uploadRevision(noteId, "# Hello World bis", "Hello World note bis"); ```
@@ -1007,10 +1072,10 @@ const { transactionId } = await akord.note.delete(noteId); ```
-#### `get(noteId, shouldDecrypt)` +#### `get(noteId, options)` - `noteId` (`string`, required) -- `shouldDecrypt` (`boolean`, optional) - default to true +- `options` ([`GetOptions`][get-options], optional) - returns `Promise` - Promise with the note object
@@ -1021,10 +1086,10 @@ const note = await akord.note.get(noteId); ```
-#### `listAll(vaultId, listOptions)` +#### `listAll(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise>` - Promise with all notes within given vault
@@ -1035,10 +1100,10 @@ const notes = await akord.note.listAll(vaultId); ```
-#### `list(vaultId, listOptions)` +#### `list(vaultId, options)` - `vaultId` (`string`, required) -- `listOptions` ([`ListOptions`](https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/list-options.ts#L1), optional) +- `options` ([`ListOptions`][list-options], optional) - returns `Promise<{ items, nextToken }>` - Promise with paginated notes within given vault
@@ -1172,44 +1237,43 @@ Update user profile along with all active memberships #### `revoke(items)` -- `items` (`Array<{ id: string, type: string }>`, required) +- `items` (`Array<{ id: string, type: `[`NodeType`][node-type]` }>`, required) - returns `Promise>` - Promise with corresponding transaction ids #### `restore(items)` -- `items` (`Array<{ id: string, type: string }>`, required) +- `items` (`Array<{ id: string, type: `[`NodeType`][node-type]` }>`, required) - returns `Promise>` - Promise with corresponding transaction ids #### `delete(items)` -- `items` (`Array<{ id: string, type: string }>`, required) +- `items` (`Array<{ id: string, type: `[`NodeType`][node-type]` }>`, required) - returns `Promise>` - Promise with corresponding transaction ids #### `move(items, parentId)` -- `items` (`Array<{ transactionId }>`, required) +- `items` (`Array<{ id: string, type: `[`NodeType`][node-type]` }>`, required) - `parentId` (`string`, optional) - returns `Promise>` - Promise with corresponding transaction ids #### `membershipChangeRole(items)` -- `items` (`Array<{ transactionId }>`, required) +- `items` (`Array<{ id: string, role: `[`RoleType`][role-type]` }>`, required) - returns `Promise>` - Promise with corresponding transaction ids -#### `stackCreate(vaultId, items, parentId, progressHook, cancelHook)` +#### `stackCreate(vaultId, items)` - `vaultId` (`string`, required) -- `items` (`Array<{ transactionId }>`, required) -- `parentId` (`string`, optional) -- `progressHook` (`(progress:number)=>void`, optional) -- `cancelHook` (`AbortController`, optional) -- returns `Promise>` - Promise with new stack ids & their corresponding transaction ids +- `items` (`Array<{ file: `[`FileLike`][file-like]`, name: string }>`, required) +- `options` (`BatchStackCreateOptions`, optional) +- returns `Promise<`[`BatchStackCreateResponse`][batch-stack-create-response]`>` - Promise with new stack ids & their corresponding transaction ids #### `membershipInvite(vaultId, items)` - `vaultId` (`string`, required) -- `items` (`Array<{ transactionId }>`, required) -- returns `Promise>` - Promise with new membership ids & their corresponding transaction ids +- `items` (`Array<{ email: string, role: `[`RoleType`][role-type]` }>`, required) +- `options` (`MembershipCreateOptions`, optional) - invitation email message, etc. +- returns `Promise<`[`BatchMembershipInviteResponse`][batch-membership-invite-response]`>` - Promise with new membership ids & their corresponding transaction ids ### Development > requires Node.js 16 @@ -1247,3 +1311,14 @@ After merging your PR to `main`: - it will update package version - will create a release - will build and publish it to NPM + +[list-options]: https://github.com/Akord-com/akord-js/blob/193062c541ad06c186d5b872ecf9066d15806b43/src/types/query-options.ts#L1 +[get-options]: https://github.com/Akord-com/akord-js/blob/193062c541ad06c186d5b872ecf9066d15806b43/src/types/query-options.ts#L9 +[vault-get-options]: https://github.com/Akord-com/akord-js/blob/193062c541ad06c186d5b872ecf9066d15806b43/src/types/query-options.ts#L14 +[file-like]: https://github.com/Akord-com/akord-js/blob/ab9bb814fa9cf73d9ed01052738c8b84a86040b2/src/types/file.ts#L8 +[storage-type]: https://github.com/Akord-com/akord-js/blob/26d1945bee727a1af45f0f9cc44c7fa9b68c5d75/src/types/node.ts#L149 +[role-type]: https://github.com/Akord-com/akord-js/blob/03e28ffd95224dbfd0a8d891a06a154298619378/src/types/membership.ts#L4 +[node-type]: https://github.com/Akord-com/akord-js/blob/03e28ffd95224dbfd0a8d891a06a154298619378/src/types/node.ts#L11 +[batch-stack-create-response]: https://github.com/Akord-com/akord-js/blob/03e28ffd95224dbfd0a8d891a06a154298619378/src/types/batch-response.ts#L1 +[batch-membership-invite-response]: https://github.com/Akord-com/akord-js/blob/03e28ffd95224dbfd0a8d891a06a154298619378/src/types/batch-response.ts#L7 +[file-upload-example]:https://github.com/Akord-com/recipes/blob/a2dbc847097973ef08586f32b0ce3192f0581ed4/nextjs-starter/src/pages/index.tsx#L66 diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index a1db3ce9..00000000 --- a/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: [['@babel/preset-env', {targets: {node: 'current'}}]], -}; \ No newline at end of file diff --git a/package.json b/package.json index a7a06e6d..3399ea1c 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,11 @@ "node": ">=16.0.0" }, "dependencies": { - "@akord/crypto": "0.10.1", + "@akord/akord-auth": "0.25.0", + "@akord/crypto": "0.10.6", "@akord/ts-cacheable": "1.0.11", - "amazon-cognito-identity-js": "^5.2.8", "axios": "^0.24.0", "blueimp-load-image": "^5.16.0", - "cross-fetch": "^3.1.5", "lodash": "^4.17.21", "mime-types": "^2.1.35", "pdfjs-dist": "^2.16.105", @@ -46,7 +45,6 @@ "web-streams-polyfill": "^3.2.1" }, "devDependencies": { - "@babel/preset-env": "^7.12.1", "@faker-js/faker": "^6.2.0", "@types/blueimp-load-image": "^5.16.0", "@types/jest": "^27.4.0", diff --git a/src/__tests__/batch.test.ts b/src/__tests__/batch.test.ts index bf3e82aa..ce8513c4 100644 --- a/src/__tests__/batch.test.ts +++ b/src/__tests__/batch.test.ts @@ -1,34 +1,20 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, folderCreate, noteCreate, testDataPath, vaultCreate } from './common'; import { email, email2, email3, password } from './data/test-credentials'; import { NodeJs } from "../types/file"; +import { firstFileName } from "./data/content"; let akord: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; -} - describe("Testing batch actions", () => { let vaultId: string; let folderId: string; let noteId: string; - let membershipId1: string; - let membershipId2: string; + let viewerId: string; + let contributorId: string; beforeEach(async () => { akord = await initInstance(email, password); @@ -36,28 +22,16 @@ describe("Testing batch actions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; }); describe("Batch revoke/restore actions", () => { it("should create folder", async () => { - const name = faker.random.words(); - folderId = (await akord.folder.create(vaultId, name)).folderId; - - const folder = await akord.folder.get(folderId); - expect(folder.status).toEqual("ACTIVE"); - expect(folder.parentId).toBeFalsy(); - expect(folder.name).toEqual(name); + folderId = await folderCreate(akord, vaultId); }); it("should create note", async () => { - const name = faker.random.words(); - const content = faker.lorem.sentences(); - - noteId = (await akord.note.create(vaultId, content, name)).noteId; - - const note = await akord.note.get(noteId); - expect(note.versions.length).toEqual(1); + noteId = await noteCreate(akord, vaultId); }); it("should revoke all items in a batch", async () => { @@ -89,22 +63,23 @@ describe("Testing batch actions", () => { describe("Batch upload", () => { it("should upload a batch of 10 files", async () => { - const file = NodeJs.File.fromPath("./src/__tests__/data/logo.png"); + const file = await NodeJs.File.fromPath(testDataPath + firstFileName); const items = [] as { file: any, name: string }[]; - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 2; i++) { const name = faker.random.words(); items.push({ file, name }); } - const response = await akord.batch.stackCreate(vaultId, items); + const response = (await akord.batch.stackCreate(vaultId, items)).data; for (let index in items) { const stack = await akord.stack.get(response[index].stackId); expect(stack.status).toEqual("ACTIVE"); + expect(stack.name).toEqual(response[index].object.name); expect(stack.versions.length).toEqual(1); - expect(stack.versions[0].title).toEqual("logo.png"); + expect(stack.versions[0].name).toEqual(firstFileName); } }); }); @@ -120,11 +95,11 @@ describe("Testing batch actions", () => { for (let item of response) { const membership = await akord.membership.get(item.membershipId); if (membership.email === email2) { - membershipId1 = item.membershipId; + contributorId = item.membershipId; expect(membership.status).toEqual("PENDING"); expect(membership.role).toEqual("CONTRIBUTOR"); } else { - membershipId2 = item.membershipId; + viewerId = item.membershipId; expect(membership.status).toEqual("PENDING"); expect(membership.role).toEqual("VIEWER"); } @@ -133,14 +108,14 @@ describe("Testing batch actions", () => { it("should change access", async () => { await akord.batch.membershipChangeRole([ - { id: membershipId1, role: "VIEWER" }, - { id: membershipId2, role: "CONTRIBUTOR" } + { id: contributorId, role: "VIEWER" }, + { id: viewerId, role: "CONTRIBUTOR" } ]) - const membership1 = await akord.membership.get(membershipId1); + const membership1 = await akord.membership.get(contributorId); expect(membership1.role).toEqual("VIEWER"); - const membership2 = await akord.membership.get(membershipId2); + const membership2 = await akord.membership.get(viewerId); expect(membership2.role).toEqual("CONTRIBUTOR"); }); }); diff --git a/src/__tests__/common.ts b/src/__tests__/common.ts new file mode 100644 index 00000000..203158ad --- /dev/null +++ b/src/__tests__/common.ts @@ -0,0 +1,53 @@ +import { Akord, Auth } from "../index"; +import faker from '@faker-js/faker'; + +export async function initInstance(email: string, password: string): Promise { + const { wallet } = await Auth.signIn(email, password); + return new Akord(wallet, { debug: true }); +} + +export const vaultCreate = async (akord: Akord) => { + const name = faker.random.words(); + const termsOfAccess = faker.lorem.sentences(); + const { vaultId, membershipId } = await akord.vault.create(name, { termsOfAccess }); + + const membership = await akord.membership.get(membershipId); + expect(membership.status).toEqual("ACCEPTED"); + expect(membership.role).toEqual("OWNER"); + + const vault = await akord.vault.get(vaultId); + expect(vault.status).toEqual("ACTIVE"); + expect(vault.name).toEqual(name); + return { vaultId, membershipId }; +} + +export const folderCreate = async (akord: Akord, vaultId: string, parentId?: string) => { + const name = faker.random.words(); + const { folderId } = await akord.folder.create(vaultId, name, { parentId: parentId }); + + const folder = await akord.folder.get(folderId); + expect(folder.status).toEqual("ACTIVE"); + if (parentId) { + expect(folder.parentId).toEqual(parentId); + } else { + expect(folder.parentId).toBeFalsy(); + } + expect(folder.name).toEqual(name); + return folderId; +} + +export const noteCreate = async (akord: Akord, vaultId: string) => { + const name = faker.random.words(); + const content = faker.lorem.sentences(); + + const { noteId } = await akord.note.create(vaultId, content, name); + + const note = await akord.note.get(noteId); + expect(note.versions.length).toEqual(1); + const { name: fileName, data } = await akord.note.getVersion(noteId); + expect(data).toEqual(content); + expect(fileName).toEqual(name); + return noteId; +} + +export const testDataPath = "./src/__tests__/data/"; diff --git a/src/__tests__/data/content.ts b/src/__tests__/data/content.ts index 433a9887..9e0df597 100644 --- a/src/__tests__/data/content.ts +++ b/src/__tests__/data/content.ts @@ -16,4 +16,8 @@ export const publicVaultId = "RNACU80_GqwUBS0annCpcaD7evriSU9mid_s-K0yvvA"; export const parentId = "33408770-f188-4586-a9ac-e50297a85eb5"; export const privateVaultId = 'j8C-RDy0F5zwQvrzesL0QaaKk7bjnLhYBFp8xpqV37k'; export const fileId = 'efb8ed89-3221-48e7-b1e5-9b94eef41319'; -export const message = 'Quae vel nemo dolorum recusandae temporibus temporibus. Hic id eum quo sint atque voluptates voluptatum. Vero sit aliquid. Nam omnis repudiandae. Corporis quasi ex id incidunt suscipit non. In ea impedit. Consequatur voluptatem et nihil ut debitis similique. Eligendi ut voluptatum fuga eos a ab iure alias molestiae. Sed reiciendis aut sapiente natus inventore iure nesciunt architecto veritatis. Ea omnis similique incidunt voluptas sit pariatur officia nihil cupiditate. Voluptas nobis molestias quia voluptas saepe labore quia consequatur dolorem. Ut quia quis ut iste inventore excepturi. Dolores incidunt eum quia aspernatur et architecto cumque provident architecto. Maiores occaecati suscipit repellat. Quia illum ex doloribus porro. Ipsa doloribus atque et aut molestiae quam magnam enim. Laborum totam adipisci non dolor neque quos saepe. Qui consequuntur sapiente quis non sit explicabo expedita. Odio voluptatibus neque maxime autem harum facilis optio. Eum esse ipsam itaque minima quia repudiandae. Cum eveniet voluptatum consequatur. Dolor mollitia perferendis fuga impedit quia enim consectetur non. Quibusdam nostrum sit at quae sit ducimus blanditiis a deserunt. Qui aperiam id est architecto ut. Ut dolores soluta consequatur at expedita. Et blanditiis nam quam cumque. Similique aut animi voluptas id quam. Id rerum est architecto dolorem quae rerum autem voluptatem. Id cum impedit est cumque quod dolores aut. Qui quo autem vel. Autem sapiente voluptates rerum architecto necessitatibus doloremque consequatur iure quo. Veritatis tenetur voluptatem praesentium. Et sit architecto veritatis modi voluptatem iusto voluptates neque. Soluta explicabo qui provident velit sunt. Quia nisi deleniti perferendis quo et enim explicabo eos. Tempore amet quisquam itaque et corrupti inventore quia illo deserunt. Voluptatum animi et. Earum in vitae nemo officia qui necessitatibus animi totam. Totam amet beatae ex totam quia est labore. Id voluptates voluptatibus. Beatae eos corporis sed ratione ipsa eum vel placeat. Voluptas est quam atque non iure necessitatibus. Ab et numquam excepturi rem vel hic delectus voluptatem. Ut et hic animi minus sint et. Commodi explicabo est quo veniam ratione quisquam iure ducimus sit. Molestias cum voluptas consectetur necessitatibus nobis eveniet veritatis reiciendis illo. Ex est officia eveniet eveniet enim voluptatum iusto labore vitae. Quam eum vero ratione veritatis ex voluptatibus. Vel repellendus voluptatum autem doloremque cupiditate cumque. Delectus aut esse occaecati.' \ No newline at end of file +export const message = 'Quae vel nemo dolorum recusandae temporibus temporibus. Hic id eum quo sint atque voluptates voluptatum. Vero sit aliquid. Nam omnis repudiandae. Corporis quasi ex id incidunt suscipit non. In ea impedit. Consequatur voluptatem et nihil ut debitis similique. Eligendi ut voluptatum fuga eos a ab iure alias molestiae. Sed reiciendis aut sapiente natus inventore iure nesciunt architecto veritatis. Ea omnis similique incidunt voluptas sit pariatur officia nihil cupiditate. Voluptas nobis molestias quia voluptas saepe labore quia consequatur dolorem. Ut quia quis ut iste inventore excepturi. Dolores incidunt eum quia aspernatur et architecto cumque provident architecto. Maiores occaecati suscipit repellat. Quia illum ex doloribus porro. Ipsa doloribus atque et aut molestiae quam magnam enim. Laborum totam adipisci non dolor neque quos saepe. Qui consequuntur sapiente quis non sit explicabo expedita. Odio voluptatibus neque maxime autem harum facilis optio. Eum esse ipsam itaque minima quia repudiandae. Cum eveniet voluptatum consequatur. Dolor mollitia perferendis fuga impedit quia enim consectetur non. Quibusdam nostrum sit at quae sit ducimus blanditiis a deserunt. Qui aperiam id est architecto ut. Ut dolores soluta consequatur at expedita. Et blanditiis nam quam cumque. Similique aut animi voluptas id quam. Id rerum est architecto dolorem quae rerum autem voluptatem. Id cum impedit est cumque quod dolores aut. Qui quo autem vel. Autem sapiente voluptates rerum architecto necessitatibus doloremque consequatur iure quo. Veritatis tenetur voluptatem praesentium. Et sit architecto veritatis modi voluptatem iusto voluptates neque. Soluta explicabo qui provident velit sunt. Quia nisi deleniti perferendis quo et enim explicabo eos. Tempore amet quisquam itaque et corrupti inventore quia illo deserunt. Voluptatum animi et. Earum in vitae nemo officia qui necessitatibus animi totam. Totam amet beatae ex totam quia est labore. Id voluptates voluptatibus. Beatae eos corporis sed ratione ipsa eum vel placeat. Voluptas est quam atque non iure necessitatibus. Ab et numquam excepturi rem vel hic delectus voluptatem. Ut et hic animi minus sint et. Commodi explicabo est quo veniam ratione quisquam iure ducimus sit. Molestias cum voluptas consectetur necessitatibus nobis eveniet veritatis reiciendis illo. Ex est officia eveniet eveniet enim voluptatum iusto labore vitae. Quam eum vero ratione veritatis ex voluptatibus. Vel repellendus voluptatum autem doloremque cupiditate cumque. Delectus aut esse occaecati.' + +export const firstFileName = "logo.png"; +export const secondFileName = "avatar.jpeg"; +export const arweaveImportFileTx = "kzGxbFW_oJ3PyYneRs9cPrChQ-k-8Fym5k9PCZNJ_HA"; \ No newline at end of file diff --git a/src/__tests__/folder.test.ts b/src/__tests__/folder.test.ts index b3f66472..885d1a61 100644 --- a/src/__tests__/folder.test.ts +++ b/src/__tests__/folder.test.ts @@ -1,27 +1,12 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, folderCreate, vaultCreate } from './common'; import { email, password } from './data/test-credentials'; let akord: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; -} - describe("Testing folder functions", () => { let vaultId: string; let rootFolderId: string; @@ -33,31 +18,15 @@ describe("Testing folder functions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; }); it("should create root folder", async () => { - const name = faker.random.words(); - const { folderId } = await akord.folder.create(vaultId, name); - - rootFolderId = folderId; - - const rootFolder = await akord.folder.get(rootFolderId); - expect(rootFolder.status).toEqual("ACTIVE"); - expect(rootFolder.parentId).toBeFalsy(); - expect(rootFolder.name).toEqual(name); + rootFolderId = await folderCreate(akord, vaultId); }); it("should create a sub folder", async () => { - const name = faker.random.words(); - const { folderId } = await akord.folder.create(vaultId, name, rootFolderId); - - subFolderId = folderId; - - const subFolder = await akord.folder.get(subFolderId); - expect(subFolder.status).toEqual("ACTIVE"); - expect(subFolder.parentId).toEqual(rootFolderId); - expect(subFolder.name).toEqual(name); + subFolderId = await folderCreate(akord, vaultId, rootFolderId); }); it("should revoke root folder", async () => { @@ -73,7 +42,7 @@ describe("Testing folder functions", () => { it("should fail adding new sub-folder to the revoked root folder", async () => { const name = faker.random.words(); await expect(async () => - await akord.folder.create(vaultId, name, rootFolderId) + await akord.folder.create(vaultId, name, { parentId: rootFolderId }) ).rejects.toThrow(Error); }); diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts deleted file mode 100644 index b5312318..00000000 --- a/src/__tests__/helpers.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Akord } from "../index"; - -export async function initInstance(email: string, password: string): Promise { - const { wallet, jwtToken } = await Akord.auth.signIn(email, password); - return new Akord(wallet, jwtToken, { debug: true }); -} \ No newline at end of file diff --git a/src/__tests__/manifest.test.ts b/src/__tests__/manifest.test.ts index 460470a0..05703b85 100644 --- a/src/__tests__/manifest.test.ts +++ b/src/__tests__/manifest.test.ts @@ -1,6 +1,5 @@ import { Akord } from "../index"; -import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, vaultCreate } from './common'; import { email, password } from './data/test-credentials'; import { createFileLike } from "../core/file"; @@ -36,30 +35,6 @@ const manifest = { } }; -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess, true); - console.log("created vault", name); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const htmlSrc = "

Hello World

"; - const htmlFile = await createFileLike([htmlSrc], "index.html", "text/html"); - const { stackId } = await akord.stack.create(vaultId, htmlFile, "index.html"); - console.log("uploaded index.html", stackId); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - - console.log(vaultId, name); - - return { vaultId }; -} - describe("Testing manifest functions", () => { let vaultId: string; @@ -69,7 +44,13 @@ describe("Testing manifest functions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; + + // upload html file + const htmlSrc = "

Hello World

"; + const htmlFile = await createFileLike([htmlSrc], "index.html", "text/html"); + const { stackId } = await akord.stack.create(vaultId, htmlFile, "index.html"); + console.log("uploaded index.html", stackId); }); // it("should create new manifest", async () => { diff --git a/src/__tests__/membership.test.ts b/src/__tests__/membership.test.ts index 7fc811a1..60e4e602 100644 --- a/src/__tests__/membership.test.ts +++ b/src/__tests__/membership.test.ts @@ -1,78 +1,108 @@ import { Akord } from "../index"; -import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; import { email, email2, password, password2 } from './data/test-credentials'; +import { initInstance, vaultCreate } from './common'; +import { Membership } from "../types/membership"; -let akord1: Akord; -let akord2: Akord; +let ownerAkordInstance: Akord; +let inviteeAkordInstance: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord1.vault.create(name, termsOfAccess); - - const membership = await akord1.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord1.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; +type Member = { + publicSigningKey: string, + address: string, + name?: string, + membershipId: string, + membership?: Membership } describe("Testing membership functions", () => { let vaultId: string; - let membershipId: string; - - beforeEach(async () => { - akord1 = await initInstance(email, password); - akord2 = await initInstance(email2, password2); - }); + let owner: Member; + let invitee: Member; beforeAll(async () => { - akord1 = await initInstance(email, password); - akord2 = await initInstance(email2, password2); - vaultId = (await vaultCreate()).vaultId; + inviteeAkordInstance = await initInstance(email2, password2); + const inviteeProfileDetails = await inviteeAkordInstance.profile.get(); + invitee = { + publicSigningKey: inviteeAkordInstance.vault.wallet.signingPublicKey(), + address: await inviteeAkordInstance.vault.wallet.getAddress(), + name: inviteeProfileDetails.name, + membershipId: "" + }; + ownerAkordInstance = await initInstance(email, password); + const ownerProfileDetails = await ownerAkordInstance.profile.get(); + owner = { + publicSigningKey: ownerAkordInstance.vault.wallet.signingPublicKey(), + address: await ownerAkordInstance.vault.wallet.getAddress(), + name: ownerProfileDetails.name, + membershipId: "" + }; + const vaultResult = await vaultCreate(ownerAkordInstance); + vaultId = vaultResult.vaultId; + owner.membershipId = vaultResult.membershipId; }); it("should invite new member", async () => { - membershipId = (await akord1.membership.invite(vaultId, email2, "CONTRIBUTOR")).membershipId; - - const membership = await akord1.membership.get(membershipId); - expect(membership.status).toEqual("PENDING"); - expect(membership.role).toEqual("CONTRIBUTOR"); + ownerAkordInstance = await initInstance(email, password); + + invitee.membershipId = (await ownerAkordInstance.membership.invite(vaultId, email2, "CONTRIBUTOR")).membershipId; + + invitee.membership = await ownerAkordInstance.membership.get(invitee.membershipId); + expect(invitee.membership.status).toEqual("PENDING"); + expect(invitee.membership.role).toEqual("CONTRIBUTOR"); + expect(invitee.membership.memberDetails.name).toBeFalsy(); + expect(invitee.membership.memberPublicSigningKey).toEqual(invitee.publicSigningKey); + expect(invitee.membership.address).toEqual(invitee.address); + expect(invitee.membership.owner).toEqual(owner.address); + + owner.membership = await ownerAkordInstance.membership.get(owner.membershipId); + expect(owner.membership.status).toEqual("ACCEPTED"); + expect(owner.membership.role).toEqual("OWNER"); + expect(owner.membership.memberDetails.name).toEqual(owner.name); + expect(owner.membership.memberPublicSigningKey).toEqual(owner.publicSigningKey); + expect(owner.membership.address).toEqual(owner.address); + expect(owner.membership.owner).toEqual(owner.address); }); it("should accept the invite", async () => { - await akord2.membership.accept(membershipId); + inviteeAkordInstance = await initInstance(email2, password2); + await inviteeAkordInstance.membership.accept(invitee.membershipId); - const membership = await akord2.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); + invitee.membership = await inviteeAkordInstance.membership.get(invitee.membershipId); + expect(invitee.membership.status).toEqual("ACCEPTED"); + expect(invitee.membership.memberDetails.name).toEqual(invitee.name); + expect(invitee.membership.memberPublicSigningKey).toEqual(invitee.publicSigningKey); + expect(invitee.membership.address).toEqual(invitee.address); + expect(invitee.membership.owner).toEqual(owner.address); // should be able to decrypt the vault name by the new member - await akord2.vault.get(vaultId); + await inviteeAkordInstance.vault.get(vaultId); }); it("should fail inviting the same member twice", async () => { + ownerAkordInstance = await initInstance(email, password); await expect(async () => - akord1.membership.invite(vaultId, email2, "VIEWER") + ownerAkordInstance.membership.invite(vaultId, email2, "VIEWER") ).rejects.toThrow(Error); }); it("should change access", async () => { - await akord1.membership.changeRole(membershipId, "VIEWER"); + ownerAkordInstance = await initInstance(email, password); + await ownerAkordInstance.membership.changeRole(invitee.membershipId, "VIEWER"); - const membership = await akord1.membership.get(membershipId); + const membership = await ownerAkordInstance.membership.get(invitee.membershipId); expect(membership.role).toEqual("VIEWER"); }); it("should revoke the invite", async () => { - await akord1.membership.revoke(membershipId); + ownerAkordInstance = await initInstance(email, password); + await ownerAkordInstance.membership.revoke(invitee.membershipId); - const membership = await akord1.membership.get(membershipId); + const membership = await ownerAkordInstance.membership.get(invitee.membershipId); expect(membership.status).toEqual("REVOKED"); + + // should be able to decrypt the vault by the owner + await ownerAkordInstance.vault.get(vaultId); }); }); \ No newline at end of file diff --git a/src/__tests__/memo.test.ts b/src/__tests__/memo.test.ts index 1f9a9ab5..5585f644 100644 --- a/src/__tests__/memo.test.ts +++ b/src/__tests__/memo.test.ts @@ -1,6 +1,6 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, vaultCreate } from './common'; import { reactionEmoji } from '../constants'; import { email, password } from './data/test-credentials'; @@ -8,21 +8,6 @@ let akord: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; -} - describe("Testing memo functions", () => { let vaultId: string; let memoId: string; @@ -33,7 +18,7 @@ describe("Testing memo functions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; }); it("should create new memo", async () => { diff --git a/src/__tests__/note.test.ts b/src/__tests__/note.test.ts index 0f5c67eb..f199d498 100644 --- a/src/__tests__/note.test.ts +++ b/src/__tests__/note.test.ts @@ -1,27 +1,12 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, noteCreate, vaultCreate } from './common'; import { email, password } from './data/test-credentials'; let akord: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; -} - describe("Testing note functions", () => { let vaultId: string; let noteId: string; @@ -32,20 +17,11 @@ describe("Testing note functions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; }); it("should create new note", async () => { - const name = faker.random.words(); - const content = faker.lorem.sentences(); - - noteId = (await akord.note.create(vaultId, content, name)).noteId; - - const note = await akord.note.get(noteId); - expect(note.versions.length).toEqual(1); - const { name: fileName, data } = await akord.note.getVersion(noteId); - expect(data).toEqual(content); - expect(fileName).toEqual(name); + noteId = await noteCreate(akord, vaultId); }); it("should upload new revision", async () => { @@ -56,7 +32,7 @@ describe("Testing note functions", () => { noteId, JSON.stringify({ content: content }), name, - "application/json" + { mimeType: "application/json" } ); const note = await akord.note.get(noteId); diff --git a/src/__tests__/profile.test.ts b/src/__tests__/profile.test.ts index f39693e1..d011b96f 100644 --- a/src/__tests__/profile.test.ts +++ b/src/__tests__/profile.test.ts @@ -1,8 +1,9 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, testDataPath } from './common'; import { email, password } from './data/test-credentials'; import { NodeJs } from "../types/file"; +import { firstFileName } from "./data/content"; let akord: Akord; @@ -16,13 +17,12 @@ describe("Testing profile functions", () => { it("should update the profile", async () => { const name = faker.random.words(); - const file = NodeJs.File.fromPath("./src/__tests__/data/logo.png"); + const file = await NodeJs.File.fromPath(testDataPath + firstFileName); const fileBuffer = await file.arrayBuffer(); await akord.profile.update(name, fileBuffer); const profileDetails = await akord.profile.get(); expect(profileDetails.name).toEqual(name); - expect(profileDetails.avatar).not.toBeNull(); - expect(Buffer.from(profileDetails.avatar || new ArrayBuffer(1))).toEqual(fileBuffer); + expect(profileDetails.avatar).toEqual(fileBuffer); }); }); \ No newline at end of file diff --git a/src/__tests__/queries.test.ts b/src/__tests__/queries.test.ts index 7ebeab39..a2582798 100644 --- a/src/__tests__/queries.test.ts +++ b/src/__tests__/queries.test.ts @@ -1,4 +1,4 @@ -import { Akord } from "../index"; +import { Akord, Auth } from "../index"; import { email, password } from './data/test-credentials'; import { vaults, fileId, message, publicVaultId, privateVaultId, parentId } from './data/content'; import { NodeJs } from "../types/file"; @@ -10,15 +10,15 @@ jest.setTimeout(3000000); describe("Testing querying directly from permaweb", () => { beforeAll(async () => { - const { jwtToken, wallet } = await Akord.auth.signIn(email, password); - privateClient = new Akord(wallet, jwtToken); + const { wallet } = await Auth.signIn(email, password); + privateClient = new Akord(wallet); publicClient = new Akord(); }); - // it("Query all vaults from Akord API", async () => { - // const result = await privateClient.vault.list(); - // expect(result).toEqual(vaults); - // }); + it("Query all vaults from Akord API", async () => { + const result = await privateClient.vault.listAll(); + expect(result).toBeTruthy(); + }); it("Should query public vault - contract state from Akord API", async () => { const contract = await publicClient.contract.getState(publicVaultId); @@ -34,7 +34,7 @@ describe("Testing querying directly from permaweb", () => { }); it("Query stacks by parent id", async () => { - const stacks = await publicClient.stack.listAll(publicVaultId, parentId); + const stacks = await publicClient.stack.listAll(publicVaultId, { parentId }); expect(stacks.length).toEqual(2); expect(stacks[0].parentId).toEqual(parentId); expect(stacks[1].parentId).toEqual(parentId); @@ -42,13 +42,13 @@ describe("Testing querying directly from permaweb", () => { // it("Query chunked file from Akord API", async () => { // const decryptedFile = await privateClient.file.get(fileId, vaults[0].id, { isChunked: true, numberOfChunks: 3 }); - // const file = NodeJs.File.fromPath("./src/__tests__/data/chunked-file.test"); + // const file = await NodeJs.File.fromPath("./src/__tests__/data/chunked-file.test"); // expect(Buffer.from(decryptedFile)).toEqual(await file.arrayBuffer()); // }); // it("Query chunked file from Akord API", async () => { // const decryptedFile = await privateClient.file.get(fileId, vaults[0].id, { isChunked: true, numberOfChunks: 3 }); - // const file = NodeJs.File.fromPath("./src/__tests__/data/chunked-file.test"); + // const file = await NodeJs.File.fromPath("./src/__tests__/data/chunked-file.test"); // expect(Buffer.from(decryptedFile)).toEqual(await file.arrayBuffer()); // }); }); diff --git a/src/__tests__/stack.test.ts b/src/__tests__/stack.test.ts index 2a59fb03..76f626ab 100644 --- a/src/__tests__/stack.test.ts +++ b/src/__tests__/stack.test.ts @@ -1,30 +1,16 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, testDataPath, vaultCreate } from './common'; import { email, password } from './data/test-credentials'; import { NodeJs } from "../types/file"; import { StorageType } from "../types/node"; import { getTxData } from "../arweave"; +import { firstFileName, secondFileName, arweaveImportFileTx } from './data/content'; let akord: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; -} - describe("Testing stack functions", () => { let vaultId: string; let stackId: string; @@ -35,88 +21,113 @@ describe("Testing stack functions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; }); it("should create new stack", async () => { - const name = faker.random.words(); + stackId = await stackCreate(vaultId); + }); - const file = NodeJs.File.fromPath("./src/__tests__/data/logo.png"); + it("should upload new revision", async () => { + await stackUploadRevision(stackId); + }); - stackId = (await akord.stack.create(vaultId, file, name)).stackId; + it("should rename the stack", async () => { + await stackRename(stackId); + }); - const stack = await akord.stack.get(stackId); - expect(stack.status).toEqual("ACTIVE"); - expect(stack.name).toEqual(name); - expect(stack.versions.length).toEqual(1); - expect(stack.versions[0].name).toEqual("logo.png"); + it("should revoke the stack", async () => { + await stackRevoke(stackId); + }); - const binary = await akord.file.get(stack.getUri(StorageType.S3, 0), vaultId); - expect(binary).toEqual(await file.arrayBuffer()); + it("should restore the stack", async () => { + await stackRestore(stackId); }); - it("should upload new revision", async () => { - const file = NodeJs.File.fromPath("./src/__tests__/data/avatar.jpeg"); + it("should import new stack from arweave tx", async () => { + await stackImport(vaultId); + }); +}); - await akord.stack.uploadRevision(stackId, file); +export const stackCreate = async (vaultId: string) => { + const name = faker.random.words(); - const stack = await akord.stack.get(stackId); - expect(stack.versions.length).toEqual(2); - expect(stack.versions[0].name).toEqual("logo.png"); - expect(stack.versions[1].name).toEqual("avatar.jpeg"); + const file = await NodeJs.File.fromPath(testDataPath + firstFileName); - const { data } = await akord.stack.getVersion(stackId); - expect(data).toEqual(await file.arrayBuffer()); + const { stackId } = await akord.stack.create(vaultId, file, name); - const firstFile = NodeJs.File.fromPath("./src/__tests__/data/logo.png"); - const { data: firstFileData } = await akord.stack.getVersion(stackId, 0); - expect(firstFileData).toEqual(await firstFile.arrayBuffer()); - }); + const stack = await akord.stack.get(stackId); + expect(stack.status).toEqual("ACTIVE"); + expect(stack.name).toEqual(name); + expect(stack.versions.length).toEqual(1); + expect(stack.versions[0].name).toEqual(firstFileName); - it("should rename the stack", async () => { - const name = faker.random.words(); + const binary = await akord.file.get(stack.getUri(StorageType.S3, 0), vaultId); + expect(binary).toEqual(await file.arrayBuffer()); + return stackId; +} - await akord.stack.rename(stackId, name); +export const stackUploadRevision = async (stackId: string) => { + const file = await NodeJs.File.fromPath(testDataPath + secondFileName); - const stack = await akord.stack.get(stackId); - expect(stack.name).toEqual(name); - expect(stack.versions.length).toEqual(2); - expect(stack.versions[0].name).toEqual("logo.png"); - expect(stack.versions[1].name).toEqual("avatar.jpeg"); + await akord.stack.uploadRevision(stackId, file); - const firstFile = NodeJs.File.fromPath("./src/__tests__/data/logo.png"); - const { data: firstFileData } = await akord.stack.getVersion(stackId, 0); - expect(firstFileData).toEqual(await firstFile.arrayBuffer()); + const stack = await akord.stack.get(stackId); + expect(stack.versions.length).toEqual(2); + expect(stack.versions[0].name).toEqual(firstFileName); + expect(stack.versions[1].name).toEqual(secondFileName); - const secondFile = NodeJs.File.fromPath("./src/__tests__/data/avatar.jpeg"); + const { data } = await akord.stack.getVersion(stackId); + expect(data).toEqual(await file.arrayBuffer()); - const { data: secondFileData } = await akord.stack.getVersion(stackId); - expect(secondFileData).toEqual(await secondFile.arrayBuffer()); - }); + const firstFile = await NodeJs.File.fromPath(testDataPath + firstFileName); + const { data: firstFileData } = await akord.stack.getVersion(stackId, 0); + expect(firstFileData).toEqual(await firstFile.arrayBuffer()); +} - it("should revoke the stack", async () => { - await akord.stack.revoke(stackId) - const stack = await akord.stack.get(stackId); - expect(stack.status).toEqual("REVOKED"); - }); +export const stackRename = async (stackId: string) => { + const name = faker.random.words(); - it("should restore the stack", async () => { - await akord.stack.restore(stackId) - const stack = await akord.stack.get(stackId); - expect(stack.status).toEqual("ACTIVE"); - }); + await akord.stack.rename(stackId, name); - it("should import new stack from arweave tx", async () => { - const fileTxId = "kzGxbFW_oJ3PyYneRs9cPrChQ-k-8Fym5k9PCZNJ_HA"; - const fileName = fileTxId + ".jpeg"; - const { stackId } = await akord.stack.import(vaultId, fileTxId); + const stack = await akord.stack.get(stackId); + expect(stack.name).toEqual(name); + expect(stack.versions.length).toEqual(2); + expect(stack.versions[0].name).toEqual(firstFileName); + expect(stack.versions[1].name).toEqual(secondFileName); - const stack = await akord.stack.get(stackId); - expect(stack.name).toEqual(fileName); - expect(stack.versions.length).toEqual(1); - expect(stack.versions[0].name).toEqual(fileName); + const firstFile = await NodeJs.File.fromPath(testDataPath + firstFileName); + const { data: firstFileData } = await akord.stack.getVersion(stackId, 0); + expect(firstFileData).toEqual(await firstFile.arrayBuffer()); - const { data } = await akord.stack.getVersion(stackId); - expect(data).toEqual(await getTxData(fileTxId)); - }); -}); + const secondFile = await NodeJs.File.fromPath(testDataPath + secondFileName); + + const { data: secondFileData } = await akord.stack.getVersion(stackId); + expect(secondFileData).toEqual(await secondFile.arrayBuffer()); +} + +export const stackRevoke = async (stackId: string) => { + await akord.stack.revoke(stackId) + const stack = await akord.stack.get(stackId); + expect(stack.status).toEqual("REVOKED"); +} + +export const stackRestore = async (stackId: string) => { + await akord.stack.restore(stackId) + const stack = await akord.stack.get(stackId); + expect(stack.status).toEqual("ACTIVE"); +} + +export const stackImport = async (vaultId: string) => { + const fileName = arweaveImportFileTx + ".jpeg"; + const { stackId } = await akord.stack.import(vaultId, arweaveImportFileTx); + + const stack = await akord.stack.get(stackId); + expect(stack.name).toEqual(fileName); + expect(stack.versions.length).toEqual(1); + expect(stack.versions[0].name).toEqual(fileName); + + const { data } = await akord.stack.getVersion(stackId); + expect(data).toEqual(await getTxData(arweaveImportFileTx)); + return stackId; +} diff --git a/src/__tests__/vault.test.ts b/src/__tests__/vault.test.ts index f5f12146..24fbbcb2 100644 --- a/src/__tests__/vault.test.ts +++ b/src/__tests__/vault.test.ts @@ -1,29 +1,14 @@ import { Akord } from "../index"; import faker from '@faker-js/faker'; -import { initInstance } from './helpers'; +import { initInstance, vaultCreate } from './common'; import { email, password } from './data/test-credentials'; let akord: Akord; jest.setTimeout(3000000); -async function vaultCreate() { - const name = faker.random.words(); - const termsOfAccess = faker.lorem.sentences(); - const { vaultId, membershipId } = await akord.vault.create(name, termsOfAccess); - - const membership = await akord.membership.get(membershipId); - expect(membership.status).toEqual("ACCEPTED"); - expect(membership.role).toEqual("OWNER"); - - const vault = await akord.vault.get(vaultId); - expect(vault.status).toEqual("ACTIVE"); - expect(vault.name).toEqual(name); - return { vaultId }; -} - describe("Testing vault functions", () => { - let vaultId: any + let vaultId: string; beforeEach(async () => { akord = await initInstance(email, password); @@ -31,7 +16,7 @@ describe("Testing vault functions", () => { beforeAll(async () => { akord = await initInstance(email, password); - vaultId = (await vaultCreate()).vaultId; + vaultId = (await vaultCreate(akord)).vaultId; }); it("should rename the vault", async () => { diff --git a/src/akord.ts b/src/akord.ts index e5f3b6ef..dac06c6f 100644 --- a/src/akord.ts +++ b/src/akord.ts @@ -12,7 +12,6 @@ import { StackService } from "./core/stack"; import { NoteService } from "./core/note"; import { ManifestService } from "./core/manifest"; import { ProfileService } from "./core/profile"; -import { Auth } from "./auth"; import { CacheBusters } from "./types/cacheable"; import { FileService } from "./core/file"; import { BatchService } from "./core/batch"; @@ -34,20 +33,17 @@ export class Akord { public batch: BatchService; public contract: ContractService; - public static init: (wallet: Wallet, jwtToken?: string, config?: ClientConfig) => Promise; - public static auth = new Auth(); + public static init: (wallet: Wallet, config?: ClientConfig) => Promise; - // TODO: JWT token provider /** * @param {ClientConfig} config * @param {Wallet} [wallet] - * @param {string} [jwtToken] */ - constructor(wallet?: Wallet, jwtToken?: string, config: ClientConfig = {}) { + constructor(wallet?: Wallet, config: ClientConfig = {}) { Logger.debug = config.debug; CacheBusters.cache = config.cache; Crypto.configure({ wallet: wallet }); - this.api = config.api ? config.api : new AkordApi(config, jwtToken); + this.api = config.api ? config.api : new AkordApi(config); this.vault = new VaultService(wallet, this.api); this.memo = new MemoService(wallet, this.api); this.folder = new FolderService(wallet, this.api); diff --git a/src/api/akord-api.ts b/src/api/akord-api.ts index ab15b789..893efd25 100644 --- a/src/api/akord-api.ts +++ b/src/api/akord-api.ts @@ -7,56 +7,58 @@ import { Membership, MembershipKeys, RoleType } from "../types/membership"; import { ContractInput, ContractState, Tags } from "../types/contract"; import { NodeType } from "../types/node"; import { Vault } from "../types/vault"; +import { Transaction } from "../types/transaction"; import { Paginated } from "../types/paginated"; +import { ListOptions, VaultApiGetOptions } from "../types/query-options"; +import { User, UserPublicInfo } from "../types/user"; +import { FileDownloadOptions, FileUploadOptions } from "../core/file"; +import { AxiosResponseHeaders } from "axios"; + +export const defaultFileUploadOptions = { + shouldBundleTransaction: true, + public: false +}; export default class AkordApi extends Api { public config!: ApiConfig; - public jwtToken: string; - constructor(config: ClientConfig, jwtToken: string) { + constructor(config: ClientConfig) { super(); this.config = apiConfig(config.env); - this.jwtToken = jwtToken; } - public async uploadData(items: { data: any, tags: Tags, metadata?: any }[], shouldBundleTransaction?: boolean) - : Promise> { - + public async uploadData(items: { data: any, tags: Tags }[], options: FileUploadOptions = defaultFileUploadOptions) + : Promise> { const resources = []; await Promise.all(items.map(async (item, index) => { const resource = await new ApiClient() .env(this.config) - .auth(this.jwtToken) - .data(item.data) - .tags(item.tags) - .bundle(shouldBundleTransaction) - .metadata(item.metadata) + .data({ data: item.data, tags: item.tags }) + .bundle(options.shouldBundleTransaction) .uploadState() - Logger.log("Uploaded state with id: " + resource.id); + Logger.log("Uploaded state with id: " + resource); resources[index] = resource; })); return resources; }; - public async postContractTransaction(contractId: string, input: ContractInput, tags: Tags, metadata?: any): Promise { - const txId = await new ApiClient() + public async postContractTransaction(contractId: string, input: ContractInput, tags: Tags, metadata?: any): Promise<{ id: string, object: T }> { + const { id, object } = await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(contractId) + .metadata(metadata) .input(input) - .metadata(metadata ? metadata : {}) .tags(tags) - .transaction() - Logger.log("Uploaded contract interaction with id: " + txId); - return txId; + .transaction() + Logger.log("Uploaded contract interaction with id: " + id); + return { id, object }; }; public async getMembers(vaultId: string): Promise> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .getMembers(); }; @@ -64,102 +66,107 @@ export default class AkordApi extends Api { public async initContractId(tags: Tags, state?: any): Promise { const contractId = await new ApiClient() .env(this.config) - .auth(this.jwtToken) .data({ tags, state }) .contract() Logger.log("Created contract with id: " + contractId); return contractId; }; - public async getUserFromEmail(email: string): Promise { - return await new ApiClient() - .env(this.config) - .auth(this.jwtToken) - .resourceId(email) - .getUser(); - }; - - public async uploadFile(file: any, tags: Tags, isPublic?: boolean, shouldBundleTransaction?: boolean, progressHook?: (progress: number, data?: any) => void, cancelHook?: AbortController): Promise<{ resourceUrl: string, resourceTx: string }> { + public async uploadFile(file: ArrayBuffer, tags: Tags, options: FileUploadOptions = defaultFileUploadOptions): Promise<{ resourceUrl: string, resourceTx: string }> { + const uploadOptions = { + ...defaultFileUploadOptions, + ...options + } const resource = await new ApiClient() .env(this.config) - .auth(this.jwtToken) .data(file) .tags(tags) - .public(isPublic) - .bundle(shouldBundleTransaction) - .progressHook(progressHook) - .cancelHook(cancelHook) + .public(uploadOptions.public) + .bundle(uploadOptions.shouldBundleTransaction) + .progressHook(uploadOptions.progressHook) + .cancelHook(uploadOptions.cancelHook) .uploadFile() Logger.log("Uploaded file with id: " + resource.id); return resource; }; - public async downloadFile(id: string, isPublic?: boolean, progressHook?: (progress: number, data?: any) => void, cancelHook?: AbortController, numberOfChunks?: number, loadedSize?: number, resourceSize?: number): Promise { + public async downloadFile(id: string, options: FileDownloadOptions = {}): Promise<{ fileData: ArrayBuffer, headers: AxiosResponseHeaders }> { const { response } = await new ApiClient() .env(this.config) - .auth(this.jwtToken) .resourceId(id) - .public(isPublic) - .numberOfChunks(numberOfChunks) - .progressHook(progressHook, loadedSize, resourceSize) - .cancelHook(cancelHook) + .public(options.public) + .numberOfChunks(options.numberOfChunks) + .progressHook(options.progressHook, options.loadedSize, options.resourceSize) + .cancelHook(options.cancelHook) .asArrayBuffer() .downloadFile(); - let fileData: any; - if (response.headers['x-amz-meta-encryptedkey']) { - fileData = response.data; - } else { - fileData = Buffer.from(response.data).toJSON(); - } + const fileData = response.data; return { fileData: fileData, headers: response.headers }; }; - public async getProfile(): Promise { + public async existsUser(email: string): Promise { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) - .getProfile(); + .resourceId(email) + .existsUser(); + } + + public async getUserPublicData(email: string): Promise { + return await new ApiClient() + .env(this.config) + .resourceId(email) + .getUserPublicData(); + }; + + public async getUser(): Promise { + return await new ApiClient() + .env(this.config) + .getUser(); }; - public async updateProfile(name: string, avatarUri: string): Promise { + public async updateUser(name: string, avatarUri: string[]): Promise { await new ApiClient() .env(this.config) - .auth(this.jwtToken) .data({ name: name, avatarUri: avatarUri }) - .updateProfile(); + .updateUser(); }; public async deleteVault(vaultId: string): Promise { await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .deleteVault(); } - public async inviteNewUser(vaultId: string, email: string, role: RoleType): Promise<{ id: string }> { + public async inviteNewUser(vaultId: string, email: string, role: RoleType, message?: string): Promise<{ id: string }> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .data({ email: email, role: role }) + .metadata({ message }) .invite(); } + public async revokeInvite(vaultId: string, membershipId: string): Promise<{ id: string }> { + return await new ApiClient() + .env(this.config) + .vaultId(vaultId) + .resourceId(membershipId) + .revokeInvite(); + } public async inviteResend(vaultId: string, membershipId: string): Promise<{ id: string }> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .resourceId(membershipId) .inviteResend(); @@ -168,7 +175,6 @@ export default class AkordApi extends Api { public async getNode(id: string, type: NodeType): Promise { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .resourceId(id) .queryParams({ type }) .getNode(); @@ -177,23 +183,27 @@ export default class AkordApi extends Api { public async getMembership(id: string): Promise { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .resourceId(id) .getMembership(); }; - public async getVault(id: string): Promise { + public async getVault(id: string, options?: VaultApiGetOptions): Promise { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .resourceId(id) + .queryParams({ + withNodes: options?.withNodes, + withMemberships: options?.deep, + withMemos: options?.deep, + withStacks: options?.deep, + withFolders: options?.deep, + }) .getVault(); }; public async getMembershipKeys(vaultId: string): Promise { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .getMembershipKeys(); }; @@ -201,7 +211,6 @@ export default class AkordApi extends Api { public async getNodeState(stateId: string): Promise { const { response } = await new ApiClient() .env(this.config) - .auth(this.jwtToken) .resourceId(stateId) .downloadState() @@ -211,7 +220,6 @@ export default class AkordApi extends Api { public async getNotifications(): Promise> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .getNotifications() }; @@ -223,7 +231,6 @@ export default class AkordApi extends Api { }): Promise { await new ApiClient() .env(this.config) - .auth(this.jwtToken) .queryParams(options) .patchNotifications() }; @@ -231,56 +238,63 @@ export default class AkordApi extends Api { public async getContractState(objectId: string): Promise { const contract = await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(objectId) .getContract(); return contract.state; }; - public async getMemberships(): Promise> { + public async getMemberships(limit?: number, nextToken?: string): Promise> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) + .queryParams({ + limit, + nextToken + }) .getMemberships(); }; - public async getVaults(): Promise> { + public async getVaults(filter = {}, limit?: number, nextToken?: string): Promise> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) + .queryParams({ + filter: JSON.stringify(filter), + limit, + nextToken + }) .getVaults(); }; - public async getNodesByVaultId(vaultId: string, type: NodeType, parentId?: string, filter = {}, limit?: number, nextToken?: string): Promise> { + public async getNodesByVaultId(vaultId: string, type: NodeType, options: ListOptions): Promise> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .queryParams({ type, - parentId, - filter: JSON.stringify(filter), - limit, - nextToken + parentId: options.parentId, + filter: JSON.stringify(options.filter ? options.filter : {}), + limit: options.limit, + nextToken: options.nextToken }) .getNodesByVaultId(); }; - public async getMembershipsByVaultId(vaultId: string, filter = {}, limit?: number, nextToken?: string): Promise> { + public async getMembershipsByVaultId(vaultId: string, options: ListOptions): Promise> { return await new ApiClient() .env(this.config) - .auth(this.jwtToken) .vaultId(vaultId) .queryParams({ - filter: JSON.stringify(filter), - limit, - nextToken + filter: JSON.stringify(options.filter ? options.filter : {}), + limit: options.limit, + nextToken: options.nextToken }) .getMembershipsByVaultId(); }; - public async getTransactions(vaultId: string): Promise> { - throw new Error("Method not implemented"); + public async getTransactions(vaultId: string): Promise> { + return await new ApiClient() + .env(this.config) + .vaultId(vaultId) + .getTransactions(); } } diff --git a/src/api/api-authenticator.ts b/src/api/api-authenticator.ts deleted file mode 100644 index 6d183189..00000000 --- a/src/api/api-authenticator.ts +++ /dev/null @@ -1,133 +0,0 @@ -import "cross-fetch/polyfill"; -import * as AmazonCognitoIdentity from "amazon-cognito-identity-js"; -import { EnvType } from "../config"; -import { apiConfig, ApiConfig } from "./config"; -import { Logger } from "../logger"; - -export default class ApiAuthenticator { - public config!: ApiConfig; - - constructor(config?: EnvType) { - this.config = apiConfig(config); - } - - public getCognitoUser(username: string): AmazonCognitoIdentity.CognitoUser { - const userPool = this.getCognitoUserPool(); - const userData = { - Username: username, - Pool: userPool - }; - const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData); - return cognitoUser; - } - - public getCognitoUserPool(): AmazonCognitoIdentity.CognitoUserPool { - const poolData = { - UserPoolId: this.config.aws_user_pools_id, - ClientId: this.config.aws_user_pools_web_client_id - }; - const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData); - return userPool; - } - - public async getJWTToken(username: string, password: string): Promise { - const { session } = await this.authenticateUser(username, password); - return session.getIdToken().getJwtToken(); - } - - public async authenticateUser(username: string, password: string): Promise<{ - user: AmazonCognitoIdentity.CognitoUser, - session: AmazonCognitoIdentity.CognitoUserSession - }> { - const cognitoUser = this.getCognitoUser(username); - const authenticationData = { - Username: username, - Password: password, - }; - const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData); - return new Promise((resolve, reject) => { - cognitoUser.authenticateUser(authenticationDetails, { - onSuccess: function (result) { - resolve({ user: cognitoUser, session: result }) - }, - onFailure: function (err) { - Logger.log(err.message); - Logger.log(JSON.stringify(err)); - return reject(err.message); - } - }) - } - ); - } - - public async getUserAttributes(email: string, password: string): Promise { - const { user } = await this.authenticateUser(email, password); - return new Promise((resolve, reject) => { - user.getUserAttributes(async function (err, result) { - if (err) { - Logger.log(err.message); - Logger.log(JSON.stringify(err)); - reject(err.message); - } - const attributes = result.reduce(function ( - attributesObject, - attribute - ) { - attributesObject[attribute.Name] = attribute.Value; - return attributesObject; - }, {}); - resolve(attributes); - }) - } - ); - } - - public async signUp(email: string, password: string, customAttributes: any, clientMetadata?: any): Promise { - let attributes = []; - - for (const [key, value] of Object.entries(customAttributes)) { - attributes.push(new AmazonCognitoIdentity.CognitoUserAttribute({ - Name: key, - Value: value - })); - } - - const userPool = this.getCognitoUserPool(); - - return new Promise((resolve, reject) => - userPool.signUp(email, password, attributes, null, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }, clientMetadata) - ); - } - - public async resendCode(email: string): Promise { - const user = this.getCognitoUser(email); - return new Promise((resolve, reject) => - user.resendConfirmationCode((err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }) - ); - } - - public async verifyAccount(email: string, code: string): Promise { - const user = this.getCognitoUser(email); - return new Promise((resolve, reject) => - user.confirmRegistration(code, false, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }) - ); - } -} \ No newline at end of file diff --git a/src/api/api-client.ts b/src/api/api-client.ts index eca98402..826f7348 100644 --- a/src/api/api-client.ts +++ b/src/api/api-client.ts @@ -2,11 +2,17 @@ import axios, { AxiosRequestConfig } from "axios"; import { v4 as uuid } from "uuid"; import { Contract, ContractInput, Tags } from "../types/contract"; import { Membership, MembershipKeys } from "../types/membership"; -import { isPaginated, Paginated, PAGINATION_HEADER } from "../types/paginated"; +import { Transaction } from "../types/transaction"; +import { nextToken, isPaginated, Paginated } from "../types/paginated"; import { Vault } from "../types/vault"; +import { Auth } from "@akord/akord-auth"; +import { Unauthorized } from "../errors/unauthorized"; +import { throwError } from "../errors/error"; +import { BadRequest } from "../errors/bad-request"; +import { NotFound } from "../errors/not-found"; +import { User, UserPublicInfo } from "../types/user"; export class ApiClient { - private _jwt: string; private _storageurl: string; private _apiurl: string; private _dir: string = "files"; @@ -18,8 +24,8 @@ export class ApiClient { private _resourceId: string; private _isPublic: boolean; private _data: any; - private _queryParams: any; - private _dataRefs: string; + private _metadata: any; + private _queryParams: any = {}; private _responseType: string = "json"; private _progressHook: (progress: any, data?: any) => void private _processed: number @@ -28,7 +34,6 @@ export class ApiClient { private _tags: Tags; private _input: ContractInput; private _vaultId: string; - private _metadata: string; private _shouldBundleTransaction: boolean; private _numberOfChunks: number; @@ -40,11 +45,6 @@ export class ApiClient { return this; } - auth(jwt: string): ApiClient { - this._jwt = jwt; - return this; - } - resourceId(resourceId: string): ApiClient { this._resourceId = resourceId; return this; @@ -75,6 +75,11 @@ export class ApiClient { return this; } + metadata(metadata: any): ApiClient { + this._metadata = metadata; + return this; + } + queryParams(queryParams: any): ApiClient { this._queryParams = queryParams; return this; @@ -85,14 +90,6 @@ export class ApiClient { return this; } - metadata(metadata: any): ApiClient { - this._metadata = metadata - if (metadata && metadata.dataRefs) { - this._dataRefs = typeof metadata.dataRefs === 'string' ? metadata.dataRefs : JSON.stringify(metadata.dataRefs); - } - return this; - } - setResponseType(responseType: string): ApiClient { this._responseType = responseType; return this; @@ -128,28 +125,40 @@ export class ApiClient { * - data() */ async contract() { - const response = await this.post(`${this._apiurl}/${this._contractUri}`); - return response.contractTxId; + const response = await this.post(`${this._apiurl}/vaults`); + return response.id; } async getContract(): Promise { return await this.public(true).get(`${this._storageurl}/${this._contractUri}/${this._vaultId}`); } - async updateProfile(): Promise { - return await this.fetch("put", `${this._apiurl}/profiles`); + async existsUser(): Promise { + try { + await this.get(`${this._apiurl}/users/${this._resourceId}`); + } catch (e) { + if (!(e instanceof NotFound)) { + throw e; + } + return false; + } + return true; } - async deleteVault(): Promise { - await this.fetch("delete", `${this._apiurl}/vaults/${this._vaultId}`); + async getUser(): Promise { + return await this.get(`${this._apiurl}/users`); } - async getUser(): Promise { + async getUserPublicData(): Promise { return await this.get(`${this._apiurl}/users/${this._resourceId}`); } - async getProfile(): Promise { - return await this.get(`${this._apiurl}/profiles`); + async updateUser(): Promise { + return await this.fetch("put", `${this._apiurl}/users`); + } + + async deleteVault(): Promise { + await this.delete(`${this._apiurl}/vaults/${this._vaultId}`); } async getMembers(): Promise> { @@ -160,11 +169,11 @@ export class ApiClient { return await this.get(`${this._apiurl}/notifications`); } - async getMemberships(): Promise> { + async getMemberships(): Promise> { return await this.get(`${this._apiurl}/memberships`); } - async getVaults(): Promise> { + async getVaults(): Promise> { return await this.get(`${this._apiurl}/vaults`); } @@ -192,6 +201,10 @@ export class ApiClient { return await this.public(true).get(`${this._apiurl}/vaults/${this._resourceId}`); } + async getTransactions(): Promise> { + return await this.get(`${this._apiurl}/vaults/${this._vaultId}/transactions`); + } + async patchNotifications(): Promise> { return await this.patch(`${this._apiurl}/notifications`); } @@ -206,6 +219,11 @@ export class ApiClient { return response.id; } + async revokeInvite(): Promise<{ id: string }> { + const response = await this.delete(`${this._apiurl}/vaults/${this._vaultId}/members/${this._resourceId}`); + return response.id; + } + async post(url: string): Promise { return this.fetch("post", url); } @@ -218,38 +236,41 @@ export class ApiClient { return this.fetch("get", url); } + async delete(url: string): Promise { + return this.fetch("delete", url); + } + async fetch(method: string, url: string): Promise { - if (!this._jwt && !this._isPublic) { - throw Error("Authentication is required to use Akord API"); + const auth = await Auth.getAuthorization() + if (!auth && !this._isPublic) { + throw new Unauthorized("Authentication is required to use Akord API"); } const config = { method, url: this._queryParams ? this.addQueryParams(url, this._queryParams) : url, headers: { - 'Authorization': 'Bearer ' + this._jwt, + 'Authorization': auth, 'Content-Type': 'application/json' } } as AxiosRequestConfig; if (this._data) { config.data = this._data; } - const response = await axios(config); - if (isPaginated(response)) { - return { items: response.data, nextToken: response.headers[PAGINATION_HEADER] } + try { + const response = await axios(config); + if (isPaginated(response)) { + return { items: response.data, nextToken: nextToken(response) } + } + return response.data; + } catch (error) { + throwError(error.response?.status, error.response?.data?.msg, error); } - return response.data; } addQueryParams = function (url: string, params: any) { - Object.entries(params).forEach(([key, value], index) => { - if (value) { - let queryParam = index === 0 ? "?" : "&"; - queryParam += encodeURIComponent(key); - queryParam += "=" + encodeURIComponent(value.toString()); - url += queryParam; - } - }); + const queryParams = new URLSearchParams(JSON.parse(JSON.stringify(params))); + url += "?" + queryParams.toString(); return url; } @@ -257,53 +278,42 @@ export class ApiClient { * * @requires: * - auth() - * - contractId() - * - input() - * - metadata() - * @uses: - * - tags() + * - vaultId() + * - data() */ - async transaction() { + async transaction() { if (!this._input) { - throw Error("Input is required to use /transactions endpoint"); + throw new BadRequest("Input is required to use /transactions endpoint"); } - if (!this._metadata) { - throw Error("Metadata is required to use /transactions endpoint"); + if (!this._tags) { + throw new BadRequest("Tags is required to use /transactions endpoint"); } this.data({ - contractId: this._vaultId, input: this._input, - metadata: this._metadata, tags: this._tags, - state: this._data + metadata: this._metadata }); - const response = await this.post(`${this._apiurl}/${this._transactionUri}`); - return response.txId; + const response = await this.post(`${this._apiurl}/vaults/${this._vaultId}/transactions`); + return response; } - /** * Schedules transaction posting * @requires: * - auth() * - resourceId() - * - metadata() * @uses: * - tags() */ async asyncTransaction() { if (!this._resourceId) { - throw Error("Resource id is required to use /transactions/files endpoint"); + throw new BadRequest("Resource id is required to use /transactions/files endpoint"); } - const tags = this._tags.filter((tag) => - tag.name !== "Public-Key" - ) - this.data({ resourceUrl: this._resourceId, - tags: tags, + tags: this._tags, async: true, numberOfChunks: this._numberOfChunks }); @@ -315,21 +325,10 @@ export class ApiClient { * @requires: * - auth() * - data() - * @uses: - * - tags() - * - dataRefs() - * - resourceId() */ async uploadState() { - this._dir = this._stateDir; - let resourceTx: string; - if (this._shouldBundleTransaction) { - resourceTx = await this.stateTransaction(); - } - else { - resourceTx = (await this.upload()).resourceTx; - } - return { resourceUrl: this._resourceId || resourceTx, id: resourceTx, resourceTx: resourceTx } + const response = await this.post(`${this._apiurl}/states`); + return response.id; } /** @@ -339,7 +338,6 @@ export class ApiClient { * - data() * @uses: * - tags() - * - dataRefs() * - resourceId() */ async uploadFile() { @@ -377,7 +375,6 @@ export class ApiClient { * @requires: * - auth() * - resourceId() - * - metadata() * @uses: * - tags() */ @@ -386,50 +383,22 @@ export class ApiClient { this._resourceId = uuid(); } - const tags = this._tags?.filter((tag) => - tag.name !== "Public-Key" - ) - this.data({ resourceUrl: this._resourceId, - tags: tags + tags: this._tags }); const response = await this.post(`${this._apiurl}/${this._transactionUri}/files`); return response.txId; } - /** - * Creates data item from uploaded resource. Schedules bundled transaction - * @requires: - * - auth() - * - metadata() - * - data() - * @uses: - * - tags() - * - resourceId() - */ - private async stateTransaction() { - const tags = this._tags.filter((tag) => - tag.name !== "Public-Key" - ) - - this.data({ - resourceUrl: this._resourceId, - tags: tags, - data: this._data - }); - - const response = await this.post(`${this._apiurl}/${this._transactionUri}/states`); - return response.txId; - } - private async upload() { - if (!this._jwt) { - throw Error("Authentication is required to use Akord API"); + const auth = await Auth.getAuthorization(); + if (!auth) { + throw new Unauthorized("Authentication is required to use Akord API"); } if (!this._data) { - throw Error('Missing data to upload. Use ApiClient#data() to add it') + throw new BadRequest('Missing data to upload. Use ApiClient#data() to add it') } if (!this._resourceId) { this._resourceId = this._isPublic ? this._publicDataDir + '/' + uuid() : uuid(); @@ -441,7 +410,7 @@ export class ApiClient { url: `${this._storageurl}/${this._dir}/${this._resourceId}`, data: this._data, headers: { - 'Authorization': 'Bearer ' + this._jwt, + 'Authorization': auth, 'Content-Type': 'application/octet-stream' }, signal: this._cancelHook ? this._cancelHook.signal : null, @@ -458,9 +427,6 @@ export class ApiClient { } } as AxiosRequestConfig - if (this._dataRefs) { - config.headers['x-amz-meta-datarefs'] = this._dataRefs; - } if (!this._shouldBundleTransaction) { config.headers['x-amz-meta-skipbundle'] = "true"; } @@ -472,27 +438,28 @@ export class ApiClient { config.headers['x-amz-meta-encryptedkey'] = tag.value; } else if (tag.name === "Initialization-Vector") { config.headers['x-amz-meta-iv'] = tag.value; - } else if (tag.name === "Public-Key") { - config.headers['x-amz-publickey'] = tag.value; } else { config.headers['x-amz-meta-' + tag.name.toLowerCase()] = tag.value; } } - config.headers['x-amz-meta-tags'] = JSON.stringify(this._tags.filter((tag) => - tag.name !== "Public-Key" - )); + config.headers['x-amz-meta-tags'] = JSON.stringify(this._tags); } - const response = await axios(config); - return { resourceUrl: this._resourceId, resourceTx: response.data.resourceTx } + try { + const response = await axios(config); + return { resourceUrl: this._resourceId, resourceTx: response.data.resourceTx }; + } catch (error) { + throwError(error.response?.status, error.response?.data?.msg, error); + } } private async download() { - if (!this._jwt && !this._isPublic) { - throw Error("Authentication is required to use Akord API"); + const auth = await Auth.getAuthorization(); + if (!auth && !this._isPublic) { + throw new Unauthorized("Authentication is required to use Akord API"); } if (!this._resourceId) { - throw Error('Missing resource id to download') + throw new BadRequest('Missing resource id to download') } const me = this; @@ -517,12 +484,15 @@ export class ApiClient { if (!this._isPublic) { config.headers = { - 'Authorization': 'Bearer ' + this._jwt, + 'Authorization': auth, } } - const response = await axios(config); - const downloadResponse = { resourceUrl: this._resourceId, response: response } - return downloadResponse + try { + const response = await axios(config); + return { resourceUrl: this._resourceId, response: response }; + } catch (error) { + throwError(error.response?.status, error.response?.data?.msg, error); + } } } diff --git a/src/api/api.ts b/src/api/api.ts index c3322dd5..b8a321f4 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -1,57 +1,65 @@ import { ContractInput, ContractState, Tags } from "../types/contract"; import { Vault } from "../types/vault"; import { Membership, MembershipKeys } from "../types/membership"; +import { Transaction } from "../types/transaction"; import { Paginated } from "../types/paginated"; +import { ListOptions, VaultApiGetOptions } from "../types/query-options"; +import { User, UserPublicInfo } from "../types/user"; +import { FileDownloadOptions, FileUploadOptions } from "../core/file"; +import { AxiosResponseHeaders } from "axios"; abstract class Api { config: any - jwtToken: string constructor() { } - abstract postContractTransaction(contractId: string, input: ContractInput, tags: Tags, metadata?: any): Promise + abstract postContractTransaction(contractId: string, input: ContractInput, tags: Tags, metadata?: any): Promise<{ id: string, object: T }> abstract initContractId(tags: Tags, state?: any): Promise + + abstract uploadFile(file: ArrayBuffer, tags: Tags, options?: FileUploadOptions): Promise<{ resourceTx: string, resourceUrl: string }> - abstract getUserFromEmail(email: string): Promise<{ address: string, publicKey: string }> - - abstract uploadFile(file: ArrayBufferLike, tags: Tags, isPublic?: boolean, shouldBundleTransaction?: boolean, progressHook?: (progress: number) => void, cancelHook?: AbortController): Promise<{ resourceTx: string, resourceUrl?: string }> - - abstract uploadData(items: { data: any, tags: Tags }[], shouldBundleTransaction?: boolean): Promise> + abstract uploadData(items: { data: any, tags: Tags }[], options?: FileUploadOptions): Promise> abstract getContractState(vaultId: string): Promise - abstract downloadFile(id: string, isPublic?: boolean, progressHook?: (progress: number, data?: any) => void, cancelHook?: AbortController, numberOfChunks?: number, loadedSize?: number, resourceSize?: number): Promise + abstract downloadFile(id: string, options?: FileDownloadOptions): Promise<{ fileData: ArrayBuffer, headers: AxiosResponseHeaders }> abstract getMembershipKeys(vaultId: string): Promise - abstract getProfile(): Promise + abstract existsUser(email: string): Promise + + abstract getUser(): Promise + + abstract getUserPublicData(email: string): Promise abstract getNode(id: string, type: string, vaultId?: string): Promise abstract getMembership(id: string, vaultId?: string): Promise - abstract getVault(id: string): Promise + abstract getVault(id: string, options?: VaultApiGetOptions): Promise abstract getNodeState(stateId: string): Promise - abstract getVaults(): Promise> + abstract getVaults(filter?: Object, limit?: number, nextToken?: string): Promise> - abstract getMemberships(): Promise> + abstract getMemberships(limit?: number, nextToken?: string): Promise> - abstract getNodesByVaultId(vaultId: string, type: string, parentId?: string, filter?: Object, limit?: number, nextToken?: string): Promise> + abstract getNodesByVaultId(vaultId: string, type: string, options?: ListOptions): Promise> - abstract getMembershipsByVaultId(vaultId: string, filter?: Object, limit?: number, nextToken?: string): Promise> + abstract getMembershipsByVaultId(vaultId: string, options?: ListOptions): Promise> abstract getMembers(vaultId: string): Promise> - abstract getTransactions(vaultId: string): Promise> + abstract getTransactions(vaultId: string): Promise> - abstract updateProfile(name: string, avatarUri: string): Promise + abstract updateUser(name: string, avatarUri: string[]): Promise abstract deleteVault(vaultId: string): Promise - abstract inviteNewUser(vaultId: string, email: string, role: string): Promise<{ id: string }> + abstract inviteNewUser(vaultId: string, email: string, role: string, message?: string): Promise<{ id: string }> + + abstract revokeInvite(vaultId: string, membershipId: string): Promise<{ id: string }> abstract inviteResend(vaultId: string, membershipId: string): Promise<{ id: string }> } diff --git a/src/api/config.ts b/src/api/config.ts index 3b96d886..179f4354 100644 --- a/src/api/config.ts +++ b/src/api/config.ts @@ -1,26 +1,15 @@ export const apiConfig = (env: string) => { switch (env) { - case "prod": - return { - apiurl: "", - storageurl: "https://api.v2.prod.permapost-storage.akord.com", - aws_user_pools_id: "eu-central-1_glTrP1Kin", - aws_user_pools_web_client_id: "7u2a1pf5i6shfo7enci6bagk7u" - }; case "v2": default: return { apiurl: "https://api.akord.com", storageurl: "https://api.v2.prod.permapost-storage.akord.com", - aws_user_pools_id: "eu-central-1_glTrP1Kin", - aws_user_pools_web_client_id: "7u2a1pf5i6shfo7enci6bagk7u" }; case "dev": return { apiurl: "https://api.akord.link", storageurl: "https://api.dev.permapost-storage.akord.link", - aws_user_pools_id: "eu-central-1_FOAlZvgHo", - aws_user_pools_web_client_id: "3m7t2tk3dpldemk3geq0otrtt9" }; } }; @@ -28,6 +17,4 @@ export const apiConfig = (env: string) => { export interface ApiConfig { apiurl: string, storageurl: string, - aws_user_pools_id: string, - aws_user_pools_web_client_id: string } diff --git a/src/arweave.ts b/src/arweave.ts index 6ee0a968..00f2ee65 100644 --- a/src/arweave.ts +++ b/src/arweave.ts @@ -1,4 +1,6 @@ import axios, { AxiosRequestConfig } from "axios"; +import { throwError } from "./errors/error"; +import { NotFound } from "./errors/not-found"; const ARWEAVE_URL = "https://arweave.net/"; @@ -13,24 +15,20 @@ const getTxData = async (id: string) => { if (response.status == 200 || response.status == 202) { return bufferToArrayBuffer(response.data); } else { - throw new Error("Cannot fetch arweave transaction data: " + id); + throwError(response?.status, response?.data?.msg); } } catch (error) { - throw new Error("Cannot fetch arweave transaction data: " + id); + throwError(error.response?.status, error.response?.data?.msg, error); } }; const getTxMetadata = async (id: string) => { - try { - const result = await graphql(getTransaction, { id }); - const txMetadata = result?.data?.transactions?.edges[0]?.node; - if (!txMetadata) { - throw new Error("Cannot fetch arweave transaction metadata: " + id); - } - return txMetadata; - } catch (error) { - throw new Error("Cannot fetch arweave transaction metadata: " + id); + const result = await graphql(getTransaction, { id }); + const txMetadata = result?.data?.transactions?.edges[0]?.node; + if (!txMetadata) { + throw new NotFound("Cannot fetch arweave transaction metadata: " + id); } + return txMetadata; }; const getTransaction = /* GraphQL */ ` @@ -72,7 +70,7 @@ const graphql = async (query: any, variables: any) => { const response = await axios(config); return response.data; } catch (error) { - throw new Error("Error while trying to make fetch request"); + throwError(error.response?.status, error.response?.data?.msg, error); } } diff --git a/src/auth.ts b/src/auth.ts deleted file mode 100644 index e032fbd6..00000000 --- a/src/auth.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Akord } from "./akord"; -import { AkordWallet } from "@akord/crypto"; -import ApiAuthenticator from "./api/api-authenticator"; - -class Auth { - apiAuthenticator: ApiAuthenticator; - - constructor() { - this.apiAuthenticator = new ApiAuthenticator(); - } - - /** - * @param {string} email - * @param {string} password - * @returns Promise with Akord Client instance, JWT token & Akord Wallet - */ - public signIn = async function (email: string, password: string): Promise<{ akord: Akord, wallet: AkordWallet, jwtToken: string }> { - const jwtToken = await this.apiAuthenticator.getJWTToken(email, password); - const userAttributes = await this.apiAuthenticator.getUserAttributes(email, password); - const wallet = await AkordWallet.importFromEncBackupPhrase(password, userAttributes["custom:encBackupPhrase"]); - return { wallet, jwtToken, akord: new Akord(wallet, jwtToken) }; - }; - - /** - * @param {string} email - * @param {string} password - * @param {any} clientMetadata JSON client metadata, ex: { clientType: "CLI" } - * @returns Promise with Akord Wallet - */ - public signUp = async function (email: string, password: string, clientMetadata: any = {}): Promise { - const wallet = await AkordWallet.create(password); - await this.apiAuthenticator.signUp(email, password, { - email, - "custom:encBackupPhrase": wallet.encBackupPhrase, - "custom:publicKey": await wallet.publicKey(), - "custom:publicSigningKey": await wallet.signingPublicKey(), - "custom:address": await wallet.getAddress(), - "custom:mode": "dark", - "custom:notifications": "true", - }, clientMetadata); - return wallet; - }; - - /** - * @param {string} email - * @param {string} code - * @returns - */ - public verifyAccount = async function (email: string, code: string): Promise { - await this.apiAuthenticator.verifyAccount(email, code); - }; -}; - -export { - Auth -} \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index a80854de..b160ee8d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,22 +1,11 @@ import { Api } from "./api/api" export interface ClientConfig { - env?: EnvType, - network?: NetworkType, + env?: "dev" | "v2" debug?: boolean, cache?: boolean, - api?: Api -} - -export enum EnvType { - PROD = "prod", - V2 = "v2", - TESTNET = "testnet", - DEV = "dev" -} - -export enum NetworkType { - LOCAL = "local", - TESTNET = "testnet", - MAINNET = "mainnet" + api?: Api, + storage?: Storage + authToken?: string + apiKey?: string } \ No newline at end of file diff --git a/src/core/batch.ts b/src/core/batch.ts index 529e5384..b08ce964 100644 --- a/src/core/batch.ts +++ b/src/core/batch.ts @@ -1,9 +1,9 @@ import { Service, ServiceFactory } from "../core"; import { v4 as uuidv4 } from "uuid"; -import { MembershipService } from "./membership"; -import { StackService } from "./stack"; +import { MembershipCreateOptions, MembershipService, activeStatus } from "./membership"; +import { StackCreateOptions, StackService } from "./stack"; import { NodeService } from "./node"; -import { Node, NodeType } from "../types/node"; +import { Node, NodeType, Stack } from "../types/node"; import { FileLike } from "../types/file"; import { BatchMembershipInviteResponse, BatchStackCreateResponse } from "../types/batch-response"; import { RoleType } from "../types/membership"; @@ -96,18 +96,13 @@ class BatchService extends Service { /** * @param {string} vaultId * @param {{file:FileLike,name:string}[]} items - * @param {string} [parentId] - * @param {(progress:number)=>void} [progressHook] - * @param {AbortController} [cancelHook] + * @param {BatchStackCreateOptions} [options] * @returns Promise with new stack ids & their corresponding transaction ids */ public async stackCreate( vaultId: string, - items: { file: FileLike, name: string, parentId?: string }[], - progressHook?: (progress: number) => void, - cancelHook?: AbortController, - processingCountHook?: (count: number) => void, - onStackCreated?: (item: { file: FileLike, name: string, parentId?: string }) => Promise + items: { file: FileLike, name: string }[], + options: BatchStackCreateOptions = {} ): Promise { const size = items.reduce((sum, stack) => { return sum + stack.file.size; @@ -116,19 +111,22 @@ class BatchService extends Service { let processedStacksCount = 0; const perFileProgress = new Map(); this.setGroupRef(items); - if (processingCountHook) { - processingCountHook(processedStacksCount); + if (options.processingCountHook) { + options.processingCountHook(processedStacksCount); } - - const data = [] as { stackId: string, transactionId: string }[]; + const data = [] as { stackId: string, transactionId: string, object: Stack }[]; const errors = [] as { name: string, message: string }[]; - const stackProgressHook = (localProgress: number, data: any) => { - const stackBytesUploaded = Math.floor(localProgress / 100 * data.total) - progress += stackBytesUploaded - (perFileProgress.get(data.id) || 0) - perFileProgress.set(data.id, stackBytesUploaded); - progressHook(Math.min(100, Math.round(progress / size * 100))); + if (options.progressHook) { + const onProgress = options.progressHook + const stackProgressHook = (localProgress: number, data: any) => { + const stackBytesUploaded = Math.floor(localProgress / 100 * data.total) + progress += stackBytesUploaded - (perFileProgress.get(data.id) || 0) + perFileProgress.set(data.id, stackBytesUploaded); + onProgress(Math.min(100, Math.round(progress / size * 100))); + } + options.progressHook = stackProgressHook; } for (const chunk of [...chunks(items, BatchService.BATCH_CHUNK_SIZE)]) { @@ -137,21 +135,21 @@ class BatchService extends Service { const service = new StackService(this.wallet, this.api); service.setGroupRef(this.groupRef); - const stackResponse = await service.create(vaultId, item.file, item.name, item.parentId, stackProgressHook, cancelHook); - if (cancelHook.signal.aborted) { + const stackResponse = await service.create(vaultId, item.file, item.name, options); + if (options.cancelHook?.signal.aborted) { return { data, errors, cancelled: items.length - processedStacksCount }; } data.push(stackResponse); processedStacksCount += 1; - processingCountHook(processedStacksCount); - if (onStackCreated) { - await onStackCreated(item); + options.processingCountHook(processedStacksCount); + if (options.onStackCreated) { + await options.onStackCreated(stackResponse.object); } } catch (e) { errors.push({ name: item.name, message: e.toString() }) }; })) - if (cancelHook.signal.aborted) { + if (options.cancelHook?.signal.aborted) { return { data, errors, cancelled: items.length - processedStacksCount }; } } @@ -161,9 +159,10 @@ class BatchService extends Service { /** * @param {string} vaultId * @param {{email:string,role:RoleType}[]} items + * @param {string} [message] optional email message - unencrypted * @returns Promise with new membership ids & their corresponding transaction ids */ - public async membershipInvite(vaultId: string, items: { email: string, role: RoleType }[]): Promise { + public async membershipInvite(vaultId: string, items: { email: string, role: RoleType }[], options: MembershipCreateOptions = {}): Promise { this.setGroupRef(items); const members = await this.api.getMembers(vaultId); const data = [] as { membershipId: string, transactionId: string }[]; @@ -173,17 +172,17 @@ class BatchService extends Service { const email = item.email.toLowerCase(); const role = item.role; const member = members.find(item => item.email?.toLowerCase() === email); - if (member) { + if (member && activeStatus.includes(member.status)) { errors.push({ email: email, message: "Membership already exists for this user." }); } else { - const userHasAccount = Boolean(await this.api.getUserFromEmail(email)); + const userHasAccount = await this.api.existsUser(email); const service = new MembershipService(this.wallet, this.api); service.setGroupRef(this.groupRef); if (userHasAccount) { - data.push(await service.invite(vaultId, email, role)); + data.push(await service.invite(vaultId, email, role, options)); } else { data.push({ - ...(await service.inviteNewUser(vaultId, email, role)), + ...(await service.inviteNewUser(vaultId, email, role, options)), transactionId: null }) } @@ -198,6 +197,11 @@ class BatchService extends Service { } } +export type BatchStackCreateOptions = StackCreateOptions & { + processingCountHook?: (count: number) => void, + onStackCreated?: (item: Stack) => Promise +}; + export { BatchService } diff --git a/src/core/contract.ts b/src/core/contract.ts index 3a4cf1ad..c773f037 100644 --- a/src/core/contract.ts +++ b/src/core/contract.ts @@ -1,28 +1,25 @@ import { Service } from "../core"; +import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; import { ContractState, Tags } from "../types/contract"; +import { Transaction } from "../types/transaction"; class ContractService extends Service { - /** - * @param tags array of name#value tags of warp contract to be created - * @param state initial state of warp contract - * @returns Promise contract Id - */ - public async create(tags: Tags, state?: any): Promise { - return await this.api.initContractId(tags, state) - } - /** * @param id vault contract id * @returns Promise with the current contract state */ - public async getState(id: string): Promise { + public async getState(id: string): Promise { const contract = await this.api.getContractState(id); this.setIsPublic(contract.public); if (contract.public) { return contract; } else { - await contract.decrypt(); + try { + await contract.decrypt(); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } } return contract; } @@ -31,9 +28,18 @@ class ContractService extends Service { * @param id vault contract id * @returns Promise with the list of all contract interactions */ - public async list(id: string): Promise> { + public async list(id: string): Promise> { return this.api.getTransactions(id); } + + /** + * @param tags array of name#value tags of warp contract to be created + * @param state initial state of warp contract + * @returns Promise contract Id + */ + public async create(tags: Tags, state?: any): Promise { + return await this.api.initContractId(tags, state) + } } export { diff --git a/src/core/file.ts b/src/core/file.ts index 2fcd397f..80ab678a 100644 --- a/src/core/file.ts +++ b/src/core/file.ts @@ -5,15 +5,14 @@ import { Logger } from "../logger"; import { ApiClient } from "../api/api-client"; import { v4 as uuid } from "uuid"; import { FileLike } from "../types/file"; -import { Blob } from 'buffer'; -import fs from "fs"; +import { Blob } from "buffer"; import { Tag, Tags } from "../types/contract"; import { BinaryLike } from "crypto"; import { getTxData, getTxMetadata } from "../arweave"; import * as mime from "mime-types"; import { CONTENT_TYPE as MANIFEST_CONTENT_TYPE, FILE_TYPE as MANIFEST_FILE_TYPE } from "./manifest"; -const DEFAULT_FILE_TYPE = "image/jpeg"; +const DEFAULT_FILE_TYPE = "text/plain"; class FileService extends Service { asyncUploadTreshold = 209715200; @@ -34,18 +33,21 @@ class FileService extends Service { } else { await this.setVaultContext(vaultId); } + const downloadOptions = options as FileDownloadOptions; + downloadOptions.public = this.isPublic; let fileBinary: ArrayBuffer; if (options.isChunked) { let currentChunk = 0; while (currentChunk < options.numberOfChunks) { const url = `${id}_${currentChunk}`; - const chunkBinary = await this.getBinary(url, options.progressHook, options.cancelHook, options.numberOfChunks, currentChunk * this.chunkSize, options.size); + downloadOptions.loadedSize = currentChunk * this.chunkSize; + const chunkBinary = await this.getBinary(url, downloadOptions); fileBinary = this.appendBuffer(fileBinary, chunkBinary); currentChunk++; } } else { - const file = await this.api.downloadFile(id, this.isPublic, options.progressHook, options.cancelHook); - fileBinary = await this.processReadRaw(file.fileData, file.headers) + const { fileData, headers } = await this.api.downloadFile(id, downloadOptions); + fileBinary = await this.processReadRaw(fileData, headers) } return fileBinary; } @@ -66,13 +68,16 @@ class FileService extends Service { } else { await this.setVaultContext(vaultId); } - const writer = await this.stream(options.name, options.size); + const downloadOptions = options as FileDownloadOptions; + downloadOptions.public = this.isPublic; + const writer = await this.stream(options.name, options.resourceSize); if (options.isChunked) { let currentChunk = 0; try { while (currentChunk < options.numberOfChunks) { const url = `${id}_${currentChunk}`; - const fileBinary = await this.getBinary(url, options.progressHook, options.cancelHook, options.numberOfChunks, currentChunk * this.chunkSize, options.size); + downloadOptions.loadedSize = currentChunk * this.chunkSize; + const fileBinary = await this.getBinary(url, downloadOptions); if (writer instanceof WritableStreamDefaultWriter) { await writer.ready } @@ -88,7 +93,7 @@ class FileService extends Service { await writer.close(); } } else { - const fileBinary = await this.getBinary(id, options.progressHook, options.cancelHook); + const fileBinary = await this.getBinary(id, downloadOptions); await writer.write(new Uint8Array(fileBinary)); await writer.close(); } @@ -96,18 +101,17 @@ class FileService extends Service { public async create( file: FileLike, - shouldBundleTransaction?: boolean, - progressHook?: (progress: number, data?: any) => void, - cancelHook?: AbortController) - : Promise<{ resourceTx: string, resourceUrl?: string, resourceHash: string, numberOfChunks?: number, chunkSize?: number }> { + options: FileUploadOptions + ): Promise { const tags = this.getFileTags(file); if (file.size > this.asyncUploadTreshold) { - return await this.uploadChunked(file, tags, progressHook, cancelHook); + return await this.uploadChunked(file, tags, options); } else { const { processedData, encryptionTags } = await this.processWriteRaw(await file.arrayBuffer()); - const resourceHash = await digestRaw(processedData); + const resourceHash = await digestRaw(new Uint8Array(processedData)); tags.push(new Tag(fileTags.FILE_HASH, resourceHash)); - return { resourceHash: resourceHash, ...await this.api.uploadFile(processedData, tags.concat(encryptionTags), this.isPublic, shouldBundleTransaction, progressHook, cancelHook) }; + options.public = this.isPublic; + return { resourceHash: resourceHash, ...await this.api.uploadFile(processedData, tags.concat(encryptionTags), options) }; } } @@ -120,11 +124,10 @@ class FileService extends Service { const tags = this.getFileTags(file); const { processedData, encryptionTags } = await this.processWriteRaw(await file.arrayBuffer()); - const resourceHash = await digestRaw(processedData); + const resourceHash = await digestRaw(new Uint8Array(processedData)); tags.push(new Tag(fileTags.FILE_HASH, resourceHash)); const resource = await new ApiClient() .env(this.api.config) - .auth(this.api.jwtToken) .data(processedData) .tags(tags.concat(encryptionTags)) .public(this.isPublic) @@ -133,8 +136,9 @@ class FileService extends Service { return { file, resourceHash, resourceUrl: resource.resourceUrl }; } - public async stream(path: string, size?: number): Promise { + public async stream(path: string, size?: number): Promise { if (typeof window === 'undefined') { + const fs = (await import("fs")).default; return fs.createWriteStream(path); } else { @@ -182,9 +186,8 @@ class FileService extends Service { private async uploadChunked( file: FileLike, tags: Tags, - progressHook?: (progress: number, data?: any) => void, - cancelHook?: AbortController - ): Promise { + options: FileUploadOptions + ): Promise { let resourceUrl = uuid(); let encryptionTags: Tags; let encryptedKey: string; @@ -214,8 +217,7 @@ class FileService extends Service { tags, resourceUrl, file.size, - progressHook, - cancelHook + options ); offset += this.chunkSize; uploadedChunks += 1; @@ -228,7 +230,6 @@ class FileService extends Service { await new ApiClient() .env(this.api.config) - .auth(this.api.jwtToken) .resourceId(resourceUrl) .tags(tags.concat(encryptionTags)) .public(this.isPublic) @@ -249,19 +250,17 @@ class FileService extends Service { tags: Tags, resourceUrl: string, resourceSize: number, - progressHook?: (progress: number, data?: any) => void, - cancelHook?: AbortController + options: FileUploadOptions ) { const resource = await new ApiClient() .env(this.api.config) - .auth(this.api.jwtToken) .resourceId(`${resourceUrl}_${chunkNumber}`) .data(chunk.processedData) .tags(tags.concat(chunk.encryptionTags)) .public(this.isPublic) .bundle(false) - .progressHook(progressHook, chunkNumber * this.chunkSize, resourceSize) - .cancelHook(cancelHook) + .progressHook(options.progressHook, chunkNumber * this.chunkSize, resourceSize) + .cancelHook(options.cancelHook) .uploadFile() Logger.log("Uploaded file with id: " + resource.resourceUrl); } @@ -272,7 +271,7 @@ class FileService extends Service { }> { const chunkNumber = offset / this.chunkSize; const arrayBuffer = await chunk.arrayBuffer(); - const encryptedData = await this.processWriteRaw(new Uint8Array(arrayBuffer), encryptedKey); + const encryptedData = await this.processWriteRaw(arrayBuffer, encryptedKey); return { encryptedData, chunkNumber } } @@ -286,10 +285,11 @@ class FileService extends Service { return tmp.buffer; } - private async getBinary(id: string, progressHook?: (progress: number) => void, cancelHook?: AbortController, numberOfChunks?: number, loadedSize?: number, resourceSize?: number) { + private async getBinary(id: string, options: FileDownloadOptions) { try { - const file = await this.api.downloadFile(id, this.isPublic, progressHook, cancelHook, numberOfChunks, loadedSize, resourceSize); - return await this.processReadRaw(file.fileData, file.headers); + options.public = this.isPublic; + const { fileData, headers } = await this.api.downloadFile(id, options); + return await this.processReadRaw(fileData, headers); } catch (e) { Logger.log(e); throw new Error( @@ -302,14 +302,14 @@ class FileService extends Service { private getFileTags(file: FileLike): Tags { const tags = [] as Tags; if (this.isPublic) { - tags.push(new Tag(fileTags.FILE_NAME, encodeURIComponent(file.name))) + tags.push(new Tag(fileTags.FILE_NAME, file.name)) if (file.lastModified) { tags.push(new Tag(fileTags.FILE_MODIFIED_AT, file.lastModified.toString())); } } - tags.push(new Tag(smartweaveTags.CONTENT_TYPE, this.contentType || file.type)); + tags.push(new Tag(smartweaveTags.CONTENT_TYPE, this.contentType || file.type || DEFAULT_FILE_TYPE)); tags.push(new Tag(fileTags.FILE_SIZE, file.size)); - tags.push(new Tag(fileTags.FILE_TYPE, file.type)); + tags.push(new Tag(fileTags.FILE_TYPE, file.type || DEFAULT_FILE_TYPE)); tags.push(new Tag(protocolTags.TIMESTAMP, JSON.stringify(Date.now()))); tags.push(new Tag(dataTags.DATA_TYPE, "File")); tags.push(new Tag(protocolTags.VAULT_ID, this.vaultId)); @@ -317,13 +317,30 @@ class FileService extends Service { } }; -type DownloadOptions = { - size?: number, - name?: string, +type DownloadOptions = FileDownloadOptions & { name?: string } + +export type FileUploadResult = { + resourceTx?: string, + resourceUrl?: string, + resourceHash?: string, + thumbnailTx?: string, + thumbnailUrl?: string, + numberOfChunks?: number, + chunkSize?: number, +} + +export type FileUploadOptions = { + progressHook?: (progress: number, data?: any) => void, + cancelHook?: AbortController, + shouldBundleTransaction?: boolean, + public?: boolean +} + +export type FileDownloadOptions = FileUploadOptions & { isChunked?: boolean, numberOfChunks?: number, - progressHook?: (progress: number) => void, - cancelHook?: AbortController + loadedSize?: number, + resourceSize?: number } async function createFileLike(sources: Array, name: string, mimeType: string, lastModified?: number) diff --git a/src/core/folder.ts b/src/core/folder.ts index 644ac57d..c54fbd7d 100644 --- a/src/core/folder.ts +++ b/src/core/folder.ts @@ -1,4 +1,4 @@ -import { NodeService } from "./node"; +import { NodeCreateOptions, NodeService } from "./node"; import { actionRefs, functions } from "../constants"; import { Folder, nodeType } from "../types/node"; @@ -9,24 +9,27 @@ class FolderService extends NodeService { /** * @param {string} vaultId * @param {string} name folder name - * @param {string} [parentId] parent folder id + * @param {NodeCreateOptions} [options] parent id, etc. * @returns Promise with new folder id & corresponding transaction id */ - public async create(vaultId: string, name: string, parentId?: string): Promise<{ - folderId: string, - transactionId: string - }> { + public async create(vaultId: string, name: string, options: NodeCreateOptions = this.defaultCreateOptions): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.FOLDER_CREATE); this.setFunction(functions.NODE_CREATE); const body = { name: await this.processWriteString(name) } - const { nodeId, transactionId } = await this.nodeCreate(body, { parentId }); - return { folderId: nodeId, transactionId }; + const { nodeId, transactionId, object } = await this.nodeCreate(body, { parentId: options.parentId }); + return { folderId: nodeId, transactionId, object }; } }; +type FolderCreateResult = { + folderId: string, + transactionId: string, + object: Folder +} + export { FolderService } \ No newline at end of file diff --git a/src/core/manifest.ts b/src/core/manifest.ts index a86b6c70..70ae45e4 100644 --- a/src/core/manifest.ts +++ b/src/core/manifest.ts @@ -4,6 +4,7 @@ import { StackService } from "./stack"; import { FolderService } from "./folder"; import { createFileLike } from "./file"; import { arrayToString } from "@akord/crypto"; +import { BadRequest } from "../errors/bad-request"; export const CONTENT_TYPE = "application/x.arweave-manifest+json"; export const FILE_TYPE = "application/json"; @@ -17,27 +18,6 @@ class ManifestService extends NodeService { objectType = nodeType.STACK; NodeType = Stack; - /** - * @param {string} vaultId - * @param {JSON} manifest manifest JSON - * @returns Promise with corresponding transaction id - */ - public async generate(vaultId: string, manifest?: JSON | Object): Promise<{ transactionId: string }> { - this.stackService.fileService.contentType = CONTENT_TYPE; - if (!manifest) { - manifest = await this.renderManifestJSON(vaultId); - } - const file = await createFileLike([JSON.stringify(manifest)], FILE_NAME, FILE_TYPE); - const manifestNode = await this.get(vaultId); - if (manifestNode) { - // update vault manifest - return await this.stackService.uploadRevision(manifestNode.id, file); - } else { - // create new vault manifest - return await this.stackService.create(vaultId, file, file.name); - } - } - /** * @returns Promise with vault manifest node */ @@ -56,17 +36,37 @@ class ManifestService extends NodeService { public async getVersion(vaultId: string, index?: number): Promise { const manifest = await this.get(vaultId); if (!manifest) { - throw new Error("A vault manifest does not exist yet. Use akord.manifest.generate(vaultId) to create it."); + throw new BadRequest("A vault manifest does not exist yet. Use akord.manifest.generate(vaultId) to create it."); } const manifestFile = await this.stackService.getVersion(manifest.id, index); return JSON.parse(arrayToString(manifestFile.data)); } + /** + * @param {string} vaultId + * @param {JSON} manifest manifest JSON + * @returns Promise with corresponding transaction id + */ + public async generate(vaultId: string, manifest?: JSON | Object): Promise<{ transactionId: string, object: Stack }> { + this.stackService.fileService.contentType = CONTENT_TYPE; + if (!manifest) { + manifest = await this.renderManifestJSON(vaultId); + } + const file = await createFileLike([JSON.stringify(manifest)], FILE_NAME, FILE_TYPE); + const manifestNode = await this.get(vaultId); + if (manifestNode) { + // update vault manifest + return await this.stackService.uploadRevision(manifestNode.id, file); + } else { + // create new vault manifest + return await this.stackService.create(vaultId, file, file.name); + } + } /** * * @returns manifest in json format - */ + */ private async renderManifestJSON(vaultId: string, indexName?: string) { // takes a flat list of folders and stacks and generates a tree const treeify = (folders: any, stacks: any) => { diff --git a/src/core/membership.ts b/src/core/membership.ts index fc75e95e..232a48dd 100644 --- a/src/core/membership.ts +++ b/src/core/membership.ts @@ -1,11 +1,15 @@ import { actionRefs, objectType, status, functions, protocolTags, smartweaveTags } from "../constants"; import { v4 as uuidv4 } from "uuid"; -import { Encrypter, generateKeyPair } from "@akord/crypto"; +import { EncryptedKeys, Encrypter, generateKeyPair } from "@akord/crypto"; import { Service, STATE_CONTENT_TYPE } from "./service"; -import { Membership, RoleType } from "../types/membership"; -import { ListOptions } from "../types/list-options"; +import { Membership, RoleType, StatusType } from "../types/membership"; +import { GetOptions, ListOptions } from "../types/query-options"; import { Tag, Tags } from "../types/contract"; import { Paginated } from "../types/paginated"; +import { BadRequest } from "../errors/bad-request"; +import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; + +export const activeStatus = [status.ACCEPTED, status.PENDING, status.INVITED] as StatusType[]; class MembershipService extends Service { objectType = objectType.MEMBERSHIP; @@ -20,19 +24,24 @@ class MembershipService extends Service { } } as ListOptions; + defaultGetOptions = { + shouldDecrypt: true, + } as GetOptions; + /** * @param {string} membershipId * @returns Promise with the decrypted membership */ - public async get(membershipId: string, vaultId?: string, shouldDecrypt = true): Promise { - const membershipProto = await this.api.getMembership(membershipId, vaultId); + public async get(membershipId: string, options: GetOptions = this.defaultGetOptions): Promise { + const getOptions = { + ...this.defaultGetOptions, + ...options + } + const membershipProto = await this.api.getMembership(membershipId, getOptions.vaultId); let membership: Membership; - if (shouldDecrypt) { + if (getOptions.shouldDecrypt) { const { isEncrypted, keys } = await this.api.getMembershipKeys(membershipProto.vaultId); - membership = new Membership(membershipProto, keys); - if (isEncrypted) { - await membership.decrypt(); - } + membership = await this.processMembership(membershipProto, isEncrypted, keys); } else { membership = new Membership(membershipProto); @@ -42,42 +51,38 @@ class MembershipService extends Service { /** * @param {string} vaultId + * @param {ListOptions} options * @returns Promise with paginated memberships within given vault */ - public async list(vaultId: string, listOptions = this.defaultListOptions): Promise> { - const response = await this.api.getMembershipsByVaultId(vaultId, listOptions.filter, listOptions.limit, listOptions.nextToken); - const { isEncrypted, keys } = listOptions.shouldDecrypt ? await this.api.getMembershipKeys(vaultId) : { isEncrypted: false, keys: [] }; + public async list(vaultId: string, options: ListOptions = this.defaultListOptions): Promise> { + const listOptions = { + ...this.defaultListOptions, + ...options + } + const response = await this.api.getMembershipsByVaultId(vaultId, listOptions); + const { isEncrypted, keys } = options.shouldDecrypt ? await this.api.getMembershipKeys(vaultId) : { isEncrypted: false, keys: [] }; + const promises = response.items + .map(async (membershipProto: Membership) => { + return await this.processMembership(membershipProto, isEncrypted && listOptions.shouldDecrypt, keys); + }) as Promise[]; + const { items, errors } = await this.handleListErrors(response.items, promises); return { - items: await Promise.all( - response.items - .map(async (membershipProto: Membership) => { - const membership = new Membership(membershipProto, keys); - if (isEncrypted) { - await membership.decrypt(); - } - return membership as Membership; - })) as Membership[], - nextToken: response.nextToken + items, + nextToken: response.nextToken, + errors } } /** - * @param {string} vaultId - * @returns Promise with all memberships within given vault - */ - public async listAll(vaultId: string, listOptions = this.defaultListOptions): Promise> { - let token = null; - let nodeArray = [] as Membership[]; - do { - const { items, nextToken } = await this.list(vaultId, listOptions); - nodeArray = nodeArray.concat(items); - token = nextToken; - listOptions.nextToken = nextToken; - if (nextToken === "null") { - token = null; - } - } while (token); - return nodeArray; + * @param {string} vaultId + * @param {ListOptions} options + * @returns Promise with all memberships within given vault + */ + public async listAll(vaultId: string, options: ListOptions = this.defaultListOptions): Promise> { + const list = async (options: ListOptions & { vaultId: string }) => { + return await this.list(options.vaultId, options); + } + return await this.paginate(list, { ...options, vaultId }); } /** @@ -85,21 +90,24 @@ class MembershipService extends Service { * @param {string} vaultId * @param {string} email invitee's email * @param {RoleType} role CONTRIBUTOR or VIEWER + * @param {MembershipCreateOptions} [options] invitation email message, etc. * @returns Promise with new membership id & corresponding transaction id */ - public async invite(vaultId: string, email: string, role: RoleType): Promise<{ - membershipId: string, - transactionId: string - }> { + public async invite(vaultId: string, email: string, role: RoleType, options: MembershipCreateOptions = {}): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.MEMBERSHIP_INVITE); this.setFunction(functions.MEMBERSHIP_INVITE); const membershipId = uuidv4(); this.setObjectId(membershipId); - const { address, publicKey } = await this.getUserEncryptionInfo(email, await this.wallet.getAddress()); + const { address, publicKey } = await this.getUserEncryptionInfo(email); const keysEncrypter = new Encrypter(this.wallet, this.dataEncrypter.keys, publicKey); - const keys = await keysEncrypter.encryptMemberKeys([]); + let keys: EncryptedKeys[]; + try { + keys = await keysEncrypter.encryptMemberKeys([]); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } const body = { keys: keys.map((keyPair: any) => { delete keyPair.publicKey; @@ -110,114 +118,143 @@ class MembershipService extends Service { this.tags = [new Tag(protocolTags.MEMBER_ADDRESS, address)] .concat(await this.getTags()); - const { data, metadata } = await this.uploadState(body); + const dataTxId = await this.uploadState(body); let input = { function: this.function, address, role, - data + data: dataTxId } - const txId = await this.api.postContractTransaction( + const { id, object } = await this.api.postContractTransaction( this.vaultId, input, this.tags, - metadata + { message: options.message } ); - return { membershipId, transactionId: txId }; + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { membershipId, transactionId: id, object: membership }; } /** * @param {string} membershipId * @returns Promise with corresponding transaction id */ - public async accept(membershipId: string): Promise<{ transactionId: string }> { + public async accept(membershipId: string): Promise { const memberDetails = await this.getProfileDetails(); await this.setVaultContextFromMembershipId(membershipId); - this.setActionRef(actionRefs.MEMBERSHIP_ACCEPT); const body = { memberDetails: await this.processMemberDetails(memberDetails, true), encPublicSigningKey: await this.processWriteString(this.wallet.signingPublicKey()) } + this.setActionRef(actionRefs.MEMBERSHIP_ACCEPT); this.setFunction(functions.MEMBERSHIP_ACCEPT); - return this.nodeUpdate(body); + + const data = await this.mergeAndUploadBody(body); + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function, data }, + await this.getTags() + ); + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } /** * @param {string} membershipId * @returns Promise with corresponding transaction id */ - public async confirm(membershipId: string): Promise<{ transactionId: string }> { + public async confirm(membershipId: string): Promise { await this.setVaultContextFromMembershipId(membershipId); this.setActionRef(actionRefs.MEMBERSHIP_CONFIRM); this.setFunction(functions.MEMBERSHIP_INVITE); - const { address, publicKey } = await this.getUserEncryptionInfo(this.object.email, await this.wallet.getAddress()); + const { address, publicKey } = await this.getUserEncryptionInfo(this.object.email); const keysEncrypter = new Encrypter(this.wallet, this.dataEncrypter.keys, publicKey); - const keys = await keysEncrypter.encryptMemberKeys([]); + let keys: EncryptedKeys[]; + try { + keys = await keysEncrypter.encryptMemberKeys([]); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } const body = { keys: keys.map((keyPair: any) => { delete keyPair.publicKey; return keyPair; }) - }; + } this.tags = [new Tag(protocolTags.MEMBER_ADDRESS, address)] .concat(await this.getTags()); - const { data, metadata } = await this.uploadState(body); + const dataTxId = await this.uploadState(body); let input = { function: this.function, address, - data, + data: dataTxId, role: this.object.role } - const txId = await this.api.postContractTransaction( + const { id, object } = await this.api.postContractTransaction( this.vaultId, input, - this.tags, - metadata + this.tags ); - return { transactionId: txId }; + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } /** * @param {string} membershipId * @returns Promise with corresponding transaction id */ - public async reject(membershipId: string): Promise<{ transactionId: string }> { + public async reject(membershipId: string): Promise { await this.setVaultContextFromMembershipId(membershipId); this.setActionRef(actionRefs.MEMBERSHIP_REJECT); this.setFunction(functions.MEMBERSHIP_REJECT); - return this.nodeUpdate(); + + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function }, + await this.getTags() + ); + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } /** * @param {string} membershipId * @returns Promise with corresponding transaction id */ - public async leave(membershipId: string): Promise<{ transactionId: string }> { + public async leave(membershipId: string): Promise { await this.setVaultContextFromMembershipId(membershipId); this.setActionRef(actionRefs.MEMBERSHIP_LEAVE); this.setFunction(functions.MEMBERSHIP_REJECT); - return this.nodeUpdate(); + + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function }, + await this.getTags() + ); + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } /** * @param {string} membershipId * @returns Promise with corresponding transaction id */ - public async revoke(membershipId: string): Promise<{ transactionId: string }> { + public async revoke(membershipId: string): Promise { await this.setVaultContextFromMembershipId(membershipId); + this.setActionRef(actionRefs.MEMBERSHIP_REVOKE); this.setFunction(functions.MEMBERSHIP_REVOKE); - let data: any; + let data: { id: string, value: string }[]; if (!this.isPublic) { // generate a new vault key pair const keyPair = await generateKeyPair(); - const memberships = await this.listAll(this.vaultId, { shouldDecrypt: false, filter: this.defaultListOptions.filter }); + const memberships = await this.listAll(this.vaultId, { shouldDecrypt: false }); this.tags = await this.getTags(); @@ -226,14 +263,20 @@ class MembershipService extends Service { for (let member of memberships) { if (member.id !== this.objectId && (member.status === status.ACCEPTED || member.status === status.PENDING)) { - const { publicKey } = await this.getUserEncryptionInfo(member.memberDetails.email, member.address); + const { publicKey } = await this.getUserEncryptionInfo(member.email); const memberKeysEncrypter = new Encrypter( this.wallet, this.dataEncrypter.keys, publicKey ); - const keys = [await memberKeysEncrypter.encryptMemberKey(keyPair)]; - const newState = await this.mergeState({ keys }); + let keys: EncryptedKeys[];; + try { + keys = [await memberKeysEncrypter.encryptMemberKey(keyPair)]; + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + const currentMemberState = member.data?.length > 0 ? await this.api.getNodeState(member.data[member.data.length - 1]) : {}; + const newState = await this.mergeState(currentMemberState, { keys }); const signature = await this.signData(newState); newMembershipStates.push({ data: newState, tags: [ @@ -249,20 +292,21 @@ class MembershipService extends Service { newMembershipRefs.push(member.id); } } - const ids = await this.api.uploadData(newMembershipStates, true); + const dataTxIds = await this.api.uploadData(newMembershipStates); data = []; newMembershipRefs.forEach((memberId, memberIndex) => { - data.push({ id: memberId, value: ids[memberIndex].id }) + data.push({ id: memberId, value: dataTxIds[memberIndex] }) }) } - const txId = await this.api.postContractTransaction( + const { id, object } = await this.api.postContractTransaction( this.vaultId, { function: this.function, data }, this.tags ); - return { transactionId: txId }; + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } /** @@ -270,11 +314,18 @@ class MembershipService extends Service { * @param {RoleType} role CONTRIBUTOR or VIEWER * @returns Promise with corresponding transaction id */ - public async changeRole(membershipId: string, role: RoleType): Promise<{ transactionId: string }> { + public async changeRole(membershipId: string, role: RoleType): Promise { await this.setVaultContextFromMembershipId(membershipId); this.setActionRef(actionRefs.MEMBERSHIP_CHANGE_ROLE); this.setFunction(functions.MEMBERSHIP_CHANGE_ROLE); - return this.nodeUpdate(null, { role }); + + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function, role }, + await this.getTags() + ); + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } /** @@ -282,15 +333,25 @@ class MembershipService extends Service { * @param {string} vaultId * @param {string} email invitee's email * @param {string} role CONTRIBUTOR or VIEWER + * @param {MembershipCreateOptions} [options] invitation email message, etc. * @returns Promise with new membership id & corresponding transaction id */ - public async inviteNewUser(vaultId: string, email: string, role: RoleType): Promise<{ + public async inviteNewUser(vaultId: string, email: string, role: RoleType, options: MembershipCreateOptions = {}): Promise<{ membershipId: string }> { - const { id } = await this.api.inviteNewUser(vaultId, email, role); + const { id } = await this.api.inviteNewUser(vaultId, email, role, options.message); return { membershipId: id }; } + /** + * Revoke invite for user without an Akord account + * @param {string} vaultId + * @param {string} membershipId + */ + public async revokeInvite(vaultId: string, membershipId: string): Promise { + await this.api.revokeInvite(vaultId, membershipId); + } + /** * @param {string} membershipId * @returns Promise with corresponding transaction id @@ -299,18 +360,26 @@ class MembershipService extends Service { const object = await this.api.getMembership(membershipId, this.vaultId); this.setActionRef(actionRefs.MEMBERSHIP_INVITE_RESEND); if (object.status !== status.PENDING && object.status !== status.INVITED) { - throw new Error("Cannot resend the invitation for member: " + membershipId + + throw new BadRequest("Cannot resend the invitation for member: " + membershipId + ". Found invalid status: " + object.status); } await this.api.inviteResend(object.vaultId, membershipId); } - async profileUpdate(membershipId: string, name: string, avatar: any): Promise<{ transactionId: string; }> { + async profileUpdate(membershipId: string, name: string, avatar: ArrayBuffer): Promise { await this.setVaultContextFromMembershipId(membershipId); - this.setActionRef(actionRefs.MEMBERSHIP_PROFILE_UPDATE); const memberDetails = await this.processMemberDetails({ name, avatar }, true); + this.setActionRef(actionRefs.MEMBERSHIP_PROFILE_UPDATE); this.setFunction(functions.MEMBERSHIP_UPDATE); - return this.nodeUpdate({ memberDetails }); + + const data = await this.mergeAndUploadBody({ memberDetails }); + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function, data }, + await this.getTags() + ); + const membership = await this.processMembership(object, !this.isPublic, this.keys); + return { transactionId: id, object: membership }; } protected async setVaultContextFromMembershipId(membershipId: string, vaultId?: string) { @@ -325,8 +394,35 @@ class MembershipService extends Service { const tags = await super.getTags(); return tags.concat(new Tag(protocolTags.MEMBERSHIP_ID, this.objectId)); } + + protected async processMembership(object: Membership, shouldDecrypt: boolean, keys?: EncryptedKeys[]): Promise { + const membership = new Membership(object, keys); + if (shouldDecrypt) { + try { + await membership.decrypt(); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + return membership; + } }; +export type MembershipCreateOptions = { + message?: string +} + +type MembershipCreateResult = { + membershipId: string, + transactionId: string, + object: Membership +} + +type MembershipUpdateResult = { + transactionId: string, + object: Membership +} + export { MembershipService } diff --git a/src/core/memo.ts b/src/core/memo.ts index ae4019dd..6f456e97 100644 --- a/src/core/memo.ts +++ b/src/core/memo.ts @@ -1,8 +1,11 @@ -import { NodeService } from "./node"; +import { NodeCreateOptions, NodeService } from "./node"; import { reactionEmoji, actionRefs, functions } from "../constants"; import lodash from "lodash"; import { Memo, MemoReaction, MemoVersion, nodeType } from "../types/node"; -import { ListOptions } from "../types/list-options"; +import { ListOptions } from "../types/query-options"; +import { NotFound } from "../errors/not-found"; +import { EncryptedKeys } from "@akord/crypto"; +import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; class MemoService extends NodeService { static readonly reactionEmoji = reactionEmoji; @@ -16,31 +19,28 @@ class MemoService extends NodeService { } as ListOptions; /** - * @param {string} vaultId - * @param {string} message - * @param {string} [parentId] parent folder id - * @returns Promise with new node id & corresponding transaction id - */ - public async create(vaultId: string, message: string, parentId?: string): Promise<{ - memoId: string, - transactionId: string - }> { + * @param {string} vaultId + * @param {string} message + * @param {NodeCreateOptions} [options] parent id, etc. + * @returns Promise with new node id & corresponding transaction id + */ + public async create(vaultId: string, message: string, options: NodeCreateOptions = this.defaultCreateOptions): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.MEMO_CREATE); this.setFunction(functions.NODE_CREATE); const body = { versions: [await this.memoVersion(message)] }; - const { nodeId, transactionId } = await this.nodeCreate(body, { parentId }); - return { memoId: nodeId, transactionId }; + const { nodeId, transactionId, object } = await this.nodeCreate(body, { parentId: options.parentId }); + return { memoId: nodeId, transactionId, object }; } /** - * @param {string} memoId - * @param {reactionEmoji} reaction - * @returns Promise with corresponding transaction id - */ - public async addReaction(memoId: string, reaction: reactionEmoji): Promise<{ transactionId: string }> { + * @param {string} memoId + * @param {reactionEmoji} reaction + * @returns Promise with corresponding transaction id + */ + public async addReaction(memoId: string, reaction: reactionEmoji): Promise { await this.setVaultContextFromNodeId(memoId, this.objectType); this.setActionRef(actionRefs.MEMO_ADD_REACTION); this.setFunction(functions.NODE_UPDATE); @@ -49,15 +49,15 @@ class MemoService extends NodeService { const currentState = await this.api.getNodeState(this.object.data[this.object.data.length - 1]); const newState = lodash.cloneDeepWith(currentState); newState.versions[newState.versions.length - 1].reactions.push(await this.memoReaction(reaction)); - const { data, metadata } = await this.uploadState(newState); + const dataTxId = await this.uploadState(newState); - const txId = await this.api.postContractTransaction( + const { id, object } = await this.api.postContractTransaction( this.vaultId, - { function: this.function, data }, - this.tags, - metadata + { function: this.function, data: dataTxId }, + this.tags ); - return { transactionId: txId } + const memo = await this.processMemo(object, !this.isPublic, this.keys); + return { transactionId: id, object: memo }; } /** @@ -65,22 +65,34 @@ class MemoService extends NodeService { * @param {reactionEmoji} reaction * @returns Promise with corresponding transaction id */ - public async removeReaction(memoId: string, reaction: reactionEmoji): Promise<{ transactionId: string }> { + public async removeReaction(memoId: string, reaction: reactionEmoji): Promise { await this.setVaultContextFromNodeId(memoId, this.objectType); this.setActionRef(actionRefs.MEMO_REMOVE_REACTION); this.setFunction(functions.NODE_UPDATE); this.tags = await this.getTags(); const body = await this.deleteReaction(reaction); - const { data, metadata } = await this.uploadState(body); + const dataTxId = await this.uploadState(body); - const txId = await this.api.postContractTransaction( + const { id, object } = await this.api.postContractTransaction( this.vaultId, - { function: this.function, data }, - this.tags, - metadata + { function: this.function, data: dataTxId }, + this.tags ); - return { transactionId: txId } + const memo = await this.processMemo(object, !this.isPublic, this.keys); + return { transactionId: id, object: memo }; + } + + protected async processMemo(object: Memo, shouldDecrypt: boolean, keys?: EncryptedKeys[]): Promise { + const memo = new Memo(object, keys); + if (shouldDecrypt) { + try { + await memo.decrypt(); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + return memo; } private async memoVersion(message: string): Promise { @@ -120,10 +132,21 @@ class MemoService extends NodeService { return key; } } - throw new Error("Could not find reaction: " + reaction + " for given user.") + throw new NotFound("Could not find reaction: " + reaction + " for given user.") } }; +type MemoCreateResult = { + memoId: string, + transactionId: string, + object: Memo +} + +type MemoUpdateResult = { + transactionId: string, + object: Memo +} + export { MemoService } \ No newline at end of file diff --git a/src/core/node.ts b/src/core/node.ts index d20b9363..1042234b 100644 --- a/src/core/node.ts +++ b/src/core/node.ts @@ -2,17 +2,18 @@ import { Service } from './service'; import { functions, protocolTags, status } from "../constants"; import { NodeLike, NodeType } from '../types/node'; import { EncryptedKeys } from '@akord/crypto'; -import { ListOptions } from '../types/list-options'; +import { GetOptions, ListOptions } from '../types/query-options'; import { Tag, Tags } from '../types/contract'; import { Paginated } from '../types/paginated'; +import { v4 as uuidv4 } from "uuid"; +import { IncorrectEncryptionKey } from '../errors/incorrect-encryption-key'; class NodeService extends Service { - - protected NodeType: new (arg0: any, arg1: EncryptedKeys[]) => NodeLike objectType: NodeType; defaultListOptions = { shouldDecrypt: true, + parentId: undefined, filter: { status: { ne: status.REVOKED }, and: { @@ -21,18 +22,78 @@ class NodeService extends Service { } } as ListOptions; + defaultGetOptions = { + shouldDecrypt: true, + } as GetOptions; + + defaultCreateOptions = { + parentId: undefined + } as NodeCreateOptions; + + /** + * @param {string} nodeId + * @returns Promise with the decrypted node + */ + public async get(nodeId: string, options: GetOptions = this.defaultGetOptions): Promise { + const getOptions = { + ...this.defaultGetOptions, + ...options + } + const nodeProto = await this.api.getNode(nodeId, this.objectType, getOptions.vaultId); + const { isEncrypted, keys } = await this.api.getMembershipKeys(nodeProto.vaultId); + const node = await this.processNode(nodeProto, isEncrypted && getOptions.shouldDecrypt, keys); + return node as T; + } + + /** + * @param {string} vaultId + * @param {ListOptions} options + * @returns Promise with paginated nodes within given vault + */ + public async list(vaultId: string, options: ListOptions = this.defaultListOptions = this.defaultListOptions): Promise> { + const listOptions = { + ...this.defaultListOptions, + ...options + } + const response = await this.api.getNodesByVaultId(vaultId, this.objectType, listOptions); + const { isEncrypted, keys } = listOptions.shouldDecrypt ? await this.api.getMembershipKeys(vaultId) : { isEncrypted: false, keys: [] }; + const promises = response.items + .map(async nodeProto => { + return await this.processNode(nodeProto, isEncrypted && listOptions.shouldDecrypt, keys); + }) as Promise[]; + const { items, errors } = await this.handleListErrors(response.items, promises); + return { + items, + nextToken: response.nextToken, + errors + } + } + + /** + * @param {string} vaultId + * @param {ListOptions} options + * @returns Promise with all nodes within given vault + */ + public async listAll(vaultId: string, options: ListOptions = this.defaultListOptions): Promise> { + const list = async (options: ListOptions & { vaultId: string }) => { + return await this.list(options.vaultId, options); + } + return await this.paginate(list, { ...options, vaultId }); + } + /** * @param {string} nodeId * @param {string} name new name * @returns Promise with corresponding transaction id */ - public async rename(nodeId: string, name: string): Promise<{ transactionId: string }> { + public async rename(nodeId: string, name: string): Promise { await this.setVaultContextFromNodeId(nodeId, this.objectType); + this.setActionRef(this.objectType.toUpperCase() + "_RENAME"); this.setFunction(functions.NODE_UPDATE); const body = { name: await this.processWriteString(name) }; - return this.nodeUpdate(body); + return this.nodeUpdate(body); } /** @@ -40,98 +101,95 @@ class NodeService extends Service { * @param {string} [parentId] new parent folder id * @returns Promise with corresponding transaction id */ - public async move(nodeId: string, parentId?: string, vaultId?: string): Promise<{ transactionId: string }> { + public async move(nodeId: string, parentId?: string, vaultId?: string): Promise { await this.setVaultContextFromNodeId(nodeId, this.objectType, vaultId); + this.setActionRef(this.objectType.toUpperCase() + "_MOVE"); this.setFunction(functions.NODE_MOVE); - return this.nodeUpdate(null, { parentId }); + return this.nodeUpdate(null, { parentId }); } /** * @param {string} nodeId * @returns Promise with corresponding transaction id */ - public async revoke(nodeId: string, vaultId?: string): Promise<{ transactionId: string }> { + public async revoke(nodeId: string, vaultId?: string): Promise { await this.setVaultContextFromNodeId(nodeId, this.objectType, vaultId); + this.setActionRef(this.objectType.toUpperCase() + "_REVOKE"); this.setFunction(functions.NODE_REVOKE); - return this.nodeUpdate(); + return this.nodeUpdate(); } /** * @param {string} nodeId * @returns Promise with corresponding transaction id */ - public async restore(nodeId: string, vaultId?: string): Promise<{ transactionId: string }> { + public async restore(nodeId: string, vaultId?: string): Promise { await this.setVaultContextFromNodeId(nodeId, this.objectType, vaultId); + this.setActionRef(this.objectType.toUpperCase() + "_RESTORE"); this.setFunction(functions.NODE_RESTORE); - return this.nodeUpdate(); + return this.nodeUpdate(); } /** * @param {string} nodeId * @returns Promise with corresponding transaction id */ - public async delete(nodeId: string, vaultId?: string): Promise<{ transactionId: string }> { + public async delete(nodeId: string, vaultId?: string): Promise { await this.setVaultContextFromNodeId(nodeId, this.objectType, vaultId); + this.setActionRef(this.objectType.toUpperCase() + "_DELETE"); this.setFunction(functions.NODE_DELETE); - return this.nodeUpdate(); + return this.nodeUpdate(); } - /** - * @param {string} nodeId - * @returns Promise with the decrypted node - */ - public async get(nodeId: string, vaultId?: string, shouldDecrypt = true): Promise { - const nodeProto = await this.api.getNode(nodeId, this.objectType, vaultId); - const { isEncrypted, keys } = await this.api.getMembershipKeys(nodeProto.vaultId); - const node = this.nodeInstance(nodeProto, keys); - if (isEncrypted && shouldDecrypt) { - await node.decrypt(); - } - return node as T; - } + protected async nodeCreate(body?: any, clientInput?: any): Promise<{ + nodeId: string, + transactionId: string, + object: T + }> { + const nodeId = uuidv4(); + this.setObjectId(nodeId); + this.setFunction(functions.NODE_CREATE); - /** - * @param {string} vaultId - * @returns Promise with paginated nodes within given vault - */ - public async list(vaultId: string, parentId?: string, listOptions = this.defaultListOptions): Promise> { - const response = await this.api.getNodesByVaultId(vaultId, this.objectType, parentId, listOptions.filter, listOptions.limit, listOptions.nextToken); - const { isEncrypted, keys } = listOptions.shouldDecrypt ? await this.api.getMembershipKeys(vaultId) : { isEncrypted: false, keys: [] }; - return { - items: await Promise.all( - response.items - .map(async nodeProto => { - const node = this.nodeInstance(nodeProto, keys); - if (isEncrypted) { - await node.decrypt(); - } - return node as NodeLike; - })) as NodeLike[], - nextToken: response.nextToken + this.tags = await this.getTags(); + + const input = { + function: this.function, + ...clientInput + }; + + if (body) { + const id = await this.uploadState(body); + input.data = id; } - } - /** - * @param {string} vaultId - * @returns Promise with all nodes within given vault - */ - public async listAll(vaultId: string, parentId?: string, listOptions = this.defaultListOptions): Promise> { - let token = null; - let nodeArray = [] as NodeLike[]; - do { - const { items, nextToken } = await this.list(vaultId, parentId, listOptions); - nodeArray = nodeArray.concat(items); - token = nextToken; - listOptions.nextToken = nextToken; - if (nextToken === "null") { - token = null; - } - } while (token); - return nodeArray; + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + input, + this.tags + ); + const node = await this.processNode(object as any, !this.isPublic, this.keys) as any; + return { nodeId, transactionId: id, object: node }; } - private nodeInstance(nodeProto: any, keys: Array): NodeLike { - return new this.NodeType(nodeProto, keys); + protected async nodeUpdate(body?: any, clientInput?: any): Promise<{ transactionId: string, object: T }> { + const input = { + function: this.function, + ...clientInput + }; + + this.tags = await this.getTags(); + + if (body) { + const id = await this.mergeAndUploadBody(body); + input.data = id; + } + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + input, + this.tags + ); + const node = await this.processNode(object as any, !this.isPublic, this.keys) as any; + return { transactionId: id, object: node }; } protected async setVaultContextFromNodeId(nodeId: string, type: NodeType, vaultId?: string) { @@ -146,6 +204,33 @@ class NodeService extends Service { const tags = await super.getTags(); return tags.concat(new Tag(protocolTags.NODE_ID, this.objectId)); } + + protected async processNode(object: NodeLike, shouldDecrypt: boolean, keys?: EncryptedKeys[]): Promise { + const node = this.nodeInstance(object, keys); + if (shouldDecrypt) { + try { + await node.decrypt(); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + return node; + } + + protected NodeType: new (arg0: any, arg1: EncryptedKeys[]) => NodeLike + + private nodeInstance(nodeProto: any, keys: Array): NodeLike { + return new this.NodeType(nodeProto, keys); + } +} + +type NodeUpdateResult = { + transactionId: string, + object: NodeLike +} + +export type NodeCreateOptions = { + parentId?: string } export { diff --git a/src/core/note.ts b/src/core/note.ts index ff661755..cd8f96f4 100644 --- a/src/core/note.ts +++ b/src/core/note.ts @@ -1,57 +1,19 @@ -import { NodeService } from "./node"; +import { NodeCreateOptions, NodeService } from "./node"; import { Stack, nodeType } from "../types/node"; import { StackService } from "./stack"; import { arrayToString } from "@akord/crypto"; import { createFileLike } from "./file"; import { Paginated } from "../types/paginated"; -enum NoteTypes { - MD = "text/markdown", - JSON = "application/json" -} - -type NoteType = "text/markdown" | "application/json"; - class NoteService extends NodeService { public stackService = new StackService(this.wallet, this.api); objectType = nodeType.STACK; NodeType = Stack; - /** - * @param {string} vaultId - * @param {string} content note content, ex: stringified JSON - * @param {string} name note name - * @param {string} [parentId] parent folder id - * @param {string} [mimeType] MIME type for the note text file, default: text/markdown - * @returns Promise with new note id & corresponding transaction id - */ - public async create(vaultId: string, content: string, name: string, parentId?: string, mimeType?: NoteType): Promise<{ - noteId: string, - transactionId: string - }> { - const noteFile = await createFileLike([content], name, mimeType ? mimeType : NoteTypes.MD); - const { stackId, transactionId } = await this.stackService.create( - vaultId, - noteFile, - name, - parentId - ); - return { noteId: stackId, transactionId }; - } - - /** - * @param {string} noteId - * @param {string} content note content, ex: stringified JSON - * @param {string} name note name - * @param {string} [mimeType] MIME type for the note text file, default: text/markdown - * @returns Promise with corresponding transaction id - */ - public async uploadRevision(noteId: string, content: string, name: string, mimeType?: NoteType): Promise<{ - transactionId: string - }> { - const noteFile = await createFileLike([content], name, mimeType ? mimeType : NoteTypes.MD); - return this.stackService.uploadRevision(noteId, noteFile); - } + defaultCreateOptions = { + parentId: undefined, + mimeType: NoteTypes.MD + } as NoteCreateOptions; /** * Get note version by index, return the latest version by default @@ -68,17 +30,75 @@ class NoteService extends NodeService { * @param {string} vaultId * @returns Promise with all notes within given vault */ - public async list(vaultId: string, parentId?: string, listOptions = this.defaultListOptions): Promise> { - const stacks = await this.stackService.list(vaultId, parentId, listOptions) as Paginated; + public async list(vaultId: string, options = this.defaultListOptions): Promise> { + const stacks = await this.stackService.list(vaultId, options) as Paginated; const notes = stacks.items.filter((stack: Stack) => this.isValidNoteType(stack.getVersion().type)); return { items: notes, nextToken: stacks.nextToken } } + /** + * @param {string} vaultId + * @param {string} content note content, ex: stringified JSON + * @param {string} name note name + * @param {NoteCreateOptions} [options] parent id, mime type, etc. + * @returns Promise with new note id & corresponding transaction id + */ + public async create(vaultId: string, content: string, name: string, options: NoteCreateOptions = this.defaultCreateOptions): Promise { + const createOptions = { + ...this.defaultCreateOptions, + ...options + } + const noteFile = await createFileLike([content], name, createOptions.mimeType); + const { stackId, transactionId, object } = await this.stackService.create( + vaultId, + noteFile, + name, + createOptions + ); + return { noteId: stackId, transactionId, object }; + } + + /** + * @param {string} noteId + * @param {string} content note content, ex: stringified JSON + * @param {string} name note name + * @param {NoteOptions} [options] parent id, mime type, etc. + * @returns Promise with corresponding transaction id + */ + public async uploadRevision(noteId: string, content: string, name: string, options: NoteOptions = this.defaultCreateOptions): Promise { + const noteFile = await createFileLike([content], name, options.mimeType); + return this.stackService.uploadRevision(noteId, noteFile); + } + private isValidNoteType(type: string) { return Object.values(NoteTypes).includes(type); } }; +enum NoteTypes { + MD = "text/markdown", + JSON = "application/json" +} + +type NoteCreateResult = { + noteId: string, + transactionId: string, + object: Stack +} + +type NoteUpdateResult = { + transactionId: string, + object: Stack +} + +type NoteType = "text/markdown" | "application/json"; + +export type NoteOptions = { + mimeType?: string +} + +export type NoteCreateOptions = NodeCreateOptions & NoteOptions + export { NoteService } \ No newline at end of file diff --git a/src/core/profile.ts b/src/core/profile.ts index b0a81e56..0caa6b98 100644 --- a/src/core/profile.ts +++ b/src/core/profile.ts @@ -4,6 +4,8 @@ import { InMemoryStorageStrategy, PCacheable, PCacheBuster } from "@akord/ts-cac import { CacheBusters } from "../types/cacheable"; import { Service } from "./service"; import { objectType } from "../constants"; +import { Membership } from "../types/membership"; +import { ListOptions } from "../types/query-options"; class ProfileService extends Service { objectType = objectType.PROFILE; @@ -30,37 +32,44 @@ class ProfileService extends Service { @PCacheBuster({ cacheBusterNotifier: CacheBusters.profile }) - public async update(name: string, avatar: any): Promise<{ transactionId: string }[]> { - let transactions = []; + public async update(name: string, avatar: ArrayBuffer): Promise<{ + transactions: { id: string, transactionId: string }[], + errors: { id: string, error: any }[] + }> { + // update profile + const user = await this.api.getUser(); + this.setObject(user); - const profilePromise = new Promise(async (resolve, reject) => { - const profile = await this.api.getProfile(); - this.setObject(profile); + this.setRawDataEncryptionPublicKey(this.wallet.publicKeyRaw()); + this.setIsPublic(false); + const profileDetails = await this.processMemberDetails({ name, avatar }, false); - this.setRawDataEncryptionPublicKey(this.wallet.publicKeyRaw()); - this.setIsPublic(false); - const profileDetails = await this.processMemberDetails({ name, avatar }, false); + const newProfileDetails = new ProfileDetails({ + ...user, + ...profileDetails, + }); + await this.api.updateUser(newProfileDetails.name, newProfileDetails.avatarUri); - // merge & upload current profile state to Arweave - const currentProfileDetails = profile.state.profileDetails; - const mergedProfileDetails = { - name: profileDetails.name || currentProfileDetails.name || currentProfileDetails.fullName, - avatarUri: profileDetails.avatarUri || currentProfileDetails.avatarUri, - } + // update user memberships + let transactions = []; - await this.api.uploadData([{ data: { profileDetails: mergedProfileDetails }, tags: [] }], false); - await this.api.updateProfile(mergedProfileDetails.name, mergedProfileDetails.avatarUri); - resolve(); - }) + const memberships = await this.listMemberships(); - const memberships = await this.api.getMemberships(); - const membershipPromiseArray = memberships.map(async (membership) => { + const membershipPromises = memberships.map(async (membership) => { const membershipService = new MembershipService(this.wallet, this.api); const { transactionId } = await membershipService.profileUpdate(membership.id, name, avatar); - transactions.push(transactionId); + transactions.push({ id: membership.id, transactionId: transactionId }); + return membership; }) - await Promise.all(membershipPromiseArray.concat([profilePromise])); - return transactions; + const { errors } = await this.handleListErrors(memberships, membershipPromises); + return { transactions, errors }; + } + + private async listMemberships(): Promise> { + const list = async (listOptions: ListOptions) => { + return await this.api.getMemberships(listOptions.limit, listOptions.nextToken); + } + return await this.paginate(list, {}); } }; diff --git a/src/core/service.ts b/src/core/service.ts index 91614b6f..671e7d5e 100644 --- a/src/core/service.ts +++ b/src/core/service.ts @@ -11,17 +11,19 @@ import { arrayToBase64, base64ToJson, deriveAddress, - AkordWallet, + EncryptedKeys } from "@akord/crypto"; -import { v4 as uuidv4 } from "uuid"; import { objectType, protocolTags, functions, dataTags, encryptionTags, smartweaveTags } from '../constants'; import lodash from "lodash"; import { Vault } from "../types/vault"; import { Tag, Tags } from "../types/contract"; import { NodeLike } from "../types/node"; -import { Membership, MembershipKeys } from "../types/membership"; +import { Membership } from "../types/membership"; import { Object, ObjectType } from "../types/object"; import { EncryptedPayload } from "@akord/crypto/lib/types"; +import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; +import { ProfileDetails } from "../types/profile-details"; +import { ListOptions } from "../types/query-options"; declare const Buffer; @@ -32,7 +34,7 @@ class Service { wallet: Wallet dataEncrypter: Encrypter - membershipKeys: MembershipKeys + keys: Array vaultId: string objectId: string @@ -56,6 +58,48 @@ class Service { ) } + setKeys(keys: EncryptedKeys[]) { + this.keys = keys; + this.dataEncrypter.setKeys(keys); + } + + setVaultId(vaultId: string) { + this.vaultId = vaultId; + } + + setGroupRef(groupRef: string) { + this.groupRef = groupRef; + } + + setIsPublic(isPublic: boolean) { + this.isPublic = isPublic; + } + + setVault(vault: Vault) { + this.vault = vault; + } + + setRawDataEncryptionPublicKey(publicKey: Uint8Array) { + this.dataEncrypter.setRawPublicKey(publicKey); + } + + async processReadRaw(data: ArrayBuffer | string, headers: any, shouldDecrypt = true): Promise { + if (this.isPublic || !shouldDecrypt) { + return data as ArrayBuffer; + } + + const encryptedPayload = this.getEncryptedPayload(data, headers); + try { + if (encryptedPayload) { + return this.dataEncrypter.decryptRaw(encryptedPayload, false); + } else { + return this.dataEncrypter.decryptRaw(data as string); + } + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + protected async setVaultContext(vaultId: string) { const vault = await this.api.getVault(vaultId); this.setVault(vault); @@ -70,88 +114,23 @@ class Service { const keys = encryptionKeys.keys.map(((keyPair: any) => { return { encPrivateKey: keyPair.encPrivateKey, - publicKey: keyPair.publicKey ? keyPair.publicKey : keyPair.encPublicKey + encPublicKey: keyPair.publicKey ? keyPair.publicKey : keyPair.encPublicKey } })) this.setKeys(keys); - if (encryptionKeys.publicKey) { - this.setRawDataEncryptionPublicKey(base64ToArray(encryptionKeys.publicKey)); - } else { - const publicKey = await this.dataEncrypter.wallet.decrypt(encryptionKeys.keys[encryptionKeys.keys.length - 1].encPublicKey); - this.setRawDataEncryptionPublicKey(publicKey); + try { + if (encryptionKeys.publicKey) { + this.setRawDataEncryptionPublicKey(base64ToArray(encryptionKeys.publicKey)); + } else { + const publicKey = await this.dataEncrypter.wallet.decrypt(encryptionKeys.keys[encryptionKeys.keys.length - 1].encPublicKey); + this.setRawDataEncryptionPublicKey(publicKey); + } + } catch (error) { + throw new IncorrectEncryptionKey(error); } } } - protected async nodeRename(name: string): Promise<{ transactionId: string }> { - const body = { - name: await this.processWriteString(name) - }; - this.setFunction(this.objectType === "Vault" ? functions.VAULT_UPDATE : functions.NODE_UPDATE); - return this.nodeUpdate(body); - } - - protected async nodeUpdate(body?: any, clientInput?: any, clientMetadata?: any): Promise<{ transactionId: string }> { - const input = { - function: this.function, - ...clientInput - }; - - this.tags = await this.getTags(); - - if (body) { - const { data, metadata } = await this.mergeAndUploadBody(body); - input.data = data; - clientMetadata = { - ...clientMetadata, - ...metadata - } - } - const txId = await this.api.postContractTransaction( - this.vaultId, - input, - this.tags, - clientMetadata - ); - return { transactionId: txId } - } - - protected async nodeCreate(body?: any, clientInput?: any, clientMetadata?: any): Promise<{ - nodeId: string, - transactionId: string - }> { - const nodeId = uuidv4(); - this.setObjectId(nodeId); - this.setFunction(functions.NODE_CREATE); - - this.tags = await this.getTags(); - - const { metadata, data } = await this.uploadState(body); - - const input = { - function: this.function, - data, - ...clientInput - }; - const txId = await this.api.postContractTransaction( - this.vaultId, - input, - this.tags, - { ...metadata, ...clientMetadata } - ); - this.setActionRef(null); - return { nodeId, transactionId: txId }; - } - - setKeys(keys: any) { - this.membershipKeys = keys; - this.dataEncrypter.setKeys(keys); - } - - setVaultId(vaultId: string) { - this.vaultId = vaultId; - } - protected setObjectId(objectId: string) { this.objectId = objectId; } @@ -168,68 +147,61 @@ class Service { this.actionRef = actionRef; } - setGroupRef(groupRef: string) { - this.groupRef = groupRef; - } - - setIsPublic(isPublic: boolean) { - this.isPublic = isPublic; - } - - setVault(vault: Vault) { - this.vault = vault; - } - protected setObject(object: NodeLike | Membership | Vault) { this.object = object; } - setRawDataEncryptionPublicKey(publicKey) { - this.dataEncrypter.setRawPublicKey(publicKey); - } - - protected async getProfileDetails() { - const profile = await this.api.getProfile(); - if (profile) { - const profileEncrypter = new Encrypter(this.wallet, profile.state.keys, null); + protected async getProfileDetails(): Promise { + const user = await this.api.getUser(); + if (user) { + const profileEncrypter = new Encrypter(this.wallet, null, null); profileEncrypter.decryptedKeys = [ { publicKey: this.wallet.publicKeyRaw(), privateKey: this.wallet.privateKeyRaw() } ] - let profileDetails = profile.state.profileDetails; - delete profileDetails.__typename; let avatar = null; - const resourceUri = this.getAvatarUri(profileDetails); + const resourceUri = this.getAvatarUri(new ProfileDetails(user)); if (resourceUri) { const { fileData, headers } = await this.api.downloadFile(resourceUri); - const encryptedData = this.getEncryptedData(fileData, headers); - if (encryptedData) { - avatar = await profileEncrypter.decryptRaw(encryptedData, false); - } else { - const dataString = arrayToString(new Uint8Array(fileData.data)); - avatar = await profileEncrypter.decryptRaw(dataString, true); + const encryptedPayload = this.getEncryptedPayload(fileData, headers); + try { + if (encryptedPayload) { + avatar = await profileEncrypter.decryptRaw(encryptedPayload, false); + } else { + const dataString = arrayToString(new Uint8Array(fileData)); + avatar = await profileEncrypter.decryptRaw(dataString, true); + } + } catch (error) { + throw new IncorrectEncryptionKey(error); } } - const decryptedProfile = await profileEncrypter.decryptObject( - profileDetails, - ['fullName', 'name', 'phone'], - ); - decryptedProfile.name = decryptedProfile.name || decryptedProfile.fullName; - delete decryptedProfile.fullName; - return { ...decryptedProfile, avatar } + try { + const decryptedProfile = await profileEncrypter.decryptObject( + user, + ['name'], + ); + return { ...decryptedProfile, avatar } + } catch (error) { + throw new IncorrectEncryptionKey(error); + } } - return {}; + return {}; } - protected async processWriteRaw(data: any, encryptedKey?: string) { - let processedData: any; + protected async processWriteRaw(data: ArrayBuffer, encryptedKey?: string) { + let processedData: ArrayBuffer; const tags = [] as Tags; if (this.isPublic) { processedData = data; } else { - const encryptedFile = await this.dataEncrypter.encryptRaw(data, false, encryptedKey) as EncryptedPayload; + let encryptedFile: EncryptedPayload; + try { + encryptedFile = await this.dataEncrypter.encryptRaw(new Uint8Array(data), false, encryptedKey) as EncryptedPayload; + } catch (error) { + throw new IncorrectEncryptionKey(error); + } processedData = encryptedFile.encryptedData.ciphertext; const { address } = await this.getActiveKey(); tags.push(new Tag(encryptionTags.IV, encryptedFile.encryptedData.iv)) @@ -246,32 +218,36 @@ class Service { }; } - protected async processWriteString(data: string) { + protected async processWriteString(data: string): Promise { if (this.isPublic) return data; - const encryptedPayload = await this.dataEncrypter.encryptRaw(stringToArray(data)); - const decodedPayload = base64ToJson(encryptedPayload as string) as any; + let encryptedPayload: string; + try { + encryptedPayload = await this.dataEncrypter.encryptRaw(stringToArray(data)) as string; + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + const decodedPayload = base64ToJson(encryptedPayload) as any; decodedPayload.publicAddress = (await this.getActiveKey()).address; delete decodedPayload.publicKey; return jsonToBase64(decodedPayload); } - protected getAvatarUri(profileDetails: any) { + protected getAvatarUri(profileDetails: ProfileDetails) { if (profileDetails.avatarUri && profileDetails.avatarUri.length) { - return [...profileDetails.avatarUri].reverse().find(resourceUri => resourceUri.startsWith("s3:"))?.replace("s3:", ""); - } - else if (profileDetails.avatarUrl) { - return profileDetails.avatarUrl; + const avatarUri = [...profileDetails.avatarUri].reverse().find(resourceUri => resourceUri.startsWith("s3:"))?.replace("s3:", ""); + return avatarUri !== "null" && avatarUri; + } else { + return null; } - return null; } - protected async processAvatar(avatar: any, shouldBundleTransaction?: boolean) { + protected async processAvatar(avatar: ArrayBuffer, shouldBundleTransaction?: boolean): Promise<{ resourceTx: string, resourceUrl: string }> { const { processedData, encryptionTags } = await this.processWriteRaw(avatar); - return this.api.uploadFile(processedData, encryptionTags, false, shouldBundleTransaction); + return this.api.uploadFile(processedData, encryptionTags, { shouldBundleTransaction, public: false }); } - protected async processMemberDetails(memberDetails: any, shouldBundleTransaction?: boolean) { - let processedMemberDetails = {} as any; + protected async processMemberDetails(memberDetails: { name?: string, avatar?: ArrayBuffer }, shouldBundleTransaction?: boolean) { + let processedMemberDetails = {} as ProfileDetails; if (memberDetails.name) { processedMemberDetails.name = await this.processWriteString(memberDetails.name); } @@ -279,65 +255,49 @@ class Service { const { resourceUrl, resourceTx } = await this.processAvatar(memberDetails.avatar, shouldBundleTransaction); processedMemberDetails.avatarUri = [`arweave:${resourceTx}`, `s3:${resourceUrl}`]; } - return processedMemberDetails; + return new ProfileDetails(processedMemberDetails); } - protected async processReadString(data: any, shouldDecrypt = true) { + protected async processReadString(data: string, shouldDecrypt = true): Promise { if (this.isPublic || !shouldDecrypt) return data; const decryptedDataRaw = await this.processReadRaw(data, {}); return arrayToString(decryptedDataRaw); } - async processReadRaw(data: any, headers: any, shouldDecrypt = true) { - if (this.isPublic || !shouldDecrypt) { - return Buffer.from(data.data); - } - - const encryptedData = this.getEncryptedData(data, headers); - if (encryptedData) { - return this.dataEncrypter.decryptRaw(encryptedData, false); - } else { - return this.dataEncrypter.decryptRaw(data); - } - } - - protected getEncryptedData(data: any, headers: any) { + protected getEncryptedPayload(data: ArrayBuffer | string, headers: any): EncryptedPayload { const encryptedKey = headers['x-amz-meta-encryptedkey']; const iv = headers['x-amz-meta-iv']; - const publicKeyIndex = headers['x-amz-meta-public-key-index']; if (encryptedKey && iv) { return { encryptedKey, encryptedData: { iv: base64ToArray(iv), - ciphertext: data - }, - publicKeyIndex + ciphertext: data as ArrayBuffer + } } } return null; } - protected async mergeAndUploadBody(body: any) { - const mergedBody = await this.mergeState(body); + protected async mergeAndUploadBody(body: any): Promise { + const currentState = this.object?.data?.length > 0 + ? await this.api.getNodeState(this.object.data[this.object.data.length - 1]) + : {}; + const mergedBody = await this.mergeState(currentState, body); return this.uploadState(mergedBody); } - protected async signData(data: any) { - if (this.wallet instanceof AkordWallet) { - const encodedBody = jsonToBase64(data) - const privateKeyRaw = this.wallet.signingPrivateKeyRaw() - const signature = await signString( - encodedBody, - privateKeyRaw - ) - return signature; - } else { - return "--TODO--" - } + protected async signData(data: any): Promise { + const encodedBody = jsonToBase64(data) + const privateKeyRaw = this.wallet.signingPrivateKeyRaw() + const signature = await signString( + encodedBody, + privateKeyRaw + ) + return signature; } - protected async uploadState(state: any) { + protected async uploadState(state: any): Promise { const signature = await this.signData(state); const tags = [ new Tag(dataTags.DATA_TYPE, "State"), @@ -352,18 +312,11 @@ class Service { } else if (this.objectType !== objectType.VAULT) { tags.push(new Tag(protocolTags.NODE_ID, this.objectId)) } - const ids = await this.api.uploadData([{ data: state, tags }], true); - const metadata = { - dataRefs: [ - { ...ids[0], modelId: this.objectId, modelType: this.objectType, data: state } - ] - } - const data = ids[0].id; - return { metadata, data } + const ids = await this.api.uploadData([{ data: state, tags }]); + return ids[0]; } - protected async mergeState(stateUpdates: any) { - const currentState = this.object.data ? await this.api.getNodeState(this.object.data[this.object.data.length - 1]) : {}; + protected async mergeState(currentState: any, stateUpdates: any) { let newState = lodash.cloneDeepWith(currentState); lodash.mergeWith( newState, @@ -376,8 +329,8 @@ class Service { return newState; } - protected async getUserEncryptionInfo(email?: string, userAddress?: string) { - const { address, publicKey } = await this.api.getUserFromEmail(email || userAddress); + protected async getUserEncryptionInfo(email: string) { + const { address, publicKey } = await this.api.getUserPublicData(email); return { address, publicKey: base64ToArray(publicKey) } } @@ -397,6 +350,32 @@ class Service { } return tags; } + + protected async handleListErrors(originalItems: Array, promises: Array>) + : Promise<{ items: Array, errors: Array<{ id: string, error: string }> }> { + const results = await Promise.all(promises.map(p => p.catch(e => e))); + const items = results.filter(result => !(result instanceof Error)); + const errors = results + .map((result, index) => ({ result, index })) + .filter((mapped) => mapped.result instanceof Error) + .map((filtered) => ({ id: (originalItems[filtered.index]).id, error: filtered.result.message })); + return { items, errors }; + } + + protected async paginate(apiCall: any, listOptions: ListOptions & { vaultId?: string }): Promise> { + let token = undefined; + let results = [] as T[]; + do { + const { items, nextToken } = await apiCall(listOptions); + results = results.concat(items); + token = nextToken; + listOptions.nextToken = nextToken; + if (nextToken === "null") { + token = undefined; + } + } while (token); + return results; + } } export { diff --git a/src/core/stack.ts b/src/core/stack.ts index 09053914..2b9f5cb4 100644 --- a/src/core/stack.ts +++ b/src/core/stack.ts @@ -1,7 +1,7 @@ -import { NodeService } from "./node"; +import { NodeCreateOptions, NodeService } from "./node"; import { actionRefs, functions, objectType } from "../constants"; import { createThumbnail } from "./thumbnail"; -import { FileService } from "./file"; +import { FileService, FileUploadResult, FileUploadOptions } from "./file"; import { FileLike } from "../types/file"; import { FileVersion, Stack, StorageType, nodeType } from "../types/node"; @@ -14,40 +14,34 @@ class StackService extends NodeService { * @param {string} vaultId * @param {FileLike} file file object * @param {string} name stack name - * @param {string} [parentId] parent folder id - * @param {(progress:number)=>void} [progressHook] - * @param {AbortController} [cancelHook] + * @param {StackCreateOptions} [options] parent id, progress hook, cancel hook, etc. * @returns Promise with new stack id & corresponding transaction id */ - public async create(vaultId: string, file: FileLike, name: string, parentId?: string, - progressHook?: (progress: number, data?: any) => void, cancelHook?: AbortController): - Promise<{ - stackId: string, - transactionId: string - }> { + public async create(vaultId: string, file: FileLike, name: string, options: StackCreateOptions = this.defaultCreateOptions): + Promise { + const createOptions = { + ...this.defaultCreateOptions, + ...options + } await this.setVaultContext(vaultId); this.setActionRef(actionRefs.STACK_CREATE); this.setFunction(functions.NODE_CREATE); const body = { name: await this.processWriteString(name ? name : file.name), - versions: [await this.uploadNewFileVersion(file, progressHook, cancelHook)] + versions: [await this.uploadNewFileVersion(file, createOptions)] }; - const { nodeId, transactionId } = await this.nodeCreate(body, { parentId }); - return { stackId: nodeId, transactionId }; + const { nodeId, transactionId, object } = await this.nodeCreate(body, { parentId: createOptions.parentId }); + return { stackId: nodeId, transactionId, object }; } /** * @param {string} vaultId * @param {string} fileTxId arweave file transaction id reference - * @param {string} [parentId] parent folder id + * @param {NodeCreateOptions} [options] parent id, etc. * @returns Promise with new stack id & corresponding transaction id */ - public async import(vaultId: string, fileTxId: string, parentId?: string): - Promise<{ - stackId: string, - transactionId: string - }> { + public async import(vaultId: string, fileTxId: string, options: NodeCreateOptions = this.defaultCreateOptions): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.STACK_CREATE); this.setFunction(functions.NODE_CREATE); @@ -65,25 +59,25 @@ class StackService extends NodeService { name: await this.processWriteString(file.name), versions: [version] }; - const { nodeId, transactionId } = await this.nodeCreate(body, { parentId }); - return { stackId: nodeId, transactionId }; + const { nodeId, transactionId, object } = await this.nodeCreate(body, { parentId: options.parentId }); + return { stackId: nodeId, transactionId, object }; } /** - * @param {string} stackId - * @param {FileLike} file file object - * @param {(progress:number)=>void} [progressHook] - * @returns Promise with corresponding transaction id - */ - public async uploadRevision(stackId: string, file: FileLike, progressHook?: (progress: number, data?: any) => void): Promise<{ transactionId: string }> { + * @param {string} stackId + * @param {FileLike} file file object + * @param {FileUploadOptions} [options] progress hook, cancel hook, etc. + * @returns Promise with corresponding transaction id + */ + public async uploadRevision(stackId: string, file: FileLike, options: FileUploadOptions = {}): Promise { await this.setVaultContextFromNodeId(stackId, this.objectType); this.setActionRef(actionRefs.STACK_UPLOAD_REVISION); const body = { - versions: [await this.uploadNewFileVersion(file, progressHook)] + versions: [await this.uploadNewFileVersion(file, options)] }; this.setFunction(functions.NODE_UPDATE); - return this.nodeUpdate(body); + return this.nodeUpdate(body); } /** @@ -96,25 +90,25 @@ class StackService extends NodeService { const stack = new Stack(await this.api.getNode(stackId, objectType.STACK, this.vaultId), null); const version = stack.getVersion(index); await this.setVaultContext(stack.vaultId); - const { fileData, headers } = await this.api.downloadFile(version.getUri(StorageType.S3), this.isPublic); + const { fileData, headers } = await this.api.downloadFile(version.getUri(StorageType.S3), { public: this.isPublic }); const data = await this.processReadRaw(fileData, headers); const name = await this.processReadString(version.name); return { name, data }; } /** - * Get stack file uri by index, return the latest arweave uri by default - * @param {string} stackId - * @param {StorageType} [type] storage type, default to arweave - * @param {number} [index] file version index, default to latest - * @returns Promise with stack file uri - */ + * Get stack file uri by index, return the latest arweave uri by default + * @param {string} stackId + * @param {StorageType} [type] storage type, default to arweave + * @param {number} [index] file version index, default to latest + * @returns Promise with stack file uri + */ public async getUri(stackId: string, type: StorageType = StorageType.ARWEAVE, index?: number): Promise { const stack = new Stack(await this.api.getNode(stackId, objectType.STACK, this.vaultId), null); return stack.getUri(type, index); } - private async uploadNewFileVersion(file: FileLike, progressHook?: any, cancelHook?: any): Promise { + private async uploadNewFileVersion(file: FileLike, options: FileUploadOptions): Promise { const { resourceTx, resourceUrl, @@ -123,7 +117,7 @@ class StackService extends NodeService { chunkSize, thumbnailTx, thumbnailUrl - } = await this.postFile(file, progressHook, cancelHook); + } = await this.postFile(file, options); const version = new FileVersion({ owner: await this.wallet.getAddress(), createdAt: JSON.stringify(Date.now()), @@ -138,12 +132,12 @@ class StackService extends NodeService { return version; } - private async postFile(file: FileLike, progressHook?: (progress: number, data?: any) => void, cancelHook?: AbortController) - : Promise<{ resourceTx: string, resourceHash: string, resourceUrl?: string, numberOfChunks?: number, chunkSize?: number, thumbnailTx?: string, thumbnailUrl?: string }> { - const filePromise = this.fileService.create(file, true, progressHook, cancelHook); + private async postFile(file: FileLike, options: FileUploadOptions) + : Promise { + const filePromise = this.fileService.create(file, options); const thumbnail = await createThumbnail(file); if (thumbnail) { - const thumbnailPromise = this.fileService.create(thumbnail, false, progressHook); + const thumbnailPromise = this.fileService.create(thumbnail, { ...options, shouldBundleTransaction: false }); const results = await Promise.all([filePromise, thumbnailPromise]); return { resourceTx: results[0].resourceTx, @@ -160,13 +154,26 @@ class StackService extends NodeService { protected async setVaultContext(vaultId: string): Promise { await super.setVaultContext(vaultId); - this.fileService.setKeys(this.membershipKeys); + this.fileService.setKeys(this.keys); this.fileService.setRawDataEncryptionPublicKey(this.dataEncrypter.publicKey); this.fileService.setVaultId(this.vaultId); this.fileService.setIsPublic(this.isPublic); } }; +export type StackCreateOptions = NodeCreateOptions & FileUploadOptions; + +type StackCreateResult = { + stackId: string, + transactionId: string, + object: Stack +} + +type StackUpdateResult = { + transactionId: string, + object: Stack +} + export { StackService } \ No newline at end of file diff --git a/src/core/vault.ts b/src/core/vault.ts index 45315bfd..f4ded403 100644 --- a/src/core/vault.ts +++ b/src/core/vault.ts @@ -1,38 +1,109 @@ -import { actionRefs, objectType, functions, protocolTags, smartweaveTags } from "../constants"; +import { actionRefs, objectType, status, functions, protocolTags, smartweaveTags } from "../constants"; import { v4 as uuidv4 } from "uuid"; -import { generateKeyPair, arrayToBase64, Encrypter } from "@akord/crypto"; +import { generateKeyPair, Encrypter, EncryptedKeys } from "@akord/crypto"; import { Vault } from "../types/vault"; import { Service, STATE_CONTENT_TYPE } from "./service"; import { Tag } from "../types/contract"; +import { ListOptions, VaultGetOptions } from "../types/query-options"; +import { Paginated } from "../types/paginated"; +import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; class VaultService extends Service { objectType = objectType.VAULT; + defaultListOptions = { + shouldDecrypt: true, + filter: { status: { eq: status.ACCEPTED } } + } as ListOptions; + + defaultGetOptions = { + shouldDecrypt: true, + deep: false + } as VaultGetOptions; + + defaultCreateOptions = { + public: false, + termsOfAccess: undefined + } as VaultCreateOptions; + + /** + * @param {string} vaultId + * @returns Promise with the decrypted vault + */ + public async get(vaultId: string, options: VaultGetOptions = this.defaultGetOptions): Promise { + const getOptions = { + ...this.defaultGetOptions, + ...options + } + const result = await this.api.getVault(vaultId, getOptions); + if (!getOptions.shouldDecrypt || result.public) { + return new Vault(result, []); + } + const { keys } = await this.api.getMembershipKeys(vaultId); + const vault = await this.processVault(result, getOptions.shouldDecrypt, keys); + return vault + } + + /** + * @param {ListOptions} options + * @returns Promise with paginated user vaults + */ + public async list(options: ListOptions = this.defaultListOptions): Promise> { + const listOptions = { + ...this.defaultListOptions, + ...options + } + const response = await this.api.getVaults(listOptions.filter, listOptions.limit, listOptions.nextToken); + const promises = response.items + .map(async (vaultProto: Vault) => { + return await this.processVault(vaultProto, listOptions.shouldDecrypt, vaultProto.keys); + }) as Promise[]; + const { items, errors } = await this.handleListErrors(response.items, promises); + return { + items, + nextToken: response.nextToken, + errors + } + } + + /** + * @param {ListOptions} options + * @returns Promise with currently authenticated user vaults + */ + public async listAll(options: ListOptions = this.defaultListOptions): Promise> { + const list = async (listOptions: ListOptions) => { + return await this.list(listOptions); + } + return await this.paginate(list, options); + } + /** * @param {string} name new vault name - * @param {string} [termsOfAccess] if the vault is intended for professional or legal use, you can add terms of access and they must be digitally signed before accessing the vault - * @param {boolean} [isPublic] + * @param {VaultCreateOptions} options public/private, terms of access, etc. * @returns Promise with new vault id, owner membership id & corresponding transaction id */ - public async create(name: string, termsOfAccess?: string, isPublic?: boolean): Promise<{ - transactionId: string, - vaultId: string, - membershipId: string - }> { + public async create(name: string, options: VaultCreateOptions = this.defaultCreateOptions): Promise { + const createOptions = { + ...this.defaultCreateOptions, + ...options + } const memberDetails = await this.getProfileDetails(); this.setActionRef(actionRefs.VAULT_CREATE); - this.setIsPublic(isPublic); + this.setIsPublic(createOptions.public); - let publicKeys: any, keys: any; + let keys: Array; if (!this.isPublic) { // generate a new vault key pair const keyPair = await generateKeyPair(); this.setRawDataEncryptionPublicKey(keyPair.publicKey); const userPublicKey = this.wallet.publicKeyRaw(); const keysEncrypter = new Encrypter(this.wallet, this.dataEncrypter.keys, userPublicKey); - keys = [await keysEncrypter.encryptMemberKey(keyPair)]; - this.setKeys([{ publicKey: arrayToBase64(keyPair.publicKey), encPrivateKey: keys[0].encPrivateKey }]); - publicKeys = [arrayToBase64(keyPair.publicKey)]; + try { + keys = [await keysEncrypter.encryptMemberKey(keyPair)]; + this.setKeys([{ encPublicKey: keys[0].encPublicKey, encPrivateKey: keys[0].encPrivateKey }]); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } } const vaultId = await this.api.initContractId([new Tag(protocolTags.NODE_TYPE, objectType.VAULT)]); @@ -46,12 +117,12 @@ class VaultService extends Service { this.tags = [ new Tag(protocolTags.MEMBER_ADDRESS, address), new Tag(protocolTags.MEMBERSHIP_ID, membershipId), - new Tag(protocolTags.PUBLIC, isPublic ? "true" : "false"), + new Tag(protocolTags.PUBLIC, createOptions.public ? "true" : "false"), ].concat(await this.getTags()); const vaultData = { name: await this.processWriteString(name), - termsOfAccess + termsOfAccess: createOptions.termsOfAccess } const vaultSignature = await this.signData(vaultData); const membershipData = { @@ -60,7 +131,7 @@ class VaultService extends Service { memberDetails: await this.processMemberDetails(memberDetails, true) } const membershipSignature = await this.signData(membershipData); - const ids = await this.api.uploadData([ + const dataTxIds = await this.api.uploadData([ { data: vaultData, tags: [ new Tag("Data-Type", "State"), @@ -81,23 +152,17 @@ class VaultService extends Service { new Tag(protocolTags.NODE_TYPE, objectType.MEMBERSHIP), new Tag(protocolTags.MEMBERSHIP_ID, membershipId) ] - }], true); - const metadata = { - dataRefs: [ - { ...ids[0], modelId: this.vaultId, modelType: objectType.VAULT, data: vaultData }, - { ...ids[1], modelId: membershipId, modelType: objectType.MEMBERSHIP, data: membershipData } - ], - publicKeys - } - const data = { vault: ids[0].id, membership: ids[1].id }; + }]); + + const data = { vault: dataTxIds[0], membership: dataTxIds[1] }; - const txId = await this.api.postContractTransaction( + const { id, object } = await this.api.postContractTransaction( this.vaultId, { function: this.function, data }, - this.tags, - metadata + this.tags ); - return { vaultId, membershipId, transactionId: txId } + const vault = await this.processVault(object, true, this.keys); + return { vaultId, membershipId, transactionId: id, object: vault }; } /** @@ -105,32 +170,57 @@ class VaultService extends Service { * @param name new vault name * @returns Promise with corresponding transaction id */ - public async rename(vaultId: string, name: string): Promise<{ transactionId: string }> { + public async rename(vaultId: string, name: string): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.VAULT_RENAME); - return this.nodeRename(name); + this.setFunction(functions.VAULT_UPDATE); + const body = { + name: await this.processWriteString(name) + }; + const data = await this.mergeAndUploadBody(body); + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function, data }, + await this.getTags() + ); + const vault = await this.processVault(object, true, this.keys); + return { transactionId: id, object: vault }; } /** * @param {string} vaultId * @returns Promise with corresponding transaction id */ - public async archive(vaultId: string): Promise<{ transactionId: string }> { + public async archive(vaultId: string): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.VAULT_ARCHIVE); this.setFunction(functions.VAULT_ARCHIVE); - return this.nodeUpdate(); + + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function }, + await this.getTags() + ); + const vault = await this.processVault(object, true, this.keys); + return { transactionId: id, object: vault }; } /** * @param {string} vaultId * @returns Promise with corresponding transaction id */ - public async restore(vaultId: string): Promise<{ transactionId: string }> { + public async restore(vaultId: string): Promise { await this.setVaultContext(vaultId); this.setActionRef(actionRefs.VAULT_RESTORE); this.setFunction(functions.VAULT_RESTORE); - return this.nodeUpdate(); + + const { id, object } = await this.api.postContractTransaction( + this.vaultId, + { function: this.function }, + await this.getTags() + ); + const vault = await this.processVault(object, true, this.keys); + return { transactionId: id, object: vault }; } /** @@ -142,44 +232,42 @@ class VaultService extends Service { return { transactionId: "" }; } - /** - * @param {string} vaultId - * @returns Promise with the decrypted vault - */ - public async get(vaultId: string, shouldDecrypt = true): Promise { - const result = await this.api.getVault(vaultId); - if (!shouldDecrypt || result.public) { - return new Vault(result, []); - } - const { keys } = await this.api.getMembershipKeys(vaultId); - const vault = new Vault(result, keys); - await vault.decrypt(); - return vault + public async setVaultContext(vaultId: string): Promise { + await super.setVaultContext(vaultId); + this.setObjectId(vaultId); + this.setObject(this.vault); } - /** - * @returns Promise with currently authenticated user vaults - */ - public async list(shouldDecrypt = true): Promise> { - const results = await this.api.getVaults(); - const vaults = []; - for (let result of results) { - const vault = new Vault(result, result.keys); - if (shouldDecrypt && !vault.public) { + protected async processVault(object: Vault, shouldDecrypt: boolean, keys?: EncryptedKeys[]): Promise { + const vault = new Vault(object, keys); + if (shouldDecrypt && !vault.public) { + try { await vault.decrypt(); + } catch (error) { + throw new IncorrectEncryptionKey(error); } - vaults.push(vault); } - return vaults; - } - - public async setVaultContext(vaultId: string): Promise { - await super.setVaultContext(vaultId); - this.setObjectId(vaultId); - this.setObject(this.vault); + return vault; } }; +export type VaultCreateOptions = { + public?: boolean, + termsOfAccess?: string // if the vault is intended for professional or legal use, you can add terms of access and they must be digitally signed before accessing the vault +} + +type VaultCreateResult = { + vaultId: string, + membershipId: string, + transactionId: string, + object: Vault +} + +type VaultUpdateResult = { + transactionId: string, + object: Vault +} + export { VaultService } \ No newline at end of file diff --git a/src/errors/bad-request.ts b/src/errors/bad-request.ts new file mode 100644 index 00000000..468cab6f --- /dev/null +++ b/src/errors/bad-request.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class BadRequest extends Error { + statusCode: number = 400; + + constructor(message: string, error?: any) { + super(message); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/errors/error.ts b/src/errors/error.ts new file mode 100644 index 00000000..368f941a --- /dev/null +++ b/src/errors/error.ts @@ -0,0 +1,20 @@ +import { BadRequest } from "./bad-request"; +import { Forbidden } from "./forbidden"; +import { InternalError } from "./internal-error"; +import { NotFound } from "./not-found"; +import { Unauthorized } from "./unauthorized"; + +export const throwError = (status: number, message?: string, error?: any) => { + switch (status) { + case 400: + throw new BadRequest(message, error); + case 401: + throw new Unauthorized(message, error); + case 403: + throw new Forbidden(message, error); + case 404: + throw new NotFound(message, error); + default: + throw new InternalError("Internal error. Please try again or contact Akord support.", error); + } +} \ No newline at end of file diff --git a/src/errors/forbidden.ts b/src/errors/forbidden.ts new file mode 100644 index 00000000..12d1ca2d --- /dev/null +++ b/src/errors/forbidden.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class Forbidden extends Error { + statusCode: number = 403; + + constructor(message: string, error?: any) { + super(message); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/errors/incorrect-encryption-key.ts b/src/errors/incorrect-encryption-key.ts new file mode 100644 index 00000000..54324eca --- /dev/null +++ b/src/errors/incorrect-encryption-key.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class IncorrectEncryptionKey extends Error { + statusCode: number = 409; + + constructor(error?: any) { + super("Incorrect encryption key."); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/errors/internal-error.ts b/src/errors/internal-error.ts new file mode 100644 index 00000000..d2312b23 --- /dev/null +++ b/src/errors/internal-error.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class InternalError extends Error { + statusCode: number = 500; + + constructor(message: string, error?: any) { + super(message); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/errors/not-found.ts b/src/errors/not-found.ts new file mode 100644 index 00000000..8f035685 --- /dev/null +++ b/src/errors/not-found.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class NotFound extends Error { + statusCode: number = 404; + + constructor(message: string, error?: any) { + super(message); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/errors/too-many-requests.ts b/src/errors/too-many-requests.ts new file mode 100644 index 00000000..3e88f033 --- /dev/null +++ b/src/errors/too-many-requests.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class TooManyRequests extends Error { + statusCode: number = 429; + + constructor(message: string, error?: any) { + super(message); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/errors/unauthorized.ts b/src/errors/unauthorized.ts new file mode 100644 index 00000000..9c6b39fd --- /dev/null +++ b/src/errors/unauthorized.ts @@ -0,0 +1,10 @@ +import { Logger } from "../logger"; + +export class Unauthorized extends Error { + statusCode: number = 401; + + constructor(message: string, error?: any) { + super(message); + Logger.log(error); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 4873f186..ea1c73a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,16 @@ import { Akord } from "./akord"; import { ClientConfig } from "./config"; import { Wallet } from "@akord/crypto"; +import { Auth } from "@akord/akord-auth"; /** * @param {Wallet} wallet - * @param {string} [jwtToken] * @param {ClientConfig={}} config * @returns Promise with Akord Client instance */ -Akord.init = async function (wallet: Wallet, jwtToken?: string, config: ClientConfig = {}): Promise { - return new Akord(wallet, jwtToken, config); +Akord.init = async function (wallet: Wallet, config: ClientConfig = {}): Promise { + Auth.configure(config); + return new Akord(wallet, config); }; -export { Akord }; +export { Akord, Auth }; diff --git a/src/types/batch-response.ts b/src/types/batch-response.ts index b244aa64..fb6ce0a0 100644 --- a/src/types/batch-response.ts +++ b/src/types/batch-response.ts @@ -1,5 +1,7 @@ +import { Stack } from "./node" + export interface BatchStackCreateResponse { - data: Array<{ stackId: string, transactionId: string }> + data: Array<{ stackId: string, transactionId: string, object: Stack }> errors: Array<{ name: string, message: string }> cancelled: number } diff --git a/src/types/contract.ts b/src/types/contract.ts index a8e753bc..f29edc13 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -46,8 +46,13 @@ export type Tags = Tag[]; export interface ContractInput { function: functions, - data?: any, + data?: DataInput, parentId?: string, address?: string, role?: string // type -} \ No newline at end of file +} + +export type DataInput = + { vault: string, membership: string } // vault:init + | { id: string, value: string }[] // membership:revoke + | string // all other transactions \ No newline at end of file diff --git a/src/types/file.ts b/src/types/file.ts index d27508c4..737282f1 100644 --- a/src/types/file.ts +++ b/src/types/file.ts @@ -1,9 +1,8 @@ -import fs from "fs"; -import path from "path"; import { Blob } from "buffer"; import * as mime from "mime-types"; import { BinaryLike } from "crypto"; import { Readable } from "stream"; +import { NotFound } from "../errors/not-found"; export type FileLike = NodeJs.File | File @@ -24,9 +23,11 @@ export namespace NodeJs { return new File(chunks, name, mimeType, lastModified); } - static fromPath(filePath: string) { + static async fromPath(filePath: string) { + const fs = (await import("fs")).default; + const path = (await import("path")).default; if (!fs.existsSync(filePath)) { - throw new Error("Could not find a file in your filesystem: " + filePath); + throw new NotFound("Could not find a file in your filesystem: " + filePath); } const stats = fs.statSync(filePath); const name = path.basename(filePath); diff --git a/src/types/list-options.ts b/src/types/list-options.ts deleted file mode 100644 index 3ab78ebc..00000000 --- a/src/types/list-options.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type ListOptions = { - shouldDecrypt: boolean, - filter: Object, - limit?: number, // the limit of the number of items in a query (default to 100) - nextToken?: string -} diff --git a/src/types/membership.ts b/src/types/membership.ts index 23de9872..9320ba77 100644 --- a/src/types/membership.ts +++ b/src/types/membership.ts @@ -14,6 +14,8 @@ export class Membership extends Encryptable { role: RoleType; data?: string[]; encPublicSigningKey: string; + email: string; + memberPublicSigningKey: string; memberDetails: ProfileDetails; vaultId: string; // remove when on warp @@ -23,20 +25,18 @@ export class Membership extends Encryptable { super(keys, null) this.id = membershipProto.id; this.owner = membershipProto.owner; + this.address = membershipProto.address; this.createdAt = membershipProto.createdAt; this.updatedAt = membershipProto.updatedAt; this.data = membershipProto.data; this.status = membershipProto.status; this.role = membershipProto.role; this.encPublicSigningKey = membershipProto.encPublicSigningKey; + this.email = membershipProto.email; + this.memberPublicSigningKey = membershipProto.memberPublicSigningKey; this.vaultId = membershipProto.vaultId; this.keys = keys; - this.memberDetails = new ProfileDetails( - membershipProto.memberDetails?.name, - membershipProto.memberDetails?.publicSigningKey, - membershipProto.memberDetails?.email, - membershipProto.memberDetails?.avatarUri, - keys, null); + this.memberDetails = new ProfileDetails(membershipProto.memberDetails, keys); } } diff --git a/src/types/node.ts b/src/types/node.ts index 65b919c0..5ea5fbec 100644 --- a/src/types/node.ts +++ b/src/types/node.ts @@ -1,5 +1,6 @@ import { Encryptable, encrypted, EncryptedKeys } from "@akord/crypto"; import { status } from "../constants"; +import { NotFound } from "../errors/not-found"; export enum nodeType { STACK = "Stack", @@ -38,7 +39,7 @@ export abstract class Node extends Encryptable { if (this.versions && this.versions[index]) { return this.versions[index]; } else { - throw new Error("A version with given index: " + index + " does not exist for node: " + this.id); + throw new NotFound("A version with given index: " + index + " does not exist for node: " + this.id); } } else { return this.versions && this.versions[this.versions.length - 1]; diff --git a/src/types/paginated.ts b/src/types/paginated.ts index 6a85c4bc..406bb29c 100644 --- a/src/types/paginated.ts +++ b/src/types/paginated.ts @@ -1,12 +1,17 @@ import { AxiosResponse } from "axios" +const PAGINATION_HEADER = 'next-page' + export type Paginated = { items: Array nextToken: string + errors?: Array<{ id: string, error: string }> } export const isPaginated = (response: AxiosResponse) => { return response.headers[PAGINATION_HEADER] !== undefined } -export const PAGINATION_HEADER = 'next-token' +export const nextToken = (response: AxiosResponse) => { + return response.headers[PAGINATION_HEADER] === "null" ? "" : response.headers[PAGINATION_HEADER] +} diff --git a/src/types/profile-details.ts b/src/types/profile-details.ts index fa8c7769..a9437955 100644 --- a/src/types/profile-details.ts +++ b/src/types/profile-details.ts @@ -2,20 +2,13 @@ import { Encryptable, encrypted, EncryptedKeys } from "@akord/crypto"; export class ProfileDetails extends Encryptable { - constructor(name: string, publicSigningKey: string, email: string, avatarUri: Array, keys?: Array, publicKey?: string) { + constructor(profileDetailsProto: any, keys?: Array, publicKey?: string) { super(keys, publicKey); - this.name = name; - this.publicSigningKey = publicSigningKey; - this.email = email; - this.avatarUri = avatarUri; + this.name = profileDetailsProto.name; + this.avatarUri = profileDetailsProto.avatarUri; } - @encrypted() name?: string; - publicSigningKey: string; - email: string; - phone?: string; - avatarUri?: string[]; - avatarUrl?: string; - avatarTx?: string; - avatar?: ArrayBuffer; - } + @encrypted() name?: string; + avatarUri?: string[]; + avatar?: ArrayBuffer; +} diff --git a/src/types/query-options.ts b/src/types/query-options.ts new file mode 100644 index 00000000..cf92b2e5 --- /dev/null +++ b/src/types/query-options.ts @@ -0,0 +1,21 @@ +export type ListOptions = { + shouldDecrypt?: boolean, + filter?: Object, + limit?: number, // the limit of the number of items in a query (default to 100) + nextToken?: string + parentId?: string +} + +export type GetOptions = { + shouldDecrypt?: boolean, + vaultId?: string +} + +export type VaultGetOptions = GetOptions & { + deep?: boolean +} + +export type VaultApiGetOptions = { + withNodes?: boolean, + deep?: boolean, // withMemberships, withMemos, withStacks, withFolders +} \ No newline at end of file diff --git a/src/types/transaction.ts b/src/types/transaction.ts new file mode 100644 index 00000000..e0d677d8 --- /dev/null +++ b/src/types/transaction.ts @@ -0,0 +1,15 @@ +import { actionRefs, functions, objectType } from "../constants" + +export interface Transaction { + id: string, + function: functions, + postedAt: string, + address: string, + publicSigningKey: string, + vaultId: string, + actionRef: actionRefs, + groupRef: string, + objectId: string, + objectType: objectType, + status: string +} \ No newline at end of file diff --git a/src/types/user.ts b/src/types/user.ts new file mode 100644 index 00000000..695ce05c --- /dev/null +++ b/src/types/user.ts @@ -0,0 +1,15 @@ +export type User = { + address: string, + publicSigningKey: string, + publicKey: string, + email: string, + name?: string, + avatarUri?: string[], + avatar?: ArrayBuffer +} + +export type UserPublicInfo = { + address: string, + publicSigningKey: string, + publicKey: string +} \ No newline at end of file diff --git a/src/types/vault.ts b/src/types/vault.ts index e198e9f6..c8fe995e 100644 --- a/src/types/vault.ts +++ b/src/types/vault.ts @@ -1,4 +1,6 @@ import { Encryptable, encrypted, EncryptedKeys } from "@akord/crypto"; +import { Membership } from "./membership"; +import { Folder, Memo, Stack } from "./node"; export class Vault extends Encryptable { id: string; @@ -10,6 +12,11 @@ export class Vault extends Encryptable { size?: number; @encrypted() name: string; + memberships?: Array; + memos?: Array; + stacks?: Array; + folders?: Array; + constructor(vaultProto: any, keys: Array) { super(keys, null); this.id = vaultProto.id; @@ -21,5 +28,9 @@ export class Vault extends Encryptable { this.status = vaultProto.status; this.data = vaultProto.data; this.keys = keys; + this.memberships = vaultProto?.memberships?.map((membership: Membership) => new Membership(membership, keys)); + this.memos = vaultProto?.memos?.map((memo: Memo) => new Memo(memo, keys)); + this.stacks = vaultProto?.stacks?.map((stack: Stack) => new Stack(stack, keys)); + this.folders = vaultProto?.folders?.map((folder: Folder) => new Folder(folder, keys)); } } diff --git a/yarn.lock b/yarn.lock index 66ca8230..4405f441 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@akord/akord-auth@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@akord/akord-auth/-/akord-auth-0.25.0.tgz#54d1e322a65a3483150b643a8315bb6c70eb5656" + integrity sha512-T5hj7THoQPKeTT/+MASTqqmZzV5GF+ohiVPL3bDXEAeVMzYt2SvVOQMm2GFB0EAq1KmEWTCXKb0gdu+lyJAloA== + dependencies: + "@akord/crypto" "0.10.0" + amazon-cognito-identity-js "6.1.2" + "@akord/browser-level@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@akord/browser-level/-/browser-level-1.0.2.tgz#5d5b580cc1de81b0195c68d2197bf1cae47159ab" @@ -12,10 +20,23 @@ module-error "^1.0.2" run-parallel-limit "^1.1.0" -"@akord/crypto@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@akord/crypto/-/crypto-0.10.1.tgz#b9bf8f739ad216ce5d0c6cad4885d87569714c34" - integrity sha512-HYKkOh7M9q2wcrDqQByeY7e0oiOUKiS4AFAadKkyAM02C3TyMZDk6OU85Kuz9wodpY8vWGq1wq461t9TNcmIyg== +"@akord/crypto@0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@akord/crypto/-/crypto-0.10.0.tgz#10b1efba51aa0a04139c8a59186513b653c228fb" + integrity sha512-1C1djkZWIeT7QIAx6LYyPFi+VHMJHWS+MfTpkNL44V8ncAcgHuVDUhlvN7NLLDAryhGWPh91RVBUoGhR0zz9Qw== + dependencies: + "@akord/browser-level" "^1.0.2" + bip39 "^3.1.0" + ethereum-cryptography "^1.2.0" + libsodium-wrappers "^0.7.11" + memory-level "^1.0.0" + reflect-metadata "^0.1.13" + tweetnacl "^1.0.3" + +"@akord/crypto@0.10.6": + version "0.10.6" + resolved "https://registry.yarnpkg.com/@akord/crypto/-/crypto-0.10.6.tgz#a331cadec9bee6b5263c61f75e3c74a88a87ba85" + integrity sha512-gmySRiefue+jIyJOM/PRPUtXPkusBwvEJh+I3HTXZU6n1ulZQC7fyQeNHb4XltL+H3EanLCGrhMN9j4EatEeVQ== dependencies: "@akord/browser-level" "^1.0.2" bip39 "^3.1.0" @@ -32,135 +53,112 @@ dependencies: rxjs "^7.5.6" -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@aws-crypto/sha256-js@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz#02acd1a1fda92896fc5a28ec7c6e164644ea32fc" + integrity sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g== + dependencies: + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/util@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-1.2.2.tgz#b28f7897730eb6538b21c18bd4de22d0ea09003c" + integrity sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg== + dependencies: + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/types@^3.1.0": + version "3.310.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.310.0.tgz#b83a0580feb38b58417abb8b4ed3eae1a0cb7bc1" + integrity sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A== + dependencies: + tslib "^2.5.0" + +"@aws-sdk/util-utf8-browser@^3.0.0": + version "3.259.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" + integrity sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw== + dependencies: + tslib "^2.3.1" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== +"@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== "@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.20.7", "@babel/generator@^7.7.2": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== +"@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.4" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== +"@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" - integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.2.1" - -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== - dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== +"@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -169,24 +167,17 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== - dependencies: - "@babel/types" "^7.20.7" - "@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== +"@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" @@ -194,43 +185,14 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" - "@babel/helper-simple-access@^7.20.2": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" @@ -238,13 +200,6 @@ dependencies: "@babel/types" "^7.20.2" -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -262,29 +217,19 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/helpers@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== dependencies: "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" "@babel/highlight@^7.18.6": version "7.18.6" @@ -295,155 +240,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" - -"@babel/plugin-proposal-async-generator-functions@^7.20.1": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz#92592e9029b13b15be0f7ce6a7aedc2879ca45a7" - integrity sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz#309c7668f2263f1c711aa399b5a9a6291eef6135" - integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -459,41 +259,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== - dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -508,7 +280,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -522,7 +294,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -550,14 +322,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== @@ -565,368 +330,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== - dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - -"@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-block-scoping@^7.20.2": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz#3e1b2aa9cbbe1eb8d644c823141a9c5c2a22392d" - integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-classes@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" - -"@babel/plugin-transform-destructuring@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-for-of@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== - dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-modules-amd@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== - dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== - dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" - -"@babel/plugin-transform-modules-systemjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== - dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-identifier" "^7.19.1" - -"@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" - -"@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - regenerator-transform "^0.15.1" - -"@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-spread@^7.19.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - -"@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/preset-env@^7.12.1": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" - integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== - dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.20.1" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.2" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.20.0" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" - "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.20.2" - "@babel/plugin-transform-classes" "^7.20.2" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.20.2" - "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.9" - "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" - "@babel/plugin-transform-function-name" "^7.18.9" - "@babel/plugin-transform-literals" "^7.18.9" - "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.19.6" - "@babel/plugin-transform-modules-commonjs" "^7.19.6" - "@babel/plugin-transform-modules-systemjs" "^7.19.6" - "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" - "@babel/plugin-transform-new-target" "^7.18.6" - "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.20.1" - "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" - "@babel/plugin-transform-reserved-words" "^7.18.6" - "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.19.0" - "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.9" - "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" - "@babel/plugin-transform-unicode-regex" "^7.18.6" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.20.2" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - core-js-compat "^3.25.1" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - -"@babel/runtime@^7.8.4": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -935,26 +345,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== +"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -1179,18 +589,10 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1201,29 +603,39 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@noble/hashes@1.2.0", "@noble/hashes@^1.2.0", "@noble/hashes@~1.2.0": +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/hashes@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -1304,9 +716,9 @@ "@babel/types" "^7.3.0" "@types/blueimp-load-image@^5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@types/blueimp-load-image/-/blueimp-load-image-5.16.0.tgz#269bb2861154f7b615f2ba05a7270a99efefda97" - integrity sha512-Q2t0/xly180IV7Ybu5pGk9MC/lrnqPZtplLh3R5T0i2tnE8Nt20gtjkLtliCD/gAlQHHScrjmP607ee++FAgpg== + version "5.16.1" + resolved "https://registry.yarnpkg.com/@types/blueimp-load-image/-/blueimp-load-image-5.16.1.tgz#4b979aac1381b611362661dcc58a8dea039a7401" + integrity sha512-Q0/BDficZLambV8YjarxJt9lwhUFNWEqv7Kzb3IQeZgtpP4IDJI0TwBl4zgi3dFd/eMXxGI1XXvdnNdxhmq9gQ== "@types/graceful-fs@^4.1.3": version "4.1.6" @@ -1348,9 +760,9 @@ integrity sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw== "@types/node@*": - version "18.13.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850" - integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/node@^17.0.8": version "17.0.45" @@ -1383,9 +795,9 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" @@ -1402,13 +814,13 @@ abstract-level@^1.0.0, abstract-level@^1.0.2: module-error "^1.0.1" queue-microtask "^1.2.3" -amazon-cognito-identity-js@^5.2.8: - version "5.2.14" - resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-5.2.14.tgz#780d633e2912971e77b00d60d5b959f26d66e6a5" - integrity sha512-9LMgLZfbypbbGTpARQ+QqglE09b1MWti11NXhcD/wPom0uhU/L90dfmUOpTwknz//eE6/dGYf004mJucWzrfxQ== +amazon-cognito-identity-js@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.1.2.tgz#975df21b0590098c2d3f455f48dbba255560bbf5" + integrity sha512-Ptutf9SLvKEM1Kr2kTPUvu/9THjQ0Si1l80iZYcS8NqScAAiDg8WjOOhQeJPcQDXt3Vym91luZ6zNW/3ErjEdQ== dependencies: + "@aws-crypto/sha256-js" "1.2.2" buffer "4.9.2" - crypto-js "^4.1.1" fast-base64-decode "^1.0.0" isomorphic-unfetch "^3.0.0" js-cookie "^2.2.1" @@ -1500,30 +912,6 @@ babel-plugin-jest-hoist@^28.1.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== - dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" - -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -1587,7 +975,7 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.21.3, browserslist@^4.21.5: +browserslist@^4.21.3: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== @@ -1649,9 +1037,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001453" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001453.tgz#6d3a1501622bf424a3cee5ad9550e640b0de3de8" - integrity sha512-R9o/uySW38VViaTrOtwfbFEiBFUh7ST3uIG4OEymIG3/uKdHDO4xk/FaqfUw0d+irSUyFPy3dZszf9VvSTPnsA== + version "1.0.30001478" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" + integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== catering@^2.1.0, catering@^2.1.1: version "2.1.1" @@ -1743,20 +1131,6 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== -core-js-compat@^3.25.1: - version "3.28.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.28.0.tgz#c08456d854608a7264530a2afa281fadf20ecee6" - integrity sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg== - dependencies: - browserslist "^4.21.5" - -cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1766,11 +1140,6 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-js@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" - integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== - debug@^4.1.0, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -1784,9 +1153,9 @@ dedent@^0.7.0: integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== deepmerge@^4.2.2: - version "4.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" - integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== detect-newline@^3.0.0: version "3.1.0" @@ -1814,9 +1183,9 @@ dotenv@^16.0.2: integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== electron-to-chromium@^1.4.284: - version "1.4.299" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.299.tgz#faa2069cd4879a73e540e533178db5c618768d41" - integrity sha512-lQ7ijJghH6pCGbfWXr6EY+KYCMaRSjgsY925r1p/TlpSfVM1VjHTcn1gAc15VM4uwti283X6QtjPTXdpoSGiZQ== + version "1.4.359" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz#5c4d13cb08032469fcd6bd36457915caa211356b" + integrity sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw== emittery@^0.10.2: version "0.10.2" @@ -1855,11 +1224,6 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - ethereum-cryptography@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" @@ -1996,9 +1360,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-flag@^3.0.0: version "3.0.0" @@ -2068,10 +1432,10 @@ is-buffer@^2.0.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== dependencies: has "^1.0.3" @@ -2561,11 +1925,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -2623,11 +1982,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -2732,13 +2086,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - node-fetch@^2.6.1: version "2.6.9" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" @@ -2915,49 +2262,6 @@ reflect-metadata@^0.1.13: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== - dependencies: - "@babel/runtime" "^7.8.4" - -regexpu-core@^5.2.1: - version "5.3.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.0.tgz#4d0d044b76fedbad6238703ae84bfdedee2cf074" - integrity sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ== - dependencies: - "@babel/regjsgen" "^0.8.0" - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -2980,12 +2284,12 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== -resolve@^1.14.2, resolve@^1.20.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.20.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -3011,13 +2315,13 @@ rxjs@^7.5.6: tslib "^2.1.0" semver@7.x, semver@^7.3.5: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + version "7.4.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" + integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -3205,7 +2509,12 @@ ts-jest@^28.0.5: semver "7.x" yargs-parser "^21.0.1" -tslib@^2.1.0: +tslib@^1.11.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.1.0, tslib@^2.3.1, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== @@ -3235,29 +2544,6 @@ unfetch@^4.2.0: resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - update-browserslist-db@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" @@ -3355,9 +2641,9 @@ yargs-parser@^21.0.1, yargs-parser@^21.1.1: integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^17.3.1: - version "17.6.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" - integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== dependencies: cliui "^8.0.1" escalade "^3.1.1"