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 + +
+ + +
+ +
+ Avatar +

User Handle

+

User description goes here.

+
+ 100 Followers | + 150 Following | + 50 Posts +
+
+ + +
+ + + + + +
+ + +
+

nothing here...

+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ +
+ + +
+

+
+ +
+
+

nothing here...

+
+
+

nothing here...

+
+
+

nothing here...

+
+
+

nothing here...

+
+
+ +
+ +

Select a DM

+
+
+ + + \ 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.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