Migration of providers to ts (#16323)

This commit is contained in:
Alexandre BODIN 2023-04-13 08:45:44 +02:00 committed by GitHub
parent dff425769a
commit 28d82d3333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 913 additions and 417 deletions

View File

@ -6,7 +6,7 @@ import { useIntl } from 'react-intl';
const RoleRow = ({ id, name, description, usersCount, icons, rowIndex, canUpdate }) => {
const { formatMessage } = useIntl();
const [, editObject] = icons;
const [, editObject] = icons;
const usersCountText = formatMessage(
{
@ -20,9 +20,11 @@ const RoleRow = ({ id, name, description, usersCount, icons, rowIndex, canUpdate
<Tr
aria-rowindex={rowIndex}
key={id}
{...(canUpdate ? onRowClick({
fn: editObject.onClick,
}) : {})}
{...(canUpdate
? onRowClick({
fn: editObject.onClick,
})
: {})}
>
<Td maxWidth={pxToRem(130)}>
<Typography ellipsis textColor="neutral800">
@ -59,11 +61,11 @@ RoleRow.propTypes = {
usersCount: PropTypes.number.isRequired,
icons: PropTypes.array.isRequired,
rowIndex: PropTypes.number.isRequired,
canUpdate: PropTypes.bool
canUpdate: PropTypes.bool,
};
RoleRow.defaultProps = {
canUpdate: false
canUpdate: false,
};
export default RoleRow;

View File

@ -4,6 +4,6 @@ module.exports = {
preset: '../../../jest-preset.unit.js',
testMatch: ['**/__tests__/**/*.test.ts'],
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest'],
'^.+\\.ts$': ['@swc/jest'],
},
};

View File

@ -1,7 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"noEmit": true
},
"include": ["types", "src"],
"exclude": ["node_modules"]

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
index.d.ts

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
index.d.ts

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
index.d.ts

3
packages/core/utils/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
const utils: any;
export default utils;

View File

@ -28,6 +28,7 @@
}
],
"main": "./lib",
"types": "./index.d.ts",
"directories": {
"lib": "./lib"
},

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
dist/

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -1,7 +0,0 @@
'use strict';
const schema = require('./schema');
module.exports = {
schema,
};

View File

@ -27,13 +27,26 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"lint": "run -T eslint ."
},
"devDependencies": {
"eslint-config-custom": "*",
"tsconfig": "*"
},
"peerDependencies": {
"@strapi/strapi": "^4.9.0"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -0,0 +1 @@
export { default as schema } from './schema';

View File

@ -1,6 +1,4 @@
'use strict';
module.exports = {
export default {
kind: 'collectionType',
collectionName: 'strapi_audit_logs',
info: {

View File

@ -1,9 +1,19 @@
'use strict';
import type { Strapi } from '@strapi/strapi';
import { schema as auditLogContentType } from './content-types/audit-log';
const auditLogContentType = require('./content-types/audit-log');
interface Event {
action: string;
date: Date;
userId: string | number;
payload: Record<string, unknown>;
}
const provider = {
async register({ strapi }) {
interface Log extends Omit<Event, 'userId'> {
user: string | number;
}
export = {
async register({ strapi }: { strapi: Strapi }) {
const contentTypes = strapi.container.get('content-types');
if (!contentTypes.keys().includes('admin::audit-log')) {
strapi.container.get('content-types').add('admin::', { 'audit-log': auditLogContentType });
@ -11,10 +21,10 @@ const provider = {
// Return the provider object
return {
async saveEvent(event) {
// Rewrite userId key to user
const auditLog = { ...event, user: event.userId };
delete auditLog.userId;
async saveEvent(event: Event) {
const { userId, ...rest } = event;
const auditLog: Log = { ...rest, user: userId };
// Save to database
await strapi.entityService.create('admin::audit-log', { data: auditLog });
@ -22,7 +32,7 @@ const provider = {
return this;
},
findMany(query) {
findMany(query: Record<string, unknown>) {
return strapi.entityService.findPage('admin::audit-log', {
populate: ['user'],
fields: ['action', 'date', 'payload'],
@ -30,14 +40,14 @@ const provider = {
});
},
findOne(id) {
findOne(id: string | number) {
return strapi.entityService.findOne('admin::audit-log', id, {
populate: ['user'],
fields: ['action', 'date', 'payload'],
});
},
deleteExpiredEvents(expirationDate) {
deleteExpiredEvents(expirationDate: Date) {
return strapi.entityService.deleteMany('admin::audit-log', {
filters: {
date: {
@ -49,5 +59,3 @@ const provider = {
};
},
};
module.exports = provider;

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
dist/

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -28,17 +28,27 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"lint": "run -T eslint ."
},
"dependencies": {
"@strapi/utils": "4.9.1",
"node-ses": "^3.0.3"
},
"devDependencies": {
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -1,18 +1,39 @@
'use strict';
import nodeSES from 'node-ses';
import utils from '@strapi/utils';
const nodeSES = require('node-ses');
const { removeUndefined } = require('@strapi/utils');
interface Settings {
defaultFrom: string;
defaultReplyTo: string;
}
module.exports = {
init(providerOptions = {}, settings = {}) {
const client = nodeSES.createClient({ ...providerOptions });
interface SendOptions {
from?: string;
to: string;
cc: string;
bcc: string;
replyTo?: string;
subject: string;
text: string;
html: string;
[key: string]: unknown;
}
interface ProviderOptions {
key: string;
secret: string;
amazon?: string;
}
export = {
init(providerOptions: ProviderOptions, settings: Settings) {
const client = nodeSES.createClient(providerOptions);
return {
send(options) {
send(options: SendOptions): Promise<void> {
return new Promise((resolve, reject) => {
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
const msg = {
const msg: nodeSES.sendEmailOptions = {
from: from || settings.defaultFrom,
to,
cc,
@ -23,7 +44,8 @@ module.exports = {
message: html,
...rest,
};
client.sendEmail(removeUndefined(msg), (err) => {
client.sendEmail(utils.removeUndefined(msg), (err) => {
if (err) {
if (err.Message) {
// eslint-disable-next-line prefer-promise-reject-errors

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,4 @@
node_modules/
.eslintrc.js
dist/
jest.config.js

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -0,0 +1,8 @@
'use strict';
module.exports = {
preset: '../../../jest-preset.unit.js',
transform: {
'^.+\\.ts$': ['@swc/jest'],
},
};

View File

@ -1,54 +0,0 @@
'use strict';
const formData = require('form-data');
const Mailgun = require('mailgun.js');
const { removeUndefined } = require('@strapi/utils');
const optionsMap = {
apiKey: { field: 'key', fn: (value) => value },
host: { field: 'url', fn: (value) => `https://${value || 'api.mailgun.net'}` },
};
module.exports = {
convertProviderOptions(providerOptions = {}) {
const newOptions = {};
if (typeof providerOptions === 'object') {
Object.keys(providerOptions).forEach((key) => {
if (Object.keys(optionsMap).includes(key)) {
newOptions[optionsMap[key].field] = optionsMap[key].fn(providerOptions[key]);
} else {
newOptions[key] = providerOptions[key];
}
});
}
return newOptions;
},
init(providerOptions = {}, settings = {}) {
const defaults = {
username: 'api',
};
const mailgun = new Mailgun(formData);
const mg = mailgun.client({ ...defaults, ...this.convertProviderOptions(providerOptions) });
return {
send(options) {
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
const data = {
from: from || settings.defaultFrom,
to,
cc,
bcc,
'h:Reply-To': replyTo || settings.defaultReplyTo,
subject,
text,
html,
...rest,
};
return mg.messages.create(providerOptions.domain, removeUndefined(data));
},
};
},
};

View File

@ -28,17 +28,29 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"test:unit": "run -T jest",
"test:unit:watch": "run -T jest --watch",
"lint": "run -T eslint ."
},
"dependencies": {
"@strapi/utils": "4.9.1",
"form-data": "^4.0.0",
"mailgun.js": "5.2.2"
"mailgun.js": "8.2.1"
},
"devDependencies": {
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",

View File

@ -1,9 +1,7 @@
'use strict';
const formData = require('form-data');
const Mailgun = require('mailgun.js');
const provider = require('../index');
import formData from 'form-data';
import Mailgun from 'mailgun.js';
import Options from 'mailgun.js/interfaces/Options';
import provider from '../index';
describe('@strapi/provider-email-mailgun', () => {
describe('.convertProviderOptions()', () => {
@ -51,6 +49,7 @@ describe('@strapi/provider-email-mailgun', () => {
const defaults = {
username: 'api',
};
const providerOptions = {
key: 'foo',
username: 'bar',
@ -60,7 +59,7 @@ describe('@strapi/provider-email-mailgun', () => {
const mg = mailgun.client({
...defaults,
...provider.convertProviderOptions(providerOptions),
});
} as Options);
expect(mg).toMatchObject({
messages: {
request: {
@ -76,7 +75,8 @@ describe('@strapi/provider-email-mailgun', () => {
it('fails to create a new Mailgun client due to missing key', () => {
const defaults = {
username: 'api',
};
} as Options;
const providerOptions = {
username: 'bar',
domain: 'baz.example.com',

View File

@ -0,0 +1,82 @@
import formData from 'form-data';
import Mailgun from 'mailgun.js';
import utils from '@strapi/utils';
import Options from 'mailgun.js/interfaces/Options';
import { MailgunMessageData } from 'mailgun.js/interfaces/Messages';
interface Settings {
defaultFrom: string;
defaultReplyTo: string;
}
interface SendOptions {
from?: string;
to: string;
cc: string;
bcc: string;
replyTo?: string;
subject: string;
text: string;
html: string;
[key: string]: unknown;
}
interface LegacyOptionMapper {
field: string;
fn(value: unknown): string;
}
type ProviderOptions = Record<string, unknown>;
const optionsMap: Record<string, LegacyOptionMapper> = {
apiKey: { field: 'key', fn: (value) => `${value}` },
host: { field: 'url', fn: (value) => `https://${value || 'api.mailgun.net'}` },
};
export = {
convertProviderOptions(providerOptions: ProviderOptions): Record<string, unknown> {
const newOptions: Record<string, unknown> = {};
if (typeof providerOptions === 'object') {
Object.keys(providerOptions).forEach((key) => {
if (Object.keys(optionsMap).includes(key)) {
newOptions[optionsMap[key].field] = optionsMap[key].fn(providerOptions[key]);
} else {
newOptions[key] = providerOptions[key];
}
});
}
return newOptions;
},
init(providerOptions: ProviderOptions, settings: Settings) {
const defaults = {
username: 'api',
};
const mailgun = new Mailgun(formData);
const mg = mailgun.client({
...defaults,
...this.convertProviderOptions(providerOptions),
} as Options);
return {
send(options: SendOptions) {
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
const data = {
from: from || settings.defaultFrom,
to,
cc,
bcc,
'h:Reply-To': replyTo || settings.defaultReplyTo,
subject,
text,
html,
...rest,
} as MailgunMessageData;
return mg.messages.create(providerOptions.domain as string, utils.removeUndefined(data));
},
};
},
};

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
dist/

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -41,17 +41,28 @@
"email": "git@roschaefer.de"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"lint": "run -T eslint ."
},
"dependencies": {
"lodash": "4.17.21",
"nodemailer": "6.9.1"
},
"devDependencies": {
"@types/nodemailer": "6.4.7",
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -1,11 +1,24 @@
'use strict';
import _ from 'lodash';
import nodemailer, { SendMailOptions } from 'nodemailer';
/**
* Module dependencies
*/
interface Settings {
defaultFrom: string;
defaultReplyTo: string;
}
const _ = require('lodash');
const nodemailer = require('nodemailer');
interface SendOptions {
from?: string;
to: string;
cc: string;
bcc: string;
replyTo?: string;
subject: string;
text: string;
html: string;
[key: string]: unknown;
}
type ProviderOptions = Parameters<typeof nodemailer.createTransport>[0];
const emailFields = [
'from',
@ -19,17 +32,17 @@ const emailFields = [
'attachments',
];
module.exports = {
export = {
provider: 'nodemailer',
name: 'Nodemailer',
init(providerOptions = {}, settings = {}) {
init(providerOptions: ProviderOptions, settings: Settings) {
const transporter = nodemailer.createTransport(providerOptions);
return {
send(options) {
send(options: SendOptions) {
// Default values.
const emailOptions = {
const emailOptions: SendMailOptions = {
..._.pick(options, emailFields),
from: options.from || settings.defaultFrom,
replyTo: options.replyTo || settings.defaultReplyTo,

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
dist/

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -1,38 +0,0 @@
'use strict';
const sendgrid = require('@sendgrid/mail');
const { removeUndefined } = require('@strapi/utils');
module.exports = {
init(providerOptions = {}, settings = {}) {
sendgrid.setApiKey(providerOptions.apiKey);
return {
send(options) {
return new Promise((resolve, reject) => {
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
const msg = {
from: from || settings.defaultFrom,
to,
cc,
bcc,
replyTo: replyTo || settings.defaultReplyTo,
subject,
text,
html,
...rest,
};
sendgrid.send(removeUndefined(msg), (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
},
};
},
};

View File

@ -28,17 +28,27 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"lint": "run -T eslint ."
},
"dependencies": {
"@sendgrid/mail": "7.7.0",
"@strapi/utils": "4.9.1"
},
"devDependencies": {
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -0,0 +1,57 @@
import sendgrid, { MailDataRequired } from '@sendgrid/mail';
import utils from '@strapi/utils';
interface Settings {
defaultFrom: string;
defaultReplyTo: string;
}
interface SendOptions {
from?: string;
to: string;
cc: string;
bcc: string;
replyTo?: string;
subject: string;
text: string;
html: string;
[key: string]: unknown;
}
interface ProviderOptions {
apiKey: string;
}
export = {
init(providerOptions: ProviderOptions, settings: Settings) {
sendgrid.setApiKey(providerOptions.apiKey);
return {
send(options: SendOptions): Promise<void> {
return new Promise((resolve, reject) => {
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
const msg: MailDataRequired = {
from: from || settings.defaultFrom,
to,
cc,
bcc,
replyTo: replyTo || settings.defaultReplyTo,
subject,
text,
html,
...rest,
};
sendgrid.send(utils.removeUndefined(msg), false, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
},
};
},
};

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,3 @@
node_modules/
.eslintrc.js
dist/

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -27,17 +27,28 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"lint": "run -T eslint ."
},
"dependencies": {
"@strapi/utils": "4.9.1",
"sendmail": "^1.6.1"
},
"devDependencies": {
"@types/sendmail": "1.4.4",
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -1,20 +1,38 @@
'use strict';
import sendmailFactory, { Options, MailInput } from 'sendmail';
import utils from '@strapi/utils';
const sendmailFactory = require('sendmail');
const { removeUndefined } = require('@strapi/utils');
interface Settings {
defaultFrom: string;
defaultReplyTo: string;
}
module.exports = {
init(providerOptions = {}, settings = {}) {
interface SendOptions {
from?: string;
to: string;
cc: string;
bcc: string;
replyTo?: string;
subject: string;
text: string;
html: string;
[key: string]: unknown;
}
type ProviderOptions = Options;
export = {
init(providerOptions: ProviderOptions, settings: Settings) {
const sendmail = sendmailFactory({
silent: true,
...providerOptions,
});
return {
send(options) {
send(options: SendOptions): Promise<void> {
return new Promise((resolve, reject) => {
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
const msg = {
const msg: MailInput = {
from: from || settings.defaultFrom,
to,
cc,
@ -26,7 +44,7 @@ module.exports = {
...rest,
};
sendmail(removeUndefined(msg), (err) => {
sendmail(utils.removeUndefined(msg), (err) => {
if (err) {
reject(err);
} else {

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,4 @@
node_modules/
dist/
.eslintrc.js
jest.config.js

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -2,4 +2,7 @@
module.exports = {
preset: '../../../jest-preset.unit.js',
transform: {
'^.+\\.ts$': ['@swc/jest'],
},
};

View File

@ -29,11 +29,17 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"test:unit": "run -T jest",
"test:unit:watch": "run -T jest --watch",
"lint": "run -T eslint ."
@ -42,6 +48,11 @@
"aws-sdk": "2.1351.0",
"lodash": "4.17.21"
},
"devDependencies": {
"@types/jest": "29.2.0",
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -1,6 +1,4 @@
'use strict';
const { getBucketFromUrl } = require('../utils');
import { getBucketFromUrl } from '../utils';
describe('Test for URLs', () => {
test('Virtual hosted style', async () => {

View File

@ -1,7 +1,7 @@
'use strict';
const AWS = require('aws-sdk');
const awsProvider = require('../index');
/* eslint-disable @typescript-eslint/ban-ts-comment */
import AWS from 'aws-sdk';
import { File } from '@strapi/plugin-upload';
import awsProvider from '../index';
jest.mock('aws-sdk');
@ -9,26 +9,37 @@ const S3InstanceMock = {
upload: jest.fn((params, callback) => callback(null, {})),
};
// @ts-ignore
AWS.S3.mockReturnValue(S3InstanceMock);
describe('AWS-S3 provider', () => {
const providerInstance = awsProvider.init({});
beforeEach(() => {
jest.clearAllMocks();
});
describe('upload', () => {
test('Should add url to file object', async () => {
const providerInstance = awsProvider.init({
s3Options: {
params: {
Bucket: 'test',
},
},
});
S3InstanceMock.upload.mockImplementationOnce((params, callback) =>
callback(null, { Location: 'https://validurl.test/tmp/test.json' })
);
const file = {
path: '/tmp/',
const file: File = {
name: 'test',
size: 100,
url: '',
path: 'tmp',
hash: 'test',
ext: '.json',
mime: 'application/json',
buffer: '',
buffer: Buffer.from(''),
};
await providerInstance.upload(file);
@ -39,15 +50,26 @@ describe('AWS-S3 provider', () => {
});
test('Should add to the url the https protocol as it is missing', async () => {
const providerInstance = awsProvider.init({
s3Options: {
params: {
Bucket: 'test',
},
},
});
S3InstanceMock.upload.mockImplementationOnce((params, callback) =>
callback(null, { Location: 'uri.test/tmp/test.json' })
);
const file = {
path: '/tmp/',
const file: File = {
name: 'test',
size: 100,
url: '',
path: 'tmp',
hash: 'test',
ext: '.json',
mime: 'application/json',
buffer: '',
buffer: Buffer.from(''),
};
await providerInstance.upload(file);
@ -58,17 +80,27 @@ describe('AWS-S3 provider', () => {
});
test('Should prepend the baseUrl to the url of the file object', async () => {
const providerInstance = awsProvider.init({ baseUrl: 'https://cdn.test' });
const providerInstance = awsProvider.init({
baseUrl: 'https://cdn.test',
s3Options: {
params: {
Bucket: 'test',
},
},
});
S3InstanceMock.upload.mockImplementationOnce((params, callback) =>
callback(null, { Location: 'https://validurl.test' })
);
const file = {
const file: File = {
name: 'test',
size: 100,
url: '',
path: 'tmp/test',
hash: 'test',
ext: '.json',
mime: 'application/json',
buffer: '',
buffer: Buffer.from(''),
};
await providerInstance.upload(file);
@ -82,17 +114,26 @@ describe('AWS-S3 provider', () => {
const providerInstance = awsProvider.init({
baseUrl: 'https://cdn.test',
rootPath: 'dir/dir2',
s3Options: {
params: {
Bucket: 'test',
},
},
});
S3InstanceMock.upload.mockImplementationOnce((params, callback) =>
callback(null, { Location: 'https://validurl.test' })
);
const file = {
const file: File = {
name: 'test',
size: 100,
url: '',
path: 'tmp/test',
hash: 'test',
ext: '.json',
mime: 'application/json',
buffer: '',
buffer: Buffer.from(''),
};
await providerInstance.upload(file);

View File

@ -1,25 +1,52 @@
'use strict';
import type { ReadStream } from 'node:fs';
import { getOr } from 'lodash/fp';
import AWS from 'aws-sdk';
import { getBucketFromUrl } from './utils';
/**
* Module dependencies
*/
interface File {
name: string;
alternativeText?: string;
caption?: string;
width?: number;
height?: number;
formats?: Record<string, unknown>;
hash: string;
ext?: string;
mime: string;
size: number;
url: string;
previewUrl?: string;
path?: string;
provider?: string;
provider_metadata?: Record<string, unknown>;
stream?: ReadStream;
buffer?: Buffer;
}
/* eslint-disable no-unused-vars */
// Public node modules.
const { getOr } = require('lodash/fp');
// TODO V5: Migrate to aws-sdk v3
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('aws-sdk/lib/maintenance_mode_message').suppress = true;
const AWS = require('aws-sdk');
const { getBucketFromUrl } = require('./utils');
function assertUrlProtocol(url) {
function assertUrlProtocol(url: string) {
// Regex to test protocol like "http://", "https://"
return /^\w*:\/\//.test(url);
}
module.exports = {
init({ baseUrl = null, rootPath = null, s3Options, ...legacyS3Options }) {
if (legacyS3Options) {
interface InitOptions extends Partial<AWS.S3.ClientConfiguration> {
baseUrl?: string;
rootPath?: string;
s3Options: AWS.S3.ClientConfiguration & {
params: {
Bucket: string; // making it required
ACL?: string;
signedUrlExpires?: string;
};
};
}
export = {
init({ baseUrl, rootPath, s3Options, ...legacyS3Options }: InitOptions) {
if (Object.keys(legacyS3Options).length > 0) {
process.emitWarning(
"S3 configuration options passed at root level of the plugin's providerOptions is deprecated and will be removed in a future release. Please wrap them inside the 's3Options:{}' property."
);
@ -34,7 +61,7 @@ module.exports = {
const filePrefix = rootPath ? `${rootPath.replace(/\/+$/, '')}/` : '';
const getFileKey = (file) => {
const getFileKey = (file: File) => {
const path = file.path ? `${file.path}/` : '';
return `${filePrefix}${path}${file.hash}${file.ext}`;
@ -42,48 +69,47 @@ module.exports = {
const ACL = getOr('public-read', ['params', 'ACL'], config);
const upload = (file, customParams = {}) =>
const upload = (file: File, customParams = {}): Promise<void> =>
new Promise((resolve, reject) => {
// upload file on S3 bucket
const fileKey = getFileKey(file);
S3.upload(
{
Key: fileKey,
Body: file.stream || Buffer.from(file.buffer, 'binary'),
ACL,
ContentType: file.mime,
...customParams,
},
(err, data) => {
if (err) {
return reject(err);
}
// set the bucket file url
if (assertUrlProtocol(data.Location)) {
file.url = baseUrl ? `${baseUrl}/${fileKey}` : data.Location;
} else {
// Default protocol to https protocol
file.url = `https://${data.Location}`;
}
resolve();
if (!file.stream && !file.buffer) {
reject(new Error('Missing file stream or buffer'));
return;
}
const params = {
Key: fileKey,
Bucket: config.params.Bucket,
Body: file.stream || file.buffer,
ACL,
ContentType: file.mime,
...customParams,
};
const onUploaded = (err: Error, data: AWS.S3.ManagedUpload.SendData) => {
if (err) {
return reject(err);
}
);
// set the bucket file url
if (assertUrlProtocol(data.Location)) {
file.url = baseUrl ? `${baseUrl}/${fileKey}` : data.Location;
} else {
// Default protocol to https protocol
file.url = `https://${data.Location}`;
}
resolve();
};
S3.upload(params, onUploaded);
});
return {
isPrivate() {
return ACL === 'private';
},
/**
* @param {Object} file
* @param {string} file.path
* @param {string} file.hash
* @param {string} file.ext
* @param {Object} customParams
* @returns {Promise<{url: string}>}
*/
getSignedUrl(file, customParams = {}) {
async getSignedUrl(file: File): Promise<{ url: string }> {
// Do not sign the url if it does not come from the same bucket.
const { bucket } = getBucketFromUrl(file.url);
if (bucket !== config.params.Bucket) {
@ -109,19 +135,20 @@ module.exports = {
);
});
},
uploadStream(file, customParams = {}) {
uploadStream(file: File, customParams = {}) {
return upload(file, customParams);
},
upload(file, customParams = {}) {
upload(file: File, customParams = {}) {
return upload(file, customParams);
},
delete(file, customParams = {}) {
delete(file: File, customParams = {}): Promise<void> {
return new Promise((resolve, reject) => {
// delete file on S3 bucket
const fileKey = getFileKey(file);
S3.deleteObject(
{
Key: fileKey,
Bucket: config.params.Bucket,
...customParams,
},
(err) => {

View File

@ -1,7 +1,10 @@
'use strict';
const ENDPOINT_PATTERN = /^(.+\.)?s3[.-]([a-z0-9-]+)\./;
interface BucketInfo {
bucket?: string | null;
err?: string;
}
/**
* Parse the bucket name from a URL.
* See all URL formats in https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html
@ -9,9 +12,9 @@ const ENDPOINT_PATTERN = /^(.+\.)?s3[.-]([a-z0-9-]+)\./;
* @param {string} fileUrl - the URL to parse
* @returns {object} result
* @returns {string} result.bucket - the bucket name
* @returns {string} result.error - if any
* @returns {string} result.err - if any
*/
function getBucketFromUrl(fileUrl) {
export function getBucketFromUrl(fileUrl: string): BucketInfo {
const uri = new URL(fileUrl);
// S3://<bucket-name>/<key>
@ -59,5 +62,3 @@ function getBucketFromUrl(fileUrl) {
// https://<bucket-name>.s3.amazonaws.com/
return { bucket: prefix.substring(0, prefix.length - 1) };
}
module.exports = { getBucketFromUrl };

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,4 @@
node_modules/
dist/
.eslintrc.js
jest.config.js

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -28,11 +28,17 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"lint": "run -T eslint ."
},
"dependencies": {
@ -40,6 +46,10 @@
"cloudinary": "^1.33.0",
"into-stream": "^5.1.0"
},
"devDependencies": {
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -1,21 +1,35 @@
'use strict';
import type { ReadStream } from 'node:fs';
import { v2 as cloudinary, ConfigOptions, UploadApiOptions } from 'cloudinary';
import intoStream from 'into-stream';
import utils from '@strapi/utils';
/**
* Module dependencies
*/
interface File {
name: string;
alternativeText?: string;
caption?: string;
width?: number;
height?: number;
formats?: Record<string, unknown>;
hash: string;
ext?: string;
mime: string;
size: number;
url: string;
previewUrl?: string;
path?: string;
provider?: string;
provider_metadata?: Record<string, unknown>;
stream?: ReadStream;
buffer?: Buffer;
}
// Public node modules.
const cloudinary = require('cloudinary').v2;
const intoStream = require('into-stream');
const { PayloadTooLargeError } = require('@strapi/utils').errors;
export = {
init(options: ConfigOptions) {
cloudinary.config(options);
module.exports = {
init(config) {
cloudinary.config(config);
const upload = (file, customConfig = {}) =>
new Promise((resolve, reject) => {
const config = {
const upload = (file: File, customConfig = {}): Promise<void> => {
return new Promise((resolve, reject) => {
const config: Partial<UploadApiOptions> = {
resource_type: 'auto',
public_id: file.hash,
};
@ -33,13 +47,17 @@ module.exports = {
(err, image) => {
if (err) {
if (err.message.includes('File size too large')) {
reject(new PayloadTooLargeError());
reject(new utils.errors.PayloadTooLargeError());
} else {
reject(new Error(`Error uploading to cloudinary: ${err.message}`));
}
return;
}
if (!image) {
return;
}
if (image.resource_type === 'video') {
file.previewUrl = cloudinary.url(`${image.public_id}.gif`, {
video_sampling: 6,
@ -62,32 +80,41 @@ module.exports = {
if (file.stream) {
file.stream.pipe(uploadStream);
} else {
} else if (file.buffer) {
intoStream(file.buffer).pipe(uploadStream);
} else {
throw new Error('Missing file stream or buffer');
}
});
};
return {
uploadStream(file, customConfig = {}) {
uploadStream(file: File, customConfig = {}) {
return upload(file, customConfig);
},
upload(file, customConfig = {}) {
upload(file: File, customConfig = {}) {
return upload(file, customConfig);
},
async delete(file, customConfig = {}) {
async delete(file: File, customConfig = {}) {
try {
const { resource_type: resourceType, public_id: publicId } = file.provider_metadata;
const response = await cloudinary.uploader.destroy(publicId, {
const { resource_type: resourceType, public_id: publicId } = file.provider_metadata ?? {};
const deleteConfig = {
resource_type: (resourceType || 'image') as string,
invalidate: true,
resource_type: resourceType || 'image',
...customConfig,
});
};
const response = await cloudinary.uploader.destroy(`${publicId}`, deleteConfig);
if (response.result !== 'ok' && response.result !== 'not found') {
throw new Error(`Error deleting on cloudinary: ${response.result}`);
throw new Error(response.result);
}
} catch (error) {
throw new Error(`Error deleting on cloudinary: ${error.message}`);
if (error instanceof Error) {
throw new Error(`Error deleting on cloudinary: ${error.message}`);
}
throw error;
}
},
};

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,2 +1,4 @@
node_modules/
dist/
.eslintrc.js
jest.config.js

View File

@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['custom/back'],
extends: ['custom/typescript'],
};

View File

@ -2,4 +2,7 @@
module.exports = {
preset: '../../../jest-preset.unit.js',
transform: {
'^.+\\.ts$': ['@swc/jest'],
},
};

View File

@ -27,11 +27,17 @@
"url": "https://strapi.io"
}
],
"main": "./lib",
"directories": {
"lib": "./lib"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"./dist"
],
"scripts": {
"build": "run -T tsc",
"build:ts": "run -T tsc",
"watch": "run -T tsc -w --preserveWatchOutput",
"clean": "run -T rimraf ./dist",
"prepublishOnly": "yarn clean && yarn build",
"test:unit": "run -T jest",
"test:unit:watch": "run -T jest --watch",
"lint": "run -T eslint ."
@ -40,6 +46,11 @@
"@strapi/utils": "4.9.1",
"fs-extra": "10.0.0"
},
"devDependencies": {
"@types/jest": "29.2.0",
"eslint-config-custom": "*",
"tsconfig": "*"
},
"engines": {
"node": ">=14.19.1 <=18.x.x",
"npm": ">=6.0.0"

View File

@ -1,4 +1,5 @@
'use strict';
import type { File } from '@strapi/plugin-upload';
import localProvider from '../index';
jest.mock('fs', () => {
return {
@ -12,11 +13,9 @@ jest.mock('fs-extra', () => {
};
});
const localProvider = require('../index');
describe('Local provider', () => {
beforeAll(() => {
globalThis.strapi = globalThis.strapi ?? {};
globalThis.strapi = {};
globalThis.strapi.dirs = { static: { public: '' } };
});
@ -28,12 +27,15 @@ describe('Local provider', () => {
test('Should have relative url to file object', async () => {
const providerInstance = localProvider.init({});
const file = {
const file: File = {
name: 'test',
size: 100,
url: '/',
path: '/tmp/',
hash: 'test',
ext: '.json',
mime: 'application/json',
buffer: '',
buffer: Buffer.from(''),
};
await providerInstance.upload(file);

View File

@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
declare const strapi: any;

View File

@ -1,27 +1,48 @@
'use strict';
import { pipeline } from 'stream';
import fs, { ReadStream } from 'fs';
import path from 'path';
import fse from 'fs-extra';
import utils from '@strapi/utils';
/**
* Module dependencies
*/
interface File {
name: string;
alternativeText?: string;
caption?: string;
width?: number;
height?: number;
formats?: Record<string, unknown>;
hash: string;
ext?: string;
mime: string;
size: number;
url: string;
previewUrl?: string;
path?: string;
provider?: string;
provider_metadata?: Record<string, unknown>;
stream?: ReadStream;
buffer?: Buffer;
}
// Public node modules.
const { pipeline } = require('stream');
const fs = require('fs');
const path = require('path');
const fse = require('fs-extra');
const {
errors: { PayloadTooLargeError },
file: { kbytesToBytes, bytesToHumanReadable },
} = require('@strapi/utils');
const { PayloadTooLargeError } = utils.errors;
const { kbytesToBytes, bytesToHumanReadable } = utils.file;
const UPLOADS_FOLDER_NAME = 'uploads';
module.exports = {
init({ sizeLimit: providerOptionsSizeLimit } = {}) {
interface InitOptions {
sizeLimit?: number;
}
interface CheckFileSizeOptions {
sizeLimit?: number;
}
export = {
init({ sizeLimit: providerOptionsSizeLimit }: InitOptions = {}) {
// TODO V5: remove providerOptions sizeLimit
if (providerOptionsSizeLimit) {
process.emitWarning(
`[deprecated] In future versions, "sizeLimit" argument will be ignored from upload.config.providerOptions. Move it to upload.config`
'[deprecated] In future versions, "sizeLimit" argument will be ignored from upload.config.providerOptions. Move it to upload.config'
);
}
@ -34,7 +55,9 @@ module.exports = {
}
return {
checkFileSize(file, { sizeLimit } = {}) {
checkFileSize(file: File, options: CheckFileSizeOptions) {
const { sizeLimit } = options ?? {};
// TODO V5: remove providerOptions sizeLimit
if (providerOptionsSizeLimit) {
if (kbytesToBytes(file.size) > providerOptionsSizeLimit)
@ -50,10 +73,16 @@ module.exports = {
);
}
},
uploadStream(file) {
uploadStream(file: File): Promise<void> {
if (!file.stream) {
return Promise.reject(new Error('Missing file stream'));
}
const { stream } = file;
return new Promise((resolve, reject) => {
pipeline(
file.stream,
stream,
fs.createWriteStream(path.join(uploadPath, `${file.hash}${file.ext}`)),
(err) => {
if (err) {
@ -67,10 +96,16 @@ module.exports = {
);
});
},
upload(file) {
upload(file: File): Promise<void> {
if (!file.buffer) {
return Promise.reject(new Error('Missing file buffer'));
}
const { buffer } = file;
return new Promise((resolve, reject) => {
// write file in public/assets folder
fs.writeFile(path.join(uploadPath, `${file.hash}${file.ext}`), file.buffer, (err) => {
fs.writeFile(path.join(uploadPath, `${file.hash}${file.ext}`), buffer, (err) => {
if (err) {
return reject(err);
}
@ -81,13 +116,13 @@ module.exports = {
});
});
},
delete(file) {
delete(file: File): Promise<string | void> {
return new Promise((resolve, reject) => {
const filePath = path.join(uploadPath, `${file.hash}${file.ext}`);
if (!fs.existsSync(filePath)) {
// eslint-disable-next-line no-promise-executor-return
return resolve("File doesn't exist");
resolve("File doesn't exist");
return;
}
// remove file from public/assets folder

View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/**"]
}

View File

@ -1,7 +1,9 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node16/tsconfig.json",
"compilerOptions": {
"declaration": true,
"sourceMap": true
"sourceMap": true,
"esModuleInterop": true
}
}

144
yarn.lock
View File

@ -7659,6 +7659,11 @@ __metadata:
"@strapi/provider-audit-logs-local@4.9.1, @strapi/provider-audit-logs-local@workspace:packages/providers/audit-logs-local":
version: 0.0.0-use.local
resolution: "@strapi/provider-audit-logs-local@workspace:packages/providers/audit-logs-local"
dependencies:
eslint-config-custom: "*"
tsconfig: "*"
peerDependencies:
"@strapi/strapi": ^4.9.0
languageName: unknown
linkType: soft
@ -7667,7 +7672,9 @@ __metadata:
resolution: "@strapi/provider-email-amazon-ses@workspace:packages/providers/email-amazon-ses"
dependencies:
"@strapi/utils": 4.9.1
eslint-config-custom: "*"
node-ses: ^3.0.3
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7676,8 +7683,10 @@ __metadata:
resolution: "@strapi/provider-email-mailgun@workspace:packages/providers/email-mailgun"
dependencies:
"@strapi/utils": 4.9.1
eslint-config-custom: "*"
form-data: ^4.0.0
mailgun.js: 5.2.2
mailgun.js: 8.2.1
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7685,8 +7694,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@strapi/provider-email-nodemailer@workspace:packages/providers/email-nodemailer"
dependencies:
"@types/nodemailer": 6.4.7
eslint-config-custom: "*"
lodash: 4.17.21
nodemailer: 6.9.1
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7696,6 +7708,8 @@ __metadata:
dependencies:
"@sendgrid/mail": 7.7.0
"@strapi/utils": 4.9.1
eslint-config-custom: "*"
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7704,7 +7718,10 @@ __metadata:
resolution: "@strapi/provider-email-sendmail@workspace:packages/providers/email-sendmail"
dependencies:
"@strapi/utils": 4.9.1
"@types/sendmail": 1.4.4
eslint-config-custom: "*"
sendmail: ^1.6.1
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7712,8 +7729,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@strapi/provider-upload-aws-s3@workspace:packages/providers/upload-aws-s3"
dependencies:
"@types/jest": 29.2.0
aws-sdk: 2.1351.0
eslint-config-custom: "*"
lodash: 4.17.21
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7723,7 +7743,9 @@ __metadata:
dependencies:
"@strapi/utils": 4.9.1
cloudinary: ^1.33.0
eslint-config-custom: "*"
into-stream: ^5.1.0
tsconfig: "*"
languageName: unknown
linkType: soft
@ -7732,7 +7754,10 @@ __metadata:
resolution: "@strapi/provider-upload-local@workspace:packages/providers/upload-local"
dependencies:
"@strapi/utils": 4.9.1
"@types/jest": 29.2.0
eslint-config-custom: "*"
fs-extra: 10.0.0
tsconfig: "*"
languageName: unknown
linkType: soft
@ -8743,6 +8768,15 @@ __metadata:
languageName: node
linkType: hard
"@types/nodemailer@npm:6.4.7":
version: 6.4.7
resolution: "@types/nodemailer@npm:6.4.7"
dependencies:
"@types/node": "*"
checksum: dc2a33a89135e04a5bea4921e8645e8453b90e3c3b05f0646f05071c5951ab697ea49ea1e503a690f04cb0a6abfc54967325c5a4036356793cfbb64ba64fb141
languageName: node
linkType: hard
"@types/normalize-package-data@npm:^2.4.0":
version: 2.4.1
resolution: "@types/normalize-package-data@npm:2.4.1"
@ -8865,6 +8899,13 @@ __metadata:
languageName: node
linkType: hard
"@types/sendmail@npm:1.4.4":
version: 1.4.4
resolution: "@types/sendmail@npm:1.4.4"
checksum: aa31facf023af0c888e6a64bfaf742d08efdf1fef4c51efd7e0e547f6f3a2d215f02bdea81f35953cefefadf9a6f1fd516bafc06f4beb708b41570e21c415e3e
languageName: node
linkType: hard
"@types/serve-index@npm:^1.9.1":
version: 1.9.1
resolution: "@types/serve-index@npm:1.9.1"
@ -9923,15 +9964,6 @@ __metadata:
languageName: node
linkType: hard
"abort-controller@npm:^3.0.0":
version: 3.0.0
resolution: "abort-controller@npm:3.0.0"
dependencies:
event-target-shim: ^5.0.0
checksum: 170bdba9b47b7e65906a28c8ce4f38a7a369d78e2271706f020849c1bfe0ee2067d4261df8bbb66eb84f79208fd5b710df759d64191db58cfba7ce8ef9c54b75
languageName: node
linkType: hard
"accepts@npm:^1.3.5, accepts@npm:^1.3.7, accepts@npm:~1.3.4, accepts@npm:~1.3.5, accepts@npm:~1.3.8":
version: 1.3.8
resolution: "accepts@npm:1.3.8"
@ -10998,6 +11030,17 @@ __metadata:
languageName: node
linkType: hard
"axios@npm:^1.3.3":
version: 1.3.5
resolution: "axios@npm:1.3.5"
dependencies:
follow-redirects: ^1.15.0
form-data: ^4.0.0
proxy-from-env: ^1.1.0
checksum: 4d6bcf933b1cdff86d4993752aaeeeedc4a7f7a4b1c942847f6884bb13fc6106610ff826b076acf0b08d8ced55dee9344bb9a11f3624c3e70ab1da1a40bb5506
languageName: node
linkType: hard
"axobject-query@npm:^2.2.0":
version: 2.2.0
resolution: "axobject-query@npm:2.2.0"
@ -11527,7 +11570,7 @@ __metadata:
languageName: node
linkType: hard
"bluebird@npm:^3.5.5, bluebird@npm:^3.7.2":
"bluebird@npm:^3.5.5":
version: 3.7.2
resolution: "bluebird@npm:3.7.2"
checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef
@ -13885,7 +13928,7 @@ __metadata:
languageName: node
linkType: hard
"data-uri-to-buffer@npm:3, data-uri-to-buffer@npm:^3.0.1":
"data-uri-to-buffer@npm:3":
version: 3.0.1
resolution: "data-uri-to-buffer@npm:3.0.1"
checksum: c59c3009686a78c071806b72f4810856ec28222f0f4e252aa495ec027ed9732298ceea99c50328cf59b151dd34cbc3ad6150bbb43e41fc56fa19f48c99e9fc30
@ -15354,7 +15397,7 @@ __metadata:
languageName: node
linkType: hard
"eslint-config-custom@4.9.1, eslint-config-custom@workspace:packages/utils/eslint-config-custom":
"eslint-config-custom@*, eslint-config-custom@4.9.1, eslint-config-custom@workspace:packages/utils/eslint-config-custom":
version: 0.0.0-use.local
resolution: "eslint-config-custom@workspace:packages/utils/eslint-config-custom"
languageName: unknown
@ -15837,13 +15880,6 @@ __metadata:
languageName: node
linkType: hard
"event-target-shim@npm:^5.0.0":
version: 5.0.1
resolution: "event-target-shim@npm:5.0.1"
checksum: 1ffe3bb22a6d51bdeb6bf6f7cf97d2ff4a74b017ad12284cc9e6a279e727dc30a5de6bb613e5596ff4dc3e517841339ad09a7eec44266eccb1aa201a30448166
languageName: node
linkType: hard
"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.4":
version: 4.0.7
resolution: "eventemitter3@npm:4.0.7"
@ -16294,16 +16330,6 @@ __metadata:
languageName: node
linkType: hard
"fetch-blob@npm:^2.1.1":
version: 2.1.2
resolution: "fetch-blob@npm:2.1.2"
peerDependenciesMeta:
domexception:
optional: true
checksum: 22d4487ce78ea4e52b432b0057d8d42922d5d93c0374b0bc2692cebdcb11bf8fac4f6d141b31f1633db1e9212effd38385adbd765a2c7412a621307058499214
languageName: node
linkType: hard
"fetch-retry@npm:^5.0.2":
version: 5.0.3
resolution: "fetch-retry@npm:5.0.3"
@ -21264,29 +21290,6 @@ __metadata:
languageName: node
linkType: hard
"ky-universal@npm:^0.8.2":
version: 0.8.2
resolution: "ky-universal@npm:0.8.2"
dependencies:
abort-controller: ^3.0.0
node-fetch: 3.0.0-beta.9
peerDependencies:
ky: ">=0.17.0"
web-streams-polyfill: ">=2.0.0"
peerDependenciesMeta:
web-streams-polyfill:
optional: true
checksum: 87ed38c5c5a5b4448502fd5a64b68f30db69d366e148e5321cd9c0cb57d616519578ff0ae50146ff92ad037ef5cd77fbc40d893675459eead0d3f13101374570
languageName: node
linkType: hard
"ky@npm:^0.25.1":
version: 0.25.1
resolution: "ky@npm:0.25.1"
checksum: ae1b7bebb48001d00d53e386e077939eeef7398a36b4fb45660f988ddb17d583d077290f2adb9706b4761f9d3b74918eb8d9f45ce799760143e104e1053b33ef
languageName: node
linkType: hard
"language-subtag-registry@npm:~0.3.2":
version: 0.3.22
resolution: "language-subtag-registry@npm:0.3.22"
@ -21999,18 +22002,14 @@ __metadata:
languageName: node
linkType: hard
"mailgun.js@npm:5.2.2":
version: 5.2.2
resolution: "mailgun.js@npm:5.2.2"
"mailgun.js@npm:8.2.1":
version: 8.2.1
resolution: "mailgun.js@npm:8.2.1"
dependencies:
axios: ^1.3.3
base-64: ^1.0.0
bluebird: ^3.7.2
ky: ^0.25.1
ky-universal: ^0.8.2
url-join: ^4.0.1
web-streams-polyfill: ^3.0.1
webpack-merge: ^5.4.0
checksum: 5fa48c4c29ef64d453225c3511a72546e028a40348b71af6a29213997791aa6ab782bddb12690c6488f83112f08df3a44b0d181ef44ef31ecb56f0dc6fa1b6a7
checksum: 512b76a8b688cc2b88c14472a2c7c9715dd927c5bb588a6197c2eed8b9effc8edddb3a7c99a1f2993c5f205154b24585db5fa573c15701401759b8702258b944
languageName: node
linkType: hard
@ -23360,16 +23359,6 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:3.0.0-beta.9":
version: 3.0.0-beta.9
resolution: "node-fetch@npm:3.0.0-beta.9"
dependencies:
data-uri-to-buffer: ^3.0.1
fetch-blob: ^2.1.1
checksum: 586af0910649cb62f1c044ddac41e71c0b0f48133fba406ad5e0fab51baff18f22cd187ea65ef690ceed7386a71910e904348105dc8eae55f907fa94df7e76ca
languageName: node
linkType: hard
"node-forge@npm:^1":
version: 1.3.1
resolution: "node-forge@npm:1.3.1"
@ -30191,7 +30180,7 @@ __metadata:
languageName: node
linkType: hard
"tsconfig@4.9.1, tsconfig@workspace:packages/utils/tsconfig":
"tsconfig@*, tsconfig@4.9.1, tsconfig@workspace:packages/utils/tsconfig":
version: 0.0.0-use.local
resolution: "tsconfig@workspace:packages/utils/tsconfig"
languageName: unknown
@ -31283,13 +31272,6 @@ __metadata:
languageName: node
linkType: hard
"web-streams-polyfill@npm:^3.0.1":
version: 3.2.1
resolution: "web-streams-polyfill@npm:3.2.1"
checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02
languageName: node
linkType: hard
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
@ -31479,7 +31461,7 @@ __metadata:
languageName: node
linkType: hard
"webpack-merge@npm:^5.4.0, webpack-merge@npm:^5.7.3":
"webpack-merge@npm:^5.7.3":
version: 5.8.0
resolution: "webpack-merge@npm:5.8.0"
dependencies: