├── .gitignore
├── README.md
├── admin
└── src
│ ├── components
│ ├── Initializer
│ │ └── index.js
│ ├── Input
│ │ └── index.js
│ └── PluginIcon
│ │ └── index.js
│ ├── index.js
│ ├── pages
│ ├── App
│ │ └── index.js
│ └── HomePage
│ │ └── index.js
│ ├── pluginId.js
│ ├── translations
│ ├── en.json
│ └── fr.json
│ └── utils
│ ├── axiosInstance.js
│ └── getTrad.js
├── package.json
├── server
├── bootstrap.js
├── config
│ └── index.js
├── content-types
│ └── index.js
├── controllers
│ ├── ai-controller.js
│ └── index.js
├── destroy.js
├── index.js
├── middlewares
│ └── index.js
├── policies
│ └── index.js
├── register.js
├── routes
│ └── index.js
└── services
│ ├── index.js
│ └── open-ai.js
├── strapi-admin.js
├── strapi-server.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | ############################
2 | # OS X
3 | ############################
4 |
5 | .DS_Store
6 | .AppleDouble
7 | .LSOverride
8 | Icon
9 | .Spotlight-V100
10 | .Trashes
11 | ._*
12 |
13 |
14 | ############################
15 | # Linux
16 | ############################
17 |
18 | *~
19 |
20 |
21 | ############################
22 | # Windows
23 | ############################
24 |
25 | Thumbs.db
26 | ehthumbs.db
27 | Desktop.ini
28 | $RECYCLE.BIN/
29 | *.cab
30 | *.msi
31 | *.msm
32 | *.msp
33 |
34 |
35 | ############################
36 | # Packages
37 | ############################
38 |
39 | *.7z
40 | *.csv
41 | *.dat
42 | *.dmg
43 | *.gz
44 | *.iso
45 | *.jar
46 | *.rar
47 | *.tar
48 | *.zip
49 | *.com
50 | *.class
51 | *.dll
52 | *.exe
53 | *.o
54 | *.seed
55 | *.so
56 | *.swo
57 | *.swp
58 | *.swn
59 | *.swm
60 | *.out
61 | *.pid
62 |
63 |
64 | ############################
65 | # Logs and databases
66 | ############################
67 |
68 | .tmp
69 | *.log
70 | *.sql
71 | *.sqlite
72 | *.sqlite3
73 |
74 |
75 | ############################
76 | # Misc.
77 | ############################
78 |
79 | *#
80 | ssl
81 | .idea
82 | nbproject
83 | public/uploads/*
84 | !public/uploads/.gitkeep
85 |
86 | ############################
87 | # Node.js
88 | ############################
89 |
90 | lib-cov
91 | lcov.info
92 | pids
93 | logs
94 | results
95 | node_modules
96 | .node_history
97 |
98 | ############################
99 | # Tests
100 | ############################
101 |
102 | testApp
103 | coverage
104 |
105 | ############################
106 | # Strapi
107 | ############################
108 |
109 | .env
110 | license.txt
111 | exports
112 | *.cache
113 | dist
114 | build
115 | .strapi-updater.json
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Strapi AI Text Generation Custom Feild with Open AI
2 |
3 | With this Custom Field, you can leverage Open AI to generate CMS content for your products, webistes, blogs or whatever your heat desires.
4 |
5 |
6 |
7 |
8 |
9 | ### ✨ Coming Soon
10 | - Change the number of words the API generates
11 | - Change the langauge model used for text generation
12 |
13 | ## 🔧 Installation
14 |
15 | Inside your Strapi Application, add the package
16 |
17 | With `npm`
18 |
19 | ```bash
20 | npm install ai-text-generation
21 | ```
22 |
23 | or `yarn`
24 |
25 | ```bash
26 | yarn add ai-text-generation
27 | ```
28 |
29 |
30 | Then get your Open AI API Token [here](https://beta.openai.com/account/api-keys) and add it to the `.env` file at the root of your project as `OPEN_AI_API_TOKEN`. Next, in `./config`, create a file called `plugins.js` to enable the plugin.
31 |
32 | ```javascript
33 | // ...
34 | 'ai-text-generation': {
35 | enabled: true,
36 | config: {
37 | apiToken: process.env.OPEN_AI_API_TOKEN,
38 | },
39 | },
40 | //...
41 | ```
42 |
43 |
44 | After, build your admin with
45 |
46 | ```bash
47 | npm run build
48 | ```
49 |
50 | or
51 |
52 | ```bash
53 | yarn build
54 | ```
55 |
56 | and start your server with
57 |
58 | ```bash
59 | npm run develop
60 | ```
61 |
62 | or
63 |
64 | ```bash
65 | yarn develop
66 | ```
67 |
68 | and now you have the power of Open AI, right in your favourite CMS.
69 | For more on how to enable Custom Feilds in your content Model, have a look at this video on ["How to install and use Strapi custom fields"](https://www.youtube.com/watch?v=hIKfvLzN6VI).
70 |
71 | ## 🛠 Contributing
72 |
73 | In this section, we'll look at how YOU can contribute to this Custom Field.
74 |
75 | ### Setting up the environment
76 |
77 | Start by creating a new Strapi project.
78 |
79 | ```bash
80 | npx create-strapi-app --quickstart strapi-plugin-dev
81 | cd strapi-plugin-dev
82 | ```
83 |
84 | Create a new plugins folder if it doesn't exist already.
85 | ```bash
86 | mkdir -p src/plugins
87 | ```
88 |
89 | Now we should clone this repository so you can work on it.
90 |
91 | ```bash
92 | git clone https://github.com/malgamves/strapi-open-ai-text-generation.git src/plugins/ai-text-generation
93 | ```
94 |
95 | Install project dependencies
96 |
97 | ```bash
98 | yarn install
99 | ```
100 |
101 | Now we need to register plugin so strapi can use it. In order to do that we need to create (if not already created) `./config/plugins.js` file and add entry to it.
102 |
103 | ```javascript
104 | // ...
105 | 'ai-text-generation': {
106 | enabled: true,
107 | config: {
108 | apiToken: process.env.OPEN_AI_API_TOKEN,
109 | },
110 | resolve: './src/plugins/ai-text-generation'
111 | },
112 | // ...
113 | ```
114 |
115 | > When contributing, you need to change the value of the `fetch()` url below https://github.com/malgamves/strapi-open-ai-text-generation/blob/78e692b214fc51f8de6bbf6a76fca4db767411eb/admin/src/components/Input/index.js#L25 to `http://localhost:1337`
116 |
117 | Rebuild the project and start the server
118 |
119 | ```bash
120 | yarn build
121 | yarn develop
122 | ```
123 |
124 | For an optimum development experience, start your Strapi server in `Watch Mode` so you don't have to build everytime you make changes as show below
125 | ```bash
126 | yarn develop --watch-admin
127 | ```
128 |
129 | Thanks to the team at [CKEditor](https://ckeditor.com/), the format of this Readme is [inspired by theirs](https://github.com/ckeditor/strapi-plugin-ckeditor).
130 |
--------------------------------------------------------------------------------
/admin/src/components/Initializer/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Initializer
4 | *
5 | */
6 |
7 | import { useEffect, useRef } from 'react';
8 | import PropTypes from 'prop-types';
9 | import pluginId from '../../pluginId';
10 |
11 | const Initializer = ({ setPlugin }) => {
12 | const ref = useRef();
13 | ref.current = setPlugin;
14 |
15 | useEffect(() => {
16 | ref.current(pluginId);
17 | }, []);
18 |
19 | return null;
20 | };
21 |
22 | Initializer.propTypes = {
23 | setPlugin: PropTypes.func.isRequired,
24 | };
25 |
26 | export default Initializer;
27 |
--------------------------------------------------------------------------------
/admin/src/components/Input/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { useIntl } from 'react-intl';
3 | import { TextInput } from '@strapi/design-system/TextInput';
4 | import { Stack } from '@strapi/design-system/Stack';
5 | import { Button } from '@strapi/design-system/Button';
6 | import { Textarea } from '@strapi/design-system';
7 | import { auth } from '@strapi/helper-plugin'
8 |
9 |
10 | export default function Index({
11 | name,
12 | error,
13 | description,
14 | onChange,
15 | value,
16 | intlLabel,
17 | attribute,
18 | }) {
19 | const { formatMessage } = useIntl();
20 | const [prompt, setPrompt] = useState('');
21 | const [err, setErr] = useState('');
22 |
23 | const generateText = async () => {
24 | try {
25 | const response = await fetch(`/ai-text-generation/generate-text`, {
26 | method: 'POST',
27 | headers: {
28 | 'Content-Type': 'application/json',
29 | 'Authorization': `Bearer ${auth.getToken()}`
30 | },
31 | body: JSON.stringify({
32 | 'model': 'text-davinci-001',
33 | 'prompt': `${prompt}`,
34 | 'temperature': 0.4,
35 | 'max_tokens': parseFloat(attribute.options.length),
36 | 'top_p': 1,
37 | 'frequency_penalty': 0,
38 | 'presence_penalty': 0
39 | })
40 | });
41 |
42 | if (!response.ok) {
43 | throw new Error(`Error! status: ${response.status}`);
44 | }
45 |
46 | const result = await response.json();
47 | const parsedResult = result.choices[0].text.replace(/(?:\r\n|\r|\n)/g, '');
48 |
49 | onChange({ target: { name, value: parsedResult, type: attribute.type } })
50 | } catch (err) {
51 | setErr(err.message);
52 | }
53 | }
54 |
55 | const clearGeneratedText = async () => {
56 | onChange({ target: { name, value: '', type: attribute.type } })
57 |
58 | }
59 |
60 | return (
61 |
62 | setPrompt(e.target.value)}
67 | value={prompt}
68 | />
69 |
70 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | )
88 | }
89 |
--------------------------------------------------------------------------------
/admin/src/components/PluginIcon/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * PluginIcon
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import styled from 'styled-components';
9 | import { Icon } from '@strapi/design-system/Icon';
10 | import { Flex } from '@strapi/design-system/Flex';
11 | import Pencil from '@strapi/icons/Pencil';
12 |
13 |
14 | const IconBox = styled(Flex)`
15 | /* Hard code color values */
16 | /* to stay consistent between themes */
17 | background-color: #f0f0ff; /* primary100 */
18 | border: 1px solid #d9d8ff; /* primary200 */
19 | svg > path {
20 | fill: #4945ff; /* primary600 */
21 | }
22 | `;
23 |
24 | const PluginIcon = () => {
25 | return (
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default PluginIcon;
--------------------------------------------------------------------------------
/admin/src/index.js:
--------------------------------------------------------------------------------
1 | import { prefixPluginTranslations } from '@strapi/helper-plugin';
2 | import pluginPkg from '../../package.json';
3 | import pluginId from './pluginId';
4 | import PluginIcon from './components/PluginIcon';
5 |
6 |
7 | const name = pluginPkg.strapi.name;
8 |
9 | export default {
10 | register(app) {
11 |
12 | app.customFields.register({
13 | name: "text-ai",
14 | pluginId: "ai-text-generation",
15 | type: "string",
16 | intlLabel: {
17 | id: "ai-text-generation.text-ai.label",
18 | defaultMessage: "Text AI",
19 | },
20 | intlDescription: {
21 | id: "ai-text-generation.text-ai.description",
22 | defaultMessage: "Let AI do your writing!",
23 | },
24 | icon: PluginIcon, // don't forget to create/import your icon component
25 | components: {
26 | Input: async () => import(/* webpackChunkName: "input-component" */ "./components/Input"),
27 | },
28 | options: {
29 | base: [
30 | /*
31 | Declare settings to be added to the "Base settings" section
32 | of the field in the Content-Type Builder
33 | */
34 | {
35 | sectionTitle: {
36 | id: 'ai-text-generation.text-ai.length',
37 | defaultMessage: 'Text Length',
38 | },
39 | items: [ // Add settings items to the section
40 | {
41 |
42 | intlLabel: {
43 | id: 'ai-text-generation.text-ai.length.label',
44 | defaultMessage: 'Select the length of your text',
45 | },
46 | name: 'options.length',
47 | type: 'select',
48 | value: '64', // these are tokens, 1 token is roughly 4 english words so this goes to approx 250 words
49 | options: [
50 | {
51 | key: '250 words',
52 | value: '250',
53 | metadatas: {
54 | intlLabel: {
55 | id: 'ai-text-generation.text-ai.length.250',
56 | defaultMessage: '250 words',
57 | },
58 | },
59 | },
60 | {
61 | key: '500 words',
62 | value: '128', // these are tokens, 1 token is roughly 4 english words so this goes to approx 500 words
63 | metadatas: {
64 | intlLabel: {
65 | id: 'ai-text-generation.text-ai.length.500',
66 | defaultMessage: '500 words',
67 | },
68 | },
69 | },
70 | ],
71 | },
72 | ],
73 | },
74 | ],
75 | },
76 | });
77 | },
78 |
79 |
80 |
81 | bootstrap(app) {},
82 | async registerTrads({ locales }) {
83 | const importedTrads = await Promise.all(
84 | locales.map((locale) => {
85 | return import(
86 | /* webpackChunkName: "translation-[request]" */ `./translations/${locale}.json`
87 | )
88 | .then(({ default: data }) => {
89 | return {
90 | data: prefixPluginTranslations(data, pluginId),
91 | locale,
92 | };
93 | })
94 | .catch(() => {
95 | return {
96 | data: {},
97 | locale,
98 | };
99 | });
100 | })
101 | );
102 |
103 | return Promise.resolve(importedTrads);
104 | },
105 | };
106 |
--------------------------------------------------------------------------------
/admin/src/pages/App/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This component is the skeleton around the actual pages, and should only
4 | * contain code that should be seen on all pages. (e.g. navigation bar)
5 | *
6 | */
7 |
8 | import React from 'react';
9 | import { Switch, Route } from 'react-router-dom';
10 | import { NotFound } from '@strapi/helper-plugin';
11 | import pluginId from '../../pluginId';
12 | import HomePage from '../HomePage';
13 |
14 | const App = () => {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/admin/src/pages/HomePage/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * HomePage
4 | *
5 | */
6 |
7 | import React from 'react';
8 | // import PropTypes from 'prop-types';
9 | import pluginId from '../../pluginId';
10 |
11 | const HomePage = () => {
12 | return (
13 |
14 |
{pluginId}'s HomePage
15 |
Happy coding
16 |
17 | );
18 | };
19 |
20 | export default HomePage;
21 |
--------------------------------------------------------------------------------
/admin/src/pluginId.js:
--------------------------------------------------------------------------------
1 | import pluginPkg from '../../package.json';
2 |
3 | const pluginId = pluginPkg.name.replace(/^(@[^-,.][\w,-]+\/|strapi-)plugin-/i, '');
4 |
5 | export default pluginId;
6 |
--------------------------------------------------------------------------------
/admin/src/translations/en.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/fr.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/utils/axiosInstance.js:
--------------------------------------------------------------------------------
1 | /**
2 | * axios with a custom config.
3 | */
4 |
5 | import axios from 'axios';
6 | import { auth } from '@strapi/helper-plugin';
7 |
8 | const instance = axios.create({
9 | baseURL: process.env.STRAPI_ADMIN_BACKEND_URL,
10 | });
11 |
12 | instance.interceptors.request.use(
13 | async (config) => {
14 | config.headers = {
15 | Authorization: `Bearer ${auth.getToken()}`,
16 | Accept: 'application/json',
17 | 'Content-Type': 'application/json',
18 | };
19 |
20 | return config;
21 | },
22 | (error) => {
23 | Promise.reject(error);
24 | }
25 | );
26 |
27 | instance.interceptors.response.use(
28 | (response) => response,
29 | (error) => {
30 | // whatever you want to do with the error
31 | if (error.response?.status === 401) {
32 | auth.clearAppStorage();
33 | window.location.reload();
34 | }
35 |
36 | throw error;
37 | }
38 | );
39 |
40 | export default instance;
41 |
--------------------------------------------------------------------------------
/admin/src/utils/getTrad.js:
--------------------------------------------------------------------------------
1 | import pluginId from '../pluginId';
2 |
3 | const getTrad = (id) => `${pluginId}.${id}`;
4 |
5 | export default getTrad;
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ai-text-generation",
3 | "version": "0.1.1",
4 | "description": "A Strapi Custom Field for Text Generation with Open AI.",
5 | "strapi": {
6 | "name": "ai-text-generation",
7 | "description": "A Strapi Custom Field for Text Generation with Open AI.",
8 | "kind": "plugin",
9 | "displayName": "AI Text Generation"
10 | },
11 | "dependencies": {
12 | "axios": "^1.2.2"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/malgamves/strapi-open-ai-text-generation.git"
17 | },
18 | "author": {
19 | "name": "Daniel Phiri",
20 | "email": "malgamves@gmail.com",
21 | "url": "https://malgamves.dev"
22 | },
23 | "maintainers": [
24 | {
25 | "name": "Daniel Phiri",
26 | "email": "malgamves@gmail.com",
27 | "url": "https://malgamves.dev"
28 | }
29 | ],
30 | "engines": {
31 | "node": ">=14.19.1 <=18.x.x",
32 | "npm": ">=6.0.0"
33 | },
34 | "license": "MIT"
35 | }
36 |
--------------------------------------------------------------------------------
/server/bootstrap.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = ({ strapi }) => {
4 | // bootstrap phase
5 | };
6 |
--------------------------------------------------------------------------------
/server/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | default: {},
5 | validator() {},
6 | };
7 |
--------------------------------------------------------------------------------
/server/content-types/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {};
4 |
--------------------------------------------------------------------------------
/server/controllers/ai-controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = ({ strapi }) => ({
4 | async generate(ctx) {
5 | ctx.body = await strapi
6 | .plugin('ai-text-generation')
7 | .service('openAi')
8 | .generateText(ctx.request.body.prompt, ctx.request.body.max_tokens);
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/server/controllers/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const aiController = require('./ai-controller');
4 |
5 | module.exports = {
6 | aiController,
7 | };
8 |
--------------------------------------------------------------------------------
/server/destroy.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = ({ strapi }) => {
4 | // destroy phase
5 | };
6 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const register = require('./register');
4 | const bootstrap = require('./bootstrap');
5 | const destroy = require('./destroy');
6 | const config = require('./config');
7 | const contentTypes = require('./content-types');
8 | const controllers = require('./controllers');
9 | const routes = require('./routes');
10 | const middlewares = require('./middlewares');
11 | const policies = require('./policies');
12 | const services = require('./services');
13 |
14 | module.exports = {
15 | register,
16 | bootstrap,
17 | destroy,
18 | config,
19 | controllers,
20 | routes,
21 | services,
22 | contentTypes,
23 | policies,
24 | middlewares,
25 | };
26 |
--------------------------------------------------------------------------------
/server/middlewares/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {};
4 |
--------------------------------------------------------------------------------
/server/policies/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {};
4 |
--------------------------------------------------------------------------------
/server/register.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = ({ strapi }) => {
4 | strapi.customFields.register({
5 | name: 'text-ai',
6 | plugin: 'ai-text-generation',
7 | type: 'string',
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | method: 'POST',
4 | path: '/generate-text',
5 | handler: 'aiController.generate',
6 | config: {
7 | policies: [],
8 | },
9 | },
10 | ];
11 |
--------------------------------------------------------------------------------
/server/services/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const openAi = require('./open-ai');
4 |
5 | module.exports = {
6 | openAi,
7 | };
8 |
--------------------------------------------------------------------------------
/server/services/open-ai.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const axios = require('axios');
4 |
5 | module.exports = ({ strapi }) => ({
6 |
7 | async generateText(prompt, max_tokens) {
8 | try {
9 | const response = await axios(
10 | {
11 | url: 'https://api.openai.com/v1/completions',
12 | method: 'POST',
13 | headers: {
14 | 'Content-Type': 'application/json',
15 | 'Authorization': `Bearer ${strapi.plugin('ai-text-generation').config('apiToken')}`
16 | },
17 | data: JSON.stringify({
18 | 'model': 'text-davinci-001',
19 | 'prompt': `${prompt}`,
20 | 'temperature': 0.4,
21 | 'max_tokens': parseFloat(max_tokens),
22 | 'top_p': 1,
23 | 'frequency_penalty': 0,
24 | 'presence_penalty': 0
25 | })
26 | })
27 |
28 |
29 | const result = await response.data;
30 | return result;
31 | }
32 | catch (err) {
33 | console.log(err.response)
34 | }
35 |
36 | }
37 |
38 | });
39 |
--------------------------------------------------------------------------------
/strapi-admin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./admin/src').default;
4 |
--------------------------------------------------------------------------------
/strapi-server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./server');
4 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | asynckit@^0.4.0:
6 | version "0.4.0"
7 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
8 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
9 |
10 | axios@^1.2.2:
11 | version "1.2.2"
12 | resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.2.tgz#72681724c6e6a43a9fea860fc558127dbe32f9f1"
13 | integrity sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==
14 | dependencies:
15 | follow-redirects "^1.15.0"
16 | form-data "^4.0.0"
17 | proxy-from-env "^1.1.0"
18 |
19 | combined-stream@^1.0.8:
20 | version "1.0.8"
21 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
22 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
23 | dependencies:
24 | delayed-stream "~1.0.0"
25 |
26 | delayed-stream@~1.0.0:
27 | version "1.0.0"
28 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
29 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
30 |
31 | follow-redirects@^1.15.0:
32 | version "1.15.2"
33 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
34 | integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
35 |
36 | form-data@^4.0.0:
37 | version "4.0.0"
38 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
39 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
40 | dependencies:
41 | asynckit "^0.4.0"
42 | combined-stream "^1.0.8"
43 | mime-types "^2.1.12"
44 |
45 | mime-db@1.52.0:
46 | version "1.52.0"
47 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
48 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
49 |
50 | mime-types@^2.1.12:
51 | version "2.1.35"
52 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
53 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
54 | dependencies:
55 | mime-db "1.52.0"
56 |
57 | proxy-from-env@^1.1.0:
58 | version "1.1.0"
59 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
60 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
61 |
--------------------------------------------------------------------------------