diff --git a/api/app/bingai.js b/api/app/bingai.js index 2ccbc2b356..dcf150d1cf 100644 --- a/api/app/bingai.js +++ b/api/app/bingai.js @@ -1,7 +1,7 @@ require('dotenv').config(); const { KeyvFile } = require('keyv-file'); -const askBing = async ({ text, progressCallback, convo }) => { +const askBing = async ({ text, onProgress, convo }) => { const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api')); const bingAIClient = new BingAIClient({ @@ -14,10 +14,7 @@ const askBing = async ({ text, progressCallback, convo }) => { proxy: process.env.PROXY || null, }); - let options = { - onProgress: async (partialRes) => await progressCallback(partialRes), - }; - + let options = { onProgress }; if (convo) { options = { ...options, ...convo }; } diff --git a/api/app/chatgpt-browser.js b/api/app/chatgpt-browser.js index 8a3c903641..a5bea22729 100644 --- a/api/app/chatgpt-browser.js +++ b/api/app/chatgpt-browser.js @@ -10,7 +10,7 @@ const clientOptions = { proxy: process.env.PROXY || null, }; -const browserClient = async ({ text, progressCallback, convo }) => { +const browserClient = async ({ text, onProgress, convo }) => { const { ChatGPTBrowserClient } = await import('@waylaidwanderer/chatgpt-api'); const store = { @@ -18,10 +18,7 @@ const browserClient = async ({ text, progressCallback, convo }) => { }; const client = new ChatGPTBrowserClient(clientOptions, store); - - let options = { - onProgress: async (partialRes) => await progressCallback(partialRes) - }; + let options = { onProgress }; if (!!convo.parentMessageId && !!convo.conversationId) { options = { ...options, ...convo }; diff --git a/api/app/chatgpt-client.js b/api/app/chatgpt-client.js index afd31e0a81..350d1210ce 100644 --- a/api/app/chatgpt-client.js +++ b/api/app/chatgpt-client.js @@ -9,17 +9,14 @@ const clientOptions = { debug: false }; -const askClient = async ({ text, progressCallback, convo }) => { +const askClient = async ({ text, onProgress, convo }) => { const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default; const store = { store: new KeyvFile({ filename: './data/cache.json' }) }; const client = new ChatGPTClient(process.env.OPENAI_KEY, clientOptions, store); - - let options = { - onProgress: async (partialRes) => await progressCallback(partialRes) - }; + let options = { onProgress }; if (!!convo.parentMessageId && !!convo.conversationId) { options = { ...options, ...convo }; diff --git a/api/app/sydney.js b/api/app/sydney.js index fe47c74f57..3466f71c17 100644 --- a/api/app/sydney.js +++ b/api/app/sydney.js @@ -1,7 +1,7 @@ require('dotenv').config(); const { KeyvFile } = require('keyv-file'); -const askSydney = async ({ text, progressCallback, convo }) => { +const askSydney = async ({ text, onProgress, convo }) => { const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api')); const sydneyClient = new BingAIClient({ @@ -15,10 +15,10 @@ const askSydney = async ({ text, progressCallback, convo }) => { let options = { jailbreakConversationId: true, - onProgress: async (partialRes) => await progressCallback(partialRes), + onProgress, }; - if (convo.parentMessageId) { + if (convo.jailbreakConversationId) { options = { ...options, jailbreakConversationId: convo.jailbreakConversationId, parentMessageId: convo.parentMessageId }; } diff --git a/api/models/Conversation.js b/api/models/Conversation.js index 6e3c646c64..622744d27a 100644 --- a/api/models/Conversation.js +++ b/api/models/Conversation.js @@ -96,7 +96,7 @@ module.exports = { const totalConvos = await Conversation.countDocuments(); const totalPages = Math.ceil(totalConvos / pageSize); const convos = await Conversation.find() - .sort({ createdAt: -1 }) + .sort({ createdAt: -1, created: -1 }) .skip((pageNumber - 1) * pageSize) .limit(pageSize) .exec(); @@ -127,7 +127,7 @@ module.exports = { const conversations = await Conversation.find({ model: null }).exec(); if (!conversations || conversations.length === 0) - return { message: 'No conversations to migrate' }; + return { message: '[Migrate] No conversations to migrate' }; for (let convo of conversations) { const messages = await getMessages({ @@ -135,22 +135,20 @@ module.exports = { messageId: { $exists: false } }); - const promises = []; let model; let oldId; + const promises = []; messages.forEach((message, i) => { const msgObj = message.toObject(); const newId = msgObj.id; if (i === 0) { message.parentMessageId = '00000000-0000-0000-0000-000000000000'; - oldId = newId; } else { message.parentMessageId = oldId; - oldId = newId; } + oldId = newId; message.messageId = newId; - message.createdAt = message.created; if (message.sender.toLowerCase() !== 'user' && !model) { model = message.sender.toLowerCase(); } @@ -164,7 +162,7 @@ module.exports = { await Conversation.findOneAndUpdate( { conversationId: convo.conversationId }, - { model, createdAt: convo.created }, + { model }, { new: true } ).exec(); } @@ -172,11 +170,11 @@ module.exports = { try { await mongoose.connection.db.collection('messages').dropIndex('id_1'); } catch (error) { - console.log("Index doesn't exist or already dropped"); + console.log("[Migrate] Index doesn't exist or already dropped"); } } catch (error) { console.log(error); - return { message: 'Error migrating conversations' }; + return { message: '[Migrate] Error migrating conversations' }; } } }; diff --git a/api/package-lock.json b/api/package-lock.json index 911fda953e..daa36e69e4 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -17,6 +17,7 @@ "express": "^4.18.2", "keyv": "^4.5.2", "keyv-file": "^0.2.0", + "lodash": "^4.17.21", "mongoose": "^6.9.0", "openai": "^3.1.0" }, diff --git a/api/package.json b/api/package.json index 28bd6769d3..6dcf344ea0 100644 --- a/api/package.json +++ b/api/package.json @@ -27,6 +27,7 @@ "express": "^4.18.2", "keyv": "^4.5.2", "keyv-file": "^0.2.0", + "lodash": "^4.17.21", "mongoose": "^6.9.0", "openai": "^3.1.0" }, diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js index d12ee17b4e..8d20b808dc 100644 --- a/api/server/routes/ask.js +++ b/api/server/routes/ask.js @@ -8,10 +8,10 @@ const { askClient, browserClient, customClient, - detectCode + // detectCode } = require('../../app/'); const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models'); -const { handleError, sendMessage } = require('./handlers'); +const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers'); const { getMessages } = require('../../models/Message'); router.use('/bing', askBing); @@ -138,37 +138,10 @@ const ask = async ({ sendMessage(res, { message: userMessage, created: true }); try { - let i = 0; - let tokens = ''; - const progressCallback = async (partial) => { - if (i === 0 && typeof partial === 'object') { - userMessage.conversationId = conversationId ? conversationId : partial.conversationId; - await saveMessage(userMessage); - sendMessage(res, { ...partial, parentMessageId: overrideParentMessageId || userMessageId, initial: true }); - i++; - } - - if (typeof partial === 'object') { - sendMessage(res, { ...partial, parentMessageId: overrideParentMessageId || userMessageId, message: true }); - } else { - tokens += partial === text ? '' : partial; - if (tokens.match(/^\n/)) { - tokens = tokens.replace(/^\n/, ''); - } - - if (tokens.includes('[DONE]')) { - tokens = tokens.replace('[DONE]', ''); - } - - // tokens = await detectCode(tokens); - sendMessage(res, { text: tokens, message: true, initial: i === 0 ? true : false }); - i++; - } - }; - + const progressCallback = createOnProgress(); let gptResponse = await client({ text, - progressCallback, + onProgress: progressCallback.call(null, model, {res, text }), convo: { parentMessageId: userParentMessageId, conversationId, @@ -204,7 +177,8 @@ const ask = async ({ gptResponse.sender = model === 'chatgptCustom' ? convo.chatGptLabel : model; // gptResponse.final = true; - gptResponse.text = await detectCode(gptResponse.text); + // gptResponse.text = await detectCode(gptResponse.text); + gptResponse.text = await handleText(gptResponse.text); if (convo.chatGptLabel?.length > 0 && model === 'chatgptCustom') { gptResponse.chatGptLabel = convo.chatGptLabel; diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js index 1871e96f40..5bb8abd4f0 100644 --- a/api/server/routes/askBing.js +++ b/api/server/routes/askBing.js @@ -3,7 +3,7 @@ const crypto = require('crypto'); const router = express.Router(); const { titleConvo, getCitations, citeText, askBing } = require('../../app/'); const { saveMessage, getConvoTitle, saveConvo } = require('../../models'); -const { handleError, sendMessage } = require('./handlers'); +const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers'); const citationRegex = /\[\^\d+?\^]/g; router.post('/', async (req, res) => { @@ -68,17 +68,10 @@ const ask = async ({ sendMessage(res, { message: userMessage, created: true }); try { - let tokens = ''; - const progressCallback = async (partial) => { - tokens += partial === text ? '' : partial; - // tokens = appendCode(tokens); - tokens = citeText(tokens, true); - sendMessage(res, { text: tokens, message: true, parentMessageId: overrideParentMessageId || userMessageId }); - }; - + const progressCallback = createOnProgress(); let response = await askBing({ text, - progressCallback, + onProgress: progressCallback.call(null, model, {res, text, parentMessageId: overrideParentMessageId || userMessageId }), convo: { ...convo, parentMessageId: userParentMessageId, @@ -120,6 +113,7 @@ const ask = async ({ response.text = citeText(response) + (links?.length > 0 && hasCitations ? `\n${links}` : ''); + response.text = await handleText(response.text); await saveMessage(response); await saveConvo({...response, model, ...convo}); diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js index 3a663184b6..a0a3bd32df 100644 --- a/api/server/routes/askSydney.js +++ b/api/server/routes/askSydney.js @@ -3,7 +3,7 @@ const crypto = require('crypto'); const router = express.Router(); const { titleConvo, getCitations, citeText, askSydney } = require('../../app/'); const { saveMessage, saveConvo, getConvoTitle } = require('../../models'); -const { handleError, sendMessage } = require('./handlers'); +const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers'); const citationRegex = /\[\^\d+?\^]/g; router.post('/', async (req, res) => { @@ -68,17 +68,10 @@ const ask = async ({ sendMessage(res, { message: userMessage, created: true }); try { - let tokens = ''; - const progressCallback = async (partial) => { - tokens += partial === text ? '' : partial; - // tokens = appendCode(tokens); - tokens = citeText(tokens, true); - sendMessage(res, { text: tokens, message: true, parentMessageId: overrideParentMessageId || userMessageId }); - }; - + const progressCallback = createOnProgress(); let response = await askSydney({ text, - progressCallback, + onProgress: progressCallback.call(null, model, {res, text, parentMessageId: overrideParentMessageId || userMessageId }), convo: { parentMessageId: userParentMessageId, conversationId, @@ -121,6 +114,7 @@ const ask = async ({ response.text = citeText(response) + (links?.length > 0 && hasCitations ? `\n${links}` : ''); + response.text = await handleText(response.text); // Save user message userMessage.conversationId = response.conversationId || conversationId; diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js index e727cacfa3..d3e613bccc 100644 --- a/api/server/routes/handlers.js +++ b/api/server/routes/handlers.js @@ -1,3 +1,7 @@ +const { citeText, detectCode } = require('../../app/'); +const _ = require('lodash'); +const sanitizeHtml = require('sanitize-html'); + const handleError = (res, message) => { res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`); res.end(); @@ -10,4 +14,46 @@ const sendMessage = (res, message) => { res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`); }; -module.exports = { handleError, sendMessage }; +const createOnProgress = () => { + let i = 0; + let tokens = ''; + + const progressCallback = async (partial, { res, text, bing = false, ...rest }) => { + tokens += partial === text ? '' : partial; + tokens = tokens.replaceAll('[DONE]', ''); + + if (tokens.match(/^\n/)) { + tokens = tokens.replace(/^\n/, ''); + } + // if (tokens.includes('```')) { + // tokens = sanitizeHtml(tokens); + // } + + if (bing) { + tokens = citeText(tokens, true); + } + + sendMessage(res, { text: tokens, message: true, initial: i === 0, ...rest }); + i++; + }; + + const onProgress = (model, opts) => { + const bingModels = new Set(['bingai', 'sydney']); + return _.partialRight(progressCallback, { ...opts, bing: bingModels.has(model) }); + }; + + return onProgress; +}; + +const handleText = async (input) => { + let text = input; + text = await detectCode(text); + // if (text.includes('```')) { + // text = sanitizeHtml(text); + // text = text.replaceAll(') =>', ') =>'); + // } + + return text; +}; + +module.exports = { handleError, sendMessage, createOnProgress, handleText }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..bd60daac40 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,293 @@ +{ + "name": "chatgpt-clone", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "sanitize-html": "^2.10.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/sanitize-html": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.10.0.tgz", + "integrity": "sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + } + }, + "dependencies": { + "deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==" + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "sanitize-html": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.10.0.tgz", + "integrity": "sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==", + "requires": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..4da6b6e8c4 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "sanitize-html": "^2.10.0" + } +}