mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Added member's subscription cancellation helper {{cancel_link}} (#11434)
no issue - The helper allows generating HTML needed to cancel or continue the member's subscription depending on subscription state. - Added public members endpoint to allow updating subscription's `cancel_at_period_end` attribute available at: `PUT /api/canary/members/subscriptions/:id/` - Added client-side hook to allow calling subscription cancellation. Allows to create elements with `data-members-cancel-subscription` / `data-members-continue-subscription` attributes which would call subscription update. - Updated schema and added migration for `current_period_end` column - As discussed we only add a single column to subscriptions table to avoid preoptimizing for future cases - Added {{cancel_link}} helper - Added error handling for {{cancel_link}} when members are disabled - Added test coverage for {{cancel_link}} helper - Bumped @tryghost/members-api version to 0.10.2. Needed to use `updateSubscription` middleware - Bumped gscan to 3.2.0. Needed to recognize new {{cancel_link}} helper
This commit is contained in:
parent
5997343279
commit
e277c6bad3
14 changed files with 344 additions and 42 deletions
32
core/frontend/helpers/cancel_link.js
Normal file
32
core/frontend/helpers/cancel_link.js
Normal file
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
|
|
11
core/frontend/helpers/tpl/cancel_link.hbs
Normal file
11
core/frontend/helpers/tpl/cancel_link.hbs
Normal file
|
@ -0,0 +1,11 @@
|
|||
{{#if cancel_at_period_end}}
|
||||
<a class="{{class}}" data-members-continue-subscription="{{id}}" href="javascript:">
|
||||
{{continueLabel}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a class="{{class}}" data-members-cancel-subscription="{{id}}" href="javascript:">
|
||||
{{cancelLabel}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
<span class="{{errorClass}}" data-members-error><!-- error message will appear here --></span>
|
|
@ -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
|
||||
};
|
|
@ -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},
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
113
core/test/unit/helpers/cancel_link_spec.js
Normal file
113
core/test/unit/helpers/cancel_link_spec.js
Normal file
|
@ -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/);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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",
|
||||
|
|
60
yarn.lock
60
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"
|
||||
|
|
Loading…
Add table
Reference in a new issue