From 86027fdb6f10c5c99fd4b226eb297948a4e0a5c0 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 30 Mar 2023 15:40:06 +0100 Subject: [PATCH] Add Migrate app (#16458) Adds the UI for self-serve migrations --- .../app/components/gh-migrate-iframe.hbs | 1 + .../admin/app/components/gh-migrate-iframe.js | 79 ++++++++++++ .../admin/app/components/gh-migrate-modal.hbs | 5 + .../admin/app/components/gh-migrate-modal.js | 10 ++ ghost/admin/app/controllers/migrate.js | 17 +++ ghost/admin/app/router.js | 2 + ghost/admin/app/routes/migrate.js | 21 +++ ghost/admin/app/services/feature.js | 1 + ghost/admin/app/services/migrate.js | 121 ++++++++++++++++++ ghost/admin/app/styles/app.css | 1 + ghost/admin/app/styles/layouts/migrate.css | 70 ++++++++++ ghost/admin/app/templates/migrate.hbs | 9 ++ ghost/admin/app/templates/settings/labs.hbs | 27 ++++ ghost/admin/package.json | 1 + ghost/core/core/shared/labs.js | 1 + yarn.lock | 118 ++++++----------- 16 files changed, 406 insertions(+), 78 deletions(-) create mode 100644 ghost/admin/app/components/gh-migrate-iframe.hbs create mode 100644 ghost/admin/app/components/gh-migrate-iframe.js create mode 100644 ghost/admin/app/components/gh-migrate-modal.hbs create mode 100644 ghost/admin/app/components/gh-migrate-modal.js create mode 100644 ghost/admin/app/controllers/migrate.js create mode 100644 ghost/admin/app/routes/migrate.js create mode 100644 ghost/admin/app/services/migrate.js create mode 100644 ghost/admin/app/styles/layouts/migrate.css create mode 100644 ghost/admin/app/templates/migrate.hbs diff --git a/ghost/admin/app/components/gh-migrate-iframe.hbs b/ghost/admin/app/components/gh-migrate-iframe.hbs new file mode 100644 index 0000000000..1f15365841 --- /dev/null +++ b/ghost/admin/app/components/gh-migrate-iframe.hbs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ghost/admin/app/components/gh-migrate-iframe.js b/ghost/admin/app/components/gh-migrate-iframe.js new file mode 100644 index 0000000000..be54474df0 --- /dev/null +++ b/ghost/admin/app/components/gh-migrate-iframe.js @@ -0,0 +1,79 @@ +import Component from '@glimmer/component'; +import {action} from '@ember/object'; +import {inject as service} from '@ember/service'; + +export default class GhMigrateIframe extends Component { + @service migrate; + @service router; + @service feature; + @service notifications; + + willDestroy() { + super.willDestroy(...arguments); + window.removeEventListener('message', this.handleIframeMessage); + } + + @action + setup() { + this.migrate.getMigrateIframe().src = this.migrate.getIframeURL(); + window.addEventListener('message', this.handleIframeMessage); + } + + @action + async handleIframeMessage(event) { + if (this.isDestroyed || this.isDestroying) { + return; + } + + // Only process messages coming from the migrate iframe + const url = new URL(this.migrate.getIframeURL()); + if (event.origin === url.origin) { + if (event.data?.request === 'apiUrl') { + await this._handleUrlRequest(); + return; + } + + if (event.data?.route) { + this._handleRouteUpdate(event.data); + return; + } + + if (event.data?.siteData) { + this._handleSiteDataUpdate(event.data); + return; + } + } + } + + // The iframe can send route updates to navigate to within Admin, as some routes + // have to be rendered within the iframe and others require to break out of it. + _handleRouteUpdate(data) { + const route = data.route; + this.migrate.isIframeTransition = route?.includes('/migrate'); + this.migrate.toggleMigrateWindow(this.migrate.isIframeTransition); + this.router.transitionTo(route); + } + + async _handleUrlRequest() { + const theToken = await this.migrate.apiToken(); + + this.migrate.getMigrateIframe().contentWindow.postMessage({ + request: 'initialData', + response: { + apiUrl: this.migrate.apiUrl, + apiToken: theToken, + darkMode: this.feature.nightShift, + stripe: this.migrate.isStripeConnected + } + }, new URL(this.migrate.getIframeURL()).origin); + } + + _handleSiteDataUpdate(data) { + this.migrate.siteData = data?.siteData ?? {}; + + if (this.migrate.siteData?.migrationComplete) { + // If we want to show a notification, this is where to do it + // this.notifications.showAlert(htmlSafe(`Migration complete!`), {type: 'success', key: 'migrate.completed'}); // Green persistent banner at the top + } + } +} diff --git a/ghost/admin/app/components/gh-migrate-modal.hbs b/ghost/admin/app/components/gh-migrate-modal.hbs new file mode 100644 index 0000000000..47d38594ab --- /dev/null +++ b/ghost/admin/app/components/gh-migrate-modal.hbs @@ -0,0 +1,5 @@ +
+
+ +
+
\ No newline at end of file diff --git a/ghost/admin/app/components/gh-migrate-modal.js b/ghost/admin/app/components/gh-migrate-modal.js new file mode 100644 index 0000000000..0beac5ec60 --- /dev/null +++ b/ghost/admin/app/components/gh-migrate-modal.js @@ -0,0 +1,10 @@ +import Component from '@glimmer/component'; +import {inject as service} from '@ember/service'; + +export default class GhMigrateModal extends Component { + @service migrate; + + get visibilityClass() { + return this.migrate.migrateWindowOpen ? 'gh-migrate' : 'gh-migrate closed'; + } +} diff --git a/ghost/admin/app/controllers/migrate.js b/ghost/admin/app/controllers/migrate.js new file mode 100644 index 0000000000..f08060bfde --- /dev/null +++ b/ghost/admin/app/controllers/migrate.js @@ -0,0 +1,17 @@ +import Controller from '@ember/controller'; +import {action} from '@ember/object'; +import {inject as service} from '@ember/service'; + +export default class MigrateController extends Controller { + @service migrate; + @service router; + + get visibilityClass() { + return this.migrate.isIframeTransition ? 'migrate iframe-migrate-container' : ' migrate fullscreen-migrate-container'; + } + + @action + closeMigrate() { + this.router.transitionTo('/settings/labs'); + } +} diff --git a/ghost/admin/app/router.js b/ghost/admin/app/router.js index 166e86b4f2..f4d3a3cd8c 100644 --- a/ghost/admin/app/router.js +++ b/ghost/admin/app/router.js @@ -112,6 +112,8 @@ Router.map(function () { }); // this.route('settings.labs.import', {path: '/settings/labs/import'}); + this.route('migrate'); + this.route('members', function () { this.route('import'); }); diff --git a/ghost/admin/app/routes/migrate.js b/ghost/admin/app/routes/migrate.js new file mode 100644 index 0000000000..bc04cc2884 --- /dev/null +++ b/ghost/admin/app/routes/migrate.js @@ -0,0 +1,21 @@ +import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; +import {inject as service} from '@ember/service'; + +export default class MigrateRoute extends AuthenticatedRoute { + @service feature; + @service session; + + beforeModel() { + super.beforeModel(...arguments); + + // Redirect to Home is feature not enabled + if (!this.feature.migrateApp) { + return this.transitionTo('home'); + } + + // Only allow Owner & Administrator to access this route + if (!this.session.user.isAdmin || !this.feature.migrateApp) { + return this.transitionTo('home'); + } + } +} diff --git a/ghost/admin/app/services/feature.js b/ghost/admin/app/services/feature.js index de24b3e9ca..dd02d87d7c 100644 --- a/ghost/admin/app/services/feature.js +++ b/ghost/admin/app/services/feature.js @@ -70,6 +70,7 @@ export default class FeatureService extends Service { @feature('websockets') websockets; @feature('stripeAutomaticTax') stripeAutomaticTax; @feature('makingItRain') makingItRain; + @feature('migrateApp') migrateApp; _user = null; diff --git a/ghost/admin/app/services/migrate.js b/ghost/admin/app/services/migrate.js new file mode 100644 index 0000000000..4365d77d67 --- /dev/null +++ b/ghost/admin/app/services/migrate.js @@ -0,0 +1,121 @@ +import Service, {inject as service} from '@ember/service'; +import {SignJWT} from 'jose'; +import {tracked} from '@glimmer/tracking'; + +export default class MigrateService extends Service { + @service ajax; + @service router; + @service ghostPaths; + @service settings; + + migrateUrl = 'https://import.ghost.org'; + migrateRouteRoot = '#/migrate'; + + @tracked migrateWindowOpen = false; + @tracked siteData = null; + @tracked previousRoute = null; + @tracked isIframeTransition = false; + + get apiUrl() { + const origin = window.location.origin; + const subdir = this.ghostPaths.subdir; + const rootURL = this.router.rootURL; + let url = this.ghostPaths.url.join(origin, subdir, rootURL); + url = url.replace(/\/$/, ''); // Strips the trailing slash + return url; + } + + async apiToken() { + // TODO: Getting the token can be improved + const ghostIntegrationsUrl = this.ghostPaths.url.api('integrations') + '?include=api_keys'; + return this.ajax.request(ghostIntegrationsUrl).then(async (response) => { + const ssmIntegration = response.integrations.find(r => r.slug === 'self-serve-migration'); + + const key = ssmIntegration.api_keys[0].secret; + const [id, secret] = key.split(':'); + + function hexToBytes(hex) { + let bytes = []; + for (let c = 0; c < hex.length; c += 2) { + bytes.push(parseInt(hex.substr(c, 2), 16)); + } + return new Uint8Array(bytes); + } + + const encodedSecret = hexToBytes(secret); + + const token = await new SignJWT({}) + .setProtectedHeader({ + alg: 'HS256', + typ: 'JWT', + kid: id + }) + .setIssuedAt() + .setExpirationTime('5m') + .setAudience('/admin/') + .sign(encodedSecret); + + return token; + }).catch((error) => { + throw error; + }); + } + + get isStripeConnected() { + return (this.settings.stripeConnectAccountId && this.settings.stripeConnectPublishableKey && this.settings.stripeConnectLivemode) ? true : false; + } + + constructor() { + super(...arguments); + } + + getIframeURL() { + let url = this.migrateUrl; + + return url; + } + + // Sends a route update to a child route in the BMA, because we can't control + // navigating to it otherwise + sendRouteUpdate(route) { + this.getMigrateIframe().contentWindow.postMessage({ + query: 'routeUpdate', + response: route + }, '*'); + } + + // Controls migrate window modal visibility and sync of the URL visible in browser + // and the URL opened on the iframe. It is responsible to non user triggered iframe opening, + // for example: by entering "/migrate" route in the URL or using history navigation (back and forward) + toggleMigrateWindow(value) { + if (this.migrateWindowOpen && value) { + // don't attempt to open again + return; + } + this.migrateWindowOpen = value; + } + + // Controls navigation to migrate window modal which is triggered from the application UI. + // For example: pressing "View migrate" link in navigation menu. It's main side effect is + // remembering the route from which the action has been triggered - "previousRoute" so it + // could be reused when closing the migrate window + openMigrateWindow(currentRoute, childRoute) { + if (this.migrateWindowOpen) { + // don't attempt to open again + return; + } + + this.previousRoute = currentRoute; + + // Ensures correct "getIframeURL" calculation when syncing iframe location + // in toggleMigrateWindow + window.location.hash = childRoute || '/migrate'; + + this.router.transitionTo(childRoute || '/migrate'); + this.toggleMigrateWindow(true); + } + + getMigrateIframe() { + return document.getElementById('migrate-frame'); + } +} diff --git a/ghost/admin/app/styles/app.css b/ghost/admin/app/styles/app.css index fc63f91c5e..953060cf7e 100644 --- a/ghost/admin/app/styles/app.css +++ b/ghost/admin/app/styles/app.css @@ -73,6 +73,7 @@ @import "layouts/offers.css"; @import "layouts/explore.css"; @import "layouts/mentions.css"; +@import "layouts/migrate.css"; /* ---------------------------✈️----------------------------- */ diff --git a/ghost/admin/app/styles/layouts/migrate.css b/ghost/admin/app/styles/layouts/migrate.css new file mode 100644 index 0000000000..66c1603256 --- /dev/null +++ b/ghost/admin/app/styles/layouts/migrate.css @@ -0,0 +1,70 @@ +.gh-migrate { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 9999; + background: var(--main-bg-color); +} + +.gh-migrate-container { + position: relative; + height: 100%; + width: 100%; +} + +.gh-migrate-fullscreen-container { + position: relative; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 10000; + height: 100vh; + background: var(--main-bg-color); + overflow: hidden; +} + +.gh-migrate .migrate-frame { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: none; + z-index: 10; + transform: translate3d(0, 0, 0); +} + +.gh-migrate-close { + display: flex; + justify-content: flex-end; + align-items: center; + padding: 2rem; + position: absolute; + top: 0; + right: 0; + z-index: 10000; /* One more than the iframe wrapper */ +} + +.gh-migrate-close a { + color: var(--middarkgrey); +} + +.gh-migrate-close a svg { + stroke: var(--middarkgrey); + width: 18px; + height: auto; +} + +.gh-migrate-close a svg path { + stroke-width: 1px; +} + +.gh-migrate-close a:hover svg { + stroke: var(--darkgrey); +} diff --git a/ghost/admin/app/templates/migrate.hbs b/ghost/admin/app/templates/migrate.hbs new file mode 100644 index 0000000000..a7a8cf32d6 --- /dev/null +++ b/ghost/admin/app/templates/migrate.hbs @@ -0,0 +1,9 @@ +
+ + + +
\ No newline at end of file diff --git a/ghost/admin/app/templates/settings/labs.hbs b/ghost/admin/app/templates/settings/labs.hbs index 6154e2dc1b..9565ce29e2 100644 --- a/ghost/admin/app/templates/settings/labs.hbs +++ b/ghost/admin/app/templates/settings/labs.hbs @@ -31,6 +31,20 @@ + {{#if this.feature.migrateApp}} +
+
+
+

Migrate content

+

Import your content from Substack

+
+ + Open + +
+
+ {{/if}} +
@@ -252,6 +266,19 @@
+
+
+
+

Migrate content

+

+ Import content from other platforms to Ghost +

+
+
+ +
+
+
{{/if}} diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 252c0ba1ca..91b929c02c 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -180,6 +180,7 @@ "*.js": "eslint" }, "dependencies": { + "jose": "4.13.1", "path-browserify": "1.0.1", "webpack": "5.76.3" } diff --git a/ghost/core/core/shared/labs.js b/ghost/core/core/shared/labs.js index 05a90d0e56..4ad5d8840e 100644 --- a/ghost/core/core/shared/labs.js +++ b/ghost/core/core/shared/labs.js @@ -33,6 +33,7 @@ const BETA_FEATURES = [ const ALPHA_FEATURES = [ 'urlCache', + 'migrateApp', 'lexicalEditor', 'websockets', 'stripeAutomaticTax', diff --git a/yarn.lock b/yarn.lock index 98afe63f05..08bf0a7fae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1964,15 +1964,15 @@ ee-log "^3.0.0" section-tests "^1.3.0" -"@elastic/elasticsearch@8.5.0", "@elastic/elasticsearch@8.6.0": - version "8.5.0" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.5.0.tgz#407aee0950a082ee76735a567f2571cf4301d4ea" - integrity sha512-iOgr/3zQi84WmPhAplnK2W13R89VXD2oc6WhlQmH3bARQwmI+De23ZJKBEn7bvuG/AHMAqasPXX7uJIiJa2MqQ== +"@elastic/elasticsearch@8.6.0": + version "8.6.0" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.6.0.tgz#c474f49808deee64b5bc5b8f938bf78f4468cb94" + integrity sha512-mN5EbbgSp1rfRmQ/5Hv7jqAK8xhGJxCg7G84xje8hSefE59P+HPPCv/+DgesCUSJdZpwXIo0DwOWHfHvktxxLw== dependencies: - "@elastic/transport" "^8.2.0" + "@elastic/transport" "^8.3.1" tslib "^2.4.0" -"@elastic/transport@^8.2.0": +"@elastic/transport@^8.3.1": version "8.3.1" resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.3.1.tgz#e7569d7df35b03108ea7aa886113800245faa17f" integrity sha512-jv/Yp2VLvv5tSMEOF8iGrtL2YsYHbpf4s+nDsItxUTLFTzuJGpnsB/xBlfsoT2kAYEnWHiSJuqrbRcpXEI/SEQ== @@ -2177,7 +2177,7 @@ ember-cli-babel "^7.26.11" ember-modifier-manager-polyfill "^1.2.0" -"@ember/render-modifiers@^1.0.2 || ^2.0.0", "@ember/render-modifiers@^2.0.0", "@ember/render-modifiers@^2.0.4": +"@ember/render-modifiers@^1.0.2 || ^2.0.0", "@ember/render-modifiers@^2.0.4": version "2.0.5" resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.0.5.tgz#4b1d9496a82ca471aeaa3ecddd94ef089450f415" integrity sha512-5cJ1niIdOJC6k6KtIn9HGbr1DATJQp4ZqMv1vbi6LKQWbVCQ3byvKONtUEi3H0wcewlrcaWCqXOgm0nACzCOQA== @@ -2308,7 +2308,7 @@ resolve "^1.8.1" semver "^7.3.2" -"@embroider/macros@0.47.2", "@embroider/macros@^0.47.2": +"@embroider/macros@0.47.2": version "0.47.2" resolved "https://registry.yarnpkg.com/@embroider/macros/-/macros-0.47.2.tgz#23cbe92cac3c24747f054e1eea2a22538bf7ebd0" integrity sha512-ViNWluJCeM5OPlM3rs8kdOz3RV5rpfXX5D2rDnc/q86xRS0xf4NFEjYRV7W6fBcD0b3v5jSHDTwrjq9Kee4rHg== @@ -2420,15 +2420,6 @@ broccoli-funnel "^3.0.5" ember-cli-babel "^7.23.1" -"@embroider/util@^0.47.2": - version "0.47.2" - resolved "https://registry.yarnpkg.com/@embroider/util/-/util-0.47.2.tgz#d06497b4b84c07ed9c7b628293bb019c533f4556" - integrity sha512-g9OqnFJPktGu9NS0Ug3Pxz1JE3jeDceeVE4IrlxDrVmBXMA/GrBvpwjolWgl6jh97cMJyExdz62jIvPHV4256Q== - dependencies: - "@embroider/macros" "0.47.2" - broccoli-funnel "^3.0.5" - ember-cli-babel "^7.23.1" - "@esbuild/android-arm64@0.17.11": version "0.17.11" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.11.tgz#52c3e6cabc19c5e4c1c0c01cb58f0442338e1c14" @@ -4989,7 +4980,7 @@ "@tryghost/root-utils" "^0.3.20" debug "^4.3.1" -"@tryghost/elasticsearch@^3.0.10": +"@tryghost/elasticsearch@^3.0.10", "@tryghost/elasticsearch@^3.0.9": version "3.0.10" resolved "https://registry.yarnpkg.com/@tryghost/elasticsearch/-/elasticsearch-3.0.10.tgz#7d0417749193afba9688834e5de35409770c7fac" integrity sha512-/oi/rqYq2Q5fEYOcGKdCTem0+cKTpHdCO3ws0F2jUq7I99pPMJcafdHQXq71A4y0veUvqAKIGbaAEzMoZ4LyMQ== @@ -5067,7 +5058,7 @@ resolved "https://registry.yarnpkg.com/@tryghost/http-cache-utils/-/http-cache-utils-0.1.7.tgz#8668d7caeae2efda571fd5858f058cf0c4ecf3de" integrity sha512-ztqN84+P6P51wegjPizSRNyxWhNNnGmKR/GmkkaMtwNmlZE4FUnjynia8DgwK7H4DtWAcXW01hilsfjiv4dlGg== -"@tryghost/http-stream@^0.1.18": +"@tryghost/http-stream@^0.1.17", "@tryghost/http-stream@^0.1.18": version "0.1.18" resolved "https://registry.yarnpkg.com/@tryghost/http-stream/-/http-stream-0.1.18.tgz#1705f447206764112b112ea9088d879936dba570" integrity sha512-Dc+8SI7AlaWKzDqgPw3CftRKxHmyBCWzC19teC4ybCBcaxAZYrNjNVhvLLh9tQT8FbKv/RYw58/uDomKFamKCg== @@ -5219,7 +5210,24 @@ lodash "^4.17.21" luxon "^1.26.0" -"@tryghost/logging@2.4.0", "@tryghost/logging@2.4.1", "@tryghost/logging@^2.4.0": +"@tryghost/logging@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.0.tgz#b6620ba3029b5d481d317a04eb5c578b0b13ae08" + integrity sha512-Q3y3gPHOGgijViCkuu2ARr4G++jnGDeafWD9JdPZO8CK6K3N20Dun3M7ujX3OY+u5UXhQTsMQw+nC5pjPRBRGg== + dependencies: + "@tryghost/bunyan-rotating-filestream" "^0.0.7" + "@tryghost/elasticsearch" "^3.0.9" + "@tryghost/http-stream" "^0.1.17" + "@tryghost/pretty-stream" "^0.1.15" + "@tryghost/root-utils" "^0.3.19" + bunyan "^1.8.15" + bunyan-loggly "^1.4.2" + fs-extra "^10.0.0" + gelf-stream "^1.1.1" + json-stringify-safe "^5.0.1" + lodash "^4.17.21" + +"@tryghost/logging@2.4.1", "@tryghost/logging@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.1.tgz#cc4697cb3b530812fe6b64f2c0a94ef15d3578b2" integrity sha512-l6OsPbDkVYRcsbmxgyTk0nNJPwRzQnigIvOJzBiF2An4edc2hNzCPifT/VQP0PRGLh5qQXDZt5YNRgd2CcoFmA== @@ -5324,7 +5332,7 @@ chalk "^4.1.0" sywac "^1.3.0" -"@tryghost/pretty-stream@^0.1.16": +"@tryghost/pretty-stream@^0.1.15", "@tryghost/pretty-stream@^0.1.16": version "0.1.16" resolved "https://registry.yarnpkg.com/@tryghost/pretty-stream/-/pretty-stream-0.1.16.tgz#e11ba791b1eee3d7f73297a6f034ac5843b81c9b" integrity sha512-gvwge1djDB2rifudWk4dsbkuiOt2iizelU/XPvG5P6tjoI4qSuyMT7nVAvfiY9yCHicZ7eB0R7YeYyyKzZUlJA== @@ -12271,7 +12279,7 @@ ember-auto-import@2.6.1, "ember-auto-import@^1.12.1 || ^2.4.3", ember-auto-impor typescript-memoize "^1.0.0-alpha.3" walk-sync "^3.0.0" -ember-auto-import@^1.11.2, ember-auto-import@^1.11.3, ember-auto-import@^1.12.0: +ember-auto-import@^1.11.3, ember-auto-import@^1.12.0: version "1.12.2" resolved "https://registry.yarnpkg.com/ember-auto-import/-/ember-auto-import-1.12.2.tgz#cc7298ee5c0654b0249267de68fb27a2861c3579" integrity sha512-gLqML2k77AuUiXxWNon1FSzuG1DV7PEPpCLCU5aJvf6fdL6rmFfElsZRh+8ELEB/qP9dT+LHjNEunVzd2dYc8A== @@ -12306,25 +12314,7 @@ ember-auto-import@^1.11.2, ember-auto-import@^1.11.3, ember-auto-import@^1.12.0: walk-sync "^0.3.3" webpack "^4.43.0" -ember-basic-dropdown@^3.0.11: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ember-basic-dropdown/-/ember-basic-dropdown-3.1.0.tgz#47c292de890d1958057736c00b8eb2b8017d530b" - integrity sha512-UISvgJHfiJ8FeXqH8ZN+NmoImN8p6Sb+85qlEv853hLuEfEYnFUqLNhea8nNl9CpFqcD3yU4dKbhYtc6nB39aQ== - dependencies: - "@ember/render-modifiers" "^2.0.0" - "@embroider/macros" "^0.47.2" - "@embroider/util" "^0.47.2" - "@glimmer/component" "^1.0.4" - "@glimmer/tracking" "^1.0.4" - ember-cli-babel "^7.26.6" - ember-cli-htmlbars "^6.0.0" - ember-cli-typescript "^4.2.1" - ember-element-helper "^0.5.5" - ember-maybe-in-element "^2.0.3" - ember-style-modifier "^0.7.0" - ember-truth-helpers "^2.1.0 || ^3.0.0" - -ember-basic-dropdown@^6.0.0: +ember-basic-dropdown@6.0.2, ember-basic-dropdown@^3.0.11, ember-basic-dropdown@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/ember-basic-dropdown/-/ember-basic-dropdown-6.0.2.tgz#af47dbd544c605cf9cbc62225185616356aeef52" integrity sha512-JgI/cy7eS/Y2WoQl7B2Mko/1aFTAlxr5d+KpQeH7rBKOFml7IQtLvhiDQrpU/FLkrQ9aLNEJtzwtDZV1xQxAKA== @@ -12921,7 +12911,7 @@ ember-cli@3.24.0: workerpool "^6.0.3" yam "^1.0.0" -ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-compatibility-helpers@^1.2.4, ember-compatibility-helpers@^1.2.5: +ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-compatibility-helpers@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.6.tgz#603579ab2fb14be567ef944da3fc2d355f779cd8" integrity sha512-2UBUa5SAuPg8/kRVaiOfTwlXdeVweal1zdNPibwItrhR0IvPrXpaqwJDlEZnWKEoB+h33V0JIfiWleSG6hGkkA== @@ -13046,7 +13036,7 @@ ember-drag-drop@0.4.8: dependencies: ember-cli-babel "^6.6.0" -ember-element-helper@^0.5.0, ember-element-helper@^0.5.5: +ember-element-helper@^0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/ember-element-helper/-/ember-element-helper-0.5.5.tgz#4a9ecb4dce57ee7f5ceb868a53c7b498c729f056" integrity sha512-Tu3hsI+/mjHBUvw62Qi+YDZtKkn59V66CjwbgfNTZZ7aHf4gFm1ow4zJ4WLnpnie8p9FvOmIUxwl5HvgPJIcFA== @@ -13157,7 +13147,7 @@ ember-in-element-polyfill@^1.0.1: ember-cli-htmlbars "^5.3.1" ember-cli-version-checker "^5.1.2" -ember-in-viewport@4.1.0: +ember-in-viewport@4.1.0, ember-in-viewport@~3.10.2: version "4.1.0" resolved "https://registry.yarnpkg.com/ember-in-viewport/-/ember-in-viewport-4.1.0.tgz#a9359a1e4a99d9d6ab32e926749dc131084ed896" integrity sha512-3y6qWXuJPPc6vX2GfxWgtr+sDjb+bdZF9babstr0lTd8t8c1b42gJ13GaJqlylZIyZz2dEXFCimX9WAeudPv9g== @@ -13171,18 +13161,6 @@ ember-in-viewport@4.1.0: intersection-observer-admin "~0.3.2" raf-pool "~0.1.4" -ember-in-viewport@~3.10.2: - version "3.10.3" - resolved "https://registry.yarnpkg.com/ember-in-viewport/-/ember-in-viewport-3.10.3.tgz#317472bb82bed11f7895821b799349c6a7406e81" - integrity sha512-hSX7p+G6hJjZaY2BAqzyuiMP7QIHzQ4g0+ZBnEwAa8GMbILFAtzPx5A4XEX8wY6dSzhHB9n9jkcWZdmaML6q8A== - dependencies: - ember-auto-import "^1.11.2" - ember-cli-babel "^7.26.3" - ember-modifier "^2.1.0" - fast-deep-equal "^2.0.1" - intersection-observer-admin "~0.3.2" - raf-pool "~0.1.4" - ember-infinity@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/ember-infinity/-/ember-infinity-2.3.0.tgz#73fe13883c212147bfba4f0b2fe8c8d2a96887d9" @@ -13285,20 +13263,7 @@ ember-modifier@4.1.0, "ember-modifier@^2.1.2 || ^3.0.0 || ^4.0.0", "ember-modifi ember-cli-normalize-entity-name "^1.0.0" ember-cli-string-utils "^1.1.0" -ember-modifier@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-2.1.2.tgz#62d18faedf972dcd9d34f90d5321fbc943d139b1" - integrity sha512-3Lsu1fV1sIGa66HOW07RZc6EHISwKt5VA5AUnFss2HX6OTfpxTJ2qvPctt2Yt0XPQXJ4G6BQasr/F35CX7UGJA== - dependencies: - ember-cli-babel "^7.22.1" - ember-cli-normalize-entity-name "^1.0.0" - ember-cli-string-utils "^1.1.0" - ember-cli-typescript "^3.1.3" - ember-compatibility-helpers "^1.2.4" - ember-destroyable-polyfill "^2.0.2" - ember-modifier-manager-polyfill "^1.2.0" - -ember-modifier@^3.0.0, ember-modifier@^3.2.7: +ember-modifier@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-3.2.7.tgz#f2d35b7c867cbfc549e1acd8d8903c5ecd02ea4b" integrity sha512-ezcPQhH8jUfcJQbbHji4/ZG/h0yyj1jRDknfYue/ypQS8fM8LrGcCMo0rjDZLzL1Vd11InjNs3BD7BdxFlzGoA== @@ -13460,14 +13425,6 @@ ember-source@3.24.0: semver "^6.1.1" silent-error "^1.1.1" -ember-style-modifier@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/ember-style-modifier/-/ember-style-modifier-0.7.0.tgz#85b3dfd7e4bc2bd546df595f2dab4fb141cf7d87" - integrity sha512-iDzffiwJcb9j6gu3g8CxzZOTvRZ0BmLMEFl+uyqjiaj72VVND9+HbLyQRw1/ewPAtinhSktxxTTdwU/JO+stLw== - dependencies: - ember-cli-babel "^7.26.6" - ember-modifier "^3.0.0" - "ember-style-modifier@^0.8.0 || ^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/ember-style-modifier/-/ember-style-modifier-1.0.0.tgz#96e5d342a255d8c1cba1637779dbb1949322e139" @@ -18776,6 +18733,11 @@ join-component@^1.1.0: resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== +jose@4.13.1: + version "4.13.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.13.1.tgz#449111bb5ab171db85c03f1bd2cb1647ca06db1c" + integrity sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ== + jose@^4.10.4: version "4.11.2" resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.2.tgz#d9699307c02e18ff56825843ba90e2fae9f09e23"