diff --git a/core/frontend/helpers/cancel_link.js b/core/frontend/helpers/cancel_link.js
new file mode 100644
index 0000000000..35ec97d8d6
--- /dev/null
+++ b/core/frontend/helpers/cancel_link.js
@@ -0,0 +1,32 @@
+// # {{cancel_link}} Helper
+// Usage: `{{cancel_link}}`, `{{cancel_link class="custom-cancel-class"}}`, `{{cancel_link cancelLabel="Cancel please!"}}`
+//
+// Should be used inside of a subscription context, e.g.: `{{#foreach @member.subscriptions}} {{cancel_link}} {{/foreach}}`
+// Outputs cancel/renew links to manage subscription renewal after the subscription period ends.
+//
+// Defaults to class="cancel-subscription-link" errorClass="cancel-subscription-error" cancelLabel="Cancel subscription" continueLabel="Continue subscription"
+
+const proxy = require('./proxy');
+
+const templates = proxy.templates;
+const errors = proxy.errors;
+const i18n = proxy.i18n;
+
+module.exports = function excerpt(options) {
+ let truncateOptions = (options || {}).hash || {};
+
+ if (this.id === undefined || this.cancel_at_period_end === undefined) {
+ throw new errors.IncorrectUsageError({message: i18n.t('warnings.helpers.cancel_link.invalidData')});
+ }
+
+ const data = {
+ id: this.id,
+ cancel_at_period_end: this.cancel_at_period_end,
+ class: truncateOptions.class || 'gh-subscription-cancel',
+ errorClass: truncateOptions.errorClass || 'gh-error gh-error-subscription-cancel',
+ cancelLabel: truncateOptions.cancelLabel || 'Cancel subscription',
+ continueLabel: truncateOptions.continueLabel || 'Continue subscription'
+ };
+
+ return templates.execute('cancel_link', data);
+};
diff --git a/core/frontend/helpers/index.js b/core/frontend/helpers/index.js
index f0e5b261c8..342ec1302d 100644
--- a/core/frontend/helpers/index.js
+++ b/core/frontend/helpers/index.js
@@ -1,13 +1,17 @@
-var coreHelpers = {},
- register = require('./register'),
- registerThemeHelper = register.registerThemeHelper,
- registerAsyncThemeHelper = register.registerAsyncThemeHelper,
- registerAllCoreHelpers;
+const proxy = require('./proxy');
+const register = require('./register');
+
+const coreHelpers = {};
+const registerThemeHelper = register.registerThemeHelper;
+const registerAsyncThemeHelper = register.registerAsyncThemeHelper;
+
+let registerAllCoreHelpers;
coreHelpers.asset = require('./asset');
coreHelpers.author = require('./author');
coreHelpers.authors = require('./authors');
coreHelpers.body_class = require('./body_class');
+coreHelpers.cancel_link = require('./cancel_link');
coreHelpers.concat = require('./concat');
coreHelpers.content = require('./content');
coreHelpers.date = require('./date');
@@ -40,12 +44,26 @@ coreHelpers.title = require('./title');
coreHelpers.twitter_url = require('./twitter_url');
coreHelpers.url = require('./url');
+function labsEnabledMembers() {
+ let self = this, args = arguments;
+
+ return proxy.labs.enabledHelper({
+ flagKey: 'members',
+ flagName: 'Members',
+ helperName: 'cancel_link',
+ helpUrl: 'https://ghost.org/faq/members/'
+ }, () => {
+ return coreHelpers.cancel_link.apply(self, args);
+ });
+}
+
registerAllCoreHelpers = function registerAllCoreHelpers() {
// Register theme helpers
registerThemeHelper('asset', coreHelpers.asset);
registerThemeHelper('author', coreHelpers.author);
registerThemeHelper('authors', coreHelpers.authors);
registerThemeHelper('body_class', coreHelpers.body_class);
+ registerThemeHelper('cancel_link', labsEnabledMembers);
registerThemeHelper('concat', coreHelpers.concat);
registerThemeHelper('content', coreHelpers.content);
registerThemeHelper('date', coreHelpers.date);
diff --git a/core/frontend/helpers/tpl/cancel_link.hbs b/core/frontend/helpers/tpl/cancel_link.hbs
new file mode 100644
index 0000000000..0d0a293f28
--- /dev/null
+++ b/core/frontend/helpers/tpl/cancel_link.hbs
@@ -0,0 +1,11 @@
+{{#if cancel_at_period_end}}
+
+ {{continueLabel}}
+
+{{else}}
+
+ {{cancelLabel}}
+
+{{/if}}
+
+
diff --git a/core/server/data/migrations/versions/3.2/01-add-cancel-at-period-end-to-subscriptions.js b/core/server/data/migrations/versions/3.2/01-add-cancel-at-period-end-to-subscriptions.js
new file mode 100644
index 0000000000..1065a01e1c
--- /dev/null
+++ b/core/server/data/migrations/versions/3.2/01-add-cancel-at-period-end-to-subscriptions.js
@@ -0,0 +1,25 @@
+const commands = require('../../../schema').commands;
+
+module.exports.up = commands.createColumnMigration({
+ table: 'members_stripe_customers_subscriptions',
+ column: 'cancel_at_period_end',
+ dbIsInCorrectState(columnExists) {
+ return columnExists === true;
+ },
+ operation: commands.addColumn,
+ operationVerb: 'Adding'
+});
+
+module.exports.down = commands.createColumnMigration({
+ table: 'members_stripe_customers_subscriptions',
+ column: 'cancel_at_period_end',
+ dbIsInCorrectState(columnExists) {
+ return columnExists === false;
+ },
+ operation: commands.dropColumn,
+ operationVerb: 'Removing'
+});
+
+module.exports.config = {
+ transaction: true
+};
diff --git a/core/server/data/schema/schema.js b/core/server/data/schema/schema.js
index 1259c33ae9..aadf4e69bd 100644
--- a/core/server/data/schema/schema.js
+++ b/core/server/data/schema/schema.js
@@ -351,6 +351,7 @@ module.exports = {
subscription_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
plan_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
status: {type: 'string', maxlength: 50, nullable: false},
+ cancel_at_period_end: {type: 'bool', nullable: false, defaultTo: false},
current_period_end: {type: 'dateTime', nullable: false},
start_date: {type: 'dateTime', nullable: false},
default_payment_card_last4: {type: 'string', maxlength: 4, nullable: true},
diff --git a/core/server/public/members.js b/core/server/public/members.js
index edf3f1baf7..47ee3e3591 100644
--- a/core/server/public/members.js
+++ b/core/server/public/members.js
@@ -133,6 +133,106 @@ Array.prototype.forEach.call(document.querySelectorAll('[data-members-signout]')
el.addEventListener('click', clickHandler);
});
+Array.prototype.forEach.call(document.querySelectorAll('[data-members-cancel-subscription]'), function (el) {
+ var errorEl = el.parentElement.querySelector('[data-members-error]');
+ function clickHandler(event) {
+ el.removeEventListener('click', clickHandler);
+ event.preventDefault();
+ el.classList.remove('error');
+ el.classList.add('loading');
+
+ var subscriptionId = el.dataset.membersCancelSubscription;
+
+ if (errorEl) {
+ errorEl.innerText = '';
+ }
+
+ return fetch('{{blog-url}}/members/ssr', {
+ credentials: 'same-origin'
+ }).then(function (res) {
+ if (!res.ok) {
+ return null;
+ }
+
+ return res.text();
+ }).then(function (identity) {
+ return fetch(`{{admin-url}}/api/canary/members/subscriptions/${subscriptionId}/`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ identity: identity,
+ cancel_at_period_end: true
+ })
+ });
+ }).then(function (res) {
+ if (res.ok) {
+ window.location.reload();
+ } else {
+ el.addEventListener('click', clickHandler);
+ el.classList.remove('loading');
+ el.classList.add('error');
+
+ if (errorEl) {
+ errorEl.innerText = 'There was an error cancelling your subscription, please try again.';
+ }
+ }
+ });
+ }
+ el.addEventListener('click', clickHandler);
+});
+
+Array.prototype.forEach.call(document.querySelectorAll('[data-members-continue-subscription]'), function (el) {
+ var errorEl = el.parentElement.querySelector('[data-members-error]');
+ function clickHandler(event) {
+ el.removeEventListener('click', clickHandler);
+ event.preventDefault();
+ el.classList.remove('error');
+ el.classList.add('loading');
+
+ var subscriptionId = el.dataset.membersContinueSubscription;
+
+ if (errorEl) {
+ errorEl.innerText = '';
+ }
+
+ return fetch('{{blog-url}}/members/ssr', {
+ credentials: 'same-origin'
+ }).then(function (res) {
+ if (!res.ok) {
+ return null;
+ }
+
+ return res.text();
+ }).then(function (identity) {
+ return fetch(`{{admin-url}}/api/canary/members/subscriptions/${subscriptionId}/`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ identity: identity,
+ cancel_at_period_end: false
+ })
+ });
+ }).then(function (res) {
+ if (res.ok) {
+ window.location.reload();
+ } else {
+ el.addEventListener('click', clickHandler);
+ el.classList.remove('loading');
+ el.classList.add('error');
+
+ if (errorEl) {
+ errorEl.innerText = 'There was an error continuing your subscription, please try again.';
+ }
+ }
+ });
+ }
+ el.addEventListener('click', clickHandler);
+});
+
var url = new URL(window.location);
if (url.searchParams.get('token')) {
url.searchParams.delete('token');
diff --git a/core/server/translations/en.json b/core/server/translations/en.json
index d4910365e7..d50047debf 100644
--- a/core/server/translations/en.json
+++ b/core/server/translations/en.json
@@ -565,6 +565,9 @@
"asset": {
"pathIsRequired": "The \\{\\{asset\\}\\} helper must be passed a path"
},
+ "cancel_link": {
+ "invalidData": "The \\{\\{cancel_link\\}\\} helper was used outside of a subscription context. See https://ghost.org/docs/api/handlebars-themes/helpers/cancel_link/."
+ },
"foreach": {
"iteratorNeeded": "Need to pass an iterator to #foreach"
},
diff --git a/core/server/web/api/canary/members/app.js b/core/server/web/api/canary/members/app.js
index 8d71da6cf1..460677f2da 100644
--- a/core/server/web/api/canary/members/app.js
+++ b/core/server/web/api/canary/members/app.js
@@ -21,6 +21,7 @@ module.exports = function setupMembersApiApp() {
// NOTE: this is wrapped in a function to ensure we always go via the getter
apiApp.post('/send-magic-link', (req, res, next) => membersService.api.middleware.sendMagicLink(req, res, next));
apiApp.post('/create-stripe-checkout-session', (req, res, next) => membersService.api.middleware.createCheckoutSession(req, res, next));
+ apiApp.put('/subscriptions/:id', (req, res, next) => membersService.api.middleware.updateSubscription(req, res, next));
// API error handling
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
diff --git a/core/test/unit/data/schema/integrity_spec.js b/core/test/unit/data/schema/integrity_spec.js
index 470de5d541..f40c3e2fe9 100644
--- a/core/test/unit/data/schema/integrity_spec.js
+++ b/core/test/unit/data/schema/integrity_spec.js
@@ -19,7 +19,7 @@ var should = require('should'),
*/
describe('DB version integrity', function () {
// Only these variables should need updating
- const currentSchemaHash = '773f8f6cd4267f50aec6af8c8b1edbd2';
+ const currentSchemaHash = '3ec33e7039a21dba597ada2a03de0526';
const currentFixturesHash = '1a0f96fa1d8b976d663eb06719be031c';
// If this test is failing, then it is likely a change has been made that requires a DB version bump,
diff --git a/core/test/unit/helpers/cancel_link_spec.js b/core/test/unit/helpers/cancel_link_spec.js
new file mode 100644
index 0000000000..84c55b2b66
--- /dev/null
+++ b/core/test/unit/helpers/cancel_link_spec.js
@@ -0,0 +1,113 @@
+const should = require('should');
+const hbs = require('../../../frontend/services/themes/engine');
+const helpers = require('../../../frontend/helpers');
+const configUtils = require('../../utils/configUtils');
+
+describe('{{cancel_link}} helper', function () {
+ before(function (done) {
+ hbs.express4({partialsDir: [configUtils.config.get('paths').helperTemplates]});
+
+ hbs.cachePartials(function () {
+ done();
+ });
+ });
+
+ const defaultLinkClass = /class="gh-subscription-cancel"/;
+ const defaultErrorElementClass = /class="gh-error gh-error-subscription-cancel"/;
+ const defaultCancelLinkText = /Cancel subscription/;
+ const defaultContinueLinkText = /Continue subscription/;
+
+ it('should throw if subscription data is incorrect', function () {
+ var runHelper = function (data) {
+ return function () {
+ helpers.cancel_link.call(data);
+ };
+ }, expectedMessage = 'The {{cancel_link}} helper was used outside of a subscription context. See https://ghost.org/docs/api/handlebars-themes/helpers/cancel_link/.';
+
+ runHelper('not an object').should.throwError(expectedMessage);
+ runHelper(function () {
+ }).should.throwError(expectedMessage);
+ });
+
+ it('can render cancel subscription link', function () {
+ const rendered = helpers.cancel_link.call({
+ id: 'sub_cancel',
+ cancel_at_period_end: false
+ });
+ should.exist(rendered);
+
+ rendered.string.should.match(defaultLinkClass);
+ rendered.string.should.match(/data-members-cancel-subscription="sub_cancel"/);
+ rendered.string.should.match(defaultCancelLinkText);
+
+ rendered.string.should.match(defaultErrorElementClass);
+ });
+
+ it('can render continue subscription link', function () {
+ const rendered = helpers.cancel_link.call({
+ id: 'sub_continue',
+ cancel_at_period_end: true
+ });
+ should.exist(rendered);
+
+ rendered.string.should.match(defaultLinkClass);
+ rendered.string.should.match(/data-members-continue-subscription="sub_continue"/);
+ rendered.string.should.match(defaultContinueLinkText);
+ });
+
+ it('can render custom link class', function () {
+ const rendered = helpers.cancel_link.call({
+ id: 'sub_cancel',
+ cancel_at_period_end: false
+ }, {
+ hash: {
+ class: 'custom-link-class'
+ }
+ });
+ should.exist(rendered);
+
+ rendered.string.should.match(/custom-link-class/);
+ });
+
+ it('can render custom error class', function () {
+ const rendered = helpers.cancel_link.call({
+ id: 'sub_cancel',
+ cancel_at_period_end: false
+ }, {
+ hash: {
+ errorClass: 'custom-error-class'
+ }
+ });
+ should.exist(rendered);
+
+ rendered.string.should.match(/custom-error-class/);
+ });
+
+ it('can render custom cancel subscription link attributes', function () {
+ const rendered = helpers.cancel_link.call({
+ id: 'sub_cancel',
+ cancel_at_period_end: false
+ }, {
+ hash: {
+ cancelLabel: 'custom cancel link text'
+ }
+ });
+ should.exist(rendered);
+
+ rendered.string.should.match(/custom cancel link text/);
+ });
+
+ it('can render custom continue subscription link attributes', function () {
+ const rendered = helpers.cancel_link.call({
+ id: 'sub_cancel',
+ cancel_at_period_end: true
+ }, {
+ hash: {
+ continueLabel: 'custom continue link text'
+ }
+ });
+ should.exist(rendered);
+
+ rendered.string.should.match(/custom continue link text/);
+ });
+});
diff --git a/core/test/unit/helpers/get_spec.js b/core/test/unit/helpers/get_spec.js
index 7508a55a82..fa20db11c5 100644
--- a/core/test/unit/helpers/get_spec.js
+++ b/core/test/unit/helpers/get_spec.js
@@ -5,9 +5,7 @@ var should = require('should'),
// Stuff we are testing
helpers = require('../../../frontend/helpers'),
models = require('../../../server/models'),
- api = require('../../../server/api'),
-
- labs = require('../../../server/services/labs');
+ api = require('../../../server/api');
describe('{{#get}} helper', function () {
var fn, inverse;
diff --git a/core/test/unit/helpers/index_spec.js b/core/test/unit/helpers/index_spec.js
index 56d277cdc5..587c210639 100644
--- a/core/test/unit/helpers/index_spec.js
+++ b/core/test/unit/helpers/index_spec.js
@@ -8,7 +8,7 @@ var should = require('should'),
describe('Helpers', function () {
var hbsHelpers = ['each', 'if', 'unless', 'with', 'helperMissing', 'blockHelperMissing', 'log', 'lookup', 'block', 'contentFor'],
ghostHelpers = [
- 'asset', 'author', 'authors', 'body_class', 'concat', 'content', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get',
+ 'asset', 'author', 'authors', 'body_class', 'cancel_link', 'concat', 'content', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get',
'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'lang', 'link', 'link_class', 'meta_description', 'meta_title', 'navigation',
'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'reading_time', 't', 'tags', 'title', 'twitter_url',
'url'
diff --git a/package.json b/package.json
index b376f08b54..cdc204bfc6 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
"dependencies": {
"@nexes/nql": "0.3.0",
"@tryghost/helpers": "1.1.19",
- "@tryghost/members-api": "0.10.1",
+ "@tryghost/members-api": "0.10.2",
"@tryghost/members-ssr": "0.7.3",
"@tryghost/social-urls": "0.1.4",
"@tryghost/string": "^0.1.3",
@@ -80,7 +80,7 @@
"ghost-storage-base": "0.0.3",
"glob": "7.1.6",
"got": "9.6.0",
- "gscan": "3.1.1",
+ "gscan": "3.2.0",
"html-to-text": "5.1.1",
"image-size": "0.8.3",
"intl": "1.2.5",
diff --git a/yarn.lock b/yarn.lock
index 3155f1cccf..5f83e5645c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -237,10 +237,10 @@
jsonwebtoken "^8.5.1"
lodash "^4.17.15"
-"@tryghost/members-api@0.10.1":
- version "0.10.1"
- resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.10.1.tgz#07b37df38fb937ae99d491cb308bc2f18a2dfaf7"
- integrity sha512-38dA8nVh3UTSJEDYKNKy98sQArokTrUoHTELEc2HddCrEhAkfezghQEDLikty8RQ9IQs4XpKh3q7JO4bQbK6Ww==
+"@tryghost/members-api@0.10.2":
+ version "0.10.2"
+ resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.10.2.tgz#c28d83a7e9817310a6e7a843f8a2a4669b52856e"
+ integrity sha512-dbA/NO6fpIxDu+b6tH3TN3i2tUrCGnue4vvOuSm0tctEWOTSIceaaiS64HFpncrsJ8obqvqY6ekilgASQWnZ1w==
dependencies:
"@tryghost/magic-link" "^0.3.2"
bluebird "^3.5.4"
@@ -2400,25 +2400,25 @@ error@^7.0.0:
string-template "~0.2.1"
es-abstract@^1.5.1:
- version "1.16.0"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d"
- integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==
+ version "1.16.3"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.3.tgz#52490d978f96ff9f89ec15b5cf244304a5bca161"
+ integrity sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==
dependencies:
- es-to-primitive "^1.2.0"
+ es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
- has-symbols "^1.0.0"
+ has-symbols "^1.0.1"
is-callable "^1.1.4"
is-regex "^1.0.4"
- object-inspect "^1.6.0"
+ object-inspect "^1.7.0"
object-keys "^1.1.1"
string.prototype.trimleft "^2.1.0"
string.prototype.trimright "^2.1.0"
-es-to-primitive@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
- integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
+es-to-primitive@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+ integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
dependencies:
is-callable "^1.1.4"
is-date-object "^1.0.1"
@@ -3837,10 +3837,10 @@ grunt@1.0.4:
path-is-absolute "~1.0.0"
rimraf "~2.6.2"
-gscan@3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/gscan/-/gscan-3.1.1.tgz#e86c2b0f93df7b1f85452584d3dbcb0b766bf901"
- integrity sha512-As5E9ghdLfEmp+JjFcZhkkPNPzsLNgMj0r/CVkgwcHcQ1rTu4WhLKH3FsZIZPObCwqWjNxJeMTU+Tf4hrDKXfw==
+gscan@3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/gscan/-/gscan-3.2.0.tgz#c3d237bd1db5df35fab9155191ede6caf05755b6"
+ integrity sha512-6M/Vtw9ko732zESJBhVforEFiCM6pbSMX6FGjOCf4NGYiEYB00LaHNczaxbhNuEa25usRGexM2AO9vFM3uP4TQ==
dependencies:
"@tryghost/extract-zip" "1.6.6"
"@tryghost/pretty-cli" "1.2.2"
@@ -3925,10 +3925,10 @@ has-flag@^3.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-has-symbols@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
- integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
+has-symbols@^1.0.0, has-symbols@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
+ integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
has-unicode@^2.0.0:
version "2.0.1"
@@ -4588,11 +4588,11 @@ is-svg@^2.0.0:
html-comment-regex "^1.1.0"
is-symbol@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
- integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
+ integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
dependencies:
- has-symbols "^1.0.0"
+ has-symbols "^1.0.1"
is-typedarray@~1.0.0:
version "1.0.0"
@@ -6322,10 +6322,10 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
-object-inspect@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
- integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==
+object-inspect@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
+ integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"