diff --git a/.gitignore b/.gitignore
index c6bba59..048c840 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,130 +1,7 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-lerna-debug.log*
-.pnpm-debug.log*
-
-# Diagnostic reports (https://nodejs.org/api/report.html)
-report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
-
-# Runtime data
-pids
-*.pid
-*.seed
-*.pid.lock
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-*.lcov
-
-# nyc test coverage
-.nyc_output
-
-# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Bower dependency directory (https://bower.io/)
-bower_components
-
-# node-waf configuration
-.lock-wscript
-
-# Compiled binary addons (https://nodejs.org/api/addons.html)
-build/Release
-
-# Dependency directories
+secrets/
node_modules/
-jspm_packages/
-
-# Snowpack dependency directory (https://snowpack.dev/)
-web_modules/
-
-# TypeScript cache
-*.tsbuildinfo
-
-# Optional npm cache directory
-.npm
-
-# Optional eslint cache
-.eslintcache
-
-# Optional stylelint cache
-.stylelintcache
-
-# Microbundle cache
-.rpt2_cache/
-.rts2_cache_cjs/
-.rts2_cache_es/
-.rts2_cache_umd/
-
-# Optional REPL history
-.node_repl_history
-
-# Output of 'npm pack'
-*.tgz
-
-# Yarn Integrity file
-.yarn-integrity
-
-# dotenv environment variable files
-.env
-.env.development.local
-.env.test.local
-.env.production.local
-.env.local
-
-# parcel-bundler cache (https://parceljs.org/)
-.cache
-.parcel-cache
-
-# Next.js build output
-.next
-out
-
-# Nuxt.js build / generate output
-.nuxt
-dist
-
-# Gatsby files
-.cache/
-# Comment in the public line in if your project uses Gatsby and not Next.js
-# https://nextjs.org/blog/next-9-1#public-directory-support
-# public
-
-# vuepress build output
-.vuepress/dist
-
-# vuepress v2.x temp and cache directory
-.temp
-.cache
-
-# Docusaurus cache and generated files
-.docusaurus
-
-# Serverless directories
-.serverless/
-
-# FuseBox cache
-.fusebox/
-
-# DynamoDB Local files
-.dynamodb/
-
-# TernJS port file
-.tern-port
-
-# Stores VSCode versions used for testing VSCode extensions
-.vscode-test
-
-# yarn v2
-.yarn/cache
-.yarn/unplugged
-.yarn/build-state.yml
-.yarn/install-state.gz
-.pnp.*
+tmp/
+cache/
+logs/
+db/
+TODO.*
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..048c840
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,7 @@
+secrets/
+node_modules/
+tmp/
+cache/
+logs/
+db/
+TODO.*
diff --git a/CSS/cards.css b/CSS/cards.css
new file mode 100644
index 0000000..a901945
--- /dev/null
+++ b/CSS/cards.css
@@ -0,0 +1,320 @@
+/* posts */
+/* container for all cards */
+.cards-container {
+ column-width: 280px; /* sets the width of each "column" */
+ column-gap: 16px; /* horizontal space between columns */
+ padding: 20px;
+ margin: auto;
+}
+
+.cards-container .post-card {
+ position: relative;
+ background-color: #2e003e;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+ color: #e6e6ff;
+ font-family: Arial, sans-serif;
+ display: inline-block; /* makes each card flow naturally into columns */
+ margin-bottom: 16px; /* adds space between items vertically */
+ width: 100%; /* ensures card takes up the column width */
+}
+
+
+.cards-container-relaxed {
+ display: grid;
+ gap: 16px;
+ grid-auto-rows: auto; /* allows each card to be only as tall as its content */
+ grid-auto-flow: dense; /* fills in gaps with smaller items if available */
+ padding: 20px;
+ grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
+ display: grid;
+}
+
+.cards-container-relaxed .post-card {
+ position: relative;
+ background-color: #2e003e;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+ color: #e6e6ff;
+ font-family: Arial, sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ max-width: 100%;
+}
+
+
+
+/* author section inside card */
+.author-section {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ width: 100%;
+}
+
+/* avatar image styling */
+.card-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ margin-right: 10px;
+ border: 2px solid #aa00ff;
+}
+
+/* author info */
+.author-info {
+ display: flex;
+ flex-direction: column;
+}
+
+/* display name and handle styling */
+.author-name {
+ font-size: 1em;
+ color: #d4b0ff;
+ margin: 0;
+}
+
+.author-handle {
+ font-size: 0.8em;
+ color: #b399ff;
+ margin: 0;
+}
+
+/* text content styling */
+.post-text {
+ overflow: hidden;
+ max-height: 200px; /* set a limit for text content */
+ text-overflow: ellipsis;
+ white-space: normal;
+}
+
+
+/* styling for original post preview */
+.original-post {
+ background-color: #3d1c3d;
+ border-radius: 4px;
+ padding: 8px;
+ margin-top: 8px;
+ width: 100%;
+ font-size: 0.8em;
+ color: #b399ff;
+}
+
+.original-author {
+ font-weight: bold;
+ margin-bottom: 4px;
+ color: #d4b0ff;
+}
+
+.original-text {
+ font-style: italic;
+ color: #e6e6ff;
+ margin: 4px 0;
+}
+
+.original-image {
+ width: 100%;
+ height: auto;
+ border-radius: 4px;
+ margin-top: 4px;
+}
+
+/* interaction counts */
+.interaction-counts {
+ font-size: 0.8em;
+ color: #b399ff;
+ margin-top: 12px;
+ width: 100%;
+ text-align: right;
+}
+
+/* repost section styling */
+.repost-section {
+ display: flex;
+ align-items: center;
+ background-color: #3d1c3d;
+ padding: 8px;
+ border-radius: 8px 8px 0 0;
+ width: 100%;
+ margin-bottom: 10px;
+}
+
+/* repost avatar */
+.repost-avatar {
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ margin-right: 8px;
+ border: 2px solid #ff66ff;
+}
+
+/* repost info */
+.repost-info .author-name {
+ font-size: 0.9em;
+ color: #d4b0ff;
+ margin: 0;
+}
+
+.repost-info .author-handle {
+ font-size: 0.8em;
+ color: #b399ff;
+ margin: 0;
+}
+
+/* styling for post image within card */
+.post-image {
+ width: 100%;
+ height: auto;
+ border-radius: 4px;
+ margin-top: 8px;
+}
+
+
+/* pinned label styling */
+.pinned-label {
+ background-color: #ffcc00; /* bright color to make it noticeable */
+ color: #333;
+ font-size: 0.8em;
+ font-weight: bold;
+ padding: 4px 8px;
+ border-radius: 4px;
+ display: inline-block;
+ margin-bottom: 8px; /* space below the label */
+}
+
+/* optional: distinct border for pinned card */
+.pinned-card {
+ border: 2px solid #ffcc00; /* match border with label color */
+ padding-top: 8px; /* adjust padding for the label */
+}
+
+/* container for video within post card */
+.video-container {
+ width: 100%;
+ max-width: 600px;
+ margin-top: 10px;
+ border-radius: 8px;
+ overflow: hidden;
+ position: relative;
+}
+
+/* video element styling */
+.post-video {
+ width: 100%;
+ height: auto;
+ border-radius: 8px;
+ background-color: #000;
+ outline: none;
+}
+
+/* add a slight shadow to the video container for depth */
+.video-container {
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.3);
+}
+
+/* hover effect to add a border around the video for focus */
+.video-container:hover {
+ border: 2px solid #aa00ff;
+ transition: border 0.3s ease;
+}
+
+/* compose button styling with icon */
+.compose-button {
+ position: fixed;
+ bottom: 20px;
+ right: 2%;
+ background-color: #6b006b;
+ color: #e6e6ff;
+ padding: 15px;
+ border: none;
+ border-radius: 50%;
+ font-size: 1.5em;
+ cursor: pointer;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.3s ease;
+}
+
+.compose-button:hover {
+ background-color: #aa00ff;
+}
+
+/* button container styling */
+.button-container {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 10px;
+}
+
+/* individual action buttons */
+.action-button {
+ background-color: #2a002e;
+ color: #e6e6ff;
+ border: none;
+ border-radius: 5px;
+ padding: 8px;
+ cursor: pointer;
+ font-size: 1.2em;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.3s ease;
+}
+
+.action-button:hover {
+ background-color: #4e004e;
+}
+
+/* specific styles for icons */
+.like-button {
+ color: #ff4d4d;
+}
+
+.delete-button {
+ color: #ff4d4d;
+}
+
+.dm-button {
+ color: #66ccff;
+}
+
+.copy-link-button {
+ color: #ffcc00;
+}
+
+.pin-button {
+ color: #ffcc00;
+}
+
+/* dropdown menu styling */
+.dropdown-menu {
+ position: absolute;
+ right: 13px;
+ background-color: #3d1c3d;
+ border-radius: 5px;
+ padding: 5px;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.3);
+ z-index: 100;
+}
+
+/* hide dropdown menu initially */
+.hidden {
+ display: none;
+}
+
+/* dropdown action button styling */
+.dropdown-menu .action-button {
+ width: 100%;
+ padding: 8px;
+ text-align: left;
+ font-size: 0.9em;
+}
\ No newline at end of file
diff --git a/CSS/compose.css b/CSS/compose.css
new file mode 100644
index 0000000..78f9f6e
--- /dev/null
+++ b/CSS/compose.css
@@ -0,0 +1,160 @@
+/* overlay backdrop */
+#overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.7); /* darker overlay for dark theme */
+ z-index: 999;
+ transition: opacity 0.3s ease;
+}
+
+/* popup form styling for dark theme */
+#new-post-form {
+ display: none;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: #333; /* dark background */
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ max-width: 400px;
+ width: 100%;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ color: #f1f1f1; /* light text for contrast */
+}
+
+/* show popup and overlay */
+#new-post-form.show, #overlay.show {
+ display: block;
+ opacity: 1;
+}
+
+/* form elements styling */
+#postContent, #postTitle, #postFile, #postGif, #postAudio {
+ width: 100%;
+ padding: 10px;
+ margin-top: 10px;
+ background-color: #444; /* dark background for inputs */
+ border: 1px solid #666;
+ border-radius: 4px;
+ color: #f1f1f1; /* light text */
+ resize: vertical;
+}
+
+button[type="submit"] {
+ display: inline-block;
+ width: 100%;
+ margin-top: 10px;
+ padding: 10px;
+ font-size: 16px;
+ background-color: #0077ff;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+button[type="submit"]:hover {
+ background-color: #005bb5;
+}
+
+/* close button */
+#closeButton {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ background: none;
+ border: none;
+ font-size: 18px;
+ color: #f1f1f1; /* light color for dark background */
+ cursor: pointer;
+}
+
+
+/* button styling */
+button#fileButton, button#gifButton, button#audioButton {
+ background: #444; /* dark background for dark theme */
+ border: none;
+ border-radius: 4px;
+ padding: 8px;
+ cursor: pointer;
+ margin: 5px 0;
+ transition: background-color 0.3s ease;
+}
+
+button#fileButton:hover, button#gifButton:hover, button#audioButton:hover {
+ background-color: #555; /* slightly lighter on hover */
+}
+
+/* icon styling */
+button#fileButton img, button#gifButton img, button#audioButton img {
+ width: 20px;
+ height: 20px;
+ vertical-align: middle;
+}
+
+/* hide actual input fields */
+#postFile, #postGif, #postAudio {
+ display: none;
+}
+
+#new-post-form button {
+ color: white;
+}
+
+/* Updated Embed widget styling for dark theme */
+.embed-widget {
+ display: none; /* Hide by default */
+ border: 1px solid #666;
+ border-radius: 8px;
+ padding: 16px;
+ margin-top: 20px;
+ background-color: #333; /* Dark background */
+ color: #f1f1f1; /* Light text for contrast */
+ max-width: 400px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
+}
+
+/* Input styling inside the widget for dark theme */
+.embed-widget input {
+ width: 100%;
+ padding: 8px;
+ margin-top: 6px;
+ margin-bottom: 12px;
+ background-color: #444; /* Dark background for inputs */
+ border: 1px solid #666;
+ border-radius: 4px;
+ color: #f1f1f1; /* Light text */
+}
+
+/* Label styling */
+.embed-widget label {
+ font-size: 14px;
+ font-weight: bold;
+ color: #f1f1f1; /* Light color for dark theme */
+}
+
+/* Button styling for adding an external embed */
+#showEmbedButton {
+ margin-top: 10px;
+ padding: 10px 16px;
+ font-size: 16px;
+ background-color: #007acc;
+ color: #fff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+#showEmbedButton:hover {
+ background-color: #005bb5;
+}
diff --git a/CSS/dropdown.css b/CSS/dropdown.css
new file mode 100644
index 0000000..c5edd6c
--- /dev/null
+++ b/CSS/dropdown.css
@@ -0,0 +1,53 @@
+/* DROPDOWN */
+/* dropdown container */
+.dropdown {
+ position: relative;
+ display: inline-block;
+ margin: 10px 0;
+}
+
+/* dropdown button styling */
+.dropdown-button {
+ background-color: #6b006b;
+ color: #e6e6ff;
+ padding: 10px 20px;
+ font-weight: bold;
+ border-radius: 5px;
+ cursor: pointer;
+ border: none;
+ font-size: 1em;
+}
+
+.dropdown-button:hover {
+ background-color: #aa00ff;
+}
+
+/* dropdown content */
+.dropdown-content {
+ display: none;
+ position: absolute;
+ background-color: #2e003e;
+ min-width: 160px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+ border-radius: 5px;
+ z-index: 1;
+ overflow: hidden;
+}
+
+/* links inside the dropdown */
+.dropdown-content a {
+ color: #e6e6ff;
+ padding: 10px;
+ text-decoration: none;
+ display: block;
+}
+
+.dropdown-content a:hover {
+ background-color: #1a001a;
+ color: #d4b0ff;
+}
+
+/* show dropdown on button click */
+.dropdown:hover .dropdown-content {
+ display: block;
+}
\ No newline at end of file
diff --git a/CSS/global.css b/CSS/global.css
new file mode 100644
index 0000000..0d04689
--- /dev/null
+++ b/CSS/global.css
@@ -0,0 +1,68 @@
+/* global styles */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ background-color: #1a001a;
+ color: #e6e6ff;
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+}
+
+.noscroll {
+ overflow: hidden;
+ height: 100%;
+}
+
+img {
+ cursor: pointer;
+}
+
+.hidden {
+ display: none;
+}
+
+.convcont {
+ text-align: center;
+ color: #b399ff; /* light purple color for visual separation */
+ margin: 8px 0; /* vertical space around the separator */
+ letter-spacing: 2px; /* spacing for the dots */
+ opacity: 0.9;
+ font-size: 1em;
+}
+
+
+#eof {
+ text-align: center;
+}
+
+/* styling for user links, e.g., @username */
+.user-link {
+ color: #d4b0ff; /* matches the light purple in your theme */
+ font-weight: bold;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.user-link:hover {
+ color: #b399ff; /* a slightly darker shade on hover */
+ text-decoration: underline;
+}
+
+/* styling for external links */
+.external-link {
+ color: #ff66ff; /* a vibrant color for external links */
+ font-weight: bold;
+ text-decoration: none;
+}
+
+.external-link:hover {
+ color: #aa00ff; /* darker shade on hover */
+ text-decoration: underline;
+}
diff --git a/CSS/loading.css b/CSS/loading.css
new file mode 100644
index 0000000..31e5612
--- /dev/null
+++ b/CSS/loading.css
@@ -0,0 +1,27 @@
+/* container for the loading image */
+#loading {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #333; /* keep background color */
+ z-index: 9999999999999999;
+ }
+
+ /* loading image styling */
+ #loading img {
+ max-width: 50%;
+ height: auto;
+ /* responsive resizing */
+ }
+
+ /* for small screens, adjust the loading image size */
+ @media (max-width: 600px) {
+ #loading img {
+ max-width: 60%;
+ }
+ }
\ No newline at end of file
diff --git a/CSS/post.css b/CSS/post.css
new file mode 100644
index 0000000..884a381
--- /dev/null
+++ b/CSS/post.css
@@ -0,0 +1,193 @@
+.dmselpopup {
+ display: none;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 80%;
+ max-width: 600px;
+ padding: 20px;
+ background-color: #2c2540;
+ border: 1px solid #3d3452;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
+ z-index: 1000;
+ overflow-y: auto;
+ max-height: 80vh;
+ border-radius: 8px;
+}
+
+.dmselpopup.active {
+ display: block;
+}
+
+.dmselpopup h2 {
+ margin-top: 0;
+ color: #b39ddb;
+}
+
+.dm-item {
+ display: flex;
+ align-items: center;
+ padding: 10px;
+ border-bottom: 1px solid #3d3452;
+ cursor: pointer;
+}
+
+.dm-item img {
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ margin-right: 15px;
+ border: 2px solid #6a5c96;
+}
+
+.dm-item .info {
+ display: flex;
+ flex-direction: column;
+}
+
+.dm-item .info .name {
+ font-weight: bold;
+ color: #e0e0ff;
+}
+
+.dm-item .info .handle {
+ color: #b39ddb;
+}
+
+.dm-item.disabled {
+ opacity: 0.5;
+ color: #72678f;
+ cursor: not-allowed;
+}
+
+.close-btn {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ cursor: pointer;
+ background: transparent;
+ border: none;
+ font-size: 18px;
+ color: #e0e0ff;
+}
+
+button {
+ background-color: #6a5c96;
+ color: #e0e0ff;
+ border: none;
+ padding: 8px 12px;
+ cursor: pointer;
+ border-radius: 4px;
+}
+
+button:hover {
+ background-color: #836aa8;
+}
+
+button:focus {
+ outline: none;
+ box-shadow: 0 0 0 2px #9e7dc7;
+}
+
+.dm-item.success {
+ background-color: #2e4d2e;
+ border: 1px solid #4caf50;
+ color: #a5d6a7;
+}
+
+.dm-item.success .info .name,
+.dm-item.success .info .handle {
+ color: #a5d6a7;
+}
+
+.dm-item.error {
+ background-color: #4d2e2e;
+ border: 1px solid #f44336;
+ color: #ef9a9a;
+}
+
+.dm-item.error .info .name,
+.dm-item.error .info .handle {
+ color: #ef9a9a;
+}
+
+/* message send success */
+.checkmark {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ background-color: #4caf50;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.checkmark::after {
+ content: '';
+ width: 12px;
+ height: 24px;
+ border: solid white;
+ border-width: 0 4px 4px 0;
+ transform: rotate(45deg);
+ animation: checkmark 0.3s ease-in-out forwards;
+ margin-top: -5px;
+}
+
+@keyframes checkmark {
+ 0% {
+ opacity: 0;
+ transform: scale(0) rotate(45deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: scale(1) rotate(45deg);
+ }
+}
+
+
+/* message send failure */
+.crossmark {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ background-color: #f44336;
+ /* Red background for error */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.crossmark::before,
+.crossmark::after {
+ content: '';
+ position: absolute;
+ width: 24px;
+ height: 4px;
+ background-color: white;
+ border-radius: 2px;
+ animation: crossmark 0.3s ease-in-out forwards;
+}
+
+/* .crossmark::before {
+ transform: rotate(45deg);
+}
+
+.crossmark::after {
+ transform: rotate(-45deg);
+} */
+
+@keyframes crossmark {
+ 0% {
+ opacity: 0;
+ transform: scale(0);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
\ No newline at end of file
diff --git a/CSS/profile.css b/CSS/profile.css
new file mode 100644
index 0000000..3aa7594
--- /dev/null
+++ b/CSS/profile.css
@@ -0,0 +1,61 @@
+.profile-container {
+ width: 98%;
+ margin: auto;
+ /* max-width: 600px; */
+ background-color: #2e003e;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.5);
+}
+
+/* banner section */
+.banner img {
+ width: 100%;
+ height: 150px;
+ object-fit: cover;
+}
+
+/* profile content */
+.profile-content {
+ padding: 20px;
+ text-align: center;
+}
+
+.avatar {
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+ border: 3px solid #aa00ff;
+ margin-top: -50px;
+}
+
+h1 {
+ font-size: 1.8em;
+ margin: 10px 0;
+ color: #d4b0ff;
+}
+
+p {
+ margin: 5px 0;
+}
+
+.counts {
+ margin: 15px 0;
+ font-size: 0.9em;
+ color: #b399ff;
+}
+
+.link {
+ color: #e6e6ff;
+ background-color: #6b006b;
+ padding: 10px 20px;
+ border-radius: 5px;
+ text-decoration: none;
+ font-weight: bold;
+ display: inline-block;
+ margin-top: 10px;
+}
+
+.link:hover {
+ background-color: #aa00ff;
+}
\ No newline at end of file
diff --git a/CSS/replies.css b/CSS/replies.css
new file mode 100644
index 0000000..d642cb3
--- /dev/null
+++ b/CSS/replies.css
@@ -0,0 +1,71 @@
+/* container for each reply and its parent post, if any */
+.reply-card-container {
+ background-color: #2a002e;
+ border-radius: 8px;
+ margin-bottom: 16px;
+ padding: 10px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ color: #e6e6ff;
+ font-family: Arial, sans-serif;
+}
+
+/* preview of parent post (smaller font size for less emphasis) */
+.parent-post-preview, .grandparent-post-preview {
+ background-color: #3d1c3d; /* distinct color for preview */
+ border-radius: 4px;
+ padding: 8px;
+ margin-bottom: 6px;
+}
+
+/* reply card styling */
+.reply-card {
+ background-color: #2e003e;
+ border-radius: 8px;
+ padding: 10px;
+}
+
+/* author section in reply */
+.reply-card .author-section {
+ display: flex;
+ align-items: center;
+ margin-bottom: 6px;
+}
+
+/* avatar styling */
+.reply-card .card-avatar {
+ width: 35px;
+ height: 35px;
+ border-radius: 50%;
+ margin-right: 8px;
+ border: 2px solid #aa00ff;
+}
+
+/* author info */
+.reply-card .author-info .author-name {
+ font-size: 0.9em;
+ color: #d4b0ff;
+ font-weight: bold;
+}
+
+/* reply text */
+.reply-card .reply-text {
+ font-size: 0.9em;
+ line-height: 1.3;
+ color: #e6e6ff;
+ margin-top: 6px;
+ margin-bottom: 6px;
+}
+
+/* embedded media */
+.reply-card .media-container {
+ margin-top: 8px;
+ display: flex;
+ justify-content: center;
+}
+
+.reply-card .media-image {
+ max-width: 80%;
+ border-radius: 6px;
+ border: 1px solid #aa00ff;
+ margin-top: 6px;
+}
diff --git a/CSS/tabs.css b/CSS/tabs.css
new file mode 100644
index 0000000..5487541
--- /dev/null
+++ b/CSS/tabs.css
@@ -0,0 +1,36 @@
+/* styles for the tab buttons */
+.tab-buttons {
+ display: flex;
+ justify-content: space-around;
+ background-color: #1da1f2;
+ padding: 10px 0;
+}
+
+.tab-buttons button {
+ border: none;
+ background: none;
+ color: white;
+ font-size: 16px;
+ cursor: pointer;
+ padding: 10px;
+}
+
+.tab-buttons button.active {
+ border-bottom: 3px solid white;
+}
+
+/* section styling */
+.content {
+ display: none;
+ padding: 20px;
+ background-color: #2e003e;
+}
+
+.content.active {
+ display: block;
+}
+
+/* Hide the full-screen button from videos */
+/* video::-webkit-media-controls-fullscreen-button {
+ display: none;
+} */
\ No newline at end of file
diff --git a/CSS/video.css b/CSS/video.css
new file mode 100644
index 0000000..ebbf398
--- /dev/null
+++ b/CSS/video.css
@@ -0,0 +1,195 @@
+/* basic styles for the video container and controls */
+.video-container {
+ position: relative;
+ width: 640px;
+ max-width: 100%;
+ background-color: black;
+}
+
+.video-element {
+ width: 100%;
+ height: auto;
+}
+
+.controls {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 3px;
+ /* background: rgba(0, 0, 0, 0.7); */
+ z-index: 10;
+}
+
+.progress-bar {
+ width: 100%;
+}
+
+button,
+input[type="range"] {
+ cursor: pointer;
+}
+
+/* style for the play/pause button */
+.play-pause {
+ width: 40px;
+ height: 40px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* triangle icon for "Play" */
+.play-pause.play::before {
+ content: '';
+ display: inline-block;
+ width: 0;
+ height: 0;
+ border-left: 12px solid white;
+ border-top: 8px solid transparent;
+ border-bottom: 8px solid transparent;
+}
+
+/* double bars for "Pause" */
+.play-pause.pause::before,
+.play-pause.pause::after {
+ content: '';
+ width: 4px;
+ height: 16px;
+ background-color: white;
+ display: inline-block;
+ margin: 0 2px;
+}
+
+
+/* fullscreen button styling */
+.fullscreen {
+ font-size: 120%;
+ color: white;
+ width: 40px;
+ height: 40px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* enter fullscreen icon */
+.fullscreen.enter::before {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ border: 2px solid white;
+ border-top: none;
+ border-left: none;
+ position: absolute;
+ top: 4px;
+ left: 4px;
+}
+
+/* exit fullscreen icon */
+.fullscreen.exit::before {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ border: 2px solid white;
+ border-bottom: none;
+ border-right: none;
+ position: absolute;
+ top: 4px;
+ left: 4px;
+}
+
+.play-icon {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 50px;
+ color: white;
+ pointer-events: none;
+ opacity: 0.7;
+}
+
+input[type="range"] {
+ /* palegoldenrod */
+ accent-color: rgb(170, 10, 117);
+ /* background: linear-gradient(90deg, #a200ff var(--progress), #1e9e04 var(--progress));
+ -webkit-appearance: none;
+ border: solid 1px #82CFD0;
+ border-radius: 8px;
+ transition: background 450ms ease-in; */
+}
+
+.video-container {
+ position: relative;
+ width: 640px;
+ max-width: 100%;
+ background-color: black;
+}
+
+.video-element {
+ width: 100%;
+ height: auto;
+}
+
+.controls {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 3px;
+ z-index: 10;
+}
+
+.progress-bar {
+ width: 100%;
+}
+
+button,
+input[type="range"] {
+ cursor: pointer;
+}
+
+.play-pause {
+ width: 40px;
+ height: 40px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+}
+
+.fullscreen {
+ font-size: 120%;
+ color: white;
+ width: 40px;
+ height: 40px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+}
+
+.volume-container {
+ position: relative;
+}
+
+.volume-button {
+ font-size: 18px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ color: white;
+ width: 40px;
+ height: 40px;
+}
diff --git a/CSS/zoom.css b/CSS/zoom.css
new file mode 100644
index 0000000..5bf1215
--- /dev/null
+++ b/CSS/zoom.css
@@ -0,0 +1,63 @@
+
+/* zoom container styles */
+.zoom-container {
+ top: 5vh;
+ position: fixed;
+ display: inline-block;
+ overflow: hidden;
+ cursor: pointer;
+ max-height: 90vh;
+ max-width: 90vw;
+ cursor: pointer;
+ z-index: 9999;
+}
+
+/* initial image styling */
+.zoom-video, .zoom-image {
+ max-height: 88vh;
+ max-width: 88vw;
+ transition: transform 0.5s ease, opacity 0.3s ease;
+ cursor: pointer;
+ border-radius: 8px;
+ border: solid 2px lightblue;
+}
+
+/* styles for full-screen zoomed view */
+.zoom-container.zoomed .zoom-image, .zoom-container.zoomed .zoom-video {
+ position: absolute;
+ top: 5%;
+ left: 0;
+ width: auto;
+ border-radius: 0; /* remove rounded corners when zoomed */
+ box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.6); /* shadow for zoom effect */
+ z-index: 999999999;
+ transform: scale(0.85);
+}
+
+/* darken background when image is zoomed */
+.zoom-container.zoomed::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ /* width: 100vw;
+ height: 100vh; */
+ background: rgba(0, 0, 0, 0.6);
+ z-index: 999;
+ transition: opacity 0.3s ease;
+ opacity: 1;
+ transform: scale(0.85);
+}
+
+.overlay {
+ background-color: rgba(0, 0, 0, 0.336);
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ position: fixed;
+ top: 0;
+ left: 0;
+ margin: 0;
+ padding: 0;
+ z-index: 998;
+}
\ No newline at end of file
diff --git a/HTML/index.html b/HTML/index.html
new file mode 100644
index 0000000..9b0fb28
--- /dev/null
+++ b/HTML/index.html
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+ Profile Display
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
![Zoomable Image]()
+
+
+
+
+
+
+
![Profile Banner]()
+
+
+
![Avatar]()
+
User Handle
+
User description goes here.
+
+ 100 Followers |
+ 150 Following |
+ 50 Posts
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
nothing here...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JS/Profile.cjs b/JS/Profile.cjs
new file mode 100644
index 0000000..ba455b0
--- /dev/null
+++ b/JS/Profile.cjs
@@ -0,0 +1,58 @@
+const { formatStr } = require('../src/renderer.cjs');
+
+
+/**
+ * @param {Profile} profileData
+ */
+function populateProfile(profileData) {
+ const bannerImg = document.querySelector('#banner-img');
+ const avatarImg = document.querySelector('#avatar-img');
+ const handleElement = document.querySelector('#handle');
+ const descriptionElement = document.querySelector('#description');
+ const followersCountElement = document.querySelector('#followersCount');
+ const followsCountElement = document.querySelector('#followsCount');
+ const postsCountElement = document.querySelector('#postsCount');
+
+ console.info(profileData);
+
+ // set element content
+ bannerImg.src = profileData.banner;
+ avatarImg.src = profileData.avatar;
+ handleElement.textContent = profileData.handle;
+ descriptionElement.innerHTML = formatStr(profileData.description);
+ followersCountElement.textContent = profileData.followersCount;
+ followsCountElement.textContent = profileData.followsCount;
+ postsCountElement.textContent = profileData.postsCount;
+}
+
+class Profile {
+ constructor(data) {
+ if (!data) throw "DATA NOT FOUND!";
+ this.did = data.did,
+ this.handle = data.handle,
+ this.displayName = data.displayName,
+ this.avatar = data.avatar,
+ this.associated = data.associated,
+ this.viewer = data.viewer,
+ this.labels = data.labels,
+ this.createdAt = data.createdAt,
+ this.description = formatStr(data.description),
+ this.indexedAt = data.indexedAt,
+ this.banner = data.banner,
+ this.followersCount = data.followersCount,
+ this.followsCount = data.followsCount,
+ this.postsCount = data.postsCount,
+ this.pinnedPost = data.pinnedPost
+ }
+
+ getProfileSummary() {
+ return `${this.handle} (${this.did}): ${this.description}`;
+ }
+
+ getPinnedPostUri() {
+ return this.pinnedPost.uri;
+ }
+}
+
+
+module.exports = { Profile, populateProfile }
\ No newline at end of file
diff --git a/JS/compose.cjs b/JS/compose.cjs
new file mode 100644
index 0000000..a59bf44
--- /dev/null
+++ b/JS/compose.cjs
@@ -0,0 +1,115 @@
+// set up the overlay and form toggling
+async function setup() {
+ const composeButton = document.querySelector('#composebtn');
+ const newPostForm = document.querySelector('#new-post-form');
+ const overlay = document.createElement('div');
+ overlay.id = 'overlay';
+ document.body.appendChild(overlay);
+
+ function openForm() {
+ newPostForm.classList.add("show");
+ overlay.classList.add("show");
+ }
+
+ composeButton.addEventListener('click', openForm);
+}
+
+
+document.addEventListener('DOMContentLoaded', setup);
+
+async function renderCompose(ipcRenderer) {
+ const overlay = document.querySelector("#overlay"),
+ postForm = document.querySelector("#postForm"),
+ statusMessage = document.querySelector("#statusMessage"),
+ closeButton = document.querySelector("#closeButton");
+
+ const newPostForm = document.querySelector("#new-post-form");
+
+ // button and hidden input elements
+ const fileButton = document.querySelector("#fileButton"),
+ gifButton = document.querySelector("#gifButton"),
+ audioButton = document.querySelector("#audioButton"),
+ postFile = document.querySelector("#postFile"),
+ postGif = document.querySelector("#postGif"),
+ postAudio = document.querySelector("#postAudio"),
+ postEmbed = document.getElementById('showEmbedButton'),
+ embedWidget = document.querySelector('#embedWidget');
+
+
+ function closeForm() {
+ newPostForm.classList.remove("show");
+ overlay.classList.remove("show");
+ }
+
+ closeButton.addEventListener("click", closeForm);
+ overlay.addEventListener("click", closeForm);
+
+ // trigger corresponding inputs when button is clicked
+ fileButton.addEventListener("click", () => postFile.click());
+ gifButton.addEventListener("click", () => {
+ postGif.style.display = 'block';
+ postGif.focus();
+ });
+ audioButton.addEventListener("click", () => postAudio.click());
+
+ postEmbed.addEventListener('click', () => {
+ embedWidget.style.display = embedWidget.style.display === 'none' || embedWidget.style.display === '' ? 'block' : 'none';
+ });
+
+ postForm.addEventListener('submit', async (event) => {
+ event.preventDefault();
+ const postContent = document.querySelector('#postContent').value.trim(),
+ gifUrl = document.querySelector('#postGif').value.trim(),
+ statusMessage = document.querySelector('#statusMessage'),
+ embedData = Array.from(embedWidget.querySelectorAll('input')).map(o => {
+ const id = o.id.replace('embed', '')
+ if (o.type === 'file') return [id, o.files[0].name];
+ else return [id, o.value];
+ });
+
+ // files from hidden inputs
+ const file = postFile.files[0];
+ const audio = postAudio.files[0];
+
+ statusMessage.textContent = "Posting...";
+
+ try {
+ ipcRenderer.invoke('new-post', JSON.stringify({ text: postContent, embed: Object.fromEntries(embedData) }));
+ statusMessage.textContent = "Posted successfully!";
+ postForm.reset();
+ postGif.style.display = 'none'; // hide gif input after submission
+ } catch (error) {
+ console.error('Error posting:', error);
+ statusMessage.textContent = 'Failed to post. Please try again.';
+ }
+ });
+}
+
+
+function displayUploadStatus(files) {
+ const statusContainer = document.querySelector('#uploadStatus'); // element to display results
+
+ // Clear previous status
+ statusContainer.innerHTML = '';
+
+ // Iterate through files and display status
+ for (const [fileName, fileStatus] of Object.entries(files)) {
+ const statusMessage = document.createElement('p');
+
+ if (fileStatus) {
+ // Upload succeeded
+ statusMessage.textContent = `File "${fileName}" uploaded successfully!`;
+ statusMessage.style.color = 'green';
+ } else {
+ // Upload failed
+ statusMessage.textContent = `File "${fileName}" failed to upload!`;
+ statusMessage.style.color = 'red';
+ }
+
+ // Append status message to the container
+ statusContainer.appendChild(statusMessage);
+ }
+}
+
+
+module.exports = { renderCompose, displayUploadStatus }
\ No newline at end of file
diff --git a/JS/likes.cjs b/JS/likes.cjs
new file mode 100644
index 0000000..73b974f
--- /dev/null
+++ b/JS/likes.cjs
@@ -0,0 +1,42 @@
+async function renderLikes(data, ipcRenderer) {
+ let container;
+ /** @type {Map} */
+ let a;
+ if (document.querySelector('#likescontainer')) {
+ container = document.querySelector('#likescontainer');
+ a = new Map(Array.from(container.querySelectorAll('.post-card')).map(o => ([o.dataset.bskyid, o])));
+ }
+ else {
+ container = document.createElement('div');
+ container.classList.add('cards-container');
+ container.id = 'likescontainer';
+ a = new Map();
+ }
+
+ // setupWorker();
+ // renderLikesFeed(posts.map(p => (p?.reply?.root?.uri || p.post.uri)?.trim()), likes, ipcRenderer);
+
+ for (const post of data) renderPostSingle(post, a, likes.map(o => ({ posturi: o.post.uri, likeuri: o.post.viewer?.like })), ipcRenderer, container);
+
+ const postids = Array.from(document.querySelectorAll('[data-like-bskyid]')).map(o => o.dataset.bskyid);
+ console.log(postids);
+
+ // "force" garbage collection
+ // delete a;
+ a.clear();
+ a = null;
+
+ // append container to the document body or a specific section
+ const postdiv = document.querySelector('#posts');
+ postdiv.querySelector('.placeholder')?.remove();
+ if (!document.querySelector('#likescontainer')) {
+ postdiv.appendChild(container);
+ document.addEventListener('click', (e) => {
+ if (e.target.tagName === "VIDEO") zoomvid(e);
+ else if (e.target.tagName === "IMG" || e.target.classList.contains('overlay')) zoomimg(e);
+ });
+ }
+}
+
+
+module.exports = renderLikes;
\ No newline at end of file
diff --git a/JS/posts.cjs b/JS/posts.cjs
new file mode 100644
index 0000000..8e8cebe
--- /dev/null
+++ b/JS/posts.cjs
@@ -0,0 +1,638 @@
+const { formatStr } = require("../src/renderer.cjs");
+
+function zoomimg(e) {
+ const { src } = e.target,
+ overlayEl = document.querySelector('.overlay'),
+ zoomContainer = document.querySelector('#imgzoom');
+
+ zoomContainer.querySelector('video').style.display = 'none';
+ zoomContainer.querySelector('img').style.display = '';
+
+ zoomContainer.querySelector('img').src = src;
+ zoomContainer.classList.toggle('zoomed');
+ overlayEl.classList.toggle('hidden');
+ document.querySelector('.profile-container').classList.toggle('.noscroll');
+}
+
+function zoomvid(e) {
+ const { src } = e.target;
+ if (src.endsWith('/assets/video-loading.mp4')) return;
+
+ const zoomContainer = document.querySelector('#imgzoom');
+ if (zoomContainer.contains(e.target)) return;
+
+ e.target.pause();
+
+ zoomContainer.querySelector('video').style.display = '';
+ zoomContainer.querySelector('img').style.display = 'none';
+
+ zoomContainer.querySelector('video').src = src;
+ zoomContainer.classList.toggle('zoomed');
+ document.querySelector('.overlay').classList.toggle('hidden');
+ document.querySelector('.profile-container').classList.toggle('.noscroll');
+
+ const isZoomed = zoomContainer.classList.contains('zoomed');
+ e.target.parentElement.querySelector('.controls').style.display = (isZoomed) ? 'none !important' : ''
+}
+
+
+// video stuff
+function createVideoEl() {
+ // create main video container
+ const videoContainer = document.createElement('div');
+ videoContainer.classList.add('video-container');
+ videoContainer.classList.add('zoom-video');
+
+ // create video element
+ const videoElement = document.createElement('video');
+ videoElement.classList.add('video-element');
+ videoElement.src = ''; // specify the video source here
+ videoElement.controls = false; // disable default controls
+ videoElement.setAttribute('playsinline', ''); // ensures mobile compatibility for inline playback
+
+ // create controls container
+ const controls = document.createElement('div');
+ controls.classList.add('controls');
+
+ // create play/pause button
+ const playPauseButton = document.createElement('button');
+ playPauseButton.classList.add('play-pause');
+ playPauseButton.textContent = '';
+
+ // create fullscreen button
+ const fullscreenButton = document.createElement('button');
+ fullscreenButton.classList.add('fullscreen');
+ fullscreenButton.textContent = '⛶';
+
+ // create progress bar
+ const progressBar = document.createElement('input');
+ progressBar.classList.add('progress-bar');
+ progressBar.type = 'range';
+ progressBar.min = '0';
+ progressBar.value = '0';
+ progressBar.step = '0.1';
+
+ // create volume button with slider container
+ const volumeContainer = document.createElement('div');
+ volumeContainer.classList.add('volume-container');
+
+ const volumeButton = document.createElement('button');
+ volumeButton.classList.add('volume-button');
+ volumeButton.textContent = '🔊';
+
+ // append volume control to volume container
+ volumeContainer.appendChild(volumeButton);
+
+ // append controls to controls container
+ controls.appendChild(playPauseButton);
+ controls.appendChild(progressBar);
+ controls.appendChild(volumeContainer); // add volume container to the interface
+ controls.appendChild(fullscreenButton);
+
+ controls.style.display = 'none';
+
+ // append video and controls to video container
+ videoContainer.appendChild(videoElement);
+ videoContainer.appendChild(controls);
+
+ // JavaScript functionality for controls
+
+ // update play/pause functionality
+ playPauseButton.addEventListener('click', () => {
+ videoContainer.querySelector('.play-icon')?.remove();
+ if (videoElement.paused || videoElement.ended) {
+ videoElement.play();
+ playPauseButton.classList.remove('play');
+ playPauseButton.classList.add('pause');
+ } else {
+ videoElement.pause();
+ playPauseButton.classList.remove('pause');
+ playPauseButton.classList.add('play');
+ }
+ });
+ fullscreenButton.onclick = (_) => videoElement.click();
+
+ // set initial icon state to "play"
+ playPauseButton.classList.add('play');
+
+ // update progress bar as video plays
+ videoElement.addEventListener('timeupdate', () => {
+ progressBar.value = (videoElement.currentTime / videoElement.duration) * 100;
+ });
+
+ videoElement.addEventListener('pause', () => {
+ playPauseButton.classList.remove('pause');
+ playPauseButton.classList.add('play');
+ });
+
+ videoElement.addEventListener('play', () => {
+ playPauseButton.classList.remove('play');
+ playPauseButton.classList.add('pause');
+ });
+
+ // allow user to skip within video by dragging progress bar
+ progressBar.addEventListener('input', () => {
+ videoElement.currentTime = (progressBar.value / 100) * videoElement.duration;
+ });
+
+ // adjust video volume with volume control
+ let preVol;
+ volumeButton.addEventListener('click', () => {
+ if (videoElement.volume) {
+ preVol = videoElement.volume;
+ videoElement.volume = '0';
+ volumeButton.textContent = '🔇';
+ }
+ else {
+ videoElement.volume = preVol || '1';
+ volumeButton.textContent = '🔊';
+ }
+ });
+
+ return videoContainer;
+}
+
+
+function sendSuccess(dmItem, failed = false) {
+ const image = dmItem.querySelector('img');
+ if (image) {
+ // Create the checkmark div
+ const checkmark = document.createElement('div');
+ checkmark.className = (failed) ? 'crossmark' : 'checkmark';
+ checkmark.style.marginRight = '10px';
+
+ // Replace the image with the checkmark
+ image.replaceWith(checkmark);
+
+ setTimeout(() => {
+ document.querySelector('#dmPopup').querySelector('.close-btn').click();
+ checkmark.replaceWith(image);
+ checkmark.remove();
+ }, 1500);
+ }
+ else console.log('no pfp found!');
+
+ dmItem.classList.add((failed) ? 'error' : 'success');
+}
+
+
+function renderDMs(posturi, follows, ipcRenderer) {
+ const dmList = document.getElementById('dmList');
+ dmList.innerHTML = '';
+
+ follows.forEach(follow => {
+ const dmItem = document.createElement('div');
+ dmItem.className = 'dm-item' + (follow.dm ? '' : ' disabled');
+ dmItem.innerHTML = `
+
+
+
${follow.displayName || follow.handle}
+
@${follow.handle}
+
+ `;
+
+ dmItem.addEventListener('click', async (e) => {
+ e.preventDefault();
+ if (!follow.dm) return alert('not allowed!');
+
+ const r = await ipcRenderer.invoke('send-post', posturi, follow.handle, follow.dm.id);
+ if (!r) sendSuccess(dmItem, true);
+ else sendSuccess(dmItem);
+ });
+
+ dmList.appendChild(dmItem);
+ });
+}
+
+
+function createButtonEls(card, ipcRenderer, idkey = 'bskyid', containerid = 'cards') {
+ // Add this to your function after creating the main card content
+ const buttonContainer = document.createElement('div');
+ buttonContainer.classList.add('button-container');
+
+ // Like button
+ const likeButton = document.createElement('button');
+ likeButton.classList.add('action-button', 'like-button');
+ likeButton.innerHTML = ``;
+ likeButton.title = 'Like';
+
+ // Like button
+ const repostButton = document.createElement('button');
+ repostButton.className = 'action-button';
+ repostButton.style.color = card.dataset.reposturi ? '#10c200' : 'grey';
+ repostButton.innerHTML = ``;
+ repostButton.title = 'Repost';
+
+ // Delete button
+ const deleteButton = document.createElement('button');
+ deleteButton.classList.add('action-button', 'delete-button');
+ deleteButton.innerHTML = '';
+ deleteButton.title = 'Delete';
+
+ // Send via DM button
+ const sendDMButton = document.createElement('button');
+ sendDMButton.classList.add('action-button', 'dm-button');
+ sendDMButton.innerHTML = '';
+ sendDMButton.title = 'Send via DM';
+
+ // Copy link button
+ const copyLinkButton = document.createElement('button');
+ copyLinkButton.classList.add('action-button', 'copy-link-button');
+ copyLinkButton.innerHTML = '';
+ copyLinkButton.title = 'Copy Link';
+
+ // Pin/Unpin toggle button
+ const pinButton = document.createElement('button');
+ pinButton.classList.add('action-button', 'pin-button');
+ pinButton.innerHTML = '';
+ pinButton.title = 'Pin/Unpin';
+
+ // Three-dot menu button to toggle dropdown
+ const menuButton = document.createElement('button');
+ menuButton.classList.add('action-button', 'menu-button');
+ menuButton.innerHTML = '';
+ menuButton.title = 'More options';
+
+ // Dropdown menu container for collapsible options
+ const dropdownMenu = document.createElement('div');
+ dropdownMenu.classList.add('dropdown-menu');
+ dropdownMenu.classList.add('hidden'); // initially hidden
+
+ dropdownMenu.append(pinButton, copyLinkButton)
+
+ // Append buttons to the container
+ buttonContainer.appendChild(likeButton);
+ buttonContainer.appendChild(repostButton);
+ buttonContainer.appendChild(deleteButton);
+ buttonContainer.appendChild(sendDMButton);
+ buttonContainer.appendChild(menuButton);
+ buttonContainer.appendChild(dropdownMenu);
+
+ // Like button functionality
+ likeButton.addEventListener('click', async () => {
+ const lurl = card.dataset.likeuri,
+ r = await ipcRenderer.invoke('post-action', 'like', card.dataset[`${idkey}`], lurl);
+
+ // if there is a url then it should return nothing
+ if (!r && !lurl) return alert("ERROR!");
+ else if (lurl) delete card.dataset.likeuri;
+ else if (r) card.dataset.likeuri = r.uri
+ likeButton.innerHTML = ``
+ });
+
+ // Delete button functionality
+ deleteButton.addEventListener('click', async () => {
+ if (confirm('Are you sure you want to delete this post?')) {
+ const r = await ipcRenderer.invoke('post-action', 'delete', card.dataset[`${idkey}`]);
+ if (!r) return alert("ERROR!");
+ card.remove();
+ // alert('Post deleted.');
+ }
+ });
+
+ // Send DM functionality
+ sendDMButton.addEventListener('click', async () => {
+ const r = await ipcRenderer.invoke('get-connections');
+ document.getElementById('dmPopup').classList.add('active');
+ renderDMs(card.dataset[`${idkey}`], r.follows, ipcRenderer);
+ });
+
+ // Copy link functionality
+ copyLinkButton.addEventListener('click', async () => {
+ const r = await ipcRenderer.invoke('post-action', 'link', card.dataset[`${idkey}`]);
+ if (!r) return alert("ERROR!");
+ navigator.clipboard.writeText(r).then(() => {
+ alert('Link copied to clipboard!');
+ });
+ });
+
+ // Repost functionality
+ repostButton.addEventListener('click', async () => {
+ const r = await ipcRenderer.invoke('post-action', 'repost', card.dataset[`${idkey}`], card.dataset.reposturi);
+
+ if (!r) alert('ERROR!');
+ else if (card.dataset.reposturi) {
+ // TODO: move the card back to it's original position or refresh the page?
+ if (!card.dataset.ismypost) card.remove();
+ delete card.dataset.reposturi;
+ repostButton.style.color = 'grey';
+ }
+ else {
+ const pinnedEl = document.querySelector(`#${containerid}container`).querySelector('.pinned-card');
+ if (pinnedEl) pinnedEl.after(card);
+ else document.querySelector(`#${containerid}container`).prepend(card);
+
+ card.dataset.reposturi = r;
+ repostButton.style.color = '#10c200';
+ }
+ });
+
+ // Pin/Unpin functionality
+ let isPinned = false;
+ pinButton.addEventListener('click', async () => {
+ return alert("TODO");
+ // const r = await ipcRenderer.invoke('post-action', 'pin', card.dataset.bskyid);
+ // return alert(r);
+
+ isPinned = !isPinned;
+ pinButton.title = isPinned ? 'Unpin' : 'Pin';
+ pinButton.classList.toggle('pinned', isPinned);
+ alert(isPinned ? 'Pinned!' : 'Unpinned!');
+ // Add logic for pinning/unpinning the post
+ });
+
+ menuButton.addEventListener('click', (e) => {
+ e.stopPropagation(); // prevent event bubbling
+ dropdownMenu.classList.toggle('hidden'); // show/hide menu
+ });
+
+ // Hide dropdown menu when clicking outside
+ document.addEventListener('click', (e) => {
+ if (!buttonContainer.contains(e.target)) {
+ dropdownMenu.classList.add('hidden'); // hide menu
+ }
+ });
+
+ return buttonContainer;
+}
+
+
+function setupWorker(name = 'bktemp') {
+ if (typeof (Worker) === "undefined") return console.error('workers not supported!\nswitching to manual...');
+
+ const w = new Worker("../JS/worker.js", { name });
+ w.postMessage('PING');
+ w.onmessage = function (event) {
+ console.log(event.data);
+ };
+
+ function stopWorker() {
+ w.terminate();
+ w = undefined;
+ }
+}
+
+/**
+ * @param {*} post
+ * @param {*} a
+ * @param {{posturi: string, likeuri: string}[]} likes
+ * @param {*} ipcRenderer
+ * @param {*} container
+ */
+function renderPostSingle(post, a, likes, ipcRenderer, container, idkey = 'bskyid', containerid) {
+ /** @type {Element} */
+ let card;
+ const cardId = (post?.reply?.root?.uri || post.post.uri)?.trim();
+
+ // if the card exists, use it; otherwise, create a new card
+ if (a.has(cardId)) card = a.get(cardId);
+ else {
+ card = document.createElement('div');
+ card.classList.add('post-card');
+ card.dataset[`${idkey}`] = cardId;
+ }
+
+ // handle repost reason if it hasn't been added to this card
+ if (post.reason && post.reason.by && !card.querySelector('.repost-section')) {
+ const aname = post.reason.by.displayName || formatStr(post.reason.by.handle);
+ const repostSection = document.createElement('div');
+ repostSection.classList.add('repost-section');
+
+ const repostAvatar = document.createElement('img');
+ repostAvatar.src = post.reason.by.avatar;
+ repostAvatar.alt = `${aname}'s avatar`;
+ repostAvatar.classList.add('card-avatar', 'repost-avatar');
+ repostAvatar.loading = 'lazy';
+
+ const repostInfo = document.createElement('div');
+ repostInfo.classList.add('repost-info');
+
+ const repostName = document.createElement('h2');
+ repostName.innerHTML = aname;
+ repostName.classList.add('author-name');
+
+ const repostHandle = document.createElement('p');
+ repostHandle.innerHTML = `${formatStr('@' + post.reason.by.handle)} reposted`;
+ repostHandle.classList.add('author-handle');
+
+ repostInfo.appendChild(repostName);
+ repostInfo.appendChild(repostHandle);
+ repostSection.appendChild(repostAvatar);
+ repostSection.appendChild(repostInfo);
+
+ card.dataset.ismypost = post.post.author.did === post.reason.by.did;
+ card.dataset.reposturi = post.post.viewer.repost;
+ card.prepend(repostSection);
+ }
+
+ // handle reply if it hasn't been added to this card
+ if (post.reply && post.reply.root && !card.querySelector('.original-post')) {
+ const originalPost = document.createElement('div');
+ originalPost.classList.add('original-post');
+
+ const repaname = post.reply.root.author.displayName || formatStr(post.reply.root.author?.handle);
+ // add the original author and text
+ const originalAuthor = document.createElement('p');
+ originalAuthor.innerHTML = `Replying to ${repaname}`;
+ originalAuthor.classList.add('original-author');
+ originalPost.appendChild(originalAuthor);
+
+ const originalText = document.createElement('p');
+ originalText.innerHTML = formatStr(post.reply.root.record.text) || 'Image reply';
+ originalText.classList.add('original-text');
+ originalPost.appendChild(originalText);
+
+ // add the original image if present
+ if (post.reply.root.embed?.images?.length > 0) {
+ const originalImage = document.createElement('img');
+ originalImage.loading = 'lazy';
+ originalImage.src = post.reply.root.embed.images[0].thumb;
+ originalImage.alt = post.reply.root.embed.images[0].alt || 'Original post image';
+ originalImage.classList.add('original-image');
+ originalPost.appendChild(originalImage);
+ }
+
+ card.appendChild(originalPost);
+
+ // add the section for "what I was replying to" if grandparentAuthor exists
+ if (post.reply.grandparentAuthor) {
+ const parentPost = document.createElement('div');
+ parentPost.classList.add('parent-post');
+
+ const spacingEl = document.createElement('p');
+ spacingEl.className = 'convcont';
+ spacingEl.textContent = '.....';
+
+ const parentAuthor = document.createElement('p');
+ const paraname = post.reply.parent.author.displayName || formatStr(post.reply.parent.author.handle);
+ parentAuthor.innerHTML = `${paraname}`;
+ parentAuthor.classList.add('original-author');
+
+ parentPost.append(spacingEl, parentAuthor);
+
+ const parentText = document.createElement('p');
+ parentText.innerHTML = formatStr(post.reply.parent.record.text) || 'Image reply';
+ parentText.classList.add('original-text');
+ parentPost.appendChild(parentText);
+
+ if (post.reply.parent.embed && post.reply.parent.embed.images && post.reply.parent.embed.images.length > 0) {
+ const parentImage = document.createElement('img');
+ parentImage.loading = 'lazy';
+ parentImage.src = post.reply.parent.embed.images[0].thumb;
+ parentImage.alt = post.reply.parent.embed.images[0].alt || 'Parent post image';
+ parentImage.classList.add('original-image');
+ parentPost.appendChild(parentImage);
+ }
+
+ card.appendChild(parentPost);
+ }
+ }
+
+ // author info section for the main post
+ if (!card.querySelector('.author-section')) {
+ const aname = post.post.author.displayName || formatStr(post.post.author.handle);
+ const authorSection = document.createElement('div');
+ authorSection.classList.add('author-section');
+
+ const avatar = document.createElement('img');
+ avatar.src = post.post.author.avatar;
+ avatar.alt = `${aname}'s avatar`;
+ avatar.classList.add('card-avatar');
+ avatar.loading = 'lazy';
+
+ const authorInfo = document.createElement('div');
+ authorInfo.classList.add('author-info');
+
+ const displayName = document.createElement('h2');
+ displayName.innerHTML = aname;
+ displayName.classList.add('author-name');
+
+ const handle = document.createElement('p');
+ handle.innerHTML = `${formatStr('@' + post.post.author.handle)}`;
+ handle.classList.add('author-handle');
+
+ authorInfo.appendChild(displayName);
+ authorInfo.appendChild(handle);
+ authorSection.appendChild(avatar);
+ authorSection.appendChild(authorInfo);
+
+ card.appendChild(authorSection);
+ }
+
+ // main post text content
+ if (!card.querySelector('.post-text')) {
+ const textContent = document.createElement('p');
+ textContent.innerHTML = formatStr(post.post.record.text);
+ textContent.classList.add('post-text');
+ card.appendChild(textContent);
+ }
+
+ // handle video embed (if present and not added yet)
+ if (post.post.embed && post.post.embed.$type === 'app.bsky.embed.video#view' && !card.querySelector('.post-video')) {
+ const videoContainer = createVideoEl(), videoPlayer = videoContainer.querySelector('video');
+
+ videoPlayer.classList.add('post-video');
+ videoPlayer.width = post.post.embed.aspectRatio?.width;
+ videoPlayer.height = post.post.embed.aspectRatio?.height;
+ videoContainer.appendChild(videoPlayer);
+ card.appendChild(videoContainer);
+ videoPlayer.src = '../assets/video-loading.mp4';
+ videoPlayer.dataset.src = post.post.embed.playlist;
+ videoPlayer.poster = post.post.embed.thumbnail;
+ }
+
+ // main post image (if it exists and hasn't been added)
+ if (post.post.embed && post.post.embed.images && post.post.embed.images.length > 0 && !card.querySelector('.post-image')) {
+ const repeatimg = card.querySelector('.original-image')?.src === post.post.embed.images[0].thumb;
+ if (!repeatimg) {
+ const imageContainer = document.createElement('div');
+ imageContainer.classList.add('image-container');
+
+ const postImage = document.createElement('img');
+ postImage.src = post.post.embed.images[0].thumb;
+ postImage.alt = post.post.embed.images[0].alt || 'Embedded image';
+ postImage.classList.add('post-image');
+ postImage.loading = 'lazy';
+
+ imageContainer.appendChild(postImage);
+ card.appendChild(imageContainer);
+ }
+ }
+
+ // interaction counts
+ if (!card.querySelector('.interaction-counts')) {
+ const counts = document.createElement('div');
+ counts.classList.add('interaction-counts');
+ counts.textContent = `💬 ${post.post.replyCount} 🔄 ${post.post.repostCount} ❤️ ${post.post.likeCount}`;
+ card.appendChild(counts);
+ }
+
+ // add the card to the container if it's a new card
+ if (!a.has(cardId)) {
+ const lurl = likes.find(o => (o.posturi === cardId));
+ if (lurl) card.dataset.likeuri = lurl.likeuri;
+ card.appendChild(createButtonEls(card, ipcRenderer, idkey, containerid));
+ container.appendChild(card);
+ a.set(cardId, card);
+ }
+}
+
+
+/**
+ * @param {*} posts
+ * @param {*} likes
+ * @param {*} pinnedPost
+ * @param {*} ipcRenderer
+ */
+module.exports = function renderPosts(posts, likes, pinnedPost, ipcRenderer, idkey = 'bskyid', containerid = 'posts') {
+ console.log(`${idkey}, ${containerid}container, ${posts.length}`);
+ let container;
+ /** @type {Map} */
+ let a;
+ if (document.querySelector(`#${containerid}container`)) {
+ container = document.querySelector(`#${containerid}container`);
+ a = new Map(Array.from(container.querySelectorAll('.post-card')).map(o => ([o.dataset[`${idkey}`], o])));
+ }
+ else {
+ container = document.createElement('div');
+ container.classList.add('cards-container');
+ container.id = containerid;
+ a = new Map();
+ }
+
+ // setupWorker();
+ // renderLikesFeed(posts.map(p => (p?.reply?.root?.uri || p.post.uri)?.trim()), likes, ipcRenderer);
+
+ for (const post of posts) renderPostSingle(post, a, likes.map(o => ({ posturi: o.post.uri, likeuri: o.post.viewer?.like })), ipcRenderer, container, idkey, containerid);
+
+ const postids = Array.from(document.querySelectorAll(`[data-${idkey}]`)).map(o => o.dataset[`${idkey} `]);
+ console.log(postids);
+
+ // "force" garbage collection
+ // delete a;
+ a.clear();
+ a = null;
+
+ if (pinnedPost) {
+ const pinnedcard = container.querySelector(`[data-${idkey}="${pinnedPost.uri}"]`);
+ if (pinnedcard) {
+ const pinnedLabel = document.createElement('div');
+ pinnedLabel.classList.add('pinned-label');
+ pinnedLabel.textContent = '📌 Pinned'; // optionally add an emoji/icon
+ pinnedcard?.prepend(pinnedLabel); // place it at the top of the pinned card
+ pinnedcard?.classList.add('pinned-card');
+ container.prepend(pinnedcard); // move it to be first
+ }
+ }
+
+ // append container to the document body or a specific section
+ const postdiv = document.getElementById(containerid);
+ postdiv.querySelector('.placeholder')?.remove();
+ if (!document.querySelector(`#${containerid}container`)) {
+ postdiv.appendChild(container);
+ document.addEventListener('click', (e) => {
+ if (e.target.tagName === "VIDEO") zoomvid(e);
+ else if (e.target.tagName === "IMG" || e.target.classList.contains('overlay')) zoomimg(e);
+ });
+ }
+}
diff --git a/JS/replies.cjs b/JS/replies.cjs
new file mode 100644
index 0000000..354f179
--- /dev/null
+++ b/JS/replies.cjs
@@ -0,0 +1,102 @@
+// function to create a single reply card, with an optional parent post shown above it
+function createReplyCard(replyData) {
+ const replyCardContainer = document.createElement('div');
+ replyCardContainer.classList.add('reply-card-container');
+
+ // add parent post if exists, along with dots if it's not the root
+ if (replyData.reply.parent) {
+ const parentPost = createPostPreview(replyData.reply.parent, true);
+ replyCardContainer.appendChild(parentPost);
+
+ // add dots if there's a grandparent (to indicate more context above)
+ if (replyData.reply.grandparentAuthor) {
+ const dots = document.createElement('p');
+ dots.classList.add('convcont');
+ dots.textContent = '.....';
+ replyCardContainer.appendChild(dots);
+ }
+ }
+
+ // main reply card
+ const replyCard = document.createElement('div');
+ replyCard.classList.add('reply-card');
+
+ // author section
+ const authorSection = document.createElement('div');
+ authorSection.classList.add('author-section');
+
+ const avatar = document.createElement('img');
+ avatar.src = replyData.post.author.avatar;
+ avatar.alt = `${replyData.post.author.handle}'s avatar`;
+ avatar.classList.add('card-avatar');
+ authorSection.appendChild(avatar);
+
+ const authorInfo = document.createElement('div');
+ authorInfo.classList.add('author-info');
+ const authorName = document.createElement('p');
+ authorName.textContent = replyData.post.author.displayName || replyData.post.author.handle;
+ authorName.classList.add('author-name');
+ authorInfo.appendChild(authorName);
+
+ authorSection.appendChild(authorInfo);
+ replyCard.appendChild(authorSection);
+
+ // reply text
+ const replyText = document.createElement('p');
+ replyText.textContent = replyData.post.record.text || 'Image reply';
+ replyText.classList.add('reply-text');
+ replyCard.appendChild(replyText);
+
+ // embedded media if available
+ if (replyData.post.embed && replyData.post.embed.external) {
+ const mediaContainer = document.createElement('div');
+ mediaContainer.classList.add('media-container');
+
+ const mediaImage = document.createElement('img');
+ mediaImage.src = replyData.post.embed.external.thumb;
+ mediaImage.alt = replyData.post.embed.external.description;
+ mediaImage.classList.add('media-image');
+ mediaImage.loading = 'lazy';
+
+ mediaContainer.appendChild(mediaImage);
+ replyCard.appendChild(mediaContainer);
+ }
+
+ replyCardContainer.appendChild(replyCard);
+ return replyCardContainer;
+}
+
+// helper function to create a condensed preview of the parent post
+function createPostPreview(post, isParent = false) {
+ const postPreview = document.createElement('div');
+ postPreview.classList.add(isParent ? 'parent-post-preview' : 'grandparent-post-preview');
+
+ // author and content preview
+ const author = document.createElement('p');
+ author.textContent = `${post.author.displayName || post.author.handle}`;
+ author.classList.add('original-author');
+ postPreview.appendChild(author);
+
+ const content = document.createElement('p');
+ content.textContent = post.record.text || 'Image reply';
+ content.classList.add('original-text');
+ postPreview.appendChild(content);
+
+ return postPreview;
+}
+
+
+module.exports = function renderReplies(replyObj) {
+ const repliesContainer = document.querySelector('#replies');
+ repliesContainer.querySelector('.placeholder')?.remove();
+
+ const { cursor, replies } = replyObj;
+
+ if (cursor) sessionStorage.setItem('repliescursor', cursor);
+ else sessionStorage.removeItem('repliescursor');
+
+ replies.forEach(reply => {
+ const replyCard = createReplyCard(reply);
+ repliesContainer.appendChild(replyCard);
+ });
+}
diff --git a/JS/script.js b/JS/script.js
new file mode 100644
index 0000000..aae15fd
--- /dev/null
+++ b/JS/script.js
@@ -0,0 +1,58 @@
+document.addEventListener('scroll', () => bottomscrolled(document.querySelector('#posts')));
+async function bottomscrolled(targetDiv) {
+ const scrollPosition = window.scrollY + window.innerHeight,
+ pageHeight = document.documentElement.scrollHeight,
+ atbottom = (scrollPosition >= pageHeight);
+
+ if (!atbottom) return;
+
+ window.electronAPI.getnewposts();
+}
+
+// function to toggle the active section
+function showSection(sectionId) {
+ // hide all sections
+ document.querySelectorAll('.content').forEach((content) => {
+ content.classList.remove('active');
+ });
+
+ // remove active class from all buttons
+ document.querySelectorAll('.tab-buttons button').forEach((button) => {
+ button.classList.remove('active');
+ });
+
+ // show the selected section and activate the button
+ document.getElementById(sectionId).classList.add('active');
+ document.getElementById(sectionId + 'Btn').classList.add('active');
+}
+
+// function to handle layout selection
+function togglerelaxed(e) {
+ e.preventDefault(); // prevent the default anchor behavior
+
+ const value = e.target.getAttribute('data-value');
+ const container = document.querySelector(`#${sessionStorage.getItem('currenttab') || 'cardscontainer'}`);
+
+ console.log(value)
+
+ if (!container) return console.warn('container not found!');
+
+ if (value === 'compact') {
+ container.classList.remove('cards-container-relaxed');
+ container.classList.add('cards-container');
+ } else {
+ container.style.gridTemplateColumns = (value !== 'large') ? 'repeat(auto-fill, minmax(280px, 1fr))' : '';
+ container.classList.remove('cards-container');
+ container.classList.add('cards-container-relaxed');
+ }
+
+ // update dropdown button text to reflect current selection
+ document.querySelector('.dropdown-button').textContent = `Layout: ${value.charAt(0).toUpperCase() + value.slice(1)}`;
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ // event listener for dropdown items
+ document.querySelectorAll('#layoutDropdown a').forEach(item => {
+ item.addEventListener('click', togglerelaxed);
+ });
+});
\ No newline at end of file
diff --git a/JS/worker.js b/JS/worker.js
new file mode 100644
index 0000000..ac6f80c
--- /dev/null
+++ b/JS/worker.js
@@ -0,0 +1,9 @@
+// listen for messages from the main thread
+self.onmessage = function (event) {
+ const data = event.data;
+
+ self.postMessage(document.querySelector('#posts')?.firstChild)
+
+ // send the result back to the main thread
+ self.postMessage(result);
+};
diff --git a/assets/loading-transparent.gif b/assets/loading-transparent.gif
new file mode 100644
index 0000000..537f1f3
Binary files /dev/null and b/assets/loading-transparent.gif differ
diff --git a/assets/loading.gif b/assets/loading.gif
new file mode 100644
index 0000000..21aa764
Binary files /dev/null and b/assets/loading.gif differ
diff --git a/assets/video-loading.mp4 b/assets/video-loading.mp4
new file mode 100644
index 0000000..6592487
Binary files /dev/null and b/assets/video-loading.mp4 differ
diff --git a/bluesky-client/.gitignore b/bluesky-client/.gitignore
new file mode 100644
index 0000000..c6bba59
--- /dev/null
+++ b/bluesky-client/.gitignore
@@ -0,0 +1,130 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+.cache
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
diff --git a/bluesky/convoManager.js b/bluesky/convoManager.js
new file mode 100644
index 0000000..dc01e0b
--- /dev/null
+++ b/bluesky/convoManager.js
@@ -0,0 +1,65 @@
+import { ChatBskyConvoDefs } from "@atproto/api";
+import { convoHeader, getDID, initSession } from "./main.js";
+
+
+
+export async function getDMs(cursor = undefined, limit = undefined) {
+ try {
+ const agent = await initSession(),
+ convos = await agent.chat.bsky.convo.listConvos({ cursor, limit });
+ return convos.data;
+ }
+ catch (err) {
+ console.error(err);
+ return null;
+ }
+}
+
+
+export async function sendPost(e, posturi, utag, dmid = undefined) {
+ try {
+ const agent = await initSession(),
+ [, did, collection, rkey] = posturi.match(/^at:\/\/([^\/]+)\/([^\/]+)\/(.+)$/),
+ post = await agent.getPost({ repo: did, rkey, collection }),
+
+ /** @type {ChatBskyConvoDefs.MessageInput} */
+ msg = {
+ embed: {
+ $type: 'app.bsky.embed.record',
+ record: {
+ cid: post.cid,
+ uri: post.uri
+ }
+ },
+ text: ''
+ }
+
+ return await sendMessage(utag, msg, dmid);
+ }
+ catch (err) {
+ console.error(err);
+ return null;
+ }
+}
+
+
+export async function sendMessage(utag, message, convoIdInp = undefined) {
+ try {
+ const agent = await initSession();
+ let convoId;
+
+ if (convoIdInp) convoId = convoIdInp;
+ else {
+ const did = await getDID(utag);
+ if (!did) return null;
+
+ const convos = (await getDMs()).convos;
+ convoId = await agent.chat.bsky.convo.getConvo({ convoId: convos.find(o => o.members.find(o => o.did === did)) });
+ }
+ return (await agent.chat.bsky.convo.sendMessage({ convoId, message }, { headers: convoHeader })).data;
+ }
+ catch (err) {
+ console.error(err);
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/bluesky/files.js b/bluesky/files.js
new file mode 100644
index 0000000..c39de6a
--- /dev/null
+++ b/bluesky/files.js
@@ -0,0 +1,120 @@
+import { dialog, ipcMain } from "electron";
+import { PassThrough, Readable } from 'stream';
+import { initSession } from "./main.js";
+import sharp from "sharp";
+import ffmpeg from 'fluent-ffmpeg';
+import fs from 'fs';
+
+
+const fnamesToPosts = {};
+
+// Store data in main process only
+ipcMain.handle('set-sensitive-data', (event, key, value) => {
+ fnamesToPosts[key] = value;
+});
+
+ipcMain.handle('get-files', (event, key) => {
+ return JSON.stringify(fnamesToPosts);
+});
+
+export const clearPostFiles = () => {
+ for (const key in fnamesToPosts) {
+ delete fnamesToPosts[key];
+ }
+}
+export const getPostFiles = () => Object.entries(fnamesToPosts);
+export const popPostFile = (fname) => {
+ if (!fname || !(fname in fnamesToPosts)) return;
+ const o = fnamesToPosts[fname];
+ delete fnamesToPosts[fname];
+ return o;
+}
+
+ipcMain.handle('clear-sensitive-data', clearPostFiles);
+
+export const isAnimated = (mimeType) => (mimeType.startsWith('video/') || mimeType.endsWith('/gif'));
+
+
+function resizeVideo(data, type) {
+ // normal video, do not convert
+ if (!type.endsWith('/gif')) {
+ const readableStream = new Readable();
+ readableStream.push(data);
+ readableStream.push(null);
+ return readableStream;
+ }
+
+ const passThroughStream = new PassThrough();
+ ffmpeg(data)
+ // .size(`${width}x?`) // Resize to specified width, keeping aspect ratio
+ .outputOptions('-c:v libx264', '-crf 28') // Set codec and compression level
+ .format('mp4') // Set format to MP4
+ .on('error', (err) => {
+ console.error('Error processing video:', err);
+ passThroughStream.destroy(err);
+ })
+ .on('end', () => {
+ console.log('Video processing completed.');
+ })
+ .pipe(passThroughStream, { end: true });
+
+ return passThroughStream;
+}
+
+
+
+function resizePhoto(data, type) {
+ const outputStream = new PassThrough();
+ sharp(data)
+ .resize({ width: 800 }) // adjust width to reduce size REMOVEME?
+ .toFormat(type)
+ .pipe(outputStream);
+
+ return outputStream;
+}
+
+
+export async function uploadFile(fobj) {
+ // bluesky does not support gifs, so EVERYTHING needs to be an mp4
+ const agent = await initSession(),
+ anim = isAnimated(fobj.type),
+ type = anim ? 'mp4' : fobj.type.split('/')?.at(1),
+ typefull = anim ? 'video/mp4' : fobj.type;
+
+ let outputStream;
+
+ if (anim) outputStream = resizeVideo(fobj.data, fobj.type);
+ else outputStream = resizePhoto(fobj.data, type);
+
+ // not reading through a stream ffs
+ // if (compressedBuffer.length > 976 * 1024) {
+ // console.error("File is still too large after compression. Try further resizing.");
+ // return;
+ // }
+
+ // upload the stream as a blob
+ const { data } = await agent.uploadBlob(outputStream, { encoding: typefull });
+ return data;
+}
+
+
+/**
+ * @param {...{name: string, type: string, size: number, data: Uint8Array}} fobjs
+ */
+export async function handleFileOpen(event, ...fobjs) {
+ const o = {};
+
+ for (const fobj of fobjs) {
+ try {
+ const blobData = await uploadFile(fobj);
+ o[fobj.name] = blobData.blob.ref;
+ fnamesToPosts[fobj.name] = blobData.blob;
+ }
+ catch (err) {
+ console.error(err);
+ o[fobj.name] = false;
+ }
+ }
+
+ return JSON.stringify(o);
+}
\ No newline at end of file
diff --git a/bluesky/main.js b/bluesky/main.js
new file mode 100644
index 0000000..e70c88a
--- /dev/null
+++ b/bluesky/main.js
@@ -0,0 +1,242 @@
+import { Agent, AppBskyFeedGenerator, CredentialSession } from '@atproto/api';
+import fs from 'fs';
+import * as crypto from 'crypto';
+import json from '../secrets/config.json' with { type: 'json' }
+import { ipcMain } from 'electron';
+import logger from '../logger.js';
+import { getHistory } from '../src/db.js';
+import convertAndServe, { clearCache } from './video.js';
+import { handleFileOpen } from './files.js';
+import post, { handlePostAction } from './post.js';
+import { sendPost } from './convoManager.js';
+
+const { uname, upass } = json.bluesky;
+const sessionFilePath = './secrets/session.json'; // path to your session file
+export const convoHeader = { "Atproto-Proxy": "did:web:api.bsky.chat#bsky_chat" };
+
+
+// function to load session data from the file
+function loadSession() {
+ try {
+ if (fs.existsSync(sessionFilePath)) {
+ const data = fs.readFileSync(sessionFilePath, 'utf-8');
+ logger.info('session loaded successfully');
+ return JSON.parse(data);
+ }
+ logger.info('no existing session found');
+ return null;
+ } catch (error) {
+ logger.error('failed to load session:', error);
+ return null;
+ }
+}
+
+// create a Bluesky session and agent
+const session = new CredentialSession(new URL('https://bsky.social'));
+const agent = new Agent(session);
+
+// function to save session data to a file, with validation
+function saveSession(data) {
+ if (!data) {
+ logger.error('No session data to save.');
+ return;
+ }
+ try {
+ fs.writeFileSync(sessionFilePath, JSON.stringify(data), 'utf-8');
+ logger.info('session saved successfully');
+ } catch (error) {
+ logger.error('failed to save session:', error);
+ }
+}
+
+// initialize and resume session if possible
+export async function initSession() {
+ if (session?.session?.active) return agent;
+
+ const savedSession = loadSession();
+ if (savedSession) {
+ try {
+ await session.resumeSession(savedSession);
+ logger.info('session resumed successfully');
+ } catch (resumeError) {
+ logger.warn('failed to resume session, attempting to refresh:', resumeError);
+ try {
+ await session.refreshSession();
+ if (session.session) {
+ saveSession(session.session); // ensure session data exists before saving
+ logger.info('session refreshed and saved successfully');
+ } else {
+ logger.error('refresh failed to retrieve valid session data.');
+ await loginAndSaveSession();
+ }
+ } catch (refreshError) {
+ logger.error('session refresh failed, logging in again:', refreshError);
+ await loginAndSaveSession();
+ }
+ }
+ } else {
+ await loginAndSaveSession();
+ }
+
+ // await agent.deleteRepost('at://did:plc:amhzdnxsvkcqjgwdh5kqmhk7/app.bsky.feed.post/3l6tg2zdph62n');
+ return agent;
+}
+
+// helper function to login and save the session
+async function loginAndSaveSession() {
+ try {
+ await session.login({ identifier: uname, password: upass });
+ if (session.session) {
+ saveSession(session.session);
+ logger.info('logged in and session saved');
+ } else {
+ logger.error('login succeeded but no session data available to save');
+ }
+ } catch (loginError) {
+ logger.error('login failed:', loginError);
+ }
+}
+
+
+export const getDID = async (utag) => {
+ try {
+ let did;
+ if (!utag || utag === '@me') {
+ did = session.did || session?.session?.did;
+ } else {
+ const resolved = await agent.resolveHandle({ handle: utag });
+ did = resolved.data.did;
+ }
+ return did;
+ }
+ catch (err) {
+ console.error(err);
+ return null;
+ }
+}
+
+
+async function getUserData(utag, allData = false) {
+ try {
+ await initSession(); // ensure session is fully initialized before proceeding
+ const did = await getDID(utag);
+ if (!did) return { err: 'DID not found!' };
+
+ const { data } = await agent.getProfile({ actor: did });
+ const output = { profile: data };
+
+ if (allData) {
+ const { data: { feed, cursor } } = await agent.getAuthorFeed({ actor: output.profile.did, limit: 20, includePins: true }),
+ { data: { feed: likes, cursor: likesCursor } } = await agent.getActorLikes({ actor: did });
+
+ output.likes = likes;
+ output.likesCursor = likesCursor;
+ output.posts = feed;
+ output.postcursor = cursor;
+
+ output.replies = await getReplies(utag);
+ }
+
+ return output;
+ } catch (err) {
+ logger.error('failed to fetch user data:', err);
+ return {};
+ }
+}
+
+
+export const getPosts = async (utag, cursor = undefined, likesCursor = undefined) => {
+ try {
+ const did = await getDID(utag);
+ if (!did) return { err: 'DID not found!' };
+
+ const likes = await agent.getActorLikes({ actor: did, cursor: likesCursor }),
+ posts = await agent.getAuthorFeed({ actor: did, limit: 20, includePins: true, cursor });
+ return { posts, likes };
+ }
+ catch (err) {
+ console.error(err);
+ return { err: 'internal server error!' }
+ }
+}
+
+
+export const getReplies = async (utag, cursorInit, limit = 20) => {
+ const did = await getDID(utag);
+ let posts = await agent.getAuthorFeed({ actor: did, limit: limit, includePins: true, cursor: cursorInit });
+ let cursor = posts.data.cursor;
+ const replies = posts.data.feed.filter(o => o.reply);
+
+ while (cursor && replies.length < limit) {
+ posts = await agent.getAuthorFeed({ actor: did, limit: limit, includePins: true, cursor });
+ replies.push(...posts.data.feed.filter(o => o.reply));
+ cursor = posts.data.cursor;
+ }
+
+ return { replies, cursor };
+}
+
+
+export const getMedia = async (utag, cursorInit, limit = 20) => {
+ const did = await getDID(utag);
+ let posts = await agent.getAuthorFeed({ actor: did, limit: limit, includePins: true, cursor: cursorInit });
+ let cursor = posts.data.cursor;
+ const replies = posts.data.feed.filter(o => o.reply);
+
+ while (cursor && replies.length < limit) {
+ posts = await agent.getAuthorFeed({ actor: did, limit, includePins: true, cursor });
+ replies.push(...posts.data.feed.filter(o => o.reply));
+ cursor = posts.data.cursor;
+ }
+
+ return { replies, cursor };
+}
+
+
+export const getConnections = async (e, utag, cursor, limit = 20) => {
+ const did = await getDID(utag),
+ { data: { follows: followsRaw, cursor: followsCursor } } = await agent.getFollows({ actor: did, cursor, limit }),
+ { data: { followers: followersRaw, cursor: followersCursor } } = await agent.getFollowers({ actor: did, cursor, limit }),
+ { data: { convos } } = await agent.chat.bsky.convo.listConvos({}, { headers: convoHeader }),
+ follows = followsRaw?.length ? (await agent.getProfiles({ actors: followsRaw.map(o => o.did) })).data.profiles : [],
+ followers = followersRaw?.length ? (await agent.getProfiles({ actors: followersRaw.map(o => o.did) })).data.profiles : [];
+
+ return {
+ follows: follows.map(f => {
+ f.dm = convos.find(o => o.members.find(m => m.did === f.did));
+ return f;
+ }), followsCursor, followers, followersCursor
+ };
+}
+
+
+// export IPC setup function
+export async function setupIPC() {
+ ipcMain.handle('getdata', async (event, utag, all = false) => {
+ const data = await getUserData(utag, all);
+ event.sender.send('udata', JSON.stringify(data));
+ });
+
+ ipcMain.handle('getposts', async (e, utag, cursor, likescursor) => {
+ if (!cursor) return e.sender.send(404);
+ const data = await getPosts(utag, cursor, likescursor);
+ if (data.code) return e.sender.send(data.code);
+ e.sender.send('posts', JSON.stringify(data));
+ clearCache();
+ });
+
+ ipcMain.handle('get-connections', getConnections);
+
+ ipcMain.handle('gethistory', (e, limit, offset) => e.sender.send('history', JSON.stringify(getHistory(limit, offset))));
+ ipcMain.handle('getreplies', async (e, limit, offset) => e.sender.send('replies', JSON.stringify(await getReplies(limit, offset))));
+ ipcMain.handle('getvideo', async (e, oldurl) => {
+ const newURL = await convertAndServe(`${crypto.randomUUID()}.mp4`, oldurl);
+ e.sender.send('video', oldurl, newURL);
+ });
+
+ ipcMain.handle('send-post', sendPost);
+ ipcMain.handle('new-post', post);
+ ipcMain.handle('upload-file', handleFileOpen);
+ ipcMain.handle('post-action', async (e, action, id, condition) => await handlePostAction(e, action, id, agent, condition));
+}
+
diff --git a/bluesky/post.js b/bluesky/post.js
new file mode 100644
index 0000000..ecffaa4
--- /dev/null
+++ b/bluesky/post.js
@@ -0,0 +1,101 @@
+import { Agent, RichText } from "@atproto/api";
+import { getPostFiles, isAnimated, popPostFile, uploadFile } from "./files.js";
+import { initSession } from "./main.js";
+
+
+async function createEmbed(imgs, vid, embed) {
+ let embdData;
+
+ if (vid) {
+ embdData = {
+ $type: 'app.bsky.embed.video',
+ video: vid[1]
+ }
+ }
+ else if (vid) {
+ embdData = {
+ $type: 'app.bsky.embed.images',
+ images: imgs.map(f => ({ alt: 'image!', image: f[1] }))
+ }
+ }
+ else if (embed) {
+ const img = popPostFile(embed.Image);
+
+ return {
+ $type: 'app.bsky.embed.external',
+ external: {
+ description: embed.Description,
+ uri: embed.Uri,
+ title: embed.Title,
+ thumb: img
+ }
+ }
+ }
+}
+
+
+export default async function post(e, postData) {
+ try {
+ const { text, embed } = JSON.parse(postData);
+
+ const agent = await initSession(),
+ files = getPostFiles(),
+ imgs = files.filter(o => !isAnimated(o[1].mimeType)),
+ vid = files.find(o => isAnimated(o[1].mimeType)),
+ rt = new RichText({ text });
+
+ agent.post({
+ text: rt.text,
+ facets: rt.facets,
+ langs: ["en-US"],
+ createdAt: new Date().toISOString(),
+ embed: await createEmbed(imgs, vid, embed)
+ });
+ }
+ catch (err) {
+ console.error(err);
+ return false;
+ }
+}
+
+/**
+ * @param {*} e
+ * @param {*} action
+ * @param {*} postid
+ * @param {Agent} agent
+ * @returns
+ */
+export async function handlePostAction(e, action, postid, agent, condition = false) {
+ try {
+ if (!postid) return 404;
+
+ let r;
+ const [, did, collection, rkey] = postid.match(/^at:\/\/([^\/]+)\/([^\/]+)\/(.+)$/),
+ post = await agent.getPost({ repo: did, rkey, collection });
+
+ if (action === 'delete') {
+ await agent.deletePost(post.uri);
+ return postid;
+ }
+ else if (action === 'like') return await condition ? agent.deleteLike(condition) : agent.like(post.uri, post.cid);
+ else if (action === 'link') return `https://bsky.app/profile/${did}/post/${rkey}`;
+ else if (action === 'repost' && condition) {
+ await agent.deleteRepost(condition);
+ return postid;
+ }
+ else if (action === 'repost') {
+ const uri = (await agent.repost(post.uri, post.cid)).uri;
+ // const author = await agent.app.bsky.feed.searchPosts({ url: post.uri });
+ // console.log(author)
+ return uri;
+ }
+ else if (action === 'pin') return null; //agent.app.bsky.feed.sendInteractions({interactions: [{event: ''}]})
+
+
+ return JSON.stringify(r);
+ }
+ catch (err) {
+ console.error(err);
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/bluesky/video.js b/bluesky/video.js
new file mode 100644
index 0000000..7d43b24
--- /dev/null
+++ b/bluesky/video.js
@@ -0,0 +1,38 @@
+import ffmpeg from 'fluent-ffmpeg';
+import path from 'path';
+import fs from 'fs';
+
+const baseVideoCachePath = path.resolve('cache', 'videos');
+if (!fs.existsSync(baseVideoCachePath)) fs.mkdirSync(baseVideoCachePath, { recursive: true });
+
+// function to convert .m3u8 to .mp4
+function convertM3U8ToMP4(inpurl, outputPath) {
+ return new Promise((resolve, reject) => {
+ ffmpeg(inpurl)
+ .outputOptions('-c copy') // copies the codec without re-encoding for faster processing
+ .output(outputPath)
+ .on('end', resolve)
+ .on('error', reject)
+ .run();
+ });
+}
+
+
+// clean up the cache
+export async function clearCache(...vids) {
+ const arr = (vids?.length) ? vids : fs.readdirSync(baseVideoCachePath);
+ await Promise.all(arr.map((p) => new Promise(resolve => fs.rm(path.resolve(baseVideoCachePath, p), resolve))));
+}
+
+
+export default async function convertAndServe(fname, m3u8url) {
+ try {
+ const newPath = path.resolve(baseVideoCachePath, fname);
+ await convertM3U8ToMP4(m3u8url, newPath);
+ return fname;
+ }
+ catch (err) {
+ console.error(err);
+ return null;
+ }
+}
diff --git a/logger.js b/logger.js
new file mode 100644
index 0000000..0c50193
--- /dev/null
+++ b/logger.js
@@ -0,0 +1,25 @@
+import { createLogger, format, transports } from 'winston';
+
+// configure log file paths
+const logFilePath = './logs/app.log';
+const errorLogFilePath = './logs/error.log';
+
+// create a custom format for log messages
+const logFormat = format.combine(
+ format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
+ format.printf(({ level, message, timestamp }) => `${timestamp} [${level}]: ${message}`)
+);
+
+// initialize the logger
+const logger = createLogger({
+ level: 'info', // default logging level
+ format: logFormat,
+ transports: [
+ new transports.Console(), // log to console
+ new transports.File({ filename: logFilePath }), // log to a general log file
+ new transports.File({ filename: errorLogFilePath, level: 'error' }) // log errors separately
+ ],
+ exitOnError: false, // prevent exit on handled exceptions
+});
+
+export default logger
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..24c9693
--- /dev/null
+++ b/main.js
@@ -0,0 +1,76 @@
+import { app, BrowserWindow, shell } from 'electron'
+import path from 'path'
+import { setupIPC } from './bluesky/main.js';
+import { insertHistory } from './src/db.js';
+import logger from './logger.js';
+import { isURL } from './src/renderer.cjs';
+
+/** @type {BrowserWindow?} */
+let mainWindow;
+
+
+const quitApp = async () => {
+ try {
+ app.quit();
+ logger.info("clearing cache...");
+ await (await import('./bluesky/video.js')).clearCache();
+ logger.info('cache cleared!');
+ }
+ catch (err) {
+ logger.error(err);
+ }
+ finally {
+ process.exit('SIGINT');
+ }
+}
+
+
+// function to create the main application window
+const createMainWindow = () => {
+ mainWindow = new BrowserWindow({
+ width: 800,
+ height: 600,
+ webPreferences: {
+ preload: path.join(import.meta.dirname, 'src/user_page_preload.cjs'), // enable preload for secure communication
+ contextIsolation: true,
+ enableRemoteModule: false,
+ nodeIntegration: true,
+ }
+ });
+
+ mainWindow.webContents.setWindowOpenHandler((details) => {
+ mainWindow.webContents.executeJavaScript(`localStorage.getItem('allowlinks') ? true : confirm('allow redicrection to ${details.url}?')`)
+ .then(r => r ? shell.openExternal(details.url) : null);
+ return { action: "deny" };
+ });
+
+ mainWindow.loadFile('HTML/index.html'); // load the main HTML file
+ mainWindow.title = 'User Page';
+
+ mainWindow.on('closed', () => {
+ mainWindow = null // dereference the window object when closed
+ });
+
+ mainWindow.webContents.on('did-navigate', (_, url) => {
+ mainWindow.webContents.executeJavaScript('document.title')
+ .then(title => insertHistory(url, title));
+ });
+}
+
+
+app.on('ready', () => {
+ setupIPC();
+ process.on('SIGINT', quitApp);
+ createMainWindow();
+});
+
+// quit the app on all windows closed, except on macOS
+app.on('window-all-closed', () => {
+ quitApp();
+ if (process.platform !== 'darwin') app.quit();
+});
+
+// recreate window if app is re-activated (macOS behavior)
+app.on('activate', () => {
+ if (mainWindow === null) createMainWindow();
+});
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..cb6fcd3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3828 @@
+{
+ "name": "bluesky-client",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "bluesky-client",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "@atproto/api": "^0.13.14",
+ "axios": "^1.7.7",
+ "better-sqlite3": "^11.5.0",
+ "cron": "^3.1.9",
+ "fluent-ffmpeg": "^2.1.3",
+ "sharp": "^0.33.5",
+ "winston": "^3.16.0"
+ },
+ "devDependencies": {
+ "@electron/rebuild": "^3.7.0",
+ "electron": "^33.1.0"
+ }
+ },
+ "node_modules/@atproto/api": {
+ "version": "0.13.14",
+ "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.13.14.tgz",
+ "integrity": "sha512-CG5UpjI1WwSasSJTGadmr07EwWvl5JV658YZHcwIIg+Psk5sDloQOUJckuo1MP6wke1z6p/BUoPL4lxAATzMzA==",
+ "license": "MIT",
+ "dependencies": {
+ "@atproto/common-web": "^0.3.1",
+ "@atproto/lexicon": "^0.4.2",
+ "@atproto/syntax": "^0.3.0",
+ "@atproto/xrpc": "^0.6.3",
+ "await-lock": "^2.2.2",
+ "multiformats": "^9.9.0",
+ "tlds": "^1.234.0",
+ "zod": "^3.23.8"
+ }
+ },
+ "node_modules/@atproto/common-web": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.3.1.tgz",
+ "integrity": "sha512-N7wiTnus5vAr+lT//0y8m/FaHHLJ9LpGuEwkwDAeV3LCiPif4m/FS8x/QOYrx1PdZQwKso95RAPzCGWQBH5j6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "graphemer": "^1.4.0",
+ "multiformats": "^9.9.0",
+ "uint8arrays": "3.0.0",
+ "zod": "^3.23.8"
+ }
+ },
+ "node_modules/@atproto/lexicon": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.2.tgz",
+ "integrity": "sha512-CXoOkhcdF3XVUnR2oNgCs2ljWfo/8zUjxL5RIhJW/UNLp/FSl+KpF8Jm5fbk8Y/XXVPGRAsv9OYfxyU/14N/pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@atproto/common-web": "^0.3.1",
+ "@atproto/syntax": "^0.3.0",
+ "iso-datestring-validator": "^2.2.2",
+ "multiformats": "^9.9.0",
+ "zod": "^3.23.8"
+ }
+ },
+ "node_modules/@atproto/syntax": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.3.0.tgz",
+ "integrity": "sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==",
+ "license": "MIT"
+ },
+ "node_modules/@atproto/xrpc": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.6.3.tgz",
+ "integrity": "sha512-S3tRvOdA9amPkKLll3rc4vphlDitLrkN5TwWh5Tu/jzk7mnobVVE3akYgICV9XCNHKjWM+IAPxFFI2qi+VW6nQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@atproto/lexicon": "^0.4.2",
+ "zod": "^3.23.8"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@dabh/diagnostics": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+ "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+ "license": "MIT",
+ "dependencies": {
+ "colorspace": "1.1.x",
+ "enabled": "2.0.x",
+ "kuler": "^2.0.0"
+ }
+ },
+ "node_modules/@electron/get": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
+ "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "env-paths": "^2.2.0",
+ "fs-extra": "^8.1.0",
+ "got": "^11.8.5",
+ "progress": "^2.0.3",
+ "semver": "^6.2.0",
+ "sumchecker": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "global-agent": "^3.0.0"
+ }
+ },
+ "node_modules/@electron/node-gyp": {
+ "version": "10.2.0-electron.1",
+ "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
+ "integrity": "sha512-lBSgDMQqt7QWMuIjS8zNAq5FI5o5RVBAcJUGWGI6GgoQITJt3msAkUrHp8YHj3RTVE+h70ndqMGqURjp3IfRyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "glob": "^8.1.0",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^10.2.1",
+ "nopt": "^6.0.0",
+ "proc-log": "^2.0.1",
+ "semver": "^7.3.5",
+ "tar": "^6.2.1",
+ "which": "^2.0.2"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": ">=12.13.0"
+ }
+ },
+ "node_modules/@electron/node-gyp/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@electron/rebuild": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz",
+ "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
+ "@malept/cross-spawn-promise": "^2.0.0",
+ "chalk": "^4.0.0",
+ "debug": "^4.1.1",
+ "detect-libc": "^2.0.1",
+ "fs-extra": "^10.0.0",
+ "got": "^11.7.0",
+ "node-abi": "^3.45.0",
+ "node-api-version": "^0.2.0",
+ "ora": "^5.1.0",
+ "read-binary-file-arch": "^1.0.6",
+ "semver": "^7.3.5",
+ "tar": "^6.0.5",
+ "yargs": "^17.0.1"
+ },
+ "bin": {
+ "electron-rebuild": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=12.13.0"
+ }
+ },
+ "node_modules/@electron/rebuild/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@electron/rebuild/node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@electron/rebuild/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@electron/rebuild/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
+ "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
+ "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
+ "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
+ "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
+ "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
+ "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
+ "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
+ "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
+ "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
+ "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
+ "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
+ "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.0.5"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
+ "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
+ "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
+ "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
+ "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
+ "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
+ "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.2.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
+ "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
+ "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@malept/cross-spawn-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
+ "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/malept"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
+ }
+ ],
+ "license": "Apache-2.0",
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/@npmcli/fs": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
+ "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promisify": "^1.1.3",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@npmcli/fs/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@npmcli/move-file": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
+ "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
+ "deprecated": "This functionality has been moved to @npmcli/fs",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@types/cacheable-request": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-cache-semantics": "*",
+ "@types/keyv": "^3.1.4",
+ "@types/node": "*",
+ "@types/responselike": "^1.0.0"
+ }
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/keyv": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/luxon": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
+ "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.17.6",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz",
+ "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/responselike": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+ "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles/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,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ansi-styles/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,
+ "license": "MIT"
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "license": "MIT"
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/await-lock": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz",
+ "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/better-sqlite3": {
+ "version": "11.5.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.5.0.tgz",
+ "integrity": "sha512-e/6eggfOutzoK0JWiU36jsisdWoHOfN9iWiW/SieKvb7SAa6aGNmBM/UKyp+/wWSXpLlWNN8tCPwoDNPhzUvuQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/boolean": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
+ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/cacache": {
+ "version": "16.1.3",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
+ "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^2.1.0",
+ "@npmcli/move-file": "^2.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "glob": "^8.0.1",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "mkdirp": "^1.0.4",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/cacache/node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cacheable-lookup": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.6.0"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^4.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^6.0.1",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "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/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "license": "ISC"
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clone-response": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/color": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+ "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.3",
+ "color-string": "^1.6.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/colorspace": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
+ "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
+ "license": "MIT",
+ "dependencies": {
+ "color": "^3.1.3",
+ "text-hex": "1.0.x"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cron": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.9.tgz",
+ "integrity": "sha512-eNZu+YhLRHLxNJNqZgYuy8mXhzMV0XXmOSh+Ls2j1yTeQLCQ04ya7oNpujxwl4lEyck1fcVa1aRVZ+T4tAC7SA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/luxon": "~3.4.0",
+ "luxon": "~3.5.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz",
+ "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "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",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+ "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/electron": {
+ "version": "33.1.0",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-33.1.0.tgz",
+ "integrity": "sha512-7KiY6MtRo1fVFLPGyHS7Inh8yZfrbUTy43nNwUgMD2CBk729BgSwOC2WhmcptNJVlzHJpVxSWkiVi2hp9mH/bw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@electron/get": "^2.0.0",
+ "@types/node": "^20.9.0",
+ "extract-zip": "^2.0.1"
+ },
+ "bin": {
+ "electron": "cli.js"
+ },
+ "engines": {
+ "node": ">= 12.20.55"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enabled": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
+ "license": "MIT"
+ },
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "license": "(MIT OR WTFPL)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/exponential-backoff": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
+ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+ "license": "MIT"
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "license": "MIT"
+ },
+ "node_modules/fluent-ffmpeg": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz",
+ "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==",
+ "license": "MIT",
+ "dependencies": {
+ "async": "^0.2.9",
+ "which": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/fluent-ffmpeg/node_modules/async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="
+ },
+ "node_modules/fluent-ffmpeg/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/fn.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
+ "license": "MIT"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "license": "MIT"
+ },
+ "node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "license": "MIT"
+ },
+ "node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/global-agent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
+ "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "es6-error": "^4.1.1",
+ "matcher": "^3.0.0",
+ "roarr": "^2.15.3",
+ "semver": "^7.3.2",
+ "serialize-error": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/global-agent/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "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,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "11.8.6",
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^4.0.0",
+ "@szmarczak/http-timer": "^4.0.5",
+ "@types/cacheable-request": "^6.0.1",
+ "@types/responselike": "^1.0.0",
+ "cacheable-lookup": "^5.0.3",
+ "cacheable-request": "^7.0.2",
+ "decompress-response": "^6.0.0",
+ "http2-wrapper": "^1.0.0-beta.5.2",
+ "lowercase-keys": "^2.0.0",
+ "p-cancelable": "^2.0.0",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "license": "MIT"
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "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",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/http2-wrapper": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "license": "ISC"
+ },
+ "node_modules/ip-address": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
+ "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsbn": "1.1.0",
+ "sprintf-js": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/iso-datestring-validator": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz",
+ "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==",
+ "license": "MIT"
+ },
+ "node_modules/jsbn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
+ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kuler": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/logform": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz",
+ "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==",
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/luxon": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
+ "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/make-fetch-happen": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
+ "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "agentkeepalive": "^4.2.1",
+ "cacache": "^16.1.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^2.0.3",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^7.0.0",
+ "ssri": "^9.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/matcher": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
+ "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-collect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-fetch": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
+ "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.1.6",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.13"
+ }
+ },
+ "node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-sized": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+ "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/multiformats": {
+ "version": "9.9.0",
+ "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
+ "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
+ "license": "(Apache-2.0 AND MIT)"
+ },
+ "node_modules/napi-build-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+ "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-abi": {
+ "version": "3.71.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz",
+ "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-abi/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-api-version": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz",
+ "integrity": "sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ }
+ },
+ "node_modules/node-api-version/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
+ "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^1.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/one-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "license": "MIT",
+ "dependencies": {
+ "fn.name": "1.x.x"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-cancelable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
+ "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^1.0.1",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/proc-log": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
+ "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
+ "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/read-binary-file-arch": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz",
+ "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "bin": {
+ "read-binary-file-arch": "cli.js"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/responselike": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/roarr": {
+ "version": "2.15.4",
+ "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
+ "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "detect-node": "^2.0.4",
+ "globalthis": "^1.0.1",
+ "json-stringify-safe": "^5.0.1",
+ "semver-compare": "^1.0.0",
+ "sprintf-js": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/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==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "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,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/semver-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/serialize-error": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
+ "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "type-fest": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
+ "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.3",
+ "semver": "^7.6.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.33.5",
+ "@img/sharp-darwin-x64": "0.33.5",
+ "@img/sharp-libvips-darwin-arm64": "1.0.4",
+ "@img/sharp-libvips-darwin-x64": "1.0.4",
+ "@img/sharp-libvips-linux-arm": "1.0.5",
+ "@img/sharp-libvips-linux-arm64": "1.0.4",
+ "@img/sharp-libvips-linux-s390x": "1.0.4",
+ "@img/sharp-libvips-linux-x64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+ "@img/sharp-linux-arm": "0.33.5",
+ "@img/sharp-linux-arm64": "0.33.5",
+ "@img/sharp-linux-s390x": "0.33.5",
+ "@img/sharp-linux-x64": "0.33.5",
+ "@img/sharp-linuxmusl-arm64": "0.33.5",
+ "@img/sharp-linuxmusl-x64": "0.33.5",
+ "@img/sharp-wasm32": "0.33.5",
+ "@img/sharp-win32-ia32": "0.33.5",
+ "@img/sharp-win32-x64": "0.33.5"
+ }
+ },
+ "node_modules/sharp/node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/sharp/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==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/sharp/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==",
+ "license": "MIT"
+ },
+ "node_modules/sharp/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
+ "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^9.0.5",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+ "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ssri": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
+ "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sumchecker": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
+ "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^5.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
+ "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+ "license": "MIT",
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar/node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar/node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+ "license": "MIT"
+ },
+ "node_modules/tlds": {
+ "version": "1.255.0",
+ "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.255.0.tgz",
+ "integrity": "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==",
+ "license": "MIT",
+ "bin": {
+ "tlds": "bin.js"
+ }
+ },
+ "node_modules/triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/uint8arrays": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
+ "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==",
+ "license": "MIT",
+ "dependencies": {
+ "multiformats": "^9.4.2"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unique-filename": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
+ "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "unique-slug": "^3.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/unique-slug": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz",
+ "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/winston": {
+ "version": "3.16.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.16.0.tgz",
+ "integrity": "sha512-xz7+cyGN5M+4CmmD4Npq1/4T+UZaz7HaeTlAruFUTjk79CNMq+P6H30vlE4z0qfqJ01VHYQwd7OZo03nYm/+lg==",
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "^1.6.0",
+ "@dabh/diagnostics": "^2.0.2",
+ "async": "^3.2.3",
+ "is-stream": "^2.0.0",
+ "logform": "^2.6.0",
+ "one-time": "^1.0.0",
+ "readable-stream": "^3.4.0",
+ "safe-stable-stringify": "^2.3.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.7.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.8.0.tgz",
+ "integrity": "sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==",
+ "license": "MIT",
+ "dependencies": {
+ "logform": "^2.6.1",
+ "readable-stream": "^4.5.2",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport/node_modules/readable-stream": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
+ "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.23.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ba9ea5a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "bluesky-client",
+ "version": "1.0.0",
+ "main": "main.js",
+ "scripts": {
+ "start": "electron .",
+ "rebuild": "electron-rebuild -f -w better-sqlite3"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "devDependencies": {
+ "@electron/rebuild": "^3.7.0",
+ "electron": "^33.1.0"
+ },
+ "type": "module",
+ "dependencies": {
+ "@atproto/api": "^0.13.14",
+ "axios": "^1.7.7",
+ "better-sqlite3": "^11.5.0",
+ "cron": "^3.1.9",
+ "fluent-ffmpeg": "^2.1.3",
+ "sharp": "^0.33.5",
+ "winston": "^3.16.0"
+ }
+}
diff --git a/src/db.js b/src/db.js
new file mode 100644
index 0000000..65e6aa8
--- /dev/null
+++ b/src/db.js
@@ -0,0 +1,41 @@
+import Database from 'better-sqlite3';
+
+// initialize the database
+const db = new Database('db/history.db');
+
+
+// create the table if it doesn't exist
+db.prepare(`CREATE TABLE IF NOT EXISTS history (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ url TEXT,
+ title TEXT,
+ timestamp TEXT
+)`).run();
+
+
+export function insertHistory(url, title) {
+ const latest = getHistory(1),
+ timestamp = new Date().toISOString();
+
+ if (latest?.at(0)?.url === url) return;
+ const stmt = db.prepare(`INSERT INTO history (url, title, timestamp) VALUES (?, ?, ?)`);
+ stmt.run(url, title, timestamp);
+}
+
+
+export function deleteHistory(id) {
+ const stmt = db.prepare(`DELETE FROM history WHERE id = ?`);
+ stmt.run(id);
+}
+
+
+export function editHistory(id, newUrl, newTitle) {
+ const stmt = db.prepare(`UPDATE history SET url = ?, title = ? WHERE id = ?`);
+ stmt.run(newUrl, newTitle, id);
+}
+
+
+export function getHistory(limit = 50, offset = 0) {
+ const stmt = db.prepare(`SELECT * FROM history ORDER BY timestamp DESC LIMIT ? OFFSET ?`);
+ return stmt.all(limit, offset);
+}
diff --git a/src/renderer.cjs b/src/renderer.cjs
new file mode 100644
index 0000000..0f4f4f4
--- /dev/null
+++ b/src/renderer.cjs
@@ -0,0 +1,34 @@
+const isURL = (ustr) => {
+ try { return new URL(ustr) }
+ catch (err) { return null }
+}
+
+
+/**
+ * formats a string by converting URLs and mentions to HTML links
+ * @param {string} str - the input string to format
+ * @returns {string} - the formatted HTML string
+ */
+function formatStr(str) {
+ if (typeof str !== 'string') return ''; // validate input
+
+ const newStr = str.split(/\s+/).map((c) => {
+ // check if string is a URL
+ if (isURL(c)) {
+ return `${c}`;
+ }
+ // check if string is a mention
+ if (c.startsWith('@')) {
+ const profileLink = c.replace('@', '');
+ return `${c}`;
+ }
+
+ // return the word as-is if it's neither
+ return c;
+ });
+
+ return newStr.join(' ');
+}
+
+
+module.exports = { formatStr, isURL };
\ No newline at end of file
diff --git a/src/user_page_preload.cjs b/src/user_page_preload.cjs
new file mode 100644
index 0000000..c64071d
--- /dev/null
+++ b/src/user_page_preload.cjs
@@ -0,0 +1,165 @@
+const { contextBridge, ipcRenderer } = require('electron');
+const { populateProfile, Profile } = require('../JS/Profile.cjs');
+const renderPosts = require('../JS/posts.cjs');
+const renderReplies = require('../JS/replies.cjs');
+const renderLikes = require('../JS/likes.cjs');
+const { displayUploadStatus, renderCompose } = require('../JS/compose.cjs');
+
+async function handleFileDialogue(e) {
+ try {
+ const file = e.target.files[0];
+ if (file) {
+ try {
+ // read file data as a buffer
+ const fileBuffer = await file.arrayBuffer();
+
+ // send file data to main process
+ const files = await ipcRenderer.invoke('upload-file', {
+ name: file.name,
+ type: file.type,
+ size: file.size,
+ data: Buffer.from(fileBuffer)
+ });
+
+ e.target.dataset.fname = file.name;
+
+ displayUploadStatus(JSON.parse(files));
+ } catch (error) {
+ console.error("Failed to upload file:", error);
+ }
+ }
+ console.log(e.target.files);
+ // console.log('savePath: ', savePath);
+ } catch (e) {
+ console.log('Error:', e);
+ }
+}
+
+
+
+window.addEventListener('DOMContentLoaded', () => {
+ renderCompose(ipcRenderer);
+ const query = new URLSearchParams(window.location.search);
+ const utag = query.get('profile') || '@me';
+
+ ipcRenderer.on('udata', (e, dataRaw) => {
+ setupMutationObserver();
+
+ const data = JSON.parse(dataRaw),
+ pObj = new Profile(data.profile);
+
+ if (data.err) throw data.err;
+
+ console.log(data);
+ if (data.postcursor) sessionStorage.setItem('postcursor', data.postcursor);
+ if (data.likesCursor) sessionStorage.setItem('likescursor', data.likesCursor);
+
+ document.querySelector('#loading')?.remove();
+ populateProfile(pObj);
+
+ if (data.posts) renderPosts(data.posts, data.likes || [], pObj?.pinnedPost, ipcRenderer);
+ if (data.replies) renderReplies(data.replies);
+ if (data.likes) renderPosts(data.likes, data.likes, null, ipcRenderer, 'bskylikeid', 'likes');
+ });
+
+ contextBridge.exposeInMainWorld('electronAPI', {
+ getnewposts: () => {
+ const cursor = sessionStorage.getItem('postcursor'),
+ likescursor = sessionStorage.getItem('likescursor');
+
+ if (cursor) ipcRenderer.invoke('getposts', utag, cursor, likescursor);
+ else if (document.querySelector('#eof')) return;
+ else {
+ const d = document.createElement('div');
+ d.id = 'eof';
+ d.innerHTML = 'Reached end of feed!
';
+ document.querySelector('#posts')?.appendChild(d);
+ }
+ },
+ getnewlikes: () => {
+ return alert("TODO (check TODO.txt)");
+ const cursor = sessionStorage.getItem('postcursor');
+
+ if (cursor) ipcRenderer.invoke('getposts', utag, cursor, likescursor);
+ else if (document.querySelector('#eof')) return;
+ else {
+ const d = document.createElement('div');
+ d.id = 'eof';
+ d.innerHTML = 'Reached end of feed!
';
+ document.querySelector('#posts')?.appendChild(d);
+ }
+ },
+ getHistory: (cursor = undefined) => ipcRenderer.invoke('gethistory', cursor),
+ getReplies: (cursor = undefined) => ipcRenderer.invoke('getreplies', cursor),
+ getVideo: (src) => ipcRenderer.invoke('getvideo', src),
+ });
+
+ ipcRenderer.on('posts', (e, rawData) => {
+ const data = JSON.parse(rawData);
+ console.log(data);
+
+ // reset all videos because the cache was cleared
+ document.querySelectorAll('.post-card video').forEach(video => {
+ video.src = '../assets/video-loading.mp4';
+ video.pause();
+ video.currentTime = 0;
+ });
+
+ if (data.err) return alert(data.err);
+ if (data.feed) renderPosts(data.posts, data.likes?.map(o => ({ posturi: o.post.uri, likeuri: o.post.viewer?.like })), null, ipcRenderer);
+
+ if (data.cursor) sessionStorage.setItem('postcursor', data.cursor);
+ else sessionStorage.removeItem('postcursor');
+ });
+
+ ipcRenderer.on('history', (e, data) => {
+ const hist = JSON.parse(data);
+ console.log(hist);
+ });
+
+ ipcRenderer.on('video', (e, oldurl, newurl) => {
+ const vel = document.querySelector(`video[data-src="${oldurl}"]`);
+ if (!vel) return console.error(`video with url ${oldurl} not found!`);
+
+ vel.parentElement.querySelector('.controls').style.display = '';
+ vel.src = `../cache/videos/${newurl}`;
+ vel.loop = false;
+ vel.removeEventListener('play', vidplaylistenerfunc);
+ vel.play();
+ });
+
+ ipcRenderer.on('replies', (e, data) => renderReplies(JSON.parse(data)));
+ ipcRenderer.invoke('getdata', utag, true);
+});
+
+const vidplaylistenerfunc = (e) => {
+ const video = e.target;
+ video.controls = false;
+ video.loop = true;
+ video.parentElement.querySelector('.play-pause').click();
+ ipcRenderer.invoke('getvideo', video.dataset.src);
+}
+
+const handleNewVideo = (video) => {
+ const playicon = document.createElement('div');
+ playicon.className = 'play-icon';
+ playicon.innerHTML = '►';
+ video.parentElement.appendChild(playicon);
+ video.addEventListener('click', vidplaylistenerfunc);
+}
+
+const setupMutationObserver = () => {
+ new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ mutation.addedNodes.forEach((node) => {
+ if (node.tagName === 'VIDEO') handleNewVideo(node);
+ else if (node.tagName === 'INPUT' && node.type == "file") node.addEventListener('change', handleFileDialogue);
+ else {
+ node.querySelectorAll?.('video').forEach(handleNewVideo);
+ document.querySelectorAll('input[type="file"]')?.forEach(el => el.addEventListener('change', handleFileDialogue));
+ }
+
+ });
+ });
+ }).observe(document.body, { childList: true, subtree: true });
+}
\ No newline at end of file