Skip to content

Instantly share code, notes, and snippets.

@ckolderup
Created September 14, 2023 07:46
Show Gist options
  • Save ckolderup/f8cdaa9ee0742183c8cb95fc239cbae6 to your computer and use it in GitHub Desktop.
Save ckolderup/f8cdaa9ee0742183c8cb95fc239cbae6 to your computer and use it in GitHub Desktop.
From b5162620292f1c87907747c3a9f75b253c11cf1d Mon Sep 17 00:00:00 2001
From: Steve Bate <svc-github@stevebate.net>
Date: Wed, 13 Sep 2023 09:11:16 +0200
Subject: [PATCH] Tag intersection support (add/remove). Also supports RSS
feeds.
---
public/style.css | 18 ++++++++++++++++++
server.js | 11 ++++++++++-
src/bookmarks-db.js | 21 ++++++++++-----------
src/pages/partials/show_bookmark.hbs | 18 +++++++++++++++++-
src/pages/tagged.hbs | 2 +-
src/routes/core.js | 46 +++++++++++++++++++++++++++-------------------
6 files changed, 83 insertions(+), 33 deletions(-)
diff --git a/public/style.css b/public/style.css
index c23c60c..e0a50e4 100644
--- a/public/style.css
+++ b/public/style.css
@@ -274,6 +274,24 @@ p.error a:visited {
margin-bottom: 4px;
}
+.bookmark .tags .tag {
+ margin-right: 0.5rem;
+}
+
+.bookmark .tags .tag-op {
+ font-weight: bold;
+}
+
+.bookmark .tags .tag-add {
+ color: green;
+ font-size: 1.1rem;
+}
+
+.bookmark .tags .tag-remove {
+ color: red;
+ font-size: 1.5rem;
+}
+
textarea {
display: block;
height: 192px;
diff --git a/server.js b/server.js
index 62f6b4a..74b3490 100644
--- a/server.js
+++ b/server.js
@@ -86,7 +86,16 @@ const hbs = create({
},
mastodonAccount() {
return process.env.MASTODON_ACCOUNT;
- }
+ },
+ ifIn(item, array, options) {
+ return array.indexOf(item) >= 0 ? options.fn(this) : options.inverse(this);
+ },
+ removeTag(tag, path) {
+ return path.split('/').filter(x => x !== tag).join('/');
+ },
+ ifThisTag(tag, path, options) {
+ return path === `/tagged/${tag}` ? options.fn(this) : options.inverse(this);
+ },
},
partialsDir: "./src/pages/partials",
extname: ".hbs",
diff --git a/src/bookmarks-db.js b/src/bookmarks-db.js
index 68a7a32..7680f35 100644
--- a/src/bookmarks-db.js
+++ b/src/bookmarks-db.js
@@ -90,7 +90,7 @@ We're using the sqlite wrapper so that we can make async / await connections
});
function massageBookmark(bookmark) {
- return addBookmarkDomain(addLinkedTags(insertRelativeTimestamp(bookmark)));
+ return addBookmarkDomain(addTags(insertRelativeTimestamp(bookmark)));
}
function massageComment(comment) {
@@ -121,12 +121,9 @@ function generateLinkedDisplayName(comment) {
return { linked_display_name: `<a href="http://${match[2]}/@${match[1]}">${match[1]}</a>`, ...comment};
}
-function addLinkedTags(bookmark) {
- const linkedTags = bookmark.tags?.split(' ').map(t => t.slice(1)).map((t) => {
- return `<a href="/tagged/${t}">#${t}</a>`;
- });
-
- return { linkedTags, ...bookmark };
+function addTags(bookmark) {
+ const tagNames = bookmark.tags?.split(' ').map(t => t.slice(1)).sort();
+ return { tagNames, ...bookmark };
}
export async function getBookmarkCount() {
@@ -145,15 +142,17 @@ export async function getBookmarks(limit=10, offset=0) {
}
}
-export async function getBookmarkCountForTag(tag) {
- const result = await db.get("SELECT count(id) as count FROM bookmarks WHERE tags LIKE ? OR tags LIKE ?", `%#${tag} %`, `%#${tag}`);
+export async function getBookmarkCountForTags(tags) {
+ const tagClauses = tags.map(tag => `(tags like '%${tag}%' OR tags like '%${tag}%')`).join(' AND ');
+ const result = await db.get(`SELECT count(id) as count from bookmarks WHERE ${tagClauses}`);
return result?.count;
}
-export async function getBookmarksForTag(tag, limit=10, offset=0) {
+export async function getBookmarksForTags(tags, limit = 10, offset = 0) {
// We use a try catch block in case of db errors
try {
- const results = await db.all("SELECT * from bookmarks WHERE tags LIKE ? OR tags LIKE ? ORDER BY updated_at DESC LIMIT ? OFFSET ?", `%#${tag} %`, `%#${tag}`, limit, offset);
+ const tagClauses = tags.map(tag => `(tags like '%${tag}%' OR tags like '%${tag}%')`).join(' AND ');
+ const results = await db.all(`SELECT * from bookmarks WHERE ${tagClauses} ORDER BY updated_at DESC LIMIT ? OFFSET ?`, limit, offset);
return results.map(b => massageBookmark(b));
} catch (dbError) {
// Database connection error
diff --git a/src/pages/partials/show_bookmark.hbs b/src/pages/partials/show_bookmark.hbs
index 3d06123..f4686c2 100644
--- a/src/pages/partials/show_bookmark.hbs
+++ b/src/pages/partials/show_bookmark.hbs
@@ -10,7 +10,23 @@
{{{htmlize bookmark.description}}}
</div>
<div class="tags">
- {{#each bookmark.linkedTags}}{{{this}}} {{/each}}
+ {{#if tagged }}
+ {{#each bookmark.tagNames}}
+ {{#ifIn this @root.pathTags~}}
+ <span class="tag tag-disabled">#{{this}}
+ {{~#ifThisTag this @root.path }}
+ {{~else}}&nbsp;<a href="{{removeTag this @root.path}}" class="tag-op tag-remove">-</a>
+ {{/ifThisTag~}}
+ </span>
+ {{else~}}
+ <span class="tag"><a href="/tagged/{{this}}">#{{this}}</a>&nbsp;<a href="{{@root.path}}/{{this}}" class="tag-op tag-add">+</a></span>
+ {{/ifIn}}
+ {{/each}}
+ {{else}}
+ {{#each bookmark.tagNames}}
+ <span class="tag"><a href="/tagged/{{this}}">#{{this}}</a></span>
+ {{/each}}
+ {{/if}}
</div>
<div class="bottom">
{{#if bookmark.comment_count includeZero=true}}
diff --git a/src/pages/tagged.hbs b/src/pages/tagged.hbs
index e5582d8..e348300 100644
--- a/src/pages/tagged.hbs
+++ b/src/pages/tagged.hbs
@@ -5,7 +5,7 @@
{{else}}
<div class="bookmarks">
{{#each bookmarks }}
- {{> show_bookmark bookmark=this }}
+ {{> show_bookmark bookmark=this tagged=true }}
{{/each}}
</div>
{{> pagination url="" pageInfo=pageInfo }}
diff --git a/src/routes/core.js b/src/routes/core.js
index 2dcb13a..a8c057b 100644
--- a/src/routes/core.js
+++ b/src/routes/core.js
@@ -92,11 +92,13 @@ router.get("/index.xml", async (req, res) => {
return res.render("bookmarks-xml", params);
});
-router.get("/tagged/:tag.xml", async (req, res) => {
+router.get("/tagged/*.xml", async (req, res) => {
+ const tags = req.params[0].split('/');
+
let params = {};
const bookmarksDb = req.app.get('bookmarksDb');
- const bookmarks = await bookmarksDb.getBookmarksForTag(req.params.tag, 20, 0);
+ const bookmarks = await bookmarksDb.getBookmarksForTags(tags, 20, 0);
if (!bookmarks) params.error = data.errorMessage;
@@ -110,32 +112,33 @@ router.get("/tagged/:tag.xml", async (req, res) => {
params.last_updated = bookmarks[0].created_at;
}
- params.feedTitle = `${req.app.get('site_name')}: Bookmarks tagged '${req.params.tag}'`;
+ params.feedTitle = `${req.app.get('site_name')}: Bookmarks tagged '${tags.join(' and ')}'`;
params.layout = false;
res.type('application/atom+xml');
return res.render("bookmarks-xml", params);
});
-router.get("/tagged/:tag", async (req, res) => {
+router.get("/tagged/*", async (req, res) => {
+ const tags = req.params[0].split('/');
+
let params = {};
const bookmarksDb = req.app.get('bookmarksDb');
+ let buildTitle = `Bookmarks tagged ${tags.join(' and ')}`;
+
+ const title = buildTitle;
+
const limit = Math.max(req.query?.limit || 10, 1);
const offset = Math.max(req.query?.offset || 0, 0);
- const totalBookmarkCount = await bookmarksDb.getBookmarkCountForTag(req.params.tag);
+ const bookmarks = await bookmarksDb.getBookmarksForTags(tags, limit, offset);
+
+ const totalBookmarkCount = await bookmarksDb.getBookmarkCountForTags(tags);
const currentPage = (limit + offset) / limit;
const totalPages = Math.ceil(totalBookmarkCount / limit);
-
- let buildTitle = `Bookmarks tagged ${req.params.tag}`;
if (totalPages > 1) {
buildTitle += ` (page ${currentPage} of ${totalPages})`;
}
- const title = buildTitle;
-
- params.tags = await bookmarksDb.getTags();
-
- const bookmarks = await bookmarksDb.getBookmarksForTag(req.params.tag, limit, offset);
// Check in case the data is empty or not setup yet
if (bookmarks && bookmarks.length < 1) {
@@ -144,14 +147,19 @@ router.get("/tagged/:tag", async (req, res) => {
params.bookmarks = bookmarks;
}
- params.tag = req.params.tag;
+ params.tags = await bookmarksDb.getTags();
+ params.feed = req.path;
params.title = title;
- params.pageInfo = { currentPage, totalPages, offset, limit,
- hasPreviousPage: currentPage > 1,
- hasNextPage: currentPage < totalPages,
- nextOffset: Math.min(offset + limit, totalPages * limit - limit),
- previousOffset: Math.max(offset - limit, 0)
- };
+ params.pageInfo = {
+ currentPage, totalPages, offset, limit,
+ hasPreviousPage: currentPage > 1,
+ hasNextPage: currentPage < totalPages,
+ nextOffset: Math.min(offset + limit, totalPages * limit - limit),
+ previousOffset: Math.max(offset - limit, 0)
+ };
+
+ params.path = req.path;
+ params.pathTags = req.path.split('/').slice(2);
// Send the page options or raw JSON data if the client requested it
return req.query.raw
From 65ac5d90cda964d9aa79c4c8caa2159668be1edf Mon Sep 17 00:00:00 2001
From: Steve Bate <svc-github@stevebate.net>
Date: Wed, 13 Sep 2023 16:56:27 +0200
Subject: [PATCH] Modify query to use params for more security.
---
src/bookmarks-db.js | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/bookmarks-db.js b/src/bookmarks-db.js
index 7680f35..12ce8bc 100644
--- a/src/bookmarks-db.js
+++ b/src/bookmarks-db.js
@@ -143,16 +143,21 @@ export async function getBookmarks(limit=10, offset=0) {
}
export async function getBookmarkCountForTags(tags) {
- const tagClauses = tags.map(tag => `(tags like '%${tag}%' OR tags like '%${tag}%')`).join(' AND ');
- const result = await db.get(`SELECT count(id) as count from bookmarks WHERE ${tagClauses}`);
+ const tagClauses = tags.map(tag => `(tags like ? OR tags like ?)`).join(' AND ');
+ const tagParams = tags.map(tag => [`%${tag}% `, `%${tag}%`]).flat();
+ const result = await db.get.apply(db, [`SELECT count(id) as count from bookmarks WHERE ${tagClauses}`, ...tagParams]);
return result?.count;
}
export async function getBookmarksForTags(tags, limit = 10, offset = 0) {
// We use a try catch block in case of db errors
try {
- const tagClauses = tags.map(tag => `(tags like '%${tag}%' OR tags like '%${tag}%')`).join(' AND ');
- const results = await db.all(`SELECT * from bookmarks WHERE ${tagClauses} ORDER BY updated_at DESC LIMIT ? OFFSET ?`, limit, offset);
+ const tagClauses = tags.map(tag => `(tags like ? OR tags like ?)`).join(' AND ');
+ const tagParams = tags.map(tag => [`%${tag}% `, `%${tag}%`]).flat();
+ const results = await db.all.apply(db, [
+ `SELECT * from bookmarks WHERE ${tagClauses} ORDER BY updated_at DESC LIMIT ? OFFSET ?`,
+ ...tagParams, limit, offset
+ ]);
return results.map(b => massageBookmark(b));
} catch (dbError) {
// Database connection error
From de998481efe5c5b167aeb9bbf0ede096e399d39d Mon Sep 17 00:00:00 2001
From: Casey Kolderup <casey@kolderup.org>
Date: Thu, 14 Sep 2023 00:17:38 -0700
Subject: [PATCH] Merge branch 'main' into pr/96
---
.eslintrc.json | 27 +
.gitignore | 3 +-
.prettierrc | 6 +
.vscode/extensions.json | 5 +
.vscode/settings.json | 15 +
FEDERATION.md | 18 +-
LICENSE.md | 114 ++-
README.md | 47 +-
package-lock.json | 4995 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
package.json | 19 +-
public/style.css | 46 +-
public/tag-autocomplete.js | 89 +-
server.js | 102 ++-
src/activity-pub-db.js | 331 ++++----
src/activitypub.js | 236 ++----
src/bookmarks-db.js | 310 +++----
src/pages/about.hbs | 67 +-
src/pages/add_bookmark.hbs | 2 +-
src/pages/admin/data.hbs | 5 +-
src/pages/admin/followers.hbs | 21 +-
src/pages/admin/following.hbs | 11 +-
src/pages/admin/index.hbs | 50 +-
src/pages/bookmark.hbs | 2 +-
src/pages/index.hbs | 2 +-
src/pages/layouts/main.hbs | 3 +
src/pages/network.hbs | 7 +-
src/pages/nonfederated.hbs | 4 +-
src/pages/partials/admin_subnav.hbs | 2 +-
src/pages/partials/edit_bookmark.hbs | 43 +-
src/pages/partials/pagination.hbs | 4 +-
src/pages/partials/show_bookmark.hbs | 12 +-
src/pages/partials/show_comment.hbs | 12 +-
src/pages/partials/tag_list.hbs | 6 +-
src/pages/search.hbs | 16 +
src/pages/tagged.hbs | 2 +-
src/routes/activitypub/inbox.js | 255 +++---
src/routes/activitypub/message.js | 24 +-
src/routes/activitypub/nodeinfo.js | 42 +-
src/routes/activitypub/user.js | 169 ++--
src/routes/activitypub/webfinger.js | 26 +-
src/routes/admin.js | 228 +++--
src/routes/auth.js | 16 +-
src/routes/bookmark.js | 158 ++--
src/routes/comment.js | 12 +-
src/routes/core.js | 121 ++-
src/routes/index.js | 20 +-
src/session-auth.js | 31 +-
src/signature.js | 171 ++++
src/util.js | 104 ++-
49 files changed, 6301 insertions(+), 1710 deletions(-)
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..1b06db5
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,27 @@
+{
+ "env": {
+ "browser": true,
+ "es2021": true
+ },
+ "extends": [
+ "airbnb-base",
+ "plugin:node/recommended",
+ "plugin:prettier/recommended"
+ ],
+ "parserOptions": {
+ "ecmaVersion": "latest",
+ "sourceType": "module"
+ },
+ "rules": {
+ "no-unused-vars": "warn",
+ "no-console": "off",
+ "no-underscore-dangle": "off",
+ "func-names": "off",
+ "import/extensions": [
+ "error",
+ {
+ "js": "ignorePackages"
+ }
+ ]
+ }
+}
diff --git a/.gitignore b/.gitignore
index 2f77be6..2a0462f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,8 @@
.glitchdotcom.json
.node-gyp
node_modules
-.data/*.db
+.data/*
+!.data/.keep
.env
.sqlite_history
*.patch
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..c78e14e
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "arrowParens": "always",
+ "printWidth": 150,
+ "singleQuote": true,
+ "trailingComma": "all"
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..26454ac
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "esbenp.prettier-vscode"
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..34ceb5f
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,15 @@
+{
+ "eslint.lintTask.enable": true,
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnPaste": false,
+ "editor.formatOnType": false,
+ "editor.formatOnSave": true,
+ "editor.formatOnSaveMode": "modificationsIfAvailable",
+ "files.autoSave": "onFocusChange",
+ "[javascript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[handlebars]": {
+ "files.insertFinalNewline": true
+ }
+}
diff --git a/FEDERATION.md b/FEDERATION.md
index eae6508..b5ce65e 100644
--- a/FEDERATION.md
+++ b/FEDERATION.md
@@ -16,12 +16,12 @@
### Object Model
-| Object Kind | Description |
-|-------------|-------------|
-| Actor | The single actor associated with the Postmarks instance. |
-| Bookmark | A Postmarks bookmark (`Note`) |
-| Comment | A comment on a bookmark (`Note`) |
-| Message | An ActivityPub `Note` published by the Actor |
+| Object Kind | Description |
+| ----------- | -------------------------------------------------------- |
+| Actor | The single actor associated with the Postmarks instance. |
+| Bookmark | A Postmarks bookmark (`Note`) |
+| Comment | A comment on a bookmark (`Note`) |
+| Message | An ActivityPub `Note` published by the Actor |
### Actor
@@ -122,6 +122,6 @@ This will retrieve a `Note` published using ActivityPub. Currently, the publishe
## Additional documentation
-* [Source Code](https://github.com/ckolderup/postmarks)
-* [Ethos](https://casey.kolderup.org/notes/edf3a659f52528da103ea4dcbb09f66f.html)
-* [Future Ideas](https://casey.kolderup.org/notes/9307f6d67bbfedbd215ae2d09caeab39.html)
\ No newline at end of file
+- [Source Code](https://github.com/ckolderup/postmarks)
+- [Ethos](https://casey.kolderup.org/notes/edf3a659f52528da103ea4dcbb09f66f.html)
+- [Future Ideas](https://casey.kolderup.org/notes/9307f6d67bbfedbd215ae2d09caeab39.html)
diff --git a/LICENSE.md b/LICENSE.md
index 1f664f1..f646164 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -3,9 +3,12 @@ project, copyright information for the project, and the legal terms under
which it's being shared. In other words, this is us using an MIT license to
say "we wrote this and you can do whatever you want with it."
-******************************************************************************
+---
+
Postmarks
-******************************************************************************
+
+---
+
MIT License
Copyright (c) 2021, Glitch, Inc.
@@ -28,14 +31,10 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-
-
-
-******************************************************************************
+---
THIRD-PARTY SOFTWARE
-
1. fastify: Fastify is a web framework focused on developer experience.
2. fastify/static: Plugin for serving static files as fast as possible.
3. handlebars.js: Minimal templating on steroids.
@@ -43,12 +42,14 @@ THIRD-PARTY SOFTWARE
5. HK Grotesk: The font we're using.
6. SQLite: The database management system.
+---
-******************************************************************************
1. fastify (also applies to fastify-formbody)
-URL: https://www.fastify.io/
- https://github.com/fastify/fastify
-******************************************************************************
+ URL: https://www.fastify.io/
+ https://github.com/fastify/fastify
+
+---
+
MIT License
Copyright (c) 2016-2020 The Fastify Team
@@ -73,15 +74,20 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-******************************************************************************
+
+---
+
END, fastify
-******************************************************************************
+---
+
+---
-******************************************************************************
2. fastify/static
-URL: https://github.com/fastify/fastify-static
-******************************************************************************
+ URL: https://github.com/fastify/fastify-static
+
+---
+
MIT License
Copyright (c) 2017-2018 Fastify
@@ -103,16 +109,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-******************************************************************************
+
+---
+
END, fastify/static
-******************************************************************************
+---
+
+---
-******************************************************************************
3. handlebars.js
-URL: https://handlebarsjs.com/
- https://github.com/handlebars-lang/handlebars.js
-******************************************************************************
+ URL: https://handlebarsjs.com/
+ https://github.com/handlebars-lang/handlebars.js
+
+---
+
Copyright (C) 2011-2019 by Yehuda Katz
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -132,15 +143,20 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-******************************************************************************
+
+---
+
END, fastify-static
-******************************************************************************
+---
+
+---
-******************************************************************************
4. fastify/view
-URL: https://github.com/fastify/point-of-view
-******************************************************************************
+ URL: https://github.com/fastify/point-of-view
+
+---
+
MIT License
Copyright (c) 2017 Fastify
@@ -162,35 +178,47 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-******************************************************************************
+
+---
+
END, fastify/view
-******************************************************************************
+---
+
+---
-******************************************************************************
5. HK Grotesk
-URL: https://hanken.co/products/hk-grotesk
-******************************************************************************
+ URL: https://hanken.co/products/hk-grotesk
+
+---
+
HK Grotesk was designed by Hanken Design Co. It is shared using a SIL OFL
license. Full license text can be found at:
https://hanken.co/pages/web-fonts-eula
-******************************************************************************
+---
+
END, HK Grotesk
-******************************************************************************
+---
+
+---
-******************************************************************************
6. SQLite
-URL: https://www.sqlite.org
-******************************************************************************
-The author disclaims copyright to this source code. In place of
+ URL: https://www.sqlite.org
+
+---
+
+The author disclaims copyright to this source code. In place of
a legal notice, here is a blessing:
- * May you do good and not evil.
- * May you find forgiveness for yourself and forgive others.
- * May you share freely, never taking more than you give.
-******************************************************************************
+- May you do good and not evil.
+- May you find forgiveness for yourself and forgive others.
+- May you share freely, never taking more than you give.
+
+---
+
END, SQLite
-******************************************************************************
+
+---
diff --git a/README.md b/README.md
index 9002eb4..d8e245c 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@
Postmarks is a bookmarking site that you own yourself and can connect the Fediverse, interacting with other Postmarks sites as well as Mastodon/FireFish/any text-based ActivityPub platform. You can read more about it here:
-* [Getting Started](https://casey.kolderup.org/notes/b059694f5064c6c6285075c894a72317.html)
-* [Ethos](https://casey.kolderup.org/notes/edf3a659f52528da103ea4dcbb09f66f.html)
-* [Future Ideas](https://casey.kolderup.org/notes/9307f6d67bbfedbd215ae2d09caeab39.html)
+- [Getting Started](https://casey.kolderup.org/notes/b059694f5064c6c6285075c894a72317.html)
+- [Ethos](https://casey.kolderup.org/notes/edf3a659f52528da103ea4dcbb09f66f.html)
+- [Future Ideas](https://casey.kolderup.org/notes/9307f6d67bbfedbd215ae2d09caeab39.html)
The site allows the owner to add, edit and delete bookmarks, but only if a valid login is provided.
Check the setup below to understand how to do that!
@@ -15,35 +15,36 @@ Check the setup below to understand how to do that!
To set your app up:
-* If you're using Glitch:
- * Rename your project immediately in the project settings, if you intend to be called something else. This determines the domain that your site lives at, which also determines the second half of your `@username@project-name.glitch.me` identity on the fediverse. NOTE: If you change this later, you will break the connection any existing followers have to your site, they'll have to re-follow the account on its new domain (and depending on the software they're following from, may even prevent them from unfollowing the old URL 😱)
- * In your `.env` editor, create a key `ADMIN_KEY` and give it a text string as a value. This is your "password" when your browser prompts you, so make it as secure as you need to protect your data.
- * Add another key to your .env called `SESSION_SECRET` and generate a random string for its value. This is your [session secret](http://expressjs.com/en/resources/middleware/session.html#secret), used to generate the hashed version of your session that gets encoded with the cookies used to store your login. If you make this string too easily guessable, you make it easier for someone to hijack your session and gain unauthorized login. Also, if you ever change this string, it will invalidate all existing cookies.
- * Edit the contents of `account.json.example` to set your `@username`, display name, bio, and avatar. (If you don't set a username, your default actor name will be 'bookmarks', so people will find you on the fediverse `@bookmarks@project-name.glitch.me`.)
- * THEN: either rename `account.json.example` to `account.json`, or copy the contents into a new file called `account.json`. Whatever `username` you have in this file when the project first starts you'll need to retain or else you'll break your followers' connection to this account.
-* Otherwise:
- * Set up your domain by editing `src/util.js` and making the definition of `export const domain` return a string that is your domain. Fun, huh?
- * Create a `.env` file in the root of the project.
- * Add the line `ADMIN_KEY=<key>` to your .env where \<key\> is the password you'll enter when the browser prompts you, and another line for `SESSION_SECRET=<secret>` where \<secret\> is a random string used when hashing your session for use in a secure cookie.
- * Make a file called `account.json` in the project root. Copy the contents of `account.json.example` into it and edit the values to set your `@username`, display name, bio, and avatar. (If you don't set a username, your default actor name will be 'bookmarks', so people will find you on the fediverse `@bookmarks@project-name.glitch.me`.)
-* If you're using Glitch, you should be done! If you're running this yourself, run `npm run start` via whatever mechanism you choose to use to host this website.
-* Click on the __Admin__ link in the footer, and enter the password (whatever you set ADMIN_KEY to in the .env).
-* You should be logged in, at which point you can configure various settings, import bookmarks, and use the "Add" links in the header and footer (as well as the bookmarklet, available in the Admin section) to save new bookmarks.
+- If you're using Glitch:
+ - Rename your project immediately in the project settings, if you intend to be called something else. This determines the domain that your site lives at, which also determines the second half of your `@username@project-name.glitch.me` identity on the fediverse. NOTE: If you change this later, you will break the connection any existing followers have to your site, they'll have to re-follow the account on its new domain (and depending on the software they're following from, may even prevent them from unfollowing the old URL 😱)
+ - In your `.env` editor, create a key `ADMIN_KEY` and give it a text string as a value. This is your "password" when your browser prompts you, so make it as secure as you need to protect your data.
+ - Add another key to your .env called `SESSION_SECRET` and generate a random string for its value. This is your [session secret](http://expressjs.com/en/resources/middleware/session.html#secret), used to generate the hashed version of your session that gets encoded with the cookies used to store your login. If you make this string too easily guessable, you make it easier for someone to hijack your session and gain unauthorized login. Also, if you ever change this string, it will invalidate all existing cookies.
+ - Edit the contents of `account.json.example` to set your `@username`, display name, bio, and avatar. (If you don't set a username, your default actor name will be 'bookmarks', so people will find you on the fediverse `@bookmarks@project-name.glitch.me`.)
+ - THEN: either rename `account.json.example` to `account.json`, or copy the contents into a new file called `account.json`. Whatever `username` you have in this file when the project first starts you'll need to retain or else you'll break your followers' connection to this account.
+- Otherwise:
+ - Set up your domain by editing `src/util.js` and making the definition of `export const domain` return a string that is your domain. Fun, huh?
+ - Create a `.env` file in the root of the project.
+ - Add the line `ADMIN_KEY=<key>` to your .env where \<key\> is the password you'll enter when the browser prompts you, and another line for `SESSION_SECRET=<secret>` where \<secret\> is a random string used when hashing your session for use in a secure cookie.
+ - Make a file called `account.json` in the project root. Copy the contents of `account.json.example` into it and edit the values to set your `@username`, display name, bio, and avatar. (If you don't set a username, your default actor name will be 'bookmarks', so people will find you on the fediverse `@bookmarks@project-name.glitch.me`.)
+- If you're using Glitch, you should be done! If you're running this yourself, run `npm run start` via whatever mechanism you choose to use to host this website.
+- Click on the **Admin** link in the footer, and enter the password (whatever you set ADMIN_KEY to in the .env).
+- You should be logged in, at which point you can configure various settings, import bookmarks, and use the "Add" links in the header and footer (as well as the bookmarklet, available in the Admin section) to save new bookmarks.
## Mastodon Verification
Setting `MASTODON_ACCOUNT` in the `.env` file will cause a link to be added to the Postmarks home page that can be used for verification with your Mastodon account. See the [Mastodon documentation](https://docs.joinmastodon.org/user/profile/#verification) for more details.
+
## Developing Postmarks
-* To automatically log all requests to a text file, set add `LOGGING_ENABLED=true` to your .env file. This will cause all incoming requests to append to `request_log.txt` in your project folder.
+- To automatically log all requests to a text file, set add `LOGGING_ENABLED=true` to your .env file. This will cause all incoming requests to append to `request_log.txt` in your project folder.
## Acknowledgments
-* The "Postmarks" name is compliments of [Casey C](https://sowe.li) (no relation to Casey K), who brainstormed dozens of ideas for the name when Casey was first trying to rename the project. Thank you!
-* Postmarks (in its default configuration) uses an edited version of Eynav Raphael's ["Postmark Stamp"](https://thenounproject.com/icon/postmark-stamp-928917/) icon from The Noun Project.
-* It also makes use of free fonts including [Averia Sans](http://iotic.com/averia/) and [Public Sans](https://public-sans.digital.gov/).
-* Much of the original form of the site's frontend is lifted from the starter projects available on [Glitch](https://glitch.com). Thank you to all the people who have contributed to those projects over the years!
-* Much of the original backend of the site is based off of Darius Kazemi's [express-activitypub](https://github.com/dariusk/express-activitypub) repo. I made a point not to just clone his repo from the start, but then ended up retyping most of it as I learned how things work. While some pieces have been upgraded, much of Darius' work creates the foundation for Postmarks' ActivityPub functionality.
+- The "Postmarks" name is compliments of [Casey C](https://sowe.li) (no relation to Casey K), who brainstormed dozens of ideas for the name when Casey was first trying to rename the project. Thank you!
+- Postmarks (in its default configuration) uses an edited version of Eynav Raphael's ["Postmark Stamp"](https://thenounproject.com/icon/postmark-stamp-928917/) icon from The Noun Project.
+- It also makes use of free fonts including [Averia Sans](http://iotic.com/averia/) and [Public Sans](https://public-sans.digital.gov/).
+- Much of the original form of the site's frontend is lifted from the starter projects available on [Glitch](https://glitch.com). Thank you to all the people who have contributed to those projects over the years!
+- Much of the original backend of the site is based off of Darius Kazemi's [express-activitypub](https://github.com/dariusk/express-activitypub) repo. I made a point not to just clone his repo from the start, but then ended up retyping most of it as I learned how things work. While some pieces have been upgraded, much of Darius' work creates the foundation for Postmarks' ActivityPub functionality.
## We built this with Glitch!
diff --git a/package-lock.json b/package-lock.json
index 54ec7b3..f9d3893 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27,10 +27,104 @@
"string-strip-html": "^13.4.2"
},
"devDependencies": {
- "nodemon": "^2.0.20"
+ "eslint": "^8.43.0",
+ "eslint-config-airbnb-base": "^15.0.0",
+ "eslint-config-prettier": "^9.0.0",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-prettier": "^5.0.0",
+ "nodemon": "^2.0.20",
+ "prettier": "^3.0.3"
},
"engines": {
- "node": "16.x"
+ "node": ">=16.x"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz",
+ "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz",
+ "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@gar/promisify": {
@@ -39,6 +133,62 @@
"integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
"optional": true
},
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz",
@@ -105,6 +255,41 @@
"node": ">=10"
}
},
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@npmcli/fs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
@@ -144,6 +329,26 @@
"node": ">=10"
}
},
+ "node_modules/@pkgr/utils": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
+ "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "fast-glob": "^3.3.0",
+ "is-glob": "^4.0.3",
+ "open": "^9.1.0",
+ "picocolors": "^1.0.0",
+ "tslib": "^2.6.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/@sindresorhus/is": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
@@ -180,6 +385,13 @@
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
},
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "peer": true
+ },
"node_modules/@types/lodash": {
"version": "4.14.197",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz",
@@ -198,6 +410,27 @@
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -289,6 +522,22 @@
"node": ">=8"
}
},
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -297,6 +546,21 @@
"node": ">=8"
}
},
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -327,11 +591,150 @@
"node": ">=10"
}
},
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+ "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-array-buffer": "^3.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz",
+ "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz",
+ "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0",
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz",
+ "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.0",
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1",
+ "is-array-buffer": "^3.0.2",
+ "is-shared-array-buffer": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/big-integer": {
+ "version": "1.6.51",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+ "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -382,6 +785,18 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
+ "node_modules/bplist-parser": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
+ "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
+ "dev": true,
+ "dependencies": {
+ "big-integer": "^1.6.44"
+ },
+ "engines": {
+ "node": ">= 5.10.0"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -403,6 +818,21 @@
"node": ">=8"
}
},
+ "node_modules/bundle-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
+ "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
+ "dev": true,
+ "dependencies": {
+ "run-applescript": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -509,6 +939,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/chalk": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
@@ -616,6 +1055,24 @@
"node": ">=14.18.0"
}
},
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
"node_modules/color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@@ -629,6 +1086,12 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
+ "node_modules/confusing-browser-globals": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+ "dev": true
+ },
"node_modules/connect-sqlite3": {
"version": "0.9.13",
"resolved": "https://registry.npmjs.org/connect-sqlite3/-/connect-sqlite3-0.9.13.tgz",
@@ -694,6 +1157,20 @@
"node": ">= 0.8"
}
},
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
@@ -762,6 +1239,46 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/default-browser": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
+ "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
+ "dev": true,
+ "dependencies": {
+ "bundle-name": "^3.0.0",
+ "default-browser-id": "^3.0.0",
+ "execa": "^7.1.1",
+ "titleize": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-browser-id": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
+ "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
+ "dev": true,
+ "dependencies": {
+ "bplist-parser": "^0.2.0",
+ "untildify": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/defer-to-connect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
@@ -770,6 +1287,34 @@
"node": ">=10"
}
},
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+ "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
+ "dev": true,
+ "dependencies": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -800,6 +1345,18 @@
"node": ">=8"
}
},
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -916,169 +1473,737 @@
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"optional": true
},
- "node_modules/es6-promisify": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz",
- "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q==",
+ "node_modules/es-abstract": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz",
+ "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.0",
+ "arraybuffer.prototype.slice": "^1.0.1",
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "es-set-tostringtag": "^2.0.1",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.2.1",
+ "get-symbol-description": "^1.0.0",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.5",
+ "is-array-buffer": "^3.0.2",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.10",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.3",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.5.0",
+ "safe-array-concat": "^1.0.0",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trim": "^1.2.7",
+ "string.prototype.trimend": "^1.0.6",
+ "string.prototype.trimstart": "^1.0.6",
+ "typed-array-buffer": "^1.0.0",
+ "typed-array-byte-length": "^1.0.0",
+ "typed-array-byte-offset": "^1.0.0",
+ "typed-array-length": "^1.0.4",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.10"
+ },
"engines": {
- "node": ">=6"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/express": {
- "version": "4.18.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
- "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+ "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+ "dev": true,
"dependencies": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
- "body-parser": "1.20.1",
- "content-disposition": "0.5.4",
- "content-type": "~1.0.4",
- "cookie": "0.5.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.2.0",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.7",
- "qs": "6.11.0",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
+ "get-intrinsic": "^1.1.3",
+ "has": "^1.0.3",
+ "has-tostringtag": "^1.0.0"
},
"engines": {
- "node": ">= 0.10.0"
+ "node": ">= 0.4"
}
},
- "node_modules/express-basic-auth": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz",
- "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==",
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+ "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+ "dev": true,
+ "peer": true,
"dependencies": {
- "basic-auth": "^2.0.1"
+ "has": "^1.0.3"
}
},
- "node_modules/express-basic-auth/node_modules/basic-auth": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
- "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
"dependencies": {
- "safe-buffer": "5.1.2"
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
},
"engines": {
- "node": ">= 0.8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/express-basic-auth/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ "node_modules/es6-promisify": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz",
+ "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q==",
+ "engines": {
+ "node": ">=6"
+ }
},
- "node_modules/express-handlebars": {
- "version": "6.0.7",
- "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-6.0.7.tgz",
- "integrity": "sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg==",
- "dependencies": {
- "glob": "^8.1.0",
- "graceful-fs": "^4.2.10",
- "handlebars": "^4.7.7"
- },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
"engines": {
- "node": ">=v12.22.9"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/express-session": {
- "version": "1.17.3",
- "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
- "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+ "node_modules/eslint": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz",
+ "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==",
+ "dev": true,
"dependencies": {
- "cookie": "0.4.2",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "~2.0.0",
- "on-headers": "~1.0.2",
- "parseurl": "~1.3.3",
- "safe-buffer": "5.2.1",
- "uid-safe": "~2.1.5"
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.49.0",
+ "@humanwhocodes/config-array": "^0.11.11",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
},
"engines": {
- "node": ">= 0.8.0"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/express-session/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "node_modules/eslint-config-airbnb-base": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+ "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+ "dev": true,
"dependencies": {
- "ms": "2.0.0"
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.32.0 || ^8.2.0",
+ "eslint-plugin-import": "^2.25.2"
}
},
- "node_modules/express-session/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "node_modules/eslint-config-airbnb-base/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
},
- "node_modules/express/node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
+ "node_modules/eslint-config-prettier": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
+ "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
},
- "engines": {
- "node": ">= 0.6"
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
}
},
- "node_modules/express/node_modules/array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
},
- "node_modules/express/node_modules/body-parser": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
- "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "node_modules/eslint-module-utils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz",
+ "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==",
+ "dev": true,
+ "peer": true,
"dependencies": {
- "bytes": "3.1.2",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.11.0",
- "raw-body": "2.5.1",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
+ "debug": "^3.2.7"
},
"engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
}
},
- "node_modules/express/node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "node_modules/eslint-plugin-es": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+ "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+ "dev": true,
+ "dependencies": {
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
+ },
"engines": {
- "node": ">= 0.8"
+ "node": ">=8.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=4.19.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.28.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz",
+ "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.findlastindex": "^1.2.2",
+ "array.prototype.flat": "^1.3.1",
+ "array.prototype.flatmap": "^1.3.1",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.7",
+ "eslint-module-utils": "^2.8.0",
+ "has": "^1.0.3",
+ "is-core-module": "^2.13.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.6",
+ "object.groupby": "^1.0.0",
+ "object.values": "^1.1.6",
+ "semver": "^6.3.1",
+ "tsconfig-paths": "^3.14.2"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-node": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+ "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+ "dev": true,
+ "dependencies": {
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-utils": "^2.0.0",
+ "ignore": "^5.1.1",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.10.1",
+ "semver": "^6.1.0"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=5.16.0"
+ }
+ },
+ "node_modules/eslint-plugin-node/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz",
+ "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.8.5"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
+ "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.1",
+ "human-signals": "^4.3.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^3.0.7",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || ^16.14.0 || >=18.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.1",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.5.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.11.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/express-basic-auth": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz",
+ "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==",
+ "dependencies": {
+ "basic-auth": "^2.0.1"
+ }
+ },
+ "node_modules/express-basic-auth/node_modules/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express-basic-auth/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/express-handlebars": {
+ "version": "6.0.7",
+ "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-6.0.7.tgz",
+ "integrity": "sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg==",
+ "dependencies": {
+ "glob": "^8.1.0",
+ "graceful-fs": "^4.2.10",
+ "handlebars": "^4.7.7"
+ },
+ "engines": {
+ "node": ">=v12.22.9"
+ }
+ },
+ "node_modules/express-session": {
+ "version": "1.17.3",
+ "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+ "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+ "dependencies": {
+ "cookie": "0.4.2",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-headers": "~1.0.2",
+ "parseurl": "~1.3.3",
+ "safe-buffer": "5.2.1",
+ "uid-safe": "~2.1.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express-session/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express-session/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "node_modules/express/node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ },
+ "node_modules/express/node_modules/body-parser": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/express/node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "engines": {
+ "node": ">= 0.8"
}
},
"node_modules/express/node_modules/call-bind": {
@@ -1209,41 +2334,6 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
- "node_modules/express/node_modules/get-intrinsic": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
- "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
- "dependencies": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/express/node_modules/has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dependencies": {
- "function-bind": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/express/node_modules/has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/express/node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -1555,6 +2645,55 @@
"node": ">= 0.8"
}
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -1577,6 +2716,18 @@
"node": "^12.20 || >= 14.13"
}
},
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1589,6 +2740,51 @@
"node": ">=8"
}
},
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
+ "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.7",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
"node_modules/form-data-encoder": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
@@ -1640,6 +2836,33 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "node_modules/function.prototype.name": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
@@ -1660,12 +2883,13 @@
}
},
"node_modules/get-intrinsic": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
- "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
+ "has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
},
"funding": {
@@ -1683,6 +2907,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/get-symbol-description": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+ "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
@@ -1721,15 +2961,57 @@
"balanced-match": "^1.0.0"
}
},
- "node_modules/glob/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.21.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+ "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "get-intrinsic": "^1.1.3"
},
- "engines": {
- "node": ">=10"
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/got": {
@@ -1761,6 +3043,12 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
"node_modules/handlebars": {
"version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
@@ -1792,6 +3080,15 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -1801,6 +3098,29 @@
"node": ">=4"
}
},
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
@@ -1812,6 +3132,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -1952,6 +3287,15 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
+ "node_modules/human-signals": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+ "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
@@ -1972,17 +3316,42 @@
"node": ">=0.10.0"
}
},
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "optional": true,
+ "devOptional": true,
"engines": {
"node": ">=0.8.19"
}
@@ -2016,79 +3385,410 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "node_modules/internal-slot": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+ "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
"optional": true
},
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "node_modules/is-array-buffer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+ "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.0",
+ "is-typed-array": "^1.1.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-lambda": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+ "optional": true
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
"dev": true,
"dependencies": {
- "binary-extensions": "^2.0.0"
+ "has-tostringtag": "^1.0.0"
},
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
"dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
"engines": {
- "node": ">=0.10.0"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "node_modules/is-typed-array": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
+ "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+ "dev": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.11"
+ },
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
"dev": true,
"dependencies": {
- "is-extglob": "^2.1.1"
+ "call-bind": "^1.0.2"
},
- "engines": {
- "node": ">=0.10.0"
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-lambda": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
- "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
- "optional": true
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
},
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "node_modules/is-wsl/node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
"engines": {
- "node": ">=0.12.0"
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "optional": true
+ "devOptional": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
@@ -2097,16 +3797,50 @@
"json-buffer": "3.0.1"
}
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/linkifyjs": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz",
"integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA=="
},
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
"node_modules/lowercase-keys": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
@@ -2198,6 +3932,34 @@
"node": ">= 0.6"
}
},
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -2217,6 +3979,18 @@
"node": ">= 0.6"
}
},
+ "node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mimic-response": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
@@ -2420,6 +4194,12 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true
},
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -2658,6 +4438,33 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/npm-run-path": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+ "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
@@ -2696,6 +4503,96 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz",
+ "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz",
+ "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz",
+ "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
+ "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -2723,6 +4620,39 @@
"wrappy": "1"
}
},
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
+ "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
+ "dev": true,
+ "dependencies": {
+ "default-browser": "^4.0.0",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/open-graph-scraper": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/open-graph-scraper/-/open-graph-scraper-5.2.3.tgz",
@@ -2749,12 +4679,59 @@
"node": ">=0.10.0"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/p-cancelable": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
"integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
"engines": {
- "node": ">=12.20"
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-map": {
@@ -2772,6 +4749,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
@@ -2803,6 +4792,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -2811,6 +4809,27 @@
"node": ">=0.10.0"
}
},
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -2823,6 +4842,42 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
+ "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -2848,6 +4903,15 @@
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -2862,6 +4926,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -2966,11 +5050,66 @@
"node": ">=8.10.0"
}
},
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
+ "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "functions-have-names": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/resolve-alpn": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/responselike": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
@@ -2994,6 +5133,16 @@
"node": ">= 4"
}
},
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
@@ -3032,6 +5181,151 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/run-applescript": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
+ "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
+ "dev": true,
+ "dependencies": {
+ "execa": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/run-applescript/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/run-applescript/node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/run-applescript/node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/run-applescript/node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/run-applescript/node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/run-applescript/node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/run-applescript/node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
+ "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -3051,6 +5345,20 @@
}
]
},
+ "node_modules/safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -3075,6 +5383,27 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -3295,17 +5624,62 @@
"node": ">=14.18.0"
}
},
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz",
+ "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz",
+ "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==",
+ "dev": true,
"dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
},
- "engines": {
- "node": ">=8"
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz",
+ "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/strip-ansi": {
@@ -3319,6 +5693,40 @@
"node": ">=8"
}
},
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -3331,6 +5739,34 @@
"node": ">=4"
}
},
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
+ "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/utils": "^2.3.1",
+ "tslib": "^2.5.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/tar": {
"version": "6.1.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
@@ -3347,11 +5783,29 @@
"node": ">=10"
}
},
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
"node_modules/tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
},
+ "node_modules/titleize": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
+ "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -3389,6 +5843,49 @@
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
+ "node_modules/tsconfig-paths": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
+ "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -3401,6 +5898,71 @@
"node": ">= 0.6"
}
},
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
+ "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1",
+ "is-typed-array": "^1.1.10"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz",
+ "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "has-proto": "^1.0.1",
+ "is-typed-array": "^1.1.10"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "has-proto": "^1.0.1",
+ "is-typed-array": "^1.1.10"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+ "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "is-typed-array": "^1.1.9"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/uglify-js": {
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
@@ -3424,6 +5986,21 @@
"node": ">= 0.8"
}
},
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -3456,6 +6033,24 @@
"node": ">= 0.8"
}
},
+ "node_modules/untildify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -3495,7 +6090,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "optional": true,
+ "devOptional": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -3506,6 +6101,41 @@
"node": ">= 8"
}
},
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
+ "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -3528,15 +6158,128 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
}
},
"dependencies": {
+ "@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true
+ },
+ "@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz",
+ "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==",
+ "dev": true
+ },
+ "@eslint/eslintrc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "@eslint/js": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz",
+ "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==",
+ "dev": true
+ },
"@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
"integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
"optional": true
},
+ "@humanwhocodes/config-array": {
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
"@mapbox/node-pre-gyp": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz",
@@ -3579,6 +6322,32 @@
}
}
},
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
"@npmcli/fs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
@@ -3606,8 +6375,22 @@
"integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
"optional": true,
"requires": {
- "mkdirp": "^1.0.4",
- "rimraf": "^3.0.2"
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "@pkgr/utils": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
+ "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "fast-glob": "^3.3.0",
+ "is-glob": "^4.0.3",
+ "open": "^9.1.0",
+ "picocolors": "^1.0.0",
+ "tslib": "^2.6.0"
}
},
"@sindresorhus/is": {
@@ -3634,6 +6417,13 @@
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
},
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "peer": true
+ },
"@types/lodash": {
"version": "4.14.197",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz",
@@ -3652,6 +6442,19 @@
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
+ "acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "requires": {}
+ },
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -3719,11 +6522,32 @@
"indent-string": "^4.0.0"
}
},
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
"anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -3748,11 +6572,108 @@
"readable-stream": "^3.6.0"
}
},
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "array-buffer-byte-length": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+ "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "is-array-buffer": "^3.0.1"
+ }
+ },
+ "array-includes": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz",
+ "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1",
+ "is-string": "^1.0.7"
+ }
+ },
+ "array.prototype.findlastindex": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz",
+ "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0",
+ "get-intrinsic": "^1.2.1"
+ }
+ },
+ "array.prototype.flat": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ }
+ },
+ "array.prototype.flatmap": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ }
+ },
+ "arraybuffer.prototype.slice": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz",
+ "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==",
+ "dev": true,
+ "requires": {
+ "array-buffer-byte-length": "^1.0.0",
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1",
+ "is-array-buffer": "^3.0.2",
+ "is-shared-array-buffer": "^1.0.2"
+ }
+ },
+ "available-typed-arrays": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+ "dev": true
+ },
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "big-integer": {
+ "version": "1.6.51",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+ "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+ "dev": true
+ },
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -3798,6 +6719,15 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
+ "bplist-parser": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
+ "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
+ "dev": true,
+ "requires": {
+ "big-integer": "^1.6.44"
+ }
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3816,6 +6746,15 @@
"fill-range": "^7.0.1"
}
},
+ "bundle-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
+ "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
+ "dev": true,
+ "requires": {
+ "run-applescript": "^5.0.0"
+ }
+ },
"bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -3900,6 +6839,12 @@
"get-intrinsic": "^1.0.2"
}
},
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
"chalk": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
@@ -3972,6 +6917,21 @@
"rfdc": "^1.3.0"
}
},
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
"color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@@ -3982,6 +6942,12 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
+ "confusing-browser-globals": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+ "dev": true
+ },
"connect-sqlite3": {
"version": "0.9.13",
"resolved": "https://registry.npmjs.org/connect-sqlite3/-/connect-sqlite3-0.9.13.tgz",
@@ -4031,6 +6997,17 @@
}
}
},
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
"css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
@@ -4077,11 +7054,55 @@
}
}
},
+ "deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "default-browser": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
+ "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
+ "dev": true,
+ "requires": {
+ "bundle-name": "^3.0.0",
+ "default-browser-id": "^3.0.0",
+ "execa": "^7.1.1",
+ "titleize": "^3.0.0"
+ }
+ },
+ "default-browser-id": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
+ "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
+ "dev": true,
+ "requires": {
+ "bplist-parser": "^0.2.0",
+ "untildify": "^4.0.0"
+ }
+ },
"defer-to-connect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
},
+ "define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+ "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
+ "dev": true,
+ "requires": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ }
+ },
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -4102,6 +7123,15 @@
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
},
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
"dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -4175,22 +7205,430 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
},
- "env-paths": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
- "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
- "optional": true
+ "env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "optional": true
+ },
+ "err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "optional": true
+ },
+ "es-abstract": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz",
+ "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==",
+ "dev": true,
+ "requires": {
+ "array-buffer-byte-length": "^1.0.0",
+ "arraybuffer.prototype.slice": "^1.0.1",
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "es-set-tostringtag": "^2.0.1",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.2.1",
+ "get-symbol-description": "^1.0.0",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.5",
+ "is-array-buffer": "^3.0.2",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.10",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.3",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.5.0",
+ "safe-array-concat": "^1.0.0",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trim": "^1.2.7",
+ "string.prototype.trimend": "^1.0.6",
+ "string.prototype.trimstart": "^1.0.6",
+ "typed-array-buffer": "^1.0.0",
+ "typed-array-byte-length": "^1.0.0",
+ "typed-array-byte-offset": "^1.0.0",
+ "typed-array-length": "^1.0.4",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.10"
+ }
+ },
+ "es-set-tostringtag": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+ "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.3",
+ "has": "^1.0.3",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "es-shim-unscopables": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+ "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "es6-promisify": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz",
+ "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q=="
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "eslint": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz",
+ "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.49.0",
+ "@humanwhocodes/config-array": "^0.11.11",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+ "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
+ "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz",
+ "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "debug": "^3.2.7"
+ }
+ },
+ "eslint-plugin-es": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+ "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+ "dev": true,
+ "requires": {
+ "eslint-utils": "^2.0.0",
+ "regexpp": "^3.0.0"
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.28.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz",
+ "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-includes": "^3.1.6",
+ "array.prototype.findlastindex": "^1.2.2",
+ "array.prototype.flat": "^1.3.1",
+ "array.prototype.flatmap": "^1.3.1",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.7",
+ "eslint-module-utils": "^2.8.0",
+ "has": "^1.0.3",
+ "is-core-module": "^2.13.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.6",
+ "object.groupby": "^1.0.0",
+ "object.values": "^1.1.6",
+ "semver": "^6.3.1",
+ "tsconfig-paths": "^3.14.2"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "eslint-plugin-node": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+ "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+ "dev": true,
+ "requires": {
+ "eslint-plugin-es": "^3.0.0",
+ "eslint-utils": "^2.0.0",
+ "ignore": "^5.1.1",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.10.1",
+ "semver": "^6.1.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-prettier": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz",
+ "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.8.5"
+ }
+ },
+ "eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true
+ },
+ "espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ }
+ },
+ "esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
},
- "err-code": {
+ "esutils": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
- "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
- "optional": true
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
},
- "es6-promisify": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz",
- "integrity": "sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q=="
+ "execa": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
+ "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.1",
+ "human-signals": "^4.3.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^3.0.7",
+ "strip-final-newline": "^3.0.0"
+ }
},
"express": {
"version": "4.18.2",
@@ -4362,29 +7800,6 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
- "get-intrinsic": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
- "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
- "requires": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
- },
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -4671,6 +8086,52 @@
}
}
},
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
"fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -4680,6 +8141,15 @@
"web-streams-polyfill": "^3.0.3"
}
},
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -4689,6 +8159,42 @@
"to-regex-range": "^5.0.1"
}
},
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
+ "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.2.7",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.3"
+ }
+ },
"form-data-encoder": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
@@ -4730,6 +8236,24 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "function.prototype.name": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ }
+ },
+ "functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true
+ },
"gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
@@ -4747,12 +8271,13 @@
}
},
"get-intrinsic": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
- "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
+ "has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
}
},
@@ -4761,6 +8286,16 @@
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
},
+ "get-symbol-description": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+ "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ }
+ },
"glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
@@ -4800,6 +8335,33 @@
"is-glob": "^4.0.1"
}
},
+ "globals": {
+ "version": "13.21.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+ "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3"
+ }
+ },
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
"got": {
"version": "12.6.1",
"resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
@@ -4823,6 +8385,12 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
"handlebars": {
"version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
@@ -4843,17 +8411,46 @@
"function-bind": "^1.1.1"
}
},
+ "has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
},
+ "has-property-descriptors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.1"
+ }
+ },
+ "has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
+ },
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
},
+ "has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -4953,6 +8550,12 @@
}
}
},
+ "human-signals": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+ "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+ "dev": true
+ },
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
@@ -4970,17 +8573,33 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true
+ },
"ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "optional": true
+ "devOptional": true
},
"indent-string": {
"version": "4.0.0",
@@ -5008,12 +8627,43 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "internal-slot": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+ "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.2.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ }
+ },
"ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
"optional": true
},
+ "is-array-buffer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+ "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.0",
+ "is-typed-array": "^1.1.10"
+ }
+ },
+ "is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "requires": {
+ "has-bigints": "^1.0.1"
+ }
+ },
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -5023,6 +8673,46 @@
"binary-extensions": "^2.0.0"
}
},
+ "is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true
+ },
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -5043,29 +8733,174 @@
"is-extglob": "^2.1.1"
}
},
+ "is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^3.0.0"
+ }
+ },
"is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
"integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
"optional": true
},
+ "is-negative-zero": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+ "dev": true
+ },
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
+ "is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-shared-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true
+ },
+ "is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "is-typed-array": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
+ "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+ "dev": true,
+ "requires": {
+ "which-typed-array": "^1.1.11"
+ }
+ },
+ "is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ },
+ "dependencies": {
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true
+ }
+ }
+ },
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "optional": true
+ "devOptional": true
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
},
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
"keyv": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
@@ -5074,16 +8909,41 @@
"json-buffer": "3.0.1"
}
},
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
"linkifyjs": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz",
"integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA=="
},
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
"lowercase-keys": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
@@ -5152,6 +9012,28 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
},
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -5165,6 +9047,12 @@
"mime-db": "1.52.0"
}
},
+ "mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true
+ },
"mimic-response": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
@@ -5321,6 +9209,12 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true
},
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -5480,6 +9374,23 @@
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
"integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw=="
},
+ "npm-run-path": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+ "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+ "dev": true,
+ "requires": {
+ "path-key": "^4.0.0"
+ },
+ "dependencies": {
+ "path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true
+ }
+ }
+ },
"npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
@@ -5509,6 +9420,72 @@
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
},
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz",
+ "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ }
+ },
+ "object.fromentries": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz",
+ "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ }
+ },
+ "object.groupby": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz",
+ "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1"
+ }
+ },
+ "object.values": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
+ "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ }
+ },
"on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -5530,6 +9507,27 @@
"wrappy": "1"
}
},
+ "onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^4.0.0"
+ }
+ },
+ "open": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
+ "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
+ "dev": true,
+ "requires": {
+ "default-browser": "^4.0.0",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "is-wsl": "^2.2.0"
+ }
+ },
"open-graph-scraper": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/open-graph-scraper/-/open-graph-scraper-5.2.3.tgz",
@@ -5552,11 +9550,43 @@
}
}
},
+ "optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "requires": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ }
+ },
"p-cancelable": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
"integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="
},
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
"p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
@@ -5566,6 +9596,15 @@
"aggregate-error": "^3.0.0"
}
},
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
"parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
@@ -5588,17 +9627,62 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
},
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "prettier": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
+ "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
+ "dev": true
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -5621,6 +9705,12 @@
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
+ "punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true
+ },
"qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -5629,6 +9719,12 @@
"side-channel": "^1.0.4"
}
},
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
"quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -5703,11 +9799,45 @@
"picomatch": "^2.2.1"
}
},
+ "regexp.prototype.flags": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
+ "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "functions-have-names": "^1.2.3"
+ }
+ },
+ "regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
"resolve-alpn": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
},
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
"responselike": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
@@ -5722,6 +9852,12 @@
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"optional": true
},
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
"rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
@@ -5750,11 +9886,113 @@
}
}
},
+ "run-applescript": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
+ "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
+ "dev": true,
+ "requires": {
+ "execa": "^5.0.0"
+ },
+ "dependencies": {
+ "execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true
+ }
+ }
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "safe-array-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
+ "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ }
+ },
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
+ "safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ }
+ },
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -5776,6 +10014,21 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -5949,6 +10202,39 @@
"strip-ansi": "^6.0.1"
}
},
+ "string.prototype.trim": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz",
+ "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz",
+ "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz",
+ "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ }
+ },
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -5957,6 +10243,25 @@
"ansi-regex": "^5.0.1"
}
},
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "peer": true
+ },
+ "strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -5966,6 +10271,22 @@
"has-flag": "^3.0.0"
}
},
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true
+ },
+ "synckit": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
+ "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==",
+ "dev": true,
+ "requires": {
+ "@pkgr/utils": "^2.3.1",
+ "tslib": "^2.5.0"
+ }
+ },
"tar": {
"version": "6.1.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
@@ -5979,11 +10300,23 @@
"yallist": "^4.0.0"
}
},
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
"tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
},
+ "titleize": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
+ "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
+ "dev": true
+ },
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -6012,6 +10345,40 @@
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
+ "tsconfig-paths": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
+ "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ },
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -6021,6 +10388,53 @@
"mime-types": "~2.1.24"
}
},
+ "typed-array-buffer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
+ "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1",
+ "is-typed-array": "^1.1.10"
+ }
+ },
+ "typed-array-byte-length": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz",
+ "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "has-proto": "^1.0.1",
+ "is-typed-array": "^1.1.10"
+ }
+ },
+ "typed-array-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==",
+ "dev": true,
+ "requires": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "has-proto": "^1.0.1",
+ "is-typed-array": "^1.1.10"
+ }
+ },
+ "typed-array-length": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+ "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "is-typed-array": "^1.1.9"
+ }
+ },
"uglify-js": {
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
@@ -6035,6 +10449,18 @@
"random-bytes": "~1.0.0"
}
},
+ "unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
"undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -6064,6 +10490,21 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
},
+ "untildify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -6097,11 +10538,37 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "optional": true,
+ "devOptional": true,
"requires": {
"isexe": "^2.0.0"
}
},
+ "which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "requires": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ }
+ },
+ "which-typed-array": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
+ "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+ "dev": true,
+ "requires": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
"wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -6124,6 +10591,12 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
}
}
}
diff --git a/package.json b/package.json
index 1c429f0..2f801a8 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
"type": "module",
"scripts": {
"start": "node server.js",
- "watch": "nodemon --ignore public/ --exec npm start"
+ "watch": "nodemon --ignore public/ --exec npm start",
+ "lint": "eslint . --ext .js"
},
"dependencies": {
"body-parser": "^1.20.2",
@@ -19,15 +20,15 @@
"express-basic-auth": "^1.2.1",
"express-handlebars": "^6.0.7",
"express-session": "^1.17.3",
+ "linkifyjs": "^4.1.1",
"node-fetch": "^3.3.2",
"open-graph-scraper": "^5.2.3",
"sqlite": "^5.0.1",
"sqlite3": "^5.1.5",
- "string-strip-html": "^13.4.2",
- "linkifyjs": "^4.1.1"
+ "string-strip-html": "^13.4.2"
},
"engines": {
- "node": "16.x"
+ "node": ">=16.x"
},
"repository": {
"url": "https://github.com/ckolderup/postmarks"
@@ -43,6 +44,12 @@
"mastodon"
],
"devDependencies": {
- "nodemon": "^2.0.20"
+ "eslint": "^8.43.0",
+ "eslint-config-airbnb-base": "^15.0.0",
+ "eslint-config-prettier": "^9.0.0",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-prettier": "^5.0.0",
+ "nodemon": "^2.0.20",
+ "prettier": "^3.0.3"
}
-}
\ No newline at end of file
+}
diff --git a/public/style.css b/public/style.css
index e0a50e4..47f756f 100644
--- a/public/style.css
+++ b/public/style.css
@@ -18,17 +18,16 @@ to this section's matching END comment to see page-specific styles.
src: url('/PublicSans-Italic-VariableFont_wght.ttf');
}
-
/* Our default values set as CSS variables */
:root {
- --color-bg: #FFE6F8;
+ --color-bg: #ffe6f8;
--color-text-main: #000000;
- --color-primary: #D7F5EF;
+ --color-primary: #d7f5ef;
--wrapper-height: 40vh;
--image-max-width: 300px;
--image-margin: 3rem;
- --font-family: "Public Sans", sans-serif;
- --font-family-header: "Public Sans", sans-serif;
+ --font-family: 'Public Sans', sans-serif;
+ --font-family-header: 'Public Sans', sans-serif;
}
/* Basic page style resets */
@@ -41,7 +40,7 @@ to this section's matching END comment to see page-specific styles.
/* Our remix on glitch button */
.btn--remix {
- font-family: "Public Sans", sans-serif;
+ font-family: 'Public Sans', sans-serif;
padding: 0.75rem 1rem;
font-size: 1.1rem;
line-height: 1rem;
@@ -49,7 +48,7 @@ to this section's matching END comment to see page-specific styles.
height: 2.75rem;
align-items: center;
cursor: pointer;
- background: #FFFFFF;
+ background: #ffffff;
border: 1px solid #000000;
box-sizing: border-box;
border-radius: 4px;
@@ -62,7 +61,7 @@ to this section's matching END comment to see page-specific styles.
margin-right: 0.5rem;
}
.btn--remix:hover {
- background-color: #D0FFF1;
+ background-color: #d0fff1;
}
/* Navigation grid */
@@ -78,7 +77,7 @@ to this section's matching END comment to see page-specific styles.
.footer a:not(.btn--remix):link,
a:not(.btn--remix):visited {
- font-family: "Public Sans", sans-serif;
+ font-family: 'Public Sans', sans-serif;
font-style: normal;
font-weight: normal;
font-size: 1.1rem;
@@ -102,7 +101,7 @@ END Glitch hello-app default styles
******************************************************************************/
body {
- font-family: "Public Sans", sans-serif;
+ font-family: 'Public Sans', sans-serif;
background-color: var(--color-bg);
}
@@ -135,8 +134,8 @@ label.small {
/* Title h1 style */
h1 {
- color: #2800FF;
- font-family: "Public Sans", sans-serif;
+ color: #2800ff;
+ font-family: 'Public Sans', sans-serif;
font-style: normal;
font-weight: 600;
font-size: 48px;
@@ -148,7 +147,7 @@ h1 {
h1 a {
text-decoration: none;
font-weight: 600;
- color: #2800FF !important;
+ color: #2800ff !important;
}
/* Very light scaling for our illustration */
@@ -165,7 +164,7 @@ h1 a {
/* Styles for our poll form and its results */
.poll-form,
.poll-results {
- background-color: #FFF3FC;
+ background-color: #fff3fc;
padding: 0 16px 16px 16px;
border-radius: 8px;
}
@@ -177,7 +176,7 @@ button,
input {
font-family: inherit;
font-size: 100%;
- background: #FFFFFF;
+ background: #ffffff;
border: 1px solid #000000;
box-sizing: border-box;
border-radius: 4px;
@@ -199,18 +198,18 @@ label {
font-size: 1rem;
}
h2 {
- color: #2800FF;
+ color: #2800ff;
}
/* Admin page */
table {
text-align: left;
border-collapse: collapse;
- background-color: #FFF3FC;
+ background-color: #fff3fc;
}
td,
th {
- border: 2px solid #FFFFFF;
+ border: 2px solid #ffffff;
padding: 0.5rem;
}
a {
@@ -220,7 +219,7 @@ a {
p.error,
p.error a:link,
p.error a:visited {
- color: #2800FF;
+ color: #2800ff;
}
.header {
@@ -267,7 +266,8 @@ p.error a:visited {
margin-bottom: 10px;
}
-.bookmark .tags a, .bookmark .bottom a {
+.bookmark .tags a,
+.bookmark .bottom a {
font-weight: normal;
}
.bookmark .tags {
@@ -353,4 +353,8 @@ textarea {
.network-post {
border-bottom: 1px solid #000000;
-}
\ No newline at end of file
+}
+
+.search input[type='submit'] {
+ margin-left: 0.5rem;
+}
diff --git a/public/tag-autocomplete.js b/public/tag-autocomplete.js
index 7523a84..b8e758d 100644
--- a/public/tag-autocomplete.js
+++ b/public/tag-autocomplete.js
@@ -1,56 +1,43 @@
const addToTaggedList = (tagToAdd, userSupplied) => {
- const tagEntry = document.querySelector("#tagEntry");
- const taggedList = document.querySelector("#taggedList");
- const datalist = document.querySelector(`#${tagEntry.getAttribute("list")}`);
+ const tagEntry = document.querySelector('#tagEntry');
+ const taggedList = document.querySelector('#taggedList');
+ const datalist = document.querySelector(`#${tagEntry.getAttribute('list')}`);
- const newTag = taggedList.appendChild(document.createElement("span"));
- newTag.classList.add("tagged");
+ const newTag = taggedList.appendChild(document.createElement('span'));
+ newTag.classList.add('tagged');
newTag.innerText = tagToAdd;
- newTag.addEventListener("click", (removeTagEvent) => {
- const formTagList = document.querySelector("#tags");
+ newTag.addEventListener('click', (removeTagEvent) => {
+ const formTagList = document.querySelector('#tags');
const tagList = JSON.parse(decodeURIComponent(formTagList.value) || []);
- formTagList.value = encodeURIComponent(JSON.stringify(
- tagList.filter((x) => x !== removeTagEvent.target.innerText)
- ));
+ formTagList.value = encodeURIComponent(JSON.stringify(tagList.filter((x) => x !== removeTagEvent.target.innerText)));
removeTagEvent.target.remove();
// because it's bound when the element is created,
// we retain the state in the element. nice!
if (!userSupplied) {
- const insertBefore = [...datalist.children].find(
- (option) => option.value.toLowerCase() > tagToAdd.toLowerCase()
- );
- datalist.insertBefore(
- document.createElement("option"),
- insertBefore
- ).value = removeTagEvent.target.innerText;
+ const insertBefore = [...datalist.children].find((option) => option.value.toLowerCase() > tagToAdd.toLowerCase());
+ datalist.insertBefore(document.createElement('option'), insertBefore).value = removeTagEvent.target.innerText;
}
});
};
const removeFromDatalist = (tagToRemove) => {
- const datalist = document.querySelector(`#${tagEntry.getAttribute("list")}`);
+ const tagEntry = document.querySelector('#tagEntry');
- const autocompleteOption = [...datalist.children].find(
- (option) => option.value === tagToRemove
- );
+ const datalist = document.querySelector(`#${tagEntry.getAttribute('list')}`);
+
+ const autocompleteOption = [...datalist.children].find((option) => option.value === tagToRemove);
if (autocompleteOption) {
datalist.removeChild(autocompleteOption);
}
};
const addToTags = (tagToAdd, userSupplied) => {
- const tagEntry = document.querySelector("#tagEntry");
- const formTagList = document.querySelector("#tags");
- const datalist = document.querySelector(`#${tagEntry.getAttribute("list")}`);
-
+ const tagEntry = document.querySelector('#tagEntry');
+ const formTagList = document.querySelector('#tags');
- if (
- JSON.parse(decodeURIComponent(formTagList.value) || '[]').some(
- (x) => x.toLowerCase() === tagToAdd.toLowerCase()
- )
- ) {
- console.log("tag already added");
+ if (JSON.parse(decodeURIComponent(formTagList.value) || '[]').some((x) => x.toLowerCase() === tagToAdd.toLowerCase())) {
+ console.log('tag already added');
tagEntry.value = null;
tagEntry.blur();
tagEntry.focus();
@@ -76,60 +63,52 @@ const addToTags = (tagToAdd, userSupplied) => {
tagEntry.focus();
};
-const tagEntry = document.querySelector("#tagEntry");
-const taggedList = document.querySelector("#taggedList");
-const formTagList = document.querySelector("#tags");
+const tagEntry = document.querySelector('#tagEntry');
-tagEntry.addEventListener("input", (e) => {
- if (e.inputType === "insertText") {
- if (e.data === "," || e.data === '#') {
+tagEntry.addEventListener('input', (e) => {
+ if (e.inputType === 'insertText') {
+ if (e.data === ',' || e.data === '#') {
addToTags(e.target.value.slice(0, -1), true);
}
- } else if (
- e.inputType === "insertReplacementText" ||
- e.inputType === undefined
- ) {
+ } else if (e.inputType === 'insertReplacementText' || e.inputType === undefined) {
// should be things like autocomplete select
addToTags(e.target.value, false);
}
});
-tagEntry.addEventListener("keydown", (e) => {
- if (e.key === "Tab") {
- if (e.target.value !== "") {
+tagEntry.addEventListener('keydown', (e) => {
+ if (e.key === 'Tab') {
+ if (e.target.value !== '') {
e.preventDefault();
const strippedInput = e.target.value.trim();
- const tagToAdd = [...e.target.list.children]
- .map((x) => x.value)
- .find((x) => new RegExp(`${strippedInput}`, "i").test(x));
+ const tagToAdd = [...e.target.list.children].map((x) => x.value).find((x) => new RegExp(`${strippedInput}`, 'i').test(x));
if (tagToAdd) {
addToTags(tagToAdd, false);
}
}
- } else if (e.key === "Enter") {
- if (e.target.value !== "") {
+ } else if (e.key === 'Enter') {
+ if (e.target.value !== '') {
e.preventDefault();
addToTags(e.target.value, true);
}
}
});
-document.querySelector('.edit-bookmark form').addEventListener("submit", (e) => {
- const tagEntry = document.querySelector("#tagEntry");
+document.querySelector('.edit-bookmark form').addEventListener('submit', () => {
console.log(tagEntry.value);
if (tagEntry.value.length > 0) {
addToTags(tagEntry.value, true);
}
});
-document.addEventListener("DOMContentLoaded", (e) => {
- const formTagList = document.querySelector("#tags");
- const taggedList = document.querySelector("#taggedList");
+document.addEventListener('DOMContentLoaded', () => {
+ const formTagList = document.querySelector('#tags');
+ const taggedList = document.querySelector('#taggedList');
taggedList.replaceChildren();
const tags = JSON.parse(decodeURIComponent(formTagList.value) || '[]');
tags.forEach((tag) => {
addToTaggedList(tag, false);
removeFromDatalist(tag);
- })
+ });
});
diff --git a/server.js b/server.js
index 74b3490..fe13642 100644
--- a/server.js
+++ b/server.js
@@ -1,24 +1,24 @@
-import * as dotenv from "dotenv";
-import express from "express";
-import cors from "cors";
-import { create } from "express-handlebars";
+import * as dotenv from 'dotenv';
+import express from 'express';
+import cors from 'cors';
+import { create } from 'express-handlebars';
-import { domain, account, simpleLogger, actorInfo } from "./src/util.js";
-import session, { isAuthenticated } from "./src/session-auth.js";
-import * as bookmarksDb from "./src/bookmarks-db.js";
-import * as apDb from "./src/activity-pub-db.js";
+import { domain, account, simpleLogger, actorInfo } from './src/util.js';
+import session, { isAuthenticated } from './src/session-auth.js';
+import * as bookmarksDb from './src/bookmarks-db.js';
+import * as apDb from './src/activity-pub-db.js';
-import routes from "./src/routes/index.js";
+import routes from './src/routes/index.js';
dotenv.config();
const PORT = process.env.PORT || 3000;
const app = express();
-app.use(express.static("public"));
+app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
-app.use(express.json({ type: "application/activity+json" }));
+app.use(express.json({ type: 'application/activity+json' }));
app.use(session());
app.use((req, res, next) => {
@@ -26,27 +26,24 @@ app.use((req, res, next) => {
return next();
});
-app.set("site_name", actorInfo.displayName || "Postmarks");
-app.set("bookmarksDb", bookmarksDb);
-app.set("apDb", apDb);
-app.set("account", account);
-app.set("domain", domain);
+app.set('site_name', actorInfo.displayName || 'Postmarks');
+app.set('bookmarksDb', bookmarksDb);
+app.set('apDb', apDb);
+app.set('account', account);
+app.set('domain', domain);
-app.disable("x-powered-by");
+app.disable('x-powered-by');
-//force HTTPS in production
-if (process.env.ENVIRONMENT === "production") {
- app.set("trust proxy", ["127.0.0.1", "10.0.0.0/8"]);
+// force HTTPS in production
+if (process.env.ENVIRONMENT === 'production') {
+ app.set('trust proxy', ['127.0.0.1', '10.0.0.0/8']);
app.use(({ secure, hostname, url, port }, response, next) => {
if (!secure) {
- return response.redirect(
- 308,
- `https://${hostname}${url}${port ? `:${port}` : ""}`
- );
+ return response.redirect(308, `https://${hostname}${url}${port ? `:${port}` : ''}`);
}
- next();
+ return next();
});
} else {
console.log("ENVIRONMENT is not 'production', HTTPS not forced");
@@ -56,25 +53,23 @@ const hbs = create({
helpers: {
pluralize(number, singular, plural) {
if (number === 1) return singular;
- else return typeof plural === "string" ? plural : singular + "s";
+ return typeof plural === 'string' ? plural : `${singular}s`;
},
htmlize(text) {
// uh-oh. ohhhh no.
- return text?.replace("\n", "<br/>");
+ return text?.replace('\n', '<br/>');
},
siteName() {
- return app.get("site_name");
+ return app.get('site_name');
},
account() {
- return app.get("account");
+ return app.get('account');
},
feedLink() {
- return `<link rel="alternate" type="application/atom+xml" href="https://${app.get(
- "domain"
- )}/index.xml" />`;
+ return `<link rel="alternate" type="application/atom+xml" href="https://${app.get('domain')}/index.xml" />`;
},
projectUrl() {
- return `https://${app.get("domain")}`;
+ return `https://${app.get('domain')}`;
},
glitchProjectName() {
return process.env.PROJECT_DOMAIN;
@@ -91,32 +86,35 @@ const hbs = create({
return array.indexOf(item) >= 0 ? options.fn(this) : options.inverse(this);
},
removeTag(tag, path) {
- return path.split('/').filter(x => x !== tag).join('/');
+ return path
+ .split('/')
+ .filter((x) => x !== tag)
+ .join('/');
},
ifThisTag(tag, path, options) {
return path === `/tagged/${tag}` ? options.fn(this) : options.inverse(this);
},
},
- partialsDir: "./src/pages/partials",
- extname: ".hbs",
+ partialsDir: './src/pages/partials',
+ extname: '.hbs',
});
-app.set("view engine", ".hbs");
-app.set("views", "./src/pages");
-app.engine(".hbs", hbs.engine);
+app.set('view engine', '.hbs');
+app.set('views', './src/pages');
+app.engine('.hbs', hbs.engine);
app.use(simpleLogger);
-app.use("/admin", isAuthenticated, routes.admin);
-app.use("/", routes.auth);
-app.use("/bookmark", routes.bookmark);
-app.use("/comment", routes.comment);
-app.use("/.well-known/webfinger", cors(), routes.webfinger);
-app.use("/u", cors(), routes.user);
-app.use("/m", cors(), routes.message);
-app.use("/", routes.core);
-app.use("/api/inbox", cors(), routes.inbox);
-app.use("/.well-known/nodeinfo", routes.nodeinfo)
-app.use("/nodeinfo/2.0", routes.nodeinfo)
-
-app.listen(PORT, () => console.log(`App listening on port ${ PORT }`));
\ No newline at end of file
+app.use('/admin', isAuthenticated, routes.admin);
+app.use('/', routes.auth);
+app.use('/bookmark', routes.bookmark);
+app.use('/comment', routes.comment);
+app.use('/.well-known/webfinger', cors(), routes.webfinger);
+app.use('/u', cors(), routes.user);
+app.use('/m', cors(), routes.message);
+app.use('/', routes.core);
+app.use('/api/inbox', cors(), routes.inbox);
+app.use('/.well-known/nodeinfo', routes.nodeinfo);
+app.use('/nodeinfo/2.0', routes.nodeinfo);
+
+app.listen(PORT, () => console.log(`App listening on port ${PORT}`));
diff --git a/src/activity-pub-db.js b/src/activity-pub-db.js
index 824f38c..e0ea8f6 100644
--- a/src/activity-pub-db.js
+++ b/src/activity-pub-db.js
@@ -5,277 +5,242 @@
*/
// Utilities we need
-import * as path from "path";
-import fs from "fs";
-import sqlite3 from "sqlite3";
-import { open } from "sqlite";
-import crypto from "crypto";
-import { account, domain, actorInfo } from "./util.js";
-
-const dbFile = "./.data/activitypub.db";
+import * as path from 'path';
+import fs from 'fs';
+import sqlite3 from 'sqlite3';
+import { open } from 'sqlite';
+import crypto from 'crypto';
+import { account, domain, actorInfo } from './util.js';
+
+const dbFile = './.data/activitypub.db';
let db;
-setup();
-
-function setup() {
- // activitypub not set up yet, skip until we have the data we need
- if (actorInfo.disabled) {
- return;
- }
-
- // Initialize the database
- const exists = fs.existsSync(dbFile);
-
- open({
- filename: dbFile,
- driver: sqlite3.Database,
- }).then(async (dBase) => {
- db = dBase;
-
- const actorName = `${account}@${domain}`;
-
- try {
- if (!exists) {
- await firstTimeSetup(actorName);
- }
-
- // re-run the profile portion of the actor setup every time in case the avatar, description, etc have changed
- const publicKey = await getPublicKey(account);
- const actorRecord = actorJson(account, domain, actorInfo, publicKey);
- await db.run(
- `UPDATE accounts SET name = ?, actor = ?`,
- actorName,
- JSON.stringify(actorRecord)
- );
- } catch (dbError) {
- console.error(dbError);
- }
- });
-}
-
-function actorJson(name, domain, actorInfo, pubkey) {
+function actorJson(pubkey) {
return {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- ],
+ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],
- id: `https://${domain}/u/${name}`,
- type: "Person",
- preferredUsername: `${name}`,
+ id: `https://${domain}/u/${account}`,
+ type: 'Person',
+ preferredUsername: `${account}`,
name: actorInfo.displayName,
summary: actorInfo.description,
icon: {
- type: "Image",
+ type: 'Image',
mediaType: `image/${path.extname(actorInfo.avatar).slice(1)}`,
url: actorInfo.avatar,
},
inbox: `https://${domain}/api/inbox`,
- outbox: `https://${domain}/u/${name}/outbox`,
- followers: `https://${domain}/u/${name}/followers`,
- following: `https://${domain}/u/${name}/following`,
+ outbox: `https://${domain}/u/${account}/outbox`,
+ followers: `https://${domain}/u/${account}/followers`,
+ following: `https://${domain}/u/${account}/following`,
publicKey: {
- id: `https://${domain}/u/${name}#main-key`,
- owner: `https://${domain}/u/${name}`,
+ id: `https://${domain}/u/${account}#main-key`,
+ owner: `https://${domain}/u/${account}`,
publicKeyPem: pubkey,
},
};
}
-function webfingerJson(name, domain) {
+function webfingerJson() {
return {
- subject: `acct:${name}@${domain}`,
+ subject: `acct:${account}@${domain}`,
links: [
{
- rel: "self",
- type: "application/activity+json",
- href: `https://${domain}/u/${name}`,
+ rel: 'self',
+ type: 'application/activity+json',
+ href: `https://${domain}/u/${account}`,
},
],
};
}
-async function firstTimeSetup(actorName) {
- const newDb = new sqlite3.Database(
- dbFile,
- sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
- (err) => {
- if (err) {
- console.log(`unable to open or create database: ${err}`);
- process.exit(1);
- }
- }
- );
-
- newDb.close();
-
- // now do it again, using the async/await library
- await open({
- filename: dbFile,
- driver: sqlite3.Database,
- }).then(async (dBase) => {
- db = dBase;
- });
-
- await db.run(
- "CREATE TABLE IF NOT EXISTS accounts (name TEXT PRIMARY KEY, privkey TEXT, pubkey TEXT, webfinger TEXT, actor TEXT, followers TEXT, following TEXT, messages TEXT, blocks TEXT)"
- );
-
- // if there is no `messages` table in the DB, create an empty table
- // TODO: index messages on bookmark_id
- await db.run(
- "CREATE TABLE IF NOT EXISTS messages (guid TEXT PRIMARY KEY, message TEXT, bookmark_id INTEGER)"
- );
- await db.run(
- "CREATE TABLE IF NOT EXISTS permissions (bookmark_id INTEGER NOT NULL UNIQUE, allowed TEXT, blocked TEXT)"
- );
-
- return new Promise((resolve, reject) => {
- crypto.generateKeyPair(
- "rsa",
- {
- modulusLength: 4096,
- publicKeyEncoding: {
- type: "spki",
- format: "pem",
- },
- privateKeyEncoding: {
- type: "pkcs8",
- format: "pem",
- },
- },
- async (err, publicKey, privateKey) => {
- if (err) return reject(err);
- try {
- const actorRecord = actorJson(account, domain, actorInfo, publicKey);
- const webfingerRecord = webfingerJson(account, domain);
-
- await db.run(
- `INSERT OR REPLACE INTO accounts (name, actor, pubkey, privkey, webfinger) VALUES (?, ?, ?, ?, ?)`,
- actorName,
- JSON.stringify(actorRecord),
- publicKey,
- privateKey,
- JSON.stringify(webfingerRecord)
- );
- return resolve();
- } catch (e) {
- return reject(e);
- }
- }
- );
- });
-}
-
-
export async function getFollowers() {
- const result = await db?.get("select followers from accounts limit 1");
+ const result = await db?.get('select followers from accounts limit 1');
return result?.followers;
}
export async function setFollowers(followersJson) {
- return await db?.run("update accounts set followers=?", followersJson);
+ return db?.run('update accounts set followers=?', followersJson);
}
export async function getFollowing() {
- const result = await db?.get("select following from accounts limit 1");
+ const result = await db?.get('select following from accounts limit 1');
return result?.following;
}
export async function setFollowing(followingJson) {
- return await db?.run("update accounts set following=?", followingJson);
+ return db?.run('update accounts set following=?', followingJson);
}
export async function getBlocks() {
- const result = await db?.get("select blocks from accounts limit 1");
+ const result = await db?.get('select blocks from accounts limit 1');
return result?.blocks;
}
export async function setBlocks(blocksJson) {
- return await db?.run("update accounts set blocks=?", blocksJson);
+ return db?.run('update accounts set blocks=?', blocksJson);
}
-export async function getActor(name) {
- const result = await db?.get("select actor from accounts limit 1");
+export async function getActor() {
+ const result = await db?.get('select actor from accounts limit 1');
return result?.actor;
}
-export async function getWebfinger(name) {
- const result = await db?.get("select webfinger from accounts limit 1");
+export async function getWebfinger() {
+ const result = await db?.get('select webfinger from accounts limit 1');
return result?.webfinger;
}
-export async function getPublicKey(name) {
- const result = await db?.get("select pubkey from accounts limit 1");
+export async function getPublicKey() {
+ const result = await db?.get('select pubkey from accounts limit 1');
return result?.pubkey;
}
-export async function getPrivateKey(name) {
- const result = await db?.get("select privkey from accounts limit 1");
+export async function getPrivateKey() {
+ const result = await db?.get('select privkey from accounts limit 1');
return result?.privkey;
}
export async function getGuidForBookmarkId(id) {
- return (await db?.get("select guid from messages where bookmark_id = ?", id))
- ?.guid;
+ return (await db?.get('select guid from messages where bookmark_id = ?', id))?.guid;
}
export async function getBookmarkIdFromMessageGuid(guid) {
- return (
- await db?.get("select bookmark_id from messages where guid = ?", guid)
- )?.bookmark_id;
+ return (await db?.get('select bookmark_id from messages where guid = ?', guid))?.bookmark_id;
}
export async function getMessage(guid) {
- return await db?.get("select message from messages where guid = ?", guid);
+ return db?.get('select message from messages where guid = ?', guid);
}
export async function findMessageGuid(bookmarkId) {
- return (
- await db?.get("select guid from messages where bookmark_id = ?", bookmarkId)
- )?.guid;
+ return (await db?.get('select guid from messages where bookmark_id = ?', bookmarkId))?.guid;
}
export async function deleteMessage(guid) {
- await db?.get("delete from messages where guid = ?", guid);
- return;
+ await db?.get('delete from messages where guid = ?', guid);
}
export async function getGlobalPermissions() {
- return await db?.get("select * from permissions where bookmark_id = 0");
+ return db?.get('select * from permissions where bookmark_id = 0');
}
-export async function setGlobalPermissions(allowed, blocked) {
- return await setPermissionsForBookmark(0, allowed, blocked);
+export async function setPermissionsForBookmark(id, allowed, blocked) {
+ return db?.run('insert or replace into permissions(bookmark_id, allowed, blocked) values (?, ?, ?)', id, allowed, blocked);
}
-export async function setPermissionsForBookmark(id, allowed, blocked) {
- return await db?.run(
- "insert or replace into permissions(bookmark_id, allowed, blocked) values (?, ?, ?)",
- id,
- allowed,
- blocked
- );
+export async function setGlobalPermissions(allowed, blocked) {
+ return setPermissionsForBookmark(0, allowed, blocked);
}
export async function getPermissionsForBookmark(id) {
- return await db?.get("select * from permissions where bookmark_id = ?", id);
+ return db?.get('select * from permissions where bookmark_id = ?', id);
}
export async function insertMessage(guid, bookmarkId, json) {
- return await db?.run(
- "insert or replace into messages(guid, bookmark_id, message) values(?, ?, ?)",
- guid,
- bookmarkId,
- json
- );
+ return db?.run('insert or replace into messages(guid, bookmark_id, message) values(?, ?, ?)', guid, bookmarkId, json);
}
export async function findMessage(object) {
- return await db?.all(
- "select * from messages where message like ?",
- `%${object}%`
+ return db?.all('select * from messages where message like ?', `%${object}%`);
+}
+
+async function firstTimeSetup(actorName) {
+ // eslint-disable-next-line no-bitwise
+ const newDb = new sqlite3.Database(dbFile, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
+ if (err) {
+ throw new Error(`unable to open or create database: ${err}`);
+ }
+ });
+
+ newDb.close();
+
+ // now do it again, using the async/await library
+ await open({
+ filename: dbFile,
+ driver: sqlite3.Database,
+ }).then(async (dBase) => {
+ db = dBase;
+ });
+
+ await db.run(
+ 'CREATE TABLE IF NOT EXISTS accounts (name TEXT PRIMARY KEY, privkey TEXT, pubkey TEXT, webfinger TEXT, actor TEXT, followers TEXT, following TEXT, messages TEXT, blocks TEXT)',
);
+
+ // if there is no `messages` table in the DB, create an empty table
+ // TODO: index messages on bookmark_id
+ await db.run('CREATE TABLE IF NOT EXISTS messages (guid TEXT PRIMARY KEY, message TEXT, bookmark_id INTEGER)');
+ await db.run('CREATE TABLE IF NOT EXISTS permissions (bookmark_id INTEGER NOT NULL UNIQUE, allowed TEXT, blocked TEXT)');
+
+ return new Promise((resolve, reject) => {
+ crypto.generateKeyPair(
+ 'rsa',
+ {
+ modulusLength: 4096,
+ publicKeyEncoding: {
+ type: 'spki',
+ format: 'pem',
+ },
+ privateKeyEncoding: {
+ type: 'pkcs8',
+ format: 'pem',
+ },
+ },
+ async (err, publicKey, privateKey) => {
+ if (err) return reject(err);
+ try {
+ const actorRecord = actorJson(publicKey);
+ const webfingerRecord = webfingerJson();
+
+ await db.run(
+ 'INSERT OR REPLACE INTO accounts (name, actor, pubkey, privkey, webfinger) VALUES (?, ?, ?, ?, ?)',
+ actorName,
+ JSON.stringify(actorRecord),
+ publicKey,
+ privateKey,
+ JSON.stringify(webfingerRecord),
+ );
+ return resolve();
+ } catch (e) {
+ return reject(e);
+ }
+ },
+ );
+ });
+}
+
+function setup() {
+ // activitypub not set up yet, skip until we have the data we need
+ if (actorInfo.disabled) {
+ return;
+ }
+
+ // Initialize the database
+ const exists = fs.existsSync(dbFile);
+
+ open({
+ filename: dbFile,
+ driver: sqlite3.Database,
+ }).then(async (dBase) => {
+ db = dBase;
+
+ const actorName = `${account}@${domain}`;
+
+ try {
+ if (!exists) {
+ await firstTimeSetup(actorName);
+ }
+
+ // re-run the profile portion of the actor setup every time in case the avatar, description, etc have changed
+ const publicKey = await getPublicKey();
+ const actorRecord = actorJson(publicKey);
+ await db.run('UPDATE accounts SET name = ?, actor = ?', actorName, JSON.stringify(actorRecord));
+ } catch (dbError) {
+ console.error(dbError);
+ }
+ });
}
+
+setup();
diff --git a/src/activitypub.js b/src/activitypub.js
index bbc95ee..0840af0 100644
--- a/src/activitypub.js
+++ b/src/activitypub.js
@@ -1,100 +1,50 @@
-import fetch from "node-fetch";
-import crypto from "crypto";
+import fetch from 'node-fetch';
+import crypto from 'crypto';
-import { actorInfo, actorMatchesUsername } from "./util.js";
+import { signedGetJSON, signedPostJSON } from './signature.js';
+import { actorInfo, actorMatchesUsername } from './util.js';
function getGuidFromPermalink(urlString) {
- return urlString.match(/m\/([a-zA-Z0-9+\/]+)/)[1];
+ return urlString.match(/m\/([a-zA-Z0-9+/]+)/)[1];
}
-export async function signAndSend(
- message,
- name,
- domain,
- db,
- targetDomain,
- inbox
-) {
- // get the private key
- let inboxFragment = inbox.replace("https://" + targetDomain, "");
- const privkey = await db.getPrivateKey(`${name}@${domain}`);
-
- if (privkey === undefined) {
- console.log(`No private key found for ${name}.`);
- } else {
- const digest = crypto
- .createHash("sha256")
- .update(JSON.stringify(message))
- .digest("base64");
- const signer = crypto.createSign("sha256");
- let d = new Date();
- let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}\ndigest: SHA-256=${digest}`;
- signer.update(stringToSign);
- signer.end();
- const signature = signer.sign(privkey);
- const signature_b64 = signature.toString("base64");
-
- const algorithm = "rsa-sha256";
- let header = `keyId="https://${domain}/u/${name}",algorithm="${algorithm}",headers="(request-target) host date digest",signature="${signature_b64}"`;
-
- try {
- const response = await fetch(inbox, {
- headers: {
- Host: targetDomain,
- Date: d.toUTCString(),
- Digest: `SHA-256=${digest}`,
- Signature: header,
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- method: "POST",
- body: JSON.stringify(message),
- });
-
- const data = await response.text();
-
- console.log(`Sent message to an inbox at ${targetDomain}!`);
- console.log("Response Status Code:", response.status);
- console.log("Response body:", data);
- } catch (error) {
- console.log("Error:", error.message);
- console.log("Stacktrace: ", error.stack);
- }
+export async function signAndSend(message, name, domain, db, targetDomain, inbox) {
+ try {
+ const response = await signedPostJSON(inbox, {
+ body: JSON.stringify(message),
+ });
+ const data = await response.text();
+
+ console.log(`Sent message to an inbox at ${targetDomain}!`);
+ console.log('Response Status Code:', response.status);
+ console.log('Response body:', data);
+ } catch (error) {
+ console.log('Error:', error.message);
+ console.log('Stacktrace: ', error.stack);
}
}
export function createNoteObject(bookmark, account, domain) {
- const guidNote = crypto.randomBytes(16).toString("hex");
+ const guidNote = crypto.randomBytes(16).toString('hex');
const d = new Date();
- const linkedTags = bookmark.tags
- ?.split(" ")
- .map((tag) => {
- const tagName = tag.slice(1);
- return `<a href=\"https://${domain}/tagged/${tagName}\" class=\"mention hashtag\" rel=\"tag\">${tag}</a>`;
- })
- .join(" ");
-
const noteMessage = {
- "@context": "https://www.w3.org/ns/activitystreams",
+ '@context': 'https://www.w3.org/ns/activitystreams',
id: `https://${domain}/m/${guidNote}`,
- type: "Note",
+ type: 'Note',
published: d.toISOString(),
attributedTo: `https://${domain}/u/${account}`,
content: `
<strong><a href="${bookmark.url}">${bookmark.title}</a></strong><br/>
- ${bookmark.description?.replace("\n", "<br/>") || ""}`,
- to: [
- `https://${domain}/u/${account}/followers/`,
- "https://www.w3.org/ns/activitystreams#Public",
- ],
+ ${bookmark.description?.replace('\n', '<br/>') || ''}`,
+ to: [`https://${domain}/u/${account}/followers/`, 'https://www.w3.org/ns/activitystreams#Public'],
tag: [],
};
- bookmark.tags?.split(" ").forEach((tag) => {
+ bookmark.tags?.split(' ').forEach((tag) => {
const tagName = tag.slice(1);
noteMessage.tag.push({
- type: "Hashtag",
+ type: 'Hashtag',
href: `https://${domain}/tagged/${tagName}`,
name: tag,
});
@@ -103,6 +53,23 @@ export function createNoteObject(bookmark, account, domain) {
return noteMessage;
}
+function createMessage(noteObject, bookmarkId, account, domain, db) {
+ const guidCreate = crypto.randomBytes(16).toString('hex');
+
+ const message = {
+ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],
+ id: `https://${domain}/m/${guidCreate}`,
+ type: 'Create',
+ actor: `https://${domain}/u/${account}`,
+ to: [`https://${domain}/u/${account}/followers/`, 'https://www.w3.org/ns/activitystreams#Public'],
+ object: noteObject,
+ };
+
+ db.insertMessage(getGuidFromPermalink(noteObject.id), bookmarkId, JSON.stringify(noteObject));
+
+ return message;
+}
+
async function createUpdateMessage(bookmark, account, domain, db) {
const guid = await db.getGuidForBookmarkId(bookmark.id);
@@ -117,12 +84,9 @@ async function createUpdateMessage(bookmark, account, domain, db) {
}
const updateMessage = {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- ],
+ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],
summary: `${account} updated the bookmark`,
- type: "Create", // this should be 'Update' but Mastodon does weird things with Updates
+ type: 'Create', // this should be 'Update' but Mastodon does weird things with Updates
actor: `https://${domain}/u/${account}`,
object: note,
};
@@ -130,51 +94,18 @@ async function createUpdateMessage(bookmark, account, domain, db) {
return updateMessage;
}
-function createMessage(noteObject, bookmarkId, account, domain, db) {
- const guidCreate = crypto.randomBytes(16).toString("hex");
-
- const createMessage = {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- ],
- id: `https://${domain}/m/${guidCreate}`,
- type: "Create",
- actor: `https://${domain}/u/${account}`,
- to: [
- `https://${domain}/u/${account}/followers/`,
- "https://www.w3.org/ns/activitystreams#Public",
- ],
- object: noteObject,
- };
-
- db.insertMessage(
- getGuidFromPermalink(noteObject.id),
- bookmarkId,
- JSON.stringify(noteObject)
- );
-
- return createMessage;
-}
-
async function createDeleteMessage(bookmark, account, domain, db) {
const guid = await db.findMessageGuid(bookmark.id);
await db.deleteMessage(guid);
const deleteMessage = {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- ],
+ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],
id: `https://${domain}/m/${guid}`,
- type: "Delete",
+ type: 'Delete',
actor: `https://${domain}/u/${account}`,
- to: [
- `https://${domain}/u/${account}/followers/`,
- "https://www.w3.org/ns/activitystreams#Public",
- ],
+ to: [`https://${domain}/u/${account}/followers/`, 'https://www.w3.org/ns/activitystreams#Public'],
object: {
- type: "Tombstone",
+ type: 'Tombstone',
id: `https://${domain}/m/${guid}`,
},
};
@@ -183,11 +114,11 @@ async function createDeleteMessage(bookmark, account, domain, db) {
}
export async function createFollowMessage(account, domain, target, db) {
- const guid = crypto.randomBytes(16).toString("hex");
+ const guid = crypto.randomBytes(16).toString('hex');
const followMessage = {
- "@context": "https://www.w3.org/ns/activitystreams",
+ '@context': 'https://www.w3.org/ns/activitystreams',
id: guid,
- type: "Follow",
+ type: 'Follow',
actor: `https://${domain}/u/${account}`,
object: target,
};
@@ -198,57 +129,49 @@ export async function createFollowMessage(account, domain, target, db) {
}
export async function createUnfollowMessage(account, domain, target, db) {
- const undoGuid = crypto.randomBytes(16).toString("hex");
+ const undoGuid = crypto.randomBytes(16).toString('hex');
const messageRows = await db.findMessage(target);
- console.log("result", messageRows)
+ console.log('result', messageRows);
const followMessages = messageRows?.filter((row) => {
- const message = JSON.parse(row.message || "{}");
- return (
- message.type === "Follow" && message.object === target
- );
+ const message = JSON.parse(row.message || '{}');
+ return message.type === 'Follow' && message.object === target;
});
if (followMessages?.length > 0) {
const undoMessage = {
- "@context": "https://www.w3.org/ns/activitystreams",
- type: "Undo",
+ '@context': 'https://www.w3.org/ns/activitystreams',
+ type: 'Undo',
id: undoGuid,
actor: `${domain}/u/${account}`,
object: followMessages.slice(-1).message,
};
return undoMessage;
- } else {
- console.log(
- "tried to find a Follow record in order to unfollow, but failed"
- );
- return null;
}
+ console.log('tried to find a Follow record in order to unfollow, but failed');
+ return null;
}
export async function getInboxFromActorProfile(profileUrl) {
- const response = await fetch(`${profileUrl}.json`);
+ const response = await signedGetJSON(`${profileUrl}.json`);
const data = await response.json();
if (data?.inbox) {
- return data.inbox
- } else {
- throw new Error(`Couldn't find inbox at supplied profile url ${profileUrl}`);
+ return data.inbox;
}
+ throw new Error(`Couldn't find inbox at supplied profile url ${profileUrl}`);
}
// actorUsername format is @username@domain
export async function lookupActorInfo(actorUsername) {
- const parsedDomain = actorUsername.split("@").slice(-1);
- const parsedUsername = actorUsername.split("@").slice(-2, -1);
+ const parsedDomain = actorUsername.split('@').slice(-1);
+ const parsedUsername = actorUsername.split('@').slice(-2, -1);
try {
- const response = await fetch(
- `https://${parsedDomain}/.well-known/webfinger/?resource=acct:${parsedUsername}@${parsedDomain}`
- );
+ const response = await fetch(`https://${parsedDomain}/.well-known/webfinger/?resource=acct:${parsedUsername}@${parsedDomain}`);
const data = await response.json();
- const selfLink = data.links.find((o) => o.rel === "self");
+ const selfLink = data.links.find((o) => o.rel === 'self');
if (!selfLink || !selfLink.href) {
throw new Error();
}
@@ -275,11 +198,11 @@ export async function broadcastMessage(bookmark, action, db, account, domain) {
const globalPermissions = await db.getGlobalPermissions();
const blocklist =
bookmarkPermissions?.blocked
- ?.split("\n")
- ?.concat(globalPermissions?.blocked?.split("\n"))
+ ?.split('\n')
+ ?.concat(globalPermissions?.blocked?.split('\n'))
.filter((x) => !x?.match(/^@([^@]+)@(.+)$/)) || [];
- //now let's try to remove the blocked users
+ // now let's try to remove the blocked users
followers.filter((actor) => {
const matches = blocklist.forEach((username) => {
actorMatchesUsername(actor, username);
@@ -291,28 +214,27 @@ export async function broadcastMessage(bookmark, action, db, account, domain) {
const noteObject = createNoteObject(await bookmark, account, domain);
let message;
switch (action) {
- case "create":
+ case 'create':
message = createMessage(noteObject, bookmark.id, account, domain, db);
break;
- case "update":
+ case 'update':
message = await createUpdateMessage(bookmark, account, domain, db);
break;
- case "delete":
+ case 'delete':
message = await createDeleteMessage(bookmark, account, domain, db);
break;
default:
- console.log("unsupported action!");
+ console.log('unsupported action!');
return;
}
- console.log(
- `sending this message to all followers: ${JSON.stringify(message)}`
- );
+ console.log(`sending this message to all followers: ${JSON.stringify(message)}`);
- for (let follower of followers) {
- let inbox = follower + "/inbox";
- let myURL = new URL(follower);
- let targetDomain = myURL.host;
+ // eslint-disable-next-line no-restricted-syntax
+ for (const follower of followers) {
+ const inbox = `${follower}/inbox`;
+ const myURL = new URL(follower);
+ const targetDomain = myURL.host;
signAndSend(message, account, domain, db, targetDomain, inbox);
}
}
diff --git a/src/bookmarks-db.js b/src/bookmarks-db.js
index 12ce8bc..7c8f417 100644
--- a/src/bookmarks-db.js
+++ b/src/bookmarks-db.js
@@ -7,144 +7,92 @@
// Utilities we need
import fs from 'fs';
import sqlite3 from 'sqlite3';
-import{ open } from 'sqlite';
+import { open } from 'sqlite';
+// unclear why eslint can't resolve this package
+// eslint-disable-next-line import/no-unresolved, node/no-missing-import
+import { stripHtml } from 'string-strip-html';
import { timeSince, account, domain } from './util.js';
-import { stripHtml } from "string-strip-html";
const ACCOUNT_MENTION_REGEX = new RegExp(`^@${account}@${domain} `);
// Initialize the database
-const dbFile = "./.data/bookmarks.db";
+const dbFile = './.data/bookmarks.db';
const exists = fs.existsSync(dbFile);
let db;
-/*
-We're using the sqlite wrapper so that we can make async / await connections
-- https://www.npmjs.com/package/sqlite
-*/
- open({
- filename: dbFile,
- driver: sqlite3.Database
- })
- .then(async dBase => {
- db = dBase;
-
- try {
- if (!exists) {
- const newDb = new sqlite3.Database(dbFile, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
- if (err) {
- console.log(`unable to open or create database: ${err}`);
- process.exit(1);
- }
- });
-
- newDb.close();
-
- // now do it again, using the async/await library
- await open({
- filename: dbFile,
- driver: sqlite3.Database
- }).then(async dBase => {
- db = dBase;
- });
-
- // Database doesn't exist yet - create Bookmarks table
- await db.run(
- "CREATE TABLE bookmarks (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, url TEXT, description TEXT, tags TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);"
- );
-
- // Add default choices to table
- const defaults = [
- {
- title: "Postmarks - Getting Started",
- url: "https://casey.kolderup.org/notes/b059694f5064c6c6285075c894a72317.html",
- description: "Some notes on setup and acknowledgements",
- tags: "#postmarks #default",
- },
- {
- title: "Postmarks - Ethos",
- url: "https://casey.kolderup.org/notes/edf3a659f52528da103ea4dcbb09f66f.html",
- description:
- "A short writeup about the influences and goals that led to the creation of Postmarks",
- tags: "#postmarks #default",
- },
- {
- title: "Postmarks - Future Ideas",
- url: "https://casey.kolderup.org/notes/9307f6d67bbfedbd215ae2d09caeab39.html",
- description:
- "Some places I hope to take the platform in the future",
- tags: "#postmarks #default",
- },
- ];
-
- await db.run('CREATE TABLE comments (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, url TEXT, content TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, visible integer BOOLEAN DEFAULT 0 NOT NULL CHECK (visible IN (0,1)), bookmark_id INTEGER, FOREIGN KEY(bookmark_id) REFERENCES bookmarks(id) ON DELETE CASCADE);');
- await db.run('CREATE UNIQUE INDEX comments_url ON comments(url)');
-
-
- const defaultsAsValuesList = defaults.map(b => `('${b.title}', '${b.url}', '${b.description}', '${b.tags}')`).join(', ');
- db.run(`INSERT INTO bookmarks (title, url, description, tags) VALUES ${defaultsAsValuesList}`);
- }
- } catch (dbError) {
- console.error(dbError);
- }
- });
-
-function massageBookmark(bookmark) {
- return addBookmarkDomain(addTags(insertRelativeTimestamp(bookmark)));
-}
-
-function massageComment(comment) {
- return generateLinkedDisplayName(stripMentionFromComment(stripHtmlFromComment(insertRelativeTimestamp(comment))));
-}
-
-function addBookmarkDomain(bookmark) {
- return { domain: new URL(bookmark.url).hostname, ...bookmark}
-}
-
-function insertRelativeTimestamp(object) {
- return { timestamp: timeSince(new Date(object.created_at).getTime()), ...object };
-}
-
// for now, strip the HTML when we retrieve it from the DB, just so that we keep as much data as possible
// if we ultimately decide that we don't want to do something fancier with keeping bold, italics, etc but
// discarding Mastodon's presentational HTML tags, then we'll remove this and handle that at the time comments get stored
function stripHtmlFromComment(comment) {
- return {...comment, content: stripHtml(comment.content).result};
+ return { ...comment, content: stripHtml(comment.content).result };
}
function stripMentionFromComment(comment) {
- return {...comment, content: comment.content.replace(ACCOUNT_MENTION_REGEX, '')};
+ return {
+ ...comment,
+ content: comment.content.replace(ACCOUNT_MENTION_REGEX, ''),
+ };
}
function generateLinkedDisplayName(comment) {
const match = comment.name.match(/^@([^@]+)@(.+)$/);
- return { linked_display_name: `<a href="http://${match[2]}/@${match[1]}">${match[1]}</a>`, ...comment};
+ return {
+ linked_display_name: `<a href="http://${match[2]}/@${match[1]}">${match[1]}</a>`,
+ ...comment,
+ };
+}
+
+function addBookmarkDomain(bookmark) {
+ return { domain: new URL(bookmark.url).hostname, ...bookmark };
+}
+
+function insertRelativeTimestamp(object) {
+ return {
+ timestamp: timeSince(new Date(object.created_at).getTime()),
+ ...object,
+ };
}
function addTags(bookmark) {
- const tagNames = bookmark.tags?.split(' ').map(t => t.slice(1)).sort();
+ const tagNames = bookmark.tags
+ ?.split(' ')
+ .map((t) => t.slice(1))
+ .sort();
return { tagNames, ...bookmark };
}
+function massageBookmark(bookmark) {
+ return addBookmarkDomain(addTags(insertRelativeTimestamp(bookmark)));
+}
+
+function massageComment(comment) {
+ return generateLinkedDisplayName(stripMentionFromComment(stripHtmlFromComment(insertRelativeTimestamp(comment))));
+}
+
export async function getBookmarkCount() {
- const result = await db.get("SELECT count(id) as count FROM bookmarks");
+ const result = await db.get('SELECT count(id) as count FROM bookmarks');
return result?.count;
}
-export async function getBookmarks(limit=10, offset=0) {
+export async function getBookmarks(limit = 10, offset = 0) {
// We use a try catch block in case of db errors
try {
- const results = await db.all("SELECT bookmarks.*, count(comments.id) as comment_count from bookmarks LEFT OUTER JOIN comments ON bookmarks.id = comments.bookmark_id AND comments.visible = 1 GROUP BY bookmarks.id ORDER BY updated_at DESC LIMIT ? OFFSET ?", limit, offset);
- return results.map(b => massageBookmark(b));
+ const results = await db.all(
+ 'SELECT bookmarks.*, count(comments.id) as comment_count from bookmarks LEFT OUTER JOIN comments ON bookmarks.id = comments.bookmark_id AND comments.visible = 1 GROUP BY bookmarks.id ORDER BY updated_at DESC LIMIT ? OFFSET ?',
+ limit,
+ offset,
+ );
+ return results.map((b) => massageBookmark(b));
} catch (dbError) {
// Database connection error
console.error(dbError);
}
+ return undefined;
}
export async function getBookmarkCountForTags(tags) {
- const tagClauses = tags.map(tag => `(tags like ? OR tags like ?)`).join(' AND ');
- const tagParams = tags.map(tag => [`%${tag}% `, `%${tag}%`]).flat();
+ const tagClauses = tags.map(() => `(tags like ? OR tags like ?)`).join(' AND ');
+ const tagParams = tags.map((tag) => [`%${tag}% `, `%${tag}%`]).flat();
const result = await db.get.apply(db, [`SELECT count(id) as count from bookmarks WHERE ${tagClauses}`, ...tagParams]);
return result?.count;
}
@@ -152,117 +100,106 @@ export async function getBookmarkCountForTags(tags) {
export async function getBookmarksForTags(tags, limit = 10, offset = 0) {
// We use a try catch block in case of db errors
try {
- const tagClauses = tags.map(tag => `(tags like ? OR tags like ?)`).join(' AND ');
- const tagParams = tags.map(tag => [`%${tag}% `, `%${tag}%`]).flat();
+ const tagClauses = tags.map(() => `(tags like ? OR tags like ?)`).join(' AND ');
+ const tagParams = tags.map((tag) => [`%${tag}% `, `%${tag}%`]).flat();
const results = await db.all.apply(db, [
- `SELECT * from bookmarks WHERE ${tagClauses} ORDER BY updated_at DESC LIMIT ? OFFSET ?`,
- ...tagParams, limit, offset
+ `SELECT * from bookmarks WHERE ${tagClauses} ORDER BY updated_at DESC LIMIT ? OFFSET ?`,
+ ...tagParams,
+ limit,
+ offset,
]);
- return results.map(b => massageBookmark(b));
+ return results.map((b) => massageBookmark(b));
} catch (dbError) {
// Database connection error
console.error(dbError);
}
+ return undefined;
}
export async function getBookmark(id) {
try {
const result = await db.get(
- "SELECT bookmarks.*, count(comments.id) as comment_count from bookmarks LEFT OUTER JOIN comments ON bookmarks.id = comments.bookmark_id AND comments.visible = 1 WHERE bookmarks.id = ?",
- id
+ 'SELECT bookmarks.*, count(comments.id) as comment_count from bookmarks LEFT OUTER JOIN comments ON bookmarks.id = comments.bookmark_id AND comments.visible = 1 WHERE bookmarks.id = ?',
+ id,
);
return massageBookmark(result);
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
}
export async function getTags() {
try {
- const allTagFields = await db.all("SELECT tags from bookmarks");
- const allTags = allTagFields
- .map((bookmarkTagList) => bookmarkTagList.tags?.split(" "))
- .flat();
- const parsedTags = allTags
- .filter((t) => t !== undefined)
- .map((t) => t.slice(1));
+ const allTagFields = await db.all('SELECT tags from bookmarks');
+ const allTags = allTagFields.map((bookmarkTagList) => bookmarkTagList.tags?.split(' ')).flat();
+ const parsedTags = allTags.filter((t) => t !== undefined).map((t) => t.slice(1));
return [...new Set(parsedTags)].sort();
} catch (dbError) {
// Database connection error
console.error(dbError);
}
+ return undefined;
}
export async function getNetworkPosts() {
try {
- const result = await db.all(
- "SELECT * from comments WHERE bookmark_id IS NULL ORDER BY created_at DESC"
- );
+ const result = await db.all('SELECT * from comments WHERE bookmark_id IS NULL ORDER BY created_at DESC');
return result;
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
}
export async function createBookmark(body) {
try {
const result = await db.run(
- "INSERT INTO bookmarks (title, url, description, tags) VALUES (?, ?, ?, ?)",
+ 'INSERT INTO bookmarks (title, url, description, tags) VALUES (?, ?, ?, ?)',
body.title,
body.url,
body.description,
- body.tags
+ body.tags,
);
return getBookmark(result.lastID);
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
}
export async function updateBookmark(id, body) {
try {
await db.run(
- "UPDATE bookmarks SET title = ?, url = ?, description = ?, tags = ? WHERE id = ?",
+ 'UPDATE bookmarks SET title = ?, url = ?, description = ?, tags = ? WHERE id = ?',
body.title,
body.url,
body.description,
body.tags,
- id
+ id,
);
- return await db.get("SELECT * from bookmarks WHERE id = ?", id);
+ return await db.get('SELECT * from bookmarks WHERE id = ?', id);
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
}
export async function deleteBookmark(id) {
try {
- await db.run("DELETE from bookmarks WHERE id = ?", id);
+ await db.run('DELETE from bookmarks WHERE id = ?', id);
} catch (dbError) {
console.error(dbError);
}
}
-export async function createComment(
- bookmarkId,
- name,
- url,
- content,
- visible = 0
-) {
+export async function createComment(bookmarkId, name, url, content, visible = 0) {
try {
- await db.run(
- "INSERT INTO comments (name, url, content, bookmark_id, visible) VALUES (?, ?, ?, ?, ?)",
- name,
- url,
- content,
- bookmarkId,
- visible
- );
+ await db.run('INSERT INTO comments (name, url, content, bookmark_id, visible) VALUES (?, ?, ?, ?, ?)', name, url, content, bookmarkId, visible);
} catch (dbError) {
console.error(dbError);
}
@@ -270,7 +207,7 @@ export async function createComment(
export async function deleteComment(url) {
try {
- await db.run("DELETE FROM comments WHERE url = ?", url);
+ await db.run('DELETE FROM comments WHERE url = ?', url);
} catch (dbError) {
console.error(dbError);
}
@@ -278,7 +215,7 @@ export async function deleteComment(url) {
export async function toggleCommentVisibility(commentId) {
try {
- await db.run("UPDATE comments SET visible = ((visible | 1) - (visible & 1)) WHERE id = ?", commentId);
+ await db.run('UPDATE comments SET visible = ((visible | 1) - (visible & 1)) WHERE id = ?', commentId);
} catch (dbError) {
console.error(dbError);
}
@@ -286,25 +223,27 @@ export async function toggleCommentVisibility(commentId) {
export async function getAllCommentsForBookmark(bookmarkId) {
try {
- const results = await db.all("SELECT * FROM comments WHERE bookmark_id = ?", bookmarkId);
+ const results = await db.all('SELECT * FROM comments WHERE bookmark_id = ?', bookmarkId);
return results.map((c) => massageComment(c));
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
}
export async function getVisibleCommentsForBookmark(bookmarkId) {
try {
- const results = await db.all("SELECT * FROM comments WHERE visible = 1 AND bookmark_id = ?", bookmarkId);
+ const results = await db.all('SELECT * FROM comments WHERE visible = 1 AND bookmark_id = ?', bookmarkId);
return results.map((c) => massageComment(c));
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
}
export async function deleteHiddenCommentsForBookmark(bookmarkId) {
try {
- await db.run("DELETE FROM comments WHERE visible = 0 AND bookmark_id = ?", bookmarkId);
+ await db.run('DELETE FROM comments WHERE visible = 0 AND bookmark_id = ?', bookmarkId);
} catch (dbError) {
console.error(dbError);
}
@@ -313,11 +252,94 @@ export async function deleteHiddenCommentsForBookmark(bookmarkId) {
export async function deleteAllBookmarks() {
try {
// Delete the bookmarks
- await db.run("DELETE from bookmarks");
+ await db.run('DELETE from bookmarks');
// Return empty array
return [];
} catch (dbError) {
console.error(dbError);
}
+ return undefined;
+}
+
+export async function searchBookmarks(keywords) {
+ try {
+ const searchFields = ['title', 'description', 'url', 'tags'];
+ const where = keywords.map(() => `(${searchFields.map((f) => `${f} like ?`).join(' or ')})`).join(' and ');
+ const keywordParams = keywords.map((kw) => Array(searchFields.length).fill(`%${kw}%`)).flat();
+ const results = await db.all.apply(db, [`SELECT * from bookmarks WHERE ${where} ORDER BY updated_at DESC LIMIT 20`, ...keywordParams]);
+ return results.map((b) => massageBookmark(b));
+ } catch (dbError) {
+ console.error(dbError);
+ }
+ return undefined;
}
+
+/*
+We're using the sqlite wrapper so that we can make async / await connections
+- https://www.npmjs.com/package/sqlite
+*/
+open({
+ filename: dbFile,
+ driver: sqlite3.Database,
+}).then(async (dBase) => {
+ db = dBase;
+
+ try {
+ if (!exists) {
+ // eslint-disable-next-line no-bitwise
+ const newDb = new sqlite3.Database(dbFile, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
+ if (err) {
+ throw new Error(`unable to open or create database: ${err}`);
+ }
+ });
+
+ newDb.close();
+
+ // now do it again, using the async/await library
+ await open({
+ filename: dbFile,
+ driver: sqlite3.Database,
+ }).then(async () => {
+ db = dBase;
+ });
+
+ // Database doesn't exist yet - create Bookmarks table
+ await db.run(
+ 'CREATE TABLE bookmarks (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, url TEXT, description TEXT, tags TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);',
+ );
+
+ // Add default choices to table
+ const defaults = [
+ {
+ title: 'Postmarks - Getting Started',
+ url: 'https://casey.kolderup.org/notes/b059694f5064c6c6285075c894a72317.html',
+ description: 'Some notes on setup and acknowledgements',
+ tags: '#postmarks #default',
+ },
+ {
+ title: 'Postmarks - Ethos',
+ url: 'https://casey.kolderup.org/notes/edf3a659f52528da103ea4dcbb09f66f.html',
+ description: 'A short writeup about the influences and goals that led to the creation of Postmarks',
+ tags: '#postmarks #default',
+ },
+ {
+ title: 'Postmarks - Future Ideas',
+ url: 'https://casey.kolderup.org/notes/9307f6d67bbfedbd215ae2d09caeab39.html',
+ description: 'Some places I hope to take the platform in the future',
+ tags: '#postmarks #default',
+ },
+ ];
+
+ await db.run(
+ 'CREATE TABLE comments (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, url TEXT, content TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, visible integer BOOLEAN DEFAULT 0 NOT NULL CHECK (visible IN (0,1)), bookmark_id INTEGER, FOREIGN KEY(bookmark_id) REFERENCES bookmarks(id) ON DELETE CASCADE);',
+ );
+ await db.run('CREATE UNIQUE INDEX comments_url ON comments(url)');
+
+ const defaultsAsValuesList = defaults.map((b) => `('${b.title}', '${b.url}', '${b.description}', '${b.tags}')`).join(', ');
+ db.run(`INSERT INTO bookmarks (title, url, description, tags) VALUES ${defaultsAsValuesList}`);
+ }
+ } catch (dbError) {
+ console.error(dbError);
+ }
+});
diff --git a/src/pages/about.hbs b/src/pages/about.hbs
index aee97d0..f133a54 100644
--- a/src/pages/about.hbs
+++ b/src/pages/about.hbs
@@ -1,49 +1,44 @@
{{#if actorInfo.disabled}}
<p>
- This is a bookmarking site running on software called <a href="https://github.com/ckolderup/postmarks">Postmarks</a>.
- It's currently running in "standalone" mode, meaning it doesn't broadcast any activity to the rest of the internet via
- the ActivityPub protocol. You can still subscribe to new bookmarks using the <a href="/index.xml">Atom feed</a>, which
- any modern RSS/feed reader should support.
+ This is a bookmarking site running on software called
+ <a href="https://github.com/ckolderup/postmarks">Postmarks</a>. It's
+ currently running in "standalone" mode, meaning it doesn't broadcast any
+ activity to the rest of the internet via the ActivityPub protocol. You can
+ still subscribe to new bookmarks using the
+ <a href="/index.xml">Atom feed</a>, which any modern RSS/feed reader should
+ support.
</p>
{{else}}
<div class="bio"><img class="avatar" src="{{actorInfo.avatar}}" /><p>
- {{actorInfo.description}}
- </p></div>
+ {{actorInfo.description}}
+ </p></div>
<h2>Follow on the Fediverse</h2>
<div>
- <input readonly type="text" id="copy-bookmarklet" value="@{{actorInfo.username}}@{{domain}}" />
+ <input
+ readonly
+ type="text"
+ id="copy-bookmarklet"
+ value="@{{actorInfo.username}}@{{domain}}"
+ />
<p><small id="copy-status">Click the code above to copy</small></p>
</div>
<script>
- document.addEventListener(
- 'click',
- function (event) {
- // Only fire if the target has id copy
- if (!event.target.matches('#copy-bookmarklet')) return;
-
- if (!navigator.clipboard) {
- // Clipboard API not available
- return;
- }
- const text = event.target.value;
- try {
- navigator.clipboard.writeText(text);
- document.getElementById('copy-status').innerText = 'Copied to clipboard';
- setTimeout(function () {
- document.getElementById('copy-status').innerText = 'Click the text above to copy it to your clipboard';
- }, 1500);
- } catch (err) {
- console.error('Failed to copy!', err);
- }
- },
- false
- );
+ document.addEventListener( 'click', function (event) { // Only fire if the
+ target has id copy if (!event.target.matches('#copy-bookmarklet')) return;
+ if (!navigator.clipboard) { // Clipboard API not available return; } const
+ text = event.target.value; try { navigator.clipboard.writeText(text);
+ document.getElementById('copy-status').innerText = 'Copied to clipboard';
+ setTimeout(function () { document.getElementById('copy-status').innerText =
+ 'Click the text above to copy it to your clipboard'; }, 1500); } catch (err)
+ { console.error('Failed to copy!', err); } }, false );
</script>
{{/if}}
-<p>Postmarks (in its default configuration) uses an edited version of Eynav Raphael's <a
- href="https://thenounproject.com/icon/postmark-stamp-928917/">"Postmark Stamp"</a> icon from The Noun Project. It
- also makes use of free fonts including <a href="http://iotic.com/averia/">Averia Sans</a> and <a
- href="https://public-sans.digital.gov/">Public Sans</a>.</p>
-
-
+<p>Postmarks (in its default configuration) uses an edited version of Eynav
+ Raphael's
+ <a href="https://thenounproject.com/icon/postmark-stamp-928917/">"Postmark
+ Stamp"</a>
+ icon from The Noun Project. It also makes use of free fonts including
+ <a href="http://iotic.com/averia/">Averia Sans</a>
+ and
+ <a href="https://public-sans.digital.gov/">Public Sans</a>.</p>
diff --git a/src/pages/add_bookmark.hbs b/src/pages/add_bookmark.hbs
index a336ae6..96cb100 100644
--- a/src/pages/add_bookmark.hbs
+++ b/src/pages/add_bookmark.hbs
@@ -5,4 +5,4 @@
<div class="bookmark">
{{> edit_bookmark bookmark=undefined }}
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/pages/admin/data.hbs b/src/pages/admin/data.hbs
index cd13a68..26c3118 100644
--- a/src/pages/admin/data.hbs
+++ b/src/pages/admin/data.hbs
@@ -11,7 +11,8 @@
<a href="/admin/activitypub.db">here</a>.
</p>
-<form action="/admin/reset" method="post">
+<form action="/admin/reset" method="post"
+ onsubmit="return confirm('Last chance! This will DELETE ALL YOUR BOOKMARKS. Are you sure you want to do this?')">
<h3>
Delete all bookmarks
</h3>
@@ -20,4 +21,4 @@
recovered.
</p>
<button type="submit">Clear bookmarks</button>
-</form>
\ No newline at end of file
+</form>
diff --git a/src/pages/admin/followers.hbs b/src/pages/admin/followers.hbs
index 5508eea..5f358d4 100644
--- a/src/pages/admin/followers.hbs
+++ b/src/pages/admin/followers.hbs
@@ -2,8 +2,9 @@
Set global permissions
</h3>
<p>
- You can set permissions here for specific users to always be allowed to, or blocked from, commenting on posts.
- Note that the same controls are allowed on a per-bookmark setting on the bookmark edit page.
+ You can set permissions here for specific users to always be allowed to, or
+ blocked from, commenting on posts. Note that the same controls are allowed on
+ a per-bookmark setting on the bookmark edit page.
</p>
<form action="/admin/permissions" method="post">
<label> Auto-allow comments from (one username per line)</label><br />
@@ -18,16 +19,16 @@
</h3>
<div class="followers">
- {{#each followers}}
+ {{#each followers}}
<div class="follower">
<div class="info">
- <a href="{{this}}">{{ this }}</a>
+ <a href="{{this}}">{{this}}</a>
</div>
<div class="actions">
<form action="/admin/followers/block" method="POST">
- <input type="hidden" name="actor" value="{{this}}"/>
+ <input type="hidden" name="actor" value="{{this}}" />
<button type="submit">Block</button>
- </form>
+ </form>
</div>
</div>
{{/each}}
@@ -41,14 +42,14 @@
{{#each blocks}}
<div class="block">
<div class="info">
- <a href="{{this}}">{{ this }}</a>
+ <a href="{{this}}">{{this}}</a>
</div>
<div class="actions">
<form action="/admin/followers/unblock" method="POST">
- <input type="hidden" name="actor" value="{{this}}"/>
+ <input type="hidden" name="actor" value="{{this}}" />
<button type="submit">Unblock</button>
- </form>
+ </form>
</div>
</div>
{{/each}}
-</div>
\ No newline at end of file
+</div>
diff --git a/src/pages/admin/following.hbs b/src/pages/admin/following.hbs
index 82cc79f..fba46b4 100644
--- a/src/pages/admin/following.hbs
+++ b/src/pages/admin/following.hbs
@@ -5,7 +5,12 @@
<div>
<form action="/admin/following/follow" method="POST">
<label for="actor">Enter username to follow</label>
- <input type="text" id="actor" name="actor" placeholder="@updates@postmarks.glitch.me"/>
+ <input
+ type="text"
+ id="actor"
+ name="actor"
+ placeholder="@updates@postmarks.glitch.me"
+ />
<button type="submit">
Follow
</button>
@@ -16,11 +21,11 @@
{{#each following}}
<div class="following">
<div class="info">
- <a href="{{this}}">{{ this }}</a>
+ <a href="{{this}}">{{this}}</a>
</div>
<div class="actions">
<form action="/admin/following/unfollow" method="POST">
- <input type="hidden" name="actor" value="{{this}}"/>
+ <input type="hidden" name="actor" value="{{this}}" />
<button type="submit">Unfollow</button>
</form>
</div>
diff --git a/src/pages/admin/index.hbs b/src/pages/admin/index.hbs
index 39ac722..b13f43c 100644
--- a/src/pages/admin/index.hbs
+++ b/src/pages/admin/index.hbs
@@ -1,33 +1,21 @@
<h3>
- Bookmarklet
- </h3>
- <p>
- You can use this bookmarklet to quickly add bookmarks around the web! Create
- a new bookmark in your browser's bookmarks toolbar, then edit the URL for
- that bookmark and insert this javascript:
- </p>
- <input readonly type="text" id="copy-bookmarklet" value="{{{bookmarklet}}}" />
- <p><small id="copy-status">Click the code above to copy</small></p>
+ Bookmarklet
+</h3>
+<p>
+ You can use this bookmarklet to quickly add bookmarks around the web! Create a
+ new bookmark in your browser's bookmarks toolbar, then edit the URL for that
+ bookmark and insert this javascript:
+</p>
+<input readonly type="text" id="copy-bookmarklet" value="{{{bookmarklet}}}" />
+<p><small id="copy-status">Click the code above to copy</small></p>
- <script>
- document.querySelector('#copy-bookmarklet').addEventListener(
- 'click',
- function (event) {
- if (!navigator.clipboard) {
- // Clipboard API not available
- return;
- }
- const text = event.target.value;
- try {
- navigator.clipboard.writeText(text);
- document.querySelector('#copy-status').innerText = 'Copied to clipboard!';
- setTimeout(function () {
- document.querySelector('#copy-status').innerText = 'Click the code above to copy it to your clipboard';
- }, 1500);
- } catch (err) {
- console.error('Failed to copy!', err);
- }
- },
- false
- );
- </script>
\ No newline at end of file
+<script>
+ document.querySelector('#copy-bookmarklet').addEventListener( 'click',
+ function (event) { if (!navigator.clipboard) { // Clipboard API not available
+ return; } const text = event.target.value; try {
+ navigator.clipboard.writeText(text);
+ document.querySelector('#copy-status').innerText = 'Copied to clipboard!';
+ setTimeout(function () { document.querySelector('#copy-status').innerText =
+ 'Click the code above to copy it to your clipboard'; }, 1500); } catch (err) {
+ console.error('Failed to copy!', err); } }, false );
+</script>
diff --git a/src/pages/bookmark.hbs b/src/pages/bookmark.hbs
index 0c7611b..5500f12 100644
--- a/src/pages/bookmark.hbs
+++ b/src/pages/bookmark.hbs
@@ -7,4 +7,4 @@
{{/each}}
{{else }}
No comments yet!
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/src/pages/index.hbs b/src/pages/index.hbs
index 53e2db3..f7e0d60 100644
--- a/src/pages/index.hbs
+++ b/src/pages/index.hbs
@@ -9,4 +9,4 @@
{{/each}}
</div>
{{> pagination url="/" pageInfo=pageInfo}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/src/pages/layouts/main.hbs b/src/pages/layouts/main.hbs
index d60ecc1..a4dd18c 100644
--- a/src/pages/layouts/main.hbs
+++ b/src/pages/layouts/main.hbs
@@ -27,6 +27,7 @@
</div>
<div class="menu">
<a href="/about">About</a>
+ <a href="/search">Search</a>
{{#if loggedIn}}
<a href="/network">Network</a>
<a href="/bookmark/new">Add</a>
@@ -51,6 +52,8 @@
<span class="divider">|</span>
<a href="/about">About</a>
<span class="divider">|</span>
+ <a href="/search">Search</a>
+ <span class="divider">|</span>
{{#if loggedIn}}
<a href="/network">Network</a>
<span class="divider">|</span>
diff --git a/src/pages/network.hbs b/src/pages/network.hbs
index bcc2846..e574581 100644
--- a/src/pages/network.hbs
+++ b/src/pages/network.hbs
@@ -8,10 +8,13 @@
{{{this.content}}}
</div>
<div class="network-post-credit">
- by {{this.name}}
+ by
+ {{this.name}}
</div>
<div class="network-post-links">
- <a href="/bookmark/new?url={{this.href}}&via={{this.url}}">(Quickadd)</a>
+ <a
+ href="/bookmark/new?url={{this.href}}&via={{this.url}}"
+ >(Quickadd)</a>
<a href="{{this.url}}">(Permalink)</a>
</div>
diff --git a/src/pages/nonfederated.hbs b/src/pages/nonfederated.hbs
index 259683d..69a7061 100644
--- a/src/pages/nonfederated.hbs
+++ b/src/pages/nonfederated.hbs
@@ -3,7 +3,7 @@
<p>
This feature is currently not enabled as it appears you don't have this app
set up to run on the fediverse. If you think that's incorrect, go back to
- the setup instructions and make sure you've followed the necessary steps
- to create an identity.
+ the setup instructions and make sure you've followed the necessary steps to
+ create an identity.
</p>
</div>
diff --git a/src/pages/partials/admin_subnav.hbs b/src/pages/partials/admin_subnav.hbs
index 4e63d04..a747266 100644
--- a/src/pages/partials/admin_subnav.hbs
+++ b/src/pages/partials/admin_subnav.hbs
@@ -4,4 +4,4 @@
<li><a href="/admin/followers">Permissions &amp; followers</a></li>
<li><a href="/admin/following">Manage your federated follows</a></li>
<li><a href="/admin/data">Data export</a></li>
-</ul>
\ No newline at end of file
+</ul>
diff --git a/src/pages/partials/edit_bookmark.hbs b/src/pages/partials/edit_bookmark.hbs
index 2fee810..fea474d 100644
--- a/src/pages/partials/edit_bookmark.hbs
+++ b/src/pages/partials/edit_bookmark.hbs
@@ -1,27 +1,38 @@
<div class="edit-bookmark">
- <form action="/bookmark/{{bookmark.id}}?ephemeral={{ephemeral}}" method="POST">
- <label> URL </label><br>
- <input type="text" name="url" value="{{bookmark.url}}"/><br>
+ <form
+ action="/bookmark/{{bookmark.id}}?ephemeral={{ephemeral}}"
+ method="POST"
+ >
+ <label> URL </label><br />
+ <input type="text" name="url" value="{{bookmark.url}}" /><br />
<p>
- <label> Title </label><br>
- <input type="text" name="title" value="{{bookmark.title}}"/><br>
- <label> Description </label><br>
- <textarea name="description">{{bookmark.description}}</textarea><br>
- <label> Tags </label><br>
- <label class="small">press tab to autocomplete, press comma or enter to save a new tag.</label>
- <input id="tagEntry" type="text" list="all-tags"/>
+ <label> Title </label><br />
+ <input type="text" name="title" value="{{bookmark.title}}" /><br />
+ <label> Description </label><br />
+ <textarea name="description">{{bookmark.description}}</textarea><br />
+ <label> Tags </label><br />
+ <label class="small">press tab to autocomplete, press comma or enter to
+ save a new tag.</label>
+ <input id="tagEntry" type="text" list="all-tags" />
<div id="taggedList"></div>
<label class="small">click on a tag above to remove it</label>
- <input type="hidden" id="tags" name="tags" value="{{{bookmark.tagsArray}}}" />
- </p><br><br>
+ <input
+ type="hidden"
+ id="tags"
+ name="tags"
+ value="{{{bookmark.tagsArray}}}"
+ />
+ </p><br /><br />
<p>
<details>
- <summary><label> Auto-allow comments from (one username per line)</label></summary>
+ <summary><label>
+ Auto-allow comments from (one username per line)</label></summary>
<textarea name="allowed">{{allowed}}</textarea>
</details>
- <br><br>
+ <br /><br />
<details>
- <summary><label> Auto-ignore comments from (one username per line)</label></summary>
+ <summary><label>
+ Auto-ignore comments from (one username per line)</label></summary>
<textarea name="blocked">{{blocked}}</textarea>
</details>
</p>
@@ -33,4 +44,4 @@
{{/if}}
</button>
</form>
- </div>
+</div>
diff --git a/src/pages/partials/pagination.hbs b/src/pages/partials/pagination.hbs
index b1117fb..5b9a2d0 100644
--- a/src/pages/partials/pagination.hbs
+++ b/src/pages/partials/pagination.hbs
@@ -5,11 +5,11 @@
{{else}}
Previous
{{/if}}
- ΓÇó Page {{currentPage}} of {{totalPages}} ΓÇó
+ ΓÇó Page {{currentPage}} of {{totalPages}} ΓÇó
{{#if hasNextPage }}
<a href="{{../url}}?offset={{nextOffset}}">Next</a>
{{else}}
Next
{{/if}}
</div>
-{{/with}}
\ No newline at end of file
+{{/with}}
diff --git a/src/pages/partials/show_bookmark.hbs b/src/pages/partials/show_bookmark.hbs
index f4686c2..7cdd99c 100644
--- a/src/pages/partials/show_bookmark.hbs
+++ b/src/pages/partials/show_bookmark.hbs
@@ -3,7 +3,8 @@
<a href="{{bookmark.url}}">{{bookmark.title}}</a>
</div>
<div class="subhead">
- <span title="{{bookmark.created_at}}">{{bookmark.timestamp}}</span> ΓÇó
+ <span title="{{bookmark.created_at}}">{{bookmark.timestamp}}</span>
+ ΓÇó
{{bookmark.domain}}
</div>
<div class="description">
@@ -31,13 +32,14 @@
<div class="bottom">
{{#if bookmark.comment_count includeZero=true}}
<a href="/bookmark/{{bookmark.id}}">
- {{bookmark.comment_count}} {{pluralize bookmark.comment_count 'comment' 'comments'}}
+ {{bookmark.comment_count}}
+ {{pluralize bookmark.comment_count "comment" "comments"}}
</a>
{{/if}}
{{#if @root.loggedIn}}
- {{#if bookmark.comment_count includeZero=true}}
- ΓÇó
- {{/if}}
+ {{#if bookmark.comment_count includeZero=true}}
+ ΓÇó
+ {{/if}}
{{/if}}
{{#if @root.loggedIn}}
<a href="/bookmark/{{bookmark.id}}/edit">edit</a>
diff --git a/src/pages/partials/show_comment.hbs b/src/pages/partials/show_comment.hbs
index 15547f0..94aed8f 100644
--- a/src/pages/partials/show_comment.hbs
+++ b/src/pages/partials/show_comment.hbs
@@ -1,7 +1,11 @@
<div class="comment">
<div class="top">
- <strong>{{{comment.linked_display_name}}}</strong> ΓÇó
- <span class="timestamp" title="{{comment.created_at}}">{{comment.timestamp}}</span>
+ <strong>{{{comment.linked_display_name}}}</strong>
+ ΓÇó
+ <span
+ class="timestamp"
+ title="{{comment.created_at}}"
+ >{{comment.timestamp}}</span>
</div>
<div class="content">
{{comment.content}}
@@ -16,5 +20,5 @@
{{/if}}
</button>
</form>
- {{/if}}
-</div>
\ No newline at end of file
+ {{/if}}
+</div>
diff --git a/src/pages/partials/tag_list.hbs b/src/pages/partials/tag_list.hbs
index afe93ac..e497ac1 100644
--- a/src/pages/partials/tag_list.hbs
+++ b/src/pages/partials/tag_list.hbs
@@ -1,7 +1,7 @@
<div class="tag-list">
<ul>
- {{#each tags }}
- <li><a href="/tagged/{{this}}">#{{this}}</a></li>
+ {{#each tags}}
+ <li><a href="/tagged/{{this}}">#{{this}}</a></li>
{{/each}}
</ul>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/pages/search.hbs b/src/pages/search.hbs
new file mode 100644
index 0000000..1438acb
--- /dev/null
+++ b/src/pages/search.hbs
@@ -0,0 +1,16 @@
+<div class="search">
+ <form action="/search">
+ <input name="query" value="{{keywords}}"><input type="submit" value="Search">
+ </form>
+</div>
+{{#if error}}
+<p class="error">
+ {{error}}
+</p>
+{{else}}
+<div class="bookmarks">
+ {{#each bookmarks }}
+ {{> show_bookmark bookmark=this }}
+ {{/each}}
+</div>
+{{/if}}
diff --git a/src/pages/tagged.hbs b/src/pages/tagged.hbs
index e348300..75cd41e 100644
--- a/src/pages/tagged.hbs
+++ b/src/pages/tagged.hbs
@@ -9,4 +9,4 @@
{{/each}}
</div>
{{> pagination url="" pageInfo=pageInfo }}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/src/routes/activitypub/inbox.js b/src/routes/activitypub/inbox.js
index 92a6555..b6f5d3f 100644
--- a/src/routes/activitypub/inbox.js
+++ b/src/routes/activitypub/inbox.js
@@ -1,26 +1,20 @@
-import express from "express";
-import crypto from "crypto";
-import fetch from "node-fetch";
-import { actorMatchesUsername, parseJSON } from "../../util.js";
-import { signAndSend, getInboxFromActorProfile } from "../../activitypub.js";
+import express from 'express';
+import crypto from 'crypto';
import * as linkify from 'linkifyjs';
+import { actorMatchesUsername, parseJSON } from '../../util.js';
+import { signAndSend, getInboxFromActorProfile } from '../../activitypub.js';
-export const router = express.Router();
-
-async function sendAcceptMessage(
- thebody,
- name,
- domain,
- req,
- res,
- targetDomain
-) {
- const db = req.app.get("apDb");
- const guid = crypto.randomBytes(16).toString("hex");
- let message = {
- "@context": "https://www.w3.org/ns/activitystreams",
+import { signedGetJSON } from '../../signature.js';
+
+const router = express.Router();
+
+async function sendAcceptMessage(thebody, name, domain, req, res, targetDomain) {
+ const db = req.app.get('apDb');
+ const guid = crypto.randomBytes(16).toString('hex');
+ const message = {
+ '@context': 'https://www.w3.org/ns/activitystreams',
id: `https://${domain}/u/${name}/accept/${guid}`,
- type: "Accept",
+ type: 'Accept',
actor: `https://${domain}/u/${name}`,
object: thebody,
};
@@ -31,18 +25,18 @@ async function sendAcceptMessage(
}
async function handleFollowRequest(req, res) {
- const domain = req.app.get("domain");
- const apDb = req.app.get("apDb");
+ const domain = req.app.get('domain');
+ const apDb = req.app.get('apDb');
const myURL = new URL(req.body.actor);
const targetDomain = myURL.hostname;
- const name = req.body.object.replace(`https://${domain}/u/`, "");
+ const name = req.body.object.replace(`https://${domain}/u/`, '');
await sendAcceptMessage(req.body, name, domain, req, res, targetDomain);
// Add the user to the DB of accounts that follow the account
// get the followers JSON for the user
- const oldFollowersText = (await apDb.getFollowers()) || "[]";
+ const oldFollowersText = (await apDb.getFollowers()) || '[]';
// update followers
let followers = parseJSON(oldFollowersText);
@@ -53,151 +47,125 @@ async function handleFollowRequest(req, res) {
} else {
followers = [req.body.actor];
}
- let newFollowersText = JSON.stringify(followers);
+ const newFollowersText = JSON.stringify(followers);
try {
// update into DB
- const newFollowers = await apDb.setFollowers(newFollowersText);
+ await apDb.setFollowers(newFollowersText);
- console.log("updated followers!");
+ console.log('updated followers!');
} catch (e) {
- console.log("error storing followers after follow", e);
+ console.log('error storing followers after follow', e);
}
return res.status(200);
}
async function handleUnfollow(req, res) {
- const domain = req.app.get("domain");
- const apDb = req.app.get("apDb");
+ const domain = req.app.get('domain');
+ const apDb = req.app.get('apDb');
const myURL = new URL(req.body.actor);
const targetDomain = myURL.hostname;
- const name = req.body.object.replace(`https://${domain}/u/`, "");
+ const name = req.body.object.replace(`https://${domain}/u/`, '');
await sendAcceptMessage(req.body, name, domain, req, res, targetDomain);
// get the followers JSON for the user
- const oldFollowersText = (await apDb.getFollowers()) || "[]";
+ const oldFollowersText = (await apDb.getFollowers()) || '[]';
// update followers
- let followers = parseJSON(oldFollowersText);
+ const followers = parseJSON(oldFollowersText);
if (followers) {
- followers.forEach((follower, idx, followers) => {
+ followers.forEach((follower, idx) => {
if (follower === req.body.actor) {
followers.splice(idx, 1);
}
});
}
- let newFollowersText = JSON.stringify(followers);
+ const newFollowersText = JSON.stringify(followers);
try {
- const updatedFollowers = await apDb.setFollowers(newFollowersText);
+ await apDb.setFollowers(newFollowersText);
return res.sendStatus(200);
} catch (e) {
- console.log("error storing followers after unfollow", e);
+ console.log('error storing followers after unfollow', e);
return res.status(500);
}
}
async function handleFollowAccepted(req, res) {
- const domain = req.app.get("domain");
- const apDb = req.app.get("apDb");
+ const apDb = req.app.get('apDb');
- const oldFollowingText = (await apDb.getFollowing()) || "[]";
+ const oldFollowingText = (await apDb.getFollowing()) || '[]';
- let follows = parseJSON(oldFollowingText);
+ let follows = parseJSON(oldFollowingText);
- if (follows) {
- follows.push(req.body.actor);
- // unique items
- follows = [...new Set(follows)];
- } else {
- follows = [req.body.actor];
- }
- let newFollowingText = JSON.stringify(follows);
+ if (follows) {
+ follows.push(req.body.actor);
+ // unique items
+ follows = [...new Set(follows)];
+ } else {
+ follows = [req.body.actor];
+ }
+ const newFollowingText = JSON.stringify(follows);
- try {
- // update into DB
- const newFollowing = await apDb.setFollowing(newFollowingText);
+ try {
+ // update into DB
+ await apDb.setFollowing(newFollowingText);
- console.log("updated following!");
- return res.status(200);
- } catch (e) {
- console.log("error storing follows after follow action", e);
- return res.status(500);
- }
+ console.log('updated following!');
+ return res.status(200);
+ } catch (e) {
+ console.log('error storing follows after follow action', e);
+ return res.status(500);
+ }
}
async function handleCommentOnBookmark(req, res, inReplyToGuid) {
- const apDb = req.app.get("apDb");
+ const apDb = req.app.get('apDb');
const bookmarkId = await apDb.getBookmarkIdFromMessageGuid(inReplyToGuid);
- if (typeof bookmarkId !== "number") {
- console.log("couldn't find a bookmark this message is related to");
- return res.sendStatus(400);
- }
+ if (typeof bookmarkId !== 'number') {
+ console.log("couldn't find a bookmark this message is related to");
+ return res.sendStatus(400);
+ }
- const bookmarkPermissions = await apDb.getPermissionsForBookmark(
- bookmarkId
- );
- const globalPermissions = await apDb.getGlobalPermissions();
-
- const bookmarkBlocks = bookmarkPermissions?.blocked?.split("\n") || [];
- const globalBlocks = globalPermissions?.blocked?.split("\n") || [];
-
- const bookmarkAllows = bookmarkPermissions?.allowed?.split("\n") || [];
- const globalAllows = globalPermissions?.allowed?.split("\n") || [];
-
- const blocklist = bookmarkBlocks
- .concat(globalBlocks)
- .filter((x) => x.match(/^@([^@]+)@(.+)$/));
- const allowlist = bookmarkAllows
- .concat(globalAllows)
- .filter((x) => x.match(/^@([^@]+)@(.+)$/));
-
- if (
- blocklist.length > 0 &&
- blocklist
- .map((username) => actorMatchesUsername(req.body.actor, username))
- .some((x) => x)
- ) {
- console.log(
- `Actor ${req.body.actor} matches a blocklist item, ignoring comment`
- );
- return res.sendStatus(403);
- }
+ const bookmarkPermissions = await apDb.getPermissionsForBookmark(bookmarkId);
+ const globalPermissions = await apDb.getGlobalPermissions();
- const response = await fetch(req.body.actor);
- const data = await response.json();
+ const bookmarkBlocks = bookmarkPermissions?.blocked?.split('\n') || [];
+ const globalBlocks = globalPermissions?.blocked?.split('\n') || [];
- const actorDomain = new URL(req.body.actor)?.hostname;
- const actorUsername = data.preferredUsername;
- const actor = `@${actorUsername}@${actorDomain}`;
+ const bookmarkAllows = bookmarkPermissions?.allowed?.split('\n') || [];
+ const globalAllows = globalPermissions?.allowed?.split('\n') || [];
- const commentUrl = req.body.object.id;
- let visible = 0;
- if (
- allowlist
- .map((username) => actorMatchesUsername(req.body.actor, username))
- .some((x) => x)
- ) {
- console.log(
- `Actor ${req.body.actor} matches an allowlist item, marking comment visible`
- );
- visible = 1;
- }
+ const blocklist = bookmarkBlocks.concat(globalBlocks).filter((x) => x.match(/^@([^@]+)@(.+)$/));
+ const allowlist = bookmarkAllows.concat(globalAllows).filter((x) => x.match(/^@([^@]+)@(.+)$/));
+
+ if (blocklist.length > 0 && blocklist.map((username) => actorMatchesUsername(req.body.actor, username)).some((x) => x)) {
+ console.log(`Actor ${req.body.actor} matches a blocklist item, ignoring comment`);
+ return res.sendStatus(403);
+ }
+
+ const response = await signedGetJSON(req.body.actor);
+ const data = await response.json();
+
+ const actorDomain = new URL(req.body.actor)?.hostname;
+ const actorUsername = data.preferredUsername;
+ const actor = `@${actorUsername}@${actorDomain}`;
+
+ const commentUrl = req.body.object.id;
+ let visible = 0;
+ if (allowlist.map((username) => actorMatchesUsername(req.body.actor, username)).some((x) => x)) {
+ console.log(`Actor ${req.body.actor} matches an allowlist item, marking comment visible`);
+ visible = 1;
+ }
- const bookmarksDb = req.app.get("bookmarksDb");
+ const bookmarksDb = req.app.get('bookmarksDb');
- bookmarksDb.createComment(
- bookmarkId,
- actor,
- commentUrl,
- req.body.object.content,
- visible
- );
+ bookmarksDb.createComment(bookmarkId, actor, commentUrl, req.body.object.content, visible);
return res.status(200);
}
@@ -208,7 +176,7 @@ async function handleFollowedPost(req, res) {
// store this for now
// TODO: determine if the actor is in your current follow list!
- const response = await fetch(`${req.body.actor}.json`);
+ const response = await signedGetJSON(`${req.body.actor}.json`);
const data = await response.json();
const actorDomain = new URL(req.body.actor)?.hostname;
@@ -217,15 +185,9 @@ async function handleFollowedPost(req, res) {
const commentUrl = req.body.object.id;
- const bookmarksDb = req.app.get("bookmarksDb");
+ const bookmarksDb = req.app.get('bookmarksDb');
- bookmarksDb.createComment(
- undefined,
- actor,
- commentUrl,
- req.body.object.content,
- false
- );
+ bookmarksDb.createComment(undefined, actor, commentUrl, req.body.object.content, false);
}
return res.status(200);
@@ -234,7 +196,7 @@ async function handleFollowedPost(req, res) {
async function handleDeleteRequest(req, res) {
console.log(JSON.stringify(req.body));
- const bookmarksDb = req.app.get("bookmarksDb");
+ const bookmarksDb = req.app.get('bookmarksDb');
const commentId = req.body?.object?.id;
@@ -245,31 +207,34 @@ async function handleDeleteRequest(req, res) {
return res.status(200);
}
-router.post("/", async function (req, res) {
+router.post('/', async function (req, res) {
// console.log(JSON.stringify(req.body));
- if (typeof req.body.object === "string" && req.body.type === "Follow") {
- return await handleFollowRequest(req, res);
- } else if (req.body.type === "Undo" && req.body.object?.type === "Follow") {
- return await handleUnfollow(req, res);
- } else if (req.body.type === "Accept" && req.body.object?.type === "Follow") {
- return await handleFollowAccepted(req, res);
- } else if (req.body.type === "Delete") {
- return await handleDeleteRequest(req, res);
- } else if (req.body.type === "Create" && req.body.object?.type === "Note") {
+ if (typeof req.body.object === 'string' && req.body.type === 'Follow') {
+ return handleFollowRequest(req, res);
+ }
+
+ if (req.body.type === 'Undo' && req.body.object?.type === 'Follow') {
+ return handleUnfollow(req, res);
+ }
+ if (req.body.type === 'Accept' && req.body.object?.type === 'Follow') {
+ return handleFollowAccepted(req, res);
+ }
+ if (req.body.type === 'Delete') {
+ return handleDeleteRequest(req, res);
+ }
+ if (req.body.type === 'Create' && req.body.object?.type === 'Note') {
console.log(JSON.stringify(req.body));
- const domain = req.app.get("domain");
- const inReplyToGuid = req.body.object?.inReplyTo?.match(
- `https://${domain}/m/(.+)`
- )?.[1];
+ const domain = req.app.get('domain');
+ const inReplyToGuid = req.body.object?.inReplyTo?.match(`https://${domain}/m/(.+)`)?.[1];
if (inReplyToGuid) {
return handleCommentOnBookmark(req, res, inReplyToGuid);
- } else {
- return handleFollowedPost(req, res);
}
-
- return res.sendStatus(400);
+ return handleFollowedPost(req, res);
}
+ return res.sendStatus(400);
});
+
+export default router;
diff --git a/src/routes/activitypub/message.js b/src/routes/activitypub/message.js
index e703f87..04c875b 100644
--- a/src/routes/activitypub/message.js
+++ b/src/routes/activitypub/message.js
@@ -1,21 +1,21 @@
import express from 'express';
-export const router = express.Router();
+const router = express.Router();
-router.get('/:guid', async function (req, res) {
- let guid = req.params.guid;
+router.get('/:guid', async (req, res) => {
+ const { guid } = req.params;
if (!guid) {
return res.status(400).send('Bad request.');
}
- else {
- let db = req.app.get('apDb');
- const result = await db.getMessage(guid);
- if (result === undefined) {
- return res.status(404).send(`No message found for ${guid}.`);
- }
- else {
- res.json(JSON.parse(result.message));
- }
+ const db = req.app.get('apDb');
+ const result = await db.getMessage(guid);
+
+ if (result === undefined) {
+ return res.status(404).send(`No message found for ${guid}.`);
}
+
+ return res.json(JSON.parse(result.message));
});
+
+export default router;
diff --git a/src/routes/activitypub/nodeinfo.js b/src/routes/activitypub/nodeinfo.js
index 883e7a3..99400c3 100644
--- a/src/routes/activitypub/nodeinfo.js
+++ b/src/routes/activitypub/nodeinfo.js
@@ -1,18 +1,18 @@
// implementation of http://nodeinfo.diaspora.software/protocol.html
-import express from "express";
-import {instanceType, instanceVersion} from "../../util.js";
+import express from 'express';
+import { instanceType, instanceVersion } from '../../util.js';
-export const router = express.Router();
+const router = express.Router();
-router.get("/", async function (req, res) {
- let domain = req.app.get("domain");
+router.get('/', async (req, res) => {
+ const domain = req.app.get('domain');
- if (req.originalUrl == "/.well-known/nodeinfo") {
- let thisNode = {
+ if (req.originalUrl === '/.well-known/nodeinfo') {
+ const thisNode = {
links: [
{
- rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
+ rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
href: `https://${domain}/nodeinfo/2.0`,
},
],
@@ -20,42 +20,40 @@ router.get("/", async function (req, res) {
res.json(thisNode);
}
- if (req.originalUrl == "/nodeinfo/2.0") {
-
- const bookmarksDb = req.app.get("bookmarksDb");
- let bookmarkCount = await bookmarksDb.getBookmarkCount();
+ if (req.originalUrl === '/nodeinfo/2.0') {
+ const bookmarksDb = req.app.get('bookmarksDb');
+ const bookmarkCount = await bookmarksDb.getBookmarkCount();
// TODO: activeMonth and activeHalfyear should be dynamic, currently static
- let nodeInfo = {
+ const nodeInfo = {
version: 2.0,
software: {
name: instanceType,
version: instanceVersion,
},
- protocols: [
- "activitypub"
- ],
+ protocols: ['activitypub'],
services: {
- outbound: ["atom1.0"],
- inbound: []
+ outbound: ['atom1.0'],
+ inbound: [],
},
usage: {
users: {
total: 1,
activeMonth: 1,
- activeHalfyear: 1
+ activeHalfyear: 1,
},
localPosts: bookmarkCount,
},
openRegistrations: false,
- metadata: {}
+ metadata: {},
};
// spec requires setting this, majority of implementations
// appear to not bother with it?
- res.type('application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"')
+ res.type('application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"');
res.json(nodeInfo);
-
}
});
+
+export default router;
diff --git a/src/routes/activitypub/user.js b/src/routes/activitypub/user.js
index b4c4d6c..dbc3b21 100644
--- a/src/routes/activitypub/user.js
+++ b/src/routes/activitypub/user.js
@@ -1,102 +1,98 @@
-import express from "express";
-import { createNoteObject } from "../../activitypub.js";
+import express from 'express';
+import { createNoteObject } from '../../activitypub.js';
-export const router = express.Router();
+const router = express.Router();
-router.get("/:name", async function (req, res) {
- let name = req.params.name;
+router.get('/:name', async (req, res) => {
+ let { name } = req.params;
if (!name) {
- return res.status(400).send("Bad request.");
- } else {
- const db = req.app.get("apDb");
- const domain = req.app.get("domain");
- const username = name;
- name = `${name}@${domain}`;
-
- const actor = await db.getActor(name);
-
- if (actor === undefined) {
- return res.status(404).send(`No actor record found for ${name}.`);
- } else {
- let tempActor = JSON.parse(actor);
- // Added this followers URI for Pleroma compatibility, see https://github.com/dariusk/rss-to-activitypub/issues/11#issuecomment-471390881
- // New Actors should have this followers URI but in case of migration from an old version this will add it in on the fly
- if (tempActor.followers === undefined) {
- tempActor.followers = `https://${domain}/u/${username}/followers`;
- }
- if (tempActor.outbox === undefined) {
- tempActor.outbox = `https://${domain}/u/${username}/outbox`;
- }
- res.json(tempActor);
- }
+ return res.status(400).send('Bad request.');
+ }
+ const db = req.app.get('apDb');
+ const domain = req.app.get('domain');
+ const username = name;
+ name = `${name}@${domain}`;
+
+ const actor = await db.getActor();
+
+ if (actor === undefined) {
+ return res.status(404).send(`No actor record found for ${name}.`);
+ }
+ const tempActor = JSON.parse(actor);
+ // Added this followers URI for Pleroma compatibility, see https://github.com/dariusk/rss-to-activitypub/issues/11#issuecomment-471390881
+ // New Actors should have this followers URI but in case of migration from an old version this will add it in on the fly
+ if (tempActor.followers === undefined) {
+ tempActor.followers = `https://${domain}/u/${username}/followers`;
+ }
+ if (tempActor.outbox === undefined) {
+ tempActor.outbox = `https://${domain}/u/${username}/outbox`;
}
+ return res.json(tempActor);
});
-router.get("/:name/followers", async function (req, res) {
- let name = req.params.name;
+router.get('/:name/followers', async (req, res) => {
+ const { name } = req.params;
if (!name) {
- return res.status(400).send("Bad request.");
- } else {
- let db = req.app.get("apDb");
- let domain = req.app.get("domain");
+ return res.status(400).send('Bad request.');
+ }
+ const db = req.app.get('apDb');
+ const domain = req.app.get('domain');
- let followers = await db.getFollowers();
+ let followers = await db.getFollowers();
- if (followers === undefined) {
- followers = [];
- } else {
- followers = JSON.parse(followers);
- }
+ if (followers === undefined) {
+ followers = [];
+ } else {
+ followers = JSON.parse(followers);
+ }
- let followersCollection = {
- type: "OrderedCollection",
+ const followersCollection = {
+ type: 'OrderedCollection',
+ totalItems: followers?.length || 0,
+ id: `https://${domain}/u/${name}/followers`,
+ first: {
+ type: 'OrderedCollectionPage',
totalItems: followers?.length || 0,
- id: `https://${domain}/u/${name}/followers`,
- first: {
- type: "OrderedCollectionPage",
- totalItems: followers?.length || 0,
- partOf: `https://${domain}/u/${name}/followers`,
- orderedItems: followers,
- id: `https://${domain}/u/${name}/followers?page=1`,
- },
- "@context": ["https://www.w3.org/ns/activitystreams"],
- };
- res.json(followersCollection);
- }
+ partOf: `https://${domain}/u/${name}/followers`,
+ orderedItems: followers,
+ id: `https://${domain}/u/${name}/followers?page=1`,
+ },
+ '@context': ['https://www.w3.org/ns/activitystreams'],
+ };
+ return res.json(followersCollection);
});
-router.get("/:name/following", async function (req, res) {
- let name = req.params.name;
+router.get('/:name/following', async (req, res) => {
+ const { name } = req.params;
if (!name) {
- return res.status(400).send("Bad request.");
- } else {
- let db = req.app.get("apDb");
- let domain = req.app.get("domain");
+ return res.status(400).send('Bad request.');
+ }
+ const db = req.app.get('apDb');
+ const domain = req.app.get('domain');
- const followingText = await db.getFollowing() || "[]";
- const following = JSON.parse(followingText);
+ const followingText = (await db.getFollowing()) || '[]';
+ const following = JSON.parse(followingText);
- let followingCollection = {
- type: "OrderedCollection",
+ const followingCollection = {
+ type: 'OrderedCollection',
+ totalItems: following?.length || 0,
+ id: `https://${domain}/u/${name}/following`,
+ first: {
+ type: 'OrderedCollectionPage',
totalItems: following?.length || 0,
- id: `https://${domain}/u/${name}/following`,
- first: {
- type: "OrderedCollectionPage",
- totalItems: following?.length || 0,
- partOf: `https://${domain}/u/${name}/following`,
- orderedItems: following,
- id: `https://${domain}/u/${name}/following?page=1`,
- },
- "@context": ["https://www.w3.org/ns/activitystreams"],
- };
- res.json(followingCollection);
- }
+ partOf: `https://${domain}/u/${name}/following`,
+ orderedItems: following,
+ id: `https://${domain}/u/${name}/following?page=1`,
+ },
+ '@context': ['https://www.w3.org/ns/activitystreams'],
+ };
+ return res.json(followingCollection);
});
-router.get("/:name/outbox", async function (req, res) {
- const domain = req.app.get("domain");
- const account = req.app.get("account");
- const bookmarksDb = req.app.get("bookmarksDb");
+router.get('/:name/outbox', async (req, res) => {
+ const domain = req.app.get('domain');
+ const account = req.app.get('account');
+ const bookmarksDb = req.app.get('bookmarksDb');
const page = req.params.page || 1;
if (page < 1) return res.status(400);
@@ -104,24 +100,25 @@ router.get("/:name/outbox", async function (req, res) {
const limit = 20;
const offset = (page - 1) * limit;
const totalBookmarkCount = await bookmarksDb.getBookmarkCount();
- const totalPages = Math.ceil(totalBookmarkCount / limit);
const bookmarks = await bookmarksDb.getBookmarks(limit, offset);
- const messages = bookmarks.map((b) => { return createNoteObject(b, account, domain)});
+ const messages = bookmarks.map((b) => createNoteObject(b, account, domain));
const outboxCollection = {
- type: "OrderedCollection",
+ type: 'OrderedCollection',
totalItems: totalBookmarkCount,
id: `https://${domain}/u/${account}/outbox`,
first: {
- type: "OrderedCollectionPage",
+ type: 'OrderedCollectionPage',
totalItems: messages.length,
partOf: `https://${domain}/u/${account}/outbox`,
orderedItems: messages,
id: `https://${domain}/u/${account}/outbox?page=${page}`,
- next: `https://${domain}/u/${account}/outbox?page=${page+1}`
+ next: `https://${domain}/u/${account}/outbox?page=${page + 1}`,
},
- "@context": ["https://www.w3.org/ns/activitystreams"],
+ '@context': ['https://www.w3.org/ns/activitystreams'],
};
return res.json(outboxCollection);
});
+
+export default router;
diff --git a/src/routes/activitypub/webfinger.js b/src/routes/activitypub/webfinger.js
index 2e4ccb1..7ceff24 100644
--- a/src/routes/activitypub/webfinger.js
+++ b/src/routes/activitypub/webfinger.js
@@ -1,21 +1,21 @@
import express from 'express';
-export const router = express.Router();
+const router = express.Router();
-router.get('/', async function (req, res) {
- let resource = req.query.resource;
+router.get('/', async (req, res) => {
+ const { resource } = req.query;
if (!resource || !resource.includes('acct:')) {
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.');
}
- else {
- let name = resource.replace('acct:','');
- let db = req.app.get('apDb');
- const webfinger = await db.getWebfinger(name);
- if (webfinger === undefined) {
- return res.status(404).send(`No webfinger record found for ${name}.`);
- }
- else {
- res.json(JSON.parse(webfinger));
- }
+
+ const name = resource.replace('acct:', '');
+ const db = req.app.get('apDb');
+ const webfinger = await db.getWebfinger();
+ if (webfinger === undefined) {
+ return res.status(404).send(`No webfinger record found for ${name}.`);
}
+
+ return res.json(JSON.parse(webfinger));
});
+
+export default router;
diff --git a/src/routes/admin.js b/src/routes/admin.js
index 0d5eb73..7ed9282 100644
--- a/src/routes/admin.js
+++ b/src/routes/admin.js
@@ -1,137 +1,129 @@
-import express from "express";
-import fetch from "node-fetch";
-import { domain, actorInfo, parseJSON } from "../util.js";
-import { isAuthenticated } from "../session-auth.js";
-import {
- lookupActorInfo,
- createFollowMessage,
- createUnfollowMessage,
- signAndSend,
- getInboxFromActorProfile
-} from "../activitypub.js";
-const DATA_PATH = "/app/.data";
-
-export const router = express.Router();
-
-router.get("/", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { title: "Admin" };
- params.layout = "admin";
+import express from 'express';
+import { domain, actorInfo, parseJSON } from '../util.js';
+import { isAuthenticated } from '../session-auth.js';
+import { lookupActorInfo, createFollowMessage, createUnfollowMessage, signAndSend, getInboxFromActorProfile } from '../activitypub.js';
+
+const DATA_PATH = '/app/.data';
+
+const router = express.Router();
+
+router.get('/', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { title: 'Admin' };
+ params.layout = 'admin';
params.bookmarklet = `javascript:(function(){w=window.open('https://${domain}/bookmark/popup?url='+encodeURIComponent(window.location.href)+'&highlight='+encodeURIComponent(window.getSelection().toString()),'postmarks','scrollbars=yes,width=550,height=600');})();`;
- return res.render("admin", params);
+ return res.render('admin', params);
});
-router.get("/bookmarks", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { title: "Admin: Import bookmarks" };
- params.layout = "admin";
+router.get('/bookmarks', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { title: 'Admin: Import bookmarks' };
+ params.layout = 'admin';
- return res.render("admin/bookmarks", params);
+ return res.render('admin/bookmarks', params);
});
-router.get("/followers", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { title: "Admin: Permissions & followers" };
- params.layout = "admin";
+router.get('/followers', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { title: 'Admin: Permissions & followers' };
+ params.layout = 'admin';
- const apDb = req.app.get("apDb");
+ const apDb = req.app.get('apDb');
if (actorInfo.disabled) {
- return res.render("nonfederated", params);
+ return res.render('nonfederated', params);
}
const permissions = await apDb.getGlobalPermissions();
try {
const followers = await apDb.getFollowers();
- params.followers = JSON.parse(followers || "[]");
+ params.followers = JSON.parse(followers || '[]');
} catch (e) {
- console.log("Error fetching followers for admin page");
+ console.log('Error fetching followers for admin page');
}
try {
const blocks = await apDb.getBlocks();
- params.blocks = JSON.parse(blocks || "[]");
+ params.blocks = JSON.parse(blocks || '[]');
} catch (e) {
- console.log("Error fetching blocks for admin page");
+ console.log('Error fetching blocks for admin page');
}
- params.allowed = permissions?.allowed || "";
- params.blocked = permissions?.blocked || "";
+ params.allowed = permissions?.allowed || '';
+ params.blocked = permissions?.blocked || '';
- return res.render("admin/followers", params);
+ return res.render('admin/followers', params);
});
-router.get("/following", isAuthenticated, async (req, res) => {
- let params = req.query.raw
- ? {}
- : { title: "Admin: Manage your federated follows" };
- params.layout = "admin";
+router.get('/following', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { title: 'Admin: Manage your federated follows' };
+ params.layout = 'admin';
- const apDb = req.app.get("apDb");
+ const apDb = req.app.get('apDb');
if (actorInfo.disabled) {
- return res.render("nonfederated", params);
+ return res.render('nonfederated', params);
}
try {
const following = await apDb.getFollowing();
- params.following = JSON.parse(following || "[]");
+ params.following = JSON.parse(following || '[]');
} catch (e) {
- console.log("Error fetching followers for admin page");
+ console.log('Error fetching followers for admin page');
}
- return res.render("admin/following", params);
+ return res.render('admin/following', params);
});
-router.get("/data", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { title: "Admin: Data export" };
- params.layout = "admin";
+router.get('/data', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { title: 'Admin: Data export' };
+ params.layout = 'admin';
- return res.render("admin/data", params);
+ return res.render('admin/data', params);
});
-router.get("/bookmarks.db", isAuthenticated, async (req, res) => {
+router.get('/bookmarks.db', isAuthenticated, async (req, res) => {
const filePath = `${DATA_PATH}/bookmarks.db`;
- res.setHeader("Content-Type", "application/vnd.sqlite3");
- res.setHeader("Content-Disposition", 'attachment; filename="bookmarks.db"');
+ res.setHeader('Content-Type', 'application/vnd.sqlite3');
+ res.setHeader('Content-Disposition', 'attachment; filename="bookmarks.db"');
res.download(filePath);
});
-router.get("/activitypub.db", isAuthenticated, async (req, res) => {
+router.get('/activitypub.db', isAuthenticated, async (req, res) => {
const filePath = `${DATA_PATH}/activitypub.db`;
- res.setHeader("Content-Type", "application/vnd.sqlite3");
- res.setHeader("Content-Disposition", 'attachment; filename="activitypub.db"');
+ res.setHeader('Content-Type', 'application/vnd.sqlite3');
+ res.setHeader('Content-Disposition', 'attachment; filename="activitypub.db"');
res.download(filePath);
});
-router.post("/followers/block", isAuthenticated, async (req, res) => {
- let db = req.app.get("apDb");
+router.post('/followers/block', isAuthenticated, async (req, res) => {
+ const db = req.app.get('apDb');
- const oldFollowersText = (await db.getFollowers()) || "[]";
+ const oldFollowersText = (await db.getFollowers()) || '[]';
// update followers
- let followers = parseJSON(oldFollowersText);
+ const followers = parseJSON(oldFollowersText);
if (followers) {
- followers.forEach((follower, idx, followers) => {
+ followers.forEach((follower, idx) => {
if (follower === req.body.actor) {
followers.splice(idx, 1);
}
});
}
- let newFollowersText = JSON.stringify(followers);
+ const newFollowersText = JSON.stringify(followers);
try {
- const updatedFollowers = await db.setFollowers(newFollowersText);
+ await db.setFollowers(newFollowersText);
} catch (e) {
- console.log("error storing followers after unfollow", e);
+ console.log('error storing followers after unfollow', e);
}
- const oldBlocksText = (await db.getBlocks()) || "[]";
+ const oldBlocksText = (await db.getBlocks()) || '[]';
let blocks = parseJSON(oldBlocksText);
@@ -142,48 +134,47 @@ router.post("/followers/block", isAuthenticated, async (req, res) => {
} else {
blocks = [req.body.actor];
}
- let newBlocksText = JSON.stringify(blocks);
+ const newBlocksText = JSON.stringify(blocks);
try {
// update into DB
- const newBlocks = await db.setBlocks(newBlocksText);
+ await db.setBlocks(newBlocksText);
- console.log("updated blocks!");
+ console.log('updated blocks!');
} catch (e) {
- console.log("error storing blocks after block action", e);
+ console.log('error storing blocks after block action', e);
}
- res.redirect("/admin/followers");
+ res.redirect('/admin/followers');
});
-router.post("/followers/unblock", isAuthenticated, async (req, res) => {
- let db = req.app.get("apDb");
+router.post('/followers/unblock', isAuthenticated, async (req, res) => {
+ const db = req.app.get('apDb');
- const oldBlocksText = (await db.getBlocks()) || "[]";
+ const oldBlocksText = (await db.getBlocks()) || '[]';
- let blocks = parseJSON(oldBlocksText);
+ const blocks = parseJSON(oldBlocksText);
if (blocks) {
- blocks.forEach((block, idx, blocks) => {
+ blocks.forEach((block, idx) => {
if (block === req.body.actor) {
blocks.splice(idx, 1);
}
});
}
- let newBlocksText = JSON.stringify(blocks);
+ const newBlocksText = JSON.stringify(blocks);
try {
- const updatedBlocks = await db.setBlocks(newBlocksText);
+ await db.setBlocks(newBlocksText);
} catch (e) {
- console.log("error storing blocks after unblock action", e);
+ console.log('error storing blocks after unblock action', e);
}
- res.redirect("/admin/followers");
+ res.redirect('/admin/followers');
});
-router.post("/following/follow", isAuthenticated, async (req, res) => {
- const db = req.app.get("apDb");
- const account = req.app.get("account");
- const domain = req.app.get("domain");
+router.post('/following/follow', isAuthenticated, async (req, res) => {
+ const db = req.app.get('apDb');
+ const account = req.app.get('account');
const canonicalUrl = await lookupActorInfo(req.body.actor);
@@ -191,39 +182,26 @@ router.post("/following/follow", isAuthenticated, async (req, res) => {
const inbox = await getInboxFromActorProfile(canonicalUrl);
if (inbox) {
- const followMessage = await createFollowMessage(
- account,
- domain,
- canonicalUrl,
- db
- );
- signAndSend(
- followMessage,
- account,
- domain,
- db,
- req.body.actor.split("@").slice(-1),
- inbox
- );
+ const followMessage = await createFollowMessage(account, domain, canonicalUrl, db);
+ signAndSend(followMessage, account, domain, db, req.body.actor.split('@').slice(-1), inbox);
}
- return res.redirect("/admin/following");
+ return res.redirect('/admin/following');
} catch (e) {
console.log(e.message);
return res.status(500).send("Couldn't process follow request");
}
});
-router.post("/following/unfollow", isAuthenticated, async (req, res) => {
- const db = req.app.get("apDb");
- const account = req.app.get("account");
- const domain = req.app.get("domain");
+router.post('/following/unfollow', isAuthenticated, async (req, res) => {
+ const db = req.app.get('apDb');
+ const account = req.app.get('account');
- const oldFollowsText = (await db.getFollowing()) || "[]";
+ const oldFollowsText = (await db.getFollowing()) || '[]';
- let follows = parseJSON(oldFollowsText);
+ const follows = parseJSON(oldFollowsText);
if (follows) {
- follows.forEach((follow, idx, follows) => {
+ follows.forEach((follow, idx) => {
if (follow === req.body.actor) {
follows.splice(idx, 1);
}
@@ -233,47 +211,33 @@ router.post("/following/unfollow", isAuthenticated, async (req, res) => {
const unfollowMessage = createUnfollowMessage(account, domain, req.body.actor, db);
- signAndSend(
- unfollowMessage,
- account,
- domain,
- db,
- new URL(req.body.actor).hostname,
- inbox
- );
-
- const oldFollowsText = (await db.getFollowing()) || "[]";
-
- follows.forEach((follow, idx, follows) => {
- if (follow === req.body.actor) {
- follows.splice(idx, 1);
- }
- });
+ signAndSend(unfollowMessage, account, domain, db, new URL(req.body.actor).hostname, inbox);
- let newFollowsText = JSON.stringify(follows);
+ const newFollowsText = JSON.stringify(follows);
try {
- const updatedFollows = await db.setFollowing(newFollowsText);
+ await db.setFollowing(newFollowsText);
} catch (e) {
- console.log("error storing follows after unfollow action", e);
+ console.log('error storing follows after unfollow action', e);
}
- res.redirect("/admin/following");
- } else {
- return res.status(500).send('Encountered an error processing existing following list');
+ return res.redirect('/admin/following');
}
+ return res.status(500).send('Encountered an error processing existing following list');
});
-router.post("/permissions", isAuthenticated, async (req, res) => {
- const apDb = req.app.get("apDb");
+router.post('/permissions', isAuthenticated, async (req, res) => {
+ const apDb = req.app.get('apDb');
await apDb.setGlobalPermissions(req.body.allowed, req.body.blocked);
- res.redirect("/admin");
+ res.redirect('/admin');
});
-router.post("/reset", isAuthenticated, async (req, res) => {
- const db = req.app.get("bookmarksDb");
+router.post('/reset', isAuthenticated, async (req, res) => {
+ const db = req.app.get('bookmarksDb');
await db.deleteAllBookmarks();
- res.redirect("/admin");
+ res.redirect('/admin');
});
+
+export default router;
diff --git a/src/routes/auth.js b/src/routes/auth.js
index 5bb9329..32237c5 100644
--- a/src/routes/auth.js
+++ b/src/routes/auth.js
@@ -1,11 +1,11 @@
-import express from "express";
-import { login, logout } from "../session-auth.js";
+import express from 'express';
+import { login, logout } from '../session-auth.js';
-export const router = express.Router();
+const router = express.Router();
-router.get("/login", (req, res) => {
- return res.render("login", { sendTo: req.query.sendTo });
-});
+router.get('/login', (req, res) => res.render('login', { sendTo: req.query.sendTo }));
-router.post("/login", login);
-router.get("/logout", logout);
+router.post('/login', login);
+router.get('/logout', logout);
+
+export default router;
diff --git a/src/routes/bookmark.js b/src/routes/bookmark.js
index 9164687..eef9aa9 100644
--- a/src/routes/bookmark.js
+++ b/src/routes/bookmark.js
@@ -1,27 +1,28 @@
-import express from "express";
-import ogScraper from "open-graph-scraper";
+import express from 'express';
+import ogScraper from 'open-graph-scraper';
-import { data, account, domain, removeEmpty } from "../util.js";
-import { broadcastMessage } from "../activitypub.js";
-import { isAuthenticated } from "../session-auth.js";
+import { data, account, domain, removeEmpty } from '../util.js';
+import { broadcastMessage } from '../activitypub.js';
+import { isAuthenticated } from '../session-auth.js';
-export const router = express.Router();
+const router = express.Router();
+export default router;
-router.get("/new", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { ephemeral: false };
- const bookmarksDb = req.app.get("bookmarksDb");
+router.get('/new', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { ephemeral: false };
+ const bookmarksDb = req.app.get('bookmarksDb');
if (req.query.url !== undefined) {
params.bookmark = {
url: decodeURI(req.query.url),
- description: "",
+ description: '',
};
- if (req.query?.highlight !== undefined && req.query?.highlight !== "") {
- params.bookmark.description += `"${decodeURI(req.query.highlight)}"`;
+ if (req.query?.highlight !== undefined && req.query?.highlight !== '') {
+ params.bookmark.description += `"${decodeURI(req.query.highlight)}"`;
}
try {
- let meta = await ogScraper({ url: decodeURI(req.query.url) });
+ const meta = await ogScraper({ url: decodeURI(req.query.url) });
if (meta?.result?.ogDescription !== undefined) {
params.bookmark.description += `"${meta?.result?.ogDescription}"`;
@@ -33,22 +34,22 @@ router.get("/new", isAuthenticated, async (req, res) => {
}
if (req.query?.via !== undefined) {
- if (params.bookmark.description !== "") {
- params.bookmark.description += "\n\n";
+ if (params.bookmark.description !== '') {
+ params.bookmark.description += '\n\n';
}
params.bookmark.description += `(via ${req.query.via})`;
}
params.tags = await bookmarksDb.getTags();
- params.title = `New Bookmark`;
+ params.title = 'New Bookmark';
params.creating = true;
- return res.render("edit_bookmark", params);
+ return res.render('edit_bookmark', params);
});
-router.get("/popup", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { ephemeral: true };
- const bookmarksDb = req.app.get("bookmarksDb");
+router.get('/popup', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { ephemeral: true };
+ const bookmarksDb = req.app.get('bookmarksDb');
if (req.query.url !== undefined) {
params.bookmark = {
@@ -56,9 +57,9 @@ router.get("/popup", isAuthenticated, async (req, res) => {
};
try {
- let meta = await ogScraper({ url: decodeURI(req.query.url) });
+ const meta = await ogScraper({ url: decodeURI(req.query.url) });
- if (req.query?.highlight !== undefined && req.query?.highlight !== "") {
+ if (req.query?.highlight !== undefined && req.query?.highlight !== '') {
params.bookmark.description = `"${decodeURI(req.query.highlight)}"`;
} else if (meta?.result?.ogDescription !== undefined) {
params.bookmark.description = `"${meta?.result?.ogDescription}"`;
@@ -70,16 +71,16 @@ router.get("/popup", isAuthenticated, async (req, res) => {
}
params.tags = await bookmarksDb.getTags();
- params.title = `New Bookmark`;
- params.layout = "popup";
+ params.title = 'New Bookmark';
+ params.layout = 'popup';
params.creating = true;
- return res.render("edit_bookmark", params);
+ return res.render('edit_bookmark', params);
});
-router.get("/:id", async (req, res) => {
- let params = {};
- const bookmarksDb = req.app.get("bookmarksDb");
+router.get('/:id', async (req, res) => {
+ const params = {};
+ const bookmarksDb = req.app.get('bookmarksDb');
params.tags = await bookmarksDb.getTags();
@@ -94,20 +95,18 @@ router.get("/:id", async (req, res) => {
params.comments = comments;
}
- return req.query.raw ? res.send(params) : res.render("bookmark", params);
+ return req.query.raw ? res.send(params) : res.render('bookmark', params);
});
-router.get("/:id/edit", isAuthenticated, async (req, res) => {
- let params = req.query.raw ? {} : { ephemeral: false };
- const bookmarksDb = req.app.get("bookmarksDb");
- const apDb = req.app.get("apDb");
+router.get('/:id/edit', isAuthenticated, async (req, res) => {
+ const params = req.query.raw ? {} : { ephemeral: false };
+ const bookmarksDb = req.app.get('bookmarksDb');
+ const apDb = req.app.get('apDb');
params.tags = await bookmarksDb.getTags();
const bookmark = await bookmarksDb.getBookmark(req.params.id);
- bookmark.tagsArray = encodeURIComponent(
- JSON.stringify(bookmark.tags?.split(" ").map((b) => b.slice(1)) || [])
- );
+ bookmark.tagsArray = encodeURIComponent(JSON.stringify(bookmark.tags?.split(' ').map((b) => b.slice(1)) || []));
const comments = await bookmarksDb.getAllCommentsForBookmark(req.params.id);
if (!bookmark) {
@@ -122,44 +121,38 @@ router.get("/:id/edit", isAuthenticated, async (req, res) => {
params.comments = comments;
}
- return req.query.raw ? res.send(params) : res.render("edit_bookmark", params);
+ return req.query.raw ? res.send(params) : res.render('edit_bookmark', params);
});
-router.post("/:id/delete", isAuthenticated, async (req, res) => {
+router.post('/:id/delete', isAuthenticated, async (req, res) => {
const params = {};
const { id } = req.params;
- const bookmarksDb = req.app.get("bookmarksDb");
- const apDb = req.app.get("apDb");
+ const bookmarksDb = req.app.get('bookmarksDb');
+ const apDb = req.app.get('apDb');
await bookmarksDb.deleteBookmark(id);
- broadcastMessage({ id }, "delete", apDb, account, domain);
+ broadcastMessage({ id }, 'delete', apDb, account, domain);
- return req.query.raw ? res.send(params) : res.redirect("/");
+ return req.query.raw ? res.send(params) : res.redirect('/');
});
-router.post(
- "/:id/delete_hidden_comments",
- isAuthenticated,
- async (req, res) => {
- const params = {};
- const { id } = req.params;
- const bookmarksDb = req.app.get("bookmarksDb");
+router.post('/:id/delete_hidden_comments', isAuthenticated, async (req, res) => {
+ const params = {};
+ const { id } = req.params;
+ const bookmarksDb = req.app.get('bookmarksDb');
- await bookmarksDb.deleteHiddenCommentsForBookmark(id);
+ await bookmarksDb.deleteHiddenCommentsForBookmark(id);
- return req.query.raw
- ? res.send(params)
- : res.redirect(`/bookmark/${id}/edit`);
- }
-);
+ return req.query.raw ? res.send(params) : res.redirect(`/bookmark/${id}/edit`);
+});
-router.post("/multiadd", isAuthenticated, async (req, res) => {
- const bookmarksDb = req.app.get("bookmarksDb");
+router.post('/multiadd', isAuthenticated, async (req, res) => {
+ const bookmarksDb = req.app.get('bookmarksDb');
- await req.body.urls.split("\n").forEach(async (url) => {
+ await req.body.urls.split('\n').forEach(async (url) => {
try {
- //use the constructor to do a rough URL validity check
+ // eslint-disable-next-line no-new
new URL(url);
} catch (e) {
console.log(`unable to parse url ${url}`);
@@ -169,7 +162,7 @@ router.post("/multiadd", isAuthenticated, async (req, res) => {
let meta = {};
try {
- meta = await ogScraper({ url: url.replace(/(\r\n|\n|\r)/gm,"") }); // remove line break from URL value
+ meta = await ogScraper({ url: url.replace(/(\r\n|\n|\r)/gm, '') }); // remove line break from URL value
if (meta?.result?.ogDescription !== undefined) {
meta.result.ogDescription = `"${meta.result.ogDescription}"`;
}
@@ -184,12 +177,12 @@ router.post("/multiadd", isAuthenticated, async (req, res) => {
});
});
- return req.query.raw ? res.sendStatus(200) : res.redirect("/");
+ return req.query.raw ? res.sendStatus(200) : res.redirect('/');
});
-router.post("/:id?", isAuthenticated, async (req, res) => {
- const bookmarksDb = req.app.get("bookmarksDb");
- const apDb = req.app.get("apDb");
+router.post('/:id?', isAuthenticated, async (req, res) => {
+ const bookmarksDb = req.app.get('bookmarksDb');
+ const apDb = req.app.get('apDb');
const params = {};
const { id } = req.params;
@@ -197,24 +190,21 @@ router.post("/:id?", isAuthenticated, async (req, res) => {
// TODO: lol this pattern is so horrible
try {
+ // eslint-disable-next-line no-new
new URL(req.body.url);
} catch {
- res.send("error: invalid URL");
+ res.send('error: invalid URL');
return;
}
console.log(req.body.tags);
- let tags = JSON.parse(decodeURIComponent(req.body.tags) || "[]")
+ let tags = JSON.parse(decodeURIComponent(req.body.tags) || '[]')
?.map((x) => `#${x}`)
- .join(" ");
- const hashtagFormat = new RegExp(
- /^(#[a-zA-Z0-9.\-_:]+ )*#[a-zA-Z0-9.\-_:]+\s*$/gm
- );
+ .join(' ');
+ const hashtagFormat = /^(#[a-zA-Z0-9.\-_:]+ )*#[a-zA-Z0-9.\-_:]+\s*$/gm;
if (tags.length > 0) {
if (!hashtagFormat.test(tags)) {
- res.send(
- `invalid tags: ${tags}\nmust be in #hashtag #format, tag name supports a-z, A-Z, 0-9 and the following word separators: -_.`
- );
+ res.send(`invalid tags: ${tags}\nmust be in #hashtag #format, tag name supports a-z, A-Z, 0-9 and the following word separators: -_.`);
return;
}
} else {
@@ -232,17 +222,13 @@ router.post("/:id?", isAuthenticated, async (req, res) => {
description: req.body.description.trim(),
tags,
});
- await apDb.setPermissionsForBookmark(
- id,
- req.body.allowed || "",
- req.body.blocked || ""
- );
+ await apDb.setPermissionsForBookmark(id, req.body.allowed || '', req.body.blocked || '');
- broadcastMessage(bookmark, "update", apDb, account, domain);
+ broadcastMessage(bookmark, 'update', apDb, account, domain);
}
} else {
- const noTitle = req.body.title === "";
- const noDescription = req.body.title === "";
+ const noTitle = req.body.title === '';
+ const noDescription = req.body.title === '';
let meta = {};
if (noTitle || noDescription) {
try {
@@ -263,12 +249,12 @@ router.post("/:id?", isAuthenticated, async (req, res) => {
bookmark = await bookmarksDb.createBookmark({
// STRONG PARAMETERS
url: mergedObject.url.trim(),
- title: mergedObject.title?.trim() || "Untitled",
- description: mergedObject.description?.trim() || "",
+ title: mergedObject.title?.trim() || 'Untitled',
+ description: mergedObject.description?.trim() || '',
tags,
});
- broadcastMessage(bookmark, "create", apDb, account, domain);
+ broadcastMessage(bookmark, 'create', apDb, account, domain);
}
params.bookmarks = bookmark;
@@ -277,8 +263,8 @@ router.post("/:id?", isAuthenticated, async (req, res) => {
// Return the info to the client
if (req.query.raw) {
res.send(params);
- } else if (req.query.ephemeral === "true") {
- res.send("<script>window.close();</script>");
+ } else if (req.query.ephemeral === 'true') {
+ res.send('<script>window.close();</script>');
} else {
res.redirect(`/bookmark/${bookmark.id}`);
}
diff --git a/src/routes/comment.js b/src/routes/comment.js
index 1ff464f..73091a9 100644
--- a/src/routes/comment.js
+++ b/src/routes/comment.js
@@ -1,12 +1,14 @@
import express from 'express';
-import { isAuthenticated } from "../session-auth.js";
+import { isAuthenticated } from '../session-auth.js';
-export const router = express.Router();
+const router = express.Router();
-router.post("/:id/toggle", isAuthenticated, async (req, res) => {
- const bookmarksDb = req.app.get("bookmarksDb");
+router.post('/:id/toggle', isAuthenticated, async (req, res) => {
+ const bookmarksDb = req.app.get('bookmarksDb');
await bookmarksDb.toggleCommentVisibility(req.params.id);
- return res.redirect(req.get("Referrer"));
+ return res.redirect(req.get('Referrer'));
});
+
+export default router;
diff --git a/src/routes/core.js b/src/routes/core.js
index a8c057b..e6b88ee 100644
--- a/src/routes/core.js
+++ b/src/routes/core.js
@@ -1,12 +1,13 @@
import express from 'express';
+import * as linkify from 'linkifyjs';
import { data, actorInfo } from '../util.js';
import { isAuthenticated } from '../session-auth.js';
-import * as linkify from 'linkifyjs';
-export const router = express.Router();
+const router = express.Router();
+export default router;
-router.get("/", async (req, res) => {
- let params = {};
+router.get('/', async (req, res) => {
+ const params = {};
const bookmarksDb = req.app.get('bookmarksDb');
@@ -16,7 +17,7 @@ router.get("/", async (req, res) => {
const currentPage = (limit + offset) / limit;
const totalPages = Math.ceil(totalBookmarkCount / limit);
- let buildTitle = `Latest bookmarks`;
+ let buildTitle = 'Latest bookmarks';
if (totalPages > 1) {
buildTitle += ` (page ${currentPage} of ${totalPages})`;
}
@@ -36,38 +37,45 @@ router.get("/", async (req, res) => {
}
params.title = title;
- params.pageInfo = { currentPage, totalPages, offset, limit,
- hasPreviousPage: currentPage > 1,
- hasNextPage: currentPage < totalPages,
- nextOffset: Math.min(offset + limit, totalPages * limit - limit),
- previousOffset: Math.max(offset - limit, 0)
- };
+ params.pageInfo = {
+ currentPage,
+ totalPages,
+ offset,
+ limit,
+ hasPreviousPage: currentPage > 1,
+ hasNextPage: currentPage < totalPages,
+ nextOffset: Math.min(offset + limit, totalPages * limit - limit),
+ previousOffset: Math.max(offset - limit, 0),
+ };
// Send the page options or raw JSON data if the client requested it
- return req.query.raw
- ? res.send(params)
- : res.render("index", params);
+ return req.query.raw ? res.send(params) : res.render('index', params);
});
-router.get("/about", async (req, res) => {
- res.render("about", { title: 'About', actorInfo, domain: req.app.get('domain')});
+router.get('/about', async (req, res) => {
+ res.render('about', {
+ title: 'About',
+ actorInfo,
+ domain: req.app.get('domain'),
+ });
});
-router.get("/network", isAuthenticated, async (req, res) => {
- const bookmarksDb = req.app.get("bookmarksDb");
+router.get('/network', isAuthenticated, async (req, res) => {
+ const bookmarksDb = req.app.get('bookmarksDb');
const posts = await bookmarksDb.getNetworkPosts();
// TODO: make quickadd able to select from list of links in post
- const linksInPosts = posts.map((post) => {
- return {...post, href: linkify.find(post.content)?.[0]?.href};
- })
-
- return res.render("network", {posts: linksInPosts});
- res.status(200).json(posts);
-})
-router.get("/index.xml", async (req, res) => {
- let params = {};
+ const linksInPosts = posts.map((post) => ({
+ ...post,
+ href: linkify.find(post.content)?.[0]?.href,
+ }));
+
+ return res.render('network', { posts: linksInPosts });
+});
+
+router.get('/index.xml', async (req, res) => {
+ const params = {};
const bookmarksDb = req.app.get('bookmarksDb');
const bookmarks = await bookmarksDb.getBookmarks(20, 0);
@@ -77,25 +85,29 @@ router.get("/index.xml", async (req, res) => {
params.setup = data.setupMessage;
} else {
params.bookmarks = bookmarks.map((bookmark) => {
- const tag_array = bookmark.tags?.split(' ').map(b => b.slice(1)) ?? [];
- const created_at = new Date(bookmark.created_at);
- return {tag_array, ...bookmark, created_at: created_at.toISOString() };
+ const tagArray = bookmark.tags?.split(' ').map((b) => b.slice(1)) ?? [];
+ const createdAt = new Date(bookmark.created_at);
+ return {
+ tag_array: tagArray,
+ ...bookmark,
+ created_at: createdAt.toISOString(),
+ };
});
- const last_updated = new Date(bookmarks[0].created_at);
- params.last_updated = last_updated.toISOString();
+ const lastUpdated = new Date(bookmarks[0].created_at);
+ params.last_updated = lastUpdated.toISOString();
}
params.feedTitle = req.app.get('site_name');
params.layout = false;
res.type('application/atom+xml');
- return res.render("bookmarks-xml", params);
+ return res.render('bookmarks-xml', params);
});
-router.get("/tagged/*.xml", async (req, res) => {
+router.get('/tagged/*.xml', async (req, res) => {
const tags = req.params[0].split('/');
- let params = {};
+ const params = {};
const bookmarksDb = req.app.get('bookmarksDb');
const bookmarks = await bookmarksDb.getBookmarksForTags(tags, 20, 0);
@@ -106,8 +118,8 @@ router.get("/tagged/*.xml", async (req, res) => {
params.setup = data.setupMessage;
} else {
params.bookmarks = bookmarks.map((bookmark) => {
- const tag_array = bookmark.tags.split(' ').map(b => b.slice(1));
- return {tag_array, ...bookmark};
+ const tagArray = bookmark.tags.split(' ').map((b) => b.slice(1));
+ return { tag_array: tagArray, ...bookmark };
});
params.last_updated = bookmarks[0].created_at;
}
@@ -116,13 +128,13 @@ router.get("/tagged/*.xml", async (req, res) => {
params.layout = false;
res.type('application/atom+xml');
- return res.render("bookmarks-xml", params);
+ return res.render('bookmarks-xml', params);
});
-router.get("/tagged/*", async (req, res) => {
+router.get('/tagged/*', async (req, res) => {
const tags = req.params[0].split('/');
- let params = {};
+ const params = {};
const bookmarksDb = req.app.get('bookmarksDb');
let buildTitle = `Bookmarks tagged ${tags.join(' and ')}`;
@@ -151,19 +163,38 @@ router.get("/tagged/*", async (req, res) => {
params.feed = req.path;
params.title = title;
params.pageInfo = {
- currentPage, totalPages, offset, limit,
+ currentPage,
+ totalPages,
+ offset,
+ limit,
hasPreviousPage: currentPage > 1,
hasNextPage: currentPage < totalPages,
nextOffset: Math.min(offset + limit, totalPages * limit - limit),
- previousOffset: Math.max(offset - limit, 0)
+ previousOffset: Math.max(offset - limit, 0),
};
params.path = req.path;
params.pathTags = req.path.split('/').slice(2);
// Send the page options or raw JSON data if the client requested it
- return req.query.raw
- ? res.send(params)
- : res.render("tagged", params);
+ return req.query.raw ? res.send(params) : res.render('tagged', params);
});
+router.get('/search', async (req, res) => {
+ try {
+ const bookmarksDb = req.app.get('bookmarksDb');
+ const params = { title: 'Search Bookmarks' };
+ if (req.query.query) {
+ params.keywords = req.query.query;
+ params.bookmarks = await bookmarksDb.searchBookmarks(req.query.query.split(' '));
+ if (params.bookmarks.length === 0) {
+ params.error = 'No matches...';
+ }
+ }
+ params.tags = await bookmarksDb.getTags();
+ return res.render('search', params);
+ } catch (err) {
+ console.log(err);
+ return res.status(500).send('Internal Server Error');
+ }
+});
diff --git a/src/routes/index.js b/src/routes/index.js
index e076f5b..70e0b6d 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -1,13 +1,13 @@
-import { router as admin } from './admin.js';
-import { router as auth } from "./auth.js";
-import { router as bookmark } from './bookmark.js';
-import { router as comment } from './comment.js';
-import { router as core } from './core.js';
-import { router as inbox } from './activitypub/inbox.js';
-import { router as message } from './activitypub/message.js';
-import { router as user } from './activitypub/user.js';
-import { router as webfinger } from './activitypub/webfinger.js';
-import { router as nodeinfo } from './activitypub/nodeinfo.js';
+import admin from './admin.js';
+import auth from './auth.js';
+import bookmark from './bookmark.js';
+import comment from './comment.js';
+import core from './core.js';
+import inbox from './activitypub/inbox.js';
+import message from './activitypub/message.js';
+import user from './activitypub/user.js';
+import webfinger from './activitypub/webfinger.js';
+import nodeinfo from './activitypub/nodeinfo.js';
export default {
admin,
diff --git a/src/session-auth.js b/src/session-auth.js
index 8f8bb94..29ca463 100644
--- a/src/session-auth.js
+++ b/src/session-auth.js
@@ -1,20 +1,19 @@
-import session from "express-session";
-import connectSqlite from "connect-sqlite3";
+import session from 'express-session';
+import connectSqlite from 'connect-sqlite3';
const SQLiteStore = connectSqlite(session);
-export default () => {
- return session({
+export default () =>
+ session({
store: new SQLiteStore({
- db: "sessions.db",
- dir: ".data/",
+ db: 'sessions.db',
+ dir: '.data/',
}),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 },
});
-};
export function isAuthenticated(req, res, next) {
if (req.session.loggedIn) next();
@@ -31,15 +30,15 @@ export function login(req, res, next) {
req.session.loggedIn = true;
}
- req.session.save((err) => {
- if (err) {
- return next(err);
+ req.session.save((saveErr) => {
+ if (saveErr) {
+ return next(saveErr);
}
- if (req.body.sendTo && req.body.sendTo.startsWith("/")) {
+ if (req.body.sendTo && req.body.sendTo.startsWith('/')) {
return res.redirect(decodeURIComponent(req.body.sendTo));
}
- return res.redirect("/");
+ return res.redirect('/');
});
});
}
@@ -51,11 +50,11 @@ export function logout(req, res, next) {
next(err);
}
- req.session.regenerate((err) => {
- if (err) {
- next(err);
+ req.session.regenerate((regenErr) => {
+ if (regenErr) {
+ next(regenErr);
}
- res.redirect("/");
+ res.redirect('/');
});
});
}
diff --git a/src/signature.js b/src/signature.js
new file mode 100644
index 0000000..473d10d
--- /dev/null
+++ b/src/signature.js
@@ -0,0 +1,171 @@
+import crypto from 'crypto';
+import fetch from 'node-fetch';
+
+import { account, domain } from './util.js';
+import { getPrivateKey } from './activity-pub-db.js';
+
+/**
+ * Returns base-64 encoded SHA-256 digest of provided data
+ *
+ * @param {string} data - UTF-8 string to be hashed
+ *
+ * @returns {string}
+ */
+function getDigest(data) {
+ return crypto.createHash('sha256').update(data).digest('base64');
+}
+
+/**
+ * Returns base-64 encoded string signed with user's RSA private key
+ *
+ * @param {string} privkey - Postmarks user's private key
+ * @param {string} data - UTF-8 string to sign
+ *
+ * @returns {string}
+ */
+function getSignature(privkey, data) {
+ const signer = crypto.createSign('sha256');
+ signer.update(data);
+ signer.end();
+ return signer.sign(privkey).toString('base64');
+}
+
+/**
+ * Returns object of params to be used for HTTP signature
+ *
+ * @param {BodyInit | null} [body] - Request body for signature digest (usually a JSON string, optional)
+ * @param {string} method - Request HTTP method name
+ * @param {string} url -
+ *
+ * @returns {Object}
+ */
+function getSignatureParams(body, method, url) {
+ const urlObj = new URL(url);
+ const path = `${urlObj.pathname}${urlObj.search}`;
+ const requestTarget = `${method.toLowerCase()} ${path}`;
+ const hostParam = urlObj.hostname;
+
+ const date = new Date();
+ const dateParam = date.toUTCString();
+
+ const params = {
+ '(request-target)': requestTarget,
+ host: hostParam,
+ date: dateParam,
+ };
+
+ // add digest param if request body is present
+ if (body) {
+ const digest = getDigest(body);
+ const digestParam = `SHA-256=${digest}`;
+ params.digest = digestParam;
+ }
+
+ return params;
+}
+
+/**
+ * Returns the full "Signature" header to be included in the signed request
+ *
+ * @param {string} signature - Base-64 encoded request signature
+ * @param {string[]} signatureKeys - Array of param names used when generating the signature
+ *
+ * @returns {string}
+ */
+function getSignatureHeader(signature, signatureKeys) {
+ return [
+ `keyId="https://${domain}/u/${account}"`,
+ `algorithm="rsa-sha256"`,
+ `headers="${signatureKeys.join(' ')}"`,
+ `signature="${signature}"`,
+ ].join(',');
+}
+
+/**
+ * Signs a fetch request with the account's RSA private key
+ *
+ * @param {URL | RequestInfo} url - URL (passed to fetch)
+ * @param {RequestInit} [init={}] - Optional fetch init object
+ *
+ * @returns {Promise<Response>}
+ */
+export async function signedFetch(url, init = {}) {
+ const privkey = await getPrivateKey(`${account}@${domain}`);
+ if (!privkey) {
+ throw new Error(`No private key found for ${account}.`);
+ }
+
+ const { headers = {}, body = null, method = 'GET', ...rest } = init;
+
+ const signatureParams = getSignatureParams(body, method, url);
+ const signatureKeys = Object.keys(signatureParams);
+ const stringToSign = Object.entries(signatureParams)
+ .map(([k, v]) => `${k}: ${v}`)
+ .join('\n');
+ const signature = getSignature(privkey, stringToSign);
+ const signatureHeader = getSignatureHeader(signature, signatureKeys);
+
+ return fetch(url, {
+ body,
+ method,
+ headers: {
+ ...headers,
+ Host: signatureParams.host,
+ Date: signatureParams.date,
+ Digest: signatureParams.digest,
+ Signature: signatureHeader,
+ },
+ ...rest,
+ });
+}
+
+/**
+ * Private: Adds JSON headers before calling {@link signedFetch}
+ *
+ * @private
+ *
+ * @param {string} [method="GET"] - HTTP method
+ * @param {URL | RequestInfo} url - URL
+ * @param {RequestInit} [init={}] - Optional fetch init object
+ *
+ * @returns {Promise<Response>}
+ */
+function _signedFetchJSON(url, method = 'GET', init = {}) {
+ const { body, headers = {}, ...rest } = init;
+ const contentTypeHeader = body ? { 'Content-Type': 'application/json' } : {};
+
+ return signedFetch(url, {
+ body,
+ headers: {
+ ...headers,
+ Accept: 'application/json',
+ ...contentTypeHeader,
+ },
+ ...rest,
+ method, // no override
+ });
+}
+
+/**
+ * Sends a signed GET request, expecting a JSON response, using {@link signedFetch}
+ *
+ * @param {URL | RequestInfo} url - URL
+ * @param {RequestInit} [init={}] - Optional fetch init object
+ *
+ * @returns {Promise<Response>}
+ */
+export function signedGetJSON(url, init = {}) {
+ return _signedFetchJSON(url, 'GET', init);
+}
+
+/**
+ * Sends a signed POST request, expecting a JSON response, using {@link signedFetch}
+ *
+ * @param {URL | RequestInfo} url - URL
+ * @param {RequestInit} [init={}] - Optional fetch init object
+ *
+ * @returns {Promise<Response>}
+ */
+export function signedPostJSON(url, init = {}) {
+ return _signedFetchJSON(url, 'POST', init);
+}
diff --git a/src/util.js b/src/util.js
index 7346e66..9eb97e2 100644
--- a/src/util.js
+++ b/src/util.js
@@ -1,14 +1,14 @@
import fs from 'fs';
import { readFile } from 'fs/promises';
import chalk from 'chalk';
-import * as dotenv from "dotenv";
+import * as dotenv from 'dotenv';
dotenv.config();
export const data = {
- "errorMessage": "Whoops! Error connecting to the databaseΓÇôplease try again!",
- "setupMessage": "🚧 Whoops! Looks like the database isn't setup yet! 🚧"
-}
+ errorMessage: 'Whoops! Error connecting to the databaseΓÇôplease try again!',
+ setupMessage: "🚧 Whoops! Looks like the database isn't setup yet! 🚧",
+};
let actorFileData = {};
try {
@@ -16,7 +16,7 @@ try {
actorFileData = JSON.parse(accountFile);
actorFileData.disabled = false;
} catch (e) {
- console.log("no account.json file found, assuming non-fediverse mode for now. restart the app to check again");
+ console.log('no account.json file found, assuming non-fediverse mode for now. restart the app to check again');
actorFileData = { disabled: true };
}
@@ -29,34 +29,38 @@ try {
const pkgFile = await readFile('package.json');
instanceData = JSON.parse(pkgFile);
} catch (e) {
- console.log("unable to read package info");
+ console.log('unable to read package info');
}
export const instanceType = instanceData.name || 'postmarks';
export const instanceVersion = instanceData.version || 'undefined';
-export function timeSince(ms) {
- var timestamp = new Date(ms);
- var now = new Date(),
- secondsPast = (now.getTime() - timestamp) / 1000;
+export function timeSince(ms) {
+ const timestamp = new Date(ms);
+ const now = new Date();
+ const secondsPast = (now.getTime() - timestamp) / 1000;
if (secondsPast < 60) {
- return parseInt(secondsPast) + 's ago';
+ return `${parseInt(secondsPast, 10)}s ago`;
}
if (secondsPast < 3600) {
- return parseInt(secondsPast / 60) + 'm ago';
+ return `${parseInt(secondsPast / 60, 10)}m ago`;
}
if (secondsPast <= 86400) {
- return parseInt(secondsPast / 3600) + 'h ago';
+ return `${parseInt(secondsPast / 3600, 10)}h ago`;
}
if (secondsPast > 86400) {
const day = timestamp.getDate();
- const month = timestamp.toDateString().match(/ [a-zA-Z]*/)[0].replace(" ", "");
- const year = timestamp.getFullYear() == now.getFullYear() ? "" : " " + timestamp.getFullYear();
- return day + " " + month + year;
+ const month = timestamp
+ .toDateString()
+ .match(/ [a-zA-Z]*/)[0]
+ .replace(' ', '');
+ const year = timestamp.getFullYear() === now.getFullYear() ? '' : ` ${timestamp.getFullYear()}`;
+ return `${day} ${month}${year}`;
}
+ return undefined;
}
-const getActualRequestDurationInMilliseconds = start => {
+const getActualRequestDurationInMilliseconds = (start) => {
const NS_PER_SEC = 1e9; // convert to nanoseconds
const NS_TO_MS = 1e6; // convert to milliseconds
const diff = process.hrtime(start);
@@ -64,7 +68,7 @@ const getActualRequestDurationInMilliseconds = start => {
};
export function removeEmpty(obj) {
- return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null && v !== ''));
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v != null && v !== ''));
}
export function parseJSON(text) {
@@ -82,53 +86,45 @@ export function parseJSON(text) {
// this function takes the two and tries to determine via some terrifying
// and brittle regex work if they're the same.
export function actorMatchesUsername(actor, username) {
- if (!username) {
- return false;
- }
- const result = username.match(/^@([^@]+)@(.+)$/);
- if (result?.length !== 3) {
- console.log(`match on ${username} isn't parseable. Blocks should be specified as @username@domain.tld.`);
- return false;
- }
- const account = result[1];
- const domain = result[2];
+ if (!username) {
+ return false;
+ }
+ const result = username.match(/^@([^@]+)@(.+)$/);
+ if (result?.length !== 3) {
+ console.log(`match on ${username} isn't parseable. Blocks should be specified as @username@domain.tld.`);
+ return false;
+ }
+ const actorAccount = result[1];
+ const actorDomain = result[2];
- const actorResult = actor.match(/^https?:\/\/([^\/]+)\/u(ser)?s?\/(.+)$/)
- if (actorResult?.length !== 4) {
- console.log(`found an unparseable actor: ${actor}. Report this to https://github.com/ckolderup/postmarks/issues !`);
- }
+ const actorResult = actor.match(/^https?:\/\/([^/]+)\/u(ser)?s?\/(.+)$/);
+ if (actorResult?.length !== 4) {
+ console.log(`found an unparseable actor: ${actor}. Report this to https://github.com/ckolderup/postmarks/issues !`);
+ }
- return (account == actorResult[3] && domain == actorResult[1]);
+ return actorAccount === actorResult[3] && actorDomain === actorResult[1];
}
-export function simpleLogger(req, res, next) { //middleware function
- let current_datetime = new Date();
- let formatted_date =
- current_datetime.getFullYear() +
- "-" +
- (current_datetime.getMonth() + 1) +
- "-" +
- current_datetime.getDate() +
- " " +
- current_datetime.getHours() +
- ":" +
- current_datetime.getMinutes() +
- ":" +
- current_datetime.getSeconds();
- let method = req.method;
- let url = req.url;
- let status = res.statusCode;
+export function simpleLogger(req, res, next) {
+ // middleware function
+ const currentDatetime = new Date();
+ const formattedDate = `${currentDatetime.getFullYear()}-${
+ currentDatetime.getMonth() + 1
+ }-${currentDatetime.getDate()} ${currentDatetime.getHours()}:${currentDatetime.getMinutes()}:${currentDatetime.getSeconds()}`;
+ const { method } = req;
+ const { url } = req;
+ const status = res.statusCode;
const start = process.hrtime();
const durationInMilliseconds = getActualRequestDurationInMilliseconds(start);
- let log = `[${chalk.blue(formatted_date)}] ${method}:${url} ${status} ${chalk.red(durationInMilliseconds.toLocaleString() + "ms")}`;
+ const log = `[${chalk.blue(formattedDate)}] ${method}:${url} ${status} ${chalk.red(`${durationInMilliseconds.toLocaleString()}ms`)}`;
console.log(log);
- if (process.env.LOGGING_ENABLED === "true") {
- fs.appendFile("request_logs.txt", log + "\n", err => {
+ if (process.env.LOGGING_ENABLED === 'true') {
+ fs.appendFile('request_logs.txt', `${log}\n`, (err) => {
if (err) {
console.log(err);
}
});
}
next();
-};
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment