mirror of
https://github.com/strapi/strapi.git
synced 2025-12-25 22:23:10 +00:00
add read/create/update route for locales
This commit is contained in:
parent
2c5e3d7872
commit
291aeb07bc
@ -1,14 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const { has, pipe, prop, pick } = require('lodash/fp');
|
||||
const { MANY_RELATIONS } = require('strapi-utils').relations.constants;
|
||||
const { MANY_RELATIONS, setCreatorFields } = require('strapi-utils').relations.constants;
|
||||
|
||||
const {
|
||||
getService,
|
||||
wrapBadRequest,
|
||||
setCreatorFields,
|
||||
pickWritableAttributes,
|
||||
} = require('../utils');
|
||||
const { getService, wrapBadRequest, pickWritableAttributes } = require('../utils');
|
||||
const { validateBulkDeleteInput, validatePagination } = require('./validation');
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const { pipe } = require('lodash/fp');
|
||||
const {
|
||||
getService,
|
||||
wrapBadRequest,
|
||||
setCreatorFields,
|
||||
pickWritableAttributes,
|
||||
} = require('../utils');
|
||||
const { setCreatorFields } = require('strapi-utils');
|
||||
|
||||
const { getService, wrapBadRequest, pickWritableAttributes } = require('../utils');
|
||||
|
||||
const findEntity = async model => {
|
||||
const entityManager = getService('entity-manager');
|
||||
|
||||
@ -98,6 +98,7 @@ const HIDDEN_CONTENT_TYPES = [
|
||||
'plugins::upload.file',
|
||||
'plugins::users-permissions.permission',
|
||||
'plugins::users-permissions.role',
|
||||
'plugins::i18n.locale',
|
||||
];
|
||||
|
||||
const isHidden = ({ uid }) => startsWith('strapi::', uid) || HIDDEN_CONTENT_TYPES.includes(uid);
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
const { prop } = require('lodash/fp');
|
||||
const wrapBadRequest = require('./wrap-bad-request');
|
||||
const setCreatorFields = require('./set-creator-fields');
|
||||
const pickWritableAttributes = require('./pick-writable-attributes');
|
||||
|
||||
// retrieve a local service
|
||||
@ -13,6 +12,5 @@ const getService = name => {
|
||||
module.exports = {
|
||||
getService,
|
||||
wrapBadRequest,
|
||||
setCreatorFields,
|
||||
pickWritableAttributes,
|
||||
};
|
||||
|
||||
@ -5,7 +5,43 @@
|
||||
"path": "/iso-locales",
|
||||
"handler": "iso-locales.listIsoLocales",
|
||||
"config": {
|
||||
"policies": []
|
||||
"policies": [
|
||||
"admin::isAuthenticatedAdmin",
|
||||
["plugins::content-manager.hasPermissions", ["plugins::i18n.locale.read"]]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/locales",
|
||||
"handler": "locales.listLocales",
|
||||
"config": {
|
||||
"policies": [
|
||||
"admin::isAuthenticatedAdmin",
|
||||
["plugins::content-manager.hasPermissions", ["plugins::i18n.locale.read"]]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/locales",
|
||||
"handler": "locales.createLocale",
|
||||
"config": {
|
||||
"policies": [
|
||||
"admin::isAuthenticatedAdmin",
|
||||
["plugins::content-manager.hasPermissions", ["plugins::i18n.locale.create"]]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "PUT",
|
||||
"path": "/locales/:id",
|
||||
"handler": "locales.updateLocale",
|
||||
"config": {
|
||||
"policies": [
|
||||
"admin::isAuthenticatedAdmin",
|
||||
["plugins::content-manager.hasPermissions", ["plugins::i18n.locale.update"]]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
74
packages/strapi-plugin-i18n/controllers/locales.js
Normal file
74
packages/strapi-plugin-i18n/controllers/locales.js
Normal file
@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
const { setCreatorFields, sanitizeEntity } = require('strapi-utils');
|
||||
const { getService } = require('../utils');
|
||||
const { validateCreateLocaleInput, validateUpdateLocaleInput } = require('../validation/locales');
|
||||
const { formatLocale } = require('../domain/locale');
|
||||
|
||||
const listLocales = async ctx => {
|
||||
const localesService = getService('locales');
|
||||
|
||||
const locales = await localesService.find();
|
||||
|
||||
const model = strapi.getModel('locale', 'i18n');
|
||||
ctx.body = sanitizeEntity(locales, { model });
|
||||
};
|
||||
|
||||
const createLocale = async ctx => {
|
||||
const { user } = ctx.state;
|
||||
const { body } = ctx.request;
|
||||
|
||||
try {
|
||||
await validateCreateLocaleInput(body);
|
||||
} catch (err) {
|
||||
return ctx.badRequest('ValidationError', err);
|
||||
}
|
||||
|
||||
const localesService = getService('locales');
|
||||
const model = strapi.getModel('locale', 'i18n');
|
||||
|
||||
const existingLocale = await localesService.findByCode(body.code);
|
||||
if (existingLocale) {
|
||||
return ctx.badRequest('This locale already exists');
|
||||
}
|
||||
|
||||
let localeToCreate = formatLocale(body);
|
||||
localeToCreate = setCreatorFields({ user })(localeToCreate);
|
||||
|
||||
const locale = await localesService.create(localeToCreate);
|
||||
|
||||
ctx.body = sanitizeEntity(locale, { model });
|
||||
};
|
||||
|
||||
const updateLocale = async ctx => {
|
||||
const { user } = ctx.state;
|
||||
const { id } = ctx.params;
|
||||
const { body } = ctx.request;
|
||||
|
||||
try {
|
||||
await validateUpdateLocaleInput(body);
|
||||
} catch (err) {
|
||||
return ctx.badRequest('ValidationError', err);
|
||||
}
|
||||
|
||||
const localesService = getService('locales');
|
||||
const model = strapi.getModel('locale', 'i18n');
|
||||
|
||||
const existingLocale = await localesService.findById(id);
|
||||
if (!existingLocale) {
|
||||
return ctx.notFound('locale.notFound');
|
||||
}
|
||||
|
||||
let updates = { name: body.name };
|
||||
updates = setCreatorFields({ user, isEdition: true })(updates);
|
||||
|
||||
const updatedLocale = await localesService.update({ id }, updates);
|
||||
|
||||
ctx.body = sanitizeEntity(updatedLocale, { model });
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
listLocales,
|
||||
createLocale,
|
||||
updateLocale,
|
||||
};
|
||||
10
packages/strapi-plugin-i18n/domain/locale.js
Normal file
10
packages/strapi-plugin-i18n/domain/locale.js
Normal file
@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const formatLocale = locale => {
|
||||
return {
|
||||
name: locale.name || null,
|
||||
code: locale.code,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = { formatLocale };
|
||||
23
packages/strapi-plugin-i18n/models/Locale.settings.json
Normal file
23
packages/strapi-plugin-i18n/models/Locale.settings.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"collectionName": "i18n_locales",
|
||||
"info": {
|
||||
"name": "locale",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"timestamps": true
|
||||
},
|
||||
"attributes": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"min": 1,
|
||||
"max": 50,
|
||||
"configurable": false
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"unique": true,
|
||||
"configurable": false
|
||||
}
|
||||
}
|
||||
}
|
||||
141
packages/strapi-plugin-i18n/oas.yml
Normal file
141
packages/strapi-plugin-i18n/oas.yml
Normal file
@ -0,0 +1,141 @@
|
||||
openapi: 3.0.2
|
||||
info:
|
||||
title: Strapi i18n Plugin
|
||||
version: 1.0.0
|
||||
servers:
|
||||
- url: http://localhost:1337
|
||||
description: Local server
|
||||
externalDocs:
|
||||
url: https://strapi.io/documentation
|
||||
description: Strapi documentation
|
||||
paths:
|
||||
/i18n/iso-locales:
|
||||
get:
|
||||
tags:
|
||||
- ISO-locales
|
||||
description: List iso-locales that the app can use
|
||||
responses:
|
||||
200:
|
||||
description: The list of the locales the app can use
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/iso-locale'
|
||||
/i18n/locales:
|
||||
get:
|
||||
tags:
|
||||
- Locales
|
||||
description: List locales used by the app
|
||||
responses:
|
||||
200:
|
||||
description: A list of locales
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/locale'
|
||||
post:
|
||||
tags:
|
||||
- Locales
|
||||
description: Create a locale
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/localeInputCreate'
|
||||
responses:
|
||||
200:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/locale'
|
||||
/i18n/locales/{id}:
|
||||
put:
|
||||
tags:
|
||||
- Locales
|
||||
description: Update the name of a locale
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/locale-id'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/localeInputUpdate'
|
||||
responses:
|
||||
200:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/locale'
|
||||
|
||||
components:
|
||||
schemas:
|
||||
id:
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: integer
|
||||
|
||||
localeInputCreate:
|
||||
type: object
|
||||
required:
|
||||
- code
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Name of the locale
|
||||
code:
|
||||
type: string
|
||||
description: ISO code of the locale
|
||||
required: true
|
||||
|
||||
localeInputUpdate:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Name of the locale
|
||||
required: true
|
||||
|
||||
locale:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
$ref: '#/components/schemas/id'
|
||||
name:
|
||||
type: string
|
||||
description: Name of the locale
|
||||
code:
|
||||
type: string
|
||||
description: ISO code of the locale
|
||||
|
||||
iso-locale:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Name of the locale
|
||||
code:
|
||||
type: string
|
||||
description: ISO code of the locale
|
||||
|
||||
parameters:
|
||||
locale-id:
|
||||
in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
description: Locale id
|
||||
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
@ -9,7 +9,8 @@
|
||||
"required": false
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "4.17.20"
|
||||
"lodash": "4.17.20",
|
||||
"strapi-utils": "3.4.5"
|
||||
},
|
||||
"author": {
|
||||
"name": "A Strapi developer",
|
||||
|
||||
39
packages/strapi-plugin-i18n/services/locales.js
Normal file
39
packages/strapi-plugin-i18n/services/locales.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
const find = async (...args) => {
|
||||
const locales = await strapi.query('locale', 'i18n').find(...args);
|
||||
|
||||
return locales;
|
||||
};
|
||||
|
||||
const findById = async id => {
|
||||
const locales = await strapi.query('locale', 'i18n').findOne({ id });
|
||||
|
||||
return locales;
|
||||
};
|
||||
|
||||
const findByCode = async code => {
|
||||
const locales = await strapi.query('locale', 'i18n').findOne({ code });
|
||||
|
||||
return locales;
|
||||
};
|
||||
|
||||
const create = async locale => {
|
||||
const locales = await strapi.query('locale', 'i18n').create(locale);
|
||||
|
||||
return locales;
|
||||
};
|
||||
|
||||
const update = async (params, updates) => {
|
||||
const locales = await strapi.query('locale', 'i18n').update(params, updates);
|
||||
|
||||
return locales;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
find,
|
||||
findById,
|
||||
findByCode,
|
||||
create,
|
||||
update,
|
||||
};
|
||||
205
packages/strapi-plugin-i18n/tests/locales.test.e2e.js
Normal file
205
packages/strapi-plugin-i18n/tests/locales.test.e2e.js
Normal file
@ -0,0 +1,205 @@
|
||||
'use strict';
|
||||
|
||||
const { createStrapiInstance } = require('../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../test/helpers/request');
|
||||
|
||||
const data = {
|
||||
locales: [],
|
||||
};
|
||||
|
||||
describe('CRUD locales', () => {
|
||||
let rq;
|
||||
let strapi;
|
||||
|
||||
beforeAll(async () => {
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
}, 60000);
|
||||
|
||||
afterAll(async () => {
|
||||
await strapi.destroy();
|
||||
});
|
||||
|
||||
describe('Creation', () => {
|
||||
test('Can create a locale', async () => {
|
||||
const locale = {
|
||||
name: 'French',
|
||||
code: 'fr',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: '/i18n/locales',
|
||||
method: 'POST',
|
||||
body: locale,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchObject({
|
||||
id: expect.anything(),
|
||||
...locale,
|
||||
});
|
||||
data.locales.push(res.body);
|
||||
});
|
||||
|
||||
test('Can create a locale if name is missing', async () => {
|
||||
const locale = {
|
||||
code: 'en',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: '/i18n/locales',
|
||||
method: 'POST',
|
||||
body: locale,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchObject({
|
||||
id: expect.anything(),
|
||||
name: null,
|
||||
code: 'en',
|
||||
});
|
||||
data.locales.push(res.body);
|
||||
});
|
||||
|
||||
test('Cannot create a locale if code is missing', async () => {
|
||||
const locale = {
|
||||
name: 'Italian',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: '/i18n/locales',
|
||||
method: 'POST',
|
||||
body: locale,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({
|
||||
data: { code: ['code is a required field'] },
|
||||
error: 'Bad Request',
|
||||
message: 'ValidationError',
|
||||
statusCode: 400,
|
||||
});
|
||||
});
|
||||
|
||||
test('Cannot create a locale if code already exists', async () => {
|
||||
const locale = {
|
||||
code: 'fr',
|
||||
name: 'random name',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: '/i18n/locales',
|
||||
method: 'POST',
|
||||
body: locale,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({ message: 'This locale already exists' });
|
||||
});
|
||||
|
||||
// - Faire documentation
|
||||
// - Finir tests
|
||||
// - voir comment stocker la locale par defaut
|
||||
// -
|
||||
|
||||
test('Can create a locale even if name already exists', async () => {
|
||||
const locale = {
|
||||
name: 'French',
|
||||
code: 'fr-FR',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: '/i18n/locales',
|
||||
method: 'POST',
|
||||
body: locale,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchObject({
|
||||
id: expect.anything(),
|
||||
...locale,
|
||||
});
|
||||
data.locales.push(res.body);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update', () => {
|
||||
test('Can update the name of a locale', async () => {
|
||||
const localeUpdate = {
|
||||
name: 'French update',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: `/i18n/locales/${data.locales[0].id}`,
|
||||
method: 'PUT',
|
||||
body: localeUpdate,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchObject({
|
||||
...data.locales[0],
|
||||
...localeUpdate,
|
||||
});
|
||||
data.locales[0] = res.body;
|
||||
});
|
||||
|
||||
test('Cannot update the code of a locale (without name)', async () => {
|
||||
const localeUpdate = {
|
||||
code: 'ak',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: `/i18n/locales/${data.locales[0].id}`,
|
||||
method: 'PUT',
|
||||
body: localeUpdate,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({
|
||||
data: {
|
||||
name: ['name is a required field'],
|
||||
undefined: ['this field has unspecified keys: code'],
|
||||
},
|
||||
error: 'Bad Request',
|
||||
message: 'ValidationError',
|
||||
statusCode: 400,
|
||||
});
|
||||
});
|
||||
|
||||
test('Cannot update the code of a locale (with name)', async () => {
|
||||
const localeUpdate = {
|
||||
name: 'French',
|
||||
code: 'ak',
|
||||
};
|
||||
|
||||
let res = await rq({
|
||||
url: `/i18n/locales/${data.locales[0].id}`,
|
||||
method: 'PUT',
|
||||
body: localeUpdate,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toMatchObject({
|
||||
data: {
|
||||
undefined: ['this field has unspecified keys: code'],
|
||||
},
|
||||
error: 'Bad Request',
|
||||
message: 'ValidationError',
|
||||
statusCode: 400,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read', () => {
|
||||
test('Can list the locales', async () => {
|
||||
let res = await rq({
|
||||
url: '/i18n/locales',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toHaveLength(data.locales.length);
|
||||
expect(res.body.sort()).toMatchObject(data.locales.sort());
|
||||
});
|
||||
});
|
||||
});
|
||||
49
packages/strapi-plugin-i18n/validation/locales.js
Normal file
49
packages/strapi-plugin-i18n/validation/locales.js
Normal file
@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const { prop } = require('lodash/fp');
|
||||
const { yup, formatYupErrors } = require('strapi-utils');
|
||||
const { isoLocales } = require('../constants');
|
||||
|
||||
const allowedLocaleCodes = isoLocales.map(prop('code'));
|
||||
|
||||
const handleReject = error => Promise.reject(formatYupErrors(error));
|
||||
|
||||
const createLocaleSchema = yup
|
||||
.object()
|
||||
.shape({
|
||||
name: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.max(50)
|
||||
.nullable(),
|
||||
code: yup
|
||||
.string()
|
||||
.oneOf(allowedLocaleCodes)
|
||||
.required(),
|
||||
})
|
||||
.noUnknown();
|
||||
|
||||
const validateCreateLocaleInput = data => {
|
||||
return createLocaleSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
|
||||
};
|
||||
|
||||
const updateLocaleSchema = yup
|
||||
.object()
|
||||
.shape({
|
||||
name: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.max(50)
|
||||
.nullable()
|
||||
.required(),
|
||||
})
|
||||
.noUnknown();
|
||||
|
||||
const validateUpdateLocaleInput = data => {
|
||||
return updateLocaleSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
validateCreateLocaleInput,
|
||||
validateUpdateLocaleInput,
|
||||
};
|
||||
@ -33,6 +33,7 @@ const contentTypes = require('./content-types');
|
||||
const webhook = require('./webhook');
|
||||
const env = require('./env-helper');
|
||||
const relations = require('./relations');
|
||||
const setCreatorFields = require('./set-creator-fields');
|
||||
|
||||
module.exports = {
|
||||
yup,
|
||||
@ -65,4 +66,5 @@ module.exports = {
|
||||
webhook,
|
||||
env,
|
||||
relations,
|
||||
setCreatorFields,
|
||||
};
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { assign, assoc } = require('lodash/fp');
|
||||
const {
|
||||
CREATED_BY_ATTRIBUTE,
|
||||
UPDATED_BY_ATTRIBUTE,
|
||||
} = require('strapi-utils').contentTypes.constants;
|
||||
const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = require('./content-types').constants;
|
||||
|
||||
module.exports = ({ user, isEdition = false }) => data => {
|
||||
if (isEdition) {
|
||||
@ -54,6 +54,7 @@ const generateTestApp = async ({ appName, database }) => {
|
||||
'strapi-plugin-graphql',
|
||||
'strapi-plugin-i18n',
|
||||
'strapi-plugin-documentation',
|
||||
'strapi-plugin-i18n',
|
||||
],
|
||||
additionalsDependencies: {},
|
||||
};
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@ -18172,6 +18172,18 @@ stealthy-require@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
|
||||
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
|
||||
|
||||
strapi-utils@3.4.5:
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/strapi-utils/-/strapi-utils-3.4.5.tgz#138665a4852c9c7c618bc537b2d65c70aaff3926"
|
||||
integrity sha512-sRkCtnIt6BWc7rKJsg3e0bGFwS09phLYFU2qFEvMLuhGmPremjt+VyBn57cdung7bsMkJ5yQF5JA0e9w4LYYPQ==
|
||||
dependencies:
|
||||
"@sindresorhus/slugify" "1.1.0"
|
||||
date-fns "^2.8.1"
|
||||
lodash "4.17.20"
|
||||
pino "^4.7.1"
|
||||
pluralize "^8.0.0"
|
||||
yup "0.29.3"
|
||||
|
||||
stream-browserify@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user