diff --git a/docs/guides/images/1-extension-page.png b/docs/guides/images/1-extension-page.png new file mode 100644 index 000000000..c908fe27a Binary files /dev/null and b/docs/guides/images/1-extension-page.png differ diff --git a/docs/guides/images/10-omnivore-api-keys.png b/docs/guides/images/10-omnivore-api-keys.png new file mode 100644 index 000000000..3d8670c3e Binary files /dev/null and b/docs/guides/images/10-omnivore-api-keys.png differ diff --git a/docs/guides/images/11-generate-key.png b/docs/guides/images/11-generate-key.png new file mode 100644 index 000000000..420dbc996 Binary files /dev/null and b/docs/guides/images/11-generate-key.png differ diff --git a/docs/guides/images/12-copy-key.png b/docs/guides/images/12-copy-key.png new file mode 100644 index 000000000..8b148a341 Binary files /dev/null and b/docs/guides/images/12-copy-key.png differ diff --git a/docs/guides/images/13-update-settings.png b/docs/guides/images/13-update-settings.png new file mode 100644 index 000000000..25b20edbb Binary files /dev/null and b/docs/guides/images/13-update-settings.png differ diff --git a/docs/guides/images/2-developer-mode.png b/docs/guides/images/2-developer-mode.png new file mode 100644 index 000000000..a06c247bd Binary files /dev/null and b/docs/guides/images/2-developer-mode.png differ diff --git a/docs/guides/images/3-load-unpacked.png b/docs/guides/images/3-load-unpacked.png new file mode 100644 index 000000000..323330a2f Binary files /dev/null and b/docs/guides/images/3-load-unpacked.png differ diff --git a/docs/guides/images/4-folder.png b/docs/guides/images/4-folder.png new file mode 100644 index 000000000..ecbc5fba5 Binary files /dev/null and b/docs/guides/images/4-folder.png differ diff --git a/docs/guides/images/5-folders.png b/docs/guides/images/5-folders.png new file mode 100644 index 000000000..5c3295dc6 Binary files /dev/null and b/docs/guides/images/5-folders.png differ diff --git a/docs/guides/images/6-installed.png b/docs/guides/images/6-installed.png new file mode 100644 index 000000000..92e0c6b6e Binary files /dev/null and b/docs/guides/images/6-installed.png differ diff --git a/docs/guides/images/7-options.png b/docs/guides/images/7-options.png new file mode 100644 index 000000000..41dc4eefc Binary files /dev/null and b/docs/guides/images/7-options.png differ diff --git a/docs/guides/images/8-options-page.png b/docs/guides/images/8-options-page.png new file mode 100644 index 000000000..a8231d6f5 Binary files /dev/null and b/docs/guides/images/8-options-page.png differ diff --git a/docs/guides/images/9-omnivore-settings.png b/docs/guides/images/9-omnivore-settings.png new file mode 100644 index 000000000..b3e15fd20 Binary files /dev/null and b/docs/guides/images/9-omnivore-settings.png differ diff --git a/pkg/extension-v3/Makefile b/pkg/extension-v3/Makefile new file mode 100644 index 000000000..807bb6d64 --- /dev/null +++ b/pkg/extension-v3/Makefile @@ -0,0 +1,26 @@ +all: firefox chrome edge + +build: * + rm -rf extension + yarn build + +firefox: + echo "building firefox package" + rm -rf extension + yarn build + FIREFOX_PKG_NAME="firefox-$(shell cat extension/manifest.json| jq -j .version).zip" ; \ + FIREFOX_SRC_NAME="firefox-$(shell cat extension/manifest.json| jq -j .version)-src.zip" ; \ + cd extension; zip -r ../$$FIREFOX_PKG_NAME *; cd ..;\ + zip -r $$FIREFOX_SRC_NAME src/* Makefile yarn.lock package.json; \ + echo "done" + +chrome: build + echo "building chrome package" + zip -r chrome-$(shell cat dist/manifest.json| jq -j .version).zip ./dist/* + +edge: build + echo "building edge package" + EDGE_PKG_NAME="omnivore-extension-edge-$(shell cat dist/manifest.json| jq -j .version).zip" ; \ + pushd dist; zip -r $${EDGE_PKG_NAME} ./*; popd; + + diff --git a/pkg/extension-v3/extension/background.js b/pkg/extension-v3/extension/background.js new file mode 100644 index 000000000..57944e188 --- /dev/null +++ b/pkg/extension-v3/extension/background.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,t=new Uint8Array(16);function n(){if(!e&&!(e="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return e(t)}const i=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;for(var r=[],o=0;o<256;++o)r.push((o+256).toString(16).substr(1));const s=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(r[e[t+0]]+r[e[t+1]]+r[e[t+2]]+r[e[t+3]]+"-"+r[e[t+4]]+r[e[t+5]]+"-"+r[e[t+6]]+r[e[t+7]]+"-"+r[e[t+8]]+r[e[t+9]]+"-"+r[e[t+10]]+r[e[t+11]]+r[e[t+12]]+r[e[t+13]]+r[e[t+14]]+r[e[t+15]]).toLowerCase();if(!function(e){return"string"==typeof e&&i.test(e)}(n))throw TypeError("Stringified UUID is invalid");return n},a=function(e,t,i){var r=(e=e||{}).random||(e.rng||n)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){i=i||0;for(var o=0;o<16;++o)t[i+o]=r[o];return t}return s(r)};const l=e=>{return t=void 0,n=void 0,r=function*(){return new Promise((t=>{chrome.storage.local.get(e,(n=>{const i=n&&n[e]||null;t(i)}))}))},new((i=void 0)||(i=Promise))((function(e,o){function s(e){try{l(r.next(e))}catch(e){o(e)}}function a(e){try{l(r.throw(e))}catch(e){o(e)}}function l(t){var n;t.done?e(t.value):(n=t.value,n instanceof i?n:new i((function(e){e(n)}))).then(s,a)}l((r=r.apply(t,n||[])).next())}));var t,n,i,r};var u=function(e,t,n,i){return new(n||(n=Promise))((function(r,o){function s(e){try{l(i.next(e))}catch(e){o(e)}}function a(e){try{l(i.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}l((i=i.apply(e,t||[])).next())}))};const c=e=>u(void 0,void 0,void 0,(function*(){var t,n;const i=yield l("omnivoreApiKey");let r={Accept:"application/json","Content-Type":"application/json"};i&&(r.Authorization=i);try{const i=null!==(n=null!==(t=yield l("omnivoreApiUrl"))&&void 0!==t?t:process.env.OMNIVORE_GRAPHQL_URL)&&void 0!==n?n:"";console.log(i);const o=yield fetch(`${i}/api/graphql`,{method:"POST",redirect:"follow",credentials:"include",mode:"cors",headers:r,body:e}),s=yield o.json();if(!("data"in s)||!s.data)throw new Error("No response data");return s.data}catch(t){console.log("[omnivore] error making api request: ",e)}}));var d=function(e,t,n,i){return new(n||(n=Promise))((function(r,o){function s(e){try{l(i.next(e))}catch(e){o(e)}}function a(e){try{l(i.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}l((i=i.apply(e,t||[])).next())}))};class g{constructor(){this.queue=[],this.isRunning=!1,this.isReady=!1,this.executeTask=e=>d(this,void 0,void 0,(function*(){var t,n;if(console.log("executing task: ",e),!this.libraryItemId)throw Error("Attempting to execute queue that is not ready.");try{let i=!1;switch(e.task){case"archive":yield(n=this.libraryItemId,u(void 0,void 0,void 0,(function*(){var e,t;const i=JSON.stringify({query:"mutation SetLinkArchived($input: ArchiveLinkInput!) {\n setLinkArchived(input: $input) {\n ... on ArchiveLinkSuccess {\n linkId\n message\n }\n ... on ArchiveLinkError {\n message\n errorCodes\n }\n }\n }\n ",variables:{input:{linkId:n,archived:!0}}}),r=yield c(i);return(null===(t=null===(e=r.setLinkArchived)||void 0===e?void 0:e.errorCodes)||void 0===t?void 0:t.length)?(console.log("[omnivore] api: error getting article:",r),r.setLinkArchived.errorCodes.indexOf("UNAUTHORIZED")>-1?(console.log("[omnivore] api is not authorized"),"unauthorized"):"failure"):"success"}))),i=!0;break;case"delete":yield function(e){return u(this,void 0,void 0,(function*(){const t=JSON.stringify({query:"mutation SetBookmarkArticle($input: SetBookmarkArticleInput!) {\n setBookmarkArticle(input: $input) {\n ... on SetBookmarkArticleSuccess {\n bookmarkedArticle {\n id\n }\n }\n ... on SetBookmarkArticleError {\n errorCodes\n }\n }\n }\n ",variables:{input:{articleID:e,bookmark:!1}}}),n=yield c(t);if(!n.setBookmarkArticle||n.setBookmarkArticle.errorCodes||!n.setBookmarkArticle.bookmarkedArticle)throw console.log("GQL Error deleting:",n),new Error("Error deleting.");return"success"}))}(this.libraryItemId),i=!0;break;case"addNote":yield function(e){var t,n,i,r;return u(this,void 0,void 0,(function*(){const o=JSON.stringify({query:"query GetArticle(\n $username: String!\n $slug: String!\n $includeFriendsHighlights: Boolean\n ) {\n article(username: $username, slug: $slug) {\n ... on ArticleSuccess {\n article {\n highlights(input: { includeFriends: $includeFriendsHighlights }) {\n ...HighlightFields\n }\n }\n }\n ... on ArticleError {\n errorCodes\n }\n }\n }\n fragment HighlightFields on Highlight {\n id\n type\n annotation\n }\n ",variables:{username:"me",slug:e.libraryItemId,includeFriendsHighlights:!1}}),s=yield c(o);if(null===(n=null===(t=s.article)||void 0===t?void 0:t.errorCodes)||void 0===n?void 0:n.length)return console.log("[omnivore] api: error getting article:",s),s.article.errorCodes.indexOf("UNAUTHORIZED")>-1?(console.log("[omnivore] api is not authorized"),"unauthorized"):"failure";console.log("DATA.ARTICLE: ",s.article);const l=null===(r=null===(i=s.article)||void 0===i?void 0:i.highlights)||void 0===r?void 0:r.find((e=>"NOTE"==e.type));if(l){const t=JSON.stringify({query:"\n mutation UpdateHighlight($input: UpdateHighlightInput!) {\n updateHighlight(input: $input) {\n ... on UpdateHighlightSuccess {\n highlight {\n id\n }\n }\n ... on UpdateHighlightError {\n errorCodes\n }\n }\n }\n ",variables:{input:{highlightId:l.id,annotation:l.annotation?l.annotation+"\n\n"+e.note:e.note}}}),n=yield c(t);return n.updateHighlight&&!n.updateHighlight.errorCodes&&n.updateHighlight.highlight?n.updateHighlight.highlight.id:void console.log("GQL Error updating note:",n)}{const t=a(),n=((e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce(((e,t)=>e+((t&=63)<36?t.toString(36):t<62?(t-26).toString(36).toUpperCase():t>62?"-":"_")),""))(8),i=JSON.stringify({query:"\n mutation CreateHighlight($input: CreateHighlightInput!) {\n createHighlight(input: $input) {\n ... on CreateHighlightSuccess {\n highlight {\n id\n }\n }\n ... on CreateHighlightError {\n errorCodes\n }\n }\n }\n ",variables:{input:{id:t,shortId:n,type:"NOTE",articleId:e.libraryItemId,annotation:e.note}}}),r=yield c(i);return r.createHighlight&&!r.createHighlight.errorCodes&&r.createHighlight.highlight?"success":(console.log("GQL Error setting note:",r),"failure")}}))}({note:e.note||"",libraryItemId:this.libraryItemId}),i=!0;break;case"setLabels":yield function(e,t){return u(this,void 0,void 0,(function*(){const n=JSON.stringify({query:"mutation SetLabels($input: SetLabelsInput!) {\n setLabels(input: $input) {\n ... on SetLabelsSuccess {\n labels {\n id\n name\n color\n }\n }\n ... on SetLabelsError {\n errorCodes\n }\n }\n }\n ",variables:{input:{pageId:e,labelIds:t}}}),i=yield c(n);if(!i.setLabels||i.setLabels.errorCodes||!i.setLabels.labels)throw console.log("GQL Error setting labels:",i),new Error("Error setting labels.");return i.setLabels.labels}))}(this.libraryItemId,null!==(t=e.labels)&&void 0!==t?t:[]),i=!0;break;case"editTitle":if(!e.title||!this.libraryItemId)throw new Error("Title not set, or library item not yet saved.");yield function(e,t){return u(this,void 0,void 0,(function*(){const n=JSON.stringify({query:"mutation UpdatePage($input: UpdatePageInput!) {\n updatePage(input: $input) {\n ... on UpdatePageSuccess {\n updatedPage {\n id\n }\n }\n ... on UpdatePageError {\n errorCodes\n }\n }\n }\n ",variables:{input:{pageId:e,title:t}}}),i=yield c(n);if(console.log(i),!i.updatePage||i.updatePage.errorCodes||!i.updatePage.updatedPage)throw console.log("GQL Error updating page:",i),new Error("Error updating title.");return"success"}))}(this.libraryItemId,e.title),i=!0;break;case"updateLabelCache":yield function(){return u(this,void 0,void 0,(function*(){const e=JSON.stringify({query:"query GetLabels {\n labels {\n ... on LabelsSuccess {\n labels {\n ...LabelFields\n }\n }\n ... on LabelsError {\n errorCodes\n }\n }\n }\n fragment LabelFields on Label {\n id\n name\n color\n description\n createdAt\n }\n "}),t=yield c(e);return t.labels&&!t.labels.errorCodes&&t.labels.labels?(yield(n={labels:t.labels.labels,labelsLastUpdated:(new Date).toISOString()},chrome.storage.local.set(n)),t.labels.labels):(console.log("GQL Error updating label cache response:",t,t),console.log(!t.labels,t.labels.errorCodes,!t.labels.labels),[]);var n}))}(),this.tabId&&chrome.tabs.sendMessage(this.tabId,{message:"updateLabelCache",status:"success",task:e.task})}i&&this.tabId&&chrome.tabs.sendMessage(this.tabId,{message:"updateToolbar",status:"success",task:e.task})}catch(t){console.log("[omnivore] task queue error: ",t),this.tabId&&(console.log("sending error message"),chrome.tabs.sendMessage(this.tabId,{message:"updateToolbar",status:"failure",task:e.task}))}}))}enqueue(e){this.queue.push(e),this.isReady&&this.runNext()}runNext(){return d(this,void 0,void 0,(function*(){if(this.isRunning||0===this.queue.length||!this.isReady)return;this.isRunning=!0;const e=this.queue.shift();if(e)try{yield this.executeTask(e)}catch(e){console.error("Task failed:",e)}finally{this.isRunning=!1,this.isReady&&this.runNext()}}))}setReady(e,t){console.log("setting ready"),this.tabId=e,this.libraryItemId=t,this.isReady=!0,this.runNext()}}var h=function(e,t,n,i){return new(n||(n=Promise))((function(r,o){function s(e){try{l(i.next(e))}catch(e){o(e)}}function a(e){try{l(i.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}l((i=i.apply(e,t||[])).next())}))};const p="undefined"!=typeof browser?browser:chrome,v={};chrome.runtime.onMessage.addListener(((e,t,n)=>h(void 0,void 0,void 0,(function*(){var n,i;console.log("message: ",e,"sender",null===(n=t.tab)||void 0===n?void 0:n.id);const r=null===(i=t.tab)||void 0===i?void 0:i.id;if("savePage"===e.action&&"object"==typeof(o=e)&&"string"==typeof o.url&&"string"==typeof o.title&&"string"==typeof o.clientRequestId&&"string"==typeof o.originalContent){const{result:t,libraryItemId:n}=yield function(e){var t,n,i;return u(this,void 0,void 0,(function*(){const r=JSON.stringify({query:"mutation SavePage ($input: SavePageInput!) {\n savePage(input:$input){\n ... on SaveSuccess {\n url\n clientRequestId\n }\n ... on SaveError {\n errorCodes\n }\n }\n }",variables:{input:Object.assign({source:"extension"},e)}}),o=yield c(r);return(null===(n=null===(t=o.savePage)||void 0===t?void 0:t.errorCodes)||void 0===n?void 0:n.length)?(console.log("[omnivore] api: error saving page:",o),o.savePage.errorCodes.indexOf("UNAUTHORIZED")>-1?(console.log("[omnivore] api is not authorized"),{result:"unauthorized"}):{result:"failure"}):{result:"success",libraryItemId:null===(i=o.savePage)||void 0===i?void 0:i.clientRequestId}}))}({url:e.url,title:e.title,clientRequestId:e.clientRequestId,originalContent:e.originalContent});if(console.log("result: ",t,"libraryItemId",n),r)switch(t){case"success":chrome.tabs.sendMessage(r,{message:"startToolbarDismiss",status:"success"}),n&&v[e.clientRequestId].setReady(r,n);break;case"failure":chrome.tabs.sendMessage(r,{message:"startToolbarDismiss",status:"failure"});break;case"unauthorized":chrome.tabs.sendMessage(r,{message:"showLoggedOutToolbar"})}}else if("enqueueTask"==e.action&&function(e){return"object"==typeof e&&"string"==typeof e.task&&"string"==typeof e.clientRequestId}(e)){const t=v[e.clientRequestId];console.log("enqueing task message: ",e),t.enqueue(Object.assign(Object.assign({},e),{libraryItemId:e.clientRequestId}))}var o;return!0})))),p.action.onClicked.addListener((e=>h(void 0,void 0,void 0,(function*(){const t=e.id;if(t){try{const e=yield(e=>h(void 0,void 0,void 0,(function*(){try{const t=yield chrome.tabs.sendMessage(e,{message:"ping"});return console.log("pingCheck: ",t),!0}catch(e){return!1}})))(t);e||(yield chrome.scripting.executeScript({target:{tabId:t},files:["content.js"]}))}catch(e){console.log("[omnivore] error injecting content script: ",e)}const e=a();v[e]=new g,chrome.tabs.sendMessage(t,{message:"savePage",clientRequestId:e})}}))))})(); \ No newline at end of file diff --git a/pkg/extension-v3/extension/content.js b/pkg/extension-v3/extension/content.js new file mode 100644 index 000000000..69945df3e --- /dev/null +++ b/pkg/extension-v3/extension/content.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={322:(e,o,t)=>{t.d(o,{W:()=>s});var n=t(454),i=t(782),r=function(e,o,t,n){return new(t||(t=Promise))((function(i,r){function s(e){try{l(n.next(e))}catch(e){r(e)}}function d(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var o;e.done?i(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(s,d)}l((n=n.apply(e,o||[])).next())}))};function s(){var e;return r(this,void 0,void 0,(function*(){(0,i.ig)();const o=document.querySelector("#omnivore-extension-root");if(!o||!o.shadowRoot)return void console.log("no statusBox to update");let t=yield(0,n.$)("labels").then((e=>{if(e&&e.length>0)return e}));(0,i.jC)("#omnivore-edit-labels-row"),null===(e=o.shadowRoot.querySelector("#omnivore-edit-label-input"))||void 0===e||e.focus();const r=o.shadowRoot.querySelector("#omnivore-edit-labels-list");if(!t)return console.error("No labels found, trying to update the cache."),void chrome.runtime.sendMessage({action:"enqueueTask",task:"updateLabelCache",clientRequestId:(0,i.mm)()});r&&(r.innerHTML="",t.sort(((e,o)=>e.name.localeCompare(o.name,void 0,{sensitivity:"base"}))).forEach((function(e,o){const t=function(e){const o=document.createElement("button"),t=document.createElement("span");t.style="width:10px;height:10px;border-radius:1000px;",t.style.setProperty("background-color",e.color);const n=document.createElement("span");n.style="margin-left: 10px;pointer-events: none;",n.innerText=e.name;const i=document.createElement("span");return i.style="margin-left: auto;pointer-events: none;",i.className="checkbox",i.innerHTML='\n \n \n \n ',o.appendChild(t),o.appendChild(n),o.appendChild(i),o.onclick=d,o.setAttribute("data-label-id",e.id),o.setAttribute("data-label-selected",e.selected?"on":"off"),o.setAttribute("tabIndex","-1"),o}(e);r.appendChild(t)})))}))}function d(e){var o;e.preventDefault();const t=null===(o=e.target)||void 0===o?void 0:o.getAttribute("data-label-id");t&&function(e,o){var t,s;r(this,void 0,void 0,(function*(){const r=null===(t=e.target)||void 0===t?void 0:t.getAttribute("data-label-selected");if(!o||!r)return;const d="on"!=r;null===(s=e.target)||void 0===s||s.setAttribute("data-label-selected",d?"on":"off");let l=yield(0,n.$)("labels").then((e=>{if(e&&e.length>0)return e}));if(!l)throw Error("No labels selected");l.find((e=>e.id===o))&&function(){var e;(0,i.PC)("#omnivore-edit-labels-status","waiting","Updating Labels...",void 0);const o=document.querySelector("#omnivore-extension-root"),t=null===(e=null==o?void 0:o.shadowRoot)||void 0===e?void 0:e.querySelector("#omnivore-edit-labels-list");if(t){const e=[...t.children].filter((e=>"on"===e.getAttribute("data-label-selected"))).map((e=>e.getAttribute("data-label-id")));chrome.runtime.sendMessage({action:"enqueueTask",task:"setLabels",clientRequestId:(0,i.mm)(),labels:e})}}()}))}(e,t)}},782:(e,o,t)=>{t.d(o,{Dz:()=>l,Jw:()=>m,PC:()=>f,Wj:()=>c,ig:()=>a,jC:()=>h,ll:()=>d,mm:()=>p});var n=t(454),i=t(322),r=function(e,o,t,n){return new(t||(t=Promise))((function(i,r){function s(e){try{l(n.next(e))}catch(e){r(e)}}function d(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var o;e.done?i(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(s,d)}l((n=n.apply(e,o||[])).next())}))};const s={waiting:'
',success:'\n \n \n \n ',failure:'\n \n \n \n \n '},d=e=>r(void 0,void 0,void 0,(function*(){var o;let t=null!==(o=document.querySelector("#omnivore-extension-root"))&&void 0!==o?o:void 0;document.body&&(console.log("existing currentToastEl: ",t),t||(t=yield r(void 0,void 0,void 0,(function*(){console.log("===== CREATING TOAST CONTAINER ===== ");const e=yield fetch(chrome.runtime.getURL("views/toast.html")),o=yield e.text(),t=document.createElement("div");if(t.tabIndex=0,t.id="omnivore-extension-root",t.attachShadow({mode:"open"}),t.style.opacity="1.0",!t.shadowRoot)return void alert("Error opening Omnivore user interface.");t.shadowRoot.innerHTML="";const n=document.createElement("div");return n.id="#omnivore-toast",n.innerHTML=o,n.tabIndex=0,t.shadowRoot.appendChild(n),document.body.appendChild(t),u(t),l("waiting"),t}))),(yield(0,n.$)("disableAutoDismiss"))&&(null==t||t.setAttribute("data-disable-auto-dismiss","true")),null==t||t.setAttribute("data-omnivore-client-request-id",e),null==t||t.focus({preventScroll:!0}),l("waiting"))})),l=(e,o=void 0)=>r(void 0,void 0,void 0,(function*(){var t;const n=document.querySelector("#omnivore-extension-root"),i=null===(t=null==n?void 0:n.shadowRoot)||void 0===t?void 0:t.querySelector(".omnivore-toast-statusBox");if(console.log("updating",e,i),i)switch(e){case"success":i.innerHTML=s.success;break;case"failure":i.innerHTML=s.failure;break;case"waiting":i.innerHTML=s.waiting}o&&("addNote"==o&&"failure"==e&&f("#omnivore-add-note-status","failure","Error adding note...",void 0),"addNote"==o&&"success"==e&&(f("#omnivore-add-note-status","success","Note updated.",2500),setTimeout((()=>{h("#omnivore-add-note-status")}),3e3)),"setLabels"==o&&"success"==e&&f("#omnivore-edit-labels-status","success","Labels Updated",2500),"setLabels"==o&&"failure"==e&&f("#omnivore-edit-labels-status","failure","Error Updating Labels...",2500),"editTitle"==o&&"failure"==e&&f("#omnivore-add-note-status","failure","Error updating title...",void 0),"editTitle"==o&&"success"==e&&(f("omnivore-edit-title-status","success","Title updated.",2500),setTimeout((()=>{h("#omnivore-edit-title-status")}),3e3)),"archive"==o&&(f("#omnivore-extra-status",e,"success"==e?"Success":"Error","success"==e?2500:void 0),"success"==e&&q()))})),a=()=>{const e=document.querySelector("#omnivore-extension-root");e&&e.setAttribute("data-disable-auto-dismiss","true")},c=e=>r(void 0,void 0,void 0,(function*(){e.status&&l(e.status);const o=yield r(void 0,void 0,void 0,(function*(){var e;const o=null!==(e=yield(0,n.$)("autoDismissTime"))&&void 0!==e?e:"2500";return Number.isNaN(Number(o))?2500:Number(o)}));setTimeout((()=>{const e=document.querySelector("#omnivore-extension-root");e&&!e.getAttribute("data-disable-auto-dismiss")&&(e.style.transition="opacity 3.5s ease;",e.style.opacity="0",setTimeout((()=>{const e=document.querySelector("#omnivore-extension-root");e&&!e.getAttribute("data-disable-auto-dismiss")&&e.remove()}),500))}),o)})),u=e=>{var o,t,n;const r=[{id:"#omnivore-toast-add-note-btn",func:w},{id:"#omnivore-toast-edit-title-btn",func:v},{id:"#omnivore-toast-edit-labels-btn",func:i.W},{id:"#omnivore-toast-read-now-btn",func:L},{id:"#omnivore-open-menu-btn",func:y},{id:"#omnivore-toast-close-btn",func:R},{id:"#omnivore-toast-login-btn",func:b},{id:"#omnivore-toast-archive-btn",func:x},{id:"#omnivore-toast-delete-btn",func:C}];for(const t of r){const n=null===(o=e.shadowRoot)||void 0===o?void 0:o.querySelector(t.id);n&&(console.log(t.id),n.addEventListener("click",t.func))}if(window.matchMedia("(max-width: 500px)").matches){const o=null===(t=e.shadowRoot)||void 0===t?void 0:t.querySelectorAll(".omnivore-top-button-label");null==o||o.forEach((e=>{e.style.display="none"}));const i=null===(n=e.shadowRoot)||void 0===n?void 0:n.querySelector("#omnivore-toast-container");i&&(i.style.width="280px",i.style.top="unset",i.style.bottom="20px")}};function v(){var e,o,t;a(),h("#omnivore-edit-title-row");let n=null!==(e=document.querySelector("#omnivore-extension-root"))&&void 0!==e?e:void 0;if(!n)return void console.log("no statusBox to update");const i=null===(o=null==n?void 0:n.shadowRoot)||void 0===o?void 0:o.querySelector("#omnivore-edit-title-textarea");i&&(i.focus(),i.onkeydown=e=>{e.cancelBubble=!0});const r=null===(t=null==n?void 0:n.shadowRoot)||void 0===t?void 0:t.querySelector("#omnivore-edit-title-form");r?r.onsubmit=e=>{var o;f("#omnivore-edit-title-status","waiting","Updating title...",void 0);const t=null!==(o=null==i?void 0:i.value)&&void 0!==o?o:"";chrome.runtime.sendMessage({action:"enqueueTask",task:"editTitle",clientRequestId:p(),title:t}),e.preventDefault()}:console.log("no form to update")}const m=()=>{a(),l("failure"),h("#omnivore-logged-out-row"),g(),f("#omnivore-logged-out-status",void 0,"You are not logged in.",void 0)},f=(e,o,t,n)=>{var i;const r=document.querySelector("#omnivore-extension-root"),d=null===(i=null==r?void 0:r.shadowRoot)||void 0===i?void 0:i.querySelector(e),l=(()=>{switch(o){case"waiting":return s.animatedLoader;case"success":return s.success;case"failure":return s.failure;default:return}})();if(l&&d){const e="failure"==o?"red":"unset";d.innerHTML=`${l}${t}`}else d&&(d.innerHTML=t);n&&d&&setTimeout((()=>{d.innerHTML=""}),n)},g=()=>{let e=document.querySelector("#omnivore-extension-root");["#omnivore-toast-edit-title-btn","#omnivore-toast-edit-labels-btn","#omnivore-toast-read-now-btn","#omnivore-toast-add-note-btn","#omnivore-open-menu-btn"].forEach((o=>{var t;const n=null===(t=null==e?void 0:e.shadowRoot)||void 0===t?void 0:t.querySelector(o);n&&(n.disabled=!0)}))},h=e=>{var o,t;let n=document.querySelector("#omnivore-extension-root");if(!n)return void console.log("toggleRow: no row to toggle");const i=null===(o=null==n?void 0:n.shadowRoot)||void 0===o?void 0:o.querySelector(e),r=null==i?void 0:i.getAttribute("data-state"),s=null===(t=null==n?void 0:n.shadowRoot)||void 0===t?void 0:t.querySelectorAll(".omnivore-toast-func-row");if(null==s||s.forEach((e=>{e.setAttribute("data-state","closed")})),i&&r){const e="open"===r?"closed":"open";i.setAttribute("data-state",e)}},p=()=>{const e=document.querySelector("#omnivore-extension-root");return null==e?void 0:e.getAttribute("data-omnivore-client-request-id")},b=()=>{window.open(new URL("/login",process.env.OMNIVORE_URL),"_blank"),q()},y=()=>{a(),h("#omnivore-extra-buttons-row")},w=()=>r(void 0,void 0,void 0,(function*(){var e,o,t;console.log("[omnivore] adding note"),a();const i=document.querySelector("#omnivore-extension-root"),s=null==i?void 0:i.getAttribute("data-omnivore-client-request-id");if(console.log("client request id: ",s),!s)return void f("#omnivore-add-note-status","failure","Error adding note...",void 0);const d=`cached-note-${document.location.href}`;a(),h("#omnivore-add-note-row");const l=null===(e=null==i?void 0:i.shadowRoot)||void 0===e?void 0:e.querySelector("#omnivore-add-note-textarea");if(l){if(d){const e=null!==(o=yield(0,n.$)(d))&&void 0!==o?o:"";l.value=e}l.value?l.select():l.focus(),l.addEventListener("input",(e=>r(void 0,void 0,void 0,(function*(){const o={};o[d]=e.target.value,yield(0,n.c)(o)})))),l.onkeydown=e=>r(void 0,void 0,void 0,(function*(){e.stopPropagation(),console.log("handling the enter key: ",e.keyCode),13==e.keyCode&&(e.metaKey||e.ctrlKey)&&(f("#omnivore-add-note-status","waiting","Adding note...",void 0),yield T(s,l.value))}))}const c=null===(t=null==i?void 0:i.shadowRoot)||void 0===t?void 0:t.querySelector("#omnivore-add-note-form");c&&(c.onsubmit=e=>r(void 0,void 0,void 0,(function*(){console.log("handling form submit"),f("#omnivore-add-note-status","waiting","Adding note...",void 0),l&&(yield T(s,l.value)),e.preventDefault(),e.stopPropagation()})))})),x=e=>r(void 0,void 0,void 0,(function*(){const o=p();try{yield chrome.runtime.sendMessage({action:"enqueueTask",task:"archive",clientRequestId:o})}catch(e){console.log("error archiving item")}e.preventDefault()})),C=e=>r(void 0,void 0,void 0,(function*(){const o=p();try{yield chrome.runtime.sendMessage({action:"enqueueTask",task:"delete",clientRequestId:o})}catch(e){console.log("error archiving item")}e.preventDefault()})),L=()=>r(void 0,void 0,void 0,(function*(){var e;a();let o=document.querySelector("#omnivore-extension-root");const t=null===(e=null==o?void 0:o.shadowRoot)||void 0===e?void 0:e.querySelector("#omnivore-toast-container");null==t||t.setAttribute("data-state","open"),window.open(new URL(`/article?url=${encodeURI(document.location.href)}`,yield(0,n.$)("omnivoreUrl")),"_blank"),q()})),q=()=>{setTimeout((()=>{R()}),1e3)},R=()=>{const e=document.querySelector("#omnivore-extension-root");e&&e.remove()},T=(e,o)=>r(void 0,void 0,void 0,(function*(){try{yield chrome.runtime.sendMessage({action:"enqueueTask",task:"addNote",note:o,clientRequestId:e})}catch(e){console.log("error adding note: ",e)}}))},454:(e,o,t)=>{t.d(o,{$:()=>n,c:()=>i});const n=e=>{return o=void 0,t=void 0,i=function*(){return new Promise((o=>{chrome.storage.local.get(e,(t=>{const n=t&&t[e]||null;o(n)}))}))},new((n=void 0)||(n=Promise))((function(e,r){function s(e){try{l(i.next(e))}catch(e){r(e)}}function d(e){try{l(i.throw(e))}catch(e){r(e)}}function l(o){var t;o.done?e(o.value):(t=o.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,d)}l((i=i.apply(o,t||[])).next())}));var o,t,n,i},i=e=>chrome.storage.local.set(e)}},o={};function t(n){var i=o[n];if(void 0!==i)return i.exports;var r=o[n]={exports:{}};return e[n](r,r.exports,t),r.exports}t.d=(e,o)=>{for(var n in o)t.o(o,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:o[n]})},t.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o);var n=t(782),i=t(322),r=function(e,o,t,n){return new(t||(t=Promise))((function(i,r){function s(e){try{l(n.next(e))}catch(e){r(e)}}function d(e){try{l(n.throw(e))}catch(e){r(e)}}function l(e){var o;e.done?i(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(s,d)}l((n=n.apply(e,o||[])).next())}))};const s=(e,o,t)=>r(void 0,void 0,void 0,(function*(){switch(console.log("[omnivore] content script message:",e),e.message){case"showLoggedOutToolbar":(0,n.Jw)(),t({success:!0});break;case"updateToolbar":(0,n.Dz)(e.status,e.task),t({success:!0});break;case"startToolbarDismiss":return(0,n.Wj)(e),void t({success:!0});case"updateLabelCache":(0,i.W)();break;default:return void t({success:!1})}}));chrome.runtime.onMessage.addListener(((e,o,t)=>r(void 0,void 0,void 0,(function*(){if("savePage"===e.message)return yield(o=e.clientRequestId,r(void 0,void 0,void 0,(function*(){console.log("[omnivore] v3 extension triggered: ",o),yield(e=>r(void 0,void 0,void 0,(function*(){chrome.runtime.onMessage.hasListener(s)||chrome.runtime.onMessage.addListener(s),yield(0,n.ll)(e)})))(o);const e=yield r(void 0,void 0,void 0,(function*(){const e=document.documentElement.outerHTML;return console.log("[omnivore] captured mainContent"),e}));console.log("[omnivore] collected page content: ",e);try{const t={clientRequestId:o,title:document.title,url:document.location.href,originalContent:e};yield chrome.runtime.sendMessage(Object.assign({action:"savePage"},t))}catch(e){console.log("error sending content: ",e)}}))),void t({success:!0});var o}))))})(); \ No newline at end of file diff --git a/pkg/extension-v3/extension/icons/icon-128.png b/pkg/extension-v3/extension/icons/icon-128.png new file mode 100644 index 000000000..d6df58ae7 Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-128.png differ diff --git a/pkg/extension-v3/extension/icons/icon-16.png b/pkg/extension-v3/extension/icons/icon-16.png new file mode 100644 index 000000000..5c0b8ad30 Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-16.png differ diff --git a/pkg/extension-v3/extension/icons/icon-19.png b/pkg/extension-v3/extension/icons/icon-19.png new file mode 100644 index 000000000..9c5f2a8ee Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-19.png differ diff --git a/pkg/extension-v3/extension/icons/icon-24.png b/pkg/extension-v3/extension/icons/icon-24.png new file mode 100644 index 000000000..a0654130b Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-24.png differ diff --git a/pkg/extension-v3/extension/icons/icon-256.png b/pkg/extension-v3/extension/icons/icon-256.png new file mode 100644 index 000000000..a4413ba25 Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-256.png differ diff --git a/pkg/extension-v3/extension/icons/icon-32.png b/pkg/extension-v3/extension/icons/icon-32.png new file mode 100644 index 000000000..2270c0762 Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-32.png differ diff --git a/pkg/extension-v3/extension/icons/icon-38.png b/pkg/extension-v3/extension/icons/icon-38.png new file mode 100644 index 000000000..1041a3f2a Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-38.png differ diff --git a/pkg/extension-v3/extension/icons/icon-48.png b/pkg/extension-v3/extension/icons/icon-48.png new file mode 100644 index 000000000..dda13bd4e Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-48.png differ diff --git a/pkg/extension-v3/extension/icons/icon-70.png b/pkg/extension-v3/extension/icons/icon-70.png new file mode 100644 index 000000000..41337352f Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-70.png differ diff --git a/pkg/extension-v3/extension/icons/icon-96.png b/pkg/extension-v3/extension/icons/icon-96.png new file mode 100644 index 000000000..3af64b0be Binary files /dev/null and b/pkg/extension-v3/extension/icons/icon-96.png differ diff --git a/pkg/extension-v3/extension/images/highlight.svg b/pkg/extension-v3/extension/images/highlight.svg new file mode 100644 index 000000000..e81aef802 --- /dev/null +++ b/pkg/extension-v3/extension/images/highlight.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/pkg/extension-v3/extension/images/label.svg b/pkg/extension-v3/extension/images/label.svg new file mode 100644 index 000000000..69fca4c19 --- /dev/null +++ b/pkg/extension-v3/extension/images/label.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pkg/extension-v3/extension/images/listen-hover.svg b/pkg/extension-v3/extension/images/listen-hover.svg new file mode 100644 index 000000000..0a8fe4a55 --- /dev/null +++ b/pkg/extension-v3/extension/images/listen-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pkg/extension-v3/extension/images/listen.svg b/pkg/extension-v3/extension/images/listen.svg new file mode 100644 index 000000000..f77b5bb33 --- /dev/null +++ b/pkg/extension-v3/extension/images/listen.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pkg/extension-v3/extension/images/pause-w-circle.svg b/pkg/extension-v3/extension/images/pause-w-circle.svg new file mode 100644 index 000000000..c8f4c1082 --- /dev/null +++ b/pkg/extension-v3/extension/images/pause-w-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/extension-v3/extension/images/playback-speed.svg b/pkg/extension-v3/extension/images/playback-speed.svg new file mode 100644 index 000000000..41dab8b19 --- /dev/null +++ b/pkg/extension-v3/extension/images/playback-speed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/extension-v3/extension/images/playback-voice.svg b/pkg/extension-v3/extension/images/playback-voice.svg new file mode 100644 index 000000000..c7b1670bd --- /dev/null +++ b/pkg/extension-v3/extension/images/playback-voice.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/pkg/extension-v3/extension/images/save.svg b/pkg/extension-v3/extension/images/save.svg new file mode 100644 index 000000000..7deeb4d1f --- /dev/null +++ b/pkg/extension-v3/extension/images/save.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pkg/extension-v3/extension/manifest.json b/pkg/extension-v3/extension/manifest.json new file mode 100644 index 000000000..fe2a13dca --- /dev/null +++ b/pkg/extension-v3/extension/manifest.json @@ -0,0 +1,42 @@ +{ + "manifest_version": 3, + "name": "Omnivore - Self Hosted", + "version": "3.0", + "description": "Save content to your Omnivore library.", + "permissions": [ + "activeTab", + "scripting", + "storage" + ], + "host_permissions": [ + "" + ], + "background": { + "service_worker": "background.js", + "scripts": ["background.js"] + }, + "web_accessible_resources": [ + { + "resources": [ + "images/*.svg", + "views/toast.html" + ], + "matches": [ + "" + ] + } + ], + "options_page": "views/options.html", + "action": { + "default_icon": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } + }, + "icons": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } +} diff --git a/pkg/extension-v3/extension/toolbar.js b/pkg/extension-v3/extension/toolbar.js new file mode 100644 index 000000000..3fc659d3a --- /dev/null +++ b/pkg/extension-v3/extension/toolbar.js @@ -0,0 +1 @@ +(()=>{"use strict";var r={322:(r,e,t)=>{t(454),t(782)},782:(r,e,t)=>{t(454),t(322)},454:(r,e,t)=>{}},e={};function t(o){var n=e[o];if(void 0!==n)return n.exports;var p=e[o]={exports:{}};return r[o](p,p.exports,t),p.exports}t.d=(r,e)=>{for(var o in e)t.o(e,o)&&!t.o(r,o)&&Object.defineProperty(r,o,{enumerable:!0,get:e[o]})},t.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),t(782)})(); \ No newline at end of file diff --git a/pkg/extension-v3/extension/views/options.html b/pkg/extension-v3/extension/views/options.html new file mode 100644 index 000000000..dd4838e6b --- /dev/null +++ b/pkg/extension-v3/extension/views/options.html @@ -0,0 +1,36 @@ + + + + Omnivore Extension Settings + + + +
+

API Key

+ + + + + +

+ + + + +

API URL

+ + + +

+ + +

OMNIVORE URL

+ + + +

+ + + + + diff --git a/pkg/extension-v3/extension/views/options.js b/pkg/extension-v3/extension/views/options.js new file mode 100644 index 000000000..357e8dab3 --- /dev/null +++ b/pkg/extension-v3/extension/views/options.js @@ -0,0 +1 @@ +function addStorage(e){return chrome.storage.local.set(e)}document.addEventListener("DOMContentLoaded",(()=>{const e=document.getElementById("save-api-key-btn"),t=document.getElementById("api-key");chrome.storage.local.get("omnivoreApiKey").then((e=>{t.value=e.omnivoreApiKey??""})),e.addEventListener("click",(e=>{addStorage({omnivoreApiKey:t.value})}));const o=document.getElementById("save-api-url-btn"),n=document.getElementById("api-url");chrome.storage.local.get("omnivoreApiUrl").then((e=>{n.value=e.omnivoreApiUrl??""})),o.addEventListener("click",(e=>{addStorage({omnivoreApiUrl:n.value})}));const r=document.getElementById("save-omnivore-url-btn"),a=document.getElementById("omnivore-url");chrome.storage.local.get("omnivoreUrl").then((e=>{a.value=e.omnivoreUrl??""})),r.addEventListener("click",(e=>{addStorage({omnivoreUrl:a.value})}))})); \ No newline at end of file diff --git a/pkg/extension-v3/extension/views/toast.html b/pkg/extension-v3/extension/views/toast.html new file mode 100644 index 000000000..adc760304 --- /dev/null +++ b/pkg/extension-v3/extension/views/toast.html @@ -0,0 +1,555 @@ + + +
+
+ +
+
+ + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +
+ + + + + + +
+ + +
diff --git a/pkg/extension-v3/firefox-3.0-src.zip b/pkg/extension-v3/firefox-3.0-src.zip new file mode 100644 index 000000000..019f0541b Binary files /dev/null and b/pkg/extension-v3/firefox-3.0-src.zip differ diff --git a/pkg/extension-v3/firefox-3.0.zip b/pkg/extension-v3/firefox-3.0.zip new file mode 100644 index 000000000..51860ce13 Binary files /dev/null and b/pkg/extension-v3/firefox-3.0.zip differ diff --git a/pkg/extension-v3/package.json b/pkg/extension-v3/package.json new file mode 100644 index 000000000..e0e510b58 --- /dev/null +++ b/pkg/extension-v3/package.json @@ -0,0 +1,25 @@ +{ + "name": "omnivore-extension", + "version": "1.0.0", + "description": "browser extension for omnivore", + "scripts": { + "build": "env webpack --mode production", + "watch": "webpack --mode development --watch" + }, + "devDependencies": { + "@types/chrome": "^0.0.268", + "@types/dompurify": "^3.0.5", + "@types/firefox-webext-browser": "^120.0.4", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^12.0.2", + "dotenv-webpack": "^8.1.0", + "ts-loader": "^8.0.0", + "typescript": "^4.0.0", + "webpack": "^5.0.0", + "webpack-cli": "^4.0.0" + }, + "dependencies": { + "nanoid": "^4.0.2", + "uuid": "^8.3.2" + } +} diff --git a/pkg/extension-v3/src/background.ts b/pkg/extension-v3/src/background.ts new file mode 100644 index 000000000..0024400f2 --- /dev/null +++ b/pkg/extension-v3/src/background.ts @@ -0,0 +1,90 @@ +import { v4 as uuidv4 } from 'uuid' +import { addNoteToLibraryItem, savePageRequest } from './scripts/omnivore-api' +import { + isAddNoteInput, + isEnqueueTaskMessage, + isSavePageInput, +} from './scripts/types' +import { TaskQueue } from './task-queue' + +const browserAPI = typeof browser !== 'undefined' ? browser : chrome + +const taskQueues: Record = {} + +chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => { + console.log('message: ', message, 'sender', sender.tab?.id) + + const tabId = sender.tab?.id + if (message.action === 'savePage' && isSavePageInput(message)) { + const { result, libraryItemId } = await savePageRequest({ + url: message.url, + title: message.title, + clientRequestId: message.clientRequestId, + originalContent: message.originalContent, + }) + console.log('result: ', result, 'libraryItemId', libraryItemId) + if (tabId) { + switch (result) { + case 'success': + chrome.tabs.sendMessage(tabId, { + message: 'startToolbarDismiss', + status: 'success', + }) + if (libraryItemId) { + const taskQueue = taskQueues[message.clientRequestId] + taskQueue.setReady(tabId, libraryItemId) + } + break + case 'failure': + chrome.tabs.sendMessage(tabId, { + message: 'startToolbarDismiss', + status: 'failure', + }) + break + case 'unauthorized': + chrome.tabs.sendMessage(tabId, { + message: 'showLoggedOutToolbar', + }) + break + } + } + } else if (message.action == 'enqueueTask' && isEnqueueTaskMessage(message)) { + const taskQueue = taskQueues[message.clientRequestId] + console.log('enqueing task message: ', message) + taskQueue.enqueue({ ...message, libraryItemId: message.clientRequestId }) + } + return true +}) + +const scriptsAlreadyLoaded = async (tabId: number) => { + try { + const pingCheck = await chrome.tabs.sendMessage(tabId, { + message: 'ping', + }) + console.log('pingCheck: ', pingCheck) + return true + } catch { + return false + } +} + +browserAPI.action.onClicked.addListener(async (tab) => { + const tabId = tab.id + if (tabId) { + try { + const scriptsLoaded = await scriptsAlreadyLoaded(tabId) + if (!scriptsLoaded) { + await chrome.scripting.executeScript({ + target: { tabId }, + files: ['content.js'], + }) + } + } catch (err) { + console.log('[omnivore] error injecting content script: ', err) + } + + const clientRequestId = uuidv4() + taskQueues[clientRequestId] = new TaskQueue() + chrome.tabs.sendMessage(tabId, { message: 'savePage', clientRequestId }) + } +}) diff --git a/pkg/extension-v3/src/icons/icon-128.png b/pkg/extension-v3/src/icons/icon-128.png new file mode 100644 index 000000000..d6df58ae7 Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-128.png differ diff --git a/pkg/extension-v3/src/icons/icon-16.png b/pkg/extension-v3/src/icons/icon-16.png new file mode 100644 index 000000000..5c0b8ad30 Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-16.png differ diff --git a/pkg/extension-v3/src/icons/icon-19.png b/pkg/extension-v3/src/icons/icon-19.png new file mode 100644 index 000000000..9c5f2a8ee Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-19.png differ diff --git a/pkg/extension-v3/src/icons/icon-24.png b/pkg/extension-v3/src/icons/icon-24.png new file mode 100644 index 000000000..a0654130b Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-24.png differ diff --git a/pkg/extension-v3/src/icons/icon-256.png b/pkg/extension-v3/src/icons/icon-256.png new file mode 100644 index 000000000..a4413ba25 Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-256.png differ diff --git a/pkg/extension-v3/src/icons/icon-32.png b/pkg/extension-v3/src/icons/icon-32.png new file mode 100644 index 000000000..2270c0762 Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-32.png differ diff --git a/pkg/extension-v3/src/icons/icon-38.png b/pkg/extension-v3/src/icons/icon-38.png new file mode 100644 index 000000000..1041a3f2a Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-38.png differ diff --git a/pkg/extension-v3/src/icons/icon-48.png b/pkg/extension-v3/src/icons/icon-48.png new file mode 100644 index 000000000..dda13bd4e Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-48.png differ diff --git a/pkg/extension-v3/src/icons/icon-70.png b/pkg/extension-v3/src/icons/icon-70.png new file mode 100644 index 000000000..41337352f Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-70.png differ diff --git a/pkg/extension-v3/src/icons/icon-96.png b/pkg/extension-v3/src/icons/icon-96.png new file mode 100644 index 000000000..3af64b0be Binary files /dev/null and b/pkg/extension-v3/src/icons/icon-96.png differ diff --git a/pkg/extension-v3/src/images/highlight.svg b/pkg/extension-v3/src/images/highlight.svg new file mode 100644 index 000000000..e81aef802 --- /dev/null +++ b/pkg/extension-v3/src/images/highlight.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/pkg/extension-v3/src/images/label.svg b/pkg/extension-v3/src/images/label.svg new file mode 100644 index 000000000..69fca4c19 --- /dev/null +++ b/pkg/extension-v3/src/images/label.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pkg/extension-v3/src/images/listen-hover.svg b/pkg/extension-v3/src/images/listen-hover.svg new file mode 100644 index 000000000..0a8fe4a55 --- /dev/null +++ b/pkg/extension-v3/src/images/listen-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pkg/extension-v3/src/images/listen.svg b/pkg/extension-v3/src/images/listen.svg new file mode 100644 index 000000000..f77b5bb33 --- /dev/null +++ b/pkg/extension-v3/src/images/listen.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pkg/extension-v3/src/images/pause-w-circle.svg b/pkg/extension-v3/src/images/pause-w-circle.svg new file mode 100644 index 000000000..c8f4c1082 --- /dev/null +++ b/pkg/extension-v3/src/images/pause-w-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/extension-v3/src/images/playback-speed.svg b/pkg/extension-v3/src/images/playback-speed.svg new file mode 100644 index 000000000..41dab8b19 --- /dev/null +++ b/pkg/extension-v3/src/images/playback-speed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/extension-v3/src/images/playback-voice.svg b/pkg/extension-v3/src/images/playback-voice.svg new file mode 100644 index 000000000..c7b1670bd --- /dev/null +++ b/pkg/extension-v3/src/images/playback-voice.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/pkg/extension-v3/src/images/save.svg b/pkg/extension-v3/src/images/save.svg new file mode 100644 index 000000000..7deeb4d1f --- /dev/null +++ b/pkg/extension-v3/src/images/save.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pkg/extension-v3/src/manifest.json b/pkg/extension-v3/src/manifest.json new file mode 100644 index 000000000..fe2a13dca --- /dev/null +++ b/pkg/extension-v3/src/manifest.json @@ -0,0 +1,42 @@ +{ + "manifest_version": 3, + "name": "Omnivore - Self Hosted", + "version": "3.0", + "description": "Save content to your Omnivore library.", + "permissions": [ + "activeTab", + "scripting", + "storage" + ], + "host_permissions": [ + "" + ], + "background": { + "service_worker": "background.js", + "scripts": ["background.js"] + }, + "web_accessible_resources": [ + { + "resources": [ + "images/*.svg", + "views/toast.html" + ], + "matches": [ + "" + ] + } + ], + "options_page": "views/options.html", + "action": { + "default_icon": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } + }, + "icons": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } +} diff --git a/pkg/extension-v3/src/scripts/content/content.ts b/pkg/extension-v3/src/scripts/content/content.ts new file mode 100644 index 000000000..27a87c517 --- /dev/null +++ b/pkg/extension-v3/src/scripts/content/content.ts @@ -0,0 +1,87 @@ +import { SavePageInput, ToolbarMessage } from '../types' +import { + showLoggedOutToolbar, + showToolbar, + startToolbarDismiss, + updateToolbarStatus +} from './toolbar' +import { editLabels } from './labels' + +const collectPageContent = async (): Promise => { + const mainContent = document.documentElement.outerHTML + console.log('[omnivore] captured mainContent') + return mainContent +} + +const handleToolbarMessage = async ( + request: any, + sender: chrome.runtime.MessageSender, + sendResponse: (response?: any) => void +) => { + console.log('[omnivore] content script message:', request) + + switch (request.message) { + case 'showLoggedOutToolbar': + showLoggedOutToolbar() + sendResponse({ success: true }) + break + case 'updateToolbar': + updateToolbarStatus(request.status, request.task) + + sendResponse({ success: true }) + break + case 'startToolbarDismiss': + startToolbarDismiss(request as ToolbarMessage) + sendResponse({ success: true }) + return + case 'updateLabelCache': + editLabels() + break + default: + sendResponse({ success: false }) + return + } +} + +const setupToolbar = async (clientRequestId: string) => { + // toolbar message listener + if (!chrome.runtime.onMessage.hasListener(handleToolbarMessage)) { + chrome.runtime.onMessage.addListener(handleToolbarMessage) + } + + await showToolbar(clientRequestId) +} + +const savePage = async (clientRequestId: string) => { + console.log('[omnivore] v3 extension triggered: ', clientRequestId) + + await setupToolbar(clientRequestId) + + const content = await collectPageContent() + console.log('[omnivore] collected page content: ', content) + + try { + const page: SavePageInput = { + clientRequestId, + title: document.title, + url: document.location.href, + originalContent: content, + } + await chrome.runtime.sendMessage({ + action: 'savePage', + ...page, + }) + } catch (err) { + console.log('error sending content: ', err) + } +} + +// toolbar message listener +chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { + switch (request.message) { + case 'savePage': + await savePage(request.clientRequestId) + sendResponse({ success: true }) + return + } +}) diff --git a/pkg/extension-v3/src/scripts/content/labels.ts b/pkg/extension-v3/src/scripts/content/labels.ts new file mode 100644 index 000000000..9dc9239f1 --- /dev/null +++ b/pkg/extension-v3/src/scripts/content/labels.ts @@ -0,0 +1,177 @@ +import { getStorageItem } from '../utils' +import { Label } from '../types' +import { cancelAutoDismiss, getClientRequestId, toggleRow, updateStatusBox, updateToolbarStatus } from './toolbar' + +export async function editLabels() { + cancelAutoDismiss() + + const currentToastEl = document.querySelector('#omnivore-extension-root') + + if (!currentToastEl || !currentToastEl.shadowRoot) { + console.log('no statusBox to update') + return + } + + let labels = await getStorageItem('labels').then((value: any) => { + if (value && value.length > 0) { + return value as Label[] + } + + return undefined + }) + + toggleRow('#omnivore-edit-labels-row') + currentToastEl.shadowRoot + .querySelector('#omnivore-edit-label-input') + ?.focus() + + const list = currentToastEl.shadowRoot.querySelector( + '#omnivore-edit-labels-list' + ) + + // Add a box for waiting for the labels. + if (!labels) { + console.error('No labels found, trying to update the cache.') + chrome.runtime.sendMessage({ + action: 'enqueueTask', + task: 'updateLabelCache', + clientRequestId: getClientRequestId(), + }) + + return; + } + + + // currentToastEl.shadowRoot.querySelector( + // '#omnivore-edit-label-input' + // ).onkeydown = labelEditorKeyDownHandler + // + // currentToastEl.shadowRoot.querySelector( + // '#omnivore-edit-label-editor' + // ).onclick = labelEditorClickHandler + // + // currentToastEl.shadowRoot + // .querySelector('#omnivore-edit-label-input') + // .addEventListener('input', (event) => { + // updateLabels(event.target.value) + // }) + + if (list) { + list.innerHTML = '' + labels + .sort((a, b) => + a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) + ) + .forEach(function (label, idx) { + const rowHtml = createLabelRow(label) + list.appendChild(rowHtml) + }) + } +} + +function createLabelRow(label: Label) { + const element = document.createElement('button') + const dot = document.createElement('span') + // @ts-ignore + dot.style = 'width:10px;height:10px;border-radius:1000px;' + dot.style.setProperty('background-color', label.color) + const title = document.createElement('span') + // @ts-ignore + title.style = 'margin-left: 10px;pointer-events: none;' + title.innerText = label.name + + const check = document.createElement('span') + // @ts-ignore + check.style = 'margin-left: auto;pointer-events: none;' + check.className = 'checkbox' + check.innerHTML = ` + + + + ` + + element.appendChild(dot) + element.appendChild(title) + element.appendChild(check) + + element.onclick = labelClick + // element.onkeydown = labelEditorKeyDownHandler + element.setAttribute('data-label-id', label.id) + element.setAttribute( + 'data-label-selected', + label['selected'] ? 'on' : 'off' + ) + element.setAttribute('tabIndex', '-1') + + return element +} + + +function labelClick(event: any) { + event.preventDefault() + const labelId = event.target?.getAttribute('data-label-id') + + if (labelId) { + toggleLabel(event, labelId) + } +} + +async function toggleLabel(event: any, labelId: string) { + const labelSelected = event.target?.getAttribute('data-label-selected') + + if (!labelId || !labelSelected) { + return + } + + const toggledValue = labelSelected == 'on' ? false : true + event.target?.setAttribute( + 'data-label-selected', + toggledValue ? 'on' : 'off' + ) + + + let labels = await getStorageItem('labels').then((value: any) => { + if (value && value.length > 0) { + return value as Label[] + } + + return undefined + }) + + if (!labels) { + throw Error("No labels selected") + } + + const label = labels.find((l) => l.id === labelId) + if (label) { + syncLabelChanges() + } + +} + +function syncLabelChanges() { + updateStatusBox( + '#omnivore-edit-labels-status', + 'waiting', + 'Updating Labels...', + undefined + ) + + const currentToastEl = document.querySelector('#omnivore-extension-root') + const labels = currentToastEl?.shadowRoot?.querySelector("#omnivore-edit-labels-list") + + if (labels) { + const setLabels = [...labels.children] + .filter((l) => l.getAttribute('data-label-selected') === 'on') + .map((l) => l.getAttribute('data-label-id')!) + + chrome.runtime.sendMessage({ + action: 'enqueueTask', + task: 'setLabels', + clientRequestId: getClientRequestId(), + labels: setLabels, + }) + } + + +} diff --git a/pkg/extension-v3/src/scripts/content/toolbar.ts b/pkg/extension-v3/src/scripts/content/toolbar.ts new file mode 100644 index 000000000..2c160877f --- /dev/null +++ b/pkg/extension-v3/src/scripts/content/toolbar.ts @@ -0,0 +1,593 @@ +import { ToolbarMessage, ToolbarStatus } from '../types' +import { getStorageItem, setStorage } from '../utils' +import { editLabels } from './labels' + +const systemIcons: { [key: string]: string } = { + waiting: '
', + success: ` + + + + `, + failure: ` + + + + + `, +} + +const createToastContainer = async (clientRequestId: string) => { + console.log('===== CREATING TOAST CONTAINER ===== ') + const file = await fetch(chrome.runtime.getURL('views/toast.html')) + const html = await file.text() + + const root = document.createElement('div') + root.tabIndex = 0 + root.id = 'omnivore-extension-root' + root.attachShadow({ mode: 'open' }) + root.style.opacity = '1.0' + + if (root.shadowRoot) { + root.shadowRoot.innerHTML = `` + } else { + alert('Error opening Omnivore user interface.') + return + } + + const toastEl = document.createElement('div') + toastEl.id = '#omnivore-toast' + toastEl.innerHTML = html + toastEl.tabIndex = 0 + root.shadowRoot.appendChild(toastEl) + + document.body.appendChild(root) + connectButtons(root) + // connectKeyboard(root) + + updateToolbarStatus('waiting') + + return root +} + +export const showToolbar = async (clientRequestId: string) => { + let currentToastEl = + document.querySelector('#omnivore-extension-root') ?? undefined + + const bodyEl = document.body + if (!bodyEl) return + + console.log('existing currentToastEl: ', currentToastEl) + if (!currentToastEl) { + currentToastEl = await createToastContainer(clientRequestId) + } + + const disableAutoDismiss = await getStorageItem('disableAutoDismiss') + if (disableAutoDismiss) { + currentToastEl?.setAttribute('data-disable-auto-dismiss', 'true') + } + currentToastEl?.setAttribute( + 'data-omnivore-client-request-id', + clientRequestId + ) + ;(currentToastEl as HTMLDivElement)?.focus({ + preventScroll: true, + }) + + updateToolbarStatus('waiting') +} + +const autoDismissTime = async () => { + const strVal = (await getStorageItem('autoDismissTime')) ?? '2500' + return !Number.isNaN(Number(strVal)) ? Number(strVal) : 2500 +} + +export const updateToolbarStatus = async ( + status: ToolbarStatus, + task: string | undefined = undefined +) => { + const currentToastEl = document.querySelector('#omnivore-extension-root') + const statusBox = currentToastEl?.shadowRoot?.querySelector( + '.omnivore-toast-statusBox' + ) + console.log('updating', status, statusBox) + + if (statusBox) { + switch (status) { + case 'success': + statusBox.innerHTML = systemIcons.success + break + case 'failure': + statusBox.innerHTML = systemIcons.failure + break + case 'waiting': + statusBox.innerHTML = systemIcons.waiting + break + } + } + + // Set a task specific message + if (task) { + if (task == 'addNote' && status == 'failure') { + updateStatusBox( + '#omnivore-add-note-status', + 'failure', + 'Error adding note...', + undefined + ) + } + if (task == 'addNote' && status == 'success') { + updateStatusBox( + '#omnivore-add-note-status', + 'success', + 'Note updated.', + 2500 + ) + setTimeout(() => { + toggleRow('#omnivore-add-note-status') + }, 3000) + } + if (task == 'setLabels' && status == 'success') { + updateStatusBox( + '#omnivore-edit-labels-status', + 'success', + 'Labels Updated', + 2500 + ) + } + if (task == 'setLabels' && status == 'failure') { + updateStatusBox( + '#omnivore-edit-labels-status', + 'failure', + 'Error Updating Labels...', + 2500 + ) + } + if (task == 'editTitle' && status == 'failure') { + updateStatusBox( + '#omnivore-add-note-status', + 'failure', + 'Error updating title...', + undefined + ) + } + if (task == 'editTitle' && status == 'success') { + updateStatusBox( + 'omnivore-edit-title-status', + 'success', + 'Title updated.', + 2500 + ) + setTimeout(() => { + toggleRow('#omnivore-edit-title-status') + }, 3000) + } + if (task == 'archive') { + updateStatusBox( + '#omnivore-extra-status', + status, + status == 'success' ? 'Success' : 'Error', + status == 'success' ? 2500 : undefined + ) + if (status == 'success') { + closeToolbarLater() + } + } + } +} + +export const cancelAutoDismiss = () => { + const currentToastEl = document.querySelector('#omnivore-extension-root') + if (currentToastEl) { + currentToastEl.setAttribute('data-disable-auto-dismiss', 'true') + } +} + +// If the user has not disabled auto dismiss on the toolbar this +// will remove it. If the user interacts with the toolbar, this +// dismiss will also be ignored. +export const startToolbarDismiss = async (message: ToolbarMessage) => { + if (message.status) { + updateToolbarStatus(message.status) + } + + const dimissTime = await autoDismissTime() + + setTimeout(() => { + const currentToastEl = document.querySelector('#omnivore-extension-root') + if ( + currentToastEl && + !currentToastEl.getAttribute('data-disable-auto-dismiss') + ) { + ;(currentToastEl as HTMLElement).style.transition = 'opacity 3.5s ease;' + ;(currentToastEl as HTMLElement).style.opacity = '0' + setTimeout(() => { + const currentToastEl = document.querySelector( + '#omnivore-extension-root' + ) + if ( + currentToastEl && + !currentToastEl.getAttribute('data-disable-auto-dismiss') + ) { + currentToastEl.remove() + } + }, 500) + } + }, dimissTime) +} + +const connectButtons = (root: HTMLElement) => { + const btns = [ + { id: '#omnivore-toast-add-note-btn', func: addNote }, + { id: '#omnivore-toast-edit-title-btn', func: editTitle }, + { id: '#omnivore-toast-edit-labels-btn', func: editLabels }, + { id: '#omnivore-toast-read-now-btn', func: readNow }, + { id: '#omnivore-open-menu-btn', func: openMenu }, + { id: '#omnivore-toast-close-btn', func: closeToolbar }, + { id: '#omnivore-toast-login-btn', func: login }, + { id: '#omnivore-toast-archive-btn', func: archive }, + { id: '#omnivore-toast-delete-btn', func: deleteItem }, + ] + + for (const btnInfo of btns) { + const btn = root.shadowRoot?.querySelector(btnInfo.id) + if (btn) { + console.log(btnInfo.id) + btn.addEventListener('click', btnInfo.func) + } + } + + var x = window.matchMedia('(max-width: 500px)') + if (x.matches) { + const labels = root.shadowRoot?.querySelectorAll( + '.omnivore-top-button-label' + ) + labels?.forEach((label) => { + label.style.display = 'none' + }) + const container = root.shadowRoot?.querySelector( + '#omnivore-toast-container' + ) + if (container) { + container.style.width = '280px' + container.style.top = 'unset' + container.style.bottom = '20px' + } + } +} + +function editTitle() { + cancelAutoDismiss() + toggleRow('#omnivore-edit-title-row') + let currentToastEl = + document.querySelector('#omnivore-extension-root') ?? undefined + + if (!currentToastEl) { + console.log('no statusBox to update') + return + } + + const titleArea = currentToastEl?.shadowRoot?.querySelector( + '#omnivore-edit-title-textarea' + ) + + if (titleArea) { + titleArea.focus() + + titleArea.onkeydown = (e) => { + e.cancelBubble = true + } + } + + const formElement = currentToastEl?.shadowRoot?.querySelector( + '#omnivore-edit-title-form' + ); + + if (!formElement) { + console.log('no form to update') + return + } + + formElement.onsubmit = (event) => { + updateStatusBox( + '#omnivore-edit-title-status', + 'waiting', + 'Updating title...', + undefined + ) + + const title = titleArea?.value ?? '' + + chrome.runtime.sendMessage({ + action: 'enqueueTask', + task: 'editTitle', + clientRequestId: getClientRequestId(), + title + }) + event.preventDefault() + } +} + +export const showLoggedOutToolbar = () => { + cancelAutoDismiss() + updateToolbarStatus('failure') + toggleRow('#omnivore-logged-out-row') + disableAllButtons() + updateStatusBox( + '#omnivore-logged-out-status', + undefined, + `You are not logged in.`, + undefined + ) +} + +export const updateStatusBox = ( + boxId: string, + state: ToolbarStatus | undefined, + message: string, + dismissAfter: number | undefined +) => { + const currentToastEl = document.querySelector('#omnivore-extension-root') + const statusBox = currentToastEl?.shadowRoot?.querySelector(boxId) + const image = (() => { + switch (state) { + case 'waiting': + return systemIcons.animatedLoader + case 'success': + return systemIcons.success + case 'failure': + return systemIcons.failure + default: + return undefined + } + })() + if (image && statusBox) { + const color = state == 'failure' ? 'red' : 'unset' + statusBox.innerHTML = `${image}${message}` + } else if (statusBox) { + statusBox.innerHTML = message + } + if (dismissAfter && statusBox) { + setTimeout(() => { + statusBox.innerHTML = '' + }, dismissAfter) + } +} + +const disableAllButtons = () => { + const actionButtons = [ + '#omnivore-toast-edit-title-btn', + '#omnivore-toast-edit-labels-btn', + '#omnivore-toast-read-now-btn', + '#omnivore-toast-add-note-btn', + '#omnivore-open-menu-btn', + ] + let currentToastEl = document.querySelector('#omnivore-extension-root') + actionButtons.forEach((btnId) => { + const btn = + currentToastEl?.shadowRoot?.querySelector(btnId) + if (btn) { + btn.disabled = true + } + }) +} + +export const toggleRow = (rowId: string) => { + let currentToastEl = document.querySelector('#omnivore-extension-root') + + if (!currentToastEl) { + console.log('toggleRow: no row to toggle') + // its possible this was called after closing the extension + // so just return + return + } + + const container = currentToastEl?.shadowRoot?.querySelector(rowId) + const initialState = container?.getAttribute('data-state') + const rows = currentToastEl?.shadowRoot?.querySelectorAll( + '.omnivore-toast-func-row' + ) + + rows?.forEach((r) => { + r.setAttribute('data-state', 'closed') + }) + + if (container && initialState) { + const newState = initialState === 'open' ? 'closed' : 'open' + container.setAttribute('data-state', newState) + } +} + +const noteCacheKey = () => { + return `cached-note-${document.location.href}` +} + +export const getClientRequestId = () => { + const currentToastEl = document.querySelector('#omnivore-extension-root') + const clientRequestId = currentToastEl?.getAttribute( + 'data-omnivore-client-request-id' + ) + return clientRequestId +} + +// +// Button functions +// + +const login = () => { + window.open(new URL(`/login`, process.env.OMNIVORE_URL), '_blank') + closeToolbarLater() +} + +const openMenu = () => { + cancelAutoDismiss() + toggleRow('#omnivore-extra-buttons-row') +} + +const addNote = async () => { + console.log('[omnivore] adding note') + cancelAutoDismiss() + + const currentToastEl = document.querySelector('#omnivore-extension-root') + const clientRequestId = currentToastEl?.getAttribute( + 'data-omnivore-client-request-id' + ) + console.log('client request id: ', clientRequestId) + if (!clientRequestId) { + // TODO: move into an error state + updateStatusBox( + '#omnivore-add-note-status', + 'failure', + 'Error adding note...', + undefined + ) + return + } + + const cachedNoteKey = noteCacheKey() + + cancelAutoDismiss() + toggleRow('#omnivore-add-note-row') + + const noteArea = + currentToastEl?.shadowRoot?.querySelector( + '#omnivore-add-note-textarea' + ) + + if (noteArea) { + if (cachedNoteKey) { + const existingNote = + ((await getStorageItem(cachedNoteKey)) as string) ?? '' + noteArea.value = existingNote + } + + if (noteArea.value) { + noteArea.select() + } else { + noteArea.focus() + } + + noteArea.addEventListener('input', async (event) => { + const note: Record = {} + note[cachedNoteKey] = (event.target as HTMLTextAreaElement).value + await setStorage(note) + }) + + noteArea.onkeydown = async (e: KeyboardEvent) => { + // e.preventDefault() + e.stopPropagation() + // Handle the enter key + console.log('handling the enter key: ', e.keyCode) + if (e.keyCode == 13 && (e.metaKey || e.ctrlKey)) { + updateStatusBox( + '#omnivore-add-note-status', + 'waiting', + 'Adding note...', + undefined + ) + + await saveNote(clientRequestId, noteArea.value) + } + } + } + + const form = currentToastEl?.shadowRoot?.querySelector( + '#omnivore-add-note-form' + ) + + if (form) { + form.onsubmit = async (event) => { + console.log('handling form submit') + updateStatusBox( + '#omnivore-add-note-status', + 'waiting', + 'Adding note...', + undefined + ) + + if (noteArea) { + await saveNote(clientRequestId, noteArea.value) + } + + event.preventDefault() + event.stopPropagation() + } + } +} + +const archive = async (event: Event) => { + const clientRequestId = getClientRequestId() + try { + await chrome.runtime.sendMessage({ + action: 'enqueueTask', + task: 'archive', + clientRequestId, + }) + } catch (err) { + console.log('error archiving item') + } + event.preventDefault() +} + +const deleteItem = async (event: Event) => { + const clientRequestId = getClientRequestId() + try { + await chrome.runtime.sendMessage({ + action: 'enqueueTask', + task: 'delete', + clientRequestId, + }) + } catch (err) { + console.log('error archiving item') + } + event.preventDefault() +} + +const readNow = async () => { + cancelAutoDismiss() + + let currentToastEl = document.querySelector('#omnivore-extension-root') + const container = currentToastEl?.shadowRoot?.querySelector( + '#omnivore-toast-container' + ) + container?.setAttribute('data-state', 'open') + + window.open( + new URL( + `/article?url=${encodeURI(document.location.href)}`, + (await getStorageItem("omnivoreUrl")) as string, + ), + '_blank' + ) + + closeToolbarLater() +} + +const closeToolbarLater = () => { + setTimeout(() => { + closeToolbar() + }, 1000) +} +const closeToolbar = () => { + const currentToastEl = document.querySelector('#omnivore-extension-root') + if (currentToastEl) { + currentToastEl.remove() + } +} + +// +// API interactions +// + +const saveNote = async (clientRequestId: string, note: string) => { + try { + await chrome.runtime.sendMessage({ + action: 'enqueueTask', + task: 'addNote', + note, + clientRequestId, + }) + } catch (err) { + console.log('error adding note: ', err) + } +} diff --git a/pkg/extension-v3/src/scripts/omnivore-api.ts b/pkg/extension-v3/src/scripts/omnivore-api.ts new file mode 100644 index 000000000..1bf6d9892 --- /dev/null +++ b/pkg/extension-v3/src/scripts/omnivore-api.ts @@ -0,0 +1,528 @@ +import { ArticleData, Label, SavePageData, SetLinkArchivedData } from './types' +import { getStorageItem, setStorage } from './utils' +import { v4 as uuidv4 } from 'uuid' +import { nanoid } from 'nanoid' + +export type ApiResult = 'success' | 'failure' | 'unauthorized' + +const gqlRequest = async (query: string) => { + const apiKey = (await getStorageItem('omnivoreApiKey')) as string | undefined + let headers = { + Accept: 'application/json', + 'Content-Type': 'application/json', + } as Record + if (apiKey) { + headers['Authorization'] = apiKey + } + + try { + const apiUrl= (await getStorageItem('omnivoreApiUrl')) as string | undefined + ?? process.env.OMNIVORE_GRAPHQL_URL + ?? '' + + console.log(apiUrl) + const response = await fetch(`${apiUrl}/api/graphql`, { + method: 'POST', + redirect: 'follow', + credentials: 'include', + mode: 'cors', + headers, + body: query, + }) + const json = await response.json() + if (!('data' in json) || !json.data) { + throw new Error('No response data') + } + return json.data + } catch (err) { + console.log('[omnivore] error making api request: ', query) + } +} + +export async function savePageRequest(input: { + url: string + title: string + clientRequestId: string + originalContent: string +}) { + const mutation = JSON.stringify({ + query: `mutation SavePage ($input: SavePageInput!) { + savePage(input:$input){ + ... on SaveSuccess { + url + clientRequestId + } + ... on SaveError { + errorCodes + } + } + }`, + variables: { + input: { + source: 'extension', + ...input, + }, + }, + }) + + const data = (await gqlRequest(mutation)) as SavePageData + if (data.savePage?.errorCodes?.length) { + console.log('[omnivore] api: error saving page:', data) + if (data.savePage.errorCodes.indexOf('UNAUTHORIZED') > -1) { + console.log('[omnivore] api is not authorized') + return { result: 'unauthorized' } + } + return { result: 'failure' } + } + return { result: 'success', libraryItemId: data.savePage?.clientRequestId } +} + +export async function addNoteToLibraryItem(input: { + libraryItemId: string + note: string +}) { + const query = JSON.stringify({ + query: `query GetArticle( + $username: String! + $slug: String! + $includeFriendsHighlights: Boolean + ) { + article(username: $username, slug: $slug) { + ... on ArticleSuccess { + article { + highlights(input: { includeFriends: $includeFriendsHighlights }) { + ...HighlightFields + } + } + } + ... on ArticleError { + errorCodes + } + } + } + fragment HighlightFields on Highlight { + id + type + annotation + } + `, + variables: { + username: 'me', + slug: input.libraryItemId, + includeFriendsHighlights: false, + }, + }) + + const data = (await gqlRequest(query)) as ArticleData + if (data.article?.errorCodes?.length) { + console.log('[omnivore] api: error getting article:', data) + if (data.article.errorCodes.indexOf('UNAUTHORIZED') > -1) { + console.log('[omnivore] api is not authorized') + return 'unauthorized' + } + return 'failure' + } + + console.log('DATA.ARTICLE: ', data.article) + const existingNote = data.article?.highlights?.find((h) => h.type == 'NOTE') + + if (existingNote) { + const mutation = JSON.stringify({ + query: ` + mutation UpdateHighlight($input: UpdateHighlightInput!) { + updateHighlight(input: $input) { + ... on UpdateHighlightSuccess { + highlight { + id + } + } + ... on UpdateHighlightError { + errorCodes + } + } + } + `, + variables: { + input: { + highlightId: existingNote.id, + annotation: existingNote.annotation + ? existingNote.annotation + '\n\n' + input.note + : input.note, + }, + }, + }) + const result = await gqlRequest(mutation) + if ( + !result.updateHighlight || + result.updateHighlight['errorCodes'] || + !result.updateHighlight.highlight + ) { + console.log('GQL Error updating note:', result) + return + } + return result.updateHighlight.highlight.id + } else { + const noteId = uuidv4() + const shortId = nanoid(8) + const mutation = JSON.stringify({ + query: ` + mutation CreateHighlight($input: CreateHighlightInput!) { + createHighlight(input: $input) { + ... on CreateHighlightSuccess { + highlight { + id + } + } + ... on CreateHighlightError { + errorCodes + } + } + } + `, + variables: { + input: { + id: noteId, + shortId: shortId, + type: 'NOTE', + articleId: input.libraryItemId, + annotation: input.note, + }, + }, + }) + const result = await gqlRequest(mutation) + if ( + !result.createHighlight || + result.createHighlight['errorCodes'] || + !result.createHighlight.highlight + ) { + console.log('GQL Error setting note:', result) + return 'failure' + } + return 'success' + } +} + +export async function updateLabelsCache(): Promise { + const query = JSON.stringify({ + query: `query GetLabels { + labels { + ... on LabelsSuccess { + labels { + ...LabelFields + } + } + ... on LabelsError { + errorCodes + } + } + } + fragment LabelFields on Label { + id + name + color + description + createdAt + } + `, + }) + + const data = await gqlRequest(query) + if (!data.labels || data.labels['errorCodes'] || !data.labels['labels']) { + console.log('GQL Error updating label cache response:', data, data) + console.log(!data.labels, data.labels['errorCodes'], !data.labels['labels']) + return [] + } + + await setStorage({ + labels: data.labels.labels, + labelsLastUpdated: new Date().toISOString(), + }) + + return data.labels.labels +} + +export async function updatePageTitle(pageId: string, title: string) { + const mutation = JSON.stringify({ + query: `mutation UpdatePage($input: UpdatePageInput!) { + updatePage(input: $input) { + ... on UpdatePageSuccess { + updatedPage { + id + } + } + ... on UpdatePageError { + errorCodes + } + } + } + `, + variables: { + input: { + pageId, + title, + }, + }, + }) + + const data = await gqlRequest(mutation) + console.log(data); + if ( + !data.updatePage || + data.updatePage['errorCodes'] || + !data.updatePage['updatedPage'] + ) { + console.log('GQL Error updating page:', data) + throw new Error('Error updating title.') + } + return 'success' +} + +export async function setLabels(pageId: string, labels: string[]) { + const mutation = JSON.stringify({ + query: `mutation SetLabels($input: SetLabelsInput!) { + setLabels(input: $input) { + ... on SetLabelsSuccess { + labels { + id + name + color + } + } + ... on SetLabelsError { + errorCodes + } + } + } + `, + variables: { + input: { + pageId, + labelIds: labels, + }, + }, + }) + + const data = await gqlRequest(mutation) + if ( + !data.setLabels || + data.setLabels['errorCodes'] || + !data.setLabels['labels'] + ) { + console.log('GQL Error setting labels:', data) + throw new Error('Error setting labels.') + } + + return data.setLabels.labels +} + +// async function appendLabelsToCache(labels) { +// const cachedLabels = await getStorageItem('labels') +// if (cachedLabels) { +// labels.forEach((l) => { +// const existing = cachedLabels.find((cached) => cached.name === l.name) +// if (!existing) { +// cachedLabels.unshift(l) +// } +// }) + +// await setStorage({ +// labels: cachedLabels, +// labelsLastUpdated: new Date().toISOString(), +// }) +// } else { +// await setStorage({ +// labels: labels, +// labelsLastUpdated: new Date().toISOString(), +// }) +// } +// } + +// async function addNote(apiUrl, pageId, noteId, shortId, note) { +// const query = JSON.stringify({ +// query: `query GetArticle( +// $username: String! +// $slug: String! +// $includeFriendsHighlights: Boolean +// ) { +// article(username: $username, slug: $slug) { +// ... on ArticleSuccess { +// article { +// highlights(input: { includeFriends: $includeFriendsHighlights }) { +// ...HighlightFields +// } +// } +// } +// ... on ArticleError { +// errorCodes +// } +// } +// } +// fragment HighlightFields on Highlight { +// id +// type +// annotation +// } +// `, +// variables: { +// username: 'me', +// slug: pageId, +// includeFriendsHighlights: false, +// }, +// }) + +// const data = await gqlRequest(apiUrl, query) +// if (!data.article || data.article['errorCodes'] || !data.article['article']) { +// console.log('GQL Error getting existing highlights:', data) +// return +// } + +// const existingNote = data.article.article.highlights.find( +// (h) => h.type == 'NOTE' +// ) + +// if (existingNote) { +// const mutation = JSON.stringify({ +// query: ` +// mutation UpdateHighlight($input: UpdateHighlightInput!) { +// updateHighlight(input: $input) { +// ... on UpdateHighlightSuccess { +// highlight { +// id +// } +// } +// ... on UpdateHighlightError { +// errorCodes +// } +// } +// } +// `, +// variables: { +// input: { +// highlightId: existingNote.id, +// annotation: existingNote.annotation +// ? existingNote.annotation + '\n\n' + note +// : note, +// }, +// }, +// }) +// const result = await gqlRequest(apiUrl, mutation) +// if ( +// !result.updateHighlight || +// result.updateHighlight['errorCodes'] || +// !result.updateHighlight.highlight +// ) { +// console.log('GQL Error updating note:', result) +// return +// } +// return result.updateHighlight.highlight.id +// } else { +// const mutation = JSON.stringify({ +// query: ` +// mutation CreateHighlight($input: CreateHighlightInput!) { +// createHighlight(input: $input) { +// ... on CreateHighlightSuccess { +// highlight { +// id +// } +// } +// ... on CreateHighlightError { +// errorCodes +// } +// } +// } +// `, +// variables: { +// input: { +// id: noteId, +// shortId: shortId, +// type: 'NOTE', +// articleId: pageId, +// annotation: note, +// }, +// }, +// }) +// const result = await gqlRequest(apiUrl, mutation) +// if ( +// !result.createHighlight || +// result.createHighlight['errorCodes'] || +// !result.createHighlight.highlight +// ) { +// console.log('GQL Error setting note:', result) +// return +// } +// return result.createHighlight.highlight.id +// } +// } + +export const archiveLibraryItem = async ( + libraryItemId: string +): Promise => { + const mutation = JSON.stringify({ + query: `mutation SetLinkArchived($input: ArchiveLinkInput!) { + setLinkArchived(input: $input) { + ... on ArchiveLinkSuccess { + linkId + message + } + ... on ArchiveLinkError { + message + errorCodes + } + } + } + `, + variables: { + input: { + linkId: libraryItemId, + archived: true, + }, + }, + }) + + const data = (await gqlRequest(mutation)) as SetLinkArchivedData + if (data.setLinkArchived?.errorCodes?.length) { + console.log('[omnivore] api: error getting article:', data) + if (data.setLinkArchived.errorCodes.indexOf('UNAUTHORIZED') > -1) { + console.log('[omnivore] api is not authorized') + return 'unauthorized' + } + return 'failure' + } + + return 'success' +} + +export async function deleteItem(pageId: string) { + const mutation = JSON.stringify({ + query: `mutation SetBookmarkArticle($input: SetBookmarkArticleInput!) { + setBookmarkArticle(input: $input) { + ... on SetBookmarkArticleSuccess { + bookmarkedArticle { + id + } + } + ... on SetBookmarkArticleError { + errorCodes + } + } + } + `, + variables: { + input: { + articleID: pageId, + bookmark: false, + }, + }, + }) + + const data = await gqlRequest(mutation) + if ( + !data.setBookmarkArticle || + data.setBookmarkArticle['errorCodes'] || + !data.setBookmarkArticle.bookmarkedArticle + ) { + console.log('GQL Error deleting:', data) + throw new Error('Error deleting.') + } + return 'success' +} diff --git a/pkg/extension-v3/src/scripts/types.ts b/pkg/extension-v3/src/scripts/types.ts new file mode 100644 index 000000000..cf0c3b95f --- /dev/null +++ b/pkg/extension-v3/src/scripts/types.ts @@ -0,0 +1,112 @@ +export type ToolbarStatus = 'waiting' | 'success' | 'failure' + +export interface ToolbarMessage { + status?: ToolbarStatus +} + +export interface SavePageData { + savePage?: SavePageResult +} + +export interface SavePageResult { + url?: string + clientRequestId?: string + errorCodes?: string[] +} + +export interface Highlight { + id: string + type: string + annotation: string +} + +export interface ArticleResult { + highlights?: Highlight[] + errorCodes?: string[] +} +export interface ArticleData { + article?: ArticleResult +} + +export interface SetLinkArchivedResult { + linkId?: string + message?: string + errorCodes?: string[] +} + +export interface SetLinkArchivedData { + setLinkArchived?: SetLinkArchivedResult +} + +export interface SavePageInput { + url: string + title: string + clientRequestId: string + originalContent: string +} + +export interface AddNoteInput { + clientRequestId: string + note: string +} + +export interface TaskInput { + clientRequestId: string + libraryItemId?: string | undefined + task: 'addNote' | 'archive' | 'editTitle' | 'delete' | 'updateLabelCache' | 'setLabels' + title?: string | undefined + note?: string | undefined + labels?: string[] | undefined +} + +export interface Label { + id: string + name: string + color: string + selected: 'on' | 'off' +} + +export function isSavePageResult(obj: any): obj is SavePageResult { + return ( + typeof obj === 'object' && + (obj.url === undefined || typeof obj.url === 'string') && + (obj.clientRequestId === undefined || + typeof obj.clientRequestId === 'string') && + (obj.errorCodes === undefined || + (Array.isArray(obj.errorCodes) && + obj.errorCodes.every((code: any) => typeof code === 'string'))) + ) +} + +export function isSavePageData(obj: any): obj is SavePageData { + return ( + typeof obj === 'object' && + (obj.savePage === undefined || isSavePageResult(obj.savePage)) + ) +} + +export function isSavePageInput(obj: any): obj is SavePageInput { + return ( + typeof obj === 'object' && + typeof obj.url === 'string' && + typeof obj.title === 'string' && + typeof obj.clientRequestId === 'string' && + typeof obj.originalContent === 'string' + ) +} + +export function isAddNoteInput(obj: any): obj is AddNoteInput { + return ( + typeof obj === 'object' && + typeof obj.note === 'string' && + typeof obj.clientRequestId === 'string' + ) +} + +export function isEnqueueTaskMessage(obj: any): obj is TaskInput { + return ( + typeof obj === 'object' && + typeof obj.task === 'string' && + typeof obj.clientRequestId === 'string' + ) +} diff --git a/pkg/extension-v3/src/scripts/utils.ts b/pkg/extension-v3/src/scripts/utils.ts new file mode 100644 index 000000000..08b0e3f59 --- /dev/null +++ b/pkg/extension-v3/src/scripts/utils.ts @@ -0,0 +1,18 @@ +export const getStorageItem = async (singleKey: string) => { + return new Promise((resolve) => { + chrome.storage.local.get(singleKey, (result) => { + const finalResult = (result && result[singleKey]) || null + resolve(finalResult) + }) + }) +} + +export const setStorage = (itemsToSet: Record) => { + return chrome.storage.local.set(itemsToSet) +} + +// function removeStorage(itemsToRemove) { +// return new Promise((resolve) => { +// browserApi.storage.local.remove(itemsToRemove, resolve) +// }) +// } diff --git a/pkg/extension-v3/src/task-queue.ts b/pkg/extension-v3/src/task-queue.ts new file mode 100644 index 000000000..dcab44b80 --- /dev/null +++ b/pkg/extension-v3/src/task-queue.ts @@ -0,0 +1,126 @@ +import { + addNoteToLibraryItem, + archiveLibraryItem, + deleteItem, setLabels, updateLabelsCache, + updatePageTitle +} from './scripts/omnivore-api' +import { TaskInput } from './scripts/types' + +export class TaskQueue { + private queue: Array = [] + private isRunning: boolean = false + private isReady: boolean = false + + private tabId: number | undefined + private libraryItemId: string | undefined + + constructor() {} + + enqueue(task: TaskInput): void { + this.queue.push(task) + + // Only run the next task if the queue is ready + if (this.isReady) { + this.runNext() + } + } + + private async runNext(): Promise { + if (this.isRunning || this.queue.length === 0 || !this.isReady) return + + this.isRunning = true + const task = this.queue.shift() + + if (task) { + try { + await this.executeTask(task) + } catch (err) { + console.error('Task failed:', err) + } finally { + this.isRunning = false + if (this.isReady) { + this.runNext() + } + } + } + } + + private executeTask = async (task: TaskInput) => { + console.log('executing task: ', task) + if (!this.libraryItemId) { + throw Error('Attempting to execute queue that is not ready.') + } + + try { + let success = false + switch (task.task) { + case 'archive': { + await archiveLibraryItem(this.libraryItemId) + success = true + break + } + case 'delete': { + await deleteItem(this.libraryItemId) + success = true + break + } + case 'addNote': { + await addNoteToLibraryItem({ + note: task.note || '', + libraryItemId: this.libraryItemId, + }) + success = true + break + } + case 'setLabels': + await setLabels(this.libraryItemId, task.labels ?? []) + success = true; + break + case 'editTitle': { + if (!task.title || !this.libraryItemId) { + throw new Error("Title not set, or library item not yet saved.") + } + + await updatePageTitle(this.libraryItemId, task.title) + success = true + break; + } + case 'updateLabelCache': { + await updateLabelsCache() + if (this.tabId) { + chrome.tabs.sendMessage(this.tabId, { + message: 'updateLabelCache', + status: 'success', + task: task.task, + }) + } + } + } + if (success && this.tabId) { + chrome.tabs.sendMessage(this.tabId, { + message: 'updateToolbar', + status: 'success', + task: task.task, + }) + } + } catch (err) { + console.log('[omnivore] task queue error: ', err) + if (this.tabId) { + console.log('sending error message') + chrome.tabs.sendMessage(this.tabId, { + message: 'updateToolbar', + status: 'failure', + task: task.task, + }) + } + } + } + + setReady(tabId: number, libraryItemId: string): void { + console.log('setting ready') + this.tabId = tabId + this.libraryItemId = libraryItemId + this.isReady = true + this.runNext() + } +} diff --git a/pkg/extension-v3/src/views/options.html b/pkg/extension-v3/src/views/options.html new file mode 100644 index 000000000..dd4838e6b --- /dev/null +++ b/pkg/extension-v3/src/views/options.html @@ -0,0 +1,36 @@ + + + + Omnivore Extension Settings + + + +
+

API Key

+ + + + + +

+ + + + +

API URL

+ + + +

+ + +

OMNIVORE URL

+ + + +

+ + + + + diff --git a/pkg/extension-v3/src/views/options.js b/pkg/extension-v3/src/views/options.js new file mode 100644 index 000000000..6a7cf8647 --- /dev/null +++ b/pkg/extension-v3/src/views/options.js @@ -0,0 +1,46 @@ +function addStorage(itemsToAdd) { + return chrome.storage.local.set(itemsToAdd) +} + +document.addEventListener('DOMContentLoaded', () => { + const saveApiButton = document.getElementById('save-api-key-btn') + const apiInput = document.getElementById('api-key') + + + chrome.storage.local.get('omnivoreApiKey').then( + apiKey => { + apiInput.value = apiKey.omnivoreApiKey ?? '' + } + ) + + saveApiButton.addEventListener('click', (e) => { + addStorage({ "omnivoreApiKey": apiInput.value }) + }) + + const saveUrlButton = document.getElementById('save-api-url-btn') + const apiUrlInput = document.getElementById('api-url') + + chrome.storage.local.get('omnivoreApiUrl').then( + url => { + apiUrlInput.value = url.omnivoreApiUrl ?? '' + } + ) + + saveUrlButton.addEventListener('click', (e) => { + addStorage({ "omnivoreApiUrl": apiUrlInput.value }) + }) + + + const urlButton = document.getElementById('save-omnivore-url-btn') + const urlInput = document.getElementById('omnivore-url') + + chrome.storage.local.get('omnivoreUrl').then( + url => { + urlInput.value = url.omnivoreUrl ?? '' + } + ) + + urlButton.addEventListener('click', (e) => { + addStorage({ "omnivoreUrl": urlInput.value }) + }) +}); diff --git a/pkg/extension-v3/src/views/toast.html b/pkg/extension-v3/src/views/toast.html new file mode 100644 index 000000000..adc760304 --- /dev/null +++ b/pkg/extension-v3/src/views/toast.html @@ -0,0 +1,555 @@ + + +
+
+ +
+
+ + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +
+ + + + + + +
+ + +
diff --git a/pkg/extension-v3/src/window.d.ts b/pkg/extension-v3/src/window.d.ts new file mode 100644 index 000000000..fd9b32bc6 --- /dev/null +++ b/pkg/extension-v3/src/window.d.ts @@ -0,0 +1,12 @@ +export {} + +declare type AndroidWebKitMessenger = { + // 1st argument is an actionID value, 2nd is jsonString + handleIdentifiableMessage: (string, string) => void +} + +declare global { + interface Window { + showToolbar?: (payload: { type: string }) => void + } +} diff --git a/pkg/extension-v3/tsconfig.json b/pkg/extension-v3/tsconfig.json new file mode 100644 index 000000000..43cf0df7e --- /dev/null +++ b/pkg/extension-v3/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "module": "esnext", + "target": "es6", + "lib": ["dom", "es6"], + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*"] +} \ No newline at end of file diff --git a/pkg/extension-v3/webpack.config.js b/pkg/extension-v3/webpack.config.js new file mode 100644 index 000000000..f877c83f4 --- /dev/null +++ b/pkg/extension-v3/webpack.config.js @@ -0,0 +1,39 @@ +const path = require('path') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const { CleanWebpackPlugin } = require('clean-webpack-plugin') + +module.exports = { + mode: 'production', + entry: { + background: './src/background.ts', + toolbar: './src/scripts/content/toolbar.ts', + content: './src/scripts/content/content.ts', + }, + module: { + rules: [ + { + test: /\.ts$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.ts', '.js'], + }, + output: { + filename: '[name].js', + path: path.resolve(__dirname, 'extension'), + }, + plugins: [ + new CleanWebpackPlugin(), + new CopyWebpackPlugin({ + patterns: [ + { from: 'src/manifest.json', to: 'manifest.json' }, + { from: 'src/icons', to: 'icons' }, + { from: 'src/images', to: 'images' }, + { from: 'src/views', to: 'views' }, + ], + }), + ], +} diff --git a/pkg/extension-v3/yarn.lock b/pkg/extension-v3/yarn.lock new file mode 100644 index 000000000..546f8ec40 --- /dev/null +++ b/pkg/extension-v3/yarn.lock @@ -0,0 +1,1467 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@sindresorhus/merge-streams@^2.1.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz" + integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== + +"@types/chrome@^0.0.268": + version "0.0.268" + resolved "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.268.tgz" + integrity sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA== + dependencies: + "@types/filesystem" "*" + "@types/har-format" "*" + +"@types/dompurify@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz" + integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg== + dependencies: + "@types/trusted-types" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.0" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz" + integrity sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/filesystem@*": + version "0.0.36" + resolved "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz" + integrity sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA== + dependencies: + "@types/filewriter" "*" + +"@types/filewriter@*": + version "0.0.33" + resolved "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz" + integrity sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g== + +"@types/firefox-webext-browser@^120.0.4": + version "120.0.4" + resolved "https://registry.npmjs.org/@types/firefox-webext-browser/-/firefox-webext-browser-120.0.4.tgz" + integrity sha512-lBrpf08xhiZBigrtdQfUaqX1UauwZ+skbFiL8u2Tdra/rklkKadYmIzTwkNZSWtuZ7OKpFqbE2HHfDoFqvZf6w== + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/har-format@*": + version "1.2.15" + resolved "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz" + integrity sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA== + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node@*": + version "22.4.1" + resolved "https://registry.npmjs.org/@types/node/-/node-22.4.1.tgz" + integrity sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg== + dependencies: + undici-types "~6.19.2" + +"@types/trusted-types@*": + version "2.0.7" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +"@webassemblyjs/ast@^1.12.1", "@webassemblyjs/ast@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@^1.12.1", "@webassemblyjs/wasm-parser@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn@^8, acorn@^8.7.1, acorn@^8.8.2: + version "8.12.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.5, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.21.10, "browserslist@>= 4.21.0": + version "4.23.3" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz" + integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + dependencies: + caniuse-lite "^1.0.30001646" + electron-to-chromium "^1.5.4" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +caniuse-lite@^1.0.30001646: + version "1.0.30001651" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz" + integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +clean-webpack-plugin@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz" + integrity sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w== + dependencies: + del "^4.1.1" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +copy-webpack-plugin@^12.0.2: + version "12.0.2" + resolved "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz" + integrity sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA== + dependencies: + fast-glob "^3.3.2" + glob-parent "^6.0.1" + globby "^14.0.0" + normalize-path "^3.0.0" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/del/-/del-4.1.1.tgz" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +dotenv-defaults@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz" + integrity sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg== + dependencies: + dotenv "^8.2.0" + +dotenv-webpack@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.1.0.tgz" + integrity sha512-owK1JcsPkIobeqjVrk6h7jPED/W6ZpdFsMPR+5ursB7/SdgDyO+VzAU+szK8C8u3qUhtENyYnj8eyXMR5kkGag== + dependencies: + dotenv-defaults "^2.0.2" + +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + +electron-to-chromium@^1.5.4: + version "1.5.11" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.11.tgz" + integrity sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +enhanced-resolve@^4.0.0: + version "4.5.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +enhanced-resolve@^5.17.0: + version "5.17.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.13.0" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz" + integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== + +errno@^0.1.3: + version "0.1.8" + resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + +escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.3, glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +globby@^14.0.0: + version "14.0.2" + resolved "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz" + integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz" + integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw== + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4: + 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== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +ignore@^5.2.4: + version "5.3.2" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@~2.0.3, inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +is-core-module@^2.13.0: + version "2.15.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== + dependencies: + hasown "^2.0.2" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json5@^2.1.2: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.0, micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +nanoid@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz" + integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.9.0: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +schema-utils@^3.1.1: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +semver@^7.3.4: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.31.6" + resolved "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz" + integrity sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-loader@^8.0.0: + version "8.4.0" + resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-8.4.0.tgz" + integrity sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^4.0.0" + loader-utils "^2.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +typescript@*, typescript@^4.0.0: + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@^4.0.0, webpack-cli@4.x.x: + version "4.10.0" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@*, "webpack@^4 || ^5", webpack@^5.0.0, webpack@^5.1.0, "webpack@>=4.0.0 <6.0.0", "webpack@4.x.x || 5.x.x": + version "5.93.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz" + integrity sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== diff --git a/pkg/extension/src/manifest.json b/pkg/extension/src/manifest.json index 9844fd65a..5f40b548f 100644 --- a/pkg/extension/src/manifest.json +++ b/pkg/extension/src/manifest.json @@ -95,4 +95,4 @@ "web_accessible_resources": [ "views/toast.html" ] -} \ No newline at end of file +} diff --git a/self-hosting/GUIDE.md b/self-hosting/GUIDE.md index 8cf317b8d..4d5552626 100644 --- a/self-hosting/GUIDE.md +++ b/self-hosting/GUIDE.md @@ -3,6 +3,7 @@ - [Docker Compose](#docker-compose) - [Nginx Reverse Proxy](#nginx-reverse-proxy) - [Cloudflare Tunnel](#cloudflare-tunnel) +- [Web Extensions](#web-extensions) - [Email](#email) - - [IMap Watcher](#imap-watcher) - - [Self Hosted Mail Server](#docker-mailserver-and-mail-watcher) @@ -136,6 +137,40 @@ Omnivore is no way affiliated with Cloudflare, it is just the method to which th [Read More](https://www.cloudflare.com/products/tunnel/) +## Web Extensions +The web extensions have been updated to support self-hosting - The manifest version 2 of these could be enabled to work with Self-hosting, but required some manual code changes. + +The extension has been updated for Manifest v3, and to hopefully ease the difficulty of making it work with Self-Hosted versions. + +These extensions have been sent for submission to the Chrome and Firefox webstore, but are awaiting approval. In the mean-time, a guide about how to install these manually is provided. + +### Chrome + +1. Navigate to the Chrome extension page at Chrome://extensions + ![Extensions PAge](../docs/guides/images/1-extension-page.png) + +2. Enable Developer mode on the Extensions page using the toggle. + ![Extensions Developer](../docs/guides/images/2-developer-mode.png) +3. Use the Load Unpacked Option to load the extension from source. + ![Extension Unpacked](../docs/guides/images/3-load-unpacked.png) +4. Navigate to the source folder, found at pkg/extension-v3/extension + ![Source](../docs/guides/images/4-folder.png) + ![Source](../docs/guides/images/5-folders.png) +5. The extension should have been installed. Go to the details page on the newly installed extension +![Installed](../docs/guides/images/6-installed.png) +6. Navigate to the options page, using the "Extensions Options" button. You should see the following page. +![Extension Options](../docs/guides/images/7-options.png) +![Extension Options](../docs/guides/images/8-options-page.png) +7. Generate an API Key using Omnivore. + ![API Options](../docs/guides/images/9-omnivore-settings.png) + ![API Options](../docs/guides/images/10-omnivore-api-keys.png) + ![API Options](../docs/guides/images/11-generate-key.png) + ![API Options](../docs/guides/images/12-copy-key.png) +8. Update the settings with the hosted options + ![API Options](../docs/guides/images/13-update-settings.png) + + + ## Emails and Newsletters