From a9430983ade9c05a78d5c29d392d67bd491c9924 Mon Sep 17 00:00:00 2001 From: Thomas Prouvot <35368290+tprouvot@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:59:11 +0100 Subject: [PATCH] 1.25 Release (#684) ## Describe your changes 1.25 Release ## Issue ticket number and link ## Checklist before requesting a review - [ ] I have read and understand the [Contributions section](https://github.com/tprouvot/Salesforce-Inspector-reloaded#contributions) - [ ] Target branch is releaseCandidate and not master - [ ] I have performed a self-review of my code - [ ] I ran the [unit tests](https://github.com/tprouvot/Salesforce-Inspector-reloaded#unit-tests) and my PR does not break any tests - [ ] I documented the changes I've made on the [CHANGES.md](https://github.com/tprouvot/Salesforce-Inspector-reloaded/blob/master/CHANGES.md) and followed actual conventions - [ ] I added a new section on [how-to.md](https://github.com/tprouvot/Salesforce-Inspector-reloaded/blob/master/docs/how-to.md) (optional) --------- Co-authored-by: Antoine Leleu <116869631+AntoineLeleu-Salesforce@users.noreply.github.com> Co-authored-by: guillaumeSF <166603639+guillaumeSF@users.noreply.github.com> Co-authored-by: nshulman Co-authored-by: Nathan Shulman Co-authored-by: Alan Jaouen <14994179+alanjaouen@users.noreply.github.com> Co-authored-by: Mehdi Cherfaoui <25982646+mehdisfdc@users.noreply.github.com> Co-authored-by: waliasandeep Co-authored-by: castellani <59917769+CastellaniBoris@users.noreply.github.com> Co-authored-by: SantiParris8 <32781893+SantiParris8@users.noreply.github.com> --- .eslintrc.js.old | 1 + CHANGES.md | 24 + README.md | 13 +- addon/button.js | 70 +- addon/data-export.css | 66 +- addon/data-export.js | 11 +- addon/data-import.css | 12 +- addon/data-import.js | 5 +- addon/data-load.css | 10 +- addon/data-load.js | 320 ++- addon/event-monitor.css | 482 ++++ addon/event-monitor.html | 23 + addon/event-monitor.js | 513 ++++ addon/field-creator.css | 705 +++++ addon/field-creator.html | 19 + addon/field-creator.js | 1731 ++++++++++++ addon/inspect.css | 6 +- addon/inspect.js | 3 +- addon/inspector.js | 16 +- addon/lib/cometd/AckExtension.js | 1 + addon/lib/cometd/BinaryExtension.js | 1 + addon/lib/cometd/CallbackPollingTransport.js | 1 + addon/lib/cometd/Client.js | 1 + addon/lib/cometd/Extension.js | 1 + addon/lib/cometd/LongPollingTransport.js | 1 + addon/lib/cometd/ReloadExtension.js | 1 + addon/lib/cometd/RequestTransport.js | 1 + addon/lib/cometd/TimeStampExtension.js | 27 + addon/lib/cometd/TimeSyncExtension.js | 1 + addon/lib/cometd/Transport.js | 1 + addon/lib/cometd/TransportRegistry.js | 1 + addon/lib/cometd/WebSocketTransport.js | 1 + addon/lib/cometd/Z85.js | 1 + addon/lib/cometd/cometd.d.ts | 221 ++ addon/lib/cometd/cometd.js | 36 + addon/lib/prism/prism.css | 3 + addon/lib/prism/prism.js | 8 + addon/links.js | 2 + addon/manifest-firefox.json | 20 +- addon/manifest-template.json | 76 +- addon/manifest.json | 58 +- addon/options.css | 45 +- addon/options.js | 248 +- addon/popup.css | 2 +- addon/popup.js | 69 +- addon/rest-explore.css | 70 +- addon/rest-explore.html | 2 + addon/rest-explore.js | 101 +- addon/sources.txt | 4 - addon/symbols.svg | 2542 +++++++++++++++++- docs/event-monitor.md | 40 + docs/field-creator.md | 73 + docs/how-to.md | 102 +- eslint.config.mjs | 1 - package-lock.json | 1041 ++++--- package.json | 14 +- 56 files changed, 8152 insertions(+), 696 deletions(-) create mode 100644 addon/event-monitor.css create mode 100644 addon/event-monitor.html create mode 100644 addon/event-monitor.js create mode 100644 addon/field-creator.css create mode 100644 addon/field-creator.html create mode 100644 addon/field-creator.js create mode 100644 addon/lib/cometd/AckExtension.js create mode 100644 addon/lib/cometd/BinaryExtension.js create mode 100644 addon/lib/cometd/CallbackPollingTransport.js create mode 100644 addon/lib/cometd/Client.js create mode 100644 addon/lib/cometd/Extension.js create mode 100644 addon/lib/cometd/LongPollingTransport.js create mode 100644 addon/lib/cometd/ReloadExtension.js create mode 100644 addon/lib/cometd/RequestTransport.js create mode 100644 addon/lib/cometd/TimeStampExtension.js create mode 100644 addon/lib/cometd/TimeSyncExtension.js create mode 100644 addon/lib/cometd/Transport.js create mode 100644 addon/lib/cometd/TransportRegistry.js create mode 100644 addon/lib/cometd/WebSocketTransport.js create mode 100644 addon/lib/cometd/Z85.js create mode 100644 addon/lib/cometd/cometd.d.ts create mode 100644 addon/lib/cometd/cometd.js create mode 100644 addon/lib/prism/prism.css create mode 100644 addon/lib/prism/prism.js delete mode 100644 addon/sources.txt create mode 100644 docs/event-monitor.md create mode 100644 docs/field-creator.md diff --git a/.eslintrc.js.old b/.eslintrc.js.old index e95543221..99e5a2c7d 100644 --- a/.eslintrc.js.old +++ b/.eslintrc.js.old @@ -32,6 +32,7 @@ module.exports = { } ], "rules": { + "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] "indent": ["error", 2, {"SwitchCase": 1, "flatTernaryExpressions": true}], "quotes": ["error", "double", {"avoidEscape": true}], "semi": ["error", "always"], diff --git a/CHANGES.md b/CHANGES.md index 4e0c6d9fc..ed6f7f57a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,29 @@ # Release Notes + +## Version 1.25 + +- Security improvements thanks to [Rikaard Hosein](https://github.com/rikaardhosein) [fix 661](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/661) +- Fix `Use Favicon Color` option which was not working key [issue 634](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/634) raised by [Gary Woodhouse](https://github.com/Garywoo) +- Add `Clear` button in Event Monitor and REST Explorer +- Fix `Field Creator` shortcut key [issue 608](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/608) +- Add `Flow Trigger Explorer` in shortcut links [feature 610](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/610) request by [JeffKrakowski](https://github.com/JeffKrakowski) +- Add `Import` / `Export` configuration from Option page [feature 570](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/570) +- Add `Field Creator` page to bulk create fields from the extension (contribution by [Santiago Parris](https://github.com/SantiParris8)) +- Add `Event Monitor` page to subscribe to Platform Events (contribution by [Antoine Leleu](https://github.com/AntoineLeleu-Salesforce)) +- Hide "What's new banner" in incognito mode [feature 517](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/517) +- Persist selected tab when reloading Options page. +- Add button in Options page to reset API Version to extension's default [feature 541](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/541) +- Enable Salesforce Inspector Reloaded on Debug flow page [feature 552](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/552) request by [Andrew Russo](https://github.com/mavtron-andrewrusso) +- Add option to highlight PROD with a 2px top border +- Add response time in REST Explore [issue 539](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/539) +- Add autocomplete feature for REST Explore page +- Add option to increase the number of saved & history query in data export +- Enable Salesforce Inspector Reloaded on Experience Builder and add compatibility for custom favicon +- Add option to colorize sandbox banner in the same color as the favicon [doc](https://tprouvot.github.io/Salesforce-Inspector-reloaded/how-to/?h=favico#customize-sandbox-banner-color) +- Add "Object Access" link in Object popup tab to display Object permission details (Winter 25 feature) +- Fix Platform Event links on popup [issue 500](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/500) + ## Version 1.24 - Fix issues [543](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/543), [538](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/538), [545](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/545) & [546](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/545) diff --git a/README.md b/README.md index 656b41d7b..020ad7948 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + # Salesforce Inspector Reloaded @@ -43,8 +43,12 @@ We all know and love Salesforce Inspector: As the great Søren Krabbe did not ha [![view - Documentation](https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge)](https://tprouvot.github.io/Salesforce-Inspector-reloaded/ "Go to extension documentation") -- Salesforce Developers Blog [Improve Your Productivity with Salesforce Inspector Reloaded](https://developer.salesforce.com/blogs/2024/07/improve-your-productivity-with-salesforce-inspector-reloaded) -- SalesforceBen [article](https://www.salesforceben.com/salesforce-inspector-reloaded/), [video](https://youtu.be/dvYp5mKxxzM?si=hBCIaGOyqAJlerea) + - Salesforce Developers Blog [Improve Your Productivity with Salesforce Inspector Reloaded](https://developer.salesforce.com/blogs/2024/07/improve-your-productivity-with-salesforce-inspector-reloaded) +- SalesforceBen : + - [Salesforce Inspector Reloaded](https://www.salesforceben.com/salesforce-inspector-reloaded/) + - [Video](https://youtu.be/dvYp5mKxxzM?si=hBCIaGOyqAJlerea) + - [Explore REST API With Salesforce Inspector Reloaded](https://www.salesforceben.com/explore-rest-api-with-salesforce-inspector-reloaded/) + - [Inspector Reloaded Update: Salesforce Event Monitor, Field Creator, and Export Config](https://www.salesforceben.com/inspector-reloaded-update-salesforce-event-monitor-field-creator-and-export-config/) - ApexHours [article](https://www.apexhours.com/salesforce-inspector-reloaded/) - SalesforceWay [podcast](https://salesforceway.com/podcast/salesforce-inspector-reloaded/) @@ -85,7 +89,6 @@ Follow steps described in [how-to documentation](https://tprouvot.github.io/Sale - [Firefox Browser Add-ons](https://addons.mozilla.org/en-US/firefox/addon/salesforce-inspector-reloaded/) - [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/salesforce-inspector-relo/noclfopoifgfgnflgkakofglfeeambpd) - ### Beta Version Welcome to the beta testing phase! Your input is crucial for refining our extension. Here's why we need you: @@ -97,7 +100,7 @@ Why Beta Testing? Report Bugs: If you discover a bug, please fill in an issue [here](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/new?assignees=tprouvot&labels=bug,beta&projects=&template=bug_report.md&title=[BETA]). Detailed bug reports help us address issues quickly. -To become a beta tester, [install the release candidate version](https://chromewebstore.google.com/detail/salesforce-inspector-relo/lopjgjcglnncikiocpacfdbkmpbfmkcf). +To become a beta tester, [install the beta version](https://chromewebstore.google.com/detail/salesforce-inspector-relo/lopjgjcglnncikiocpacfdbkmpbfmkcf). Thank you for shaping our extension's future! Your feedback makes it better. ### Local Installation diff --git a/addon/button.js b/addon/button.js index f58ac2731..04a51e107 100644 --- a/addon/button.js +++ b/addon/button.js @@ -2,10 +2,12 @@ /* global showStdPageDetails */ -// sfdcBody = normal Salesforce page -// ApexCSIPage = Developer Console -// auraLoadingBox = Lightning / Salesforce1 -if (document.querySelector("body.sfdcBody, body.ApexCSIPage, #auraLoadingBox") || location.host.endsWith("visualforce.com")) { +// sfdcBody: normal Salesforce page +// ApexCSIPage: Developer Console +// auraLoadingBox: Lightning / Salesforce1 +// studioBody: Exoperience Builder +// flowContainer: Flow Debugger +if (document.querySelector("body.sfdcBody, body.ApexCSIPage, #auraLoadingBox, #studioBody, #flowContainer") || location.host.endsWith("visualforce.com")) { // We are in a Salesforce org chrome.runtime.sendMessage({message: "getSfHost", url: location.href}, sfHost => { if (sfHost) { @@ -104,14 +106,43 @@ function initButton(sfHost, inInspector) { buttonElement.appendChild(img); } + function observeElement(selector, callback) { + const targetNode = document.querySelector(selector); + + if (targetNode) { + callback(targetNode); + } else { + const observer = new MutationObserver((mutations, obs) => { + const targetNode = document.querySelector(selector); + if (targetNode) { + callback(targetNode); + obs.disconnect(); + } + }); + + observer.observe(document, { + childList: true, + subtree: true + }); + } + } + function setFavicon(sfHost) { // Only update favicon if enabled, otherwise keep default - let {[sfHost + "_customFavicon"]: fav} = iFrameLocalStorage; + let {[sfHost + "_customFavicon"]: fav, "colorizeProdBanner": colorizeProd, [sfHost + "_isSandbox"]: isSandbox, [sfHost + "_trialExpirationDate"]: trialExpDate} = iFrameLocalStorage; if (fav) { - let link = document.createElement("link"); + let current = document.querySelector('link[rel="shortcut icon"]'); + let link = current ? current : document.createElement("link"); link.setAttribute("rel", "icon"); link.orgType = "image/x-icon"; if (fav.indexOf("http") == -1){ + let extensionPage = window.location.href.indexOf(chrome.i18n.getMessage("@@extension_id")) != -1; + if (iFrameLocalStorage.colorizeSandboxBanner === "true" && !extensionPage && (isSandbox === "true" || (trialExpDate && trialExpDate !== "null"))){ + colorizeBanner(fav, isSandbox); + } + if (colorizeProd === "true" && isSandbox === "false" && trialExpDate === "null"){ + addBorder(fav); + } fav = "data:image/svg+xml;base64," + btoa(``); } link.href = fav; @@ -119,6 +150,33 @@ function initButton(sfHost, inInspector) { } } + function colorizeBanner(faviconColor, isSandbox){ + //header selector depends on the env type (sandbox or trial) + const bannerSelector = isSandbox === "true" ? "#oneHeader > div.slds-color__background_gray-1.slds-text-align_center.slds-size_full.slds-text-body_regular.oneSystemMessage > div.slds-notify_alert.system-message.level-info.slds-theme_info" : "#oneHeader > div.slds-trial-header.slds-grid.oneTrialHeader.oneTrialExperience"; + + observeElement(bannerSelector, (banner) => { + banner.style.backgroundColor = faviconColor; + //update sandbox name and Logout action color for new UI + [...banner.children].forEach(child => child.style.color = "white"); + }); + } + + function addBorder(fav){ + const style = document.createElement("style"); + style.textContent = ` + body::before { + content: ''; + display: block; + position: fixed; + width: 100%; + height: 2px; + background-color: ${fav}; + z-index: 9999; + } + `; + document.head.appendChild(style); + } + function loadPopup() { btn.addEventListener("click", () => { const isInactive = !rootEl.classList.contains("insext-active"); diff --git a/addon/data-export.css b/addon/data-export.css index 9fd5da8d5..651675ce5 100644 --- a/addon/data-export.css +++ b/addon/data-export.css @@ -61,7 +61,7 @@ body { overflow: hidden; margin: 0; background-color: #B0C4DF; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_blue_background.png'); + background-image: url(images/lightning_blue_background.png); background-repeat: no-repeat; background-size: contain; background-position: 0px 48px; @@ -244,7 +244,7 @@ input[type=default] { } input[type=search] { - background-image: url('chrome-extension://__MSG_@@extension_id__/images/search.svg'); + background-image: url(images/search.svg); background-repeat: no-repeat; background-size: 1rem; background-position: 10px 7px; @@ -253,7 +253,7 @@ input[type=search] { } input[type=save] { - background-image: url('chrome-extension://__MSG_@@extension_id__/images/save.svg'); + background-image: url(images/save.svg); background-repeat: no-repeat; background-size: 1rem; background-position: 10px 7px; @@ -366,7 +366,7 @@ textarea[readonly] { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1.4rem; ; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/help.svg'); + -webkit-mask-image: url(images/help.svg); -webkit-mask-position: center; background-color: #919191; } @@ -516,15 +516,15 @@ button.toggle:hover .button-toggle-icon { } button.expand .button-toggle-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/down.svg'); + -webkit-mask-image: url(images/down.svg); } button.contract .button-toggle-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/up.svg'); + -webkit-mask-image: url(images/up.svg); } button.toggle .button-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/light_bulb.svg'); + -webkit-mask-image: url(images/light_bulb.svg); } ul > li > a > span { @@ -597,112 +597,112 @@ svg.button-icon { } .relationshipName .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/relate.svg'); + -webkit-mask-image: url(images/relate.svg); background-color: #0070d2; } .object .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/sobject.svg'); + -webkit-mask-image: url(images/sobject.svg); background-color: #04844B; } .variable .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/variable.svg'); + -webkit-mask-image: url(images/variable.svg); } .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/quotation_marks.svg'); + -webkit-mask-image: url(images/quotation_marks.svg); } .null .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/steps.svg'); + -webkit-mask-image: url(images/steps.svg); } .fieldName .autocomplete-icon { /* default icon */ - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/question_mark.svg'); + -webkit-mask-image: url(images/question_mark.svg); } .fieldName.reference .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/record_lookup.svg'); + -webkit-mask-image: url(images/record_lookup.svg); } .fieldName.string .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/string.svg'); + -webkit-mask-image: url(images/string.svg); } .fieldName.id .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/anchor.svg'); + -webkit-mask-image: url(images/anchor.svg); } .fieldName.picklist .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/picklist.svg'); + -webkit-mask-image: url(images/picklist.svg); } .fieldName.multipicklist .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/multi-picklist.svg'); + -webkit-mask-image: url(images/multi-picklist.svg); } .fieldName.boolean .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/boolean.svg'); + -webkit-mask-image: url(images/boolean.svg); } .fieldName.phone .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/call.svg'); + -webkit-mask-image: url(images/call.svg); } .fieldName.textarea .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/textarea.svg'); + -webkit-mask-image: url(images/textarea.svg); } .fieldName.url .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/link.svg'); + -webkit-mask-image: url(images/link.svg); } .fieldName.int .autocomplete-icon, .fieldName.double .autocomplete-icon, .fieldName.long .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/number.svg'); + -webkit-mask-image: url(images/number.svg); } .fieldName.address .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/home.svg'); + -webkit-mask-image: url(images/home.svg); } .fieldName.datetime .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/date-time.svg'); + -webkit-mask-image: url(images/date-time.svg); } .fieldName.date .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/date.svg'); + -webkit-mask-image: url(images/date.svg); } .fieldName.currency .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/currency.svg'); + -webkit-mask-image: url(images/currency.svg); } .fieldName.email .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/email.svg'); + -webkit-mask-image: url(images/email.svg); } .fieldName.location .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/checkin.svg'); + -webkit-mask-image: url(images/checkin.svg); } .fieldName.percent .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/percent.svg'); + -webkit-mask-image: url(images/percent.svg); } .fieldName.encryptedstring .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/lock.svg'); + -webkit-mask-image: url(images/lock.svg); } .fieldName.time .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/clock.svg'); + -webkit-mask-image: url(images/clock.svg); } .fieldName.complexvalue .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/advanced_function.svg'); + -webkit-mask-image: url(images/advanced_function.svg); } .header { diff --git a/addon/data-export.js b/addon/data-export.js index 851ee9ed8..4407ad836 100644 --- a/addon/data-export.js +++ b/addon/data-export.js @@ -87,9 +87,11 @@ class Model { this.exportStatus = "Ready"; this.exportError = null; this.exportedData = null; - this.queryHistory = new QueryHistory("insextQueryHistory", 100); + let historyNb = localStorage.getItem("numberOfQueriesInHistory"); + this.queryHistory = new QueryHistory("insextQueryHistory", historyNb ? historyNb : 100); this.selectedHistoryEntry = null; - this.savedHistory = new QueryHistory("insextSavedQueryHistory", 50); + let savedNb = localStorage.getItem("numberOfQueriesSaved"); + this.savedHistory = new QueryHistory("insextSavedQueryHistory", savedNb ? savedNb : 50); this.selectedSavedEntry = null; this.expandAutocomplete = false; this.expandSavedOptions = false; @@ -681,7 +683,8 @@ class Model { } let contextValueField = contextValueFields[0]; let queryMethod = useToolingApi ? "tooling/query" : vm.queryAll ? "queryAll" : "query"; - let whereClause = contextValueField.field.name + " like '%" + searchTerm.replace(/'/g, "\\'") + "%'"; + //let whereClause = contextValueField.field.name + " like '%" + searchTerm.replace(/'/g, "\\'") + "%'"; + let whereClause = contextValueField.field.name + " like '%" + searchTerm.replace(/([\\'])/g, "\\$1") + "%'"; if (contextValueField.sobjectDescribe.name.toLowerCase() === "recordtype"){ let sobject = contextPath.split(".")[0]; sobject = sobject.toLowerCase() === "recordtype" ? vm.autocompleteResults.sobjectName : sobject; @@ -1470,7 +1473,7 @@ class App extends React.Component { ? h("button", {disabled: !model.canDelete(), onClick: this.onDeleteRecords, title: "Open the 'Data Import' page with preloaded records to delete (< 20k records). 'Id' field needs to be queried", className: "delete-btn"}, "Delete Records") : null, ), h("input", {placeholder: "Filter Results", type: "search", value: model.resultsFilter, onInput: this.onResultsFilterInput}), - h("label", {title: "With this option, additionnal columns corresponding to Object names are removed from the query results and the exported data. These columns are useful during data import to automatically map objects."}, + h("label", {title: "With this option, additional columns corresponding to Object names are removed from the query results and the exported data. These columns are useful during data import to automatically map objects."}, h("input", {type: "checkbox", checked: model.prefHideRelations, onChange: this.onPrefHideRelationsChange}), " ", h("span", {}, "Hide Object Columns") diff --git a/addon/data-import.css b/addon/data-import.css index c58fbf838..f32bba21e 100644 --- a/addon/data-import.css +++ b/addon/data-import.css @@ -46,14 +46,14 @@ body { height: 100%; display: flex; background-color: #B0C4DF; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_blue_background.png'); + background-image: url(images/lightning_blue_background.png); background-repeat: no-repeat; background-size: contain; background-position: 0px 48px; } .prod { background-color: #e0a4b5; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_red_background.png'); + background-image: url(images/lightning_red_background.png); } #root { display: flex; @@ -122,7 +122,7 @@ body { height: 1.4rem;; -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1.4rem;; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/help.svg'); + -webkit-mask-image: url(images/help.svg); -webkit-mask-position: center; background-color: #919191; } @@ -490,13 +490,13 @@ button.toggle:hover .button-toggle-icon { margin-right: 0; } button.expand .button-toggle-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/down.svg'); + -webkit-mask-image: url(images/down.svg); } button.contract .button-toggle-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/up.svg'); + -webkit-mask-image: url(images/up.svg); } .button.field-info .button-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/salesforce-inspector-logo.svg'); + -webkit-mask-image: url(images/salesforce-inspector-logo.svg); } .cancel-btn { color: #c23934; diff --git a/addon/data-import.js b/addon/data-import.js index 283b04da8..30d31e802 100644 --- a/addon/data-import.js +++ b/addon/data-import.js @@ -57,7 +57,8 @@ class Model { if (args.has("sobject")) { this.importType = args.get("sobject"); } - if (localStorage.getItem(sfHost + "_isSandbox") != "true") { + let trialExpDate = localStorage.getItem(sfHost + "_trialExpirationDate"); + if (localStorage.getItem(sfHost + "_isSandbox") != "true" && (!trialExpDate || trialExpDate === "null")) { //change background color for production document.body.classList.add("prod"); } @@ -943,7 +944,7 @@ class App extends React.Component { model.apiType = e.target.value; model.updateAvailableActions(); model.importAction = model.availableActions[0].value; - model.importActionName = model.allActions.find(action => action.value == model.importAction).label; + model.importActionName = allActions.find(action => action.value == model.importAction).label; model.updateImportTableResult(); model.didUpdate(); } diff --git a/addon/data-load.css b/addon/data-load.css index 422ffcf13..16617af72 100644 --- a/addon/data-load.css +++ b/addon/data-load.css @@ -129,21 +129,21 @@ .pop-menu a.view-salesforce .icon { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1rem; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/salesforce1.svg'); + -webkit-mask-image: url(images/salesforce1.svg); -webkit-mask-position: center; } .pop-menu a.view-inspector .icon { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1rem; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/salesforce-inspector-logo.svg'); + -webkit-mask-image: url(images/salesforce-inspector-logo.svg); -webkit-mask-position: center; } .pop-menu a.query-record .icon { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1rem; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/record_lookup.svg'); + -webkit-mask-image: url(images/record_lookup.svg); -webkit-mask-position: center; background-color: #706E6B; } @@ -151,7 +151,7 @@ .pop-menu a.copy-id .icon { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1rem; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/copy.svg'); + -webkit-mask-image: url(images/copy.svg); -webkit-mask-position: center; background-color: #706E6B; } @@ -159,7 +159,7 @@ .pop-menu a.download-salesforce .icon { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1rem; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/down.svg'); + -webkit-mask-image: url(images/down.svg); -webkit-mask-position: center; } diff --git a/addon/data-load.js b/addon/data-load.js index 24dfad656..6855b6787 100644 --- a/addon/data-load.js +++ b/addon/data-load.js @@ -380,34 +380,42 @@ export function initScrollTable(scroller) { scrolled.className = "scrolltable-scrolled"; scroller.appendChild(scrolled); - let initialRowHeight = 15; // constant: The initial estimated height of a row before it is rendered - let initialColWidth = 50; // constant: The initial estimated width of a column before it is rendered - let bufferHeight = 500; // constant: The number of pixels to render above and below the current viewport - let bufferWidth = 500; // constant: The number of pixels to render to the left and right of the current viewport - let headerRows = 1; // constant: The number of header rows - let headerCols = 0; // constant: The number of header columns - - let rowHeights = []; // The height in pixels of each row - let rowVisible = []; // The visibility of each row. 0 = hidden, 1 = visible + let initialRowHeight = 15; + let initialColWidth = 50; + // Dynamic buffer calculation based on viewport size + let bufferHeight = Math.min(500, scroller.offsetHeight); + let bufferWidth = Math.min(500, scroller.offsetWidth); + let headerRows = 1; + let headerCols = 0; + + let rowHeights = []; + let rowVisible = []; let rowCount = 0; - let totalHeight = 0; // The sum of heights of visible cells - let firstRowIdx = 0; // The index of the first rendered row - let firstRowTop = 0; // The distance from the top of the table to the top of the first rendered row - let lastRowIdx = 0; // The index of the row below the last rendered row - let lastRowTop = 0; // The distance from the top of the table to the bottom of the last rendered row (the top of the row below the last rendered row) - let colWidths = []; // The width in pixels of each column - let colVisible = []; // The visibility of each column. 0 = hidden, 1 = visible + let totalHeight = 0; + let firstRowIdx = 0; + let firstRowTop = 0; + let lastRowIdx = 0; + let lastRowTop = 0; + let colWidths = []; + let colVisible = []; let colCount = 0; - let totalWidth = 0; // The sum of widths of visible cells - let firstColIdx = 0; // The index of the first rendered column - let firstColLeft = 0; // The distance from the left of the table to the left of the first rendered column - let lastColIdx = 0; // The index of the column to the right of the last rendered column - let lastColLeft = 0; // The distance from the left of the table to the right of the last rendered column (the left of the column after the last rendered column) + let totalWidth = 0; + let firstColIdx = 0; + let firstColLeft = 0; + let lastColIdx = 0; + let lastColLeft = 0; + + function updateBuffers() { + // Recalculate buffers when viewport changes + bufferHeight = Math.min(500, scroller.offsetHeight); + bufferWidth = Math.min(500, scroller.offsetWidth); + console.log("Buffers updated:", {bufferHeight, bufferWidth}); + } function dataChange(newData) { + console.log("Data changed"); data = newData; if (data == null || data.rowVisibilities.length == 0 || data.colVisibilities.length == 0) { - // First render, or table was cleared rowHeights = []; rowVisible = []; rowCount = 0; @@ -427,7 +435,6 @@ export function initScrollTable(scroller) { lastColLeft = 0; renderData({force: true}); } else { - // Data or visibility was changed let newRowCount = data.rowVisibilities.length; for (let r = rowCount; r < newRowCount; r++) { rowHeights[r] = initialRowHeight; @@ -460,6 +467,7 @@ export function initScrollTable(scroller) { } renderData({force: true}); } + updateBuffers(); // Ensure buffers are updated when data changes } let scrollTop = 0; @@ -467,130 +475,192 @@ export function initScrollTable(scroller) { let offsetHeight = 0; let offsetWidth = 0; function viewportChange() { - if (scrollTop == scroller.scrollTop - && scrollLeft == scroller.scrollLeft - && offsetHeight == scroller.offsetHeight - && offsetWidth == scroller.offsetWidth - ) { - return; + // Enhanced viewport change detection + let newScrollTop = scroller.scrollTop; + let newScrollLeft = scroller.scrollLeft; + let newOffsetHeight = scroller.offsetHeight; + let newOffsetWidth = scroller.offsetWidth; + + if (scrollTop !== newScrollTop || scrollLeft !== newScrollLeft + || offsetHeight !== newOffsetHeight || offsetWidth !== newOffsetWidth) { + console.log("Viewport changed:", { + scrollTop: newScrollTop, + scrollLeft: newScrollLeft, + offsetHeight: newOffsetHeight, + offsetWidth: newOffsetWidth + }); + scrollTop = newScrollTop; + scrollLeft = newScrollLeft; + offsetHeight = newOffsetHeight; + offsetWidth = newOffsetWidth; + updateBuffers(); + renderData({force: false}); } - renderData({force: false}); } function renderData({force}) { - scrollTop = scroller.scrollTop; - scrollLeft = scroller.scrollLeft; - offsetHeight = scroller.offsetHeight; - offsetWidth = scroller.offsetWidth; - - if (rowCount == 0 || colCount == 0) { - scrolled.textContent = ""; // Delete previously rendered content - scrolled.style.height = "0px"; - scrolled.style.width = "0px"; - return; - } + try { + console.log("Rendering data. Force:", force); + scrollTop = scroller.scrollTop; + scrollLeft = scroller.scrollLeft; + offsetHeight = scroller.offsetHeight; + offsetWidth = scroller.offsetWidth; - if (!force && firstRowTop <= scrollTop && (lastRowTop >= scrollTop + offsetHeight || lastRowIdx == rowCount) && firstColLeft <= scrollLeft && (lastColLeft >= scrollLeft + offsetWidth || lastColIdx == colCount)) { - return; - } - console.log("render"); - - while (firstRowTop < scrollTop - bufferHeight && firstRowIdx < rowCount - 1) { - firstRowTop += rowVisible[firstRowIdx] * rowHeights[firstRowIdx]; - firstRowIdx++; - } - while (firstRowTop > scrollTop - bufferHeight && firstRowIdx > 0) { - firstRowIdx--; - firstRowTop -= rowVisible[firstRowIdx] * rowHeights[firstRowIdx]; - } - while (firstColLeft < scrollLeft - bufferWidth && firstColIdx < colCount - 1) { - firstColLeft += colVisible[firstColIdx] * colWidths[firstColIdx]; - firstColIdx++; - } - while (firstColLeft > scrollLeft - bufferWidth && firstColIdx > 0) { - firstColIdx--; - firstColLeft -= colVisible[firstColIdx] * colWidths[firstColIdx]; - } + if (rowCount == 0 || colCount == 0) { + scrolled.textContent = ""; + scrolled.style.height = "0px"; + scrolled.style.width = "0px"; + return; + } - lastRowIdx = firstRowIdx; - lastRowTop = firstRowTop; - while (lastRowTop < scrollTop + offsetHeight + bufferHeight && lastRowIdx < rowCount) { - lastRowTop += rowVisible[lastRowIdx] * rowHeights[lastRowIdx]; - lastRowIdx++; - } - lastColIdx = firstColIdx; - lastColLeft = firstColLeft; - while (lastColLeft < scrollLeft + offsetWidth + bufferWidth && lastColIdx < colCount) { - lastColLeft += colVisible[lastColIdx] * colWidths[lastColIdx]; - lastColIdx++; - } + if (!force && firstRowTop <= scrollTop && (lastRowTop >= scrollTop + offsetHeight || lastRowIdx == rowCount) && firstColLeft <= scrollLeft && (lastColLeft >= scrollLeft + offsetWidth || lastColIdx == colCount)) { + return; + } + console.log("Rendering table"); - scrolled.textContent = ""; // Delete previously rendered content - scrolled.style.height = totalHeight + "px"; - scrolled.style.width = totalWidth + "px"; + while (firstRowTop < scrollTop - bufferHeight && firstRowIdx < rowCount - 1) { + firstRowTop += rowVisible[firstRowIdx] * rowHeights[firstRowIdx]; + firstRowIdx++; + } + while (firstRowTop > scrollTop - bufferHeight && firstRowIdx > 0) { + firstRowIdx--; + firstRowTop -= rowVisible[firstRowIdx] * rowHeights[firstRowIdx]; + } + while (firstColLeft < scrollLeft - bufferWidth && firstColIdx < colCount - 1) { + firstColLeft += colVisible[firstColIdx] * colWidths[firstColIdx]; + firstColIdx++; + } + while (firstColLeft > scrollLeft - bufferWidth && firstColIdx > 0) { + firstColIdx--; + firstColLeft -= colVisible[firstColIdx] * colWidths[firstColIdx]; + } - let table = document.createElement("table"); - let cellsVisible = false; - for (let r = firstRowIdx; r < lastRowIdx; r++) { - if (rowVisible[r] == 0) { - continue; + lastRowIdx = firstRowIdx; + lastRowTop = firstRowTop; + while (lastRowTop < scrollTop + offsetHeight + bufferHeight && lastRowIdx < rowCount) { + lastRowTop += rowVisible[lastRowIdx] * rowHeights[lastRowIdx]; + lastRowIdx++; } - let row = data.table[r]; - let tr = document.createElement("tr"); - for (let c = firstColIdx; c < lastColIdx; c++) { - if (colVisible[c] == 0) { - continue; - } - let cell = row[c]; - let td = document.createElement("td"); - td.className = "scrolltable-cell"; - if (r < headerRows || c < headerCols) { - td.className += " header"; + lastColIdx = firstColIdx; + lastColLeft = firstColLeft; + while (lastColLeft < scrollLeft + offsetWidth + bufferWidth && lastColIdx < colCount) { + lastColLeft += colVisible[lastColIdx] * colWidths[lastColIdx]; + lastColIdx++; + } + + scrolled.textContent = ""; + scrolled.style.height = totalHeight + "px"; + scrolled.style.width = totalWidth + "px"; + + let table = document.createElement("table"); + let cellsVisible = false; + + // Ensure firstRowIdx never goes below headerRows + firstRowIdx = Math.max(headerRows, firstRowIdx); + + // Render header rows separately to ensure they're always visible + for (let r = 0; r < headerRows; r++) { + if (rowVisible[r] == 0) continue; + let row = data.table[r]; + let tr = document.createElement("tr"); + for (let c = firstColIdx; c < lastColIdx; c++) { + if (colVisible[c] == 0) continue; + let cell = row[c]; + let td = document.createElement("td"); + td.className = "scrolltable-cell header"; + td.style.minWidth = colWidths[c] + "px"; + td.style.height = rowHeights[r] + "px"; + renderCell(data, cell, td); + tr.appendChild(td); } - td.style.minWidth = colWidths[c] + "px"; - td.style.height = rowHeights[r] + "px"; // min-height does not work on table cells, but height acts as min-height - renderCell(data, cell, td); - tr.appendChild(td); - cellsVisible = true; + table.appendChild(tr); } - table.appendChild(tr); - } - table.style.top = firstRowTop + "px"; - table.style.left = firstColLeft + "px"; - scrolled.appendChild(table); - // Before this point we invalidate style and layout. After this point we recalculate style and layout, and we do not invalidate them again. - if (cellsVisible) { - let tr = table.firstElementChild; - for (let r = firstRowIdx; r < lastRowIdx; r++) { + + // Render data rows + for (let r = Math.max(headerRows, firstRowIdx); r < lastRowIdx; r++) { if (rowVisible[r] == 0) { continue; } - let rowRect = tr.firstElementChild.getBoundingClientRect(); - let oldHeight = rowHeights[r]; - let newHeight = Math.max(oldHeight, rowRect.height); - rowHeights[r] = newHeight; - totalHeight += newHeight - oldHeight; - lastRowTop += newHeight - oldHeight; - tr = tr.nextElementSibling; + let row = data.table[r]; + let tr = document.createElement("tr"); + for (let c = firstColIdx; c < lastColIdx; c++) { + if (colVisible[c] == 0) { + continue; + } + let cell = row[c]; + let td = document.createElement("td"); + td.className = "scrolltable-cell"; + if (c < headerCols) { + td.className += " header"; + } + td.style.minWidth = colWidths[c] + "px"; + td.style.height = rowHeights[r] + "px"; + renderCell(data, cell, td); + tr.appendChild(td); + cellsVisible = true; + } + table.appendChild(tr); } - let td = table.firstElementChild.firstElementChild; - for (let c = firstColIdx; c < lastColIdx; c++) { - if (colVisible[c] == 0) { - continue; + + // Adjust table position to prevent header overlap at the top + let tableTop = Math.max(0, firstRowTop); + table.style.top = tableTop + "px"; + table.style.left = firstColLeft + "px"; + scrolled.appendChild(table); + + if (cellsVisible) { + // Start adjusting heights from the first data row, not header + let tr = table.children[headerRows]; + for (let r = Math.max(headerRows, firstRowIdx); r < lastRowIdx; r++) { + if (rowVisible[r] == 0) { + continue; + } + let rowRect = tr.firstElementChild.getBoundingClientRect(); + let oldHeight = rowHeights[r]; + let newHeight = Math.max(oldHeight, rowRect.height); + rowHeights[r] = newHeight; + totalHeight += newHeight - oldHeight; + lastRowTop += newHeight - oldHeight; + tr = tr.nextElementSibling; + } + let td = table.firstElementChild.firstElementChild; + for (let c = firstColIdx; c < lastColIdx; c++) { + if (colVisible[c] == 0) { + continue; + } + let colRect = td.getBoundingClientRect(); + let oldWidth = colWidths[c]; + let newWidth = Math.max(oldWidth, colRect.width); + colWidths[c] = newWidth; + totalWidth += newWidth - oldWidth; + lastColLeft += newWidth - oldWidth; + td = td.nextElementSibling; } - let colRect = td.getBoundingClientRect(); - let oldWidth = colWidths[c]; - let newWidth = Math.max(oldWidth, colRect.width); - colWidths[c] = newWidth; - totalWidth += newWidth - oldWidth; - lastColLeft += newWidth - oldWidth; - td = td.nextElementSibling; } + console.log("Render complete"); + } catch (error) { + console.error("Error in renderData:", error); + // Enhanced error logging + console.log("Current state:", { + rowCount, + colCount, + firstRowIdx, + lastRowIdx, + firstColIdx, + lastColIdx, + scrollTop, + scrollLeft, + offsetHeight, + offsetWidth + }); } } dataChange(null); scroller.addEventListener("scroll", viewportChange); + // Added resize event listener to handle viewport changes + window.addEventListener("resize", viewportChange); + return { viewportChange, dataChange diff --git a/addon/event-monitor.css b/addon/event-monitor.css new file mode 100644 index 000000000..a19c2f404 --- /dev/null +++ b/addon/event-monitor.css @@ -0,0 +1,482 @@ +@font-face { + font-family:'Salesforce Sans'; + src:url(fonts/SalesforceSans-Light.woff2); + font-weight:300 +} +@font-face { + font-family:'Salesforce Sans'; + src:url(fonts/SalesforceSans-LightItalic.woff2); + font-style:italic; + font-weight:300 +} +@font-face { + font-family:'Salesforce Sans'; + src:url(fonts/SalesforceSans-Regular.woff2); + font-weight:400 +} +@font-face { + font-family:'Salesforce Sans'; + src:url(fonts/SalesforceSans-Italic.woff2); + font-style:italic; + font-weight:400 +} +@font-face { + font-family:'Salesforce Sans'; + src:url(fonts/SalesforceSans-Bold.woff2); + font-weight:700 +} +@font-face { + font-family:'Salesforce Sans'; + src:url(fonts/SalesforceSans-BoldItalic.woff2); + font-style:italic; + font-weight:700 +} +* { + box-sizing: border-box; + vertical-align: middle; +} +html { + height: 100%; +} +body { + font-family: "Salesforce Sans", Arial, sans-serif; + font-size: .8125rem; + overflow: hidden; + margin: 0; + height: 100%; + display: flex; + background-color: #B0C4DF; + background-image: url(images/lightning_blue_background.png); + background-repeat: no-repeat; + background-size: contain; + background-position: 0px 48px; +} +.prod { + background-color: #e0a4b5; + background-image: url(images/lightning_red_background.png); +} +#root { + display: flex; + flex-grow: 1; + width: 100%; +} +[data-reactroot] { + display: flex; + flex-grow: 1; + flex-direction: column; + width: 100%; +} +[hidden] { + display: none !important; +} +#user-info { + background: #f7f9fb; + min-height: 48px; + display: flex; + align-items: center; + padding: 0 12px; + flex-wrap: wrap; +} +#user-info h1 { + padding: 0 6px 0 10px; +} +#user-info span { + font-size: 1em; +} +.sf-link { + background-color: rgb(6, 28, 63); + border-radius: 3px; + line-height: 1.8em; + text-decoration: none; + display: inline-block; + padding: 2px; + color: white; + padding-right: 1em; +} +.sf-link svg { + width: 1.8em;; + height: 1.8em;; + display: block; + margin-left: 1px; + margin-right: 1em; + float: left; + background-color: #ef7ead; + border-radius: 2px; + fill: white; +} +#help-btn { + margin-left: auto; + text-decoration: none; + font-size: 1.5rem; + font-weight: 700; + color: #919191; + display: flex; + align-items: center; +} +#help-btn:hover .icon{ + background-color: #818181; +} +#help-btn .icon { + display: inline-block; + width: 1.4rem; + height: 1.4rem;; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 1.4rem;; + -webkit-mask-image: url(images/help.svg); + -webkit-mask-position: center; + background-color: #919191; +} +#spinner { + left: -15px; + top: 9px; +} +.flex-right { + margin-left: auto; + display: flex; + align-items: center; +} +.help-text { + background: #fff; + border: 1px solid #DDDBDA; + padding: 0 15px; + border-radius: 0.25rem; + margin-top: 10px; + margin-bottom: 5px; +} +.area { + background-color: #F8F8F8; + padding: 8px 12px; + border-radius: 5px; + border: 1px solid #DDDBDA; + margin: 12px 12px 0 12px; +} +.area-header { + margin-bottom: 12px; +} +h1 { + font-size: 1rem; + display: inline; + color: #080707; + font-weight: 700; + line-height: 1.25; + margin: revert; +} +#result-area h1 { + margin-right: 1em; +} +@media (max-width: 670px) { + .conf-section { + display: block; + } +} +.conf-label, .columns-label, label { + font-weight: normal; + color: #4a4a56; + display: flex; + align-items: center; +} +.conf-label { + display: inline-block; + text-align: left; + width: auto; + padding-right: 0.5em; +} +.conf-value { + display: inline-block; + margin-right: 1em; + width: auto; +} +.conf-replay-value { + width:100px; +} +.radio-buttons label { + margin-right: 10px; + align-items: end; +} +.conf-line { + margin-top: 5px; +} +.conf-error { + color: #a00; + margin-left: 12px; + display: flex; + align-items: center; +} +.conf-error button{ + margin: 0 0 0 6px; +} +.confError { + box-shadow: 0 0 3px 1px #a00; +} +.confError + .confError { + margin-top: 5px; +} +.columns-mapping .conf-line { + padding: 5px 0; + margin: 0; +} +.flex-wrapper { + display: flex; + align-items: center; +} +.columns-mapping label { + display: block; + margin-bottom: 2px; +} +.columns-mapping .conf-label { + display: block; +} +.columns-mapping .conf-value { + overflow-y: auto; + display: flex; + flex-direction: column; + padding: 5px; + max-height: calc(100% - 34px); +} +.columns-mapping .conf-line input[type=search] { + flex-grow: 1; + width: auto; +} +.status-group { + display: flex; + flex-wrap: wrap; +} +.status-group div { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +.status-group label { + font-weight: bold; + color: #4a4a56; + padding-right: 15px; + white-space: nowrap; +} +label.statusGroupEmpty { + color: lightgray; + font-weight: normal; +} +.batch-size { + width: 3.4em; +} +.char-btn { + color: white; + text-decoration: none; + background-color: gray; + display: inline-block; + width: 14px; + height: 14px; + border-radius: 7px; + line-height: 14px; + text-align: center; +} +#confirm-replayId { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0,0,0,0.8); + z-index: 99999; +} +#confirm-dialog { + width: 400px; + position: relative; + margin: 10% auto; + border-radius: 10px; + background: #fff; + padding: 20px; +} +.dialog-buttons { + text-align: center; +} +.button-space { + margin-right: 20px; +} +select, +input[type=search], +input[type=number], +input[type=text] { + font-family: inherit; + padding: 5px 13px; + border: 1px solid #DDDBDA; + border-radius: 0.25rem; + height: 32px; + position: relative; +} +input.object-list { + width: 296px; +} +input[type="checkbox"] { + width: 1rem; + height: 1rem; +} +.configure-import input[type="checkbox"] { + margin: 0; +} +textarea:focus, +button:active, +button:focus, +input:active, +input:focus, +select:active, +select:focus { + border: 1px solid rgb(21, 137, 238); + box-shadow: rgb(6, 28, 63) 0px 0px 3px 0px; + outline: none; + z-index: 1; +} +.button-group button:active:not(:first-child), +.button-group button:focus:not(:first-child), +.button-group input:active:not(:first-child), +.button-group input:focus:not(:first-child), +.button-group select:focus:not(:first-child), +.button-group select:focus:not(:first-child) { + margin-left: -1px; + border: 1px solid rgb(21, 137, 238); +} +button:active, +button:focus { + background-color: rgb(238, 241, 246); + color: #005fb2; +} +button:disabled, +input:disabled { + color: #dddbda; + cursor: default; +} +button:disabled:hover { + background-color: #fff; + color: #dddbda; +} +.highlighted { + background-color: rgb(0, 112, 210); + border-color: rgb(0, 112, 210); + color: #fff; +} +.highlighted:hover, +.highlighted:active { + background-color: rgb(0, 95, 178); + color: #fff; +} +.highlighted:disabled { + background-color: #c9c7c5; + border-color: #c9c7c5; + color: #fff; +} +.highlighted:disabled:hover { + background-color: #c9c7c5; + color: #fff; +} +option[value="null"][disabled] { + display: none; +} +textarea { + outline: none; + border-radius: 0.25rem; + border: 1px solid #DDDBDA; +} +button, .button { + border: 1px solid #DDDBDA; + height: 32px; + display: inline-block; + text-decoration: none; + background-color: white; + padding: 0 16px; + color: #0070d2; + cursor: pointer; + outline: none; + position: relative; + line-height: 32px; + border-radius: 0.25rem; + margin-right: 10px; + white-space: nowrap; +} +button:hover, .button:hover { + background-color: #F4F6F9; + color: #005fb2; +} +button.toggle { + padding: 0 11px; +} +.button.field-info { + margin: 0 0 0 5px; + padding: 0 8px; + line-height: 28px; +} +.button-toggle-icon, +.button-icon { + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 1rem; + background-color: #706E6B; + display: inline-block; + width: 16px; + height: 16px; +} +button.toggle:hover .button-icon, +button.toggle:hover .button-toggle-icon { + background-color: #004487; +} +.flex-right button:last-child { + margin-right: 0; +} +button.expand .button-toggle-icon { + -webkit-mask-image: url(images/down.svg); +} +button.contract .button-toggle-icon { + -webkit-mask-image: url(images/up.svg); +} +.button.field-info .button-icon { + -webkit-mask-image: url(images/salesforce-inspector-logo.svg); +} +.area.result-area { + padding: 0; +} +#result-area { + flex: 1 1 0; + margin-bottom: 12px; + display: flex; + flex-direction: column; + padding: 0; +} +.result-bar { + display: flex; + align-items: center; + padding: 8px 12px; +} +#result-table { + overflow: auto; + flex: 1 1 0; + background-color: #fff; + border-top: 1px solid #DDDBDA; +} +.event-box { + margin: 4px; + padding: 8px; + clear:both; + float:left; + white-space: pre-wrap; +} +.event-not-selected { + border-bottom: 1px solid #DDDBDA; +} +.event-selected { + border: 2px solid rgb(3, 153, 63); +} +.channel-listening { + margin-left: 2%; + color: green +} + +.channel-error { + margin-left: 2%; + color: red; +} +.reset-margin{ + margin: 0;; +} +code { + width: -moz-available; + width: -webkit-fill-available; + width: fill-available; +} \ No newline at end of file diff --git a/addon/event-monitor.html b/addon/event-monitor.html new file mode 100644 index 000000000..0097b4390 --- /dev/null +++ b/addon/event-monitor.html @@ -0,0 +1,23 @@ + + + + + + Event Monitor + + + + + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/addon/event-monitor.js b/addon/event-monitor.js new file mode 100644 index 000000000..d30221cdb --- /dev/null +++ b/addon/event-monitor.js @@ -0,0 +1,513 @@ +/* global React ReactDOM */ +import {sfConn, apiVersion} from "./inspector.js"; +// Import the CometD library +import {CometD} from "./lib/cometd/cometd.js"; +import {copyToClipboard} from "./data-load.js"; + +const channelSuffix = "/event/"; +const channelTypes = [ + {value: "standardPlatformEvent", label: "Standard Platform Event"}, + {value: "platformEvent", label: "Custom Platform Event"} +]; + +class Model { + + constructor(sfHost, sessionId, args) { + this.sfHost = sfHost; + this.sessionId = sessionId; + this.args = args; + this.sfLink = "https://" + this.sfHost; + this.spinnerCount = 0; + this.showHelp = false; + this.userInfo = "..."; + this.events = []; + this.selectedChannelType = ""; + this.channels = []; + this.stdPlatformEvent = []; + this.customPlatformEvent = []; + this.selectedChannel = null; + this.channelListening = ""; + this.channelError = ""; + this.isListenning = false; + this.selectedEvent; + this.selectedEventIndex = undefined; + this.replayId = -1; + this.cometd = {}; + this.subscription = {}; + this.confirmPopup = false; + this.popConfirmed = false; + this.isProd = false; + + this.spinFor(sfConn.soap(sfConn.wsdl(apiVersion, "Partner"), "getUserInfo", {}).then(res => { + this.userInfo = res.userFullName + " / " + res.userName + " / " + res.organizationName; + })); + + let trialExpDate = localStorage.getItem(sfHost + "_trialExpirationDate"); + if (localStorage.getItem(sfHost + "_isSandbox") != "true" && (!trialExpDate || trialExpDate === "null")) { + //change background color for production + document.body.classList.add("prod"); + this.isProd = true; + } + + if (args.has("channel")) { + let channel = args.get("channel"); + this.selectedChannel = channel; + this.selectedChannelType = channel.endsWith("__e") ? "platformEvent" : "standardPlatformEvent"; + } else if(args.get("channelType")){ + this.selectedChannelType = args.get("channelType") + } else{ + this.selectedChannelType = channelTypes[0].value; + } + } + /** + * Notify React that we changed something, so it will rerender the view. + * Should only be called once at the end of an event or asynchronous operation, since each call can take some time. + * All event listeners (functions starting with "on") should call this function if they update the model. + * Asynchronous operations should use the spinFor function, which will call this function after the asynchronous operation completes. + * Other functions should not call this function, since they are called by a function that does. + * @param cb A function to be called once React has processed the update. + */ + didUpdate(cb) { + if (this.reactCallback) { + this.reactCallback(cb); + } + if (this.testCallback) { + this.testCallback(); + } + if (window.Prism) { + window.Prism.highlightAll(); + } + } + + copyAsJson() { + copyToClipboard(JSON.stringify(this.selectedEvent ? this.selectedEvent : this.events, null, " "), null, " "); + } + + clearEvents(){ + this.events = []; + } + + + /** + * Show the spinner while waiting for a promise. + * didUpdate() must be called after calling spinFor. + * didUpdate() is called when the promise is resolved or rejected, so the caller doesn't have to call it, when it updates the model just before resolving the promise, for better performance. + * @param promise The promise to wait for. + */ + spinFor(promise) { + this.spinnerCount++; + promise + .catch(err => { + console.error("spinFor", err); + }) + .then(() => { + this.spinnerCount--; + this.didUpdate(); + }) + .catch(err => console.log("error handling failed", err)); + } + + toggleHelp() { + this.showHelp = !this.showHelp; + } +} + +let h = React.createElement; + +class App extends React.Component { + constructor(props) { + super(props); + this.getEventChannels = this.getEventChannels.bind(this); + this.onChannelTypeChange = this.onChannelTypeChange.bind(this); + this.onChannelSelection = this.onChannelSelection.bind(this); + this.onSuscribeToChannel = this.onSuscribeToChannel.bind(this); + this.onUnsuscribeToChannel = this.onUnsuscribeToChannel.bind(this); + this.onToggleHelp = this.onToggleHelp.bind(this); + this.onSelectEvent = this.onSelectEvent.bind(this); + this.onReplayIdChange = this.onReplayIdChange.bind(this); + this.onCopyAsJson = this.onCopyAsJson.bind(this); + this.onClearEvents = this.onClearEvents.bind(this); + this.confirmPopupYes = this.confirmPopupYes.bind(this); + this.confirmPopupNo = this.confirmPopupNo.bind(this); + this.retrievePlatformEvent = this.retrievePlatformEvent.bind(this); + this.disableSubscribe = this.disableSubscribe.bind(this); + this.getEventChannels(); + } + + async retrievePlatformEvent(channelType, sfHost){ + let sessionChannel = JSON.parse(sessionStorage.getItem(sfHost + "_" + channelType)); + let channels = sessionChannel ? sessionChannel : []; + let query; + + if(channels.length == 0){ + if (channelType == "standardPlatformEvent"){ + query = "SELECT Label, QualifiedApiName, DeveloperName FROM EntityDefinition" + + " WHERE IsCustomizable = FALSE AND IsEverCreatable = TRUE" + + " AND QualifiedApiName LIKE '%Event' AND (NOT QualifiedApiName LIKE '%ChangeEvent')" + + " ORDER BY Label ASC LIMIT 200"; + } else if (channelType == "platformEvent") { + query = "SELECT QualifiedApiName, Label FROM EntityDefinition" + + " WHERE isCustomizable = TRUE" + + " AND KeyPrefix LIKE 'e%' ORDER BY Label ASC"; + } + await sfConn.rest("/services/data/v" + apiVersion + "/tooling/query?q=" + encodeURIComponent(query)) + .then(result => { + result.records.forEach((channel) => { + channels.push({ + name: channel.QualifiedApiName, + label: channel.Label + " (" + channel.QualifiedApiName + ")" + }); + }); + }) + .catch(err => { + console.error("An error occured fetching Event Channels of type " + channelType + ": ", err.message); + }); + sessionStorage.setItem(sfHost + "_" + channelType, JSON.stringify(channels)); + } + return channels; + } + + async getEventChannels(){ + let {model} = this.props; + switch (model.selectedChannelType){ + case "standardPlatformEvent": + if (!model.stdPlatformEvent.length){ + model.stdPlatformEvent = await this.retrievePlatformEvent(model.selectedChannelType, model.sfHost); + } + model.channels = model.stdPlatformEvent; + break; + case "platformEvent": + if (!model.customPlatformEvent.length){ + model.customPlatformEvent = await this.retrievePlatformEvent(model.selectedChannelType, model.sfHost); + if (!model.customPlatformEvent.length){ + model.customPlatformEvent.push({ + name: null, + label: "! No custom platform event found !" + }); + } + } + model.channels = model.customPlatformEvent; + } + if (model.args.has("channel")) { + model.selectedChannel = model.args.get("channel"); + } else { + model.selectedChannel = model.channels[0].name; + } + model.didUpdate(); + } + + onChannelTypeChange(e) { + let {model} = this.props; + model.selectedChannelType = e.target.value; + + const urlParams = new URLSearchParams(window.location.search); + urlParams.set('channelType', model.selectedChannelType); + window.history.replaceState(null, '', '?' + urlParams.toString()); + + this.getEventChannels(); + model.didUpdate(); + } + + onChannelSelection(e) { + let { model } = this.props; + model.selectedChannel = e.target.value; + + const urlParams = new URLSearchParams(window.location.search); + urlParams.set('channel', model.selectedChannel); + window.history.replaceState(null, '', '?' + urlParams.toString()); + + model.didUpdate(); + } + + + onReplayIdChange(e) { + let {model} = this.props; + model.replayId = e.target.value; + model.popConfirmed = false; + model.didUpdate(); + } + + async onSuscribeToChannel() { + let {model} = this.props; + model.spinnerCount++; + model.didUpdate(); + //PopUp Confirmation in case of replay Id = -2 + if (model.replayId == -2 && model.popConfirmed == false){ + model.confirmPopup = true; + model.didUpdate(); + return; + } + model.channelError = ""; + model.isListenning = true; + + // Create the CometD object. + const cometd = new CometD(); + cometd.configure({ + url: model.sfLink + "/cometd/" + apiVersion, + requestHeaders: { + Authorization: "Bearer" + model.sessionId + }, + appendMessageTypeToURL: false + }); + cometd.websocketEnabled = false; + + //Load Salesforce Replay Extension + let replayExtension = new cometdReplayExtension(); + replayExtension.setChannel(channelSuffix +model.selectedChannel); + replayExtension.setReplay(model.replayId); + replayExtension.setExtensionEnabled = true; + cometd.registerExtension("SalesforceReplayExtension", replayExtension); + + cometd.handshake((h) => { + if (h.successful) { + model.cometd = cometd; + // Subscribe to receive messages from the server. + model.subscription = cometd.subscribe(channelSuffix + model.selectedChannel, + (message) => { + model.events.unshift(JSON.parse(JSON.stringify(message.data))); + model.didUpdate(); + }, (subscribeReply) => { + if (subscribeReply.successful) { + model.channelListening = "Listening on " + channelSuffix + model.selectedChannel + " ..."; + } else { + model.channelError = "Error : " + subscribeReply.error; + model.isListenning = false; + } + model.spinnerCount--; + model.didUpdate(); + } + ); + } + }); + } + + async onUnsuscribeToChannel() { + let {model} = this.props; + model.cometd.unsubscribe(model.subscription, (unsubscribeReply) => { + console.log("unsubscribeReply"); + console.log(unsubscribeReply); + }); + model.cometd.disconnect((disconnectReply) => { + if (disconnectReply.successful) { + model.channelListening = ""; + model.isListenning = false; + model.didUpdate(); + } + }); + model.didUpdate(); + } + + onSelectEvent(e){ + e.preventDefault(); + let {model} = this.props; + model.selectedEventIndex = e.target.id; + model.selectedEvent = model.events[e.target.id]; + model.didUpdate(); + } + + onCopyAsJson() { + let {model} = this.props; + model.copyAsJson(); + model.didUpdate(); + } + + onClearEvents(){ + let {model} = this.props; + model.clearEvents(); + model.didUpdate(); + } + + onToggleHelp(e) { + let {model} = this.props; + model.toggleHelp(); + model.didUpdate(); + } + + confirmPopupYes() { + let {model} = this.props; + model.popConfirmed = true; + model.confirmPopup = false; + this.onSuscribeToChannel(); + } + + confirmPopupNo() { + let {model} = this.props; + model.confirmPopup = false; + model.replayId = -1; + model.didUpdate(); + } + + disableSubscribe(){ + let {model} = this.props; + return model.isListenning || model.selectedChannel == null; + } + + render() { + let {model} = this.props; + + return h("div", {}, + h("div", {id: "user-info"}, + h("a", {href: model.sfLink, className: "sf-link"}, + h("svg", {viewBox: "0 0 24 24"}, + h("path", {d: "M18.9 12.3h-1.5v6.6c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-5.1h-3.6v5.1c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-6.6H5.1c-.1 0-.3-.1-.3-.2s0-.2.1-.3l6.9-7c.1-.1.3-.1.4 0l7 7v.3c0 .1-.2.2-.3.2z"}) + ), + " Salesforce Home" + ), + h("h1", {}, "Event Monitor"), + h("span", {}, " / " + model.userInfo), + h("div", {className: "flex-right"}, + + h("div", {id: "spinner", role: "status", className: "slds-spinner slds-spinner_small slds-spinner_inline", hidden: model.spinnerCount == 0}, + h("span", {className: "slds-assistive-text"}), + h("div", {className: "slds-spinner__dot-a"}), + h("div", {className: "slds-spinner__dot-b"}), + ), + h("a", {href: "#", id: "help-btn", title: "Import Help", onClick: this.onToggleHelp}, + h("div", {className: "icon"}) + ), + ), + ), + h("div", {className: "area"}, + h("div", {className: "area-header"}, + h("h1", {}, "Subscribe to a channel") + ), + h("div", {className: "conf-line"}, + h("label", {title: "Channel Selection"}, + h("span", {className: "conf-label"}, "Channel Type :"), + h("span", {className: "conf-value"}, + h("select", {value: model.selectedChannelType, + onChange: this.onChannelTypeChange, + disabled: model.isListenning + }, + ...channelTypes.map((type, index) => h("option", {key: index, value: type.value}, type.label) + ) + ) + ), + h("span", {className: "conf-label"}, "Channel :"), + h("span", {className: "conf-value"}, + h("select", {value: model.selectedChannel, onChange: this.onChannelSelection, disabled: model.isListenning}, + ...model.channels.map((entity, index) => h("option", {key: index, value: entity.name}, entity.label)) + ) + ), + h("span", {className: "conf-label"}, "Replay From :"), + h("span", {className: "conf-value"}, + h("input", {type: "number", className: "conf-replay-value", value: model.replayId, onChange: this.onReplayIdChange, disabled: model.isListenning}) + ), + h("button", {onClick: this.onSuscribeToChannel, title: "Suscribe to channel", disabled: this.disableSubscribe()}, "Subscribe"), + h("button", {onClick: this.onUnsuscribeToChannel, title: "Unsuscribe to channel", disabled: !model.isListenning}, "Unsubscribe") + ) + ), + h("div", {hidden: !model.showHelp, className: "help-text"}, + h("h3", {}, "Event Monitor Help"), + h("p", {}, "Use for monitor Platform Event queue."), + h("p", {}, "Subscribe to a channel to see events in the result area. Use 'Replay From' to define the scope."), + h("p", {}, "Supports Standard and Custom Platform Events") + ) + ), + h("div", {className: "area", id: "result-area"}, + h("div", {className: "result-bar"}, + h("h1", {}, "Event Result"), + h("div", {className: "button-group"}, + h("button", {disabled: model.events.length == 0, onClick: this.onCopyAsJson, title: "Copy raw JSON to clipboard"}, "Copy") + ), + h("span", {className: "channel-listening"}, model.channelListening), + h("span", {className: "channel-error"}, model.channelError), + h("span", {className: "result-status flex-right"}, + h("div", {className: "button-group"}, + h("button", {disabled: model.events.length == 0, onClick: this.onClearEvents, title: "Clear Events"}, "Clear") + ) + ), + ), + h("div", {id: "result-table"}, + h("div", {}, + h("pre", {className: "language-json reset-margin"}, // Set the language class to JSON for Prism to highlight + model.events.map((event, index) => h("code", {onClick: this.onSelectEvent, id: index, key: index, value: event, className: `language-json event-box ${model.selectedEventIndex == index ? "event-selected" : "event-not-selected"}`}, + JSON.stringify(event, null, 4)) + ) + ) + ) + ), + model.confirmPopup ? h("div", {}, + h("div", {id: "confirm-replayId"}, + h("div", {id: "confirm-dialog"}, + h("h1", {}, "Important"), + model.isProd ? h("p", {}, "WARNING : You are on a PRODUCTION.") : null, + h("p", {}, "Use this option sparingly. Subscribing with the -2 option when a large number of event messages are stored can slow performance."), + h("p", {}, "Hitting the daily limit can break existing integration!"), + h("div", {className: "dialog-buttons"}, + h("button", {onClick: this.confirmPopupYes}, "Subscribe"), + h("button", {onClick: this.confirmPopupNo, className: "cancel-btn"}, "Cancel") + ) + ) + ) + ) : null + ) + ); + } +} + +{ + let args = new URLSearchParams(location.search.slice(1)); + let sfHost = args.get("host"); + initButton(sfHost, true); + sfConn.getSession(sfHost).then((res) => { + + let root = document.getElementById("root"); + let model = new Model(sfHost, res, args); + model.reactCallback = cb => { + ReactDOM.render(h(App, {model}), root, cb); + }; + ReactDOM.render(h(App, {model}), root); + + if (parent && parent.isUnitTest) { // for unit tests + parent.insextTestLoaded({model}); + } + }); +} + +function cometdReplayExtension() { + let REPLAY_FROM_KEY = "replay"; + let _cometd; + let _extensionEnabled; + let _replay; + let _channel; + + this.setExtensionEnabled = function(extensionEnabled) { + _extensionEnabled = extensionEnabled; + }; + + this.setReplay = function(replay) { + _replay = parseInt(replay, 10); + }; + + this.setChannel = function(channel) { + _channel = channel; + }; + + this.registered = function(name, cometd) { + _cometd = cometd; + }; + + this.incoming = function(message) { + if (message.channel === "/meta/handshake") { + if (message.ext && message.ext[REPLAY_FROM_KEY] == true) { + _extensionEnabled = true; + } + } else if (message.channel === _channel && message.data && message.data.event && message.data.event.replayId) { + _replay = message.data.event.replayId; + } + }; + + this.outgoing = function(message) { + if (message.channel === "/meta/subscribe") { + if (_extensionEnabled) { + if (!message.ext) { message.ext = {}; } + + let replayFromMap = {}; + replayFromMap[_channel] = _replay; + + message.ext[REPLAY_FROM_KEY] = replayFromMap; + } + } + }; +}; diff --git a/addon/field-creator.css b/addon/field-creator.css new file mode 100644 index 000000000..a0e0d2a3a --- /dev/null +++ b/addon/field-creator.css @@ -0,0 +1,705 @@ +@font-face { + font-family: 'Salesforce Sans'; + src: url(fonts/SalesforceSans-Light.woff2); + font-weight: 300} +@font-face { + font-family: 'Salesforce Sans'; + src: url(fonts/SalesforceSans-LightItalic.woff2); + font-style: italic; + font-weight: 300} +@font-face { + font-family: 'Salesforce Sans'; + src: url(fonts/SalesforceSans-Regular.woff2); + font-weight: 400} +@font-face { + font-family: 'Salesforce Sans'; + src: url(fonts/SalesforceSans-Italic.woff2); + font-style: italic; + font-weight: 400} +@font-face { + font-family: 'Salesforce Sans'; + src: url(fonts/SalesforceSans-Bold.woff2); + font-weight: 700} +@font-face { + font-family: 'Salesforce Sans'; + src: url(fonts/SalesforceSans-BoldItalic.woff2); + font-style: italic; + font-weight: 700} +* { + box-sizing: border-box;} +html, +body, +#root, +[data-reactroot] { + height: 100%; + line-height: 1.5; + color: #16325c;} +[data-reactroot] { + display: flex; + flex-direction: column;} +body { + font-family: "Salesforce Sans", Arial, sans-serif; + font-size: .8125rem; + overflow: hidden; + margin: 0; + background-color: #B0C4DF; + background-image: url(images/lightning_blue_background.png); + background-repeat: no-repeat; + background-size: contain; + background-position: 0px 48px; +} +.prod { + background-color: #e0a4b5; + background-image: url(images/lightning_red_background.png); +} +#user-info { + background: #f7f9fb; + height: 48px; + display: flex; + align-items: center; + padding: 0 12px; + flex-wrap: wrap;} +#user-info h1 { + padding: 0 6px 0 10px;} +#user-info span { + font-size: 1em;} +.input-textBox{ + width: 335px; + font-family: inherit; + padding: 5px 13px; + border: 1px solid #DDDBDA; + border-radius: 0.25rem; + height: 32px; + position: relative;} +.firstHeader{ + overflow-y: clip; + min-height: 130px;} +.notification_container{ + position: fixed; + top: 80px; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999; + display: flex; + align-items: center; + background-color: rgb(186, 5, 23); + color: white; + border-radius: 4px; + font-size: 16px; + padding: 12px 24px; + box-sizing: border-box; + text-align: left} +.modalBlackBase{ + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1050; + overflow-y: auto} +.sf-link { + background-color: rgb(6, 28, 63); + border-radius: 3px; + line-height: 1.8em; + text-decoration: none; + display: inline-block; + padding: 2px; + color: white; + padding-right: 1em;} +.sf-link svg { + width: 1.8em; + ; + height: 1.8em; + ; + display: block; + margin-left: 1px; + margin-right: 1em; + float: left; + background-color: #ef7ead; + border-radius: 2px; + fill: white;} +textarea { + display: block; + width: 100%; + resize: vertical; + word-wrap: normal; + font-size: 0.9rem; + padding: 8px 10px; + border-radius: 0.25rem; + border: 1px solid #DDDBDA;} +.fieldsLink:visited { + color: rgb(0, 0, 238)} +textarea[hidden] { + display: none;} +.help-text { + background: #fff; + border: 1px solid #DDDBDA; + padding: 0 15px; + border-radius: 0.25rem; + margin-top: 10px; + margin-bottom: 5px;} +#query { + height: 5em; + min-height: 7em;} +#result-area { + flex: 1 1 0; + margin-bottom: 12px; + display: flex; + flex-direction: column; + padding: 0;} +.result-bar { + margin-bottom: 3px; + display: flex; + align-items: center; + padding: 8px 12px;} +.result-info { + font-style: italic; + margin-left: 9px; + color: #8c8c8c;} +#result-text { + flex: 1 1 0; + resize: none; + white-space: pre;} +#result-table { + overflow: auto; + flex: 1 1 0; + background-color: #fff; + border-top: 1px solid #DDDBDA;} +.table-responsive { + overflow-x: auto; + max-width: 100%; + -webkit-overflow-scrolling: touch; /* For smoother scrolling on iOS devices */ + margin-bottom: 1rem;} +.area { + overflow-x: auto; + background-color: #F8F8F8; + padding: 8px 12px; + border-radius: 5px; + border: 1px solid #DDDBDA; + margin: 12px 12px 0 12px;} +h1 { + font-size: 1rem; + display: inline; + color: #080707; + font-weight: 700; + line-height: 1.25;} +.query-controls { + display: flex; + justify-content: space-between; + align-items: flex-end;} +.query-controls .button-group { + margin-bottom: 8px;} +.query-options { + margin-bottom: 8px;} +.query-options label { + margin-right: 10px;} +.area input[type="radio"], +.area input[type="checkbox"] { + margin: 0 2px 0 0;} +.area label { + white-space: nowrap; + cursor: pointer;} +.area * { + vertical-align: middle} +.query-history-controls { + display: flex; + flex-wrap: wrap; + justify-content: flex-end;} +select, +input[type=search], +input[type=save], +input[type=default] { + width: 8.5rem; + font-family: inherit; + padding: 5px 13px; + border: 1px solid #DDDBDA; + height: 32px; + position: relative; + border-radius: 0.25rem;} +input[type=search] { + background-image: url(images/search.svg); + background-repeat: no-repeat; + background-size: 1rem; + background-position: 10px 7px; + padding-left: 35px;} +input[type=save] { + background-image: url(images/save.svg); + background-repeat: no-repeat; + background-size: 1rem; + background-position: 10px 7px; + padding-left: 35px;} +input[type="checkbox"] { + width: 1rem; + height: 1rem;} +textarea:not([readonly]):focus, +button:active, +button:focus, +input:active, +input:focus, +select:active, +select:focus { + border: 1px solid rgb(21, 137, 238); + box-shadow: rgb(6, 28, 63) 0px 0px 3px 0px; + outline: none; + z-index: 1;} +.button-group button:active:not(:first-child), +.button-group button:focus:not(:first-child), +.button-group input:active:not(:first-child), +.button-group input:focus:not(:first-child), +.button-group select:focus:not(:first-child), +.button-group select:focus:not(:first-child) { + margin-left: -1px; + border: 1px solid rgb(21, 137, 238);} +button:disabled, +input:disabled { + color: #dddbda; + cursor: default;} +.form-group{ + padding-top:5px; + padding-bottom:5px;} +button:disabled:hover, +input:disabled:hover { + background-color: #fff; + color: #dddbda;} +.highlighted { + background-color: rgb(0, 112, 210); + border-color: rgb(0, 112, 210); + color: #fff;} +button:active { + background-color: rgb(238, 241, 246)!important; + color: #005fb2!important;} +.highlighted:hover { + background-color: rgb(0, 95, 178); + color: #fff;} +.ulItem{ + position: fixed; + width: 400px; + max-height: 200px; +overflow-y: auto; +z-index: 10001; /* Increased z-index*/ +border: 1px solid #ccc; +border-top: none; +border-radius: 0 0 4px 4px; +background-color: white; +list-style: none; +padding: 0; +margin: 0; +box-shadow: 0 4px 8px rgba(0,0,0,0.1);} +.highlighted:disabled { + background-color: #c9c7c5!important; + border-color: #c9c7c5!important; + color: #fff!important;} +.highlighted:disabled:hover { + background-color: #c9c7c5; + color: #fff;} +option[value="null"][disabled] { + display: none;} +textarea[readonly] { + outline: none; + border-radius: 0; + border: none; + border-top: 1px solid #DDDBDA;} +#help-btn { + margin-left: auto; + text-decoration: none; + font-size: 1.5rem; + font-weight: 700; + color: #919191; + display: flex; + align-items: center;} +#help-btn:hover .icon { + background-color: #818181;} +#help-btn .icon { + display: inline-block; + width: 1.4rem; + height: 1.4rem; + ; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 1.4rem; + ; + -webkit-mask-image: url(images/help.svg); + -webkit-mask-position: center; + background-color: #919191;} +#spinner { + left: -15px; + top: 9px;} +.flex-right { + margin-left: auto; + display: flex; + align-items: center;} +#result-area h1 { + margin-right: 1em;} +.cancel-btn { + margin-left: 1em; + color: #c23934;} +.cancel-btn:not(:disabled):hover, +.cancel-btn:not(:disabled):active, +.cancel-btn:not(:disabled):focus { + color: #a12b2b;} +.delete-btn { + background-color: #c23934; + border-color: #c23934; + color: white; + /* Allows to still show the title even when disabled as it contains useful information */ + pointer-events: auto;} +.delete-btn:not(:disabled):hover, +.delete-btn:not(:disabled):focus { + background-color: #a61a14; + border-color: #c23934; + color: white;} +.delete-btn:not(:disabled):active { + background-color: #870500; + border-color: #870500;} +.delete-btn:disabled, .delete-btn:disabled:hover { + background-color: #c9c7c5; + border-color: #c9c7c5; + color: white;} +.char-btn { + color: white; + text-decoration: none; + background-color: gray; + display: inline-block; + width: 14px; + height: 14px; + border-radius: 7px; + line-height: 14px; + text-align: center; + margin: 1px 0 0 3px;} +.char-btn[hidden], +button[hidden], +.button[hidden] { + display: none;} +.button-group { + white-space: nowrap;} +.button-group button, +.button-group .button, +.button-group select, +.button-group input { + border-left-width: 0; + border-radius: 0; + margin-right: 0} +/* div.button-group:not(:only-of-type) { + margin-left: 10px;} +*/ +.button-group> :first-child, +.button-group>[hidden]+* { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-left-width: 1px;} +.button-group> :last-child { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + margin-right: 10px;} +button, +.button { + border: 1px solid #DDDBDA; + height: 32px; + display: inline-block; + text-decoration: none; + background-color: white; + padding: 0 16px; + color: #0070d2; + cursor: pointer; + outline: none; + position: relative; + line-height: 32px; + border-radius: 0.25rem; + margin-right: 10px; + white-space: nowrap;} +button:hover, +.button:hover { + background-color: #F4F6F9; + color: #005fb2;} +button.toggle { + padding: 0 11px;} +button.toggle .button-toggle-icon, +button.toggle .button-icon { + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 1rem; + background-color: #706E6B; + display: inline-block; + width: 16px; + height: 16px;} +button.toggle:hover .button-icon, +button.toggle:hover .button-toggle-icon { + background-color: #004487;} +.flex-right button:last-child { + margin-right: 0;} +button.expand .button-toggle-icon { + -webkit-mask-image: url(images/down.svg);} +button.contract .button-toggle-icon { + -webkit-mask-image: url(images/up.svg);} +button.toggle .button-icon { + -webkit-mask-image: url(images/light_bulb.svg);} +.autocomplete-header { + display: flex; + align-items: flex-end; + margin-top: 8px;} +.autocomplete-header span { + font-size: 1rem;} +.autocomplete-results { + overflow: hidden; + margin-top: 7px; + display: flex; + flex-wrap: nowrap;} +.expanded .autocomplete-results { + flex-wrap: wrap;} +.autocomplete-results a { + border: 1px solid #DDDBDA; + border-radius: 0.25rem; + padding: 0px 4px; + text-decoration: none; + color: #006dcc;} +.autocomplete-results a:hover, +.autocomplete-results a:active { + background-color: #eff1f5; + color: #005fb2;} +.autocomplete-result, +.autocomplete-results span { + margin: 0 4px 4px 0; + white-space: nowrap;} +.autocomplete-icon { + display: inline-block; + margin: -1px 2px 0 1px; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 0.95rem; + background-color: #706E6B; + width: 16px; + height: 16px;} +.relationshipName { + font-style: italic;} +.relationshipName .autocomplete-icon { + -webkit-mask-image: url(images/relate.svg); + background-color: #0070d2;} +.object .autocomplete-icon { + -webkit-mask-image: url(images/sobject.svg); + background-color: #04844B;} +.modal-dialog { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 90%; + background-color: #fff; + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 8px; + scrollbar-width: auto; + scrollbar-color: #B0C4DF transparent;} +.modal-dialog::-webkit-scrollbar { + width: 8px;} +.modal-dialog::-webkit-scrollbar-track { + background: transparent;} +.modal-dialog::-webkit-scrollbar-thumb { + background-color: #B0C4DF; + border-radius: 4px; + border: 2px solid transparent;} +.modal-title { + font-size: 1.25rem; + font-weight: 700; + color: #16325c;} +.variable .autocomplete-icon { + -webkit-mask-image: url(images/variable.svg);} +.autocomplete-icon { + -webkit-mask-image: url(images/quotation_marks.svg);} +.null .autocomplete-icon { + -webkit-mask-image: url(images/steps.svg);} +.fieldName .autocomplete-icon { + /* default icon */ + -webkit-mask-image: url(images/question_mark.svg);} +.fieldName.reference .autocomplete-icon { + -webkit-mask-image: url(images/record_lookup.svg);} +.fieldName.string .autocomplete-icon { + -webkit-mask-image: url(images/string.svg);} +.fieldName.id .autocomplete-icon { + -webkit-mask-image: url(images/anchor.svg);} +.fieldName.picklist .autocomplete-icon { + -webkit-mask-image: url(images/picklist.svg);} +.fieldName.multipicklist .autocomplete-icon { + -webkit-mask-image: url(images/multi-picklist.svg);} +.fieldName.boolean .autocomplete-icon { + -webkit-mask-image: url(images/boolean.svg);} +.fieldName.phone .autocomplete-icon { + -webkit-mask-image: url(images/call.svg);} +.fieldName.textarea .autocomplete-icon { + -webkit-mask-image: url(images/textarea.svg);} +.fieldName.url .autocomplete-icon { + -webkit-mask-image: url(images/link.svg);} +.fieldName.int .autocomplete-icon, +.fieldName.double .autocomplete-icon, +.fieldName.long .autocomplete-icon { + -webkit-mask-image: url(images/number.svg);} +.fieldName.address .autocomplete-icon { + -webkit-mask-image: url(images/home.svg);} +.fieldName.datetime .autocomplete-icon { + -webkit-mask-image: url(images/date-time.svg);} +.fieldName.date .autocomplete-icon { + -webkit-mask-image: url(images/date.svg);} +.fieldName.currency .autocomplete-icon { + -webkit-mask-image: url(images/currency.svg);} +.fieldName.email .autocomplete-icon { + -webkit-mask-image: url(images/email.svg);} +.fieldName.location .autocomplete-icon { + -webkit-mask-image: url(images/checkin.svg);} +.fieldName.percent .autocomplete-icon { + -webkit-mask-image: url(images/percent.svg);} +.fieldName.encryptedstring .autocomplete-icon { + -webkit-mask-image: url(images/lock.svg);} +.fieldName.time .autocomplete-icon { + -webkit-mask-image: url(images/clock.svg);} +.fieldName.complexvalue .autocomplete-icon { + -webkit-mask-image: url(images/advanced_function.svg);} +.header { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 2;} +.expanded{ + overflow: auto; + height: 200px;} +/* Positioning */ +.relativePosition { position: relative;} +.zIndex1 { z-index: 1;} +/* Margins */ +.marginLeft10 { margin-left: 10px;} +.marginRight10 { margin-right: 10px;} +/* Widths */ +.width400 { width: 400px;} +.width100 { width: 100%;} +.maxWidth18 { max-width: 18%;} +/* List Item Styles */ +.objectListItem { + padding: 8px 12px; + cursor: pointer; + transition: background-color 0.2s;} +.objectListItem:hover { background-color: #f0f0f0;} +/* Text Styles */ +.errorText { color: red;} +/* Modal Styles */ +.modalOverlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000;} +.modalContent { + background-color: white; + padding: 20px; + border-radius: 8px; + width: 500px; + max-width: 90%;} +.modalHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px;} +.closeButton { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer;} +.importTextarea { + width: 100%; + height: 200px; + margin-bottom: 15px;} +.modalFooter { + display: flex; + justify-content: flex-end;} +/* Notification Styles */ +.notificationContent { + display: flex; + align-items: center;} +.errorIcon { + display: inline-flex; + align-items: center; + margin-right: 10px;} +.closeIcon { + margin-left: 10px; + display: inline-flex; + align-items: center;} +/* Icon Styles */ +.iconFill { fill: white;} +.width24px { width: 24px;} +.height24px { height: 24px;} +/* Profiles Modal */ +.cursorPointer { cursor: pointer;} +.userSelectNone { user-select: none;} +.fullWidth { width: 100%;} +.borderCollapse { border-collapse: collapse;} +.marginBottom20 { margin-bottom: 20px;} +.tableCell { + padding: 8px; + border-bottom: 1px solid #ddd;} +.textAlignLeft { text-align: left;} +.textAlignCenter { text-align: center;} +.flexCenter { + display: flex; + align-items: center; + justify-content: center;} +.marginRight5 { margin-right: 5px;} +.overflowYHidden { overflow-y: hidden;} +.height80 { height: 80%;} +.maxWidth600 { max-width: 600px;} +.flexColumn { + display: flex; + flex-direction: column;} +.height100 { height: 100%;} +.flexSpaceBetween { + display: flex; + justify-content: space-between;} +.alignItemsCenter { align-items: center;} +.marginBottom15 { margin-bottom: 15px;} +.backgroundNone { background: none;} +.borderNone { border: none;} +.fontSize1_5 { font-size: 1.5rem;} +.fontWeightBold { font-weight: bold;} +.overflowYAuto { overflow-y: auto;} +.flexGrow1 { flex-grow: 1;} +.marginRight-10 { margin-right: -10px;} +.paddingRight10 { padding-right: 10px;} +.scrollbarThin { scrollbar-width: thin;} +.scrollbarColorBlue { scrollbar-color: #B0C4DF transparent;} +.padding8 { padding: 8px;} +.border1SolidCcc { border: 1px solid #ccc;} +.borderRadius4 { border-radius: 4px;} +.marginTop15 { margin-top: 15px;} +.borderTop1SolidE5 { border-top: 1px solid #e5e5e5;} +.padding15_0 { padding: 15px 0;} +.backgroundWhite { background-color: #fff;} +.stickyBottom { + position: sticky; + bottom: 0;} +/* Field Options */ +.maxWidth500 { max-width: 500px;} +.maxHeight90vh { max-height: 90vh;} +.backgroundTransparent { background-color: transparent;} +.borderBottomNone { border-bottom: none;} +.padding0_0_10_0 { padding: 0 0 10px 0;} +.margin0 { margin: 0;} +.absoluteRightTop { + position: absolute; + right: 0; + top: 0;} +.padding10_0_20_0 { padding: 10px 0 20px 0;} +.maxHeightCalc90vh-150px { max-height: calc(90vh - 150px);} +.flexEnd { + display: flex; + justify-content: flex-end;} +.padding10_0_0_0 { padding: 10px 0 0 0;} +/* Field Row */ + +.width20px { width: 20px;} +.fillBlue { fill: #005fb2;} +.fillGreen { fill: #2e844a;} +.fillRed { fill: #ba0517;} +.fillGray { fill: #9c9c9c;} +.verticalAlignMiddle { vertical-align: middle;} +.fontSize20 { font-size: 20px;} +.btn100 { width: 100%;} \ No newline at end of file diff --git a/addon/field-creator.html b/addon/field-creator.html new file mode 100644 index 000000000..f0c54ed49 --- /dev/null +++ b/addon/field-creator.html @@ -0,0 +1,19 @@ + + + + + + ... + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/addon/field-creator.js b/addon/field-creator.js new file mode 100644 index 000000000..1a719f08f --- /dev/null +++ b/addon/field-creator.js @@ -0,0 +1,1731 @@ +/* global React ReactDOM field-creator.js */ +import {sfConn, apiVersion} from "./inspector.js"; + +let h = React.createElement; + +class ProfilesModal extends React.Component { + constructor(props) { + super(props); + this.state = { + allEditProfiles: false, + allReadProfiles: false, + allEditPermissionSets: false, + allReadPermissionSets: false, + isProfilesExpanded: false, + isPermissionSetsExpanded: true, + searchTerm: "", + permissions: this.initializePermissions(props.field, props.permissionSets) + }; + } + + handleSearchChange = (event) => { + this.setState({searchTerm: event.target.value}, this.updateAllCheckboxes); + }; + + componentDidUpdate(prevProps) { + if (prevProps.field !== this.props.field) { + this.setState({ + permissions: this.initializePermissions(this.props.field, this.props.permissionSets) + }, this.updateAllCheckboxes); + } + } + + initializePermissions(field, permissionSets) { + const permissions = Object.keys(permissionSets).reduce((acc, name) => { + acc[name] = {edit: false, read: false}; + return acc; + }, {}); + + if (field && field.profiles && Array.isArray(field.profiles)) { + field.profiles.forEach(profile => { + if (permissions[profile.name]) { + permissions[profile.name] = { + edit: profile.access === "edit", + read: profile.access === "edit" || profile.access === "read" + }; + } + }); + } + return permissions; + } + + handlePermissionChange = (name, type) => { + this.setState(prevState => ({ + permissions: { + ...prevState.permissions, + [name]: { + ...prevState.permissions[name], + [type]: !prevState.permissions[name][type], + ...(type === "edit" ? {read: true} : {}) + } + } + }), this.updateAllCheckboxes); + }; + + handleSelectAll = (type, tableType) => { + const stateKey = `all${type.charAt(0).toUpperCase() + type.slice(1)}${tableType}`; + const allSelected = !this.state[stateKey]; + + const filteredItems = this.getFilteredItems(tableType); + + this.setState(prevState => { + const updatedPermissions = {...prevState.permissions}; + filteredItems.forEach(([name]) => { + updatedPermissions[name] = { + ...updatedPermissions[name], + [type]: allSelected, + ...(type === "edit" ? {read: true} : {}) + }; + }); + + return { + [stateKey]: allSelected, + permissions: updatedPermissions + }; + }, this.updateAllCheckboxes); + }; + + updateAllCheckboxes = () => { + const {permissions} = this.state; + + const filteredProfiles = this.getFilteredItems("Profiles"); + const filteredPermissionSets = this.getFilteredItems("PermissionSets"); + + const allEditProfiles = filteredProfiles.every(([name]) => permissions[name].edit); + const allReadProfiles = filteredProfiles.every(([name]) => permissions[name].read); + const allEditPermissionSets = filteredPermissionSets.every(([name]) => permissions[name].edit); + const allReadPermissionSets = filteredPermissionSets.every(([name]) => permissions[name].read); + + this.setState({ + allEditProfiles, + allReadProfiles, + allEditPermissionSets, + allReadPermissionSets + }); + }; + + getFilteredItems = (tableType) => { + const {permissionSets} = this.props; + const {searchTerm} = this.state; + + const items = Object.entries(permissionSets) + .filter(([_, profile]) => + tableType === "Profiles" ? profile !== null : profile === null + ) + .sort((a, b) => + tableType === "Profiles" + ? a[1].localeCompare(b[1]) + : a[0].localeCompare(b[0]) + ); + + return items.filter(([name, profile]) => + (profile || name).toLowerCase().includes(searchTerm.toLowerCase()) + ); + }; + + applyToAllFields = () => { + const {permissions} = this.state; + this.props.onApplyToAllFields(permissions); + }; + + toggleSection = (section) => { + const stateKey = `is${section.replace(" ", "")}Expanded`; + this.setState(prevState => ({ + [stateKey]: !prevState[stateKey] + })); + }; + + render() { + const {field, permissionSets, onSave, onClose} = this.props; + const { + permissions, + allEditProfiles, + allReadProfiles, + allEditPermissionSets, + allReadPermissionSets, + searchTerm, + } = this.state; + + const filterItems = (items) => items.filter(([name, profile]) => + (profile || name).toLowerCase().includes(searchTerm.toLowerCase()) + ); + + const profiles = filterItems(Object.entries(permissionSets) + .filter(([_, profile]) => profile !== null) + .sort((a, b) => a[1].localeCompare(b[1]))); + + const permissionSetsOnly = filterItems(Object.entries(permissionSets) + .filter(([_, profile]) => profile === null) + .sort((a, b) => a[0].localeCompare(b[0]))); + + const renderTable = (items, title) => + h("div", {key: title}, + h("h5", { + onClick: () => this.toggleSection(title), + className: "cursorPointer userSelectNone" + }, + `${title} ${this.state[`is${title.replace(" ", "")}Expanded`] ? "▼" : "▶"}` + ), + this.state[`is${title.replace(" ", "")}Expanded`] && h("table", {className: "fullWidth borderCollapse marginBottom20"}, + h("thead", null, + h("tr", null, + h("th", {className: "tableCell textAlignLeft"}, "Name"), + h("th", {className: "tableCell textAlignCenter"}, + h("div", {className: "flexCenter"}, + h("span", {className: "marginRight5"}, "Edit"), + h("input", { + type: "checkbox", + checked: title === "Profiles" ? allEditProfiles : allEditPermissionSets, + onChange: () => this.handleSelectAll("edit", title.replace(" ", "")) + }) + ) + ), + h("th", {className: "tableCell textAlignCenter"}, + h("div", {className: "flexCenter"}, + h("span", {className: "marginRight5"}, "Read"), + h("input", { + type: "checkbox", + checked: title === "Profiles" ? allReadProfiles : allReadPermissionSets, + onChange: () => this.handleSelectAll("read", title.replace(" ", "")) + }) + ) + ) + ) + ), + h("tbody", null, + items.map(([name, profile]) => + h("tr", {key: name}, + h("td", {className: "tableCell"}, profile || name), + h("td", {className: "tableCell textAlignCenter"}, + h("input", { + type: "checkbox", + checked: permissions[name].edit, + onChange: () => this.handlePermissionChange(name, "edit") + }) + ), + h("td", {className: "tableCell textAlignCenter"}, + h("input", { + type: "checkbox", + checked: permissions[name].read, + onChange: () => this.handlePermissionChange(name, "read") + }) + ) + ) + ) + ) + ) + ); + + return h("div", {className: "modalBlackBase", onClick: onClose}, + h("div", { + className: "modal-dialog overflowYHidden height80 maxWidth600 flexColumn", + onClick: (e) => e.stopPropagation() + }, + h("div", {className: "modal-content relativePosition height100 flexColumn"}, + h("div", {className: "modal-header flexSpaceBetween alignItemsCenter marginBottom15"}, + h("h1", {className: "modal-title"}, "Set Field Permissions"), + h("button", { + type: "button", + "aria-label": "Close permission modal button", + className: "close cursorPointer backgroundNone borderNone fontSize1_5 fontWeightBold", + onClick: onClose + }, "×") + ), + h("div", {className: "modal-body overflowYAuto flexGrow1 marginRight-10 paddingRight10 scrollbarThin scrollbarColorBlue"}, + h("input", { + type: "text", + placeholder: "Search profiles and permission sets...", + value: this.state.searchTerm, + onChange: this.handleSearchChange, + className: "fullWidth padding8 border1SolidCcc borderRadius4" + }), h("p", {}, "Please consider granting field access to Permission Sets instead of Profiles ", + h("a", {href: "https://admin.salesforce.com/blog/2023/permissions-updates-learn-moar-spring-23", target: ""}, "?") + ), + + renderTable(permissionSetsOnly, "Permission Sets"), + renderTable(profiles, "Profiles") + ), + h("div", {className: "modal-footer marginTop15 flexEnd borderTop1SolidE5 padding15_0 backgroundWhite stickyBottom"}, + h("button", { + type: "button", + "aria-label": "Close button", + className: "btn btn-default marginRight10", + onClick: onClose + }, "Cancel"), + h("button", { + type: "button", + "aria-label": "Save permission for this field", + className: "btn btn-primary highlighted marginRight10", + onClick: () => { + const updatedProfiles = Object.entries(permissions).reduce((acc, [name, perm]) => { + if (perm.edit || perm.read) { + acc.push({ + name, + access: perm.edit ? "edit" : "read" + }); + } + return acc; + }, []); + + const updatedField = { + ...field, + profiles: updatedProfiles + }; + onSave(updatedField); + } + }, "Save"), + h("button", { + "aria-label": "Apply the permission to all fields in the table", + type: "button", + className: "btn btn-secondary", + onClick: this.applyToAllFields + }, "Apply to All Fields") + ) + ) + ) + ); + } +} + +class FieldOptionModal extends React.Component { + constructor(props) { + super(props); + this.state = { + field: {...props.field}, + }; + } + + handleInputChange = (event) => { + const {name, value, type, checked} = event.target; + const newValue = type === "checkbox" ? checked : value; + + this.setState((prevState) => ({ + field: { + ...prevState.field, + [name]: newValue, + }, + })); + }; + + handleSave = () => { + this.props.onSave(this.state.field); + }; + + renderFieldOptions = () => { + const {field} = this.state; + switch (field.type) { + case "Checkbox": + return h("div", {className: "field_options Checkbox_options"}, + h("div", {className: "form-group"}, + h("label", null, "Default Value"), + h("div", {className: "radio"}, + h("label", null, + h("input", { + type: "radio", + name: "checkboxDefault", + value: "checked", + checked: field.checkboxDefault === "checked", + onChange: this.handleInputChange + }), + " Checked" + ) + ), + h("div", {className: "radio"}, + h("label", null, + h("input", { + type: "radio", + name: "checkboxDefault", + value: "unchecked", + checked: field.checkboxDefault === "unchecked", + onChange: this.handleInputChange + }), + " Unchecked" + ) + ) + ), + this.renderDescriptionAndHelpText() + ); + + case "Currency": + return h("div", {className: "field_options Currency_options"}, + h("div", {className: "form-group"}, + h("label", {htmlFor: "currencyLength"}, "Length"), + h("input", { + type: "text", + id: "currencyLength", + name: "precision", + className: "form-control input-textBox", + placeholder: "Max is 18 - Decimal Places", + value: field.precision, + onChange: this.handleInputChange + }) + ), + h("div", {className: "form-group"}, + h("label", {htmlFor: "currencyDecimalPlaces"}, "Decimal Places"), + h("input", { + type: "text", + id: "currencyDecimalPlaces", + name: "decimal", + className: "form-control input-textBox", + placeholder: "Max is 18 - Length", + value: field.decimal, + onChange: this.handleInputChange + }) + ), + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox() + ); + + case "Date": + case "DateTime": + case "Email": + case "Phone": + case "Url": + return h("div", {className: `field_options ${field.type}_options`}, + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox(), + field.type === "Email" && this.renderUniqueCheckbox(), + field.type === "Email" && this.renderExternalIdCheckbox() + ); + + case "Location": + return h("div", {className: "field_options Location_options"}, + h("div", {className: "form-group"}, + h("label", null, "Latitude and Longitude Display Notation"), + h("div", {className: "radio"}, + h("label", null, + h("input", { + type: "radio", + name: "geodisplay", + value: "degrees", + checked: field.geodisplay === "degrees", + onChange: this.handleInputChange + }), + " Degrees, Minutes, Seconds" + ) + ), + h("div", {className: "radio"}, + h("label", null, + h("input", { + type: "radio", + name: "geodisplay", + value: "decimal", + checked: field.geodisplay === "decimal", + onChange: this.handleInputChange + }), + " Decimal" + ) + ) + ), + h("div", {className: "form-group"}, + h("label", {htmlFor: "geolocationDecimalPlaces"}, "Decimal Places"), + h("input", { + type: "text", + id: "geolocationDecimalPlaces", + name: "decimal", + className: "form-control input-textBox", + value: field.decimal, + onChange: this.handleInputChange + }) + ), + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox() + ); + + case "Number": + case "Percent": + return h("div", {className: `field_options ${field.type}_options`}, + h("div", {className: "form-group"}, + h("label", {htmlFor: `${field.type.toLowerCase()}Length`}, "Length"), + h("input", { + type: "text", + id: `${field.type.toLowerCase()}Length`, + name: "precision", + className: "form-control input-textBox", + placeholder: "Max is 18 less Decimal Places", + value: field.precision, + onChange: this.handleInputChange + }) + ), + h("div", {className: "form-group"}, + h("label", {htmlFor: `${field.type.toLowerCase()}DecimalPlaces`}, "Decimal Places"), + h("input", { + type: "text", + id: `${field.type.toLowerCase()}DecimalPlaces`, + name: "decimal", + className: "form-control input-textBox", + placeholder: "Max is 18 less Length", + value: field.decimal, + onChange: this.handleInputChange + }) + ), + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox(), + field.type === "Number" && this.renderUniqueCheckbox(), + field.type === "Number" && this.renderExternalIdCheckbox() + ); + + case "Picklist": + case "MultiselectPicklist": + return h("div", {className: `field_options ${field.type}_options`}, + h("div", {className: "form-group"}, + h("label", {htmlFor: `${field.type.toLowerCase()}Options`}, "Picklist Values"), + h("textarea", { + id: `${field.type.toLowerCase()}Options`, + name: "picklistvalues", + className: "form-control", + rows: "5", + placeholder: "Enter picklist values separated by line breaks.", + value: field.picklistvalues, + onChange: this.handleInputChange + }) + ), + h("div", {className: "checkbox"}, + h("label", null, + h("input", { + type: "checkbox", + id: `${field.type.toLowerCase()}SortAlpha`, + name: "sortalpha", + checked: field.sortalpha, + onChange: this.handleInputChange + }), + " Sort values alphabetically" + ) + ), + h("div", {className: "checkbox"}, + h("label", null, + h("input", { + type: "checkbox", + id: `${field.type.toLowerCase()}FirstValueDefault`, + name: "firstvaluedefault", + checked: field.firstvaluedefault, + onChange: this.handleInputChange + }), + " Use first value as default" + ) + ), + field.type === "MultiselectPicklist" && h("div", {className: "form-group"}, + h("label", {htmlFor: "picklist-multiVisibleLines"}, "# Visible Lines"), + h("input", { + type: "text", + id: "picklist-multiVisibleLines", + name: "vislines", + className: "form-control input-textBox", + placeholder: "This field is required.", + value: field.vislines, + onChange: this.handleInputChange + }) + ), + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox() + ); + + case "Text": + return h("div", {className: "field_options Text_options"}, + h("div", {className: "form-group"}, + h("label", {htmlFor: "textLength"}, "Length"), + h("input", { + type: "text", + id: "textLength", + name: "length", + className: "form-control input-textBox", + placeholder: "Max is 255 characters.", + value: field.length || 255, + onChange: this.handleInputChange + }) + ), + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox(), + this.renderUniqueCheckbox(), + this.renderExternalIdCheckbox() + ); + + case "TextArea": + return h("div", {className: "field_options TextArea_options"}, + this.renderDescriptionAndHelpText(), + this.renderRequiredCheckbox() + ); + + case "LongTextArea": + case "Html": + return h("div", {className: `field_options ${field.type}_options`}, + h("div", {className: "form-group"}, + h("label", {htmlFor: `${field.type.toLowerCase()}Length`}, "Length"), + h("input", { + type: "text", + id: `${field.type.toLowerCase()}Length`, + name: "length", + className: "form-control", + placeholder: "Max is 131,072 characters.", + value: field.length, + onChange: this.handleInputChange + }) + ), + h("div", {className: "form-group"}, + h("label", {htmlFor: `${field.type.toLowerCase()}VisibleLines`}, "# Visible Lines"), + h("input", { + type: "text", + id: `${field.type.toLowerCase()}VisibleLines`, + name: "vislines", + className: "form-control", + placeholder: "This field is required.", + value: field.vislines, + onChange: this.handleInputChange + }) + ), + this.renderDescriptionAndHelpText() + ); + + default: + return null; + } + }; + + renderDescriptionAndHelpText = () => { + const {field} = this.state; + return h("div", null, + h("div", {className: "form-group"}, + h("label", {htmlFor: "description"}, "Description"), + h("textarea", { + id: "description", + name: "description", + className: "form-control", + rows: "3", + value: field.description || "", + onChange: this.handleInputChange + }) + ), + h("div", {className: "form-group"}, + h("label", {htmlFor: "helpText"}, "Help Text"), + h("textarea", { + id: "helpText", + name: "helptext", + className: "form-control", + rows: "3", + value: field.helptext || "", + onChange: this.handleInputChange + }) + ) + ); + }; + + renderRestrictToDefinedValues = () => { + const {field} = this.state; + return h("div", {className: "checkbox"}, + h("label", null, + h("input", { + type: "checkbox", + id: "restrictToDefinedValues", + name: "restrictToDefinedValues", + checked: field.restrictToDefinedValues || false, + onChange: this.handleInputChange + }), + " Restrict picklist to the values defined in the value set" + ) + ); + }; + + renderRequiredCheckbox = () => { + const {field} = this.state; + return h("div", {className: "checkbox"}, + h("label", null, + h("input", { + type: "checkbox", + id: "required", + name: "required", + checked: field.required, + onChange: this.handleInputChange + }), + " Required" + ) + ); + }; + + renderUniqueCheckbox = () => { + const {field} = this.state; + return h("div", {className: "checkbox"}, + h("label", null, + h("input", { + type: "checkbox", + id: "unique", + name: "uniqueSetting", + checked: field.uniqueSetting, + onChange: this.handleInputChange + }), + " Unique" + ) + ); + }; + + renderExternalIdCheckbox = () => { + const {field} = this.state; + return h("div", {className: "checkbox"}, + h("label", null, + h("input", { + type: "checkbox", + id: "externalId", + name: "external", + checked: field.external, + onChange: this.handleInputChange + }), + " External ID" + ) + ); + }; + + render() { + return h("div", { + className: "modal fade show modalBlackBase", + id: "fieldOptionModal", + onClick: this.props.onClose, + role: "dialog", + "aria-labelledby": "fieldOptionModalLabel", + "aria-hidden": "true" + }, + h("div", { + className: "modal-dialog maxWidth500 maxHeight90vh overflowYAuto", + onClick: (e) => e.stopPropagation() + }, + h("div", {className: "modal-content borderNone backgroundTransparent"}, + h("div", {className: "modal-header borderBottomNone padding0_0_10_0 relativePosition"}, + h("h4", {className: "modal-title textAlignCenter width100 margin0"}, "Set Field Options"), + h("button", { + "aria-label": "Close Button", + type: "button", + className: "close absoluteRightTop backgroundTransparent borderNone fontSize1_5 fontWeightBold cursorPointer", + onClick: this.props.onClose + }, + h("span", {"aria-hidden": "true"}, "×") + ) + ), + h("div", { + className: "modal-body padding10_0_20_0 maxHeightCalc90vh-150px overflowYAuto" + }, + this.renderFieldOptions() + ), + h("div", { + className: "modal-footer flexEnd padding10_0_0_0 borderTop1SolidE5" + }, + h("button", { + "aria-label": "Close Button", + className: "btn btn-secondary", + onClick: this.props.onClose + }, "Cancel"), + h("button", { + "aria-label": "Save options button", + className: "btn btn-primary highlighted", + onClick: this.handleSave + }, "Save") + ) + ) + ) + ); + } +} + +// Define the React components +class FieldRow extends React.Component { + render() { + document.title = "Field Creator (beta)"; + + let deploymentStatus; + switch (this.props.field.deploymentStatus) { + case "pending": + deploymentStatus = h("svg", { + className: "slds-button slds-icon_x-small slds-icon-text-default slds-m-top_xxx-small width20px", + viewBox: "0 0 52 52" + }, + h("use", {xlinkHref: "symbols.svg#clock", className: "fillBlue"}) + ); + break; + case "success": + deploymentStatus = h("svg", { + className: "slds-button slds-icon_x-small slds-icon-text-default slds-m-top_xxx-small width20px", + viewBox: "0 0 52 52" + }, + h("use", {xlinkHref: "symbols.svg#success", className: "fillGreen"}) + ); + break; + case "error": + deploymentStatus = h("svg", { + className: "slds-button slds-icon_x-small slds-icon-text-default slds-m-top_xxx-small width20px", + viewBox: "0 0 52 52" + }, + h("use", {xlinkHref: "symbols.svg#error", className: "fillRed"}) + ); + break; + default: + deploymentStatus = ""; + } + + return ( + h("tr", null, + h("td", {className: "text-center verticalAlignMiddle"}, + h("div", {className: "text-center verticalAlignMiddle"}, + h("svg", { + className: "slds-button slds-icon_x-small slds-icon-text-default slds-m-top_xxx-small cursorPointer width20px", + viewBox: "0 0 52 52", + onClick: () => this.props.onClone(this.props.index) + }, + h("use", {xlinkHref: "symbols.svg#clone", className: "fillBlue"}) + ) + ) + ), + h("td", {className: "text-center verticalAlignMiddle"}, + h("div", {className: "text-center verticalAlignMiddle"}, + h("svg", { + className: "slds-button slds-icon_x-small slds-icon-text-default slds-m-top_xxx-small cursorPointer width20px", + viewBox: "0 0 52 52", + onClick: () => this.props.onDelete(this.props.index) + }, + h("use", {xlinkHref: "symbols.svg#delete", className: "fillGray"}) + ) + ) + ), + h("td", {className: "form-control-cell verticalAlignMiddle"}, + h("div", {className: "flexCenter"}, + h("input", { + type: "text", + className: "input-textBox", + placeholder: "Field label...", + value: this.props.field.label, + onChange: (e) => this.props.onLabelChange(this.props.index, e.target.value) + }) + ) + ), + h("td", {className: "form-control-cell verticalAlignMiddle"}, + h("div", {className: "flexCenter"}, + h("input", { + type: "text", + className: "input-textBox", + placeholder: "Field name...", + value: this.props.field.name, + onChange: (e) => this.props.onNameChange(this.props.index, e.target.value) + }) + ) + ), + h("td", {className: "form-control-cell verticalAlignMiddle"}, + h("div", {className: "flexCenter"}, + h("select", { + className: "form-control", + value: this.props.field.type, + onChange: (e) => this.props.onTypeChange(this.props.index, e.target.value) + }, + h("option", {value: "Checkbox"}, "Checkbox"), + h("option", {value: "Currency"}, "Currency"), + h("option", {value: "Date"}, "Date"), + h("option", {value: "DateTime"}, "Date / Time"), + h("option", {value: "Email"}, "Email"), + h("option", {value: "Location"}, "Geolocation"), + h("option", {value: "Number"}, "Number"), + h("option", {value: "Percent"}, "Percent"), + h("option", {value: "Phone"}, "Phone"), + h("option", {value: "Picklist"}, "Picklist"), + h("option", {value: "MultiselectPicklist"}, "Picklist (Multi-Select)"), + h("option", {value: "Text"}, "Text"), + h("option", {value: "TextArea"}, "Text Area"), + h("option", {value: "LongTextArea"}, "Text Area (Long)"), + h("option", {value: "Html"}, "Text Area (Rich)"), + h("option", {value: "Url"}, "URL") + ) + ) + ), + h("td", null, + h("button", { + "aria-label": "Open options modal for this field button", + className: "btn btn-sm btn100", + onClick: () => this.props.onEditOptions(this.props.index) + }, "Options") + ), + h("td", null, + h("button", { + "aria-label": "Open permission modal for this field button", + className: "btn btn-sm btn100", + onClick: () => this.props.onEditProfiles(this.props.index) + }, "Permissions") + ), + h("td", {className: "text-center verticalAlignMiddle"}, + h("div", { + className: "text-center verticalAlignMiddle fontSize20 cursorPointer", + onClick: () => this.props.onShowDeploymentStatus(this.props.index) + }, + deploymentStatus + ) + ) + ) + ); + } +} + +class FieldsTable extends React.Component { + render() { + return ( + h("div", {className: "table-responsive", style: {overflowX: "auto", maxWidth: "100%"}}, + h("table", { + className: "table table-hover", + id: "fields_table", + style: { + width: "100%", + maxWidth: "1000px", // Adjust this value as needed + margin: "0 auto", // This centers the table + } + }, + h("thead", null, + h("tr", null, + h("th", {style: {width: "5%"}}), + h("th", {style: {width: "5%"}}), + h("th", {style: {width: "20%"}}, "Label"), + h("th", {style: {width: "20%"}}, "API Name (__c)"), + h("th", {style: {width: "20%"}}, "Type"), + h("th", {style: {width: "10%"}}, "Options"), + h("th", {style: {width: "10%"}}, "Permissions"), + h("th", {style: {width: "10%"}}) + ) + ), + h("tbody", null, + this.props.fields.map((field, index) => + h(FieldRow, { + key: index, + index, + field, + onDelete: this.props.onDelete, + onClone: this.props.onClone, + onLabelChange: this.props.onLabelChange, + onNameChange: this.props.onNameChange, + onTypeChange: this.props.onTypeChange, + onEditOptions: this.props.onEditOptions, + onEditProfiles: this.props.onEditProfiles, + onShowDeploymentStatus: this.props.onShowDeploymentStatus + }) + ) + ) + ) + ) + ); + } +} + +class App extends React.Component { + + constructor(props) { + super(props); + this.state = { + objects: [], + profiles: [], + permissionSets: {}, + fields: [{label: "", name: "", type: "Text"}], + showProfilesModal: false, + currentFieldIndex: null, + showModal: false, + showImportModal: false, + allFieldsHavePermissions: true, + importCsvContent: "", + importError: "", + objectSearch: "", + fieldErrorMessage: "", + errorMessageClickable: false, + userInfo: "...", + filteredObjects: [] + }; + let trialExpDate = localStorage.getItem(sfHost + "_trialExpirationDate"); + if (localStorage.getItem(sfHost + "_isSandbox") != "true" && (!trialExpDate || trialExpDate === "null")) { + //change background color for production + document.body.classList.add("prod"); + } + } + + componentDidMount() { + this.fetchObjects(); + this.fetchPermissionSets(); + this.fetchUserInfo(); + } + + handleObjectSearch = (e) => { + const searchTerm = e.target.value.toLowerCase(); + + // Sort the filtered objects based on relevance + const sortedFilteredObjects = this.state.objects + .filter(obj => + obj.name.toLowerCase().includes(searchTerm) + || obj.label.toLowerCase().includes(searchTerm) + ) + .sort((a, b) => { + const aName = a.name.toLowerCase(); + const bName = b.name.toLowerCase(); + const aLabel = a.label.toLowerCase(); + const bLabel = b.label.toLowerCase(); + + // Helper function to calculate match score + const getMatchScore = (str) => { + if (str === searchTerm) return 4; // Exact match + if (str.startsWith(searchTerm)) return 3; // Starts with + if (str.includes(searchTerm)) return 2; // Contains + return 0; // No match + }; + + const aScore = Math.max(getMatchScore(aName), getMatchScore(aLabel)); + const bScore = Math.max(getMatchScore(bName), getMatchScore(bLabel)); + + if (aScore !== bScore) return bScore - aScore; // Higher score first + + // If scores are equal, prioritize shorter strings + const aLength = Math.min(aName.length, aLabel.length); + const bLength = Math.min(bName.length, bLabel.length); + if (aLength !== bLength) return aLength - bLength; + + // If lengths are equal, sort alphabetically + return aName.localeCompare(bName); + }); + + this.setState({ + objectSearch: e.target.value, + filteredObjects: sortedFilteredObjects, + }); + }; + + handleObjectSelect = (obj) => { + let objectName = obj.name; + this.setState({ + selectedObject: obj, + objectSearch: objectName, + filteredObjects: [], + }); + }; + + fetchUserInfo() { + const wsdl = sfConn.wsdl(apiVersion, "Partner"); + sfConn.soap(wsdl, "getUserInfo", {}) + .then(res => { + const userInfo = `${res.userFullName} / ${res.userName} / ${res.organizationName}`; + this.setState({userInfo}); + }) + .catch(err => { + console.error("Error fetching user info:", err); + }); + } + + setFieldPermissions(field, fieldId, objectName) { + const accessToken = sfConn.sessionId; + const instanceUrl = `https://${sfConn.instanceHostname}`; + const fieldPermissionUrl = `${instanceUrl}/services/data/v${apiVersion}/sobjects/FieldPermissions/`; + + if (!field.profiles || !Array.isArray(field.profiles)) { + return Promise.resolve([]); + } + const permissionPromises = field.profiles.map(profile => { + const permissionSetId = this.state.permissionSetMap[profile.name] || profile.name; + const fieldPermissionBody = { + ParentId: permissionSetId, + SobjectType: objectName, + Field: `${objectName}.${field.name}__c`, + PermissionsEdit: profile.access === "edit", + PermissionsRead: profile.access === "edit" || profile.access === "read" + }; + + return fetch(fieldPermissionUrl, { + method: "POST", + headers: { + "Authorization": `Bearer ${accessToken}`, + "Content-Type": "application/json" + }, + body: JSON.stringify(fieldPermissionBody) + }) + .then(response => response.json()); + }); + + return Promise.all(permissionPromises); + } + + createField(field, objectName) { + const accessToken = sfConn.sessionId; + const instanceUrl = `https://${sfConn.instanceHostname}`; + const metadataUrl = `${instanceUrl}/services/data/v${apiVersion}/tooling/sobjects/CustomField`; + + const newField = { + FullName: `${objectName}.${field.name}__c`, + Metadata: { + label: field.label, + type: this.mapFieldType(field.type), + description: field.description, + inlineHelpText: field.helptext, + required: field.required || false, + unique: field.uniqueSetting || false, + externalId: field.external || false, + trackFeedHistory: false, + trackHistory: false, + trackTrending: false + } + }; + + // Add specific options based on field type + switch (field.type) { + case "Checkbox": + newField.Metadata.defaultValue = field.checkboxDefault === "checked"; + break; + + case "Currency": + case "Number": + case "Percent": + newField.Metadata.precision = parseInt(field.precision) || 18; + newField.Metadata.scale = parseInt(field.decimal) || 0; + break; + + case "Date": + case "DateTime": + case "Email": + case "Phone": + case "Url": + // No additional options for these types + break; + + case "Location": + newField.Metadata.displayLocationInDecimal = field.geodisplay === "decimal"; + newField.Metadata.scale = parseInt(field.decimal) || 0; + break; + + case "Picklist": + case "MultiselectPicklist": + newField.Metadata.valueSet = { + valueSetDefinition: { + sorted: field.sortalpha || false, + value: field.picklistvalues.split("\n").map((value, index) => ({ + fullName: value.trim(), + default: field.firstvaluedefault && index === 0 + })) + } + }; + if (field.type === "MultiselectPicklist") { + newField.Metadata.visibleLines = parseInt(field.vislines) || 4; + } + break; + + case "Text": + newField.Metadata.length = parseInt(field.length) || 255; + break; + + case "TextArea": + // No additional options for TextArea + break; + + case "LongTextArea": + case "Html": + newField.Metadata.length = parseInt(field.length) || 32768; + newField.Metadata.visibleLines = parseInt(field.vislines) || 6; + break; + + default: + console.warn(`Unsupported field type: ${field.type}`); + } + + return fetch(metadataUrl, { + method: "POST", + headers: { + "Authorization": `Bearer ${accessToken}`, + "Content-Type": "application/json" + }, + body: JSON.stringify(newField) + }) + .then(response => { + if (!response.ok) { + return response.json().then(errorData => { + throw new Error(JSON.stringify(errorData)); + }); + } + return response.json(); + }) + .then(data => this.setFieldPermissions(field, data.id, objectName)) + .catch(error => { + console.error("Error creating field:", error); + throw error; + }); + } + + mapFieldType(uiType) { + const typeMap = { + "Checkbox": "Checkbox", + "Currency": "Currency", + "Date": "Date", + "DateTime": "DateTime", + "Email": "Email", + "Location": "Location", + "Number": "Number", + "Percent": "Percent", + "Phone": "Phone", + "Picklist": "Picklist", + "MultiselectPicklist": "MultiselectPicklist", + "Text": "Text", + "TextArea": "TextArea", + "LongTextArea": "LongTextArea", + "Html": "Html", + "Url": "Url" + }; + return typeMap[uiType] || uiType; + } + + //TODO cache entity from popup.js + fetchObjects = async () => { + try { + const entityMap = new Map(); + const addEntity = (entity, api) => { + let existingEntity = entityMap.get(entity.name); + if (existingEntity) { + // Update existing entity + Object.assign(existingEntity, entity); + if (!existingEntity.availableApis.includes(api)) { + existingEntity.availableApis.push(api); + } + // Keep layoutable true if it was true in either call + existingEntity.layoutable = existingEntity.layoutable || entity.layoutable; + } else { + // Add new entity + entityMap.set(entity.name, { + ...entity, + availableApis: [api], + availableKeyPrefix: entity.keyPrefix || null, + layoutable: entity.layoutable || false // Default to false if not specified + }); + } + }; + + const getObjects = async (url, api) => { + try { + const describe = await sfConn.rest(url); + describe.sobjects.forEach(sobject => { + addEntity({...sobject, layoutable: sobject.layoutable || false}, api); + }); + } catch (err) { + console.error("list " + api + " sobjects", err); + } + }; + + //TODO cache entityDefinitionCount from popup.js + const getEntityDefinitionCount = async () => { + try { + const res = await sfConn.rest("/services/data/v" + apiVersion + "/tooling/query?q=" + encodeURIComponent("SELECT COUNT() FROM EntityDefinition")); + return res.totalSize; + } catch (err) { + console.error("count entity definitions: ", err); + return 0; + } + }; + + const getEntityDefinitions = async () => { + const entityDefinitionCount = await getEntityDefinitionCount(); + const batchSize = 2000; + const batches = Math.ceil(entityDefinitionCount / batchSize); + const batchPromises = []; + + for (let bucket = 0; bucket < batches; bucket++) { + let offset = bucket > 0 ? " OFFSET " + (bucket * batchSize) : ""; + let query = `SELECT QualifiedApiName, Label, KeyPrefix, DurableId, IsCustomSetting, RecordTypesSupported, NewUrl, IsEverCreatable FROM EntityDefinition ORDER BY QualifiedApiName ASC LIMIT ${batchSize}${offset}`; + + let batchPromise = sfConn.rest("/services/data/v" + apiVersion + "/tooling/query?q=" + encodeURIComponent(query)) + .then(respEntity => { + for (let record of respEntity.records) { + addEntity({ + name: record.QualifiedApiName, + label: record.Label, + keyPrefix: record.KeyPrefix, + durableId: record.DurableId, + isCustomSetting: record.IsCustomSetting, + recordTypesSupported: record.RecordTypesSupported, + newUrl: record.NewUrl, + isEverCreatable: record.IsEverCreatable, + // Don't set layoutable here, as it should come from describe calls + }, "EntityDef"); + } + }).catch(err => { + console.error("list entity definitions: ", err); + }); + + batchPromises.push(batchPromise); + } + + return Promise.all(batchPromises); + }; + + // Fetch objects from different APIs + await Promise.all([ + getObjects("/services/data/v" + apiVersion + "/sobjects/", "regularApi"), + getObjects("/services/data/v" + apiVersion + "/tooling/sobjects/", "toolingApi"), + getEntityDefinitions(), + ]); + + const sObjectsList = Array.from(entityMap.values()); + const layoutableObjects = sObjectsList.filter(obj => obj.layoutable === true); + this.setState({objects: layoutableObjects}); + } catch (error) { + console.error("Error fetching objects:", error); + this.setState({fieldErrorMessage: "Error fetching object data."}); + } + }; + + fetchPermissionSets = () => { + const accessToken = sfConn.sessionId; + const instanceUrl = `https://${sfConn.instanceHostname}`; + const permissionSetsUrl = `${instanceUrl}/services/data/v${apiVersion}/query/?q=SELECT+Id,Name,Profile.Name+FROM+PermissionSet`; + + fetch(permissionSetsUrl, { + method: "GET", + headers: { + "Authorization": `Bearer ${accessToken}`, + "Content-Type": "application/json" + } + }) + .then(response => response.json()) + .then(data => { + let permissionSets = {}; + let permissionSetMap = {}; + data.records.forEach(record => { + permissionSets[record.Name] = record.Profile ? record.Profile.Name : null; + permissionSetMap[record.Name] = record.Id; + }); + + this.setState({permissionSets, permissionSetMap}); + }) + .catch(error => { + console.error("Error fetching permission sets:", error); + }); + }; + + addRow = () => { + this.setState((prevState) => ({ + fields: [...prevState.fields, {label: "", name: "", type: "Text"}], + })); + this.checkAllFieldsHavePermissions(); + }; + + removeRow = (index) => { + this.setState((prevState) => ({ + fields: prevState.fields.filter((_, i) => i !== index), + })); + }; + + cloneRow = (index) => { + this.setState((prevState) => { + const clonedField = {...prevState.fields[index]}; + delete clonedField.deploymentStatus; + delete clonedField.deploymentError; + + return { + fields: [...prevState.fields, clonedField], + }; + }); + }; + + onLabelChange = (index, label) => { + this.setState((prevState) => ({ + fields: prevState.fields.map((field, i) => { + if (i === index) { + field.label = label; + field.name = label.replace(/\s+(\w)/g, (_, letter) => letter.toUpperCase()); + delete field.deploymentStatus; + delete field.deploymentError; + } + return field; + }), + })); + }; + + onNameChange = (index, name) => { + this.setState((prevState) => ({ + fields: prevState.fields.map((field, i) => { + if (i === index) { + field.name = name; + delete field.deploymentStatus; + delete field.deploymentError; + } + return field; + }), + })); + }; + + onTypeChange = (index, type) => { + this.setState((prevState) => ({ + fields: prevState.fields.map((field, i) => + i === index ? {...field, type} : field + ), + })); + }; + + onEditOptions = (index) => { + this.setState({ + showModal: true, + currentFieldIndex: index, + }); + }; + + openImportModal = () => { + this.setState({showImportModal: true, importCsvContent: "", importError: ""}); + }; + + closeImportModal = () => { + this.setState({showImportModal: false, importCsvContent: "", importError: ""}); + }; + + handleImportCsvChange = (event) => { + this.setState({importCsvContent: event.target.value}); + }; + + importCsv = () => { + const {importCsvContent} = this.state; + // Helper function to detect the separator + const detectSeparator = (content) => { + const potentialSeparators = [",", ";", "\t", "|"]; + const lines = content.split("\n").filter(line => line.trim() !== ""); // Remove empty lines + if (lines.length === 0) { + return ","; // Default to comma if no content + } + // Check the first line for the most frequent separator + const firstLine = lines[0]; + let maxSeparator = ","; + let maxCount = 0; + potentialSeparators.forEach(separator => { + const count = firstLine.split(separator).length; + if (count > maxCount) { + maxCount = count; + maxSeparator = separator; + } + }); + return maxSeparator; + }; + // Detect separator dynamically + const separator = detectSeparator(importCsvContent); + const lines = importCsvContent.split("\n"); + const newFields = []; + let hasError = false; + const validTypes = [ + "Checkbox", "Currency", "Date", "DateTime", "Email", "Location", "Number", + "Percent", "Phone", "Picklist", "MultiselectPicklist", "Text", "TextArea", + "LongTextArea", "Html", "Url" + ]; + lines.forEach((line, index) => { + const [label, name, type] = line.split(separator).map(item => item.trim()); + if (label && name && type) { + if (validTypes.includes(type)) { + newFields.push({label, name, type}); + } else { + this.setState({importError: `Invalid type "${type}" on line ${index + 1}`}); + hasError = true; + } + } + }); + if (!hasError) { + this.setState(prevState => ({ + fields: [...prevState.fields, ...newFields], + showImportModal: false, + importCsvContent: "", + importError: "" + })); + } + }; + + onShowDeploymentStatus = (index) => { + const field = this.state.fields[index]; + if (field.deploymentStatus === "error") { + let errorMessage = "Deployment Error"; + try { + const errorData = JSON.parse(field.deploymentError); + errorMessage = errorData[0]?.message || errorMessage; + + } catch (e) { + console.error("Catch error", e); + errorMessage = field.deploymentError || errorMessage; + } + this.setState({fieldErrorMessage: errorMessage}); + } else if (field.deploymentStatus === "pending") { + this.setState({fieldErrorMessage: "Field deployment is in progress"}); + } + }; + + onEditProfiles = (index) => { + this.setState({ + showProfilesModal: true, + currentFieldIndex: index, + }); + }; + + onCloseModal = () => { + this.setState({ + showModal: false, + currentFieldIndex: null, + }); + }; + + onCloseProfilesModal = () => { + this.setState({ + showProfilesModal: false, + currentFieldIndex: null, + }); + }; + + onSaveFieldProfiles = (updatedField) => { + const {fields, currentFieldIndex} = this.state; + fields[currentFieldIndex] = updatedField; + this.setState({ + fields, + showProfilesModal: false, + currentFieldIndex: null, + }); + this.checkAllFieldsHavePermissions(); + }; + + applyToAllFields = (permissions) => { + const {fields} = this.state; + const updatedFields = fields.map(field => { + const updatedProfiles = Object.entries(permissions).reduce((acc, [name, perm]) => { + if (perm.edit || perm.read) { + acc.push({ + name, + access: perm.edit ? "edit" : "read" + }); + } + return acc; + }, []); + return {...field, profiles: updatedProfiles}; + }); + + this.setState({ + fields: updatedFields, + showProfilesModal: false, + currentFieldIndex: null + }, () => { + // This callback will be executed after the state has been updated + this.checkAllFieldsHavePermissions(); + }); + }; + + onSaveFieldOptions = (updatedField) => { + const {fields, currentFieldIndex} = this.state; + fields[currentFieldIndex] = updatedField; + this.setState({ + fields, + showModal: false, + currentFieldIndex: null, + }); + }; + + clearAll = () => { + location.reload(); + }; + + checkAllFieldsHavePermissions = () => { + if (this.state.fields.every(field => field.profiles && field.profiles.length > 0)) { + this.setState({allFieldsHavePermissions: true}); + return true; + } else { + this.setState({allFieldsHavePermissions: false}); + return false; + } + }; + + deploy = () => { + const {fields} = this.state; + this.checkAllFieldsHavePermissions(); + const fieldsToProcess = fields.filter(field => field.deploymentStatus !== "success"); + + if (fieldsToProcess.length === 0) { + alert("All fields have already been successfully deployed."); + return; + } + + const updatedFields = fields.map(field => + field.deploymentStatus !== "success" + ? {...field, deploymentStatus: "pending"} + : field + ); + this.setState({fields: updatedFields}); + + fieldsToProcess.forEach((field) => { + const index = fields.findIndex(f => f === field); + this.createField(field, this.state.selectedObject.name) + .then(() => { + const newFields = [...this.state.fields]; + newFields[index].deploymentStatus = "success"; + this.setState({fields: newFields}); + }) + .catch(error => { + const newFields = [...this.state.fields]; + newFields[index].deploymentStatus = "error"; + newFields[index].deploymentError = error.message; + this.setState({fields: newFields}); + }); + }); + }; + + render() { + const {fields, showModal, showProfilesModal, currentFieldIndex, userInfo, selectedObject} = this.state; + + return ( + h("div", {onClick: (e) => this.setState({ + filteredObjects: [] + })}, + h("div", {id: "user-info"}, + h("a", {href: `https://${sfConn.instanceHostname}`, className: "sf-link"}, + h("svg", {viewBox: "0 0 24 24"}, + h("path", {d: "M18.9 12.3h-1.5v6.6c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-5.1h-3.6v5.1c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-6.6H5.1c-.1 0-.3-.1-.3-.2s0-.2.1-.3l6.9-7c.1-.1.3-.1.4 0l7 7v.3c0 .1-.2.2-.3.2z"}) + ), + " Salesforce Home" + ), + h("h1", {}, "Field Creator (beta)"), + h("span", {}, " / " + userInfo), + h("div", {className: "flex-right"}, + h("span", {className: "slds-assistive-text"}), + h("div", {className: "slds-spinner__dot-a"}), + h("div", {className: "slds-spinner__dot-b"}), + ), + h("a", {href: "https://tprouvot.github.io/Salesforce-Inspector-reloaded/field-creator/", target: "_blank", id: "help-btn", title: "Field Creator Help", onClick: null}, + h("div", {className: "icon"}) + ), + ), + h("div", {className: "relativePosition"}, + h("div", {className: "area firstHeader relativePosition zIndex1"}, + h("div", {className: "form-group"}, + h("label", {htmlFor: "object_select"}, "Select Object"), + selectedObject && h("a", { + href: selectedObject.name.endsWith("__mdt") + ? `https://${sfConn.instanceHostname}/lightning/setup/CustomMetadata/page?address=%2F${selectedObject.durableId}%3Fsetupid%3DCustomMetadata` + : `https://${sfConn.instanceHostname}/lightning/setup/ObjectManager/${selectedObject.name}/FieldsAndRelationships/view`, + target: "_blank", + className: "fieldsLink marginLeft10", + rel: "noopener noreferrer" + }, "(Fields)"), h("br", null), + h("div", {className: "relativePosition width400"}, + h("input", { + type: "text", + id: "object_select", + className: "form-control input-textBox width100", + placeholder: "Search and select object...", + value: this.state.objectSearch, + onChange: this.handleObjectSearch + }), + this.state.filteredObjects.length > 0 && h("ul", { + onClick: (e) => e.stopPropagation(), + className: "ulItem" + }, + this.state.filteredObjects.map(obj => + h("li", { + key: obj.name, + onClick: () => this.handleObjectSelect(obj), + className: "objectListItem" + }, + `${obj.name} (${obj.label})` + ) + ) + ) + ) + ), + h("br", null), + h("div", {className: "col-xs-12 text-center", id: "deploy"}, + h("button", {"aria-label": "Clear Button", className: "btn btn-large", onClick: this.clearAll}, "Clear All"), + h("button", {"aria-label": "Open Import modal button", className: "btn btn-large", onClick: this.openImportModal}, "Import"), + h("button", {"disabled": !this.state.selectedObject, "aria-label": "Deploy Button", className: "btn btn-large highlighted", onClick: this.deploy}, "Deploy Fields"), + !this.state.allFieldsHavePermissions && h("p", {className: "errorText"}, "Some fields are missing permissions."), + ) + ) + ), + h("div", {className: "area"}, + h(FieldsTable, { + fields, + onDelete: this.removeRow, + onClone: this.cloneRow, + onLabelChange: this.onLabelChange, + onNameChange: this.onNameChange, + onTypeChange: this.onTypeChange, + onEditOptions: this.onEditOptions, + onEditProfiles: this.onEditProfiles, + onShowDeploymentStatus: this.onShowDeploymentStatus + }), + h("button", {"aria-label": "Add Row/New field to table", className: "btn btn-sm highlighted maxWidth18", id: "add_row", onClick: this.addRow}, "Add Row") + ), + showProfilesModal && h(ProfilesModal, { + field: fields[currentFieldIndex], + permissionSets: this.state.permissionSets, + onSave: this.onSaveFieldProfiles, + onClose: this.onCloseProfilesModal, + onApplyToAllFields: this.applyToAllFields + }), + showModal && h(FieldOptionModal, { + field: fields[currentFieldIndex], + onSave: this.onSaveFieldOptions, + onClose: this.onCloseModal + }), + this.state.showImportModal && h("div", {onClick: this.closeImportModal, className: "modalOverlay"}, + h("div", {onClick: (e) => e.stopPropagation(), className: "modalContent"}, + h("div", {className: "modalHeader"}, + h("h2", null, "CSV Import (beta)"), + h("button", { + onClick: this.closeImportModal, + "aria-label": "Close Import Modal", + className: "closeButton" + }, "×") + ), + h("p", null, "Enter " + (localStorage.getItem("csvSeparator") || ",") + " separated values of Label, ApiName, Type."), + h("textarea", { + value: this.state.importCsvContent, + onChange: this.handleImportCsvChange, + className: "importTextarea" + }), + this.state.importError && h("p", {className: "errorText"}, this.state.importError), + h("div", {className: "modalFooter"}, + h("button", { + "aria-label": "Cancel button", + onClick: this.closeImportModal, + className: "marginRight10" + }, "Cancel"), + h("button", { + "aria-label": "Import button", + onClick: this.importCsv, + className: "btn btn-primary highlighted" + }, "Import") + ) + ) + ), + + this.state.fieldErrorMessage && h("div", {className: "notification_container"}, + h("div", {className: "slds-notify slds-notify_toast slds-theme_error notificationContent"}, + h("span", {className: "errorIcon"}, + h("svg", {className: "slds-icon width24px height24px", "aria-hidden": "true"}, + h("use", {xlinkHref: "symbols.svg#error", className: "iconFill"}) + ) + ), + h("span", {className: "slds-text-heading_small"}, + this.state.fieldErrorMessage, + this.state.errorMessageClickable && h("a", { + href: "#", + onClick: (e) => { + e.preventDefault(); + localStorage.setItem("enableEntityDefinitionCaching", true); + this.setState({fieldErrorMessage: null, errorMessageClickable: false}); + this.fetchObjects(); + }, + style: {color: "inherit", textDecoration: "underline"} + }, "Click here to enable") + ), + h("a", { + title: "Close", + onClick: () => this.setState({fieldErrorMessage: null, errorMessageClickable: false}), + className: "closeIcon" + }, + h("svg", {className: "slds-icon width24px height24px", "aria-hidden": "true"}, + h("use", {xlinkHref: "symbols.svg#close", className: "iconFill"}) + ) + ) + ) + )) + ); + } +} + +let args = new URLSearchParams(location.search.slice(1)); +let sfHost = args.get("host"); +initButton(sfHost, true); +sfConn.getSession(sfHost).then(() => { + let root = document.getElementById("root"); + ReactDOM.render( + h(App, { + sfHost + }), + root + ); +}); diff --git a/addon/inspect.css b/addon/inspect.css index 42ca7c399..aae462b1b 100644 --- a/addon/inspect.css +++ b/addon/inspect.css @@ -50,7 +50,7 @@ body, .prod { background-color: #e0a4b5; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_red_background.png'); + background-image: url(images/lightning_red_background.png); } [data-reactroot] { @@ -71,7 +71,7 @@ body { font-size: .8125rem; margin: 0; background-color: #B0C4DF; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_blue_background.png'); + background-image: url(images/lightning_blue_background.png); background-repeat: no-repeat; background-size: contain; background-position: 0px 48px; @@ -293,7 +293,7 @@ thead{ display: inline-block; width: 16px; height: 16px; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/settings.svg'); + -webkit-mask-image: url(images/settings.svg); margin: 3px 0px -3px 0px; } diff --git a/addon/inspect.js b/addon/inspect.js index 4eae4dae8..7a97d291d 100644 --- a/addon/inspect.js +++ b/addon/inspect.js @@ -40,7 +40,8 @@ class Model { this.objectSetupLinksRequested = false; this.popupTmpReactElement = undefined; this.popupReactElement = undefined; - if (localStorage.getItem(sfHost + "_isSandbox") != "true") { + let trialExpDate = localStorage.getItem(sfHost + "_trialExpirationDate"); + if (localStorage.getItem(sfHost + "_isSandbox") != "true" && (!trialExpDate || trialExpDate === "null")) { //change background color for production document.body.classList.add("prod"); } diff --git a/addon/inspector.js b/addon/inspector.js index cc7ae7f23..a7466e76e 100644 --- a/addon/inspector.js +++ b/addon/inspector.js @@ -1,4 +1,5 @@ -export let apiVersion = localStorage.getItem("apiVersion") == null ? "61.0" : localStorage.getItem("apiVersion"); +export let defaultApiVersion = "62.0"; +export let apiVersion = localStorage.getItem("apiVersion") == null ? defaultApiVersion : localStorage.getItem("apiVersion"); export let sessionError; export function nullToEmptyString(value) { // For react input fields, the value may not be null or undefined, so this will clean the value @@ -32,13 +33,14 @@ export let sfConn = { this.sessionId = message.key; } } - const IS_SANDBOX = "isSandbox"; - if (localStorage.getItem(sfHost + "_" + IS_SANDBOX) == null) { - sfConn.rest("/services/data/v" + apiVersion + "/query/?q=SELECT+IsSandbox,+InstanceName+FROM+Organization").then(res => { - localStorage.setItem(sfHost + "_" + IS_SANDBOX, res.records[0].IsSandbox); + if (localStorage.getItem(sfHost + "_trialExpirationDate") == null) { + sfConn.rest("/services/data/v" + apiVersion + "/query/?q=SELECT+IsSandbox,+InstanceName+,TrialExpirationDate+FROM+Organization").then(res => { + localStorage.setItem(sfHost + "_isSandbox", res.records[0].IsSandbox); localStorage.setItem(sfHost + "_orgInstance", res.records[0].InstanceName); + localStorage.setItem(sfHost + "_trialExpirationDate", res.records[0].TrialExpirationDate); }); } + return this.sessionId; }, async rest(url, {logErrors = true, method = "GET", api = "normal", body = undefined, bodyType = "json", responseType = "json", headers = {}, progressHandler = null} = {}, rawResponse) { @@ -49,8 +51,8 @@ export let sfConn = { let xhr = new XMLHttpRequest(); url += (url.includes("?") ? "&" : "?") + "cache=" + Math.random(); const sfHost = "https://" + this.instanceHostname; - xhr.open(method, sfHost + url, true); - + const fullUrl = new URL(url, sfHost); + xhr.open(method, fullUrl.toString(), true); xhr.setRequestHeader("Accept", "application/json; charset=UTF-8"); if (api == "bulk") { diff --git a/addon/lib/cometd/AckExtension.js b/addon/lib/cometd/AckExtension.js new file mode 100644 index 000000000..2cf226746 --- /dev/null +++ b/addon/lib/cometd/AckExtension.js @@ -0,0 +1 @@ +import{Extension as e}from"./Extension.js";export class AckExtension extends e{#a=!1;#b=0;#c(t,s){this.cometd._debug(t,s)}registered(e,t){super.registered(e,t),this.#c("AckExtension: executing registration callback")}unregistered(){this.#c("AckExtension: executing unregistration callback"),super.unregistered()}incoming(e){let t=e.channel,s=e.ext;if("/meta/handshake"===t){if(s){let c=s.ack;if("object"==typeof c){this.#a=!0===c.enabled;let n=c.batch;"number"==typeof n&&(this.#b=n)}else this.#a=!0===c}this.#c("AckExtension: server supports acknowledgements",this.#a)}else"/meta/connect"===t&&e.successful&&this.#a&&s&&"number"==typeof s.ack&&(this.#b=s.ack,this.#c("AckExtension: server sent batch",this.#b));return e}outgoing(e){let t=e.channel;return e.ext||(e.ext={}),"/meta/handshake"===t?(e.ext.ack=this.cometd&&!1!==this.cometd.ackEnabled,this.#a=!1,this.#b=0):"/meta/connect"===t&&this.#a&&(e.ext.ack=this.#b,this.#c("AckExtension: client sending batch",this.#b)),e}} diff --git a/addon/lib/cometd/BinaryExtension.js b/addon/lib/cometd/BinaryExtension.js new file mode 100644 index 000000000..6a2a47178 --- /dev/null +++ b/addon/lib/cometd/BinaryExtension.js @@ -0,0 +1 @@ +import{Extension as t}from"./Extension.js";import{Z85 as a}from"./Z85.js";export class BinaryExtension extends t{incoming(t){if(!/^\/meta\//.test(t.channel)){let e=t.ext;if(e){let n=e.binary;n&&(t.data.data=a.decode(t.data.data))}}return t}outgoing(t){if(!/^\/meta\//.test(t.channel)){let e=t.ext;if(e){let n=e.binary;n&&(t.data.data=a.encode(t.data.data))}}return t}} diff --git a/addon/lib/cometd/CallbackPollingTransport.js b/addon/lib/cometd/CallbackPollingTransport.js new file mode 100644 index 000000000..40bbf20c4 --- /dev/null +++ b/addon/lib/cometd/CallbackPollingTransport.js @@ -0,0 +1 @@ +import{RequestTransport as e}from"./RequestTransport.js";export class CallbackPollingTransport extends e{#a=0;accept(e,s,t){return!0}jsonpSend(e){console.log("jsonpSend...."),console.log("packet : "),console.log(e),console.log(e.url);let s=window.document.getElementsByTagName("head")[0],t=window.document.createElement("script"),n="_cometd_jsonp_"+this.#a++;window[n]=r=>{s.removeChild(t),delete window[n],e.onSuccess(r)};let r=e.url;r+=0>r.indexOf("?")?"?":"&",r+="jsonp="+n,r+="&message="+encodeURIComponent(e.body),t.src=r,t.async=!0!==e.sync,t.type="text/javascript",t.onerror=s=>{e.onError("jsonp "+s.type)},s.appendChild(t)}transportSend(e,s){let t=0,n=e.messages.length,r=[];for(;n>0;){let o=JSON.stringify(e.messages.slice(t,t+n)),i=e.url.length+encodeURI(o).length,a=this.configuration.maxURILength;if(i>a){if(1===n){let l="Bayeux message too big ("+i+" bytes, max is "+a+") for transport "+this;return this.setTimeout(()=>this.transportFailure(e,s,{exception:l}),0),!1}--n;continue}r.push(n),t+=n,n=e.messages.length-t}let c=e;if(r.length>1){let h=0,u=r[0];this.debug("Transport",this.type,"split",e.messages.length,"messages into",r.join(" + ")),(c=this.cometd._mixin(!1,{},e)).messages=e.messages.slice(h,u),c.onSuccess=e.onSuccess,c.onFailure=e.onFailure;for(let p=1;p{let t=!1;try{let n=this.convertToMessages(e);0===n.length?this.transportFailure(c,s,{httpCode:204}):(t=!0,this.transportSuccess(c,s,n))}catch(r){this.debug(r),t||this.transportFailure(c,s,{exception:r})}},onError:(e,t)=>{let n={reason:e,exception:t};m?this.setTimeout(()=>{this.transportFailure(c,s,n)},0):this.transportFailure(c,s,n)}}),m=!1,!0}catch(d){return this.setTimeout(()=>{this.transportFailure(c,s,{exception:d})},0),!1}}} diff --git a/addon/lib/cometd/Client.js b/addon/lib/cometd/Client.js new file mode 100644 index 000000000..61c0008bf --- /dev/null +++ b/addon/lib/cometd/Client.js @@ -0,0 +1 @@ +import{TransportRegistry as e}from"./TransportRegistry.js";import{CallbackPollingTransport as t}from"./CallbackPollingTransport.js";import{LongPollingTransport as s}from"./LongPollingTransport.js";import{WebSocketTransport as i}from"./WebSocketTransport.js";function Scheduler(){let e=0,t={};this.register=s=>{let i=++e;return t[i]=s,i},this.unregister=e=>{let s=t[e];return delete t[e],s},this.setTimeout=(e,t)=>window.setTimeout(e,t),this.clearTimeout=e=>{window.clearTimeout(e)}}function WorkerScheduler(){let e={};self.onmessage=t=>{let s=t.data,i=e[s.id];switch(s.type){case"setTimeout":e[s.id]=self.setTimeout(()=>{delete e[s.id],self.postMessage({id:s.id})},s.delay);break;case"clearTimeout":delete e[s.id],i&&self.clearTimeout(i);break;default:throw Error("Unknown command "+s.type)}}}export class CometD{#a=new Scheduler;#b;#c=!1;#d=new e;#e;#f="disconnected";#g=0;#h=null;#i=0;#j=[];#k=!1;#l=0;#m={};#n={};#o=0;#p=null;#q=[];#r={};#s;#t;#u={};#v={};#w=!1;#x=!1;#y=0;#z=0;#A=null;#B={useWorkerScheduler:!0,protocol:null,stickyReconnect:!0,connectTimeout:0,maxConnections:2,backoffIncrement:1e3,maxBackoff:6e4,logLevel:"info",maxNetworkDelay:1e4,requestHeaders:{},appendMessageTypeToURL:!0,autoBatch:!1,urls:{},maxURILength:2e3,maxSendBayeuxMessageSize:8192,advice:{timeout:6e4,interval:0,reconnect:void 0,maxInterval:0}};constructor(e){this.#b=e||"default",window.WebSocket&&this.registerTransport("websocket",new i),this.registerTransport("long-polling",new s),this.registerTransport("callback-polling",new t)}static #C(n,a){try{return n[a]}catch(o){return}}_mixin(e,t,s){let i=t||{};for(let n=2;n="A"&&h<="Z"||h>="a"&&h<="z"}static #F(c){return c>="0"&&c<="9"}static #G(l){switch(l){case" ":case"!":case"#":case"$":case"(":case")":case"*":case"+":case"-":case".":case"/":case"@":case"_":case"{":case"~":case"}":return!0;default:return!1}}static #H(u){if(!CometD.#D(u)||u.length<2||"/"!==u.charAt(0))return!1;for(let d=1;d0&&!(m>=Math.pow(10,p));)b+="0";return b+m}#K(k,$){if(window.console){let y=window.console[k];if(CometD.#I(y)){let C=new Date;[].splice.call($,0,0,CometD.#J(C.getHours(),2)+":"+CometD.#J(C.getMinutes(),2)+":"+CometD.#J(C.getSeconds(),2)+"."+CometD.#J(C.getMilliseconds(),3)),y.apply(window.console,$)}}}_warn(){this.#K("warn",arguments)}_info(){"warn"!==this.#B.logLevel&&this.#K("info",arguments)}_debug(){"debug"===this.#B.logLevel&&this.#K("debug",arguments)}static #L(x){return RegExp("(^https?://)?(((\\[[^\\]]+])|([^:/?#]+))(:(\\d+))?)?([^?#]*)(.*)?").exec(x)}#M(v){return!!window.location&&!!window.location.host&&!!v&&v!==window.location.host}#N(T){this._debug("Configuring cometd object with",T),CometD.#D(T)&&(T={url:T}),T||(T={}),this.#B=this._mixin(!1,this.#B,T);let D=this.getURL();if(!D)throw Error("Missing required configuration parameter 'url' specifying the Bayeux server URL");let w=CometD.#L(D),_=w[2],L=w[8],I=w[9];if(this.#c=this.#M(_),this.#B.appendMessageTypeToURL){if(void 0!==I&&I.length>0)this._info("Appending message type to URI",L,I,"is not supported, disabling 'appendMessageTypeToURL' configuration"),this.#B.appendMessageTypeToURL=!1;else{let S=L.split("/"),F=S.length-1;L.match(/\/$/)&&(F-=1),S[F].indexOf(".")>=0&&(this._info("Appending message type to URI",L,"is not supported, disabling 'appendMessageTypeToURL' configuration"),this.#B.appendMessageTypeToURL=!1)}}if(window.Worker&&window.Blob&&window.URL&&this.#B.useWorkerScheduler){let B=WorkerScheduler.toString();B=B.substring(B.indexOf("{")+1,B.lastIndexOf("}"));let R=new window.Blob([B],{type:"application/json"}),M=window.URL.createObjectURL(R),U=new window.Worker(M);this.#a.setTimeout=(e,t)=>{let s=this.#a.register(e);return U.postMessage({id:s,type:"setTimeout",delay:t}),s},this.#a.clearTimeout=e=>{this.#a.unregister(e),U.postMessage({id:e,type:"clearTimeout"})},U.onmessage=e=>{let t=e.data.id,s=this.#a.unregister(t);s&&s()}}}#O(E){if(E){let P=this.#m[E.channel];P&&P[E.id]&&(delete P[E.id],this._debug("Removed",E.listener?"listener":"subscription",E))}}#P(q){q&&!q.listener&&this.#O(q)}#Q(){for(let A in this.#m)if(this.#m.hasOwnProperty(A)){let O=this.#m[A];if(O)for(let j in O)O.hasOwnProperty(j)&&this.#P(O[j])}}#R(N){let z=this.getStatus();z!==N&&(this._debug("Status",z,"->",N),this.#f=N)}#S(){let H=this.#f;return"disconnecting"===H||"disconnected"===H}#T(){let W=++this.#g;return""+W}#U(Q,V,Z,G,J){try{return V.call(Q,G)}catch(K){let X=this.onExtensionException;if(CometD.#I(X)){this._debug("Invoking extension exception handler",Z,K);try{X.call(this,K,Z,J,G)}catch(Y){this._info("Exception during execution of extension exception handler",Z,Y)}}else this._info("Exception during execution of extension",Z,K);return G}}#V(ee){for(let et=0;et=0&&null!=ea;--eo){let er=this.#q[eo],eh=er.extension.outgoing;if(CometD.#I(eh)){let ec=this.#U(er.extension,eh,er.name,ea,!0);ea=void 0===ec?ea:ec}}return ea}#X(el,eu){let ed=this.#m[el];if(ed){for(let eg in ed)if(ed.hasOwnProperty(eg)){let ef=ed[eg];if(ef)try{ef.callback.call(ef.scope,eu)}catch(em){let ep=this.onListenerException;if(CometD.#I(ep)){this._debug("Invoking listener exception handler",ef,em);try{ep.call(this,em,ef,ef.listener,eu)}catch(eb){this._info("Exception during execution of listener exception handler",ef,eb)}}else this._info("Exception during execution of listener",ef,eu,em)}}}}#Y(ek,e$){this.#X(ek,e$);let ey=ek.split("/"),eC=ey.length-1;for(let ex=eC;ex>0;--ex){let ev=ey.slice(0,ex).join("/")+"/*";ex===eC&&this.#X(ev,e$),ev+="*",this.#X(ev,e$)}}#Z(){null!==this.#p&&this.clearTimeout(this.#p),this.#p=null}#$(eT,eD){this.#Z();let ew=this.#r.interval+eD;this._debug("Function scheduled in",ew,"ms, interval =",this.#r.interval,"backoff =",this.#o,eT),this.#p=this.setTimeout(eT,ew)}#_(e8,e_,eL){for(let eI=0;eI{try{this.#aa(e)}catch(t){this._info("Exception during handling of messages",t)}},onFailure:(e,t,s)=>{try{let i=this.getTransport();s.connectionType=i?i.type:"unknown",this.#ab(e,t,s)}catch(n){this._info("Exception during handling of failure",n)}}};this._debug("Send",eM),this.getTransport().send(eM,e_)}#ac(eU){this.#i>0||!0===this.#k?this.#j.push(eU):this.#_([eU],!1)}send(e){this.#ac(e)}#ad(){this.#o=0}#ae(){return this.#o0&&this.#_(eE,!1)}#ah(){if(--this.#i,this._debug("Ending batch, depth",this.#i),this.#i<0)throw Error("Calls to startBatch() and endBatch() are not paired");0!==this.#i||this.isDisconnected()||this.#k||this.#ag()}#ai(){if(!this.isDisconnected()){let eP={id:this.#T(),channel:"/meta/connect",connectionType:this.getTransport().type};this.#x||(eP.advice={timeout:0}),this.#R("connecting"),this._debug("Connect sent",eP),this.#_([eP],!0,"connect"),this.#R("connected")}}#aj(e0){this.#R("connecting"),this.#$(()=>{this.#ai()},e0)}#ak(eq){eq&&(this.#r=this._mixin(!1,{},this.#B.advice,eq),this._debug("New advice",this.#r))}#al(eA){this.#Z();let eO=this.getTransport();if(eA&&eO&&eO.abort(),this.#c=!1,this.#e=null,this.#R("disconnected"),this.#h=null,this.#i=0,this.#ad(),this.#w=!1,this.#x=!1,this.#y=0,this.#A=null,this.#j.length>0){let ej=this.#j;this.#j=[],this.#ab(void 0,ej,{reason:"Disconnected"})}}#am(eN,ez,eH){let eW=this.onTransportException;if(CometD.#I(eW)){this._debug("Invoking transport exception handler",eN,ez,eH);try{eW.call(this,eH,eN,ez)}catch(eQ){this._info("Exception during execution of transport exception handler",eQ)}}}#an(eV,e4){CometD.#I(eV)&&(e4=eV,eV=void 0),this.#h=null,this.clearSubscriptions(),this.isDisconnected()&&this.#d.reset(!0),this.#ak({}),this.#i=0,this.#k=!0,this.#s=eV,this.#t=e4;let e7=this.getURL(),e9=this.#d.findTransportTypes("1.0",this.#c,e7),e6={id:this.#T(),version:"1.0",minimumVersion:"1.0",channel:"/meta/handshake",supportedConnectionTypes:e9,advice:{timeout:this.#r.timeout,interval:this.#r.interval}},eZ=this._mixin(!1,{},this.#s,e6);if(this._putCallback(eZ.id,e4),!this.#e&&(this.#e=this.#d.negotiateTransport(e9,"1.0",this.#c,e7),!this.#e)){let e1="Could not find initial transport among: "+this.getTransportTypes();throw this._warn(e1),Error(e1)}this._debug("Initial transport is",this.#e.type),this.#R("handshaking"),this._debug("Handshake sent",eZ),this.#_([eZ],!1,"handshake")}#ao(e2){this.#R("handshaking"),this.#k=!0,this.#$(()=>{this.#an(this.#s,this.#t)},e2)}#ap(eG,eJ){try{eG.call(this,eJ)}catch(eK){let eX=this.onCallbackException;if(CometD.#I(eX)){this._debug("Invoking callback exception handler",eK);try{eX.call(this,eK,eJ)}catch(eY){this._info("Exception during execution of callback exception handler",eY)}}else this._info("Exception during execution of message callback",eK)}}_getCallback(e){return this.#u[e]}_putCallback(e,t){let s=this._getCallback(e);return CometD.#I(t)&&(this.#u[e]=t),s}#aq(e3){let e5=this._getCallback([e3.id]);CometD.#I(e5)&&(delete this.#u[e3.id],this.#ap(e5,e3))}#ar(te){let tt=this.#v[te.id];if(delete this.#v[te.id],tt){this._debug("Handling remote call response for",te,"with context",tt);let ts=tt.timeout;ts&&this.clearTimeout(ts);let ti=tt.callback;if(CometD.#I(ti))return this.#ap(ti,te),!0}return!1}onTransportFailure(e,t,s){this._debug("Transport failure",t,"for",e);let i=this.getTransportRegistry(),n=this.getURL(),a=this.#M(CometD.#L(n)[2]),o=i.findTransportTypes("1.0",a,n);if("none"===t.action){if("/meta/handshake"===e.channel&&!t.transport){let r="Could not negotiate transport, client=["+o+"], server=["+e.supportedConnectionTypes+"]";this._warn(r);let h=this.getTransport();if(h){let c=h.type;this.#am(c,null,{reason:r,connectionType:c,transport:h})}}}else if(t.delay=this.getBackoffPeriod(),"/meta/handshake"===e.channel){if(!t.transport){let l=this.#e?this.#e.type:null,u=i.negotiateTransport(o,"1.0",a,n);if(u){let d=u.type;this._debug("Transport",l,"->",d),this.#am(l,d,e.failure),t.action="handshake",t.transport=u}else this._warn("Could not negotiate transport, client=["+o+"]"),this.#am(l,null,e.failure),t.action="none"}"none"!==t.action&&this.increaseBackoffPeriod()}else{let g=new Date().getTime();if(0===this.#y&&(this.#y=g),"retry"===t.action){t.delay=this.increaseBackoffPeriod();let f=this.#r.maxInterval;if(f>0){let m=this.#r.timeout+this.#r.interval+f,p=g-this.#y;p+this.#o>m&&(t.action="handshake")}}"handshake"===t.action&&(t.delay=0,i.reset(!1),this.resetBackoffPeriod())}s.call(this,t)}#as(tn){this._debug("Transport failure handling",tn),tn.transport&&(this.#e=tn.transport),tn.url&&(this.#e.url=tn.url);let ta=tn.action,to=tn.delay||0;switch(ta){case"handshake":this.#ao(to);break;case"retry":this.#aj(to);break;case"none":this.#al(!0);break;default:throw Error("Unknown action "+ta)}}#at(tr,th){this.#aq(tr),this.#Y("/meta/handshake",tr),this.#Y("/meta/unsuccessful",tr),this.isDisconnected()&&(th.action="none"),this.onTransportFailure(tr,th,e=>this.#as(e))}#au(tc){let tl=this.getURL();if(tc.successful){let tu=this.#M(CometD.#L(tl)[2]),td=this.#d.negotiateTransport(tc.supportedConnectionTypes,tc.version,tu,tl);if(null===td){tc.successful=!1,this.#at(tc,{cause:"negotiation",action:"none",transport:null});return}{let tg=this.#e;tg!==td&&(this._debug("Transport",tg&&tg.type,"->",td.type),this.#e=td)}this.#h=tc.clientId,this.#k=!1,this.#ag(),tc.reestablish=this.#w,this.#w=!0,this.#aq(tc),this.#Y("/meta/handshake",tc),this.#z=tc["x-messages"]||0;let tf=this.isDisconnected()?"none":this.#r.reconnect||"retry";switch(tf){case"retry":this.#ad(),0===this.#z?this.#aj(0):this._debug("Processing",this.#z,"handshake-delivered messages");break;case"none":this.#al(!0);break;default:throw Error("Unrecognized advice action "+tf)}}else this.#at(tc,{cause:"unsuccessful",action:this.#r.reconnect||"handshake",transport:this.getTransport()})}#av(tm){this.#at(tm,{cause:"failure",action:"handshake",transport:null})}#aw(tp){return"disconnected"===this.getStatus()||!!this.#A&&this.#A.id===tp.id&&(this.#A=null,!0)}#ax(tb,tk){this.#Y("/meta/connect",tb),this.#Y("/meta/unsuccessful",tb),this.isDisconnected()&&(tk.action="none"),this.onTransportFailure(tb,tk,e=>this.#as(e))}#ay(t$){if(this.#aw(t$)){if(this.#x=t$.successful,this.#x){this.#Y("/meta/connect",t$);let ty=this.isDisconnected()?"none":this.#r.reconnect||"retry";switch(ty){case"retry":this.#ad(),this.#aj(this.#o);break;case"none":this.#al(!1);break;default:throw Error("Unrecognized advice action "+ty)}}else this.#ax(t$,{cause:"unsuccessful",action:this.#r.reconnect||"retry",transport:this.getTransport()})}else this._debug("Mismatched /meta/connect reply",t$)}#az(tC){this.#aw(tC)?(this.#x=!1,this.#ax(tC,{cause:"failure",action:"retry",transport:null})):this._debug("Mismatched /meta/connect failure",tC)}#aA(tx){this.#al(!0),this.#aq(tx),this.#Y("/meta/disconnect",tx),this.#Y("/meta/unsuccessful",tx)}#aB(tv){tv.successful?(this.#al(!1),this.#aq(tv),this.#Y("/meta/disconnect",tv)):this.#aA(tv)}#aC(tT){this.#aA(tT)}#aD(tD){let tw=this.#m[tD.subscription];if(tw){for(let t8 in tw)if(tw.hasOwnProperty(t8)){let t_=tw[t8];t_&&!t_.listener&&(delete tw[t8],this._debug("Removed failed subscription",t_))}}this.#aq(tD),this.#Y("/meta/subscribe",tD),this.#Y("/meta/unsuccessful",tD)}#aE(tL){tL.successful?(this.#aq(tL),this.#Y("/meta/subscribe",tL)):this.#aD(tL)}#aF(tI){this.#aD(tI)}#aG(tS){this.#aq(tS),this.#Y("/meta/unsubscribe",tS),this.#Y("/meta/unsuccessful",tS)}#aH(tF){tF.successful?(this.#aq(tF),this.#Y("/meta/unsubscribe",tF)):this.#aG(tF)}#aI(tB){this.#aG(tB)}#aJ(tR){this.#ar(tR)||(this.#aq(tR),this.#Y("/meta/publish",tR),this.#Y("/meta/unsuccessful",tR))}#aK(tM){void 0!==tM.data?!this.#ar(tM)&&(this.#Y(tM.channel,tM),this.#z>0&&(--this.#z,0===this.#z&&(this._debug("Processed last handshake-delivered message"),this.#aj(0)))):void 0===tM.successful?this._warn("Unknown Bayeux Message",tM):tM.successful?(this.#aq(tM),this.#Y("/meta/publish",tM)):this.#aJ(tM)}#aL(tU){this.#aJ(tU)}#aM(tE){if(this.#y=0,void 0===(tE=this.#V(tE))||null===tE)return;this.#ak(tE.advice);let tP=tE.channel;switch(tP){case"/meta/handshake":this.#au(tE);break;case"/meta/connect":this.#ay(tE);break;case"/meta/disconnect":this.#aB(tE);break;case"/meta/subscribe":this.#aE(tE);break;case"/meta/unsubscribe":this.#aH(tE);break;default:this.#aK(tE)}}receive(e){this.#aM(e)}#aa(t0){this._debug("Received",t0);for(let tq=0;tq=0)return s.splice(i,1),!0}return!1}_getTransportListeners(e){return this.#n[e]}addListener(e,t,s){if(arguments.length<2)throw Error("Illegal arguments number: required 2, got "+arguments.length);if(!CometD.#D(e))throw Error("Illegal argument type: channel must be a string");return this.#aP(e,t,s,!0)}removeListener(e){if(!e||!e.channel||!("id"in e))throw Error("Invalid argument: expected subscription, not "+e);this.#O(e)}clearListeners(){this.#m={}}subscribe(e,t,s,i,n){if(arguments.length<2)throw Error("Illegal arguments number: required 2, got "+arguments.length);if(!CometD.#H(e))throw Error("Illegal argument: invalid channel "+e);if(this.isDisconnected())throw Error("Illegal state: disconnected");CometD.#I(t)&&(n=i,i=s,s=t,t=void 0),CometD.#I(i)&&(n=i,i=void 0);let a=!this.#aN(e),o=this.#aP(e,t,s,!1);if(a){let r={id:this.#T(),channel:"/meta/subscribe",subscription:e},h=this._mixin(!1,{},i,r);this._putCallback(h.id,n),this.#ac(h)}else CometD.#I(n)&&this.setTimeout(()=>{this.#ap(n,{id:this.#T(),successful:!0,channel:"/meta/subscribe",subscription:e})},0);return o}unsubscribe(e,t,s){if(arguments.length<1)throw Error("Illegal arguments number: required 1, got "+arguments.length);if(this.isDisconnected())throw Error("Illegal state: disconnected");CometD.#I(t)&&(s=t,t=void 0),this.removeListener(e);let i=e.channel;if(this.#aN(i))CometD.#I(s)&&this.setTimeout(()=>{this.#ap(s,{id:this.#T(),successful:!0,channel:"/meta/unsubscribe",subscription:i})},0);else{let n={id:this.#T(),channel:"/meta/unsubscribe",subscription:i},a=this._mixin(!1,{},t,n);this._putCallback(a.id,s),this.#ac(a)}}resubscribe(e,t){if(this.#P(e),e)return this.subscribe(e.channel,e.scope,e.callback,t)}clearSubscriptions(){this.#Q()}publish(e,t,s,i){if(arguments.length<1)throw Error("Illegal arguments number: required 1, got "+arguments.length);if(!CometD.#H(e))throw Error("Illegal argument: invalid channel "+e);if(/^\/meta\//.test(e))throw Error("Illegal argument: cannot publish to meta channels");if(this.isDisconnected())throw Error("Illegal state: disconnected");CometD.#I(t)?(i=t,t={},s=void 0):CometD.#I(s)&&(i=s,s=void 0);let n={id:this.#T(),channel:e,data:t},a=this._mixin(!1,{},s,n);this._putCallback(a.id,i),this.#ac(a)}publishBinary(e,t,s,i,n,a){CometD.#I(t)?(a=t,t=new ArrayBuffer(0),s=!0,i=void 0,n=void 0):CometD.#I(s)?(a=s,s=!0,i=void 0,n=void 0):CometD.#I(i)?(a=i,i=void 0,n=void 0):CometD.#I(n)&&(a=n,n=void 0);let o={meta:i,data:t,last:s},r=this._mixin(!1,n,{ext:{binary:{}}});this.publish(e,o,r,a)}remoteCall(e,t,s,i,n){if(arguments.length<1)throw Error("Illegal arguments number: required 1, got "+arguments.length);if(!CometD.#D(e))throw Error("Illegal argument type: target must be a string");if(this.isDisconnected())throw Error("Illegal state: disconnected");if(CometD.#I(t)?(n=t,t={},s=this.#B.maxNetworkDelay,i=void 0):CometD.#I(s)?(n=s,s=this.#B.maxNetworkDelay,i=void 0):CometD.#I(i)&&(n=i,i=void 0),"number"!=typeof s)throw Error("Illegal argument type: timeout must be a number");e.match(/^\//)||(e="/"+e);let a="/service"+e;if(!CometD.#H(a))throw Error("Illegal argument: invalid target "+e);let o={id:this.#T(),channel:a,data:t},r=this._mixin(!1,{},i,o),h={callback:n};s>0&&(h.timeout=this.setTimeout(()=>{this._debug("Timing out remote call",r,"after",s,"ms"),this.#aJ({id:r.id,error:"406::timeout",successful:!1,failure:{message:r,reason:"Remote Call Timeout"}})},s),this._debug("Scheduled remote call timeout",r,"in",s,"ms")),this.#v[r.id]=h,this.#ac(r)}remoteCallBinary(e,t,s,i,n,a,o){CometD.#I(t)?(o=t,t=new ArrayBuffer(0),s=!0,i=void 0,n=this.#B.maxNetworkDelay,a=void 0):CometD.#I(s)?(o=s,s=!0,i=void 0,n=this.#B.maxNetworkDelay,a=void 0):CometD.#I(i)?(o=i,i=void 0,n=this.#B.maxNetworkDelay,a=void 0):CometD.#I(n)?(o=n,n=this.#B.maxNetworkDelay,a=void 0):CometD.#I(a)&&(o=a,a=void 0);let r={meta:i,data:t,last:s},h=this._mixin(!1,a,{ext:{binary:{}}});this.remoteCall(e,r,n,h,o)}getStatus(){return this.#f}isDisconnected(){return this.#S()}setBackoffIncrement(e){this.#B.backoffIncrement=e}getBackoffIncrement(){return this.#B.backoffIncrement}getBackoffPeriod(){return this.#o}increaseBackoffPeriod(){return this.#ae()}resetBackoffPeriod(){this.#ad()}setLogLevel(e){this.#B.logLevel=e}registerExtension(e,t){if(arguments.length<2)throw Error("Illegal arguments number: required 2, got "+arguments.length);if(!CometD.#D(e))throw Error("Illegal argument type: extension name must be a string");let s=!1;for(let i=0;i{try{this._debug("Invoking timed function",e),e()}catch(t){this._debug("Exception invoking timed function",e,t)}},t)}clearTimeout(e){this.#a.clearTimeout(e)}} diff --git a/addon/lib/cometd/Extension.js b/addon/lib/cometd/Extension.js new file mode 100644 index 000000000..c65097f78 --- /dev/null +++ b/addon/lib/cometd/Extension.js @@ -0,0 +1 @@ +export class Extension{#a;registered(e,t){this.#a=t}unregistered(){this.#a=null}get cometd(){return this.#a}incoming(e){return e}outgoing(e){return e}} diff --git a/addon/lib/cometd/LongPollingTransport.js b/addon/lib/cometd/LongPollingTransport.js new file mode 100644 index 000000000..b1baf8564 --- /dev/null +++ b/addon/lib/cometd/LongPollingTransport.js @@ -0,0 +1 @@ +import{RequestTransport as t}from"./RequestTransport.js";export class LongPollingTransport extends t{#a=!0;accept(t,s,e){return this.#a||!s}#b(){return new window.XMLHttpRequest}#c(s){try{s.context=this.context}catch(e){this.debug("Could not copy transport context into XHR",e)}}xhrSend(t){let s=this.#b();this.#c(s),s.withCredentials=!0,s.open("POST",t.url,!0!==t.sync);let e=t.headers;if(e)for(let r in e)e.hasOwnProperty(r)&&s.setRequestHeader(r,e[r]);return s.setRequestHeader("Content-Type","application/json"),s.onload=()=>{200===s.status?t.onSuccess(s.responseText):t.onError(s.statusText)},s.onabort=s.onerror=()=>{t.onError(s.statusText)},s.send(t.body),s}transportSend(t,s){this.debug("Transport",this.type,"sending request",s.id,"envelope",t);try{let e=!0;return s.xhr=this.xhrSend({transport:this,url:t.url,sync:t.sync,headers:this.configuration.requestHeaders,body:this.convertToJSON(t.messages),onSuccess:e=>{this.debug("Transport",this.type,"received response",e);let r=!1;try{let o=this.convertToMessages(e);0===o.length?(this.#a=!1,this.transportFailure(t,s,{httpCode:204})):(r=!0,this.transportSuccess(t,s,o))}catch(n){if(this.debug(n),!r){this.#a=!1;let i={exception:n};i.httpCode=this.xhrStatus(s.xhr),this.transportFailure(t,s,i)}}},onError:(r,o)=>{this.debug("Transport",this.type,"received error",r,o),this.#a=!1;let n={reason:r,exception:o};n.httpCode=this.xhrStatus(s.xhr),e?this.setTimeout(()=>{this.transportFailure(t,s,n)},0):this.transportFailure(t,s,n)}}),e=!1,!0}catch(r){return this.debug("Transport",this.type,"exception:",r),this.#a=!1,this.setTimeout(()=>{this.transportFailure(t,s,{exception:r})},0),!1}}reset(t){super.reset(t),this.#a=!0}} diff --git a/addon/lib/cometd/ReloadExtension.js b/addon/lib/cometd/ReloadExtension.js new file mode 100644 index 000000000..b52356a07 --- /dev/null +++ b/addon/lib/cometd/ReloadExtension.js @@ -0,0 +1 @@ +import{Extension as e}from"./Extension.js";export class ReloadExtension extends e{#a={};#b="org.cometd.reload";#c=!1;#d=!1;constructor(e){super(),this.configure(e)}#e(t){if(this.#a.handshakeResponse){this.#d=!0;let s=this.cometd.getTransport();s&&s.abort(),this.configure(t);let a=JSON.stringify(this.#a);this.cometd._debug("Reload extension saving state",a),window.sessionStorage.setItem(this.#b,a)}}#f(n){return this.#a.url===n.url}configure(e){e&&"string"==typeof e.name&&(this.#b=e.name)}_receive(e){this.cometd.receive(e)}registered(e,t){super.registered(e,t),this.cometd.reload=e=>this.#e(e),this.cometd.addListener("/meta/handshake",e=>this.#g(e))}unregistered(){delete this.cometd.reload,super.unregistered()}outgoing(e){switch(e.channel){case"/meta/handshake":{this.#a={url:this.cometd.getURL()};let t=window.sessionStorage.getItem(this.#b);if(this.cometd._debug("Reload extension found state",t),t)try{let s=JSON.parse(t);if(window.sessionStorage.removeItem(this.#b),s.handshakeResponse&&this.#f(s)){this.cometd._debug("Reload extension restoring state",s);let a=this.cometd._getCallback(e.id);return this.cometd.setTimeout(()=>{this.cometd._debug("Reload extension replaying handshake response",s.handshakeResponse),this.#a.handshakeResponse=s.handshakeResponse,this.#a.transportType=s.transportType,this.cometd._putCallback(e.id,a);let t=this.cometd._mixin(!0,{},this.#a.handshakeResponse,{id:e.id,ext:{reload:!0}});t.supportedConnectionTypes=[this.#a.transportType],this._receive(t),this.cometd._debug("Reload extension replayed handshake response",t)},0),this.#c||(this.#c=!0,this.cometd.startBatch()),null}this.cometd._debug("Reload extension could not restore state",s)}catch(n){this.cometd._debug("Reload extension error while trying to restore state",n)}break}case"/meta/connect":if(!0===this.#d)return this.cometd._debug("Reload extension aborting /meta/connect during reload"),null;this.#a.transportType||(this.#a.transportType=e.connectionType,this.cometd._debug("Reload extension tracked transport type",this.#a.transportType));break;case"/meta/disconnect":this.#a={}}return e}incoming(e){switch(e.channel){case"/meta/handshake":e.successful&&!this.#a.handshakeResponse&&(this.#a.handshakeResponse=e,this.cometd._debug("Reload extension tracked handshake response",e));break;case"/meta/connect":this.#c&&(this.#c=!1,this.cometd.endBatch());break;case"/meta/disconnect":this.#c&&(this.#c=!1,this.cometd.endBatch()),this.#a={}}return e}#g(i){!0!==i.successful&&(this.#a={})}} diff --git a/addon/lib/cometd/RequestTransport.js b/addon/lib/cometd/RequestTransport.js new file mode 100644 index 000000000..34a859f41 --- /dev/null +++ b/addon/lib/cometd/RequestTransport.js @@ -0,0 +1 @@ +import{Transport as e}from"./Transport.js";export class RequestTransport extends e{#a=0;#b=null;#c=[];#d=[];#e(t){for(;this.#d.length>0;){let s=this.#d[0],i=s[0],r=s[1];if(i.url===t.url&&i.sync===t.sync){this.#d.shift(),t.messages=t.messages.concat(i.messages),this.debug("Coalesced",i.messages.length,"messages from request",r.id);continue}break}}#f(n,o,a){let h=this.notifyTransportTimeout(n.messages);if(h>0)this.debug("Transport",this.type,"extended waiting for message replies of request",o.id,":",h,"ms"),o.timeout=this.setTimeout(()=>{this.#f(n,o,a+h)},h);else{o.expired=!0;let u="Transport "+this+" expired waiting for message replies of request "+o.id+": "+a+" ms",l={reason:u},p=o.xhr;l.httpCode=this.xhrStatus(p),this.abortXHR(p),this.debug(u),this.complete(o,!1,o.metaConnect),n.onFailure(p,n.messages,l)}}#g(c,m){if(this.transportSend(c,m)&&(m.expired=!1,!c.sync)){let d=this.configuration.maxNetworkDelay;!0===m.metaConnect&&(d+=this.advice.timeout),this.debug("Transport",this.type,"started waiting for message replies of request",m.id,":",d,"ms"),m.timeout=this.setTimeout(()=>{this.#f(c,m,d)},d)}}#h(g){let q=++this.#a,f={id:q,metaConnect:!1,envelope:g};this.#c.length=0&&this.#c.splice(y,1),this.#d.length>0){let $=this.#d.shift(),v=$[0],R=$[1];this.debug("Transport dequeued request",R.id),x?(this.configuration.autoBatch&&this.#e(v),this.#h(v),this.debug("Transport",this.type,"completed request",T.id,v)):this.setTimeout(()=>{this.complete(R,!1,R.metaConnect);let e={reason:"Previous request failed"},t=R.xhr;e.httpCode=this.xhrStatus(t),v.onFailure(t,v.messages,e)},0)}}complete(e,t,s){s?this.#i(e):this.#j(e,t)}transportSend(e,t){throw Error("Abstract")}transportSuccess(e,t,s){t.expired||(this.clearTimeout(t.timeout),this.debug("Transport",this.type,"cancelled waiting for message replies"),this.complete(t,!0,t.metaConnect),s&&s.length>0?e.onSuccess(s):e.onFailure(t.xhr,e.messages,{httpCode:204}))}transportFailure(e,t,s){t.expired||(this.clearTimeout(t.timeout),this.debug("Transport",this.type,"cancelled waiting for failed message replies"),this.complete(t,!1,t.metaConnect),e.onFailure(t.xhr,e.messages,s))}#k(S){if(null!==this.#b)throw Error("Concurrent /meta/connect requests not allowed, request id="+this.#b.id+" not yet completed");let _=++this.#a;this.debug("Transport",this.type,"/meta/connect send, request",_,"envelope",S);let w={id:_,metaConnect:!0,envelope:S};this.#g(S,w),this.#b=w}send(e,t){t?this.#k(e):this.#h(e)}abort(){super.abort();for(let e=0;ethis.#a&&(this.#c.shift(),this.#b.shift());let m=this.#c.length,g=0,o=0;for(let h=0;h0)return o}catch(n){this.cometd._info("Exception during execution of transport listener",i,n)}}return 0}debug(){this.cometd._debug.apply(this.cometd,arguments)}get configuration(){return this.cometd.getConfiguration()}get advice(){return this.cometd.getAdvice()}setTimeout(t,e){return this.cometd.setTimeout(t,e)}clearTimeout(t){this.cometd.clearTimeout(t)}convertToJSON(t){let e=this.configuration.maxSendBayeuxMessageSize,r="[";for(let i=0;i0&&(r+=",");let o=t[i],n=JSON.stringify(o);if(n.length>e)throw Error("maxSendBayeuxMessageSize "+e+" exceeded");r+=n}return r+"]"}convertToMessages(t){if(null==t)return[];if("string"==typeof t||t instanceof String)try{return JSON.parse(t)}catch(e){throw this.debug("Could not convert to JSON the following string:",t),e}if(Array.isArray(t))return t;if(t instanceof Object)return[t];throw Error("Conversion Error "+t+", typeof "+typeof t)}accept(t,e,r){throw Error("Abstract")}get type(){return this.#a}get url(){return this.#c}set url(t){this.#c=t}send(t,e){throw Error("Abstract")}reset(t){this.debug("Transport",this.type,"reset",t?"initial":"retry")}abort(){this.debug("Transport",this.type,"aborted")}toString(){return this.type}} diff --git a/addon/lib/cometd/TransportRegistry.js b/addon/lib/cometd/TransportRegistry.js new file mode 100644 index 000000000..68c0a1a07 --- /dev/null +++ b/addon/lib/cometd/TransportRegistry.js @@ -0,0 +1 @@ +export class TransportRegistry{#a=[];#b={};getTransportTypes(){return this.#a.slice(0)}add(t,s,e){let r=!1;for(let p=0;p=0){d=!0,$.splice(T,1);let k=u[b][0],w=u[b][1];delete u[b],$.length>0&&(u[$.join(",")]=[k,w]);break}}}d&&this.debug("Transport",this.type,"removed envelope, envelopes",u)}#n(y){if(this.#e)return;let f=this.cometd.getURL().replace(/^http/,"ws");this.debug("Transport",this.type,"connecting to URL",f);try{let S=this.configuration.protocol;y.webSocket=S?new window.WebSocket(f,S):new window.WebSocket(f),this.#e=y}catch(x){throw this.#a=!1,this.debug("Exception while creating WebSocket object",x),x}this.#c=!1!==this.configuration.stickyReconnect;let v=this.configuration.connectTimeout;v>0&&(y.connectTimer=this.setTimeout(()=>{this.debug("Transport",this.type,"timed out while connecting to URL",f,":",v,"ms"),this.#h(y,{code:1e3,reason:"Connect Timeout"})},v));let _=()=>{this.debug("Transport",this.type,"onopen",y),y.connectTimer&&this.clearTimeout(y.connectTimer),this.#k(y)?(this.#e=null,this.#d=y,this.#b=!0,this.#o(y)):(this.cometd._warn("Closing extra WebSocket connection",this,"active connection",this.#d),this.#h(y,{code:1e3,reason:"Extra Connection"}))},C=e=>{e=e||{code:1e3},this.debug("Transport",this.type,"onclose",y,e,"connecting",this.#e,"current",this.#d),y.connectTimer&&this.clearTimeout(y.connectTimer),this.#j(y,e)},E=e=>{this.debug("Transport",this.type,"onmessage",e,y),this.#p(y,e)};y.webSocket.onopen=_,y.webSocket.onclose=C,y.webSocket.onerror=()=>{C({code:1e3,reason:"Error"})},y.webSocket.onmessage=E,this.debug("Transport",this.type,"configured callbacks on",y)}#q(O,R,W){let F=this.notifyTransportTimeout([R]);F>0?(this.debug("Transport",this.type,"extended waiting for message replies:",F,"ms"),O.timeouts[R.id]=this.setTimeout(()=>{this.#q(O,R,W+F)},F)):(this.debug("Transport",this.type,"expired waiting for message reply",R.id,":",W,"ms"),this.#h(O,{code:1e3,reason:"Message Timeout"}))}#r(j,P,L){let M;try{M=this.convertToJSON(P.messages)}catch(U){this.debug("Transport",this.type,"exception:",U);let D=[];for(let N=0;N{this._notifyFailure(P.onFailure,j,P.messages,{exception:U})},0);return}j.webSocket.send(M),this.debug("Transport",this.type,"sent",P,"/meta/connect =",L);let I=this.configuration.maxNetworkDelay;L&&(I+=this.advice.timeout,this.#f=!0);let J=[];for(let q=0;q{this.#q(j,z,I)},I))}this.debug("Transport",this.type,"started waiting for message replies",I,"ms, messageIds:",J,", timeouts:",j.timeouts)}_notifySuccess(e,t){e.call(this,t)}_notifyFailure(e,t,s,o){e.call(this,t,s,o)}#s(B,G,H){try{null===B?(B=this.#e||{envelopes:{},timeouts:{}},this.#l(B,G,H),this.#n(B)):(this.#l(B,G,H),this.#r(B,G,H))}catch(K){this.setTimeout(()=>{this.#h(B,{code:1e3,reason:"Exception",exception:K})},0)}}#o(Q){let V=Q.envelopes;for(let X in this.debug("Transport",this.type,"opened",Q,"pending messages",V),V)if(V.hasOwnProperty(X)){let Y=V[X],Z=Y[0],ee=Y[1];this.#g=Z.onSuccess,this.#r(Q,Z,ee)}}#p(et,es){this.debug("Transport",this.type,"received websocket message",es,et);let eo=!1,ei=this.convertToMessages(es.data),en=[];for(let ec=0;ec","(",")","[","]","{","}","@","%","$","#"];static #b=[0,68,0,84,83,82,72,0,75,76,70,65,0,63,62,69,0,1,2,3,4,5,6,7,8,9,64,0,73,66,74,71,81,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,77,0,78,67,0,0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,79,0,80,0,0];static encode($){let x=null;if($ instanceof ArrayBuffer?x=$:$.buffer instanceof ArrayBuffer?x=$.buffer:Array.isArray($)&&(x=new Uint8Array($).buffer),null==x)throw Error("Cannot Z85 encode "+$);let e=x.byteLength,t=e%4,l=4-(0===t?4:t),_=new DataView(x),n="",o=0;for(let f=0;f=e;if(o=256*o+(r?0:_.getUint8(f)),(f+1)%4==0){let c=52200625;for(let a=5;a>0;--a){if(!r||a>l){let i=Math.floor(o/c)%85;n+=Z85.#a[i]}c/=85}o=0}}return n}static decode($){let x=$.length%5,e=5-(0===x?5:x);for(let t=0;t=1;)r void; + +export type LogLevel = "warn" | "info" | "debug"; + +export interface Configuration { + url: string; + logLevel?: LogLevel; + useWorkerScheduler?: boolean; + protocol?: string; + stickyReconnect?: boolean; + connectTimeout?: number; + maxConnections?: number; + backoffIncrement?: number; + maxBackoff?: number; + maxNetworkDelay?: number; + requestHeaders?: object; + appendMessageTypeToURL?: boolean; + autoBatch?: boolean; + urls?: object; + maxURILength?: number; + maxSendBayeuxMessageSize?: number; + advice?: Advice; +} + +export interface ListenerHandle { +} + +export interface SubscriptionHandle { +} + +export type Status = "disconnected" | "handshaking" | "connecting" | "connected" | "disconnecting"; + +export interface Extension { + incoming?(message: Message): Message | null; + outgoing?(message: Message): Message | null; + registered?(name: string, cometd: CometD): void; + unregistered?(): void; +} + +export class CometD { + constructor(name?: string); + + registerTransport(type: string, transport: Transport, index?: number): boolean; + + unregisterTransport(type: string): Transport | null; + + unregisterTransports(): void; + + getTransportTypes(): string[]; + + findTransport(name: string): Transport | null; + + getTransportRegistry(): TransportRegistry; + + configure(options: Configuration | string): void; + + handshake(handshakeCallback?: Callback): void; + handshake(handshakeProps: object, handshakeCallback?: Callback): void; + + disconnect(disconnectCallback?: Callback): void; + disconnect(disconnectProps: object, disconnectCallback?: Callback): void; + + batch(group: () => void): void; + + addListener(channel: string, messageCallback: Callback): ListenerHandle; + + removeListener(handle: ListenerHandle): void; + + clearListeners(): void; + + subscribe(channel: string, messageCallback: Callback, subscribeCallback?: Callback): SubscriptionHandle; + subscribe(channel: string, messageCallback: Callback, subscribeProps: object, subscribeCallback?: Callback): SubscriptionHandle; + + unsubscribe(handle: SubscriptionHandle, unsubscribeCallback?: Callback): void; + unsubscribe(handle: SubscriptionHandle, unsubscribeProps: object, unsubscribeCallback?: Callback): void; + + resubscribe(handle: SubscriptionHandle, subscribeProps?: object): SubscriptionHandle; + + clearSubscriptions(): void; + + publish(channel: string, content: any, publishCallback?: Callback): void; + publish(channel: string, content: any, publishProps: object, publishCallback?: Callback): void; + + publishBinary(channel: string, data: any, last: boolean, publishCallback?: Callback): void; + publishBinary(channel: string, data: any, last: boolean, meta: object, publishCallback?: Callback): void; + publishBinary(channel: string, data: any, last: boolean, meta: object, publishProps: object, publishCallback?: Callback): void; + + remoteCall(target: string, content: any, callback?: Callback): void; + remoteCall(target: string, content: any, timeout: number, callback?: Callback): void; + remoteCall(target: string, content: any, timeout: number, callProps: object, callback?: Callback): void; + + remoteCallBinary(target: string, data: any, last: boolean, callback?: Callback): void; + remoteCallBinary(target: string, data: any, last: boolean, meta: object, callback?: Callback): void; + remoteCallBinary(target: string, data: any, last: boolean, meta: object, timeout: number, callback?: Callback): void; + remoteCallBinary(target: string, data: any, last: boolean, meta: object, timeout: number, callProps: object, callback?: Callback): void; + + getStatus(): Status; + + isDisconnected(): boolean; + + setBackoffIncrement(period: number): void; + + getBackoffIncrement(): number; + + getBackoffPeriod(): number; + + setLogLevel(level: LogLevel): void; + + registerExtension(name: string, extension: Extension): boolean; + + unregisterExtension(name: string): boolean; + + getExtension(name: string): Extension | null; + + getName(): string; + + getClientId(): string | null; + + getURL(): string | null; + + getTransport(): Transport | null; + + getConfiguration(): Configuration; + + getAdvice(): Advice; + + setTimeout(fn: () => void, delay: number): any; + + clearTimeout(handle: any): void; + + reload?(): void; + + websocketEnabled?: boolean; +} + +export type Bytes = number[] | ArrayBuffer | DataView | + Int8Array | Uint8Array | Uint8ClampedArray | + Int16Array | Uint16Array | + Int32Array | Uint32Array; + +export interface Z85 { + encode(bytes: Bytes): string; + + decode(string: string): ArrayBuffer; +} + +export class AckExtension implements Extension { +} + +export class BinaryExtension implements Extension { +} + +export class ReloadExtension implements Extension { +} + +export class TimeStampExtension implements Extension { +} + +export class TimeSyncExtension implements Extension { +} diff --git a/addon/lib/cometd/cometd.js b/addon/lib/cometd/cometd.js new file mode 100644 index 000000000..3d1bf53cc --- /dev/null +++ b/addon/lib/cometd/cometd.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Export main APIs. +export * from "./Client.js"; + +// Export extensions to applications that want to +// instantiate them, and/or implement custom ones. +export * from "./Extension.js" +export * from "./AckExtension.js" +export * from "./BinaryExtension.js" +export * from "./ReloadExtension.js" +export * from "./TimeStampExtension.js" +export * from "./TimeSyncExtension.js" + +// Export transports to application that want +// to instantiate them explicitly, and/or +// extend them, and/or implement custom ones. +export * from "./Transport.js" +export * from "./RequestTransport.js" +export * from "./CallbackPollingTransport.js" +export * from "./LongPollingTransport.js" +export * from "./WebSocketTransport.js" diff --git a/addon/lib/prism/prism.css b/addon/lib/prism/prism.css new file mode 100644 index 000000000..bd02c56c9 --- /dev/null +++ b/addon/lib/prism/prism.css @@ -0,0 +1,3 @@ +/* PrismJS 1.29.0 +https://prismjs.com/download.html#themes=prism-coy&languages=json+sql */ +code[class*=language-],pre[class*=language-]{color:#000;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{position:relative;overflow:visible;padding:1px}pre[class*=language-]>code{position:relative;z-index:1;background-color:#fdfdfd;background-image:linear-gradient(transparent 50%,rgba(69,142,209,.04) 50%);background-size:3em 3em;background-origin:content-box;background-attachment:local}code[class*=language-]{max-height:inherit;height:inherit;padding:0 1em;display:block;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background-color:#fdfdfd;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin-bottom:1em}:not(pre)>code[class*=language-]{position:relative;padding:.2em;border-radius:.3em;color:#c92c2c;border:1px solid rgba(0,0,0,.1);display:inline;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#7d8b99}.token.punctuation{color:#5f6364}.token.boolean,.token.constant,.token.deleted,.token.function-name,.token.number,.token.property,.token.symbol,.token.tag{color:#c92c2c}.token.attr-name,.token.builtin,.token.char,.token.function,.token.inserted,.token.selector,.token.string{color:#2f9c0a}.token.entity,.token.operator,.token.url,.token.variable{color:#a67f59;background:rgba(255,255,255,.5)}.token.atrule,.token.attr-value,.token.class-name,.token.keyword{color:#1990b8}.token.important,.token.regex{color:#e90}.language-css .token.string,.style .token.string{color:#a67f59;background:rgba(255,255,255,.5)}.token.important{font-weight:400}.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.namespace{opacity:.7}@media screen and (max-width:767px){pre[class*=language-]:after,pre[class*=language-]:before{bottom:14px;box-shadow:none}}pre[class*=language-].line-numbers.line-numbers{padding-left:0}pre[class*=language-].line-numbers.line-numbers code{padding-left:3.8em}pre[class*=language-].line-numbers.line-numbers .line-numbers-rows{left:0}pre[class*=language-][data-line]{padding-top:0;padding-bottom:0;padding-left:0}pre[data-line] code{position:relative;padding-left:4em}pre .line-highlight{margin-top:0} \ No newline at end of file diff --git a/addon/lib/prism/prism.js b/addon/lib/prism/prism.js new file mode 100644 index 000000000..18b332517 --- /dev/null +++ b/addon/lib/prism/prism.js @@ -0,0 +1,8 @@ +/* PrismJS 1.29.0 +https://prismjs.com/download.html#themes=prism&languages=markup+clike+apex+json+sql */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var t={"included-cdata":{pattern://i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml; +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}; +!function(e){var t=/\b(?:(?:after|before)(?=\s+[a-z])|abstract|activate|and|any|array|as|asc|autonomous|begin|bigdecimal|blob|boolean|break|bulk|by|byte|case|cast|catch|char|class|collect|commit|const|continue|currency|date|datetime|decimal|default|delete|desc|do|double|else|end|enum|exception|exit|export|extends|final|finally|float|for|from|get(?=\s*[{};])|global|goto|group|having|hint|if|implements|import|in|inner|insert|instanceof|int|integer|interface|into|join|like|limit|list|long|loop|map|merge|new|not|null|nulls|number|object|of|on|or|outer|override|package|parallel|pragma|private|protected|public|retrieve|return|rollback|select|set|short|sObject|sort|static|string|super|switch|synchronized|system|testmethod|then|this|throw|time|transaction|transient|trigger|try|undelete|update|upsert|using|virtual|void|webservice|when|where|while|(?:inherited|with|without)\s+sharing)\b/i,n="\\b(?:(?=[a-z_]\\w*\\s*[<\\[])|(?!))[A-Z_]\\w*(?:\\s*\\.\\s*[A-Z_]\\w*)*\\b(?:\\s*(?:\\[\\s*\\]|<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>))*".replace(//g,(function(){return t.source}));function i(e){return RegExp(e.replace(//g,(function(){return n})),"i")}var a={keyword:t,punctuation:/[()\[\]{};,:.<>]/};e.languages.apex={comment:e.languages.clike.comment,string:e.languages.clike.string,sql:{pattern:/((?:[=,({:]|\breturn)\s*)\[[^\[\]]*\]/i,lookbehind:!0,greedy:!0,alias:"language-sql",inside:e.languages.sql},annotation:{pattern:/@\w+\b/,alias:"punctuation"},"class-name":[{pattern:i("(\\b(?:class|enum|extends|implements|instanceof|interface|new|trigger\\s+\\w+\\s+on)\\s+)"),lookbehind:!0,inside:a},{pattern:i("(\\(\\s*)(?=\\s*\\)\\s*[\\w(])"),lookbehind:!0,inside:a},{pattern:i("(?=\\s*\\w+\\s*[;=,(){:])"),inside:a}],trigger:{pattern:/(\btrigger\s+)\w+\b/i,lookbehind:!0,alias:"class-name"},keyword:t,function:/\b[a-z_]\w*(?=\s*\()/i,boolean:/\b(?:false|true)\b/i,number:/(?:\B\.\d+|\b\d+(?:\.\d+|L)?)\b/i,operator:/[!=](?:==?)?|\?\.?|&&|\|\||--|\+\+|[-+*/^&|]=?|:|<{1,3}=?/,punctuation:/[()\[\]{};,.]/}}(Prism); +Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json; diff --git a/addon/links.js b/addon/links.js index e39e7502c..8d83e6f13 100644 --- a/addon/links.js +++ b/addon/links.js @@ -183,6 +183,7 @@ export let setupLinks = [ {label: "Approval Processes", link: "/lightning/setup/ApprovalProcesses/home", section: "Platform Tools > Process Automation", prod: false}, {label: "Automation Home", link: "/lightning/setup/ProcessHome/home", section: "Platform Tools > Process Automation", prod: false}, {label: "Flows", link: "/lightning/setup/Flows/home", section: "Platform Tools > Process Automation", prod: false}, + {label: "Flow Trigger Explorer", link: "/interaction_explorer/flowExplorer.app", section: "Platform Tools > Process Automation", prod: false}, {label: "Migrate to Flow", link: "/lightning/setup/MigrateToFlowTool/home", section: "Platform Tools > Process Automation", prod: false}, {label: "Next Best Action", link: "/lightning/setup/NextBestAction/home", section: "Platform Tools > Process Automation", prod: false}, {label: "Paused And Failed Flow Interviews", link: "/lightning/setup/Pausedflows/home", section: "Platform Tools > Process Automation", prod: false}, @@ -337,6 +338,7 @@ export let setupLinks = [ {label: "Certificate and Key Management", link: "/lightning/setup/CertificatesAndKeysManagement/home", section: "Settings > Security", prod: false}, {label: "Delegated Administration", link: "/lightning/setup/DelegateGroups/home", section: "Settings > Security", prod: false}, //Settings > Security > Event Monitoring + {label: "Event Log File Browser", link: "/lightning/setup/ElfBrowser/home", section: "Settings > Security > Event Monitoring", prod: false}, {label: "Event Monitoring Settings", link: "/lightning/setup/EventMonitoringSetup/home", section: "Settings > Security > Event Monitoring", prod: false}, {label: "Transaction Security Policies", link: "/lightning/setup/TransactionSecurityNew/home", section: "Settings > Security > Event Monitoring", prod: false}, //Settings > Security diff --git a/addon/manifest-firefox.json b/addon/manifest-firefox.json index 763ba1270..4381808fb 100644 --- a/addon/manifest-firefox.json +++ b/addon/manifest-firefox.json @@ -1,8 +1,8 @@ { "name": "Salesforce Inspector Reloaded", "description": "Productivity tools for Salesforce administrators and developers to inspect data and metadata directly from the Salesforce UI.", - "version": "1.240", - "version_name": "1.24", + "version": "1.250", + "version_name": "1.25", "icons": { "128": "icon128.png" }, @@ -51,12 +51,20 @@ "https://*.builder.salesforce-experience.com/*" ], "all_frames": true, - "css": ["button.css", "inspect-inline.css"], - "js": ["button.js", "inspect-inline.js"] + "css": [ + "button.css", + "inspect-inline.css" + ], + "js": [ + "button.js", + "inspect-inline.js" + ] } ], "background": { - "scripts": ["background.js"] + "scripts": [ + "background.js" + ] }, "web_accessible_resources": [ "popup.html", @@ -73,4 +81,4 @@ ], "incognito": "spanning", "manifest_version": 2 -} +} \ No newline at end of file diff --git a/addon/manifest-template.json b/addon/manifest-template.json index e899d7d0d..354089d9f 100644 --- a/addon/manifest-template.json +++ b/addon/manifest-template.json @@ -1,18 +1,18 @@ { - "name": "Salesforce Inspector reloaded", + "name": "Salesforce Inspector Reloaded", "description": "Productivity tools for Salesforce administrators and developers to inspect data and metadata directly from the Salesforce UI.", - "version": "1.24", + "version": "1.25", + "version_name": "1.25", "icons": { "128": "icon128.png" }, - "applications": { - "gecko": { - "id": "jid1-DBcuAQpfLMcvOQ@jetpack", - "strict_min_version": "60.0" - } + "action": { + "default_title": "Open popup" }, "minimum_chrome_version": "88", - "permissions": ["cookies"], + "permissions": [ + "cookies" + ], "host_permissions": [ "https://*.salesforce.com/*", "https://*.salesforce-setup.com/*", @@ -26,7 +26,8 @@ "https://*.cloudforce.mil/*", "https://*.visualforce.mil/*", "https://*.crmforce.mil/*", - "https://*.force.com.mcas.ms/*" + "https://*.force.com.mcas.ms/*", + "https://*.builder.salesforce-experience.com/*" ], "content_scripts": [ { @@ -47,11 +48,18 @@ "https://*.cloudforce.mil/*", "https://*.visualforce.mil/*", "https://*.crmforce.mil/*", - "https://*.lightning.force.com.mcas.ms/*" + "https://*.lightning.force.com.mcas.ms/*", + "https://*.builder.salesforce-experience.com/*" ], "all_frames": true, - "css": ["button.css", "inspect-inline.css"], - "js": ["button.js", "inspect-inline.js", "links.js"] + "css": [ + "button.css", + "inspect-inline.css" + ], + "js": [ + "button.js", + "inspect-inline.js" + ] } ], "background": { @@ -67,13 +75,49 @@ "inspect.html", "metadata-retrieve.html", "explore-api.html", + "rest-explore.html", "limits.html", + "field-creator.html", "options.html" ], - "matches": ["https://*/*"], + "matches": [ + "https://*/*" + ], "extension_ids": [] } ], - "incognito": "replaced-at-build", - "manifest_version": 3 -} + "incognito": "split", + "manifest_version": 3, + "commands": { + "open-popup": { + "description": "Open popup" + }, + "data-export": { + "description": "Data Export" + }, + "data-import": { + "description": "Data Import" + }, + "options": { + "description": "Options" + }, + "metadata-retrieve": { + "description": "Retrieve Metadata" + }, + "limits": { + "description": "Org Limits" + }, + "field-creator": { + "description": "Field Creator" + }, + "explore-api": { + "description": "Explore API" + }, + "rest-explore": { + "description": "REST Explore" + }, + "event-monitor": { + "description": "Event Monitor" + } + } +} \ No newline at end of file diff --git a/addon/manifest.json b/addon/manifest.json index c55514cf4..fff455fdf 100644 --- a/addon/manifest.json +++ b/addon/manifest.json @@ -1,8 +1,8 @@ { "name": "Salesforce Inspector Reloaded", "description": "Productivity tools for Salesforce administrators and developers to inspect data and metadata directly from the Salesforce UI.", - "version": "1.241", - "version_name": "1.24", + "version": "1.250", + "version_name": "1.25", "icons": { "128": "icon128.png" }, @@ -10,7 +10,9 @@ "default_title": "Open popup" }, "minimum_chrome_version": "88", - "permissions": ["cookies"], + "permissions": [ + "cookies" + ], "host_permissions": [ "https://*.salesforce.com/*", "https://*.salesforce-setup.com/*", @@ -24,7 +26,8 @@ "https://*.cloudforce.mil/*", "https://*.visualforce.mil/*", "https://*.crmforce.mil/*", - "https://*.force.com.mcas.ms/*" + "https://*.force.com.mcas.ms/*", + "https://*.builder.salesforce-experience.com/*" ], "content_scripts": [ { @@ -45,11 +48,18 @@ "https://*.cloudforce.mil/*", "https://*.visualforce.mil/*", "https://*.crmforce.mil/*", - "https://*.lightning.force.com.mcas.ms/*" + "https://*.lightning.force.com.mcas.ms/*", + "https://*.builder.salesforce-experience.com/*" ], "all_frames": true, - "css": ["button.css", "inspect-inline.css"], - "js": ["button.js", "inspect-inline.js"] + "css": [ + "button.css", + "inspect-inline.css" + ], + "js": [ + "button.js", + "inspect-inline.js" + ] } ], "background": { @@ -67,9 +77,31 @@ "explore-api.html", "rest-explore.html", "limits.html", - "options.html" + "field-creator.html", + "options.html", + "event-monitor.html" + ], + "matches": [ + "https://*.salesforce.com/*", + "https://*.salesforce-setup.com/*", + "https://*.visual.force.com/*", + "https://*.vf.force.com/*", + "https://*.lightning.force.com/*", + "https://*.cloudforce.com/*", + "https://*.visualforce.com/*", + "https://*.sfcrmapps.cn/*", + "https://*.sfcrmproducts.cn/*", + "https://*.salesforce.mil/*", + "https://*.visual.force.mil/*", + "https://*.vf.force.mil/*", + "https://*.lightning.force.mil/*", + "https://*.cloudforce.mil/*", + "https://*.visualforce.mil/*", + "https://*.crmforce.mil/*", + "https://*.lightning.force.com.mcas.ms/*", + "https://*.force.com/*", + "https://*.builder.salesforce-experience.com/*" ], - "matches": ["https://*/*"], "extension_ids": [] } ], @@ -94,11 +126,17 @@ "limits": { "description": "Org Limits" }, + "field-creator": { + "description": "Field Creator" + }, "explore-api": { "description": "Explore API" }, "rest-explore": { "description": "REST Explore" + }, + "event-monitor": { + "description": "Event Monitor" } } -} +} \ No newline at end of file diff --git a/addon/options.css b/addon/options.css index 1c0beba9b..c569aac5a 100644 --- a/addon/options.css +++ b/addon/options.css @@ -46,7 +46,7 @@ body { height: 100%; display: flex; background-color: #B0C4DF !important; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_blue_background.png') !important; + background-image: url(images/lightning_blue_background.png) !important; background-repeat: no-repeat !important; background-size: contain !important; background-position: 0px 48px !important; @@ -146,7 +146,7 @@ body { .prod { background-color: #e0a4b5 !important; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_red_background.png') !important; + background-image: url(images/lightning_red_background.png) !important; background-repeat: no-repeat !important; background-size: contain !important; background-position: 0px 48px !important; @@ -159,4 +159,45 @@ body { .icon{ width: 24px; height: 24px; +} +button, +.button { + border: 1px solid #DDDBDA; + height: 32px; + display: inline-block; + text-decoration: none; + background-color: white; + padding: 0 16px; + color: #0070d2; + cursor: pointer; + outline: none; + position: relative; + line-height: 32px; + border-radius: 0.25rem; + margin-right: 10px; + white-space: nowrap; +} + +button:hover, +.button:hover { + background-color: #F4F6F9; + color: #005fb2; +} +.button-brand { + background-color: #0070d2; + border-color: #0070d2; + color: #fff; +} + +.button-brand:focus:not([disabled]), +.button-brand:hover:not([disabled]) { + background-color: #005fb2; + border-color: #005fb2; + color: #fff; +} + +.button-brand:active:not([disabled]) { + background-color: #005fb2; + border-color: #005fb2; + color: #fff; } \ No newline at end of file diff --git a/addon/options.js b/addon/options.js index 1202a37ee..b13e04120 100644 --- a/addon/options.js +++ b/addon/options.js @@ -1,5 +1,5 @@ /* global React ReactDOM */ -import {sfConn, apiVersion, nullToEmptyString} from "./inspector.js"; +import {sfConn, apiVersion, defaultApiVersion, nullToEmptyString} from "./inspector.js"; /* global initButton */ import {DescribeInfo} from "./data-load.js"; @@ -9,7 +9,8 @@ class Model { this.sfHost = sfHost; this.sfLink = "https://" + this.sfHost; this.userInfo = "..."; - if (localStorage.getItem(sfHost + "_isSandbox") != "true") { + let trialExpDate = localStorage.getItem(sfHost + "_trialExpirationDate"); + if (localStorage.getItem(sfHost + "_isSandbox") != "true" && (!trialExpDate || trialExpDate === "null")) { //change background color for production document.body.classList.add("prod"); } @@ -63,9 +64,15 @@ class OptionsTabSelector extends React.Component { super(props); this.model = props.model; this.sfHost = this.model.sfHost; + + // Get the tab from the URL or default to 1 + const urlParams = new URLSearchParams(window.location.search); + const initialTabId = parseInt(urlParams.get("selectedTab")) || 1; + this.state = { - selectedTabId: 1 + selectedTabId: initialTabId }; + this.tabs = [ { id: 1, @@ -81,7 +88,9 @@ class OptionsTabSelector extends React.Component { {option: Option, props: {type: "toggle", title: "Disable query input autofocus", key: "disableQueryInputAutoFocus"}}, {option: Option, props: {type: "toggle", title: "Popup Dark theme", key: "popupDarkTheme"}}, {option: Option, props: {type: "toggle", title: "Show 'Generate Access Token' button", key: "popupGenerateTokenButton", default: true}}, - {option: FaviconOption, props: {key: this.sfHost + "_customFavicon", tooltip: "You may need to add this domain to CSP trusted domains to see the favicon in Salesforce."}} + {option: FaviconOption, props: {key: this.sfHost + "_customFavicon", tooltip: "You may need to add this domain to CSP trusted domains to see the favicon in Salesforce."}}, + {option: Option, props: {type: "toggle", title: "Use favicon color on sandbox banner", key: "colorizeSandboxBanner"}}, + {option: Option, props: {type: "toggle", title: "Highlight PROD with a top border (color from favicon)", key: "colorizeProdBanner"}} ] }, { @@ -105,6 +114,8 @@ class OptionsTabSelector extends React.Component { {option: Option, props: {type: "toggle", title: "Show 'Delete Records' button ", key: "showDeleteRecordsButton", default: true}}, {option: Option, props: {type: "toggle", title: "Hide additional Object columns by default on Data Export", key: "hideObjectNameColumnsDataExport", default: false}}, {option: Option, props: {type: "toggle", title: "Include formula fields from suggestion", key: "includeFormulaFieldsFromExportAutocomplete", default: true}}, + {option: Option, props: {type: "number", title: "Number of queries stored in the history", key: "numberOfQueriesInHistory", default: 100}}, + {option: Option, props: {type: "number", title: "Number of saved queries", key: "numberOfQueriesSaved", default: 50}}, {option: Option, props: {type: "text", title: "Query Templates", key: "queryTemplates", placeholder: "SELECT Id FROM// SELECT Id FROM WHERE//SELECT Id FROM WHERE IN//SELECT Id FROM WHERE LIKE//SELECT Id FROM ORDER BY//SELECT ID FROM MYTEST__c//SELECT ID WHERE"}} ] }, @@ -131,7 +142,14 @@ class OptionsTabSelector extends React.Component { onTabSelect(e) { e.preventDefault(); - this.setState({selectedTabId: e.target.tabIndex}); + const selectedTabId = e.target.tabIndex; + + // Update the URL with the selected tab + const url = new URL(window.location); + url.searchParams.set("selectedTab", selectedTabId); + window.history.pushState({}, "", url); + + this.setState({selectedTabId}); } render() { @@ -237,6 +255,7 @@ class APIVersionOption extends React.Component { constructor(props) { super(props); this.onChangeApiVersion = this.onChangeApiVersion.bind(this); + this.onRestoreDefaultApiVersion = this.onRestoreDefaultApiVersion.bind(this); this.state = {apiVersion: localStorage.getItem("apiVersion") ? localStorage.getItem("apiVersion") : apiVersion}; } @@ -246,15 +265,23 @@ class APIVersionOption extends React.Component { localStorage.setItem("apiVersion", apiVersion + ".0"); } + onRestoreDefaultApiVersion(){ + localStorage.removeItem("apiVersion"); + this.setState({apiVersion: defaultApiVersion}); + } + render() { return h("div", {className: "slds-grid slds-border_bottom slds-p-horizontal_small slds-p-vertical_xx-small"}, h("div", {className: "slds-col slds-size_4-of-12 text-align-middle"}, h("span", {}, "API Version") ), - h("div", {className: "slds-col slds-size_7-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}), - h("div", {className: "slds-col slds-size_1-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}, + h("div", {className: "slds-col slds-size_5-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}), + h("div", {className: "slds-col slds-size_3-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}, + this.state.apiVersion != defaultApiVersion ? h("div", {className: "slds-form-element__control"}, + h("button", {className: "button button-brand", onClick: this.onRestoreDefaultApiVersion, title: "Restore Extension's default version"}, "Restore Default") + ) : null, h("div", {className: "slds-form-element__control slds-col slds-size_2-of-12"}, - h("input", {type: "number", required: true, id: "apiVersionInput", className: "slds-input", value: nullToEmptyString(this.state.apiVersion.split(".0")[0]), onChange: this.onChangeApiVersion}), + h("input", {type: "number", required: true, className: "slds-input", value: nullToEmptyString(this.state.apiVersion.split(".0")[0]), onChange: this.onChangeApiVersion}), ) ) ); @@ -368,16 +395,16 @@ class Option extends React.Component { render() { const id = this.key; - const isText = this.type == "text"; + const isTextOrNumber = this.type == "text" || this.type == "number"; return h("div", {className: "slds-grid slds-border_bottom slds-p-horizontal_small slds-p-vertical_xx-small"}, h("div", {className: "slds-col slds-size_4-of-12 text-align-middle"}, h("span", {}, this.title, h(Tooltip, {tooltip: this.tooltip, idKey: this.key}) ) ), - isText ? (h("div", {className: "slds-col slds-size_2-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}, + isTextOrNumber ? (h("div", {className: "slds-col slds-size_2-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}, h("div", {className: "slds-form-element__control slds-col slds-size_5-of-12"}, - h("input", {type: "text", id, className: "slds-input", placeholder: this.placeholder, value: nullToEmptyString(this.state[this.key]), onChange: this.onChange}) + h("input", {type: this.type, id, className: "slds-input", placeholder: this.placeholder, value: nullToEmptyString(this.state[this.key]), onChange: this.onChange}) ) )) : (h("div", {className: "slds-col slds-size_7-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}), @@ -401,10 +428,41 @@ class FaviconOption extends React.Component { super(props); this.sfHost = props.model.sfHost; this.onChangeFavicon = this.onChangeFavicon.bind(this); + this.populateFaviconColors = this.populateFaviconColors.bind(this); + this.onToogleSmartMode = this.onToogleSmartMode.bind(this); + let favicon = localStorage.getItem(this.sfHost + "_customFavicon") ? localStorage.getItem(this.sfHost + "_customFavicon") : ""; let isInternal = favicon.length > 0 && !favicon.startsWith("http"); + let smartMode = true; this.tooltip = props.tooltip; - this.state = {favicon, isInternal}; + this.state = {favicon, isInternal, smartMode}; + this.colorShades = { + dev: [ + "DeepSkyBlue", "DodgerBlue", "RoyalBlue", "MediumBlue", "CornflowerBlue", + "SlateBlue", "SteelBlue", "SkyBlue", "PowderBlue", "MediumSlateBlue", + "Indigo", "BlueViolet", "MediumPurple", "CadetBlue", "Aqua", + "Turquoise", "DarkTurquoise", "Teal", "LightSlateGray", "MidnightBlue" + ], + uat: [ + "MediumOrchid", "Orchid", "DarkOrchid", "DarkViolet", "DarkMagenta", + "Purple", "BlueViolet", "Indigo", "DarkSlateBlue", "RebeccaPurple", + "MediumPurple", "MediumSlateBlue", "SlateBlue", "Plum", "Violet", + "Thistle", "Magenta", "DarkOrchid", "Fuchsia", "DarkPurple" + ], + int: [ + "LimeGreen", "SeaGreen", "MediumSeaGreen", "ForestGreen", "Green", + "DarkGreen", "YellowGreen", "OliveDrab", "DarkOliveGreen", + "SpringGreen", "LawnGreen", "DarkKhaki", + "GreenYellow", "DarkSeaGreen", "MediumAquamarine", "DarkCyan", + "Teal", "Jade", "MediumForestGreen", "HunterGreen" + ], + full: [ + "Orange", "DarkOrange", "Coral", "Tomato", "OrangeRed", + "Salmon", "IndianRed", "Sienna", "Chocolate", "SaddleBrown", + "Peru", "DarkSalmon", "RosyBrown", "Brown", "Maroon", + "Tangerine", "Peach", "BurntOrange", "Pumpkin", "Amber" + ] + }; } onChangeFavicon(e) { @@ -413,6 +471,71 @@ class FaviconOption extends React.Component { localStorage.setItem(this.sfHost + "_customFavicon", favicon); } + onToogleSmartMode(e) { + let smartMode = e.target.checked; + this.setState({smartMode}); + } + + populateFaviconColors(){ + let orgs = Object.keys(localStorage).filter((localKey) => + localKey.endsWith("_isSandbox") + ); + + orgs.forEach((org) => { + let sfHost = org.substring(0, org.indexOf("_isSandbox")); + let existingColor = localStorage.getItem(sfHost + "_customFavicon"); + + if (!existingColor) { // Only assign a color if none is set + const chosenColor = this.getColorForHost(sfHost, this.state.smartMode); + if (chosenColor) { + console.info(sfHost + "_customFavicon", chosenColor); + localStorage.setItem(sfHost + "_customFavicon", chosenColor); + if (sfHost === this.sfHost) { + this.setState({favicon: chosenColor}); + } + } + } else { + console.info(sfHost + " already has a customFavicon: " + existingColor); + } + }); + } + + getEnvironmentType(sfHost) { + // Function to get environment type based on sfHost + if (sfHost.includes("dev")) return "dev"; + if (sfHost.includes("uat")) return "uat"; + if (sfHost.includes("int") || sfHost.includes("sit")) return "int"; + if (sfHost.includes("full")) return "full"; + return null; + } + + getColorForHost(sfHost, smartMode) { + // Attempt to get the environment type + const envType = this.getEnvironmentType(sfHost); + + // Check if smartMode is true and environment type is valid + if (smartMode && envType && this.colorShades[envType].length > 0) { + // Select a random color from the corresponding environment shades + const randomIndex = Math.floor(Math.random() * this.colorShades[envType].length); + const chosenColor = this.colorShades[envType][randomIndex]; + this.colorShades[envType].splice(randomIndex, 1); // Remove the used color from the list + return chosenColor; + } else { + // If no environment type matches or smartMode is false, use a random color from all available shades + const allColors = Object.values(this.colorShades).flat(); + if (allColors.length > 0) { + const randomIndex = Math.floor(Math.random() * allColors.length); + const chosenColor = allColors[randomIndex]; + allColors.splice(randomIndex, 1); // Remove the used color from the list + return chosenColor; + } else { + console.warn("No more colors available."); + return null; + } + } + } + + render() { return h("div", {className: "slds-grid slds-border_bottom slds-p-horizontal_small slds-p-vertical_xx-small"}, h("div", {className: "slds-col slds-size_4-of-12 text-align-middle"}, @@ -421,14 +544,29 @@ class FaviconOption extends React.Component { ) ), h("div", {className: "slds-col slds-size_2-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}, - h("div", {className: "slds-form-element__control slds-col slds-size_6-of-12"}, + h("div", {className: "slds-form-element__control"}, h("input", {type: "text", className: "slds-input", placeholder: "All HTML Color Names, Hex code or external URL", value: nullToEmptyString(this.state.favicon), onChange: this.onChangeFavicon}), ), - h("div", {className: "slds-form-element__control slds-col slds-size_1-of-12 slds-p-left_small"}, + h("div", {className: "slds-form-element__control slds-col"}, this.state.isInternal ? h("svg", {className: "icon"}, h("circle", {r: "12", cx: "12", cy: "12", fill: this.state.favicon}) ) : null ) + ), + h("div", {className: "slds-col slds-size_2-of-12 slds-form-element slds-grid slds-grid_align-end slds-grid_vertical-align-center slds-gutters_small"}, + h("div", {dir: "rtl", className: "slds-form-element__control slds-col "}, + h("label", {className: "slds-checkbox_toggle slds-grid"}, + h("input", {type: "checkbox", required: true, className: "slds-input", checked: this.state.smartMode, onChange: this.onToogleSmartMode}), + h("span", {className: "slds-checkbox_faux_container center-label"}, + h("span", {className: "slds-checkbox_faux"}), + h("span", {className: "slds-checkbox_on", title: "Use favicon based on org name (DEV : blue, UAT :green ..)"}, "Smart"), + h("span", {className: "slds-checkbox_off", title: "Use random favicon"}, "Random"), + ) + ) + ), + h("div", {className: "slds-form-element__control"}, + h("button", {className: "button button-brand", onClick: this.populateFaviconColors, title: "Use favicon for all orgs I've visited"}, "Populate All") + ) ) ); } @@ -546,23 +684,87 @@ class App extends React.Component { constructor(props) { super(props); this.foo = undefined; + + this.exportOptions = this.exportOptions.bind(this); + this.importOptions = this.importOptions.bind(this); + this.state = {importTitle: "Export Options"}; + } + + exportOptions() { + const localStorageData = { ...localStorage }; + const jsonData = JSON.stringify(localStorageData, null, 2); + const blob = new Blob([jsonData], { type: "application/json" }); + const link = document.createElement("a"); + const url = URL.createObjectURL(blob); + link.href = url; + link.download = "reloadedConfiguration.json"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + + importOptions() { + const fileInput = this.refs.fileInput; + + if (!fileInput.files.length) { + console.error('No file selected.'); + return; + } + + const file = fileInput.files[0]; + const reader = new FileReader(); + + reader.onload = (event) => { + try { + const importedData = JSON.parse(event.target.result); + for (const [key, value] of Object.entries(importedData)) { + localStorage.setItem(key, value); + } + this.setState({ importStyle: "green", importTitle: "Import Successful" }); + } catch (error) { + this.setState({ importStyle: "red", importTitle: "Import Failed" }); + console.error('Error parsing JSON file:', error); + } + }; + reader.readAsText(file); } render() { - let {model} = this.props; + let { model } = this.props; return h("div", {}, - h("div", {id: "user-info", className: "slds-border_bottom"}, - h("a", {href: model.sfLink, className: "sf-link"}, - h("svg", {viewBox: "0 0 24 24"}, - h("path", {d: "M18.9 12.3h-1.5v6.6c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-5.1h-3.6v5.1c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-6.6H5.1c-.1 0-.3-.1-.3-.2s0-.2.1-.3l6.9-7c.1-.1.3-.1.4 0l7 7v.3c0 .1-.2.2-.3.2z"}) + h("div", { id: "user-info", className: "slds-border_bottom" }, + h("a", { href: model.sfLink, className: "sf-link" }, + h("svg", { viewBox: "0 0 24 24" }, + h("path", { d: "M18.9 12.3h-1.5v6.6c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-5.1h-3.6v5.1c0 .2-.1.3-.3.3h-3c-.2 0-.3-.1-.3-.3v-6.6H5.1c-.1 0-.3-.1-.3-.2s0-.2.1-.3l6.9-7c.1-.1.3-.1.4 0l7 7v.3c0 .1-.2.2-.3.2z" }) ), " Salesforce Home" ), - h("h1", {className: "slds-text-title_bold"}, "Options"), + h("h1", { className: "slds-text-title_bold" }, "Options"), h("span", {}, " / " + model.userInfo), - h("div", {className: "flex-right"})), - h("div", {className: "main-container slds-card slds-m-around_small", id: "main-container_header"}, - h(OptionsTabSelector, {model})) + h("div", { className: "flex-right" }, + h("button", { className: "slds-button slds-button_icon slds-button_icon-border-filled", onClick: this.exportOptions, title: "Export Options" }, + h("svg", { className: "slds-button__icon"}, + h("use", { xlinkHref: "symbols.svg#download" }) + ) + ), + h("button", { className: "slds-button slds-button_icon slds-button_icon-border-filled slds-m-left_x-small", onClick: () => this.refs.fileInput.click(), title: this.state.importTitle }, + h("svg", { className: "slds-button__icon", style: { color: this.state.importStyle } }, + h("use", { xlinkHref: "symbols.svg#upload" }) + ) + ), + // Hidden file input for importing options + h("input", { + type: "file", + style: { display: 'none' }, + ref: "fileInput", + onChange: this.importOptions, + accept: "application/json" + }) + ) + ), + h("div", { className: "main-container slds-card slds-m-around_small", id: "main-container_header" }, + h(OptionsTabSelector, { model }) + ) ); } } diff --git a/addon/popup.css b/addon/popup.css index 79f9ba047..7b63fdbdc 100644 --- a/addon/popup.css +++ b/addon/popup.css @@ -347,7 +347,7 @@ a.icon { } a.icon-url { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/link.svg'); + -webkit-mask-image: url(images/link.svg); } .pointer { cursor: pointer; diff --git a/addon/popup.js b/addon/popup.js index 8a4afb01f..eaed882bd 100644 --- a/addon/popup.js +++ b/addon/popup.js @@ -81,8 +81,10 @@ class App extends React.PureComponent { isFieldsPresent: false, exportHref: "data-export.html?" + hostArg, importHref: "data-import.html?" + hostArg, + eventMonitorHref: "event-monitor.html?" + hostArg, + fieldCreatorHref: "field-creator.html?" + hostArg, limitsHref: "limits.html?" + hostArg, - latestNotesViewed: localStorage.getItem("latestReleaseNotesVersionViewed") === this.props.addonVersion + latestNotesViewed: localStorage.getItem("latestReleaseNotesVersionViewed") === this.props.addonVersion || browser.extension.inIncognitoContext }; this.onContextUrlMessage = this.onContextUrlMessage.bind(this); this.onShortcutKey = this.onShortcutKey.bind(this); @@ -109,6 +111,7 @@ class App extends React.PureComponent { this.setState({ exportHref: "data-export.html?" + exportArg, importHref: "data-import.html?" + importArg, + eventMonitorHref: "event-monitor.html?" + importArg, limitsHref: "limits.html?" + limitsArg }); } @@ -133,17 +136,18 @@ class App extends React.PureComponent { onShortcutKey(e) { const refs = this.refs; const actionMap = { - "m": ["all", "clickShowDetailsBtn"], "a": ["all", "clickAllDataBtn"], "f": ["all", "clickShowFieldAPINameBtn"], "n": ["all", "clickNewBtn"], "e": ["click", "dataExportBtn"], "i": ["click", "dataImportBtn"], "l": ["click", "limitsBtn"], + "t": ["click", "fieldCreatorBtn"], "d": ["click", "metaRetrieveBtn"], "x": ["click", "apiExploreBtn"], "h": ["click", "homeBtn"], "p": ["click", "optionsBtn"], + "m": ["click", "eventMonitorBtn"], "o": ["tab", "objectTab"], "u": ["tab", "userTab"], "s": ["tab", "shortcutTab"], @@ -214,7 +218,7 @@ class App extends React.PureComponent { inInspector, addonVersion } = this.props; - let {isInSetup, contextUrl, apiVersionInput, exportHref, importHref, limitsHref, isFieldsPresent, latestNotesViewed} = this.state; + let {isInSetup, contextUrl, apiVersionInput, exportHref, importHref, eventMonitorHref, fieldCreatorHref, limitsHref, isFieldsPresent, latestNotesViewed} = this.state; let hostArg = new URLSearchParams(); hostArg.set("host", sfHost); let linkInNewTab = JSON.parse(localStorage.getItem("openLinksInNewTab")); @@ -289,7 +293,7 @@ class App extends React.PureComponent { }) ), h("div", {className: "main", id: "mainTabs"}, - h(AllDataBox, {ref: "showAllDataBox", sfHost, showDetailsSupported: !inLightning && !inInspector, linkTarget, contextUrl, onContextRecordChange: this.onContextRecordChange, isFieldsPresent}), + h(AllDataBox, {ref: "showAllDataBox", sfHost, showDetailsSupported: !inLightning && !inInspector, linkTarget, contextUrl, onContextRecordChange: this.onContextRecordChange, isFieldsPresent, eventMonitorHref}), h("div", {className: "slds-p-vertical_x-small slds-p-horizontal_x-small slds-border_bottom"}, h("div", {className: "slds-m-bottom_xx-small"}, h("a", {ref: "dataExportBtn", href: exportHref, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, "Data ", h("u", {}, "E"), "xport")) @@ -297,9 +301,12 @@ class App extends React.PureComponent { h("div", {className: "slds-m-bottom_xx-small"}, h("a", {ref: "dataImportBtn", href: importHref, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, "Data ", h("u", {}, "I"), "mport")) ), - h("div", {}, + h("div", {className: "slds-m-bottom_xx-small"}, h("a", {ref: "limitsBtn", href: limitsHref, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, "Org ", h("u", {}, "L"), "imits")) ), + h("div", {}, + h("a", {ref: "fieldCreatorBtn", href: fieldCreatorHref, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, "Field Crea", h("u", {}, "t"), "or (beta)")) + ), ), h("div", {className: "slds-p-vertical_x-small slds-p-horizontal_x-small slds-border_bottom"}, // Advanded features should be put below this line, and the layout adjusted so they are below the fold @@ -310,7 +317,10 @@ class App extends React.PureComponent { h("a", {ref: "apiExploreBtn", href: "explore-api.html?" + hostArg, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, "E", h("u", {}, "x"), "plore API")) ), h("div", {className: "slds-m-bottom_xx-small"}, - h("a", {ref: "restExploreBtn", href: "rest-explore.html?" + hostArg, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, h("u", {}, "R"), "EST Explore (beta)")) + h("a", {ref: "restExploreBtn", href: "rest-explore.html?" + hostArg, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, h("u", {}, "R"), "EST Explore")) + ), + h("div", {className: "slds-m-bottom_xx-small"}, + h("a", {ref: "eventMonitorBtn", href: eventMonitorHref, target: linkTarget, className: "page-button slds-button slds-button_neutral"}, h("span", {}, "Event ", h("u", {}, "M"), "onitor")) ), localStorage.getItem("popupGenerateTokenButton") !== "false" ? h("div", {className: "slds-m-bottom_xx-small"}, h("a", @@ -602,7 +612,7 @@ class AllDataBox extends React.PureComponent { render() { let {activeSearchAspect, sobjectsLoading, contextRecordId, contextSobject, contextUserId, contextOrgId, contextPath, sobjectsList} = this.state; - let {sfHost, showDetailsSupported, linkTarget, onContextRecordChange, isFieldsPresent} = this.props; + let {sfHost, showDetailsSupported, linkTarget, onContextRecordChange, isFieldsPresent, eventMonitorHref} = this.props; return ( h("div", {className: "slds-p-top_small slds-p-horizontal_x-small slds-p-bottom_x-small slds-border_bottom" + (this.isLoading() ? " loading " : "")}, @@ -613,7 +623,7 @@ class AllDataBox extends React.PureComponent { h("li", {ref: "orgTab", onClick: this.onAspectClick, "data-aspect": this.SearchAspectTypes.org, className: (activeSearchAspect == this.SearchAspectTypes.org) ? "active" : ""}, h("span", {}, "O", h("u", {}, "r"), "g")) ), (activeSearchAspect == this.SearchAspectTypes.sobject) - ? h(AllDataBoxSObject, {ref: "showAllDataBoxSObject", sfHost, showDetailsSupported, sobjectsList, sobjectsLoading, contextRecordId, contextSobject, linkTarget, onContextRecordChange, isFieldsPresent}) + ? h(AllDataBoxSObject, {ref: "showAllDataBoxSObject", sfHost, showDetailsSupported, sobjectsList, sobjectsLoading, contextRecordId, contextSobject, linkTarget, onContextRecordChange, isFieldsPresent, eventMonitorHref}) : (activeSearchAspect == this.SearchAspectTypes.users) ? h(AllDataBoxUsers, {ref: "showAllDataBoxUsers", sfHost, linkTarget, contextUserId, contextOrgId, contextPath, setIsLoading: (value) => { this.setIsLoading("usersBox", value); }}, "Users") : (activeSearchAspect == this.SearchAspectTypes.shortcuts) @@ -992,13 +1002,13 @@ class AllDataBoxSObject extends React.PureComponent { } render() { - let {sfHost, showDetailsSupported, sobjectsList, linkTarget, contextRecordId, isFieldsPresent} = this.props; + let {sfHost, showDetailsSupported, sobjectsList, linkTarget, contextRecordId, isFieldsPresent, eventMonitorHref} = this.props; let {selectedValue, recordIdDetails} = this.state; return ( h("div", {}, h(AllDataSearch, {ref: "allDataSearch", sfHost, onDataSelect: this.onDataSelect, sobjectsList, getMatches: this.getMatches, inputSearchDelay: 0, placeholderText: "Record id, id prefix or object name", title: "Click to show recent items", resultRender: this.resultRender}), selectedValue - ? h(AllDataSelection, {ref: "allDataSelection", sfHost, showDetailsSupported, selectedValue, linkTarget, recordIdDetails, contextRecordId, isFieldsPresent}) + ? h(AllDataSelection, {ref: "allDataSelection", sfHost, showDetailsSupported, selectedValue, linkTarget, recordIdDetails, contextRecordId, isFieldsPresent, eventMonitorHref}) : h("div", {className: "all-data-box-inner empty"}, "No record to display") ) ); @@ -1651,23 +1661,25 @@ class AllDataSelection extends React.PureComponent { */ getObjectSetupLink(sobjectName, durableId, isCustomSetting) { if (sobjectName.endsWith("__mdt")) { - return this.getCustomMetadataLink(durableId); + return this.getMetadataLink(durableId, "CustomMetadata"); + } else if (sobjectName.endsWith("__e")) { + return this.getMetadataLink(durableId, "EventObjects"); } else if (isCustomSetting) { - return "https://" + this.props.sfHost + "/lightning/setup/CustomSettings/page?address=%2F" + durableId + "?setupid=CustomSettings"; + return this.getMetadataLink(durableId, "CustomSettings"); } else if (sobjectName.endsWith("__c")) { return "https://" + this.props.sfHost + "/lightning/setup/ObjectManager/" + durableId + "/Details/view"; } else { return "https://" + this.props.sfHost + "/lightning/setup/ObjectManager/" + sobjectName + "/Details/view"; } } - getCustomMetadataLink(durableId) { - return "https://" + this.props.sfHost + "/lightning/setup/CustomMetadata/page?address=%2F" + durableId + "%3Fsetupid%3DCustomMetadata"; + getMetadataLink(durableId, type){ + return `https://${this.props.sfHost}/lightning/setup/${type}/page?address=%2F${durableId}%3Fsetupid%3D${type}`; } getObjectFieldsSetupLink(sobjectName, durableId, isCustomSetting) { if (sobjectName.endsWith("__mdt")) { - return this.getCustomMetadataLink(durableId); + return this.getMetadataLink(durableId, "CustomMetadata"); } else if (isCustomSetting) { - return "https://" + this.props.sfHost + "/lightning/setup/CustomSettings/page?address=%2F" + durableId + "?setupid=CustomSettings"; + return this.getMetadataLink(durableId, "CustomSettings"); } else if (sobjectName.endsWith("__c") || sobjectName.endsWith("__kav")) { return "https://" + this.props.sfHost + "/lightning/setup/ObjectManager/" + durableId + "/FieldsAndRelationships/view"; } else { @@ -1684,6 +1696,9 @@ class AllDataSelection extends React.PureComponent { return "https://" + this.props.sfHost + "/lightning/o/" + sobjectName + "/list"; } } + getObjectListAccess(sobjectName) { + return "https://" + this.props.sfHost + "/lightning/setup/ObjectManager/" + sobjectName + "/ObjectAccess/view"; + } getRecordTypesLink(sfHost, sobjectName, durableId) { if (sobjectName.endsWith("__c") || sobjectName.endsWith("__kav")) { return "https://" + sfHost + "/lightning/setup/ObjectManager/" + durableId + "/RecordTypes/view"; @@ -1700,6 +1715,9 @@ class AllDataSelection extends React.PureComponent { getNewObjectUrl(sfHost, newUrl){ return "https://" + sfHost + newUrl; } + getSubscribeUrl(name){ + return this.props.eventMonitorHref + "&channel=" + name; + } setFlowDefinitionId(recordId){ if (recordId && !this.state.flowDefinitionId){ if (recordId.startsWith("301")){ @@ -1714,7 +1732,7 @@ class AllDataSelection extends React.PureComponent { } } render() { - let {sfHost, showDetailsSupported, contextRecordId, selectedValue, linkTarget, recordIdDetails, isFieldsPresent} = this.props; + let {sfHost, showDetailsSupported, contextRecordId, selectedValue, linkTarget, recordIdDetails, isFieldsPresent, eventMonitorHref} = this.props; let {flowDefinitionId} = this.state; // Show buttons for the available APIs. let buttons = selectedValue.sobject.availableApis ? Array.from(selectedValue.sobject.availableApis) : []; @@ -1739,10 +1757,16 @@ class AllDataSelection extends React.PureComponent { h("th", {}, "Links:"), h("td", {}, h("a", {href: this.getObjectFieldsSetupLink(selectedValue.sobject.name, selectedValue.sobject.durableId, selectedValue.sobject.isCustomSetting), target: linkTarget}, "Fields"), - h("span", {}, " / "), - h("a", {href: this.getRecordTypesLink(sfHost, selectedValue.sobject.name, selectedValue.sobject.durableId), target: linkTarget}, "Record Types"), - h("span", {}, " / "), - h("a", {href: this.getObjectListLink(selectedValue.sobject.name, selectedValue.sobject.keyPrefix, selectedValue.sobject.isCustomSetting), target: linkTarget}, "Object List") + selectedValue.sobject.recordTypesSupported?.recordTypeInfos?.length > 0 ? h("span", {}, + h("span", {}, " / "), + h("a", {href: this.getRecordTypesLink(sfHost, selectedValue.sobject.name, selectedValue.sobject.durableId), target: linkTarget}, "Record Types"), + ) : null, + selectedValue.sobject.name.endsWith("__e") ? null : h("span", {}, h("span", {}, " / "), + h("a", {href: this.getObjectListLink(selectedValue.sobject.name, selectedValue.sobject.keyPrefix, selectedValue.sobject.isCustomSetting), target: linkTarget}, "List") + ), + selectedValue.sobject.name.endsWith("__e") || selectedValue.sobject.name.endsWith("__mdt") ? null : h("span", {}, h("span", {}, " / "), + h("a", {href: this.getObjectListAccess(selectedValue.sobject.name, selectedValue.sobject.keyPrefix, selectedValue.sobject.isCustomSetting), target: linkTarget}, "Access") + ) ), ), h("tr", {}, @@ -1790,7 +1814,8 @@ class AllDataSelection extends React.PureComponent { : " (Not readable)" ))), isFieldsPresent ? h("a", {ref: "showFieldApiNameBtn", onClick: showApiName, target: linkTarget, className: "slds-m-top_xx-small page-button slds-button slds-button_neutral"}, h("span", {}, "Show ", h("u", {}, "f"), "ields API names")) : null, - selectedValue.sobject.isEverCreatable ? h("a", {ref: "showNewBtn", href: this.getNewObjectUrl(sfHost, selectedValue.sobject.newUrl), target: linkTarget, className: "slds-m-top_xx-small page-button slds-button slds-button_neutral"}, h("span", {}, h("u", {}, "N"), "ew " + selectedValue.sobject.label)) : null, + selectedValue.sobject.isEverCreatable && !selectedValue.sobject.name.endsWith("__e") ? h("a", {ref: "showNewBtn", href: this.getNewObjectUrl(sfHost, selectedValue.sobject.newUrl), target: linkTarget, className: "slds-m-top_xx-small page-button slds-button slds-button_neutral"}, h("span", {}, h("u", {}, "N"), "ew " + selectedValue.sobject.label)) : null, + selectedValue.sobject.name.endsWith("__e") ? h("a", {href: this.getSubscribeUrl(selectedValue.sobject.name), target: linkTarget, className: "slds-m-top_xx-small page-button slds-button slds-button_neutral"}, h("span", {}, h("u", {}), "Subscribe to Event")) : null, ) ); } diff --git a/addon/rest-explore.css b/addon/rest-explore.css index a104e1eb1..e9d7f27b7 100644 --- a/addon/rest-explore.css +++ b/addon/rest-explore.css @@ -61,7 +61,7 @@ body { overflow: hidden; margin: 0; background-color: #B0C4DF; - background-image: url('chrome-extension://__MSG_@@extension_id__/images/lightning_blue_background.png'); + background-image: url(images/lightning_blue_background.png); background-repeat: no-repeat; background-size: contain; background-position: 0px 48px; @@ -243,7 +243,7 @@ input[type=default] { } input[type=search] { - background-image: url('chrome-extension://__MSG_@@extension_id__/images/search.svg'); + background-image: url(images/search.svg); background-repeat: no-repeat; background-size: 1rem; background-position: 10px 7px; @@ -251,7 +251,7 @@ input[type=search] { } input[type=save] { - background-image: url('chrome-extension://__MSG_@@extension_id__/images/save.svg'); + background-image: url(images/save.svg); background-repeat: no-repeat; background-size: 1rem; background-position: 10px 7px; @@ -266,6 +266,7 @@ input[type="checkbox"] { input.query-control{ width: -moz-available; width: -webkit-fill-available; + width: fill-available; } textarea:not([readonly]):focus, button:active, @@ -368,7 +369,7 @@ textarea[readonly] { -webkit-mask-repeat: no-repeat; -webkit-mask-size: 1.4rem; ; - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/help.svg'); + -webkit-mask-image: url(images/help.svg); -webkit-mask-position: center; background-color: #919191; } @@ -518,15 +519,15 @@ button.toggle:hover .button-toggle-icon { } button.expand .button-toggle-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/down.svg'); + -webkit-mask-image: url(images/down.svg); } button.contract .button-toggle-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/up.svg'); + -webkit-mask-image: url(images/up.svg); } button.toggle .button-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/light_bulb.svg'); + -webkit-mask-image: url(images/light_bulb.svg); } .autocomplete-header { @@ -585,112 +586,112 @@ button.toggle .button-icon { } .relationshipName .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/relate.svg'); + -webkit-mask-image: url(images/relate.svg); background-color: #0070d2; } .object .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/sobject.svg'); + -webkit-mask-image: url(images/sobject.svg); background-color: #04844B; } .variable .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/variable.svg'); + -webkit-mask-image: url(images/variable.svg); } .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/quotation_marks.svg'); + -webkit-mask-image: url(images/quotation_marks.svg); } .null .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/steps.svg'); + -webkit-mask-image: url(images/steps.svg); } .fieldName .autocomplete-icon { /* default icon */ - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/question_mark.svg'); + -webkit-mask-image: url(images/question_mark.svg); } .fieldName.reference .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/record_lookup.svg'); + -webkit-mask-image: url(images/record_lookup.svg); } .fieldName.string .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/string.svg'); + -webkit-mask-image: url(images/string.svg); } .fieldName.id .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/anchor.svg'); + -webkit-mask-image: url(images/anchor.svg); } .fieldName.picklist .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/picklist.svg'); + -webkit-mask-image: url(images/picklist.svg); } .fieldName.multipicklist .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/multi-picklist.svg'); + -webkit-mask-image: url(images/multi-picklist.svg); } .fieldName.boolean .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/boolean.svg'); + -webkit-mask-image: url(images/boolean.svg); } .fieldName.phone .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/call.svg'); + -webkit-mask-image: url(images/call.svg); } .fieldName.textarea .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/textarea.svg'); + -webkit-mask-image: url(images/textarea.svg); } .fieldName.url .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/link.svg'); + -webkit-mask-image: url(images/link.svg); } .fieldName.int .autocomplete-icon, .fieldName.double .autocomplete-icon, .fieldName.long .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/number.svg'); + -webkit-mask-image: url(images/number.svg); } .fieldName.address .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/home.svg'); + -webkit-mask-image: url(images/home.svg); } .fieldName.datetime .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/date-time.svg'); + -webkit-mask-image: url(images/date-time.svg); } .fieldName.date .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/date.svg'); + -webkit-mask-image: url(images/date.svg); } .fieldName.currency .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/currency.svg'); + -webkit-mask-image: url(images/currency.svg); } .fieldName.email .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/email.svg'); + -webkit-mask-image: url(images/email.svg); } .fieldName.location .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/checkin.svg'); + -webkit-mask-image: url(images/checkin.svg); } .fieldName.percent .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/percent.svg'); + -webkit-mask-image: url(images/percent.svg); } .fieldName.encryptedstring .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/lock.svg'); + -webkit-mask-image: url(images/lock.svg); } .fieldName.time .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/clock.svg'); + -webkit-mask-image: url(images/clock.svg); } .fieldName.complexvalue .autocomplete-icon { - -webkit-mask-image: url('chrome-extension://__MSG_@@extension_id__/images/advanced_function.svg'); + -webkit-mask-image: url(images/advanced_function.svg); } .header { @@ -725,4 +726,7 @@ button.toggle .button-icon { } .blue{ color:#0070d2; +} +.reset-margin{ + margin: 0;; } \ No newline at end of file diff --git a/addon/rest-explore.html b/addon/rest-explore.html index 69a61fa3d..096e759f8 100644 --- a/addon/rest-explore.html +++ b/addon/rest-explore.html @@ -9,6 +9,7 @@ + @@ -16,6 +17,7 @@ + diff --git a/addon/rest-explore.js b/addon/rest-explore.js index 30cb654b6..0a462d6f6 100644 --- a/addon/rest-explore.js +++ b/addon/rest-explore.js @@ -72,7 +72,6 @@ class Model { this.spinnerCount = 0; this.userInfo = "..."; this.winInnerHeight = 0; - this.queryAll = false; this.autocompleteResults = {sobjectName: "", title: "\u00A0", results: []}; this.autocompleteClick = null; this.isWorking = false; @@ -85,7 +84,6 @@ class Model { this.selectedSavedEntry = null; this.expandSavedOptions = false; this.startTime = null; - this.lastStartTime = null; this.totalTime = 0; this.autocompleteState = ""; this.autocompleteProgress = {}; @@ -95,6 +93,8 @@ class Model { this.canSendRequest = true; this.resultClass = "neutral"; this.request = {endpoint: "", method: "get", body: ""}; + this.apiList; + this.filteredApiList; this.requestTemplates = localStorage.getItem("requestTemplates") ? this.requestTemplates = localStorage.getItem("requestTemplates").split("//") : [ {key: "getLimit", endpoint: `/services/data/v${apiVersion}/limits`, method: "GET", body: ""}, {key: "getAccount", endpoint: `/services/data/v${apiVersion}/query/?q=SELECT+Id,Name+FROM+Account+LIMIT+1`, method: "GET", body: ""}, @@ -113,10 +113,30 @@ class Model { this.request.method = args.get("method"); } else if (this.queryHistory.list[0]) { this.request = this.queryHistory.list[0]; + this.didUpdate(); } else { this.request = this.requestTemplates[0]; } + this.spinFor(sfConn.rest(`/services/data/v${apiVersion}/`, {}) + .catch(err => { + if (err.name != "AbortError") { + this.autocompleteResults = { + title: "Error: " + err.message, + results: [] + }; + } + return null; + }) + .then((result) => { + this.apiList = Object.keys(result) + .map(key => ({ + key, + "endpoint": result[key] + })) + .sort((a, b) => a.key.localeCompare(b.key)); + })); + if (args.has("error")) { this.exportError = args.get("error") + " " + args.get("error_description"); } @@ -142,6 +162,9 @@ class Model { copyAsJson() { copyToClipboard(this.apiResponse.value, null, " "); } + clear(){ + this.apiResponse.value = ""; + } selectSavedEntry() { let delimiter = ":"; if (this.selectedSavedEntry != null) { @@ -205,9 +228,12 @@ class Model { } doSend() { - //sfConn.rest(recordUrl, {method: "PATCH", body: record, headers: this.headerCallout}).then(() => { + this.startTime = performance.now(); + this.canSendRequest = false; this.spinFor(sfConn.rest(this.request.endpoint, {method: this.request.method, body: this.request.body, bodyType: "raw", progressHandler: this.autocompleteProgress}, true) .catch(err => { + this.canSendRequest = true; + this.totalTime = performance.now() - this.startTime; if (err.name != "AbortError") { this.autocompleteResults = { title: "Error: " + err.message, @@ -218,6 +244,7 @@ class Model { }) .then((result) => { //generate key with timestamp + this.totalTime = performance.now() - this.startTime; this.request.key = Date.now(); this.queryHistory.add(this.request); if (!result) { @@ -225,7 +252,7 @@ class Model { return; } this.parseResponse(result, "Success"); - console.log(result); + this.canSendRequest = true; })); } @@ -237,7 +264,20 @@ class Model { code: result.status, value: result.response ? JSON.stringify(result.response, null, " ") : "NONE" }; - // The results can be quite large and take a long time to render, so we only want to render a result once the user has explicitly selected it. + if (this.resultClass === "success"){ + let newApis = Object.keys(result.response) + .filter(key => typeof result.response[key] == "string" && result.response[key].startsWith("/services/data/")) + .map(key => ({ + key, + "endpoint": result.response[key] + })); + newApis.forEach(api => { + if (!this.apiList.some(existingApi => existingApi.key === api.key)) { + this.apiList.push(api); + } + }); + this.filteredApiList = this.apiList.filter(api => api.endpoint.toLowerCase().includes(this.request.endpoint.toLowerCase())); + } } } @@ -258,6 +298,7 @@ class App extends React.Component { this.onToggleSavedOptions = this.onToggleSavedOptions.bind(this); this.onSend = this.onSend.bind(this); this.onCopyAsJson = this.onCopyAsJson.bind(this); + this.onClearResponse = this.onClearResponse.bind(this); this.onUpdateBody = this.onUpdateBody.bind(this); this.onSetQueryName = this.onSetQueryName.bind(this); this.onSetEndpoint = this.onSetEndpoint.bind(this); @@ -344,6 +385,11 @@ class App extends React.Component { model.copyAsJson(); model.didUpdate(); } + onClearResponse(){ + let {model} = this.props; + model.clear(); + model.didUpdate(); + } onUpdateBody(e){ let {model} = this.props; model.request.body = e.target.value; @@ -358,16 +404,19 @@ class App extends React.Component { onSetEndpoint(e){ let {model} = this.props; model.request.endpoint = e.target.value; + model.filteredApiList = model.apiList.filter(api => api.endpoint.toLowerCase().includes(e.target.value.toLowerCase())); model.didUpdate(); } + componentDidMount() { let {model} = this.props; let endpointInput = this.refs.endpoint; + endpointInput.value = model.request.endpoint; addEventListener("keydown", e => { if ((e.ctrlKey && e.key == "Enter") || e.key == "F5") { e.preventDefault(); - model.doExport(); + model.doSend(); model.didUpdate(); } }); @@ -397,11 +446,23 @@ class App extends React.Component { } componentDidUpdate() { this.recalculateSize(); + if (window.Prism) { + window.Prism.highlightAll(); + } } canSendRequest(){ let {model} = this.props; model.canSendRequest = model.request.method === "GET" || model.request.body.length > 1; } + autocompleteClick(value){ + let {model} = this.props; + model.request.method = "GET"; + this.refs.endpoint.value = value.endpoint; + model.request.endpoint = value.endpoint; + model.request.body = ""; + model.filteredApiList = []; + model.didUpdate(); + } recalculateSize() { //TODO // Investigate if we can use the IntersectionObserver API here instead, once it is available. @@ -420,7 +481,7 @@ class App extends React.Component { ), " Salesforce Home" ), - h("h1", {}, "REST Explore (beta)"), + h("h1", {}, "REST Explore"), h("span", {}, " / " + model.userInfo), h("div", {className: "flex-right"}, h("div", {id: "spinner", role: "status", className: "slds-spinner slds-spinner_small slds-spinner_inline", hidden: model.spinnerCount == 0}, @@ -493,7 +554,15 @@ class App extends React.Component { ), h("input", {ref: "endpoint", className: "slds-input query-control slds-m-right_medium", type: "default", placeholder: "/services/data/v" + apiVersion, onChange: this.onSetEndpoint}), h("div", {className: "flex-right"}, - h("button", {tabIndex: 1, disabled: !model.canSendRequest, onClick: this.onSend, title: "Ctrl+Enter / F5", className: "highlighted"}, "Send"), + h("button", {tabIndex: 1, disabled: !model.canSendRequest, onClick: this.onSend, title: "Ctrl+Enter / F5", className: "highlighted"}, "Send") + ) + ), + h("div", {className: "autocomplete-box"}, + h("div", {className: "autocomplete-header"}), + h("div", {className: "autocomplete-results"}, + model.filteredApiList?.length > 0 ? model.filteredApiList.map(r => + h("div", {className: "autocomplete-result", key: r.key}, h("a", {tabIndex: 0, title: r.key, onClick: e => { e.preventDefault(); this.autocompleteClick(r); model.didUpdate(); }, href: "#", className: "fieldName url"}, h("div", {className: "autocomplete-icon"}), r.key), " ") + ) : null ), ), h("div", {className: "autocomplete-box slds-m-top_medium"}, @@ -509,15 +578,21 @@ class App extends React.Component { h("div", {className: "button-group"}, h("button", {disabled: !model.apiResponse, onClick: this.onCopyAsJson, title: "Copy raw API output to clipboard"}, "Copy") ), - model.apiResponse && h("span", {className: "result-status flex-right"}, - h("span", {className: "status-code"}, "Status: " + model.apiResponse.code) - ), + h("span", {className: "result-status flex-right"}, + model.apiResponse && h("div", + h("span", {}, model.totalTime.toFixed(1) + "ms"), + h("span", {className: "slds-m-left_medium status-code"}, "Status: " + model.apiResponse.code) + ), + h("div", {className: "slds-m-left_medium button-group"}, + h("button", {disabled: !model.apiResponse, onClick: this.onClearResponse, title: "Clear Response"}, "Clear") + ) + ) ), h("textarea", {id: "result-text", readOnly: true, value: model.exportError || "", hidden: model.exportError == null}), h("div", {id: "result-table", ref: "scroller", hidden: model.exportError != null}, model.apiResponse && h("div", {}, - h("div", {}, - h("textarea", {readOnly: true, value: model.apiResponse.value}) + h("pre", {className: "language-json reset-margin"}, // Set the language class to JSON for Prism to highlight + h("code", {className: "language-json"}, model.apiResponse.value) ) ) ) diff --git a/addon/sources.txt b/addon/sources.txt deleted file mode 100644 index 99c12371d..000000000 --- a/addon/sources.txt +++ /dev/null @@ -1,4 +0,0 @@ -https://unpkg.com/react@15.4.0/dist/react.js -https://unpkg.com/react-dom@15.4.0/dist/react-dom.js -https://unpkg.com/react@15.4.0/dist/react.min.js -https://unpkg.com/react-dom@15.4.0/dist/react-dom.min.js \ No newline at end of file diff --git a/addon/symbols.svg b/addon/symbols.svg index 7d7ad3f0d..4bbd8da58 100644 --- a/addon/symbols.svg +++ b/addon/symbols.svg @@ -1 +1,2541 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/event-monitor.md b/docs/event-monitor.md new file mode 100644 index 000000000..94e7b74a0 --- /dev/null +++ b/docs/event-monitor.md @@ -0,0 +1,40 @@ +# Event Monitor Page + +## Overview +The Event Monitor page allows users to subscribe to and display Salesforce Platform Events in real-time. It supports both Standard and Custom Platform Events. The page uses the CometD library to connect to Salesforce and stream events directly to the user's browser. + +![image](https://github.com/user-attachments/assets/e24e10cf-2b6c-4d04-ad6e-3377c833ef1d) + + +## Key Features +- **Subscribe to Platform Events:** Users can select a channel (Standard or Custom Platform Events) and subscribe to it to receive live updates. +- **View Events:** Events are displayed in a structured format, allowing users to monitor event data as it arrives. +- **Replay Events:** Users can specify a `Replay From` value to replay past events from a specific point in time. +- **Copy Event Data:** Users can copy the event data in JSON format for further analysis or troubleshooting. + +## Subscribe to an Event from the Popup +You can also directly access to the Event Monitor page and pre-select the Event from the popup by click the `Subscribe to Event` button. + +Subscribe from popup + +## Replay From Parameter Warning +The `Replay From` parameter is a powerful feature that allows users to replay past events by specifying an event replay ID. However, it must be used with **great caution** in production environments. + +### **Warning** +- Setting the `Replay From` parameter to `-2` (to replay all events) can quickly consume your daily limit of platform events. +- **Exceeding this limit can disrupt existing integrations and cause significant issues in a production environment.** +- Always consider the implications of replaying a large number of events, especially in a production setting. + +## Usage Instructions +1. **Select Channel Type:** Choose between Standard or Custom Platform Events. +2. **Select Channel:** Pick the specific event channel you want to monitor. +3. **Set Replay From (Optional):** Define the starting point for replaying events. Use the default value `-1` to receive only new events. +4. **Subscribe:** Click the "Subscribe" button to start monitoring the selected channel. +5. **Unsubscribe:** Click the "Unsubscribe" button to stop monitoring. + +## Production Considerations +If you are monitoring events in a production environment, the interface will highlight the potential risks, especially when using the replay functionality. Be cautious and ensure you fully understand the impact of subscribing to large volumes of events. + +--- + +**Note:** Always test the functionality in a sandbox environment before deploying it to production to avoid unintended consequences. diff --git a/docs/field-creator.md b/docs/field-creator.md new file mode 100644 index 000000000..f5b0567ef --- /dev/null +++ b/docs/field-creator.md @@ -0,0 +1,73 @@ +# Field Creator + +![image](https://github.com/user-attachments/assets/5dfe73a1-b218-471a-93b5-0f281a90ba44) + +## Supported Field Types + +The Field Creator feature supports the following field types: + +- **Checkbox** +- **Currency** +- **Number** +- **Percent** +- **Date** +- **DateTime** +- **Email** +- **Phone** +- **Url** +- **Location** +- **Picklist** +- **Multiselect Picklist** +- **Text** +- **TextArea** +- **LongTextArea** +- **Html** + +## Getting Started + +1. Open the Field Creator through the pop-up. + + + +2. Select the object you want to create fields for from the dropdown menu. + + + +## Creating Fields + +1. Click "Add Row" to add a new field. +2. Fill in the Label, API Name, and select the Field Type. +3. Click "Options" to set additional field properties (This modal will be dynamic depending on the field type). +4. Click "Permissions" to set field-level security, use the "Apply to All Fields" option in the Permissions modal to quickly set permissions for all fields. + + + + + + + +## Bulk Import (beta) + + + + +1. Click "Import CSV" to open the import modal. +2. Enter comma-separated values in the format: Label, API Name, Type. (The separator can be configured from the extension options) +3. Click "Import" to add the fields to your list. + +## Deploying Fields + +1. Review your field list for accuracy. +2. Click "Deploy Fields" to create the fields in your Salesforce org. +3. Check the deployment status icon for each field. + + + + +## Additional Features + +- Use "Clone" to duplicate a field row. +- Use "Delete" to remove a field row. +- Click "Clear All" to reset the entire field list. + + \ No newline at end of file diff --git a/docs/how-to.md b/docs/how-to.md index e0aa8695a..35d34836a 100644 --- a/docs/how-to.md +++ b/docs/how-to.md @@ -112,7 +112,7 @@ Example: If you want to _always_ open extension's links in a new tab, you can set the `openLinksInNewTab` property to `true` -![image](https://github.com/tprouvot/Salesforce-Inspector-reloaded/assets/35368290/e6ae08a9-1ee9-4809-a820-1377aebcd547) +Open link in a new tab If you want to open popup keyboard shortcuts, you can use the 'ctrl' (windows) or 'command' (mac) key with the corresponding key. Example: @@ -122,13 +122,15 @@ Example: - Org Limits : l - Download Metadata : d - Explore API : x +- Event Monitor : m +- Field Creator : f ## Disable metadata search from Shortcut tab By default when you enter keyword in the Shortcut tab, the search is performed on the Setup link shortcuts _AND_ metadata (Flows, PermissionSets and Profiles). -If you want to disable the search on the metadata, set `metadataShortcutSearch` to `false` +If you want to disable the search on the metadata, update related option: -![image](https://github.com/tprouvot/Salesforce-Inspector-reloaded/assets/35368290/a31566d8-0ad4-47e5-a1ab-3eada43b3430) +image ## Enable / Disable Flow scrollability @@ -173,9 +175,9 @@ Since Winter 24, there is a beta functionality to view a summary of the Permissi image -You can enable this view for the Shortcut search by creating a new localVariable as shown below. +You can enable this view for the Shortcut search by enabling the option as shown below. -![image](https://github.com/tprouvot/Salesforce-Inspector-reloaded/assets/35368290/f3093e4b-438c-4795-b64a-8d37651906a5) +Enable Summary Then when you click on a PermissionSet / PermissionSetGroups search result, you'll be redirected to the summary. @@ -194,7 +196,7 @@ If you want to prevent auto assignment rules, set the `createUpdateRestCalloutHe Since the plugin's api version is only updated when all productions have been updated to the new release, you may want to use the latest version during preview windows. > [!IMPORTANT] -> When you manually update the API version, it won't be overriden by extension future updates. +> When you manually update the API version, it won't be overridden by extension future updates. ![2023-11-10_09-50-55 (1)](https://github.com/tprouvot/Salesforce-Inspector-reloaded/assets/35368290/6ae51a29-9887-41a6-8148-d9e12c2dc10d) @@ -229,6 +231,10 @@ are displayed. ## Test GraphQL query +> [!WARNING] +> DEPRECATED : Since you can use Data Export to test GraphQL and also REST Explore to run the request, this should not be useful anymore. + + - Open popup and click on "Explore API" button. - Right click on the page and select "Inspect" - Execute the code in dev console: @@ -247,42 +253,24 @@ From the option page, you can customize the default favicon by: The customization is linked to the org, it means you can have different colors for DEV and UAT env for example. -Customize favicon +image + +Now if you want to populate all the orgs you visited with a custom favicon, you have two options: +- Smart mode enabled: this will analyze your environment name and populate a favicon based on this (blue for dev, green for int, purple for uat and orange for full) +- Random: this will choose a random color among all the predefined colors + +Then you click on Populate All and that's it! +Note: orgs with an existing customized favicon won't be affected. + +## Customize sandbox banner color + +From the option page, enable "Use favicon color on sandbox banner" +image + + +image -Now if you want to set a random favicon for all of your visited orgs, open dev console from one of the extension page and paste following code in dev console: -``` js -let colors = [ - 'olive', 'darkorange', 'pink', 'purple', 'firebrick', 'hotpink', 'skyblue', - 'lightcoral', 'gold', 'indigo', 'teal', 'lime', 'crimson', 'peru', 'cyan', - 'tomato', 'orchid', 'magenta', 'mediumvioletred', 'dodgerblue', 'slateblue', - 'sienna', 'maroon', 'mediumseagreen', 'plum', 'turquoise', 'deepskyblue', - 'rosybrown', 'slategray', 'darkslateblue', 'palevioletred' -]; - -let orgs = Object.keys(localStorage).filter((localKey) => - localKey.endsWith("_isSandbox") -); - -orgs.forEach((org) => { - let sfHost = org.substring(0, org.indexOf("_isSandbox")); - let existingColor = localStorage.getItem(sfHost + "_customFavicon"); - - if (!existingColor) { // Only assign a color if none is set - if (colors.length === 0) { - console.warn("No more colors available."); - return; - } - let randomIndex = Math.floor(Math.random() * colors.length); - let randomFavicon = colors[randomIndex]; - colors.splice(randomIndex, 1); // Remove the used color from the list - console.info(sfHost + "_customFavicon", randomFavicon); - localStorage.setItem(sfHost + "_customFavicon", randomFavicon); - } else { - console.info(sfHost + " already has a customFavicon: " + existingColor); - } -}); -``` ## Select all fields in a query @@ -321,4 +309,36 @@ ie for KeepAccountTeam: {"OwnerChangeOptions": {"options": [{"type": "KeepAccountTeam", "execute": true}]}} ``` -SOAP Custom Headers \ No newline at end of file +SOAP Custom Headers + +## Highlight PROD with a top border + +Production environment are critical, to avoid confusion with other orgs, you can enable an option which will add a 2px border on the top of the Salesforce UI and also in the extension's pages. + +Under `User Experience` tab, enable the option `Highlight PROD with a top border (color from favicon)`. + +highlight prod with a top border + + +## Import / Export configuration (saved query etc.) + +### With 1.25 and above +To export and import your current configuration, go to the options page and click the corresponding icon in the header: + +Import / Export Configuration + +### Export Configuration with 1.24 and below +From any page of the extension (Options, Data Export etc.), right click on the page and select `Inspect`. +Then, from the browser console, paste the following code and press enter. +``` js +const localStorageData = { ...localStorage }; +const jsonData = JSON.stringify(localStorageData, null, 2); +const blob = new Blob([jsonData], { type: "application/json" }); +const link = document.createElement("a"); +const url = URL.createObjectURL(blob); +link.href = url; +link.download = "reloadedConfiguration.json"; +document.body.appendChild(link); +link.click(); +document.body.removeChild(link); +``` \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 3b75cbded..16a2bad9b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -30,7 +30,6 @@ export default [ "target/" ], rules: { - "react/prop-types": 0, "indent": ["error", 2, {"SwitchCase": 1, "flatTernaryExpressions": true}], "quotes": ["error", "double", {"avoidEscape": true}], "semi": ["error", "always"], diff --git a/package-lock.json b/package-lock.json index e74d38a73..b65e1f5bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,13 +6,13 @@ "": { "name": "salesforce-inspector", "devDependencies": { - "@eslint/compat": "^1.0.3", - "@eslint/js": "^9.4.0", - "eslint": "^9.4.0", - "eslint-plugin-react": "^7.34.2", + "@eslint/compat": "^1.1.1", + "@eslint/js": "^9.9.1", + "eslint": "^9.12.0", + "eslint-plugin-react": "^7.35.0", "fs-extra": "11.2.0", - "globals": "^15.3.0", - "replace-in-file": "7.1.0", + "globals": "^15.9.0", + "replace-in-file": "8.1.0", "zip-dir": "2.0.0" } }, @@ -21,6 +21,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -36,6 +37,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -44,42 +46,55 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/compat": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.0.3.tgz", - "integrity": "sha512-9RaroPQaU2+SDcWav1YfuipwqnHccoiXZdUsicRQsQ/vH2wkEmRVcj344GapG/FnCeZRtqj0n6PshI+s9xkkAQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.1.1.tgz", + "integrity": "sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/config-array": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.15.1.tgz", - "integrity": "sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.3", + "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", - "minimatch": "^3.0.5" + "minimatch": "^3.1.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -103,6 +118,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -111,28 +127,64 @@ } }, "node_modules/@eslint/js": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.4.0.tgz", - "integrity": "sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg==", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.3.tgz", - "integrity": "sha512-HAbhAYKfsAC2EkTqve00ibWIZlaU74Z1EHwAjYr4PXF0YU2VEA1zSIKSSpKszRLRWwHzzRZXvK632u+uXzvsvw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", + "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -142,9 +194,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, "engines": { "node": ">=18.18" @@ -154,46 +206,138 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -206,6 +350,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -215,6 +360,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -231,6 +377,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -240,6 +387,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -254,13 +402,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -277,6 +427,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -297,6 +448,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -317,6 +469,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -335,6 +488,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -348,29 +502,21 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/arraybuffer.prototype.slice": { @@ -378,6 +524,7 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -396,16 +543,18 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -420,13 +569,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -437,6 +588,7 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -456,6 +608,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -465,6 +618,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -481,6 +635,7 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -495,6 +650,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -506,25 +662,29 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -539,6 +699,7 @@ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -556,6 +717,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -573,6 +735,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -586,10 +749,11 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -606,13 +770,15 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -630,6 +796,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -647,6 +814,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -654,17 +822,26 @@ "node": ">=0.10.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es-abstract": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -725,6 +902,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -737,6 +915,7 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -746,6 +925,7 @@ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -771,6 +951,7 @@ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -783,6 +964,7 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4", "has-tostringtag": "^1.0.2", @@ -797,6 +979,7 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.0" } @@ -806,6 +989,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -819,10 +1003,11 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -832,6 +1017,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -840,28 +1026,32 @@ } }, "node_modules/eslint": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.4.0.tgz", - "integrity": "sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA==", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", + "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/config-array": "^0.15.1", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.4.0", + "@eslint/js": "9.12.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.1", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -870,14 +1060,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { @@ -887,45 +1074,54 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-plugin-react": { - "version": "7.34.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.2.tgz", - "integrity": "sha512-2HCmrU+/JNigDN6tg55cRDKCQWicYAPB38JGSFDQt95jDm8rrvSUo7YPkOIm5l6ts1j1zCvysNcasvfTMQzUOw==", + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", - "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -939,9 +1135,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -951,14 +1147,14 @@ } }, "node_modules/espree": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", - "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, "dependencies": { - "acorn": "^8.11.3", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "eslint-visitor-keys": "^4.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -968,10 +1164,11 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -996,6 +1193,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -1005,6 +1203,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -1013,34 +1212,29 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } + "license": "MIT" }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -1053,6 +1247,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1069,6 +1264,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -1081,22 +1277,42 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1106,17 +1322,12 @@ "node": ">=14.14" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1126,6 +1337,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -1144,6 +1356,7 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1153,6 +1366,7 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -1162,6 +1376,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -1181,6 +1396,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "es-errors": "^1.3.0", @@ -1194,20 +1410,21 @@ } }, "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1218,6 +1435,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -1230,27 +1448,33 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", - "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1263,6 +1487,7 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -1279,6 +1504,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -1290,13 +1516,15 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1306,6 +1534,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1315,6 +1544,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -1327,6 +1557,7 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1339,6 +1570,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1351,6 +1583,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -1366,6 +1599,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1374,10 +1608,11 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -1386,13 +1621,15 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -1409,32 +1646,24 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.0", @@ -1449,6 +1678,7 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1" @@ -1465,6 +1695,7 @@ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -1480,6 +1711,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" }, @@ -1492,6 +1724,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -1508,6 +1741,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1516,12 +1750,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1532,6 +1770,7 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, + "license": "MIT", "dependencies": { "is-typed-array": "^1.1.13" }, @@ -1547,6 +1786,7 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -1562,6 +1802,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1571,6 +1812,7 @@ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -1583,6 +1825,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1592,6 +1835,7 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -1607,6 +1851,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1619,6 +1864,7 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1631,6 +1877,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1643,6 +1890,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -1653,20 +1901,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -1683,6 +1923,7 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1695,6 +1936,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7" }, @@ -1710,6 +1952,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -1725,6 +1968,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" }, @@ -1740,6 +1984,7 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" }, @@ -1755,6 +2000,7 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1767,6 +2013,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -1779,6 +2026,7 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4" @@ -1791,22 +2039,25 @@ } }, "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "get-intrinsic": "^1.2.1", @@ -1815,17 +2066,35 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1837,25 +2106,29 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -1868,6 +2141,7 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -1883,6 +2157,7 @@ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -1895,6 +2170,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -1904,6 +2180,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -1917,6 +2194,7 @@ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, + "license": "MIT", "dependencies": { "immediate": "~3.0.5" } @@ -1926,6 +2204,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -1940,13 +2219,15 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -1954,11 +2235,19 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1966,32 +2255,49 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2001,6 +2307,7 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2010,6 +2317,7 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", @@ -2028,6 +2336,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2042,6 +2351,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2055,28 +2365,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2089,20 +2383,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -2120,6 +2406,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2135,6 +2422,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -2145,17 +2433,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -2168,6 +2465,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2177,6 +2475,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2185,13 +2484,32 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2201,6 +2519,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -2209,13 +2528,15 @@ "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -2227,41 +2548,24 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2272,11 +2576,19 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2298,6 +2610,7 @@ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "define-properties": "^1.2.1", @@ -2312,20 +2625,34 @@ } }, "node_modules/replace-in-file": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-7.2.0.tgz", - "integrity": "sha512-CiLXVop3o8/h2Kd1PwKPPimmS9wUV0Ki6Fl8+1ITD35nB3Gl/PrW5IONpTE0AXk0z4v8WYcpEpdeZqMXvSnWpg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-8.1.0.tgz", + "integrity": "sha512-ySYfKDH6uFVC4Wt6DcZ2UX0UMCqE7xhWGVqje/15WFVwHxnel1gdZjV8nOW9Nms6h/yQE2MWyeIToeqFLZkp4w==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "glob": "^8.1.0", + "chalk": "^5.3.0", + "glob": "^10.4.2", "yargs": "^17.7.2" }, "bin": { "replace-in-file": "bin/cli.js" }, "engines": { - "node": ">=10" + "node": ">=18" + } + }, + "node_modules/replace-in-file/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/require-directory": { @@ -2333,6 +2660,7 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2342,6 +2670,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -2359,48 +2688,17 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4", @@ -2414,23 +2712,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -2448,6 +2742,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2457,6 +2752,7 @@ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -2474,6 +2770,7 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -2488,13 +2785,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2507,6 +2806,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2516,6 +2816,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -2529,11 +2830,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } @@ -2543,6 +2858,23 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -2557,6 +2889,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2578,11 +2911,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2601,6 +2946,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2615,6 +2961,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2632,6 +2979,21 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2644,6 +3006,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -2656,6 +3019,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2668,6 +3032,7 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2679,13 +3044,15 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -2698,6 +3065,7 @@ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -2712,6 +3080,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -2731,6 +3100,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -2751,6 +3121,7 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -2771,6 +3142,7 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -2786,6 +3158,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -2795,6 +3168,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -2803,13 +3177,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -2825,6 +3201,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -2837,13 +3214,14 @@ } }, "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", "dev": true, + "license": "MIT", "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.0.5", "is-finalizationregistry": "^1.0.2", @@ -2852,8 +3230,8 @@ "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -2862,17 +3240,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -2891,6 +3264,7 @@ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -2905,11 +3279,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2922,17 +3307,31 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -2942,6 +3341,7 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -2960,6 +3360,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -2969,6 +3370,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2981,6 +3383,7 @@ "resolved": "https://registry.npmjs.org/zip-dir/-/zip-dir-2.0.0.tgz", "integrity": "sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==", "dev": true, + "license": "MIT", "dependencies": { "async": "^3.2.0", "jszip": "^3.2.2" diff --git a/package.json b/package.json index 93dadf2f2..cc1cffad3 100644 --- a/package.json +++ b/package.json @@ -8,13 +8,13 @@ "chrome-release-build": "node scripts/release-build.js chrome" }, "devDependencies": { - "@eslint/compat": "^1.0.3", - "@eslint/js": "^9.4.0", - "eslint": "^9.4.0", - "eslint-plugin-react": "^7.34.2", + "@eslint/compat": "^1.1.1", + "@eslint/js": "^9.9.1", + "eslint": "^9.12.0", + "eslint-plugin-react": "^7.35.0", "fs-extra": "11.2.0", - "globals": "^15.3.0", - "replace-in-file": "7.1.0", + "globals": "^15.9.0", + "replace-in-file": "8.1.0", "zip-dir": "2.0.0" } -} +} \ No newline at end of file