Repository: buttercup/buttercup-core
Pull request #320 (2023-01-28 18:31:55)

ESM
Entire library converted to ESM.


Patch:
diff --git a/README.md b/README.md
index a669773..4f422b5 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,9 @@ In a _web_ environment, use the following:
 import { Vault } from "buttercup/web";
 ```
 
-_Buttercup core supports Node version 12 and up. Most features may work on Node 10, but it is not officially supported._
+Buttercup core is ESM - you must either import it in a project that is also ESM, or use a build tool like Webpack.
+
+_Buttercup core supports Node version 14 and up. For older Node versions use v6 and earlier._
 
 ## Usage
 
@@ -146,6 +148,10 @@ fileDatasource
 
 Using just a datasource is not recommended as saving and loading is quite low-level and cumbersome. Check the [browser extension](https://github.com/buttercup/buttercup-browser-extension) or [desktop application](https://github.com/buttercup/buttercup-desktop) for examples of how to use the `VaultManager` and other helpful classes.
 
+### Development
+
+To develop Buttercup Core, clone the repository on a Linux or Mac OS machine. Ensure that you're running NodeJS version 16/18. Install all dependencies by running `npm install` in the project directory. Run the tests to ensure everything is functioning correctly: `npm test`.
+
 ## Vault Formats
 
 Buttercup currently supports [2 concurrent vault formats](VAULT_FORMAT.md), as it is in the process of transitioning from **Format A** (legacy) to **Format B**. You can switch the operational format by doing the following:
diff --git a/karma.conf.js b/karma.conf.cjs
similarity index 92%
rename from karma.conf.js
rename to karma.conf.cjs
index 48cc018..f83a732 100644
--- a/karma.conf.js
+++ b/karma.conf.cjs
@@ -1,4 +1,4 @@
-const webpackConfig = require("./webpack.config.js");
+const webpackConfig = require("./webpack.config.cjs");
 
 const CI =  !!process.env.CI;
 
@@ -38,7 +38,7 @@ module.exports = config => config.set({
 
     basePath: __dirname,
 
-    browsers: CI ? ["ChromeHeadless", "FirefoxHeadless"] : ["FirefoxHeadless"],
+    browsers: CI ? ["ChromeHeadless", "FirefoxHeadless"] : ["ChromeHeadless"],
 
     captureTimeout: 60000,
 
@@ -63,6 +63,8 @@ module.exports = config => config.set({
 
     frameworks: ["mocha", "sinon", "webpack"],
 
+    // logLevel: config.LOG_DEBUG,
+
     plugins: [
         require("karma-webpack"),
         require("karma-chrome-launcher"),
diff --git a/package-lock.json b/package-lock.json
index 8fd5dc1..08f0118 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,10 +10,10 @@
       "license": "MIT",
       "dependencies": {
         "@buttercup/channel-queue": "^1.2.0",
-        "@buttercup/dropbox-client": "^1.1.3",
+        "@buttercup/dropbox-client": "^2.0.0",
         "@buttercup/googledrive-client": "^1.3.3",
         "cowl": "^1.1.0",
-        "crypto-random-string": "^3.3.1",
+        "crypto-random-string": "^5.0.0",
         "eventemitter3": "^4.0.7",
         "fast-levenshtein": "^3.0.0",
         "foreachasync": "^5.1.3",
@@ -26,10 +26,10 @@
         "layerr": "^0.1.2",
         "pako": "^1.0.11",
         "path-posix": "^1.0.0",
-        "pify": "^5.0.0",
+        "pify": "^6.1.0",
         "url-join": "^4.0.1",
-        "uuid": "^8.3.2",
-        "webdav": "^4.11.2"
+        "uuid": "^9.0.0",
+        "webdav": "^5.0.0-r1"
       },
       "devDependencies": {
         "@babel/cli": "^7.20.7",
@@ -61,6 +61,7 @@
         "nyc": "^15.1.0",
         "prettier": "^1.19.1",
         "prettier-check": "^2.0.0",
+        "resolve-typescript-plugin": "^2.0.0",
         "rimraf": "^3.0.2",
         "sinon": "^15.0.1",
         "sleep-promise": "^9.1.0",
@@ -72,7 +73,7 @@
         "webpack-cli": "^5.0.1"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=14"
       }
     },
     "node_modules/@ampproject/remapping": {
@@ -1747,14 +1748,26 @@
       }
     },
     "node_modules/@buttercup/dropbox-client": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/@buttercup/dropbox-client/-/dropbox-client-1.1.3.tgz",
-      "integrity": "sha512-3IPc+4TEIoQaGOl9vFIM4cWo3Qbc60HK/2h7oiflpLb5FOMb9kjvpi7T5bJDFaYzWhsDQphRObJyNGj8Xz6OVA==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@buttercup/dropbox-client/-/dropbox-client-2.0.0.tgz",
+      "integrity": "sha512-G08Eqh03jAAu9ZmkhnUcYCmw9OmP4qGBwUJwzgyA7f8mrh2DQr2dhJNbq2JymQvVEy2Lmmo8/O8HXXFSfRXhIQ==",
       "dependencies": {
-        "cowl": "^1.1.0",
-        "hot-patcher": "^0.5.0"
+        "cross-fetch": "^3.1.5",
+        "hot-patcher": "^2.0.0",
+        "layerr": "^1.0.0",
+        "url-parse": "^1.5.10"
       }
     },
+    "node_modules/@buttercup/dropbox-client/node_modules/hot-patcher": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.0.tgz",
+      "integrity": "sha512-rwJ0ZqSFgm+5oD0KiGBVinyPWRJESRSsHlEWDzZjyOe/OfhD9tynHqUyUIGX2fWuV+BihW4nXxeoZRJVHid64w=="
+    },
+    "node_modules/@buttercup/dropbox-client/node_modules/layerr": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/layerr/-/layerr-1.0.0.tgz",
+      "integrity": "sha512-PlgghXyHzISC50rHl4alYWKp91wFSCdIHa/qK+OZ4SgZAsRlHX1AWJIalvS0v5Xl6/mvPe1ZSIBkhiWQ5CNR2w=="
+    },
     "node_modules/@buttercup/googledrive-client": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/@buttercup/googledrive-client/-/googledrive-client-1.3.3.tgz",
@@ -2543,15 +2556,6 @@
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
       "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
-    "node_modules/axios": {
-      "version": "0.27.2",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
-      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
-      "dependencies": {
-        "follow-redirects": "^1.14.9",
-        "form-data": "^4.0.0"
-      }
-    },
     "node_modules/babel-loader": {
       "version": "9.1.2",
       "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz",
@@ -3688,6 +3692,14 @@
         "sha.js": "^2.4.8"
       }
     },
+    "node_modules/cross-fetch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+      "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+      "dependencies": {
+        "node-fetch": "2.6.7"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -3713,14 +3725,28 @@
       }
     },
     "node_modules/crypto-random-string": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.3.1.tgz",
-      "integrity": "sha512-5j88ECEn6h17UePrLi6pn1JcLtAiANa3KExyr9y9Z5vo2mv56Gh3I4Aja/B9P9uyMwyxNHAHWv+nE72f30T5Dg==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-5.0.0.tgz",
+      "integrity": "sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==",
       "dependencies": {
-        "type-fest": "^0.8.1"
+        "type-fest": "^2.12.2"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/crypto-random-string/node_modules/type-fest": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+      "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/custom-event": {
@@ -4555,6 +4581,7 @@
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
       "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+      "dev": true,
       "funding": [
         {
           "type": "individual",
@@ -7003,6 +7030,25 @@
         "path-to-regexp": "^1.7.0"
       }
     },
+    "node_modules/node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/node-preload": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
@@ -7766,11 +7812,11 @@
       }
     },
     "node_modules/pify": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
-      "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-6.1.0.tgz",
+      "integrity": "sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw==",
       "engines": {
-        "node": ">=10"
+        "node": ">=14.16"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -8317,6 +8363,27 @@
         "node": ">=4"
       }
     },
+    "node_modules/resolve-typescript-plugin": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-typescript-plugin/-/resolve-typescript-plugin-2.0.0.tgz",
+      "integrity": "sha512-bkQMP9BVupQfZM9N6SS/QOy6PZh4dL4/d9LBhC+fmb5CdE6geGcm2fZUfXii2xXqwmCOu4xp2QTAnZhIBwGadQ==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "2.4.0"
+      },
+      "engines": {
+        "node": "^14 || ^16 || >=18"
+      },
+      "peerDependencies": {
+        "webpack": "^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/resolve-typescript-plugin/node_modules/tslib": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+      "dev": true
+    },
     "node_modules/restore-cursor": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -9407,6 +9474,11 @@
         "node": "*"
       }
     },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+    },
     "node_modules/tree-kill": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -9772,9 +9844,9 @@
       }
     },
     "node_modules/uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+      "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
       "bin": {
         "uuid": "dist/bin/uuid"
       }
@@ -9830,16 +9902,16 @@
       }
     },
     "node_modules/webdav": {
-      "version": "4.11.2",
-      "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.11.2.tgz",
-      "integrity": "sha512-Ht9TPD5EB7gYW0YmhRcE5NW0/dn/HQfyLSPQY1Rw1coQ5MQTUooAQ9Bpqt4EU7QLw0b95tX4cU59R+SIojs9KQ==",
+      "version": "5.0.0-r1",
+      "resolved": "https://registry.npmjs.org/webdav/-/webdav-5.0.0-r1.tgz",
+      "integrity": "sha512-ssZl5Fd0AEXpGsUZNZN2LpnYyLAhntgzR+pHFjpH/C7KiyQRYS9UzOLuo66Uu2U7ArHCyF4J135xqlmfbUClsA==",
       "dependencies": {
-        "axios": "^0.27.2",
         "base-64": "^1.0.0",
         "byte-length": "^1.0.2",
+        "cross-fetch": "^3.1.5",
         "fast-xml-parser": "^3.19.0",
         "he": "^1.2.0",
-        "hot-patcher": "^1.0.0",
+        "hot-patcher": "^2.0.0",
         "layerr": "^0.1.2",
         "md5": "^2.3.0",
         "minimatch": "^5.1.0",
@@ -9849,7 +9921,7 @@
         "url-parse": "^1.5.10"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=14"
       }
     },
     "node_modules/webdav/node_modules/brace-expansion": {
@@ -9861,9 +9933,9 @@
       }
     },
     "node_modules/webdav/node_modules/hot-patcher": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-1.0.0.tgz",
-      "integrity": "sha512-3H8VH0PreeNsKMZw16nTHbUp4YoHCnPlawpsPXGJUR4qENDynl79b6Xk9CIFvLcH1qungBsCuzKcWyzoPPalTw=="
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.0.tgz",
+      "integrity": "sha512-rwJ0ZqSFgm+5oD0KiGBVinyPWRJESRSsHlEWDzZjyOe/OfhD9tynHqUyUIGX2fWuV+BihW4nXxeoZRJVHid64w=="
     },
     "node_modules/webdav/node_modules/minimatch": {
       "version": "5.1.0",
@@ -9876,6 +9948,11 @@
         "node": ">=10"
       }
     },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
     "node_modules/webpack": {
       "version": "5.75.0",
       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
@@ -10199,6 +10276,15 @@
         "url": "https://opencollective.com/webpack"
       }
     },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
     "node_modules/which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -11629,12 +11715,26 @@
       }
     },
     "@buttercup/dropbox-client": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/@buttercup/dropbox-client/-/dropbox-client-1.1.3.tgz",
-      "integrity": "sha512-3IPc+4TEIoQaGOl9vFIM4cWo3Qbc60HK/2h7oiflpLb5FOMb9kjvpi7T5bJDFaYzWhsDQphRObJyNGj8Xz6OVA==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@buttercup/dropbox-client/-/dropbox-client-2.0.0.tgz",
+      "integrity": "sha512-G08Eqh03jAAu9ZmkhnUcYCmw9OmP4qGBwUJwzgyA7f8mrh2DQr2dhJNbq2JymQvVEy2Lmmo8/O8HXXFSfRXhIQ==",
       "requires": {
-        "cowl": "^1.1.0",
-        "hot-patcher": "^0.5.0"
+        "cross-fetch": "^3.1.5",
+        "hot-patcher": "^2.0.0",
+        "layerr": "^1.0.0",
+        "url-parse": "^1.5.10"
+      },
+      "dependencies": {
+        "hot-patcher": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.0.tgz",
+          "integrity": "sha512-rwJ0ZqSFgm+5oD0KiGBVinyPWRJESRSsHlEWDzZjyOe/OfhD9tynHqUyUIGX2fWuV+BihW4nXxeoZRJVHid64w=="
+        },
+        "layerr": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/layerr/-/layerr-1.0.0.tgz",
+          "integrity": "sha512-PlgghXyHzISC50rHl4alYWKp91wFSCdIHa/qK+OZ4SgZAsRlHX1AWJIalvS0v5Xl6/mvPe1ZSIBkhiWQ5CNR2w=="
+        }
       }
     },
     "@buttercup/googledrive-client": {
@@ -12293,15 +12393,6 @@
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
       "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
-    "axios": {
-      "version": "0.27.2",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
-      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
-      "requires": {
-        "follow-redirects": "^1.14.9",
-        "form-data": "^4.0.0"
-      }
-    },
     "babel-loader": {
       "version": "9.1.2",
       "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz",
@@ -13176,6 +13267,14 @@
         "sha.js": "^2.4.8"
       }
     },
+    "cross-fetch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+      "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+      "requires": {
+        "node-fetch": "2.6.7"
+      }
+    },
     "cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -13195,11 +13294,18 @@
       "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
     },
     "crypto-random-string": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.3.1.tgz",
-      "integrity": "sha512-5j88ECEn6h17UePrLi6pn1JcLtAiANa3KExyr9y9Z5vo2mv56Gh3I4Aja/B9P9uyMwyxNHAHWv+nE72f30T5Dg==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-5.0.0.tgz",
+      "integrity": "sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==",
       "requires": {
-        "type-fest": "^0.8.1"
+        "type-fest": "^2.12.2"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "2.19.0",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+          "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
+        }
       }
     },
     "custom-event": {
@@ -13831,7 +13937,8 @@
     "follow-redirects": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
-      "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ=="
+      "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+      "dev": true
     },
     "foreachasync": {
       "version": "5.1.3",
@@ -15644,6 +15751,14 @@
         "path-to-regexp": "^1.7.0"
       }
     },
+    "node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "requires": {
+        "whatwg-url": "^5.0.0"
+      }
+    },
     "node-preload": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
@@ -16213,9 +16328,9 @@
       "dev": true
     },
     "pify": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
-      "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA=="
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-6.1.0.tgz",
+      "integrity": "sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw=="
     },
     "pkg-dir": {
       "version": "4.2.0",
@@ -16638,6 +16753,23 @@
       "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
       "dev": true
     },
+    "resolve-typescript-plugin": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-typescript-plugin/-/resolve-typescript-plugin-2.0.0.tgz",
+      "integrity": "sha512-bkQMP9BVupQfZM9N6SS/QOy6PZh4dL4/d9LBhC+fmb5CdE6geGcm2fZUfXii2xXqwmCOu4xp2QTAnZhIBwGadQ==",
+      "dev": true,
+      "requires": {
+        "tslib": "2.4.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+          "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+          "dev": true
+        }
+      }
+    },
     "restore-cursor": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -17447,6 +17579,11 @@
         }
       }
     },
+    "tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+    },
     "tree-kill": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -17702,9 +17839,9 @@
       "dev": true
     },
     "uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+      "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
     },
     "validate-npm-package-license": {
       "version": "3.0.4",
@@ -17745,16 +17882,16 @@
       }
     },
     "webdav": {
-      "version": "4.11.2",
-      "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.11.2.tgz",
-      "integrity": "sha512-Ht9TPD5EB7gYW0YmhRcE5NW0/dn/HQfyLSPQY1Rw1coQ5MQTUooAQ9Bpqt4EU7QLw0b95tX4cU59R+SIojs9KQ==",
+      "version": "5.0.0-r1",
+      "resolved": "https://registry.npmjs.org/webdav/-/webdav-5.0.0-r1.tgz",
+      "integrity": "sha512-ssZl5Fd0AEXpGsUZNZN2LpnYyLAhntgzR+pHFjpH/C7KiyQRYS9UzOLuo66Uu2U7ArHCyF4J135xqlmfbUClsA==",
       "requires": {
-        "axios": "^0.27.2",
         "base-64": "^1.0.0",
         "byte-length": "^1.0.2",
+        "cross-fetch": "^3.1.5",
         "fast-xml-parser": "^3.19.0",
         "he": "^1.2.0",
-        "hot-patcher": "^1.0.0",
+        "hot-patcher": "^2.0.0",
         "layerr": "^0.1.2",
         "md5": "^2.3.0",
         "minimatch": "^5.1.0",
@@ -17773,9 +17910,9 @@
           }
         },
         "hot-patcher": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-1.0.0.tgz",
-          "integrity": "sha512-3H8VH0PreeNsKMZw16nTHbUp4YoHCnPlawpsPXGJUR4qENDynl79b6Xk9CIFvLcH1qungBsCuzKcWyzoPPalTw=="
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.0.tgz",
+          "integrity": "sha512-rwJ0ZqSFgm+5oD0KiGBVinyPWRJESRSsHlEWDzZjyOe/OfhD9tynHqUyUIGX2fWuV+BihW4nXxeoZRJVHid64w=="
         },
         "minimatch": {
           "version": "5.1.0",
@@ -17787,6 +17924,11 @@
         }
       }
     },
+    "webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
     "webpack": {
       "version": "5.75.0",
       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
@@ -17999,6 +18141,15 @@
       "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
       "dev": true
     },
+    "whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "requires": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
diff --git a/package.json b/package.json
index d91ce84..adcc0de 100644
--- a/package.json
+++ b/package.json
@@ -2,8 +2,15 @@
   "name": "buttercup",
   "version": "6.17.0",
   "description": "A NodeJS password vault.",
-  "main": "dist/index.node.js",
-  "types": "dist/index.node.d.ts",
+  "type": "module",
+  "exports": {
+    ".": {
+      "browser": "./dist/web/index.js",
+      "default": "./dist/node/index.js"
+    },
+    "./web": "./dist/web/index.js"
+  },
+  "types": "dist/node/index.d.ts",
   "contributors": [
     {
       "name": "Perry Mitchell",
@@ -13,8 +20,9 @@
   "scripts": {
     "analyse": "ANALYSE=bundle npm run build",
     "build": "run-s clean build:node build:web",
-    "build:node": "tsc",
-    "build:web": "webpack --mode=production",
+    "build:node": "tsc && npm run build:node:rename",
+    "build:node:rename": "mv ./dist/node/index.node.js ./dist/node/index.js && mv ./dist/node/index.node.d.ts ./dist/node/index.d.ts",
+    "build:web": "webpack --config webpack.config.cjs --mode=production",
     "clean": "rimraf ./dist && rimraf ./web",
     "dev": "concurrently -k 'npm run dev:node' 'npm run dev:web'",
     "dev:node": "tsc --watch",
@@ -31,7 +39,7 @@
     "test:node": "npm run build:node && nyc mocha -r test/index.js --timeout 15000 'test/{unit,integration}/**/*.spec.js'",
     "test:unit": "npm run build:node && mocha -r test/index.js 'test/unit/**/*.spec.js'",
     "test:unit:watch": "nodemon --exec 'npm run test:unit' --watch source --watch test",
-    "test:web": "karma start",
+    "test:web": "karma start karma.conf.cjs",
     "test:web:production": "BUNDLE=production karma start"
   },
   "files": [
@@ -42,7 +50,7 @@
     "*.md"
   ],
   "engines": {
-    "node": ">=12"
+    "node": ">=14"
   },
   "lint-staged": {
     "{source,test}/**/*.{js,ts}": [
@@ -74,10 +82,10 @@
   "homepage": "https://github.com/buttercup/buttercup-core#readme",
   "dependencies": {
     "@buttercup/channel-queue": "^1.2.0",
-    "@buttercup/dropbox-client": "^1.1.3",
+    "@buttercup/dropbox-client": "^2.0.0",
     "@buttercup/googledrive-client": "^1.3.3",
     "cowl": "^1.1.0",
-    "crypto-random-string": "^3.3.1",
+    "crypto-random-string": "^5.0.0",
     "eventemitter3": "^4.0.7",
     "fast-levenshtein": "^3.0.0",
     "foreachasync": "^5.1.3",
@@ -90,10 +98,10 @@
     "layerr": "^0.1.2",
     "pako": "^1.0.11",
     "path-posix": "^1.0.0",
-    "pify": "^5.0.0",
+    "pify": "^6.1.0",
     "url-join": "^4.0.1",
-    "uuid": "^8.3.2",
-    "webdav": "^4.11.2"
+    "uuid": "^9.0.0",
+    "webdav": "^5.0.0-r1"
   },
   "devDependencies": {
     "@babel/cli": "^7.20.7",
@@ -125,6 +133,7 @@
     "nyc": "^15.1.0",
     "prettier": "^1.19.1",
     "prettier-check": "^2.0.0",
+    "resolve-typescript-plugin": "^2.0.0",
     "rimraf": "^3.0.2",
     "sinon": "^15.0.1",
     "sleep-promise": "^9.1.0",
diff --git a/source/attachments/AttachmentManager.ts b/source/attachments/AttachmentManager.ts
index 87ab2c7..6c9aecb 100644
--- a/source/attachments/AttachmentManager.ts
+++ b/source/attachments/AttachmentManager.ts
@@ -1,23 +1,18 @@
-import Credentials from "../credentials/Credentials";
-import Entry from "../core/Entry";
-import Vault from "../core/Vault";
-import VaultSource from "../core/VaultSource";
-import { getSharedAppEnv } from "../env/appEnv";
-import { generateUUID } from "../tools/uuid";
-import { decryptAttachment, encryptAttachment, getBufferSize } from "../tools/attachments";
-import { AttachmentDetails } from "../types";
+import { Credentials } from "../credentials/Credentials.js";
+import { Entry } from "../core/Entry.js";
+import { Vault } from "../core/Vault.js";
+import { VaultSource } from "../core/VaultSource.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { generateUUID } from "../tools/uuid.js";
+import { decryptAttachment, encryptAttachment, getBufferSize } from "../tools/attachments.js";
+import { AttachmentDetails } from "../types.js";
 
 const ATTACHMENTS_KEY_LENGTH = 48;
 
-/**
- * Attachment manager
- * @memberof module:Buttercup
- */
-export default class AttachmentManager {
+export class AttachmentManager {
     /**
      * Get a new attachment ID
      * @static
-     * @memberof AttachmentManager
      */
     static newAttachmentID(): string {
         return generateUUID();
@@ -40,7 +35,6 @@ export default class AttachmentManager {
      * @param entry Entry instance the attachment can be found on
      * @param attachmentID The attachment ID
      * @throws {Error} Throws if the attachment isn't found
-     * @memberof AttachmentManager
      */
     async getAttachment(entry: Entry, attachmentID: string): Promise<Buffer | ArrayBuffer> {
         this._checkAttachmentSupport();
@@ -58,7 +52,6 @@ export default class AttachmentManager {
      * @param entry Entry instance the attachment can be found on
      * @param attachmentID The attachment ID
      * @returns The details or null if the attachment isn't found
-     * @memberof AttachmentManager
      */
     async getAttachmentDetails(entry: Entry, attachmentID: string): Promise<AttachmentDetails> {
         this._checkAttachmentSupport();
@@ -70,7 +63,6 @@ export default class AttachmentManager {
     /**
      * List all attachments
      * @param entry Entry instance the attachment can be found on
-     * @memberof AttachmentManager
      */
     async listAttachments(entry: Entry): Promise<Array<AttachmentDetails>> {
         this._checkAttachmentSupport();
@@ -86,7 +78,6 @@ export default class AttachmentManager {
      *  the entry
      * @param entry Entry instance the attachment can be found on
      * @param attachmentID The attachment ID
-     * @memberof AttachmentManager
      */
     async removeAttachment(entry: Entry, attachmentID: string) {
         this._checkAttachmentSupport();
@@ -107,7 +98,6 @@ export default class AttachmentManager {
      * @param type The MIME type
      * @param timestamp Optional timestamp override for the creation of the
      *  attachment
-     * @memberof AttachmentManager
      */
     async setAttachment(
         entry: Entry,
diff --git a/source/core/Entry.ts b/source/core/Entry.ts
index 863ce83..49a32a4 100644
--- a/source/core/Entry.ts
+++ b/source/core/Entry.ts
@@ -1,17 +1,16 @@
-import VaultItem from "./VaultItem";
-import { generateUUID } from "../tools/uuid";
-import { getEntryURLs, EntryURLType } from "../tools/entry";
-import Group from "./Group";
-import Vault from "./Vault";
-import { EntryChange, EntryPropertyValueType, EntryType, GroupID, PropertyKeyValueObject } from "../types";
+import { VaultItem } from "./VaultItem.js";
+import { generateUUID } from "../tools/uuid.js";
+import { getEntryURLs, EntryURLType } from "../tools/entry.js";
+import { Group } from "./Group.js";
+import { Vault } from "./Vault.js";
+import { EntryChange, EntryPropertyValueType, EntryType, GroupID, PropertyKeyValueObject } from "../types.js";
 
 /**
  * Entry class - some secret item, login or perhaps
  * even a credit card
  * @augments VaultItem
- * @memberof module:Buttercup
  */
-export default class Entry extends VaultItem {
+export class Entry extends VaultItem {
     static Attributes = Object.freeze({
         AttachmentPrefix: "BC_ENTRY_ATTACHMENT:",
         FacadeType: "BC_ENTRY_FACADE_TYPE",
@@ -24,7 +23,6 @@ export default class Entry extends VaultItem {
      * @param vault The target vault instance
      * @param parentGroupID The target group to
      *  create the entry in (cannot be vault-level)
-     * @memberof Entry
      * @static
      */
     static createNew(vault: Vault, parentGroupID: GroupID): Entry {
@@ -52,7 +50,6 @@ export default class Entry extends VaultItem {
      * @see moveToGroup
      * @see Vault.getTrashGroup
      * @returns Whether or not the item was deleted
-     * @memberof Entry
      */
     delete(skipTrash: boolean = false): boolean {
         const trashGroup = this.vault.getTrashGroup();
@@ -77,7 +74,6 @@ export default class Entry extends VaultItem {
      * Delete an attribute
      * @param attribute The attribute name
      * @throws {Error} Throws if the attribute doesn't exist, or cannot be deleted
-     * @memberof Entry
      * @returns Self
      */
     deleteAttribute(attribute: string): this {
@@ -89,7 +85,6 @@ export default class Entry extends VaultItem {
      * Delete a property
      * @throws {Error} Throws if property doesn't exist, or cannot be deleted
      * @param property The property to delete
-     * @memberof Entry
      * @returns Self
      */
     deleteProperty(property: string): this {
@@ -105,7 +100,6 @@ export default class Entry extends VaultItem {
      * @returns The attribute value or an object
      *  containing all attribute keys and their values if no attribute name
      *  is provided
-     * @memberof Entry
      */
     getAttribute(attribute?: string): PropertyKeyValueObject | string | undefined {
         const attributes = this.vault.format.getEntryAttributes(this._source) || {};
@@ -118,7 +112,6 @@ export default class Entry extends VaultItem {
 
     /**
      * Get an array of all history changes made to the entry
-     * @memberof Entry
      */
     getChanges(): Array<EntryChange> {
         return this.vault.format.getEntryChanges(this._source);
@@ -127,7 +120,6 @@ export default class Entry extends VaultItem {
     /**
      * Get the containing group for the entry
      * @returns The parent group
-     * @memberof Entry
      * @throws {Error} Throws if no parent group found
      */
     getGroup(): Group {
@@ -146,7 +138,6 @@ export default class Entry extends VaultItem {
      * @param property The name of the property to fetch
      * @returns The property value or an object with all
      *  values if no property specified
-     * @memberof Entry
      */
     getProperty(property?: string): PropertyKeyValueObject | string | undefined {
         const raw = this.vault.format.getEntryProperties(this._source);
@@ -160,7 +151,6 @@ export default class Entry extends VaultItem {
      * Set the value type of an entry property
      * @param property The property to get the value type of
      * @returns The property value type
-     * @memberof Entry
      */
     getPropertyValueType(property: string): EntryPropertyValueType {
         const attr = this.getAttribute(`${Entry.Attributes.FieldTypePrefix}${property}`) as EntryPropertyValueType;
@@ -175,7 +165,6 @@ export default class Entry extends VaultItem {
      *  a regular expression, it is then tested against all property keys. If a
      *  string, it's compared exactly.
      * @returns A key-value object of the matching properties
-     * @memberof Entry
      */
     getProperties(propertyExpression?: RegExp | string): PropertyKeyValueObject {
         const raw = this.vault.format.getEntryProperties(this._source);
@@ -192,7 +181,6 @@ export default class Entry extends VaultItem {
     /**
      * Get the entry type
      * @returns
-     * @memberof Entry
      */
     getType(): EntryType {
         return <EntryType | undefined>this.getAttribute(Entry.Attributes.FacadeType) || EntryType.Login;
@@ -208,7 +196,6 @@ export default class Entry extends VaultItem {
      *  - "any" - Return all found URLs
      * @param urlTypePreference The URL type preference
      * @returns An array of URLs
-     * @memberof Entry
      */
     getURLs(urlTypePreference: EntryURLType): Array<string> {
         return getEntryURLs(this.getProperty() as { [key: string]: string }, urlTypePreference);
@@ -217,7 +204,6 @@ export default class Entry extends VaultItem {
     /**
      * Check if the entry is in the trash
      * @returns Whether or not the entry is in the trash
-     * @memberof Entry
      */
     isInTrash(): boolean {
         const group = this.getGroup();
@@ -228,7 +214,6 @@ export default class Entry extends VaultItem {
      * Move the entry to another group
      * @returns Returns self
      * @param group The target group
-     * @memberof Entry
      */
     moveToGroup(group: Group): this {
         this.vault.format.moveEntry(this.id, group.id);
@@ -240,7 +225,6 @@ export default class Entry extends VaultItem {
      * @param attribute The name of the attribute
      * @param value The value to set
      * @returns Returns self
-     * @memberof Entry
      */
     setAttribute(attribute: string, value: string): this {
         this.vault.format.setEntryAttribute(this.id, attribute, value);
@@ -252,7 +236,6 @@ export default class Entry extends VaultItem {
      * @param property The property name
      * @param value The property value
      * @returns Returns self
-     * @memberof Entry
      */
     setProperty(property: string, value: string): this {
         this.vault.format.setEntryProperty(this.id, property, value);
@@ -264,7 +247,6 @@ export default class Entry extends VaultItem {
      * @param property The property to set the value type for
      * @param valueType The new value type to set, or `null` to remove
      * @returns Returns self
-     * @memberof Entry
      */
     setPropertyValueType(property: string, valueType: EntryPropertyValueType | null): this {
         if (valueType === null) {
diff --git a/source/core/Group.ts b/source/core/Group.ts
index 9bbc1e8..f1dcabd 100644
--- a/source/core/Group.ts
+++ b/source/core/Group.ts
@@ -1,18 +1,18 @@
-import VaultItem from "./VaultItem";
-import Entry from "./Entry";
-import Vault from "./Vault";
-import { generateUUID } from "../tools/uuid";
-import { moveGroupBetweenVaults } from "../tools/sharing";
-import { findGroupsByTitle, getAllChildGroups } from "../search/groups";
-import { findEntriesByProperty, getAllChildEntries } from "../search/entries";
-import { EntryID, GroupID } from "../types";
+import { VaultItem } from "./VaultItem.js";
+import { Entry } from "./Entry.js";
+import { Vault } from "./Vault.js";
+import { generateUUID } from "../tools/uuid.js";
+import { moveGroupBetweenVaults } from "../tools/sharing.js";
+import { findGroupsByTitle, getAllChildGroups } from "../search/groups.js";
+import { findEntriesByProperty, getAllChildEntries } from "../search/entries.js";
+import { EntryID, GroupID } from "../types.js";
 
 /**
  * Group class - contains Entrys
  * @augments VaultItem
  * @memberof module:Buttercup
  */
-export default class Group extends VaultItem {
+export class Group extends VaultItem {
     static Attribute = Object.freeze({
         Role: "bc_group_role"
     });
diff --git a/source/core/Vault.ts b/source/core/Vault.ts
index dd4f479..ba79d62 100644
--- a/source/core/Vault.ts
+++ b/source/core/Vault.ts
@@ -1,18 +1,18 @@
 import EventEmitter from "eventemitter3";
-import { getDefaultFormat } from "../io/formatRouter";
-import { findGroupsByTitle } from "../search/groups";
-import { findEntriesByProperty } from "../search/entries";
-import Group from "./Group";
-import Entry from "./Entry";
-import { EntryID, GroupID, History } from "../types";
-import VaultFormat from "../io/VaultFormat";
+import { getDefaultFormat } from "../io/formatRouter.js";
+import { findGroupsByTitle } from "../search/groups.js";
+import { findEntriesByProperty } from "../search/entries.js";
+import { Group } from "./Group.js";
+import { Entry } from "./Entry.js";
+import { VaultFormat } from "../io/VaultFormat.js";
+import { EntryID, GroupID, History } from "../types.js";
 
 /**
  * Vault class - Contains Groups and Entrys
  * @augments EventEmitter
  * @memberof module:Buttercup
  */
-export default class Vault extends EventEmitter {
+export class Vault extends EventEmitter {
     static Attribute = Object.freeze({
         AttachmentsKey: "BC_ATTACHMENTS_KEY"
     });
diff --git a/source/core/VaultItem.ts b/source/core/VaultItem.ts
index deb2c7e..ccb8558 100644
--- a/source/core/VaultItem.ts
+++ b/source/core/VaultItem.ts
@@ -1,10 +1,10 @@
-import Vault from "./Vault";
-import { VaultPermission } from "../types";
+import { Vault } from "./Vault.js";
+import { VaultPermission } from "../types.js";
 
 /**
  * Base vault member class (for Entry, Group etc.)
  */
-export default class VaultItem {
+export class VaultItem {
     _boundUpdateRefs: () => void;
 
     _source: any;
@@ -12,7 +12,6 @@ export default class VaultItem {
     /**
      * Reference to the containing vault
      * @protected
-     * @memberof VaultItem
      */
     _vault: Vault = null;
 
@@ -34,7 +33,6 @@ export default class VaultItem {
     /**
      * The ID of the entry or group
      * @readonly
-     * @memberof VaultItem
      */
     get id(): string {
         return this._vault.format.getItemID(this._source);
@@ -42,7 +40,6 @@ export default class VaultItem {
 
     /**
      * The current granted permissions
-     * @memberof VaultItem
      */
     get permissions(): Array<string> {
         return [...this._source.permissions];
@@ -51,7 +48,6 @@ export default class VaultItem {
     /**
      * The vault this item belongs to
      * @readonly
-     * @memberof VaultItem
      */
     get vault(): Vault {
         return this._vault;
@@ -60,7 +56,6 @@ export default class VaultItem {
     /**
      * Grant a new permission to the member
      * @param perm The permission to grant
-     * @memberof VaultItem
      */
     grantPermission(perm: string) {
         if (!this.hasPermission(perm)) {
@@ -71,7 +66,6 @@ export default class VaultItem {
     /**
      * Check if the member has a permission
      * @param perm The permission to check for
-     * @memberof VaultItem
      */
     hasPermission(perm: string): boolean {
         return this._source.permissions.includes(perm);
@@ -79,7 +73,6 @@ export default class VaultItem {
 
     /**
      * Revoke all permissions
-     * @memberof VaultItem
      */
     revokeAllPermissions() {
         this._source.permissions = [];
@@ -88,7 +81,6 @@ export default class VaultItem {
     /**
      * Revoke a single permission
      * @param perm The permission to revoke
-     * @memberof VaultItem
      */
     revokePermission(perm: string) {
         this._source.permissions = this._source.permissions.filter(current => current !== perm);
@@ -97,7 +89,6 @@ export default class VaultItem {
     /**
      * Clean up all of the data in the vault item
      * @protected
-     * @memberof VaultItem
      */
     _cleanUp() {
         this._vault = null;
@@ -107,7 +98,6 @@ export default class VaultItem {
     /**
      * Update source references
      * @protected
-     * @memberof VaultItem
      */
     _updateRefs() {
         // No-op for vault item
diff --git a/source/core/VaultManager.ts b/source/core/VaultManager.ts
index ab083d2..22ee73e 100644
--- a/source/core/VaultManager.ts
+++ b/source/core/VaultManager.ts
@@ -2,10 +2,10 @@ import EventEmitter from "eventemitter3";
 import { Layerr } from "layerr";
 import isPromise from "is-promise";
 import { ChannelQueue } from "@buttercup/channel-queue";
-import MemoryStorageInterface from "../storage/MemoryStorageInterface";
-import VaultSource from "./VaultSource";
-import StorageInterface from "../storage/StorageInterface";
-import { SetTimeout, VaultSourceID } from "../types";
+import { MemoryStorageInterface } from "../storage/MemoryStorageInterface.js";
+import { VaultSource } from "./VaultSource.js";
+import { StorageInterface } from "../storage/StorageInterface.js";
+import { SetTimeout, VaultSourceID } from "../types.js";
 
 export interface InterruptedAutoUpdateFunction {
     (): void | Promise<any>;
@@ -36,7 +36,7 @@ const STORAGE_KEY_PREFIX_TEST = /^bcup_vaultmgr_[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]
  * @augments EventEmitter
  * @memberof module:Buttercup
  */
-export default class VaultManager extends EventEmitter {
+export class VaultManager extends EventEmitter {
     /**
      * Key prefix for stored vaults
      * @static
diff --git a/source/core/VaultSource.ts b/source/core/VaultSource.ts
index f590d70..c063f04 100644
--- a/source/core/VaultSource.ts
+++ b/source/core/VaultSource.ts
@@ -1,19 +1,19 @@
 import EventEmitter from "eventemitter3";
 import { ChannelQueue } from "@buttercup/channel-queue";
 import { Layerr } from "layerr";
-import Vault from "./Vault";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
-import { getUniqueID } from "../tools/encoding";
-import { getSourceOfflineArchive, sourceHasOfflineCopy, storeSourceOfflineCopy } from "../tools/vaultManagement";
-import { credentialsToDatasource, prepareDatasourceCredentials } from "../datasources/register";
-import { generateVaultInsights } from "../insight/vault";
-import AttachmentManager from "../attachments/AttachmentManager";
-import TextDatasource from "../datasources/TextDatasource";
-import VaultManager from "./VaultManager";
-import { VaultFormatID, VaultSourceID, VaultSourceStatus } from "../types";
-import { convertFormatAVault } from "../io/formatB/conversion";
-import { VaultFormatB } from "../index.common";
+import { Vault } from "./Vault.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
+import { getUniqueID } from "../tools/encoding.js";
+import { getSourceOfflineArchive, sourceHasOfflineCopy, storeSourceOfflineCopy } from "../tools/vaultManagement.js";
+import { credentialsToDatasource, prepareDatasourceCredentials } from "../datasources/register.js";
+import { generateVaultInsights } from "../insight/vault.js";
+import { AttachmentManager } from "../attachments/AttachmentManager.js";
+import { TextDatasource } from "../datasources/TextDatasource.js";
+import { VaultManager } from "./VaultManager.js";
+import { convertFormatAVault } from "../io/formatB/conversion.js";
+import { VaultFormatB } from "../index.common.js";
+import { VaultFormatID, VaultSourceID, VaultSourceStatus } from "../types.js";
 
 interface StateChangeEnqueuedFunction {
     (): void | Promise<any>;
@@ -58,7 +58,7 @@ function processDehydratedCredentials(credentialsString: string, masterPassword:
  * @augments EventEmitter
  * @memberof module:Buttercup
  */
-export default class VaultSource extends EventEmitter {
+export class VaultSource extends EventEmitter {
     static STATUS_LOCKED = VaultSourceStatus.Locked;
     static STATUS_PENDING = VaultSourceStatus.Pending;
     static STATUS_UNLOCKED = VaultSourceStatus.Unlocked;
diff --git a/source/credentials/Credentials.ts b/source/credentials/Credentials.ts
index 657c6c3..ee292e7 100644
--- a/source/credentials/Credentials.ts
+++ b/source/credentials/Credentials.ts
@@ -1,7 +1,7 @@
-import { generateUUID } from "../tools/uuid";
-import { credentialsAllowsPurpose, getCredentials, setCredentials } from "./channel";
-import { getSharedAppEnv } from "../env/appEnv";
-import { CredentialsData, CredentialsPayload, DatasourceConfiguration } from "../types";
+import { generateUUID } from "../tools/uuid.js";
+import { credentialsAllowsPurpose, getCredentials, setCredentials } from "./channel.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { CredentialsData, CredentialsPayload, DatasourceConfiguration } from "../types.js";
 
 /**
  * The signature of legacy encrypted credentials
@@ -51,7 +51,7 @@ function unsignEncryptedContent(content: string): string {
  * environment. Credential properties are stored in memory
  * and are inaccessible to public functions.
  */
-export default class Credentials {
+export class Credentials {
     static PURPOSE_DECRYPT_VAULT = "vault-decrypt";
     static PURPOSE_ENCRYPT_VAULT = "vault-encrypt";
     static PURPOSE_SECURE_EXPORT = "secure-export";
diff --git a/source/credentials/channel.ts b/source/credentials/channel.ts
index b84bd6e..5b1fd98 100644
--- a/source/credentials/channel.ts
+++ b/source/credentials/channel.ts
@@ -1,4 +1,4 @@
-import { CredentialsPayload } from "../types";
+import { CredentialsPayload } from "../types.js";
 
 const __store = {};
 
diff --git a/source/datasources/DatasourceAuthManager.ts b/source/datasources/DatasourceAuthManager.ts
index ac9bf91..d5f9278 100644
--- a/source/datasources/DatasourceAuthManager.ts
+++ b/source/datasources/DatasourceAuthManager.ts
@@ -1,7 +1,7 @@
 import EventEmitter from "eventemitter3";
 import { forEachAsync } from "foreachasync";
 import globals from "global";
-import TextDatasource from "./TextDatasource";
+import { TextDatasource } from "./TextDatasource.js";
 
 function markGlobalPresence() {
     if (typeof globals._bcupAuthMgr !== "undefined") {
diff --git a/source/datasources/DropboxDatasource.ts b/source/datasources/DropboxDatasource.ts
index ee3fd15..fa320db 100644
--- a/source/datasources/DropboxDatasource.ts
+++ b/source/datasources/DropboxDatasource.ts
@@ -1,9 +1,9 @@
-import TextDatasource from "./TextDatasource";
-import { fireInstantiationHandlers, registerDatasource } from "./register";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
-import { getSharedAppEnv } from "../env/appEnv";
-import { DatasourceConfigurationDropbox, DatasourceLoadedData, EncryptedContent, History } from "../types";
+import { TextDatasource } from "./TextDatasource.js";
+import { fireInstantiationHandlers, registerDatasource } from "./register.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { DatasourceConfigurationDropbox, DatasourceLoadedData, EncryptedContent, History } from "../types.js";
 
 /**
  * Datasource for Dropbox archives
diff --git a/source/datasources/FileDatasource.ts b/source/datasources/FileDatasource.ts
index 5d62d2a..3bdc5b0 100644
--- a/source/datasources/FileDatasource.ts
+++ b/source/datasources/FileDatasource.ts
@@ -1,11 +1,11 @@
 import fs from "fs";
 import path from "path";
 import pify from "pify";
-import TextDatasource from "./TextDatasource";
-import { fireInstantiationHandlers, registerDatasource } from "./register";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
-import { ATTACHMENT_EXT } from "../tools/attachments";
+import { TextDatasource } from "./TextDatasource.js";
+import { fireInstantiationHandlers, registerDatasource } from "./register.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
+import { ATTACHMENT_EXT } from "../tools/attachments.js";
 import {
     AttachmentDetails,
     BufferLike,
@@ -14,14 +14,14 @@ import {
     EncryptedContent,
     History,
     VaultID
-} from "../types";
+} from "../types.js";
 
 /**
  * File datasource for loading and saving files
  * @augments TextDatasource
  * @memberof module:Buttercup
  */
-export default class FileDatasource extends TextDatasource {
+export class FileDatasource extends TextDatasource {
     _filename: string;
     mkdir: Function;
     readFile: Function;
diff --git a/source/datasources/GoogleDriveDatasource.ts b/source/datasources/GoogleDriveDatasource.ts
index 15741ca..663c8ca 100644
--- a/source/datasources/GoogleDriveDatasource.ts
+++ b/source/datasources/GoogleDriveDatasource.ts
@@ -1,11 +1,11 @@
 import { createClient } from "@buttercup/googledrive-client";
 import { Layerr } from "layerr";
-import DatasourceAuthManager from "./DatasourceAuthManager";
-import TextDatasource from "./TextDatasource";
-import { fireInstantiationHandlers, registerDatasource } from "./register";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
-import { DatasourceConfigurationGoogleDrive } from "../types";
+import DatasourceAuthManager from "./DatasourceAuthManager.js";
+import { TextDatasource } from "./TextDatasource.js";
+import { fireInstantiationHandlers, registerDatasource } from "./register.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
+import { DatasourceConfigurationGoogleDrive } from "../types.js";
 
 const DATASOURCE_TYPE = "googledrive";
 
diff --git a/source/datasources/MemoryDatasource.ts b/source/datasources/MemoryDatasource.ts
index 740204a..b8eccff 100644
--- a/source/datasources/MemoryDatasource.ts
+++ b/source/datasources/MemoryDatasource.ts
@@ -1,7 +1,7 @@
-import TextDatasource from "./TextDatasource";
-import { fireInstantiationHandlers, registerDatasource } from "./register";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
+import { TextDatasource } from "./TextDatasource.js";
+import { fireInstantiationHandlers, registerDatasource } from "./register.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
 import {
     AttachmentDetails,
     BufferLike,
@@ -11,7 +11,7 @@ import {
     History,
     MemoryStore,
     VaultID
-} from "../types";
+} from "../types.js";
 
 const TYPE = "memory";
 
@@ -20,7 +20,7 @@ const TYPE = "memory";
  * @augments TextDatasource
  * @memberof module:Buttercup
  */
-export default class MemoryDatasource extends TextDatasource {
+export class MemoryDatasource extends TextDatasource {
     protected _property: string;
     private _store: MemoryStore;
 
diff --git a/source/datasources/TextDatasource.ts b/source/datasources/TextDatasource.ts
index 07a992e..e1dfea5 100644
--- a/source/datasources/TextDatasource.ts
+++ b/source/datasources/TextDatasource.ts
@@ -1,9 +1,9 @@
 import EventEmitter from "eventemitter3";
 import hash from "hash.js";
-import Credentials from "../credentials/Credentials";
-import { credentialsAllowsPurpose, getCredentials } from "../credentials/channel";
-import { detectFormat, getFormatForID } from "../io/formatRouter";
-import { fireInstantiationHandlers, registerDatasource } from "./register";
+import { Credentials } from "../credentials/Credentials.js";
+import { credentialsAllowsPurpose, getCredentials } from "../credentials/channel.js";
+import { detectFormat, getFormatForID } from "../io/formatRouter.js";
+import { fireInstantiationHandlers, registerDatasource } from "./register.js";
 import {
     AttachmentDetails,
     BufferLike,
@@ -14,13 +14,13 @@ import {
     VaultID,
     VaultInsights,
     VaultSourceID
-} from "../types";
+} from "../types.js";
 
 /**
  * Datasource for text input and output
  * @memberof module:Buttercup
  */
-export default class TextDatasource extends EventEmitter {
+export class TextDatasource extends EventEmitter {
     _content: EncryptedContent;
     _credentials: Credentials;
 
diff --git a/source/datasources/WebDAVDatasource.ts b/source/datasources/WebDAVDatasource.ts
index 5d17335..30efead 100644
--- a/source/datasources/WebDAVDatasource.ts
+++ b/source/datasources/WebDAVDatasource.ts
@@ -1,11 +1,11 @@
 import { WebDAVClient } from "webdav";
 import pathPosix from "path-posix";
-import TextDatasource from "./TextDatasource";
-import { fireInstantiationHandlers, registerDatasource } from "./register";
-import { getSharedAppEnv } from "../env/appEnv";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
-import { ATTACHMENT_EXT } from "../tools/attachments";
+import { TextDatasource } from "./TextDatasource.js";
+import { fireInstantiationHandlers, registerDatasource } from "./register.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
+import { ATTACHMENT_EXT } from "../tools/attachments.js";
 import {
     AttachmentDetails,
     BufferLike,
@@ -13,7 +13,7 @@ import {
     DatasourceLoadedData,
     History,
     VaultID
-} from "../types";
+} from "../types.js";
 
 const MAX_DATA_SIZE = 200 * 1024 * 1024; // 200 MB
 
diff --git a/source/datasources/register.ts b/source/datasources/register.ts
index 617f25e..807cec3 100644
--- a/source/datasources/register.ts
+++ b/source/datasources/register.ts
@@ -1,6 +1,6 @@
-import { getCredentials } from "../credentials/channel";
-import Credentials from "../credentials/Credentials";
-import TextDatasource from "./TextDatasource";
+import { getCredentials } from "../credentials/channel.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { TextDatasource } from "./TextDatasource.js";
 
 const __datasources = {};
 const __datasourceFlags = {};
diff --git a/source/env/appEnv.ts b/source/env/appEnv.ts
index ba9e421..ba0a976 100644
--- a/source/env/appEnv.ts
+++ b/source/env/appEnv.ts
@@ -1,3 +1,3 @@
-export { getSharedAppEnv } from "./core/singleton";
+export { getSharedAppEnv } from "./core/singleton.js";
 
 export type AppEnvMapper = <T extends { [key: string]: any }>(template: T) => T;
diff --git a/source/env/core/appEnv.ts b/source/env/core/appEnv.ts
index a9b1d05..c2fc3d8 100644
--- a/source/env/core/appEnv.ts
+++ b/source/env/core/appEnv.ts
@@ -1,4 +1,4 @@
-import { assignObjImmutableProp } from "./prop";
+import { assignObjImmutableProp } from "./prop.js";
 
 export interface AppEnv {
     getProperty: AppEnvGetPropertyMethod;
diff --git a/source/env/core/singleton.ts b/source/env/core/singleton.ts
index a2e7fca..fed9dff 100644
--- a/source/env/core/singleton.ts
+++ b/source/env/core/singleton.ts
@@ -1,6 +1,6 @@
-import { AppEnv, createAppEnv } from "./appEnv";
-import { assignObjImmutableProp } from "./prop";
-import { getGlobal } from "./global";
+import { AppEnv, createAppEnv } from "./appEnv.js";
+import { assignObjImmutableProp } from "./prop.js";
+import { getGlobal } from "./global.js";
 
 const GLOBAL_INSTANCE_REF = "@@__ButtercupAppEnv";
 
diff --git a/source/env/native/compression.v1.ts b/source/env/native/compression.v1.ts
index d0cdf2a..13a9cb7 100644
--- a/source/env/native/compression.v1.ts
+++ b/source/env/native/compression.v1.ts
@@ -1,4 +1,6 @@
-import { gzip, ungzip } from "pako";
+import pako from "pako";
+
+const { gzip, ungzip } = pako;
 
 /**
  * Compress text using GZIP
diff --git a/source/env/native/crypto.ts b/source/env/native/crypto.ts
index b4afecf..21fc51c 100644
--- a/source/env/native/crypto.ts
+++ b/source/env/native/crypto.ts
@@ -1,6 +1,6 @@
 import { createAdapter } from "iocane";
 import cryptoRandomString from "crypto-random-string";
-import { CRYPTO_PBKDF2_ROUNDS, CRYPTO_RANDOM_STRING_CHARS } from "../core/constants";
+import { CRYPTO_PBKDF2_ROUNDS, CRYPTO_RANDOM_STRING_CHARS } from "../core/constants.js";
 
 let __derivationRoundsOverride = CRYPTO_PBKDF2_ROUNDS;
 
diff --git a/source/env/native/index.ts b/source/env/native/index.ts
index 6db09a9..79fca28 100644
--- a/source/env/native/index.ts
+++ b/source/env/native/index.ts
@@ -1,12 +1,12 @@
-import { getCryptoResources } from "./crypto";
-import { getCompressionResources as getV1CompressionResources } from "./compression.v1";
-import { getCompressionResources as getV2CompressionResources } from "./compression.v2";
-import { getEnvironmentResources } from "./environment";
-import { getNetResources } from "./net";
-import { getRNGResources } from "./rng";
-import { getEncodingResources } from "./encoding";
-import { AppEnv } from "../core/appEnv";
-import { AppEnvMapper } from "../appEnv";
+import { getCryptoResources } from "./crypto.js";
+import { getCompressionResources as getV1CompressionResources } from "./compression.v1.js";
+import { getCompressionResources as getV2CompressionResources } from "./compression.v2.js";
+import { getEnvironmentResources } from "./environment.js";
+import { getNetResources } from "./net.js";
+import { getRNGResources } from "./rng.js";
+import { getEncodingResources } from "./encoding.js";
+import { AppEnv } from "../core/appEnv.js";
+import { AppEnvMapper } from "../appEnv.js";
 
 export function applyNativeConfiguration(appEnv: AppEnv, map: AppEnvMapper) {
     appEnv.setProperties(
diff --git a/source/env/web/compression.v2.ts b/source/env/web/compression.v2.ts
index 3b53704..6b21b88 100644
--- a/source/env/web/compression.v2.ts
+++ b/source/env/web/compression.v2.ts
@@ -1,5 +1,5 @@
 import { gzip, ungzip } from "pako";
-import { base64ToBytes, bytesToBase64 } from "../../tools/encoding";
+import { base64ToBytes, bytesToBase64 } from "../../tools/encoding.js";
 
 /**
  * Compress text using GZIP
diff --git a/source/env/web/crypto.ts b/source/env/web/crypto.ts
index 77f3dac..ecc3d1c 100644
--- a/source/env/web/crypto.ts
+++ b/source/env/web/crypto.ts
@@ -1,5 +1,5 @@
 import { createAdapter } from "iocane/web";
-import { CRYPTO_PBKDF2_ROUNDS, CRYPTO_RANDOM_STRING_CHARS } from "../core/constants";
+import { CRYPTO_PBKDF2_ROUNDS, CRYPTO_RANDOM_STRING_CHARS } from "../core/constants.js";
 
 const UINT16_MAX = 65535;
 
diff --git a/source/env/web/index.ts b/source/env/web/index.ts
index 222f57a..aa93171 100644
--- a/source/env/web/index.ts
+++ b/source/env/web/index.ts
@@ -1,12 +1,12 @@
-import { getCryptoResources } from "./crypto";
-import { getCompressionResources as getV1CompressionResources } from "./compression.v1";
-import { getCompressionResources as getV2CompressionResources } from "./compression.v2";
-import { getEnvironmentResources } from "./environment";
-import { getNetResources } from "./net";
-import { getRNGResources } from "./rng";
-import { getEncodingResources } from "./encoding";
-import { AppEnv } from "../core/appEnv";
-import { AppEnvMapper } from "../appEnv";
+import { getCryptoResources } from "./crypto.js";
+import { getCompressionResources as getV1CompressionResources } from "./compression.v1.js";
+import { getCompressionResources as getV2CompressionResources } from "./compression.v2.js";
+import { getEnvironmentResources } from "./environment.js";
+import { getNetResources } from "./net.js";
+import { getRNGResources } from "./rng.js";
+import { getEncodingResources } from "./encoding.js";
+import { AppEnv } from "../core/appEnv.js";
+import { AppEnvMapper } from "../appEnv.js";
 
 export function applyWebConfiguration(appEnv: AppEnv, map: AppEnvMapper) {
     appEnv.setProperties(
diff --git a/source/env/web/net.ts b/source/env/web/net.ts
index 6a6c133..ff47e01 100644
--- a/source/env/web/net.ts
+++ b/source/env/web/net.ts
@@ -1,4 +1,4 @@
-import { createClient } from "webdav/web";
+import { createClient } from "webdav";
 import { DropboxClient } from "@buttercup/dropbox-client";
 
 export function getNetResources() {
diff --git a/source/facades/entry.ts b/source/facades/entry.ts
index 4a65999..cb341ad 100644
--- a/source/facades/entry.ts
+++ b/source/facades/entry.ts
@@ -1,7 +1,7 @@
-import facadeFieldFactories from "./entryFields";
-import { createFieldDescriptor, getEntryPropertyValueType, setEntryPropertyValueType } from "./tools";
-import Entry from "../core/Entry";
-import Group from "../core/Group";
+import facadeFieldFactories from "./entryFields.js";
+import { createFieldDescriptor, getEntryPropertyValueType, setEntryPropertyValueType } from "./tools.js";
+import { Entry } from "../core/Entry.js";
+import { Group } from "../core/Group.js";
 import {
     EntryFacade,
     EntryFacadeField,
@@ -11,7 +11,7 @@ import {
     EntryPropertyValueType,
     GroupID,
     VaultFacade
-} from "../types";
+} from "../types.js";
 
 export interface CreateEntryFacadeOptions {
     type?: EntryType;
diff --git a/source/facades/entryFields.ts b/source/facades/entryFields.ts
index 129bfb7..a6444a6 100644
--- a/source/facades/entryFields.ts
+++ b/source/facades/entryFields.ts
@@ -1,6 +1,6 @@
-import { createFieldDescriptor } from "./tools";
-import Entry from "../core/Entry";
-import { EntryFacadeField, EntryPropertyType, EntryPropertyValueType, EntryType } from "../types";
+import { createFieldDescriptor } from "./tools.js";
+import { Entry } from "../core/Entry.js";
+import { EntryFacadeField, EntryPropertyType, EntryPropertyValueType, EntryType } from "../types.js";
 
 const DIGIT = "\\d";
 
diff --git a/source/facades/symbols.ts b/source/facades/symbols.ts
index 88da41a..af6f374 100644
--- a/source/facades/symbols.ts
+++ b/source/facades/symbols.ts
@@ -1,4 +1,4 @@
-import { EntryPropertyValueType, EntryType } from "../types";
+import { EntryPropertyValueType, EntryType } from "../types.js";
 
 export type EntryPropertyTypeIndex = {
     [key in EntryPropertyValueType]: {
diff --git a/source/facades/tools.ts b/source/facades/tools.ts
index f5cd304..e513187 100644
--- a/source/facades/tools.ts
+++ b/source/facades/tools.ts
@@ -1,6 +1,6 @@
-import Entry from "../core/Entry";
-import { generateUUID } from "../tools/uuid";
-import { EntryFacadeField, EntryFacadeFieldFormatting, EntryPropertyType, EntryPropertyValueType } from "../types";
+import { Entry } from "../core/Entry.js";
+import { generateUUID } from "../tools/uuid.js";
+import { EntryFacadeField, EntryFacadeFieldFormatting, EntryPropertyType, EntryPropertyValueType } from "../types.js";
 
 export interface CreateFieldDescriptorOptions {
     formatting?: boolean | EntryFacadeFieldFormatting;
diff --git a/source/facades/vault.ts b/source/facades/vault.ts
index 6594aa8..f67750c 100644
--- a/source/facades/vault.ts
+++ b/source/facades/vault.ts
@@ -1,11 +1,11 @@
-import { consumeEntryFacade, createEntryFacade } from "./entry";
-import { idSignifiesNew } from "./tools";
-import { FACADE_VERSION } from "./symbols";
-import Entry from "../core/Entry";
-import Group from "../core/Group";
-import Vault from "../core/Vault";
-import { EntryFacade, GroupFacade, GroupID, VaultFacade } from "../types";
-import { generateUUID } from "../tools/uuid";
+import { consumeEntryFacade, createEntryFacade } from "./entry.js";
+import { idSignifiesNew } from "./tools.js";
+import { FACADE_VERSION } from "./symbols.js";
+import { Entry } from "../core/Entry.js";
+import { Group } from "../core/Group.js";
+import { Vault } from "../core/Vault.js";
+import { generateUUID } from "../tools/uuid.js";
+import { EntryFacade, GroupFacade, GroupID, VaultFacade } from "../types.js";
 
 export interface ConsumeVaultFacadeOptions {
     mergeMode?: boolean;
diff --git a/source/index.common.ts b/source/index.common.ts
index 23b5071..6787208 100644
--- a/source/index.common.ts
+++ b/source/index.common.ts
@@ -1,40 +1,35 @@
 export {
-    default as VaultManager,
     InterruptedAutoUpdateFunction,
+    VaultManager,
     VaultManagerAddSourceOptions,
     VaultManagerOptions
-} from "./core/VaultManager";
-export {
-    default as VaultSource,
-    VaultSourceConfig,
-    VaultSourceMetadata,
-    VaultSourceUnlockOptions
-} from "./core/VaultSource";
-export { default as Vault } from "./core/Vault";
-export { default as Group } from "./core/Group";
-export { default as Entry } from "./core/Entry";
-export { default as AttachmentManager } from "./attachments/AttachmentManager";
+} from "./core/VaultManager.js";
+export { VaultSource, VaultSourceConfig, VaultSourceMetadata, VaultSourceUnlockOptions } from "./core/VaultSource.js";
+export { Vault } from "./core/Vault.js";
+export { Group } from "./core/Group.js";
+export { Entry } from "./core/Entry.js";
+export { AttachmentManager } from "./attachments/AttachmentManager.js";
 
-export { detectFormat, getDefaultFormat, setDefaultFormat } from "./io/formatRouter";
-export { default as VaultFormatA } from "./io/VaultFormatA";
+export { detectFormat, getDefaultFormat, setDefaultFormat } from "./io/formatRouter.js";
+export { VaultFormatA } from "./io/VaultFormatA.js";
 export {
     getSignature as getFormatASignature,
     hasValidSignature as hasValidFormatASignature
-} from "./io/formatA/signing";
-export { default as VaultFormatB } from "./io/VaultFormatB";
+} from "./io/formatA/signing.js";
+export { VaultFormatB } from "./io/VaultFormatB.js";
 export {
     getSignature as getFormatBSignature,
     hasValidSignature as hasValidFormatBSignature
-} from "./io/formatB/signing";
+} from "./io/formatB/signing.js";
 
-export { default as TextDatasource } from "./datasources/TextDatasource";
-export { default as MemoryDatasource } from "./datasources/MemoryDatasource";
-export { default as FileDatasource } from "./datasources/FileDatasource";
-export { default as WebDAVDatasource } from "./datasources/WebDAVDatasource";
-export { default as DropboxDatasource } from "./datasources/DropboxDatasource";
-export { default as GoogleDriveDatasource } from "./datasources/GoogleDriveDatasource";
-export { default as DatasourceAuthManager } from "./datasources/DatasourceAuthManager";
-export { registerDatasource } from "./datasources/register";
+export { TextDatasource } from "./datasources/TextDatasource.js";
+export { MemoryDatasource } from "./datasources/MemoryDatasource.js";
+export { FileDatasource } from "./datasources/FileDatasource.js";
+export { default as WebDAVDatasource } from "./datasources/WebDAVDatasource.js";
+export { default as DropboxDatasource } from "./datasources/DropboxDatasource.js";
+export { default as GoogleDriveDatasource } from "./datasources/GoogleDriveDatasource.js";
+export { default as DatasourceAuthManager } from "./datasources/DatasourceAuthManager.js";
+export { registerDatasource } from "./datasources/register.js";
 
 export {
     ConsumeVaultFacadeOptions,
@@ -45,8 +40,8 @@ export {
     consumeVaultFacade,
     createGroupFacade,
     createVaultFacade
-} from "./facades/vault";
-export { isOTPURI, isVaultFacade } from "./facades/detection";
+} from "./facades/vault.js";
+export { isOTPURI, isVaultFacade } from "./facades/detection.js";
 export {
     CreateEntryFacadeOptions,
     consumeEntryFacade,
@@ -55,13 +50,13 @@ export {
     fieldsToProperties,
     getEntryFacadePath,
     setEntryFacadePropertyValueType
-} from "./facades/entry";
+} from "./facades/entry.js";
 export {
     CreateFieldDescriptorOptions,
     createFieldDescriptor,
     getEntryPropertyValueType,
     setEntryPropertyValueType
-} from "./facades/tools";
+} from "./facades/tools.js";
 export {
     DEFAULT_ENTRY_TYPE,
     DEFAULT_FIELD_TYPE,
@@ -69,27 +64,27 @@ export {
     FIELD_VALUE_TYPES,
     EntryPropertyTypeIndex,
     EntryTypeIndex
-} from "./facades/symbols";
+} from "./facades/symbols.js";
 
-export { default as Credentials } from "./credentials/Credentials";
+export { Credentials } from "./credentials/Credentials.js";
 
-export { default as StorageInterface } from "./storage/StorageInterface";
-export { default as MemoryStorageInterface } from "./storage/MemoryStorageInterface";
+export { StorageInterface } from "./storage/StorageInterface.js";
+export { MemoryStorageInterface } from "./storage/MemoryStorageInterface.js";
 
-export { SearchResult } from "./search/BaseSearch";
-export { VaultEntrySearch as Search } from "./search/VaultEntrySearch"; // compat @todo remove
-export { VaultEntrySearch } from "./search/VaultEntrySearch";
-export { VaultFacadeEntrySearch } from "./search/VaultFacadeEntrySearch";
-export { SearchKey, buildSearcher } from "./search/searcher";
+export { SearchResult } from "./search/BaseSearch.js";
+export { VaultEntrySearch as Search } from "./search/VaultEntrySearch.js"; // compat @todo remove
+export { VaultEntrySearch } from "./search/VaultEntrySearch.js";
+export { VaultFacadeEntrySearch } from "./search/VaultFacadeEntrySearch.js";
+export { SearchKey, buildSearcher } from "./search/searcher.js";
 
-export { AppEnv, AppEnvGetPropertyOptions } from "./env/core/appEnv";
-export { getSharedAppEnv } from "./env/appEnv";
+export { AppEnv, AppEnvGetPropertyOptions } from "./env/core/appEnv.js";
+export { getSharedAppEnv } from "./env/appEnv.js";
 
-export { generateUUID } from "./tools/uuid";
-export { EntryURLType, getEntryPath, getEntryURLs } from "./tools/entry";
-export { base64ToBytes, bytesToBase64, decodeBase64String, encodeBase64String } from "./tools/encoding";
+export { generateUUID } from "./tools/uuid.js";
+export { EntryURLType, getEntryPath, getEntryURLs } from "./tools/entry.js";
+export { base64ToBytes, bytesToBase64, decodeBase64String, encodeBase64String } from "./tools/encoding.js";
 
-export * from "./types";
+export * from "./types.js";
 
 /**
  * @module Buttercup
diff --git a/source/index.node.ts b/source/index.node.ts
index 5a81397..67a4b8a 100644
--- a/source/index.node.ts
+++ b/source/index.node.ts
@@ -1,6 +1,6 @@
-import { getSharedAppEnv } from "./env/core/singleton";
-import { AppEnvMapper } from "./env/appEnv";
-import { applyNativeConfiguration } from "./env/native/index";
+import { getSharedAppEnv } from "./env/core/singleton.js";
+import { AppEnvMapper } from "./env/appEnv.js";
+import { applyNativeConfiguration } from "./env/native/index.js";
 
 let __hasInitialised = false;
 
@@ -15,4 +15,4 @@ export function init(appEnvMapper: AppEnvMapper = x => x) {
     applyNativeConfiguration(appEnv, appEnvMapper);
 }
 
-export * from "./index.common";
+export * from "./index.common.js";
diff --git a/source/index.web.ts b/source/index.web.ts
index addd79b..f8441bc 100644
--- a/source/index.web.ts
+++ b/source/index.web.ts
@@ -1,7 +1,7 @@
-import { getSharedAppEnv } from "./env/core/singleton";
-import { AppEnvMapper } from "./env/appEnv";
-import { applyWebConfiguration } from "./env/web/index";
-import * as localFileClient from "./web/localFileClient";
+import { getSharedAppEnv } from "./env/core/singleton.js";
+import { AppEnvMapper } from "./env/appEnv.js";
+import { applyWebConfiguration } from "./env/web/index.js";
+import * as localFileClient from "./web/localFileClient.js";
 
 let __hasInitialised = false;
 
@@ -16,9 +16,9 @@ export function init(appEnvMapper: AppEnvMapper = x => x) {
     applyWebConfiguration(appEnv, appEnvMapper);
 }
 
-export { default as LocalFileDatasource } from "./web/LocalFileDatasource";
-export { default as LocalStorageInterface } from "./web/LocalStorageInterface";
-export { default as LocalStorageDatasource } from "./web/LocalStorageDatasource";
+export { default as LocalFileDatasource } from "./web/LocalFileDatasource.js";
+export { default as LocalStorageInterface } from "./web/LocalStorageInterface.js";
+export { default as LocalStorageDatasource } from "./web/LocalStorageDatasource.js";
 export { localFileClient };
 
-export * from "./index.common";
+export * from "./index.common.js";
diff --git a/source/insight/vault.ts b/source/insight/vault.ts
index f61a518..63e1d3b 100644
--- a/source/insight/vault.ts
+++ b/source/insight/vault.ts
@@ -1,5 +1,5 @@
-import Vault from "../core/Vault";
-import { VaultInsights } from "../types";
+import { Vault } from "../core/Vault.js";
+import { VaultInsights } from "../types.js";
 
 const DIGITS = /[0-9]+/;
 const LOWER_CASE = /[a-z]+/;
diff --git a/source/io/VaultFormat.ts b/source/io/VaultFormat.ts
index 6ab4766..c4f777d 100644
--- a/source/io/VaultFormat.ts
+++ b/source/io/VaultFormat.ts
@@ -1,6 +1,6 @@
 import EventEmitter from "eventemitter3";
-import Credentials from "../credentials/Credentials";
-import Vault from "../core/Vault";
+import { Credentials } from "../credentials/Credentials.js";
+import { Vault } from "../core/Vault.js";
 import {
     EntryChange,
     EntryID,
@@ -14,13 +14,13 @@ import {
     History,
     PropertyKeyValueObject,
     VaultFormatID
-} from "../types";
+} from "../types.js";
 
 function notImplemented() {
     throw new Error("Not implemented");
 }
 
-export default class VaultFormat extends EventEmitter {
+export class VaultFormat extends EventEmitter {
     static encodeRaw(rawContent: History, credentials: Credentials) {
         notImplemented();
     }
diff --git a/source/io/VaultFormatA.ts b/source/io/VaultFormatA.ts
index 3e5fc32..d6ea366 100644
--- a/source/io/VaultFormatA.ts
+++ b/source/io/VaultFormatA.ts
@@ -1,7 +1,7 @@
 import { Layerr } from "layerr";
-import VaultFormat from "./VaultFormat";
-import Vault from "../core/Vault";
-import Credentials from "../credentials/Credentials";
+import { VaultFormat } from "./VaultFormat.js";
+import { Vault } from "../core/Vault.js";
+import { Credentials } from "../credentials/Credentials.js";
 import {
     executeArchiveID,
     executeComment,
@@ -22,7 +22,7 @@ import {
     executeSetEntryProperty,
     executeSetGroupAttribute,
     executeTitleGroup
-} from "./formatA/commands";
+} from "./formatA/commands.js";
 import {
     COMMAND_MANIFEST,
     InigoCommand as Inigo,
@@ -30,16 +30,16 @@ import {
     getAllEntries,
     getAllGroups,
     stripDestructiveCommands
-} from "./formatA/tools";
-import Flattener from "./formatA/Flattener";
-import { getFormat, hasValidSignature, sign, stripSignature, vaultContentsEncrypted } from "./formatA/signing";
-import { describeVaultDataset } from "./formatA/describe";
-import VaultComparator from "./formatA/VaultComparator";
-import { getSharedAppEnv } from "../env/appEnv";
-import { decodeStringValue, isEncoded } from "../tools/encoding";
-import { generateUUID } from "../tools/uuid";
-import { getCredentials } from "../credentials/channel";
-import { historyArrayToString, historyStringToArray } from "./common";
+} from "./formatA/tools.js";
+import { Flattener } from "./formatA/Flattener.js";
+import { getFormat, hasValidSignature, sign, stripSignature, vaultContentsEncrypted } from "./formatA/signing.js";
+import { describeVaultDataset } from "./formatA/describe.js";
+import VaultComparator from "./formatA/VaultComparator.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { decodeStringValue, isEncoded } from "../tools/encoding.js";
+import { generateUUID } from "../tools/uuid.js";
+import { getCredentials } from "../credentials/channel.js";
+import { historyArrayToString, historyStringToArray } from "./common.js";
 import {
     EntryChange,
     EntryChangeType,
@@ -53,7 +53,7 @@ import {
     VaultFormatID,
     VaultID,
     EntryPropertyType
-} from "../types";
+} from "../types.js";
 
 const COMMANDS = {
     aid: executeArchiveID,
@@ -89,8 +89,8 @@ function emptyVault(): FormatAVault {
     };
 }
 
-export default class VaultFormatA extends VaultFormat {
-    static encodeRaw(rawContent: History, credentials: Credentials): Promise<string> {
+export class VaultFormatA extends VaultFormat {
+    static async encodeRaw(rawContent: History, credentials: Credentials): Promise<string> {
         const compress = getSharedAppEnv().getProperty("compression/v1/compressText");
         const encrypt = getSharedAppEnv().getProperty("crypto/v1/encryptText");
         const { masterPassword } = getCredentials(credentials.id);
diff --git a/source/io/VaultFormatB.ts b/source/io/VaultFormatB.ts
index f87b9d9..e914a6f 100644
--- a/source/io/VaultFormatB.ts
+++ b/source/io/VaultFormatB.ts
@@ -1,16 +1,16 @@
-import VaultFormat from "./VaultFormat";
-import Vault from "../core/Vault";
-import { generateUUID } from "../tools/uuid";
-import { getSharedAppEnv } from "../env/appEnv";
-import { getCredentials } from "../credentials/channel";
-import Credentials from "../credentials/Credentials";
-import { historyArrayToString, historyStringToArray } from "./common";
-import { hasValidSignature, sign, stripSignature, vaultContentsEncrypted } from "./formatB/signing";
-import { historiesDiffer } from "./formatB/compare";
-import { mergeRawVaults } from "./formatB/merge";
-import { valuesObjectToKeyValueObject } from "./formatB/conversion";
-import { newRawValue, valueToHistoryItem } from "./formatB/history";
-import { getDateString, getTimestamp } from "../tools/date";
+import { VaultFormat } from "./VaultFormat.js";
+import { Vault } from "../core/Vault.js";
+import { generateUUID } from "../tools/uuid.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { getCredentials } from "../credentials/channel.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { historyArrayToString, historyStringToArray } from "./common.js";
+import { hasValidSignature, sign, stripSignature, vaultContentsEncrypted } from "./formatB/signing.js";
+import { historiesDiffer } from "./formatB/compare.js";
+import { mergeRawVaults } from "./formatB/merge.js";
+import { valuesObjectToKeyValueObject } from "./formatB/conversion.js";
+import { newRawValue, valueToHistoryItem } from "./formatB/history.js";
+import { getDateString, getTimestamp } from "../tools/date.js";
 import {
     EntryChange,
     EntryChangeType,
@@ -24,7 +24,7 @@ import {
     PropertyKeyValueObject,
     VaultFormatID,
     VaultID
-} from "../types";
+} from "../types.js";
 
 function emptyVault(): FormatBVault {
     return {
@@ -36,7 +36,7 @@ function emptyVault(): FormatBVault {
     };
 }
 
-export default class VaultFormatB extends VaultFormat {
+export class VaultFormatB extends VaultFormat {
     static encodeRaw(rawContent: History, credentials: Credentials): Promise<string> {
         const compress = getSharedAppEnv().getProperty("compression/v2/compressText");
         const encrypt = getSharedAppEnv().getProperty("crypto/v1/encryptText");
diff --git a/source/io/common.ts b/source/io/common.ts
index e94354f..c1ac06a 100644
--- a/source/io/common.ts
+++ b/source/io/common.ts
@@ -1,4 +1,4 @@
-import { History, VaultFormatID } from "../types";
+import { History, VaultFormatID } from "../types.js";
 
 /**
  * Convert array of history lines to a string
diff --git a/source/io/formatA/Flattener.ts b/source/io/formatA/Flattener.ts
index 0550ec1..fafebe6 100644
--- a/source/io/formatA/Flattener.ts
+++ b/source/io/formatA/Flattener.ts
@@ -1,6 +1,6 @@
-import { describeVaultDataset } from "./describe";
-import VaultFormatA from "../VaultFormatA";
-import { FormatAVault } from "../../types";
+import { describeVaultDataset } from "./describe.js";
+import { VaultFormatA } from "../VaultFormatA.js";
+import { FormatAVault } from "../../types.js";
 
 /**
  * Check if a command should be preserved (not flattened)
@@ -19,7 +19,7 @@ function mustBePreserved(command: string): boolean {
 /**
  * Flattener class for flattening archive history sets
  */
-export default class Flattener {
+export class Flattener {
     /**
      * Minimum history lines before flattening can occur
      * @static
diff --git a/source/io/formatA/VaultComparator.ts b/source/io/formatA/VaultComparator.ts
index 7278eed..646d793 100644
--- a/source/io/formatA/VaultComparator.ts
+++ b/source/io/formatA/VaultComparator.ts
@@ -1,6 +1,6 @@
-import Vault from "../../core/Vault";
-import { createVaultFacade } from "../../facades/vault";
-import { History } from "../../types";
+import { Vault } from "../../core/Vault.js";
+import { createVaultFacade } from "../../facades/vault.js";
+import { History } from "../../types.js";
 
 const PRIMATIVES = ["string", "number", "boolean", "undefined"];
 
diff --git a/source/io/formatA/commands.ts b/source/io/formatA/commands.ts
index 12507b2..a4dc3ab 100644
--- a/source/io/formatA/commands.ts
+++ b/source/io/formatA/commands.ts
@@ -4,8 +4,8 @@ import {
     findGroupContainingEntryID,
     findGroupContainingGroupID,
     generateEntryLegacyHistoryItem
-} from "./tools";
-import { EntryID, EntryPropertyType, FormatAEntry, FormatAGroup, FormatAVault, GroupID, VaultID } from "../../types";
+} from "./tools.js";
+import { EntryID, EntryPropertyType, FormatAEntry, FormatAGroup, FormatAVault, GroupID, VaultID } from "../../types.js";
 
 export function executeArchiveID(vault: FormatAVault, opts: any, id: VaultID) {
     if (opts.shareID) {
diff --git a/source/io/formatA/describe.ts b/source/io/formatA/describe.ts
index 21e67e1..d8dba0e 100644
--- a/source/io/formatA/describe.ts
+++ b/source/io/formatA/describe.ts
@@ -1,5 +1,5 @@
-import { InigoCommand as Inigo } from "./tools";
-import { FormatAGroup, FormatAVault, GroupID, History } from "../../types";
+import { InigoCommand as Inigo } from "./tools.js";
+import { FormatAGroup, FormatAVault, GroupID, History } from "../../types.js";
 
 const { Command } = Inigo;
 
diff --git a/source/io/formatA/tools.ts b/source/io/formatA/tools.ts
index 9d0bf08..55615cc 100644
--- a/source/io/formatA/tools.ts
+++ b/source/io/formatA/tools.ts
@@ -1,5 +1,5 @@
-import { encodeStringValue } from "../../tools/encoding";
-import { generateUUID } from "../../tools/uuid";
+import { encodeStringValue } from "../../tools/encoding.js";
+import { generateUUID } from "../../tools/uuid.js";
 import {
     EntryLegacyHistoryItem,
     EntryID,
@@ -8,7 +8,7 @@ import {
     FormatAGroup,
     FormatAVault,
     GroupID
-} from "../../types";
+} from "../../types.js";
 
 interface FormatACommandArgument {
     test: RegExp;
diff --git a/source/io/formatB/compare.ts b/source/io/formatB/compare.ts
index a2af8fd..9d22ae7 100644
--- a/source/io/formatB/compare.ts
+++ b/source/io/formatB/compare.ts
@@ -1,5 +1,5 @@
-import { sortValueHistory } from "./history";
-import { FormatBKeyValueObject, FormatBValue, FormatBVault, History } from "../../types";
+import { sortValueHistory } from "./history.js";
+import { FormatBKeyValueObject, FormatBValue, FormatBVault, History } from "../../types.js";
 
 export function historiesDiffer(historyA: History, historyB: History) {
     const vaultA = inflateHistory(historyA);
diff --git a/source/io/formatB/conversion.ts b/source/io/formatB/conversion.ts
index 379b30b..4356628 100644
--- a/source/io/formatB/conversion.ts
+++ b/source/io/formatB/conversion.ts
@@ -1,6 +1,6 @@
-import { newRawValue } from "./history";
-import { getDateString } from "../../tools/date";
-import { getAllEntries as getAllFormatAEntries, getAllGroups as getAllFormatAGroups } from "../formatA/tools";
+import { newRawValue } from "./history.js";
+import { getDateString } from "../../tools/date.js";
+import { getAllEntries as getAllFormatAEntries, getAllGroups as getAllFormatAGroups } from "../formatA/tools.js";
 import {
     EntryPropertyType,
     FormatAEntry,
@@ -11,7 +11,7 @@ import {
     FormatBKeyValueObject,
     FormatBVault,
     PropertyKeyValueObject
-} from "../../types";
+} from "../../types.js";
 
 export function convertFormatAEntry(entry: FormatAEntry): FormatBEntry {
     const changes = entry.history || [];
diff --git a/source/io/formatB/history.ts b/source/io/formatB/history.ts
index 6c36006..7097a5e 100644
--- a/source/io/formatB/history.ts
+++ b/source/io/formatB/history.ts
@@ -1,5 +1,5 @@
-import { getTimestamp } from "../../tools/date";
-import { EntryPropertyType, FormatBValue, FormatBValueHistoryItem } from "../../types";
+import { getTimestamp } from "../../tools/date.js";
+import { EntryPropertyType, FormatBValue, FormatBValueHistoryItem } from "../../types.js";
 
 export const MAX_ATTRIBUTE_HISTORY = 3;
 export const MAX_PROPERTY_HISTORY = 5;
diff --git a/source/io/formatB/merge.ts b/source/io/formatB/merge.ts
index c67aae5..8c647d8 100644
--- a/source/io/formatB/merge.ts
+++ b/source/io/formatB/merge.ts
@@ -1,5 +1,5 @@
-import { cloneValue, mergeValues } from "./history";
-import { EntryPropertyType, FormatBEntry, FormatBGroup, FormatBKeyValueObject, FormatBVault } from "../../types";
+import { cloneValue, mergeValues } from "./history.js";
+import { EntryPropertyType, FormatBEntry, FormatBGroup, FormatBKeyValueObject, FormatBVault } from "../../types.js";
 
 function cloneEntry(entry: FormatBEntry): FormatBEntry {
     return {
diff --git a/source/io/formatRouter.ts b/source/io/formatRouter.ts
index 8d0ba1c..5a6b27c 100644
--- a/source/io/formatRouter.ts
+++ b/source/io/formatRouter.ts
@@ -1,7 +1,7 @@
-import VaultFormatA from "./VaultFormatA";
-import VaultFormatB from "./VaultFormatB";
-import { hasValidSignature as isFormatBSigned } from "./formatB/signing";
-import { VaultFormatID } from "../types";
+import { VaultFormatA } from "./VaultFormatA.js";
+import { VaultFormatB } from "./VaultFormatB.js";
+import { hasValidSignature as isFormatBSigned } from "./formatB/signing.js";
+import { VaultFormatID } from "../types.js";
 
 const DEFAULT_FORMAT = VaultFormatA;
 
diff --git a/source/search/BaseSearch.ts b/source/search/BaseSearch.ts
index b912e8b..86a9fa6 100644
--- a/source/search/BaseSearch.ts
+++ b/source/search/BaseSearch.ts
@@ -1,8 +1,8 @@
 import levenshtein from "fast-levenshtein";
-import StorageInterface from "../storage/StorageInterface";
-import { buildSearcher } from "./searcher";
-import Vault from "../core/Vault";
-import { EntryID, EntryType, GroupID, VaultFacade, VaultID } from "../types";
+import { StorageInterface } from "../storage/StorageInterface.js";
+import { buildSearcher } from "./searcher.js";
+import { Vault } from "../core/Vault.js";
+import { EntryID, EntryType, GroupID, VaultFacade, VaultID } from "../types.js";
 
 interface DomainScores {
     [domain: string]: number;
diff --git a/source/search/VaultEntrySearch.ts b/source/search/VaultEntrySearch.ts
index 5e38ca5..fc8e073 100644
--- a/source/search/VaultEntrySearch.ts
+++ b/source/search/VaultEntrySearch.ts
@@ -1,8 +1,8 @@
-import { BaseSearch, ProcessedSearchEntry, SearcherFactory } from "./BaseSearch";
-import Vault from "../core/Vault";
-import Entry from "../core/Entry";
-import { EntryURLType, getEntryURLs } from "../tools/entry";
-import StorageInterface from "../storage/StorageInterface";
+import { BaseSearch, ProcessedSearchEntry, SearcherFactory } from "./BaseSearch.js";
+import { Vault } from "../core/Vault.js";
+import { Entry } from "../core/Entry.js";
+import { EntryURLType, getEntryURLs } from "../tools/entry.js";
+import { StorageInterface } from "../storage/StorageInterface.js";
 
 async function extractEntries(vault: Vault, memory: StorageInterface): Promise<Array<ProcessedSearchEntry>> {
     // Get scores
diff --git a/source/search/VaultFacadeEntrySearch.ts b/source/search/VaultFacadeEntrySearch.ts
index 34ca7e4..070d7ac 100644
--- a/source/search/VaultFacadeEntrySearch.ts
+++ b/source/search/VaultFacadeEntrySearch.ts
@@ -1,8 +1,8 @@
-import { BaseSearch, ProcessedSearchEntry, SearcherFactory } from "./BaseSearch";
-import { EntryURLType, getEntryURLs } from "../tools/entry";
-import { fieldsToProperties } from "../facades/entry";
-import StorageInterface from "../storage/StorageInterface";
-import { EntryFacade, VaultFacade } from "../types";
+import { BaseSearch, ProcessedSearchEntry, SearcherFactory } from "./BaseSearch.js";
+import { EntryURLType, getEntryURLs } from "../tools/entry.js";
+import { fieldsToProperties } from "../facades/entry.js";
+import { StorageInterface } from "../storage/StorageInterface.js";
+import { EntryFacade, VaultFacade } from "../types.js";
 
 async function extractEntries(facade: VaultFacade, memory: StorageInterface): Promise<Array<ProcessedSearchEntry>> {
     // Get scores
diff --git a/source/search/entries.ts b/source/search/entries.ts
index 26d130f..9f97921 100644
--- a/source/search/entries.ts
+++ b/source/search/entries.ts
@@ -1,6 +1,6 @@
-import Entry from "../core/Entry";
-import Group from "../core/Group";
-import { GroupID } from "../types";
+import { Entry } from "../core/Entry.js";
+import { Group } from "../core/Group.js";
+import { GroupID } from "../types.js";
 
 export function findEntriesByProperty(
     entries: Array<Entry>,
diff --git a/source/search/groups.ts b/source/search/groups.ts
index 032efc9..1cf81cd 100644
--- a/source/search/groups.ts
+++ b/source/search/groups.ts
@@ -1,5 +1,5 @@
-import Group from "../core/Group";
-import { GroupID } from "../types";
+import { Group } from "../core/Group.js";
+import { GroupID } from "../types.js";
 
 /**
  * Find groups by their title
diff --git a/source/storage/MemoryStorageInterface.ts b/source/storage/MemoryStorageInterface.ts
index 24f6a74..b964b8d 100644
--- a/source/storage/MemoryStorageInterface.ts
+++ b/source/storage/MemoryStorageInterface.ts
@@ -1,11 +1,11 @@
-import StorageInterface from "./StorageInterface";
+import { StorageInterface } from "./StorageInterface.js";
 
 /**
  * Storage interface for memory storage
  * @augments StorageInterface
  * @memberof module:Buttercup
  */
-export default class MemoryStorageInterface extends StorageInterface {
+export class MemoryStorageInterface extends StorageInterface {
     _store: { [property: string]: string } = {};
 
     /**
diff --git a/source/storage/StorageInterface.ts b/source/storage/StorageInterface.ts
index 8b66f86..fdf76e0 100644
--- a/source/storage/StorageInterface.ts
+++ b/source/storage/StorageInterface.ts
@@ -3,7 +3,7 @@
  * @see MemoryStorageInterface
  * @memberof module:Buttercup
  */
-export default class StorageInterface {
+export class StorageInterface {
     /**
      * Get all keys as an array
      * @memberof StorageInterface
diff --git a/source/tools/attachments.ts b/source/tools/attachments.ts
index 3bd69fe..54a7b2a 100644
--- a/source/tools/attachments.ts
+++ b/source/tools/attachments.ts
@@ -1,7 +1,7 @@
-import { getSharedAppEnv } from "../env/appEnv";
-import { getCredentials } from "../credentials/channel";
-import Credentials from "../credentials/Credentials";
-import { BufferLike } from "../types";
+import { getSharedAppEnv } from "../env/appEnv.js";
+import { getCredentials } from "../credentials/channel.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { BufferLike } from "../types.js";
 
 export const ATTACHMENT_EXT = "bcatt";
 
diff --git a/source/tools/date.ts b/source/tools/date.ts
index 59b44d7..43ae775 100644
--- a/source/tools/date.ts
+++ b/source/tools/date.ts
@@ -1,4 +1,4 @@
-import { DateString, UTCTimestamp } from "../types";
+import { DateString, UTCTimestamp } from "../types.js";
 
 export function getDateFromDateString(dateString: DateString): Date {
     return new Date(dateString);
diff --git a/source/tools/encoding.ts b/source/tools/encoding.ts
index 09c121a..671afa4 100644
--- a/source/tools/encoding.ts
+++ b/source/tools/encoding.ts
@@ -1,5 +1,5 @@
-import { generateUUID } from "./uuid";
-import { getSharedAppEnv } from "../env/appEnv";
+import { generateUUID } from "./uuid.js";
+import { getSharedAppEnv } from "../env/appEnv.js";
 
 const ENCODED_STRING_PATTERN = /^utf8\+base64:(|[a-zA-Z0-9+\/=]+)$/;
 export const ENCODED_STRING_PREFIX = "utf8+base64:";
diff --git a/source/tools/entry.ts b/source/tools/entry.ts
index 1351830..071edf6 100644
--- a/source/tools/entry.ts
+++ b/source/tools/entry.ts
@@ -1,7 +1,7 @@
-import Entry from "../core/Entry";
-import Group from "../core/Group";
-import { objectValues } from "./polyfill";
-import { GroupID } from "../types";
+import { Entry } from "../core/Entry.js";
+import { Group } from "../core/Group.js";
+import { objectValues } from "./polyfill.js";
+import { GroupID } from "../types.js";
 
 export enum EntryURLType {
     Any = "any",
diff --git a/source/tools/sharing.ts b/source/tools/sharing.ts
index 4ec94c1..7b3067d 100644
--- a/source/tools/sharing.ts
+++ b/source/tools/sharing.ts
@@ -1,6 +1,6 @@
-import Group from "../core/Group";
-import Vault from "../core/Vault";
-import { History } from "../types";
+import { Group } from "../core/Group.js";
+import { Vault } from "../core/Vault.js";
+import { History } from "../types.js";
 
 const SHARE_COMMAND_EXP = /^\$[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\s/;
 
diff --git a/source/tools/uuid.ts b/source/tools/uuid.ts
index f429fef..2aae344 100644
--- a/source/tools/uuid.ts
+++ b/source/tools/uuid.ts
@@ -1,4 +1,4 @@
-import { getSharedAppEnv } from "../env/appEnv";
+import { getSharedAppEnv } from "../env/appEnv.js";
 
 /**
  * Generate a UUID (v4)
diff --git a/source/tools/vaultManagement.ts b/source/tools/vaultManagement.ts
index a80d61e..132179a 100644
--- a/source/tools/vaultManagement.ts
+++ b/source/tools/vaultManagement.ts
@@ -1,5 +1,5 @@
-import StorageInterface from "../storage/StorageInterface";
-import { VaultSourceID } from "../types";
+import { StorageInterface } from "../storage/StorageInterface.js";
+import { VaultSourceID } from "../types.js";
 
 const STORAGE_PREFIX = "bcup_archivecache_";
 
diff --git a/source/tools/vaultSearch.ts b/source/tools/vaultSearch.ts
index 333f649..af15086 100644
--- a/source/tools/vaultSearch.ts
+++ b/source/tools/vaultSearch.ts
@@ -1,5 +1,5 @@
-import Group from "../core/Group";
-import Entry from "../core/Entry";
+import { Group } from "../core/Group.js";
+import { Entry } from "../core/Entry.js";
 
 /**
  * Find entry instances by filtering with a compare function
diff --git a/source/web/LocalFileDatasource.ts b/source/web/LocalFileDatasource.ts
index b694160..2f90d0e 100644
--- a/source/web/LocalFileDatasource.ts
+++ b/source/web/LocalFileDatasource.ts
@@ -1,9 +1,15 @@
-import { fireInstantiationHandlers, registerDatasource } from "../datasources/register";
-import TextDatasource from "../datasources/TextDatasource";
-import { getCredentials } from "../credentials/channel";
-import { buildClient } from "./localFileClient";
-import { DatasourceLoadedData, EncryptedContent, History } from "../types";
-import Credentials from "../credentials/Credentials";
+import { fireInstantiationHandlers, registerDatasource } from "../datasources/register.js";
+import { TextDatasource } from "../datasources/TextDatasource.js";
+import { getCredentials } from "../credentials/channel.js";
+import { buildClient } from "./localFileClient.js";
+import { Credentials } from "../credentials/Credentials.js";
+import {
+    CredentialsPayload,
+    DatasourceConfiguration,
+    DatasourceLoadedData,
+    EncryptedContent,
+    History
+} from "../types.js";
 
 /**
  * Local file datasource, connecting via the desktop
@@ -18,9 +24,9 @@ export default class LocalFileDatasource extends TextDatasource {
 
     constructor(credentials: Credentials) {
         super(credentials);
-        const { data: credentialData } = getCredentials(credentials.id);
+        const { data: credentialData } = getCredentials(credentials.id) as CredentialsPayload;
         const { datasource: datasourceConfig } = credentialData;
-        const { path, token } = datasourceConfig;
+        const { path, token } = datasourceConfig as DatasourceConfiguration;
         this._path = path;
         this._token = token;
         this.client = buildClient(token);
diff --git a/source/web/LocalStorageDatasource.ts b/source/web/LocalStorageDatasource.ts
index e9f1dd0..c07b221 100644
--- a/source/web/LocalStorageDatasource.ts
+++ b/source/web/LocalStorageDatasource.ts
@@ -1,9 +1,15 @@
-import TextDatasource from "../datasources/TextDatasource";
-import { fireInstantiationHandlers, registerDatasource } from "../datasources/register";
-import Credentials from "../credentials/Credentials";
-import { getCredentials } from "../credentials/channel";
-import LocalStorageInterface from "./LocalStorageInterface";
-import { DatasourceLoadedData, EncryptedContent, History } from "../types";
+import { TextDatasource } from "../datasources/TextDatasource.js";
+import { fireInstantiationHandlers, registerDatasource } from "../datasources/register.js";
+import { Credentials } from "../credentials/Credentials.js";
+import { getCredentials } from "../credentials/channel.js";
+import LocalStorageInterface from "./LocalStorageInterface.js";
+import {
+    CredentialsPayload,
+    DatasourceConfiguration,
+    DatasourceLoadedData,
+    EncryptedContent,
+    History
+} from "../types.js";
 
 const TYPE = "localstorage";
 
@@ -23,9 +29,9 @@ export default class LocalStorageDatasource extends TextDatasource {
      */
     constructor(credentials: Credentials) {
         super(credentials);
-        const { data: credentialData } = getCredentials(credentials.id);
+        const { data: credentialData } = getCredentials(credentials.id) as CredentialsPayload;
         const { datasource: datasourceConfig } = credentialData;
-        const { property } = datasourceConfig;
+        const { property } = datasourceConfig as DatasourceConfiguration;
         this._property = property;
         this._storage = new LocalStorageInterface();
         this.type = TYPE;
diff --git a/source/web/LocalStorageInterface.ts b/source/web/LocalStorageInterface.ts
index de5963b..368bee1 100644
--- a/source/web/LocalStorageInterface.ts
+++ b/source/web/LocalStorageInterface.ts
@@ -1,4 +1,4 @@
-import StorageInterface from "../storage/StorageInterface";
+import { StorageInterface } from "../storage/StorageInterface.js";
 
 export function getStorage(): Storage {
     return window.localStorage;
@@ -19,8 +19,8 @@ export default class LocalStorageInterface extends StorageInterface {
      * Get all keys from storage
      * @returns A promise that resolves with an array of keys
      */
-    getAllKeys() {
-        return Promise.resolve(Object.keys(this.storage));
+    async getAllKeys() {
+        return Object.keys(this.storage);
     }
 
     /**
@@ -28,9 +28,9 @@ export default class LocalStorageInterface extends StorageInterface {
      * @param name The key name
      * @returns A promise that resolves with the value
      */
-    getValue(name: string): Promise<string> {
+    async getValue(name: string): Promise<string | null> {
         const value = this.storage.getItem(name);
-        return Promise.resolve(value);
+        return value;
     }
 
     /**
@@ -38,9 +38,8 @@ export default class LocalStorageInterface extends StorageInterface {
      * @param name The key name
      * @returns A promise that resolves when the removal has completed
      */
-    removeKey(name: string): Promise<void> {
+    async removeKey(name: string): Promise<void> {
         this.storage.removeItem(name);
-        return Promise.resolve();
     }
 
     /**
@@ -49,8 +48,7 @@ export default class LocalStorageInterface extends StorageInterface {
      * @param value The value to set
      * @returns A promise that resolves when the value is set
      */
-    setValue(name: string, value: string): Promise<void> {
+    async setValue(name: string, value: string): Promise<void> {
         this.storage.setItem(name, value);
-        return Promise.resolve();
     }
 }
diff --git a/source/web/localFileClient.ts b/source/web/localFileClient.ts
index fc8a43d..a426de7 100644
--- a/source/web/localFileClient.ts
+++ b/source/web/localFileClient.ts
@@ -1,5 +1,5 @@
 import joinURL from "url-join";
-import { getSharedAppEnv } from "../env/appEnv";
+import { getSharedAppEnv } from "../env/appEnv.js";
 
 const BASE_URL = "http://localhost:12821";
 
diff --git a/tsconfig.json b/tsconfig.json
index 31a5052..36a49ad 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,11 +1,13 @@
 {
     "compilerOptions": {
-        "allowJs": true,
+        "allowSyntheticDefaultImports": true,
+        "forceConsistentCasingInFileNames": true,
         "esModuleInterop": true,
-        "outDir": "./dist",
-        "module": "CommonJS",
+        "outDir": "./dist/node",
+        "module": "es2020",
+        "moduleResolution": "node",
         "declaration": true,
-        "target": "es6"
+        "target": "es2020"
     },
     "include": [
         "./source/**/*"
diff --git a/webpack.config.js b/webpack.config.cjs
similarity index 89%
rename from webpack.config.js
rename to webpack.config.cjs
index e34b424..b08019e 100644
--- a/webpack.config.js
+++ b/webpack.config.cjs
@@ -2,10 +2,11 @@ const path = require("path");
 const exec = require("child_process").exec;
 const { DefinePlugin } = require("webpack");
 const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
+const ResolveTypeScriptPlugin = require("resolve-typescript-plugin");
 
 const SOURCE = path.resolve(__dirname, "./source");
 const WEB_ENTRY = path.join(SOURCE, "index.web.ts");
-const DIST = path.resolve(__dirname, "./web");
+const DIST = path.resolve(__dirname, "./dist/web");
 
 const plugins = [
     new DefinePlugin({
@@ -61,7 +62,7 @@ const baseConfig = {
     plugins,
 
     resolve: {
-        extensions: [".ts", ".js"],
+        extensions: [".js"],
         fallback: {
             crypto: false,
             dns: false,
@@ -72,7 +73,10 @@ const baseConfig = {
             path: false,
             stream: false,
             util: false
-        }
+        },
+        plugins: [
+            new ResolveTypeScriptPlugin()
+        ]
     },
 
     target: "web"


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2fb3c93..128e161 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        node-version: [12.x, 14.x, 16.x, 18.x]
+        node-version: [14.x, 16.x, 18.x]
     steps:
       - uses: actions/checkout@v2
       - name: Node.js specs ${{ matrix.node-version }}
@@ -20,7 +20,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        node-version: [16.x]
+        node-version: [18.x]
     steps:
       - uses: actions/checkout@v2
       - name: Node.js web specs ${{ matrix.node-version }}
diff --git a/test/index.js b/test/index.js
index 6b44392..2fd26a8 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,6 +1,4 @@
-const { expect } = require("chai");
-const sinon = require("sinon");
-const { init } = require("../dist/index.node.js");
+import { init } from "../dist/node/index.js";
 
 let closed = false;
 
@@ -10,9 +8,7 @@ init(env => ({
 }));
 
 Object.assign(global, {
-    expect,
     setClosedEnv: isClosed => {
         closed = isClosed;
-    },
-    sinon
+    }
 });
diff --git a/test/integration/attachments.spec.js b/test/integration/attachments.spec.js
index e6d1fc2..efff1a3 100644
--- a/test/integration/attachments.spec.js
+++ b/test/integration/attachments.spec.js
@@ -1,8 +1,10 @@
-const path = require("path");
-const fs = require("fs");
-const pify = require("pify");
-const tmp = require("tmp");
-const {
+import { basename, dirname, join, resolve } from "node:path";
+import { readFile, stat } from "node:fs/promises";
+import { fileURLToPath } from "node:url";
+import { expect } from "chai";
+import sinon from "sinon";
+import { dirSync } from "tmp";
+import {
     AttachmentManager,
     Credentials,
     MemoryStorageInterface,
@@ -12,12 +14,12 @@ const {
     VaultManager,
     VaultSource,
     setDefaultFormat
-} = require("../../dist/index.node.js");
+} from "../../dist/node/index.js";
 
-const IMAGE_PATH = path.resolve(__dirname, "../resources/attachments/image.png");
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
 
-const readFile = pify(fs.readFile);
-const stat = pify(fs.stat);
+const IMAGE_PATH = resolve(__dirname, "../resources/attachments/image.png");
 
 describe("AttachmentManager", function() {
     [
@@ -40,7 +42,7 @@ describe("AttachmentManager", function() {
                 },
                 {
                     type: "file",
-                    path: path.join(tmp.dirSync().name, "vault.bcup")
+                    path: join(dirSync().name, "vault.bcup")
                 }
             ].forEach(datasourceConfig => {
                 describe(`using datasource type: ${datasourceConfig.type}`, function() {
@@ -75,7 +77,7 @@ describe("AttachmentManager", function() {
                             this.entry,
                             attachmentID,
                             attachmentData,
-                            path.basename(IMAGE_PATH),
+                            basename(IMAGE_PATH),
                             "image/png",
                             nowDate
                         );
@@ -110,7 +112,7 @@ describe("AttachmentManager", function() {
                                 this.entry,
                                 attachmentID,
                                 attachmentData,
-                                path.basename(IMAGE_PATH),
+                                basename(IMAGE_PATH),
                                 "image/png",
                                 nowDate
                             );
@@ -143,7 +145,7 @@ describe("AttachmentManager", function() {
                                 this.entry,
                                 this.attachmentID,
                                 this.attachmentData,
-                                path.basename(IMAGE_PATH),
+                                basename(IMAGE_PATH),
                                 "image/png"
                             );
                             await this.source.attachmentManager.setAttachment(
@@ -207,7 +209,7 @@ describe("AttachmentManager", function() {
                                     this.entry,
                                     this.attachmentID,
                                     newData,
-                                    path.basename(IMAGE_PATH),
+                                    basename(IMAGE_PATH),
                                     "image/png"
                                 );
                             } catch (err) {
diff --git a/test/integration/core/VaultManager.spec.js b/test/integration/core/VaultManager.spec.js
index 97b79d7..fbcfe1e 100644
--- a/test/integration/core/VaultManager.spec.js
+++ b/test/integration/core/VaultManager.spec.js
@@ -1,6 +1,8 @@
-const path = require("path");
-const tmp = require("tmp");
-const {
+import { join } from "path";
+import { expect } from "chai";
+import sinon from "sinon";
+import { dir } from "tmp";
+import {
     Credentials,
     FileDatasource,
     Group,
@@ -12,7 +14,7 @@ const {
     VaultSource,
     VaultSourceStatus,
     setDefaultFormat
-} = require("../../../dist/index.node.js");
+} from "../../../dist/node/index.js";
 
 async function createTextSourceCredentials() {
     const vault = new Vault();
@@ -43,7 +45,7 @@ describe("VaultManager", function() {
                 this.vaultManager = new VaultManager({
                     autoUpdate: false
                 });
-                tmp.dir((err, dirPath, cleanup) => {
+                dir((err, dirPath, cleanup) => {
                     if (err) return document(err);
                     this.tmpDir = dirPath;
                     this.cleanup = cleanup;
@@ -79,7 +81,7 @@ describe("VaultManager", function() {
             });
 
             it("can merge differences between local and remote vaults", async function() {
-                const vaultPath = path.join(this.tmpDir, "vault.bcup");
+                const vaultPath = join(this.tmpDir, "vault.bcup");
                 // Init first
                 const creds = Credentials.fromDatasource(
                     {
diff --git a/test/integration/core/VaultSource.spec.js b/test/integration/core/VaultSource.spec.js
index c26f125..bba4b58 100644
--- a/test/integration/core/VaultSource.spec.js
+++ b/test/integration/core/VaultSource.spec.js
@@ -1,5 +1,5 @@
-const { expect } = require("chai");
-const {
+import { expect } from "chai";
+import {
     Credentials,
     MemoryDatasource,
     Vault,
@@ -9,7 +9,7 @@ const {
     VaultManager,
     VaultSource,
     setDefaultFormat
-} = require("../../../dist/index.node.js");
+} from "../../../dist/node/index.js";
 
 async function createTextSourceCredentials() {
     const memoryProperty = `test${Math.random()}`;
diff --git a/test/integration/datasources/FileDatasource.spec.js b/test/integration/datasources/FileDatasource.spec.js
index 5c01ef6..52ffd80 100644
--- a/test/integration/datasources/FileDatasource.spec.js
+++ b/test/integration/datasources/FileDatasource.spec.js
@@ -1,7 +1,8 @@
-const path = require("path");
-const { v4: uuid } = require("uuid");
-const tmp = require("tmp");
-const { Credentials, FileDatasource, Vault } = require("../../../dist/index.node.js");
+import { join } from "path";
+import { expect } from "chai";
+import { v4 as uuid } from "uuid";
+import { dir } from "tmp";
+import { Credentials, FileDatasource, Vault } from "../../../dist/node/index.js";
 
 describe("FileDatasource", function() {
     beforeEach(function(done) {
@@ -10,11 +11,11 @@ describe("FileDatasource", function() {
             .findGroupsByTitle("General")[0]
             .createEntry("Test")
             .setProperty("username", "test");
-        tmp.dir((err, dirPath, cleanup) => {
+        dir((err, dirPath, cleanup) => {
             if (err) return done(err);
             this.fds = new FileDatasource(
                 Credentials.fromDatasource({
-                    path: path.join(dirPath, "vault.bcup")
+                    path: join(dirPath, "vault.bcup")
                 })
             );
             this.cleanup = cleanup;
diff --git a/test/integration/datasources/MemoryDatasource.spec.js b/test/integration/datasources/MemoryDatasource.spec.js
index 5519020..022f29f 100644
--- a/test/integration/datasources/MemoryDatasource.spec.js
+++ b/test/integration/datasources/MemoryDatasource.spec.js
@@ -1,5 +1,6 @@
-const { v4: uuid } = require("uuid");
-const { Credentials, MemoryDatasource, Vault } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { v4 as uuid } from "uuid";
+import { Credentials, MemoryDatasource, Vault } from "../../../dist/node/index.js";
 
 describe("MemoryDatasource", function() {
     beforeEach(function() {
diff --git a/test/integration/env/appEnv.spec.js b/test/integration/env/appEnv.spec.js
index 62f2b47..b84d03b 100644
--- a/test/integration/env/appEnv.spec.js
+++ b/test/integration/env/appEnv.spec.js
@@ -1,4 +1,5 @@
-const { getSharedAppEnv } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { getSharedAppEnv } from "../../../dist/node/index.js";
 
 describe("appEnv", function() {
     describe("compression", function() {
diff --git a/test/integration/facades/entry.spec.js b/test/integration/facades/entry.spec.js
index f488e79..d07066a 100644
--- a/test/integration/facades/entry.spec.js
+++ b/test/integration/facades/entry.spec.js
@@ -1,4 +1,5 @@
-const {
+import { expect } from "chai";
+import {
     EntryPropertyValueType,
     Vault,
     VaultFormatA,
@@ -8,7 +9,7 @@ const {
     getEntryPropertyValueType,
     setEntryFacadePropertyValueType,
     setEntryPropertyValueType
-} = require("../../../dist/index.node.js");
+} from "../../../dist/node/index.js";
 
 describe("Entry facades", function() {
     [
diff --git a/test/integration/facades/vault.spec.js b/test/integration/facades/vault.spec.js
index 9b5d7a5..a0d7add 100644
--- a/test/integration/facades/vault.spec.js
+++ b/test/integration/facades/vault.spec.js
@@ -1,4 +1,5 @@
-const {
+import { expect } from "chai";
+import {
     Entry,
     Group,
     Vault,
@@ -9,7 +10,7 @@ const {
     createFieldDescriptor,
     createGroupFacade,
     createVaultFacade
-} = require("../../../dist/index.node.js");
+} from "../../../dist/node/index.js";
 
 describe("Vault facades", function() {
     [
diff --git a/test/integration/fileVaultManagement.spec.js b/test/integration/fileVaultManagement.spec.js
index 1614894..240797d 100644
--- a/test/integration/fileVaultManagement.spec.js
+++ b/test/integration/fileVaultManagement.spec.js
@@ -1,5 +1,6 @@
-const tmp = require("tmp");
-const { Credentials, VaultManager, VaultSource } = require("../../dist/index.node.js");
+import { expect } from "chai";
+import { fileSync } from "tmp";
+import { Credentials, VaultManager, VaultSource } from "../../dist/node/index.js";
 
 describe("VaultManager", function() {
     describe("with file datasource", function() {
@@ -8,7 +9,7 @@ describe("VaultManager", function() {
                 autoUpdate: false
             });
             // source 1
-            this.tmp1 = tmp.fileSync();
+            this.tmp1 = fileSync();
             const creds = Credentials.fromDatasource(
                 {
                     type: "file",
@@ -23,7 +24,7 @@ describe("VaultManager", function() {
                 initialiseRemote: true
             });
             // source 2
-            this.tmp2 = tmp.fileSync();
+            this.tmp2 = fileSync();
             const creds2 = Credentials.fromDatasource(
                 {
                     type: "file",
diff --git a/test/integration/formatA.spec.js b/test/integration/formatA.spec.js
index 3cf1f59..044a0ee 100644
--- a/test/integration/formatA.spec.js
+++ b/test/integration/formatA.spec.js
@@ -1,4 +1,5 @@
-const { Credentials, MemoryDatasource, Vault, VaultFormatA } = require("../../dist/index.node.js");
+import { expect } from "chai";
+import { Credentials, MemoryDatasource, Vault, VaultFormatA } from "../../dist/node/index.js";
 
 describe("Format A", function() {
     function reduceCommandsToPadIDs(output, cmd) {
diff --git a/test/integration/formatAToBConversion.spec.js b/test/integration/formatAToBConversion.spec.js
index 756d7c1..93749b5 100644
--- a/test/integration/formatAToBConversion.spec.js
+++ b/test/integration/formatAToBConversion.spec.js
@@ -1,5 +1,5 @@
-const { expect } = require("chai");
-const {
+import { expect } from "chai";
+import {
     Credentials,
     TextDatasource,
     Vault,
@@ -8,7 +8,7 @@ const {
     VaultManager,
     VaultSource,
     setDefaultFormat
-} = require("../../dist/index.node.js");
+} from "../../dist/node/index.js";
 
 async function createTextSourceCredentials() {
     const vault = new Vault();
diff --git a/test/integration/formatB.spec.js b/test/integration/formatB.spec.js
index 85f1ea5..f402b89 100644
--- a/test/integration/formatB.spec.js
+++ b/test/integration/formatB.spec.js
@@ -1,5 +1,6 @@
-const tmp = require("tmp");
-const { Credentials, FileDatasource, Group, Vault, VaultFormatB } = require("../../dist/index.node.js");
+import { expect } from "chai";
+import { fileSync } from "tmp";
+import { Credentials, FileDatasource, Group, Vault, VaultFormatB } from "../../dist/node/index.js";
 
 describe("Format B", function() {
     beforeEach(function() {
@@ -33,7 +34,7 @@ describe("Format B", function() {
     });
 
     it("can save and load to a file", async function() {
-        const tempFile = tmp.fileSync().name;
+        const tempFile = fileSync().name;
         const creds = Credentials.fromDatasource(
             {
                 type: "file",
diff --git a/test/integration/merging.spec.js b/test/integration/merging.spec.js
index fe8a6d7..e4d9eff 100644
--- a/test/integration/merging.spec.js
+++ b/test/integration/merging.spec.js
@@ -1,11 +1,12 @@
-const {
+import { expect } from "chai";
+import {
     Credentials,
     Vault,
     VaultFormatB,
     VaultManager,
     VaultSource,
     setDefaultFormat
-} = require("../../dist/index.node.js");
+} from "../../dist/node/index.js";
 
 describe("merging", function() {
     beforeEach(async function() {
diff --git a/test/integration/oldVaults.spec.js b/test/integration/oldVaults.spec.js
index 86383cc..1b0de04 100644
--- a/test/integration/oldVaults.spec.js
+++ b/test/integration/oldVaults.spec.js
@@ -1,19 +1,24 @@
-const fs = require("fs");
-const path = require("path");
-const { Credentials, FileDatasource, Vault } = require("../../dist/index.node.js");
+import { expect } from "chai";
+import { readdirSync } from "node:fs";
+import { dirname, join, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+import { Credentials, FileDatasource, Vault } from "../../dist/node/index.js";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
 
 describe("reading old vaults", function() {
     describe("Format A", function() {
-        const VAULTS_DIR = path.resolve(__dirname, "../resources/vaults/format-a");
+        const VAULTS_DIR = resolve(__dirname, "../resources/vaults/format-a");
         const PASSWORD = "this is a long password used for a test archive!";
 
-        fs.readdirSync(VAULTS_DIR).forEach(filename => {
+        readdirSync(VAULTS_DIR).forEach(filename => {
             const [, version] = /test-archive-(\d+\.\d+\.\d+(\-[0-9a-zA-Z.]+)?)\.bcup/.exec(filename);
             describe(`v${version}`, function() {
                 const readVault = () => {
                     const creds = Credentials.fromDatasource(
                         {
-                            path: path.join(VAULTS_DIR, filename)
+                            path: join(VAULTS_DIR, filename)
                         },
                         PASSWORD
                     );
@@ -33,16 +38,16 @@ describe("reading old vaults", function() {
     });
 
     describe("Format B", function() {
-        const VAULTS_DIR = path.resolve(__dirname, "../resources/vaults/format-b");
+        const VAULTS_DIR = resolve(__dirname, "../resources/vaults/format-b");
         const PASSWORD = "this is a long password used for a test vault!";
 
-        fs.readdirSync(VAULTS_DIR).forEach(filename => {
+        readdirSync(VAULTS_DIR).forEach(filename => {
             const [, version] = /test-vault-(\d+\.\d+\.\d+(\-[0-9a-zA-Z.]+)?)\.bcup/.exec(filename);
             describe(`v${version}`, function() {
                 const readVault = async () => {
                     const creds = Credentials.fromDatasource(
                         {
-                            path: path.join(VAULTS_DIR, filename)
+                            path: join(VAULTS_DIR, filename)
                         },
                         PASSWORD
                     );
diff --git a/test/integration/openCredentials.spec.js b/test/integration/openCredentials.spec.js
index ac0356e..ba008df 100644
--- a/test/integration/openCredentials.spec.js
+++ b/test/integration/openCredentials.spec.js
@@ -1,10 +1,5 @@
-const {
-    Credentials,
-    MemoryDatasource,
-    VaultManager,
-    VaultSource,
-    registerDatasource
-} = require("../../dist/index.node.js");
+import { expect } from "chai";
+import { Credentials, MemoryDatasource, VaultManager, VaultSource, registerDatasource } from "../../dist/node/index.js";
 
 class FakeDatasource extends MemoryDatasource {
     constructor(creds) {
diff --git a/test/integration/search/VaultEntrySearch.perf.spec.js b/test/integration/search/VaultEntrySearch.perf.spec.js
index 3a854bf..4143ee8 100644
--- a/test/integration/search/VaultEntrySearch.perf.spec.js
+++ b/test/integration/search/VaultEntrySearch.perf.spec.js
@@ -1,5 +1,6 @@
-const randomString = require("crypto-random-string");
-const { MemoryStorageInterface, Vault, VaultEntrySearch } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import randomString from "crypto-random-string";
+import { MemoryStorageInterface, Vault, VaultEntrySearch } from "../../../dist/node/index.js";
 
 const ENTRY_COUNT_MAX = 12;
 const ENTRY_COUNT_MIN = 5;
diff --git a/test/integration/search/VaultEntrySearch.spec.js b/test/integration/search/VaultEntrySearch.spec.js
index 02a20cc..99ad7ac 100644
--- a/test/integration/search/VaultEntrySearch.spec.js
+++ b/test/integration/search/VaultEntrySearch.spec.js
@@ -1,11 +1,5 @@
-const {
-    Entry,
-    EntryType,
-    Group,
-    MemoryStorageInterface,
-    Vault,
-    VaultEntrySearch
-} = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { Entry, EntryType, Group, MemoryStorageInterface, Vault, VaultEntrySearch } from "../../../dist/node/index.js";
 
 describe("VaultEntrySearch", function() {
     beforeEach(function() {
diff --git a/test/integration/search/VaultFacadeEntrySearch.spec.js b/test/integration/search/VaultFacadeEntrySearch.spec.js
index 5d006cb..0c37b1c 100644
--- a/test/integration/search/VaultFacadeEntrySearch.spec.js
+++ b/test/integration/search/VaultFacadeEntrySearch.spec.js
@@ -1,10 +1,11 @@
-const {
+import { expect } from "chai";
+import {
     Group,
     MemoryStorageInterface,
     Vault,
     VaultFacadeEntrySearch,
     createVaultFacade
-} = require("../../../dist/index.node.js");
+} from "../../../dist/node/index.js";
 
 describe("VaultFacadeEntrySearch", function() {
     beforeEach(function() {
diff --git a/test/integration/vaultRefs.spec.js b/test/integration/vaultRefs.spec.js
index 4b47691..a3f4a2f 100644
--- a/test/integration/vaultRefs.spec.js
+++ b/test/integration/vaultRefs.spec.js
@@ -1,11 +1,12 @@
-const {
+import { expect } from "chai";
+import {
     Credentials,
     VaultFormatA,
     VaultFormatB,
     VaultManager,
     VaultSource,
     setDefaultFormat
-} = require("../../dist/index.node.js");
+} from "../../dist/node/index.js";
 
 describe("Vault", function() {
     [
diff --git a/test/unit/core/Entry.spec.js b/test/unit/core/Entry.spec.js
index ccb2292..2fce271 100644
--- a/test/unit/core/Entry.spec.js
+++ b/test/unit/core/Entry.spec.js
@@ -1,4 +1,5 @@
-const {
+import { expect } from "chai";
+import {
     EntryChangeType,
     EntryPropertyValueType,
     Group,
@@ -7,8 +8,7 @@ const {
     VaultFormatB,
     VaultPermission,
     setDefaultFormat
-} = require("../../../dist/index.node.js");
-const { expect } = require("chai");
+} from "../../../dist/node/index.js";
 
 describe("core/Entry", function() {
     [
diff --git a/test/unit/core/Group.spec.js b/test/unit/core/Group.spec.js
index a781741..6779680 100644
--- a/test/unit/core/Group.spec.js
+++ b/test/unit/core/Group.spec.js
@@ -1,4 +1,5 @@
-const { Entry, Group, Vault, VaultPermission, generateUUID } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { Entry, Group, Vault, VaultPermission, generateUUID } from "../../../dist/node/index.js";
 
 describe("core/Group", function() {
     beforeEach(function() {
diff --git a/test/unit/core/Vault.spec.js b/test/unit/core/Vault.spec.js
index 26d37ca..f0abef7 100644
--- a/test/unit/core/Vault.spec.js
+++ b/test/unit/core/Vault.spec.js
@@ -1,5 +1,6 @@
-const { Group, Vault } = require("../../../dist/index.node.js");
-const { expect } = require("chai");
+import { expect } from "chai";
+import sinon from "sinon";
+import { Group, Vault } from "../../../dist/node/index.js";
 
 describe("core/Vault", function() {
     it("can be instantiated", function() {
diff --git a/test/unit/credentials/Credentials.spec.js b/test/unit/credentials/Credentials.spec.js
index 2e89f48..9fc20e0 100644
--- a/test/unit/credentials/Credentials.spec.js
+++ b/test/unit/credentials/Credentials.spec.js
@@ -1,4 +1,5 @@
-const { Credentials } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { Credentials } from "../../../dist/node/index.js";
 
 describe("credentials/Credentials", function() {
     beforeEach(function() {
diff --git a/test/unit/datasources/GoogleDriveDatasource.spec.js b/test/unit/datasources/GoogleDriveDatasource.spec.js
index 0073a09..6f6b796 100644
--- a/test/unit/datasources/GoogleDriveDatasource.spec.js
+++ b/test/unit/datasources/GoogleDriveDatasource.spec.js
@@ -1,5 +1,6 @@
-const { Credentials, GoogleDriveDatasource } = require("../../../dist/index.node.js");
-const { getCredentials } = require("../../../dist/credentials/channel.js");
+import { expect } from "chai";
+import { Credentials, GoogleDriveDatasource } from "../../../dist/node/index.js";
+import { getCredentials } from "../../../dist/node/credentials/channel.js";
 
 describe("datasources/GoogleDriveDatasource", function() {
     beforeEach(function() {
diff --git a/test/unit/facades/detection.spec.js b/test/unit/facades/detection.spec.js
index 1b83d51..fe0b6cd 100644
--- a/test/unit/facades/detection.spec.js
+++ b/test/unit/facades/detection.spec.js
@@ -1,4 +1,5 @@
-const { isVaultFacade } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { isVaultFacade } from "../../../dist/node/index.js";
 
 describe("facades/detection", function() {
     describe("isVaultFacade", function() {
diff --git a/test/unit/facades/entry.spec.js b/test/unit/facades/entry.spec.js
index b2a1d30..6291708 100644
--- a/test/unit/facades/entry.spec.js
+++ b/test/unit/facades/entry.spec.js
@@ -1,4 +1,5 @@
-const {
+import { expect } from "chai";
+import {
     Entry,
     EntryPropertyType,
     EntryType,
@@ -8,7 +9,7 @@ const {
     createFieldDescriptor,
     createVaultFacade,
     getEntryFacadePath
-} = require("../../../dist/index.node.js");
+} from "../../../dist/node/index.js";
 
 describe("facades/entry", function() {
     describe("createEntryFacade", function() {
diff --git a/test/unit/facades/tools.spec.js b/test/unit/facades/tools.spec.js
index 0ac7cd9..99e30de 100644
--- a/test/unit/facades/tools.spec.js
+++ b/test/unit/facades/tools.spec.js
@@ -1,10 +1,5 @@
-const {
-    Entry,
-    EntryPropertyValueType,
-    EntryType,
-    Vault,
-    createFieldDescriptor
-} = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { Entry, EntryPropertyValueType, EntryType, Vault, createFieldDescriptor } from "../../../dist/node/index.js";
 
 describe("facades/tools", function() {
     beforeEach(function() {
diff --git a/test/unit/facades/vault.spec.js b/test/unit/facades/vault.spec.js
index e2e4412..02f10af 100644
--- a/test/unit/facades/vault.spec.js
+++ b/test/unit/facades/vault.spec.js
@@ -1,4 +1,5 @@
-const { Vault, createGroupFacade, createVaultFacade } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { Vault, createGroupFacade, createVaultFacade } from "../../../dist/node/index.js";
 
 describe("facades/vault", function() {
     beforeEach(function() {
diff --git a/test/unit/io/formatA/VaultComparator.spec.js b/test/unit/io/formatA/VaultComparator.spec.js
index d52d1db..2976c06 100644
--- a/test/unit/io/formatA/VaultComparator.spec.js
+++ b/test/unit/io/formatA/VaultComparator.spec.js
@@ -1,5 +1,6 @@
-const VaultComparator = require("../../../../dist/io/formatA/VaultComparator.js").default;
-const { Vault } = require("../../../../dist/index.node.js");
+import { expect } from "chai";
+import VaultComparator from "../../../../dist/node/io/formatA/VaultComparator.js";
+import { Vault } from "../../../../dist/node/index.js";
 
 describe("core/VaultComparator", function() {
     beforeEach(function() {
diff --git a/test/unit/io/formatA/describe.spec.js b/test/unit/io/formatA/describe.spec.js
index 8827ce6..b91fb5e 100644
--- a/test/unit/io/formatA/describe.spec.js
+++ b/test/unit/io/formatA/describe.spec.js
@@ -1,6 +1,7 @@
-const { describeVaultDataset } = require("../../../../dist/io/formatA/describe");
-const { decodeStringValue, isEncoded } = require("../../../../dist/tools/encoding");
-const { Vault } = require("../../../../dist/index.node.js");
+import { expect } from "chai";
+import { describeVaultDataset } from "../../../../dist/node/io/formatA/describe.js";
+import { decodeStringValue, isEncoded } from "../../../../dist/node/tools/encoding.js";
+import { Vault } from "../../../../dist/node/index.js";
 
 function decodeHistory(lines) {
     return lines.map(line => {
diff --git a/test/unit/io/formatB/conversion.spec.js b/test/unit/io/formatB/conversion.spec.js
index c63f783..d3cda7b 100644
--- a/test/unit/io/formatB/conversion.spec.js
+++ b/test/unit/io/formatB/conversion.spec.js
@@ -1,22 +1,23 @@
-const nested = require("nested-property");
-const { convertFormatAEntry, convertFormatAGroup } = require("../../../../dist/io/formatB/conversion");
+import { expect } from "chai";
+import { get } from "nested-property";
+import { convertFormatAEntry, convertFormatAGroup } from "../../../../dist/node/io/formatB/conversion.js";
 
 describe("io/formatB/conversion", function() {
     function testEntry(actualPath, expectedPath) {
         it("sets correct entry ID", function() {
-            const actual = nested.get(this, actualPath);
-            const expected = nested.get(this, expectedPath);
+            const actual = get(this, actualPath);
+            const expected = get(this, expectedPath);
             expect(actual).to.have.property("id", expected.id);
         });
 
         it("sets correct entry parent ID", function() {
-            const actual = nested.get(this, actualPath);
-            const expected = nested.get(this, expectedPath);
+            const actual = get(this, actualPath);
+            const expected = get(this, expectedPath);
             expect(actual).to.have.property("g", expected.parentID);
         });
 
         it("sets correct entry attributes", function() {
-            const actual = nested.get(this, actualPath);
+            const actual = get(this, actualPath);
             expect(actual)
                 .to.have.property("a")
                 .that.is.an("object");
@@ -27,7 +28,7 @@ describe("io/formatB/conversion", function() {
         });
 
         it("sets correct entry properties", function() {
-            const actual = nested.get(this, actualPath);
+            const actual = get(this, actualPath);
             expect(actual)
                 .to.have.property("p")
                 .that.is.an("object");
@@ -40,19 +41,19 @@ describe("io/formatB/conversion", function() {
 
     function testGroup(actualPath, expectedPath) {
         it("sets correct group ID", function() {
-            const actual = nested.get(this, actualPath);
-            const expected = nested.get(this, expectedPath);
+            const actual = get(this, actualPath);
+            const expected = get(this, expectedPath);
             expect(actual).to.have.property("id", expected.id);
         });
 
         it("sets correct group title", function() {
-            const actual = nested.get(this, actualPath);
-            const expected = nested.get(this, expectedPath);
+            const actual = get(this, actualPath);
+            const expected = get(this, expectedPath);
             expect(actual).to.have.property("t", expected.title);
         });
 
         it("sets correct group attributes", function() {
-            const actual = nested.get(this, actualPath);
+            const actual = get(this, actualPath);
             expect(actual)
                 .to.have.property("a")
                 .that.is.an("object");
@@ -63,8 +64,8 @@ describe("io/formatB/conversion", function() {
         });
 
         it("sets correct group parent ID", function() {
-            const actual = nested.get(this, actualPath);
-            const expected = nested.get(this, expectedPath);
+            const actual = get(this, actualPath);
+            const expected = get(this, expectedPath);
             expect(actual).to.have.property("g", expected.parentID);
         });
     }
diff --git a/test/unit/tools/encoding.spec.js b/test/unit/tools/encoding.spec.js
index a43d798..f4eec5f 100644
--- a/test/unit/tools/encoding.spec.js
+++ b/test/unit/tools/encoding.spec.js
@@ -1,7 +1,7 @@
-const { base64ToBytes, bytesToBase64, decodeBase64String, encodeBase64String } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { base64ToBytes, bytesToBase64, decodeBase64String, encodeBase64String } from "../../../dist/node/index.js";
 
 const BASE64 = "VGVzdDogdGV4dCE=";
-const BASE64_BYTES = [];
 const RAW = "Test: text!";
 const RAW_BYTES = new Uint8Array([84, 101, 115, 116, 58, 32, 116, 101, 120, 116, 33]);
 
diff --git a/test/unit/tools/entry.spec.js b/test/unit/tools/entry.spec.js
index d3c6973..7834ecc 100644
--- a/test/unit/tools/entry.spec.js
+++ b/test/unit/tools/entry.spec.js
@@ -1,4 +1,5 @@
-const { Vault, getEntryPath } = require("../../../dist/index.node.js");
+import { expect } from "chai";
+import { Vault, getEntryPath } from "../../../dist/node/index.js";
 
 describe("tools/entry", function() {
     describe("getEntryPath", function() {
diff --git a/test/web/LocalStorageDatasource.spec.js b/test/web/LocalStorageDatasource.spec.js
index bc00a52..4313cab 100644
--- a/test/web/LocalStorageDatasource.spec.js
+++ b/test/web/LocalStorageDatasource.spec.js
@@ -1,5 +1,5 @@
 import { v4 as uuid } from "uuid";
-import { Credentials, LocalStorageDatasource, Vault } from "../../source/index.web";
+import { Credentials, LocalStorageDatasource, Vault } from "../../source/index.web.js";
 
 describe("LocalStorageDatasource", function() {
     beforeEach(function() {
diff --git a/test/web/MemoryDatasource.spec.js b/test/web/MemoryDatasource.spec.js
index 4846128..bec5148 100644
--- a/test/web/MemoryDatasource.spec.js
+++ b/test/web/MemoryDatasource.spec.js
@@ -1,5 +1,5 @@
 import { v4 as uuid } from "uuid";
-import { Credentials, MemoryDatasource, Vault } from "../../source/index.web";
+import { Credentials, MemoryDatasource, Vault } from "../../source/index.web.js";
 
 function arrayBuffersAreEqual(a, b) {
     return dataViewsAreEqual(new DataView(a), new DataView(b));
diff --git a/test/web/Vault.spec.js b/test/web/Vault.spec.js
index 782b514..636a2e7 100644
--- a/test/web/Vault.spec.js
+++ b/test/web/Vault.spec.js
@@ -1,4 +1,4 @@
-import { Group, Vault } from "../../source/index.web";
+import { Group, Vault } from "../../source/index.web.js";
 
 describe("Vault", function() {
     it("can be instantiated", function() {
diff --git a/test/web/VaultEntrySearch.spec.js b/test/web/VaultEntrySearch.spec.js
index 87ecfa2..c300355 100644
--- a/test/web/VaultEntrySearch.spec.js
+++ b/test/web/VaultEntrySearch.spec.js
@@ -1,4 +1,4 @@
-import { Group, MemoryStorageInterface, Vault, VaultEntrySearch } from "../../source/index.web";
+import { Group, MemoryStorageInterface, Vault, VaultEntrySearch } from "../../source/index.web.js";
 
 describe("VaultEntrySearch", function() {
     beforeEach(function() {
diff --git a/test/web/attachments.spec.js b/test/web/attachments.spec.js
index 811780e..7d7598a 100644
--- a/test/web/attachments.spec.js
+++ b/test/web/attachments.spec.js
@@ -4,9 +4,9 @@ import {
     MemoryStorageInterface,
     VaultManager,
     VaultSource
-} from "../../source/index.web";
+} from "../../source/index.web.js";
 
-const IMAGE_DATA = require("../resources/attachments/image.png");
+import IMAGE_DATA from "../resources/attachments/image.png";
 
 // compare ArrayBuffers
 function arrayBuffersAreEqual(a, b) {
diff --git a/test/web/env/appEnv.spec.js b/test/web/env/appEnv.spec.js
index e3d4c94..1a6cd6d 100644
--- a/test/web/env/appEnv.spec.js
+++ b/test/web/env/appEnv.spec.js
@@ -1,4 +1,4 @@
-import { getSharedAppEnv } from "../../../source/index.web";
+import { getSharedAppEnv } from "../../../source/index.web.js";
 
 describe("appEnv", function() {
     describe("compression", function() {
diff --git a/test/web/index.js b/test/web/index.js
index f864461..476b16b 100644
--- a/test/web/index.js
+++ b/test/web/index.js
@@ -1,6 +1,6 @@
 import { expect } from "chai";
-import { init } from "../../source/index.web";
-import { getSharedAppEnv } from "../../source/env/appEnv";
+import { init } from "../../source/index.web.js";
+import { getSharedAppEnv } from "../../source/env/appEnv.js";
 
 init();