Support sending unknown mx emojis as sprite sheet
This commit is contained in:
		
							parent
							
								
									2b537f42f0
								
							
						
					
					
						commit
						49d9d31b30
					
				
					 7 changed files with 512 additions and 34 deletions
				
			
		| 
						 | 
					@ -17,13 +17,19 @@ const eventToMessage = sync.require("../converters/event-to-message")
 | 
				
			||||||
const api = sync.require("../../matrix/api")
 | 
					const api = sync.require("../../matrix/api")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string})[]}} message
 | 
					 * @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer}[], pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer})[]}} message
 | 
				
			||||||
 * @returns {Promise<DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer}[]}>}
 | 
					 * @returns {Promise<DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer}[]}>}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function resolvePendingFiles(message) {
 | 
					async function resolvePendingFiles(message) {
 | 
				
			||||||
	if (!message.pendingFiles) return message
 | 
						if (!message.pendingFiles) return message
 | 
				
			||||||
	const files = await Promise.all(message.pendingFiles.map(async p => {
 | 
						const files = await Promise.all(message.pendingFiles.map(async p => {
 | 
				
			||||||
		let fileBuffer
 | 
							let fileBuffer
 | 
				
			||||||
 | 
							if ("buffer" in p) {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									name: p.name,
 | 
				
			||||||
 | 
									file: p.buffer
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if ("key" in p) {
 | 
							if ("key" in p) {
 | 
				
			||||||
			// Encrypted
 | 
								// Encrypted
 | 
				
			||||||
			const d = crypto.createDecipheriv("aes-256-ctr", Buffer.from(p.key, "base64url"), Buffer.from(p.iv, "base64url"))
 | 
								const d = crypto.createDecipheriv("aes-256-ctr", Buffer.from(p.key, "base64url"), Buffer.from(p.iv, "base64url"))
 | 
				
			||||||
| 
						 | 
					@ -44,7 +50,7 @@ async function resolvePendingFiles(message) {
 | 
				
			||||||
	}))
 | 
						}))
 | 
				
			||||||
	const newMessage = {
 | 
						const newMessage = {
 | 
				
			||||||
		...message,
 | 
							...message,
 | 
				
			||||||
		files
 | 
							files: files.concat(message.files || [])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	delete newMessage.pendingFiles
 | 
						delete newMessage.pendingFiles
 | 
				
			||||||
	return newMessage
 | 
						return newMessage
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										95
									
								
								m2d/converters/emoji-sheet.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								m2d/converters/emoji-sheet.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,95 @@
 | 
				
			||||||
 | 
					// @ts-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assert = require("assert").strict
 | 
				
			||||||
 | 
					const {pipeline} = require("stream").promises
 | 
				
			||||||
 | 
					const sharp = require("sharp")
 | 
				
			||||||
 | 
					const {GIFrame} = require("giframe")
 | 
				
			||||||
 | 
					const utils = require("./utils")
 | 
				
			||||||
 | 
					const fetch = require("node-fetch").default
 | 
				
			||||||
 | 
					const streamMimeType = require("stream-mime-type")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SIZE = 48
 | 
				
			||||||
 | 
					const RESULT_WIDTH = 400
 | 
				
			||||||
 | 
					const IMAGES_ACROSS = Math.floor(RESULT_WIDTH / SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Composite a bunch of Matrix emojis into a kind of spritesheet image to upload to Discord.
 | 
				
			||||||
 | 
					 * @param {string[]} mxcs mxc URLs, in order
 | 
				
			||||||
 | 
					 * @returns {Promise<Buffer>} PNG image
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function compositeMatrixEmojis(mxcs) {
 | 
				
			||||||
 | 
						let buffers = await Promise.all(mxcs.map(async mxc => {
 | 
				
			||||||
 | 
							const abortController = new AbortController()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								const url = utils.getPublicUrlForMxc(mxc)
 | 
				
			||||||
 | 
								assert(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/** @type {import("node-fetch").Response} res */
 | 
				
			||||||
 | 
								// If it turns out to be a GIF, we want to abandon the connection without downloading the whole thing.
 | 
				
			||||||
 | 
								// If we were using connection pooling, we would be forced to download the entire GIF.
 | 
				
			||||||
 | 
								// So we set no agent to ensure we are not connection pooling.
 | 
				
			||||||
 | 
								// @ts-ignore the signal is slightly different from the type it wants (still works fine)
 | 
				
			||||||
 | 
								const res = await fetch(url, {agent: false, signal: abortController.signal})
 | 
				
			||||||
 | 
								const {stream, mime} = await streamMimeType.getMimeType(res.body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (mime === "image/png" || mime === "image/jpeg" || mime === "image/webp") {
 | 
				
			||||||
 | 
									/** @type {{info: sharp.OutputInfo, buffer: Buffer}} */
 | 
				
			||||||
 | 
									const result = await new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
										const transformer = sharp()
 | 
				
			||||||
 | 
											.resize(SIZE, SIZE, {fit: "contain"})
 | 
				
			||||||
 | 
											.png({compressionLevel: 0})
 | 
				
			||||||
 | 
											.toBuffer((err, buffer, info) => {
 | 
				
			||||||
 | 
												if (err) return reject(err)
 | 
				
			||||||
 | 
												resolve({info, buffer})
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										pipeline(
 | 
				
			||||||
 | 
											stream,
 | 
				
			||||||
 | 
											transformer
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									return result.buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else if (mime === "image/gif") {
 | 
				
			||||||
 | 
									const giframe = new GIFrame(0)
 | 
				
			||||||
 | 
									stream.on("data", chunk => {
 | 
				
			||||||
 | 
										giframe.feed(chunk)
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									const frame = await giframe.getFrame()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const buffer = await sharp(frame.pixels, {raw: {width: frame.width, height: frame.height, channels: 4}})
 | 
				
			||||||
 | 
										.resize(SIZE, SIZE, {fit: "contain", background: {r: 0, g: 0, b: 0, alpha: 0}})
 | 
				
			||||||
 | 
										.png({compressionLevel: 0})
 | 
				
			||||||
 | 
										.toBuffer({resolveWithObject: true})
 | 
				
			||||||
 | 
									return buffer.data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// unsupported mime type
 | 
				
			||||||
 | 
									console.error(`I don't know what a ${mime} emoji is.`)
 | 
				
			||||||
 | 
									return null
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} finally {
 | 
				
			||||||
 | 
								abortController.abort()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const totalWidth = Math.min(buffers.length, IMAGES_ACROSS) * SIZE
 | 
				
			||||||
 | 
						const imagesDown = Math.ceil(buffers.length / IMAGES_ACROSS)
 | 
				
			||||||
 | 
						const totalHeight = imagesDown * SIZE
 | 
				
			||||||
 | 
						const comp = []
 | 
				
			||||||
 | 
						let left = 0, top = 0
 | 
				
			||||||
 | 
						for (const buffer of buffers) {
 | 
				
			||||||
 | 
							if (Buffer.isBuffer(buffer)) {
 | 
				
			||||||
 | 
								comp.push({left, top, input: buffer})
 | 
				
			||||||
 | 
								;(left += SIZE) + SIZE > RESULT_WIDTH && (left = 0, top += SIZE)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const output = await sharp({create: {width: totalWidth, height: totalHeight, channels: 4, background: {r: 0, g: 0, b: 0, alpha: 0}}})
 | 
				
			||||||
 | 
							.composite(comp)
 | 
				
			||||||
 | 
							.png()
 | 
				
			||||||
 | 
							.toBuffer({resolveWithObject: true})
 | 
				
			||||||
 | 
						return output.data
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports.compositeMatrixEmojis = compositeMatrixEmojis
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,8 @@ const {sync, db, discord, select, from} = passthrough
 | 
				
			||||||
const file = sync.require("../../matrix/file")
 | 
					const file = sync.require("../../matrix/file")
 | 
				
			||||||
/** @type {import("../converters/utils")} */
 | 
					/** @type {import("../converters/utils")} */
 | 
				
			||||||
const utils = sync.require("../converters/utils")
 | 
					const utils = sync.require("../converters/utils")
 | 
				
			||||||
 | 
					/** @type {import("./emoji-sheet")} */
 | 
				
			||||||
 | 
					const emojiSheet = sync.require("./emoji-sheet")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BLOCK_ELEMENTS = [
 | 
					const BLOCK_ELEMENTS = [
 | 
				
			||||||
	"ADDRESS", "ARTICLE", "ASIDE", "AUDIO", "BLOCKQUOTE", "BODY", "CANVAS",
 | 
						"ADDRESS", "ARTICLE", "ASIDE", "AUDIO", "BLOCKQUOTE", "BODY", "CANVAS",
 | 
				
			||||||
| 
						 | 
					@ -117,15 +119,21 @@ turndownService.addRule("inlineLink", {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** @type {string[]} SPRITE SHEET EMOJIS FEATURE: mxc urls for the currently processing message */
 | 
				
			||||||
 | 
					let endOfMessageEmojis = []
 | 
				
			||||||
turndownService.addRule("emoji", {
 | 
					turndownService.addRule("emoji", {
 | 
				
			||||||
	filter: function (node, options) {
 | 
						filter: function (node, options) {
 | 
				
			||||||
		if (node.nodeName !== "IMG" || !node.hasAttribute("data-mx-emoticon") || !node.getAttribute("src")) return false
 | 
							if (node.nodeName !== "IMG" || !node.hasAttribute("data-mx-emoticon") || !node.getAttribute("src") || !node.getAttribute("title")) return false
 | 
				
			||||||
		let row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(node.getAttribute("src"))
 | 
							return true
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						replacement: function (content, node) {
 | 
				
			||||||
 | 
							const mxcUrl = node.getAttribute("src")
 | 
				
			||||||
 | 
							let row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(mxcUrl)
 | 
				
			||||||
		if (!row) {
 | 
							if (!row) {
 | 
				
			||||||
			// We don't know what this is... but maybe we can guess based on the name?
 | 
								// We don't know what this is... but maybe we can guess based on the name?
 | 
				
			||||||
			const guessedName = node.getAttribute("title")?.replace?.(/^:|:$/g, "")
 | 
								const guessedName = node.getAttribute("title").replace(/^:|:$/g, "")
 | 
				
			||||||
			if (!guessedName) return false
 | 
								for (const guild of discord?.guilds.values() || []) {
 | 
				
			||||||
			for (const guild of discord.guilds.values()) {
 | 
					 | 
				
			||||||
				/** @type {{name: string, id: string, animated: number}[]} */
 | 
									/** @type {{name: string, id: string, animated: number}[]} */
 | 
				
			||||||
				// @ts-ignore
 | 
									// @ts-ignore
 | 
				
			||||||
				const emojis = guild.emojis
 | 
									const emojis = guild.emojis
 | 
				
			||||||
| 
						 | 
					@ -136,21 +144,18 @@ turndownService.addRule("emoji", {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!row) return false
 | 
							if (row) {
 | 
				
			||||||
		node.setAttribute("data-emoji-id", row.id)
 | 
								const animatedChar = row.animated ? "a" : ""
 | 
				
			||||||
		node.setAttribute("data-emoji-name", row.name)
 | 
								return `<${animatedChar}:${row.name}:${row.id}>`
 | 
				
			||||||
		node.setAttribute("data-emoji-animated-char", row.animated ? "a" : "")
 | 
							} else {
 | 
				
			||||||
		return true
 | 
								if (endOfMessageEmojis.includes(mxcUrl)) {
 | 
				
			||||||
	},
 | 
									// After control returns to the main converter, it will rewind over this, delete this section, and upload the emojis as a sprite sheet.
 | 
				
			||||||
 | 
									return `<::>`
 | 
				
			||||||
	replacement: function (content, node) {
 | 
								} else {
 | 
				
			||||||
		/** @type {string} */
 | 
									// This emoji is not at the end of the message, it is in the middle. We don't upload middle emojis as a sprite sheet.
 | 
				
			||||||
		const id = node.getAttribute("data-emoji-id")
 | 
									return `[${node.getAttribute("title")}](${utils.getPublicUrlForMxc(mxcUrl)})`
 | 
				
			||||||
		/** @type {string} */
 | 
								}
 | 
				
			||||||
		const animatedChar = node.getAttribute("data-emoji-animated-char")
 | 
							}
 | 
				
			||||||
		/** @type {string} */
 | 
					 | 
				
			||||||
		const name = node.getAttribute("data-emoji-name")
 | 
					 | 
				
			||||||
		return `<${animatedChar}:${name}:${id}>`
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,6 +225,29 @@ function splitDisplayName(displayName) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * At the time of this executing, we know what the end of message emojis are, and we know that at least one of them is unknown.
 | 
				
			||||||
 | 
					 * This function will strip them from the content and generate the correct pending file of the sprite sheet.
 | 
				
			||||||
 | 
					 * @param {string} content
 | 
				
			||||||
 | 
					 * @param {{id: string, name: string}[]} attachments
 | 
				
			||||||
 | 
					 * @param {({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer})[]} pendingFiles
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function uploadEndOfMessageSpriteSheet(content, attachments, pendingFiles) {
 | 
				
			||||||
 | 
						if (!content.includes("<::>")) return content // No unknown emojis, nothing to do
 | 
				
			||||||
 | 
						// Remove known and unknown emojis from the end of the message
 | 
				
			||||||
 | 
						const r = /<a?:[a-zA-Z0-9_-]*:[0-9]*>\s*$/
 | 
				
			||||||
 | 
						while (content.match(r)) {
 | 
				
			||||||
 | 
							content = content.replace(r, "")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Create a sprite sheet of known and unknown emojis from the end of the message
 | 
				
			||||||
 | 
						const buffer = await emojiSheet.compositeMatrixEmojis(endOfMessageEmojis)
 | 
				
			||||||
 | 
						// Attach it
 | 
				
			||||||
 | 
						const name = "emojis.png"
 | 
				
			||||||
 | 
						attachments.push({id: "0", name})
 | 
				
			||||||
 | 
						pendingFiles.push({name, buffer})
 | 
				
			||||||
 | 
						return content
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {Ty.Event.Outer_M_Room_Message | Ty.Event.Outer_M_Room_Message_File | Ty.Event.Outer_M_Sticker | Ty.Event.Outer_M_Room_Message_Encrypted_File} event
 | 
					 * @param {Ty.Event.Outer_M_Room_Message | Ty.Event.Outer_M_Room_Message_File | Ty.Event.Outer_M_Sticker | Ty.Event.Outer_M_Room_Message_Encrypted_File} event
 | 
				
			||||||
 * @param {import("discord-api-types/v10").APIGuild} guild
 | 
					 * @param {import("discord-api-types/v10").APIGuild} guild
 | 
				
			||||||
| 
						 | 
					@ -251,7 +279,7 @@ async function eventToMessage(event, guild, di) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let content = event.content.body // ultimate fallback
 | 
						let content = event.content.body // ultimate fallback
 | 
				
			||||||
	const attachments = []
 | 
						const attachments = []
 | 
				
			||||||
	/** @type {({name: string, url: string} | {name: string, url: string, key: string, iv: string})[]} */
 | 
						/** @type {({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer})[]} */
 | 
				
			||||||
	const pendingFiles = []
 | 
						const pendingFiles = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Convert content depending on what the message is
 | 
						// Convert content depending on what the message is
 | 
				
			||||||
| 
						 | 
					@ -387,11 +415,27 @@ async function eventToMessage(event, guild, di) {
 | 
				
			||||||
			// input = input.replace(/ /g, " ")
 | 
								// input = input.replace(/ /g, " ")
 | 
				
			||||||
			// There is also a corresponding test to uncomment, named "event2message: whitespace is retained"
 | 
								// There is also a corresponding test to uncomment, named "event2message: whitespace is retained"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// SPRITE SHEET EMOJIS FEATURE: Emojis at the end of the message that we don't know about will be reuploaded as a sprite sheet.
 | 
				
			||||||
 | 
								// First we need to determine which emojis are at the end.
 | 
				
			||||||
 | 
								endOfMessageEmojis = []
 | 
				
			||||||
 | 
								let match
 | 
				
			||||||
 | 
								let last = input.length
 | 
				
			||||||
 | 
								while ((match = input.slice(0, last).match(/<img [^>]*>\s*$/))) {
 | 
				
			||||||
 | 
									if (!match[0].includes("data-mx-emoticon")) break
 | 
				
			||||||
 | 
									const mxcUrl = match[0].match(/\bsrc="(mxc:\/\/[^"]+)"/)
 | 
				
			||||||
 | 
									if (mxcUrl) endOfMessageEmojis.unshift(mxcUrl[1])
 | 
				
			||||||
 | 
									if (typeof match.index !== "number") break
 | 
				
			||||||
 | 
									last = match.index
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// @ts-ignore bad type from turndown
 | 
								// @ts-ignore bad type from turndown
 | 
				
			||||||
			content = turndownService.turndown(input)
 | 
								content = turndownService.turndown(input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// It's designed for commonmark, we need to replace the space-space-newline with just newline
 | 
								// It's designed for commonmark, we need to replace the space-space-newline with just newline
 | 
				
			||||||
			content = content.replace(/  \n/g, "\n")
 | 
								content = content.replace(/  \n/g, "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// SPRITE SHEET EMOJIS FEATURE:
 | 
				
			||||||
 | 
								content = await uploadEndOfMessageSpriteSheet(content, attachments, pendingFiles)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// Looks like we're using the plaintext body!
 | 
								// Looks like we're using the plaintext body!
 | 
				
			||||||
			content = event.content.body
 | 
								content = event.content.body
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,14 @@ const data = require("../../test/data")
 | 
				
			||||||
const {MatrixServerError} = require("../../matrix/mreq")
 | 
					const {MatrixServerError} = require("../../matrix/mreq")
 | 
				
			||||||
const {db, select} = require("../../passthrough")
 | 
					const {db, select} = require("../../passthrough")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function slow() {
 | 
				
			||||||
 | 
						if (process.argv.includes("--slow")) {
 | 
				
			||||||
 | 
							return test
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return test.skip
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {string} roomID
 | 
					 * @param {string} roomID
 | 
				
			||||||
 * @param {string} eventID
 | 
					 * @param {string} eventID
 | 
				
			||||||
| 
						 | 
					@ -1744,3 +1752,81 @@ test("event2message: animated emojis work", async t => {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test("event2message: unknown emojis in the middle are linked", async t => {
 | 
				
			||||||
 | 
						t.deepEqual(
 | 
				
			||||||
 | 
							await eventToMessage({
 | 
				
			||||||
 | 
								type: "m.room.message",
 | 
				
			||||||
 | 
								sender: "@cadence:cadence.moe",
 | 
				
			||||||
 | 
								content: {
 | 
				
			||||||
 | 
									msgtype: "m.text",
 | 
				
			||||||
 | 
									body: "wrong body",
 | 
				
			||||||
 | 
									format: "org.matrix.custom.html",
 | 
				
			||||||
 | 
									formatted_body: 'a <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/RLMgJGfgTPjIQtvvWZsYjhjy\" title=\":ms_robot_grin:\" alt=\":ms_robot_grin:\"> b'
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU",
 | 
				
			||||||
 | 
								room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								messagesToDelete: [],
 | 
				
			||||||
 | 
								messagesToEdit: [],
 | 
				
			||||||
 | 
								messagesToSend: [{
 | 
				
			||||||
 | 
									username: "cadence [they]",
 | 
				
			||||||
 | 
									content: "a [:ms_robot_grin:](https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/RLMgJGfgTPjIQtvvWZsYjhjy) b",
 | 
				
			||||||
 | 
									avatar_url: undefined
 | 
				
			||||||
 | 
								}]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					slow()("event2message: unknown emoji in the end is reuploaded as a sprite sheet", async t => {
 | 
				
			||||||
 | 
						const messages = await eventToMessage({
 | 
				
			||||||
 | 
							type: "m.room.message",
 | 
				
			||||||
 | 
							sender: "@cadence:cadence.moe",
 | 
				
			||||||
 | 
							content: {
 | 
				
			||||||
 | 
								msgtype: "m.text",
 | 
				
			||||||
 | 
								body: "wrong body",
 | 
				
			||||||
 | 
								format: "org.matrix.custom.html",
 | 
				
			||||||
 | 
								formatted_body: 'a b <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/RLMgJGfgTPjIQtvvWZsYjhjy\" title=\":ms_robot_grin:\" alt=\":ms_robot_grin:\">'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU",
 | 
				
			||||||
 | 
							room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						const testResult = {
 | 
				
			||||||
 | 
							content: messages.messagesToSend[0].content,
 | 
				
			||||||
 | 
							fileName: messages.messagesToSend[0].pendingFiles[0].name,
 | 
				
			||||||
 | 
							fileContentStart: messages.messagesToSend[0].pendingFiles[0].buffer.subarray(0, 90).toString("base64")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.deepEqual(testResult, {
 | 
				
			||||||
 | 
							content: "a b",
 | 
				
			||||||
 | 
							fileName: "emojis.png",
 | 
				
			||||||
 | 
							fileContentStart: "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAPoAAAD6AG1e1JrAAALkklEQVR4nM1ZeWyUxxV/azAGwn0JMJUppPhce++1Oc1i"
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					slow()("event2message: known and unknown emojis in the end are reuploaded as a sprite sheet", async t => {
 | 
				
			||||||
 | 
						t.comment("SKIPPED")
 | 
				
			||||||
 | 
						const messages = await eventToMessage({
 | 
				
			||||||
 | 
							type: "m.room.message",
 | 
				
			||||||
 | 
							sender: "@cadence:cadence.moe",
 | 
				
			||||||
 | 
							content: {
 | 
				
			||||||
 | 
								msgtype: "m.text",
 | 
				
			||||||
 | 
								body: "wrong body",
 | 
				
			||||||
 | 
								format: "org.matrix.custom.html",
 | 
				
			||||||
 | 
								formatted_body: 'known unknown: <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC\" title=\":hippo:\" alt=\":hippo:\"> <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/wcouHVjbKJJYajkhJLsyeJAA\" title=\":ms_robot_dress:\" alt=\":ms_robot_dress:\"> and known unknown: <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/WbYqNlACRuicynBfdnPYtmvc\" title=\":hipposcope:\" alt=\":hipposcope:\"> <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/HYcztccFIPgevDvoaWNsEtGJ\" title=\":ms_robot_cat:\" alt=\":ms_robot_cat:\">'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU",
 | 
				
			||||||
 | 
							room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						const testResult = {
 | 
				
			||||||
 | 
							content: messages.messagesToSend[0].content,
 | 
				
			||||||
 | 
							fileName: messages.messagesToSend[0].pendingFiles[0].name,
 | 
				
			||||||
 | 
							fileContentStart: messages.messagesToSend[0].pendingFiles[0].buffer.subarray(0, 90).toString("base64")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						require("fs").writeFileSync("/tmp/emojis.png", messages.messagesToSend[0].pendingFiles[0].buffer)
 | 
				
			||||||
 | 
						t.deepEqual(testResult, {
 | 
				
			||||||
 | 
							content: "known unknown: <:hippo:230201364309868544> [:ms_robot_dress:](https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/wcouHVjbKJJYajkhJLsyeJAA) and known unknown:",
 | 
				
			||||||
 | 
							fileName: "emojis.png",
 | 
				
			||||||
 | 
							fileContentStart: "iVBORw0KGgoAAAANSUhEUgAAAGAAAAAwCAYAAADuFn/PAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAT5UlEQVR4nOVbCXSVRZauR9gMsoYlvKwvARKSkPUlJOyL"
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										263
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										263
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -13,8 +13,8 @@
 | 
				
			||||||
        "better-sqlite3": "^8.3.0",
 | 
					        "better-sqlite3": "^8.3.0",
 | 
				
			||||||
        "chunk-text": "^2.0.1",
 | 
					        "chunk-text": "^2.0.1",
 | 
				
			||||||
        "cloudstorm": "^0.8.0",
 | 
					        "cloudstorm": "^0.8.0",
 | 
				
			||||||
        "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#440130ef343c8183a81c7c09809731484aa3a182",
 | 
					        "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3",
 | 
				
			||||||
        "giframe": "github:cloudrac3r/giframe#v0.3.0",
 | 
					        "giframe": "github:cloudrac3r/giframe#v0.4.1",
 | 
				
			||||||
        "heatsync": "^2.4.1",
 | 
					        "heatsync": "^2.4.1",
 | 
				
			||||||
        "js-yaml": "^4.1.0",
 | 
					        "js-yaml": "^4.1.0",
 | 
				
			||||||
        "matrix-appservice": "^2.0.0",
 | 
					        "matrix-appservice": "^2.0.0",
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,9 @@
 | 
				
			||||||
        "node-fetch": "^2.6.7",
 | 
					        "node-fetch": "^2.6.7",
 | 
				
			||||||
        "pngjs": "^7.0.0",
 | 
					        "pngjs": "^7.0.0",
 | 
				
			||||||
        "prettier-bytes": "^1.0.4",
 | 
					        "prettier-bytes": "^1.0.4",
 | 
				
			||||||
 | 
					        "sharp": "^0.32.6",
 | 
				
			||||||
        "snowtransfer": "^0.8.0",
 | 
					        "snowtransfer": "^0.8.0",
 | 
				
			||||||
 | 
					        "stream-mime-type": "^1.0.2",
 | 
				
			||||||
        "try-to-catch": "^3.0.1",
 | 
					        "try-to-catch": "^3.0.1",
 | 
				
			||||||
        "turndown": "^7.1.2",
 | 
					        "turndown": "^7.1.2",
 | 
				
			||||||
        "xxhash-wasm": "^1.0.2"
 | 
					        "xxhash-wasm": "^1.0.2"
 | 
				
			||||||
| 
						 | 
					@ -309,6 +311,11 @@
 | 
				
			||||||
        "node": ">=16"
 | 
					        "node": ">=16"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@tokenizer/token": {
 | 
				
			||||||
 | 
					      "version": "0.3.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/istanbul-lib-coverage": {
 | 
					    "node_modules/@types/istanbul-lib-coverage": {
 | 
				
			||||||
      "version": "2.0.4",
 | 
					      "version": "2.0.4",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
 | 
				
			||||||
| 
						 | 
					@ -451,6 +458,11 @@
 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					        "url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/b4a": {
 | 
				
			||||||
 | 
					      "version": "1.6.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/backtracker": {
 | 
					    "node_modules/backtracker": {
 | 
				
			||||||
      "version": "3.3.2",
 | 
					      "version": "3.3.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/backtracker/-/backtracker-3.3.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/backtracker/-/backtracker-3.3.2.tgz",
 | 
				
			||||||
| 
						 | 
					@ -780,11 +792,22 @@
 | 
				
			||||||
        "node": ">=12.0.0"
 | 
					        "node": ">=12.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/color": {
 | 
				
			||||||
 | 
					      "version": "4.2.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "color-convert": "^2.0.1",
 | 
				
			||||||
 | 
					        "color-string": "^1.9.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=12.5.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/color-convert": {
 | 
					    "node_modules/color-convert": {
 | 
				
			||||||
      "version": "2.0.1",
 | 
					      "version": "2.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
 | 
					      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "color-name": "~1.1.4"
 | 
					        "color-name": "~1.1.4"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
| 
						 | 
					@ -795,8 +818,16 @@
 | 
				
			||||||
    "node_modules/color-name": {
 | 
					    "node_modules/color-name": {
 | 
				
			||||||
      "version": "1.1.4",
 | 
					      "version": "1.1.4",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
 | 
				
			||||||
      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
 | 
					      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
 | 
				
			||||||
      "dev": true
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/color-string": {
 | 
				
			||||||
 | 
					      "version": "1.9.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "color-name": "^1.0.0",
 | 
				
			||||||
 | 
					        "simple-swizzle": "^0.2.2"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/colorette": {
 | 
					    "node_modules/colorette": {
 | 
				
			||||||
      "version": "1.4.0",
 | 
					      "version": "1.4.0",
 | 
				
			||||||
| 
						 | 
					@ -1003,9 +1034,9 @@
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/detect-libc": {
 | 
					    "node_modules/detect-libc": {
 | 
				
			||||||
      "version": "2.0.1",
 | 
					      "version": "2.0.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
 | 
				
			||||||
      "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
 | 
					      "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
 | 
				
			||||||
      "engines": {
 | 
					      "engines": {
 | 
				
			||||||
        "node": ">=8"
 | 
					        "node": ">=8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -1026,7 +1057,7 @@
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/discord-markdown": {
 | 
					    "node_modules/discord-markdown": {
 | 
				
			||||||
      "version": "2.4.1",
 | 
					      "version": "2.4.1",
 | 
				
			||||||
      "resolved": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#440130ef343c8183a81c7c09809731484aa3a182",
 | 
					      "resolved": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3",
 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "simple-markdown": "^0.7.2"
 | 
					        "simple-markdown": "^0.7.2"
 | 
				
			||||||
| 
						 | 
					@ -1210,6 +1241,11 @@
 | 
				
			||||||
        "node": ">= 0.8"
 | 
					        "node": ">= 0.8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/fast-fifo": {
 | 
				
			||||||
 | 
					      "version": "1.3.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/fastest-levenshtein": {
 | 
					    "node_modules/fastest-levenshtein": {
 | 
				
			||||||
      "version": "1.0.16",
 | 
					      "version": "1.0.16",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
 | 
				
			||||||
| 
						 | 
					@ -1219,6 +1255,22 @@
 | 
				
			||||||
        "node": ">= 4.9.1"
 | 
					        "node": ">= 4.9.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/file-type": {
 | 
				
			||||||
 | 
					      "version": "16.5.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "readable-web-to-node-stream": "^3.0.0",
 | 
				
			||||||
 | 
					        "strtok3": "^6.2.4",
 | 
				
			||||||
 | 
					        "token-types": "^4.1.1"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "url": "https://github.com/sindresorhus/file-type?sponsor=1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/file-uri-to-path": {
 | 
					    "node_modules/file-uri-to-path": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -1377,7 +1429,7 @@
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/giframe": {
 | 
					    "node_modules/giframe": {
 | 
				
			||||||
      "version": "0.3.0",
 | 
					      "version": "0.3.0",
 | 
				
			||||||
      "resolved": "git+ssh://git@github.com/cloudrac3r/giframe.git#abbadea54772051a1e08f68c5046fb8f94329e44",
 | 
					      "resolved": "git+ssh://git@github.com/cloudrac3r/giframe.git#1630f4d3b2bf5acd197409c85edd11e0da72d0a1",
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/github-from-package": {
 | 
					    "node_modules/github-from-package": {
 | 
				
			||||||
| 
						 | 
					@ -1614,6 +1666,11 @@
 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					        "url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/is-arrayish": {
 | 
				
			||||||
 | 
					      "version": "0.3.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/is-bigint": {
 | 
					    "node_modules/is-bigint": {
 | 
				
			||||||
      "version": "1.0.4",
 | 
					      "version": "1.0.4",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2116,6 +2173,11 @@
 | 
				
			||||||
        "node": ">=10"
 | 
					        "node": ">=10"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/node-addon-api": {
 | 
				
			||||||
 | 
					      "version": "6.1.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/node-fetch": {
 | 
					    "node_modules/node-fetch": {
 | 
				
			||||||
      "version": "2.6.12",
 | 
					      "version": "2.6.12",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2289,6 +2351,18 @@
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
 | 
				
			||||||
      "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
 | 
					      "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/peek-readable": {
 | 
				
			||||||
 | 
					      "version": "4.1.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=8"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "github",
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/Borewit"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/pngjs": {
 | 
					    "node_modules/pngjs": {
 | 
				
			||||||
      "version": "7.0.0",
 | 
					      "version": "7.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2403,6 +2477,11 @@
 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					        "url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/queue-tick": {
 | 
				
			||||||
 | 
					      "version": "1.0.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/range-parser": {
 | 
					    "node_modules/range-parser": {
 | 
				
			||||||
      "version": "1.2.1",
 | 
					      "version": "1.2.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2467,6 +2546,34 @@
 | 
				
			||||||
        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 | 
					        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/readable-web-to-node-stream": {
 | 
				
			||||||
 | 
					      "version": "3.0.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "readable-stream": "^3.6.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=8"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "github",
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/Borewit"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/readable-web-to-node-stream/node_modules/readable-stream": {
 | 
				
			||||||
 | 
					      "version": "3.6.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "inherits": "^2.0.3",
 | 
				
			||||||
 | 
					        "string_decoder": "^1.1.1",
 | 
				
			||||||
 | 
					        "util-deprecate": "^1.0.1"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">= 6"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/regexp.prototype.flags": {
 | 
					    "node_modules/regexp.prototype.flags": {
 | 
				
			||||||
      "version": "1.5.0",
 | 
					      "version": "1.5.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2660,6 +2767,48 @@
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
 | 
					      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/sharp": {
 | 
				
			||||||
 | 
					      "version": "0.32.6",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==",
 | 
				
			||||||
 | 
					      "hasInstallScript": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "color": "^4.2.3",
 | 
				
			||||||
 | 
					        "detect-libc": "^2.0.2",
 | 
				
			||||||
 | 
					        "node-addon-api": "^6.1.0",
 | 
				
			||||||
 | 
					        "prebuild-install": "^7.1.1",
 | 
				
			||||||
 | 
					        "semver": "^7.5.4",
 | 
				
			||||||
 | 
					        "simple-get": "^4.0.1",
 | 
				
			||||||
 | 
					        "tar-fs": "^3.0.4",
 | 
				
			||||||
 | 
					        "tunnel-agent": "^0.6.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=14.15.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "url": "https://opencollective.com/libvips"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/sharp/node_modules/tar-fs": {
 | 
				
			||||||
 | 
					      "version": "3.0.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "mkdirp-classic": "^0.5.2",
 | 
				
			||||||
 | 
					        "pump": "^3.0.0",
 | 
				
			||||||
 | 
					        "tar-stream": "^3.1.5"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/sharp/node_modules/tar-stream": {
 | 
				
			||||||
 | 
					      "version": "3.1.6",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "b4a": "^1.6.4",
 | 
				
			||||||
 | 
					        "fast-fifo": "^1.2.0",
 | 
				
			||||||
 | 
					        "streamx": "^2.15.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/shebang-command": {
 | 
					    "node_modules/shebang-command": {
 | 
				
			||||||
      "version": "2.0.0",
 | 
					      "version": "2.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2751,6 +2900,14 @@
 | 
				
			||||||
        "@types/react": ">=16.0.0"
 | 
					        "@types/react": ">=16.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/simple-swizzle": {
 | 
				
			||||||
 | 
					      "version": "0.2.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "is-arrayish": "^0.3.1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/snowtransfer": {
 | 
					    "node_modules/snowtransfer": {
 | 
				
			||||||
      "version": "0.8.3",
 | 
					      "version": "0.8.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.8.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.8.3.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2828,6 +2985,30 @@
 | 
				
			||||||
        "node": ">= 0.4"
 | 
					        "node": ">= 0.4"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/stream-head": {
 | 
				
			||||||
 | 
					      "version": "2.0.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/stream-head/-/stream-head-2.0.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-aRkUMcmgbDl2Yjd5LqsB1LKB58Ot3JZ4ffuFMkFuvkPQT5X5XFMr4YK2dctApc+d3o52CXU1KUFisYaF/4zjAQ==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "through2": "4.0.2"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/stream-mime-type": {
 | 
				
			||||||
 | 
					      "version": "1.0.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/stream-mime-type/-/stream-mime-type-1.0.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-80GzRn7JICPDEPBhSyqJjbztqX66+3DpkuUUcgDHtRBQlZRTkbCz0BsISggUl7AnyinJk9zyHVnd2lftlZXDdg==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "file-type": "^16.0.1",
 | 
				
			||||||
 | 
					        "mime-types": "^2.1.27",
 | 
				
			||||||
 | 
					        "stream-head": "^2.0.1"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/streamsearch": {
 | 
					    "node_modules/streamsearch": {
 | 
				
			||||||
      "version": "1.1.0",
 | 
					      "version": "1.1.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2836,6 +3017,15 @@
 | 
				
			||||||
        "node": ">=10.0.0"
 | 
					        "node": ">=10.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/streamx": {
 | 
				
			||||||
 | 
					      "version": "2.15.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "fast-fifo": "^1.1.0",
 | 
				
			||||||
 | 
					        "queue-tick": "^1.0.1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/string_decoder": {
 | 
					    "node_modules/string_decoder": {
 | 
				
			||||||
      "version": "1.3.0",
 | 
					      "version": "1.3.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -2902,6 +3092,22 @@
 | 
				
			||||||
        "node": ">=0.10.0"
 | 
					        "node": ">=0.10.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/strtok3": {
 | 
				
			||||||
 | 
					      "version": "6.3.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@tokenizer/token": "^0.3.0",
 | 
				
			||||||
 | 
					        "peek-readable": "^4.1.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "github",
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/Borewit"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/supertape": {
 | 
					    "node_modules/supertape": {
 | 
				
			||||||
      "version": "8.3.0",
 | 
					      "version": "8.3.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/supertape/-/supertape-8.3.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/supertape/-/supertape-8.3.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -3096,6 +3302,27 @@
 | 
				
			||||||
      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
 | 
					      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/through2": {
 | 
				
			||||||
 | 
					      "version": "4.0.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "readable-stream": "3"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/through2/node_modules/readable-stream": {
 | 
				
			||||||
 | 
					      "version": "3.6.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "inherits": "^2.0.3",
 | 
				
			||||||
 | 
					        "string_decoder": "^1.1.1",
 | 
				
			||||||
 | 
					        "util-deprecate": "^1.0.1"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">= 6"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/toidentifier": {
 | 
					    "node_modules/toidentifier": {
 | 
				
			||||||
      "version": "1.0.1",
 | 
					      "version": "1.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
 | 
				
			||||||
| 
						 | 
					@ -3104,6 +3331,22 @@
 | 
				
			||||||
        "node": ">=0.6"
 | 
					        "node": ">=0.6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/token-types": {
 | 
				
			||||||
 | 
					      "version": "4.2.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@tokenizer/token": "^0.3.0",
 | 
				
			||||||
 | 
					        "ieee754": "^1.2.1"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "github",
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/Borewit"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/tr46": {
 | 
					    "node_modules/tr46": {
 | 
				
			||||||
      "version": "0.0.3",
 | 
					      "version": "0.0.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@
 | 
				
			||||||
    "chunk-text": "^2.0.1",
 | 
					    "chunk-text": "^2.0.1",
 | 
				
			||||||
    "cloudstorm": "^0.8.0",
 | 
					    "cloudstorm": "^0.8.0",
 | 
				
			||||||
    "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3",
 | 
					    "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#abc56d544072a1dc5624adfea455b0e902adf7b3",
 | 
				
			||||||
    "giframe": "github:cloudrac3r/giframe#v0.3.0",
 | 
					    "giframe": "github:cloudrac3r/giframe#v0.4.1",
 | 
				
			||||||
    "heatsync": "^2.4.1",
 | 
					    "heatsync": "^2.4.1",
 | 
				
			||||||
    "js-yaml": "^4.1.0",
 | 
					    "js-yaml": "^4.1.0",
 | 
				
			||||||
    "matrix-appservice": "^2.0.0",
 | 
					    "matrix-appservice": "^2.0.0",
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,9 @@
 | 
				
			||||||
    "node-fetch": "^2.6.7",
 | 
					    "node-fetch": "^2.6.7",
 | 
				
			||||||
    "pngjs": "^7.0.0",
 | 
					    "pngjs": "^7.0.0",
 | 
				
			||||||
    "prettier-bytes": "^1.0.4",
 | 
					    "prettier-bytes": "^1.0.4",
 | 
				
			||||||
 | 
					    "sharp": "^0.32.6",
 | 
				
			||||||
    "snowtransfer": "^0.8.0",
 | 
					    "snowtransfer": "^0.8.0",
 | 
				
			||||||
 | 
					    "stream-mime-type": "^1.0.2",
 | 
				
			||||||
    "try-to-catch": "^3.0.1",
 | 
					    "try-to-catch": "^3.0.1",
 | 
				
			||||||
    "turndown": "^7.1.2",
 | 
					    "turndown": "^7.1.2",
 | 
				
			||||||
    "xxhash-wasm": "^1.0.2"
 | 
					    "xxhash-wasm": "^1.0.2"
 | 
				
			||||||
| 
						 | 
					@ -44,6 +46,7 @@
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js | tap-dot",
 | 
					    "test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js | tap-dot",
 | 
				
			||||||
 | 
					    "test-slow": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js -- --slow | tap-dot",
 | 
				
			||||||
    "cover": "c8 --skip-full -r html -r text supertape --no-check-assertions-count --format fail test/test.js"
 | 
					    "cover": "c8 --skip-full -r html -r text supertape --no-check-assertions-count --format fail test/test.js"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,5 +139,6 @@ I recommend developing in Visual Studio Code so that the JSDoc x TypeScript anno
 | 
				
			||||||
* (3) node-fetch@2: I like it and it does what I want.
 | 
					* (3) node-fetch@2: I like it and it does what I want.
 | 
				
			||||||
* (0) pngjs: Lottie stickers are converted to bitmaps with the vendored Rlottie WASM build, then the bitmaps are converted to PNG with pngjs.
 | 
					* (0) pngjs: Lottie stickers are converted to bitmaps with the vendored Rlottie WASM build, then the bitmaps are converted to PNG with pngjs.
 | 
				
			||||||
* (0) prettier-bytes: It does what I want and has no dependencies.
 | 
					* (0) prettier-bytes: It does what I want and has no dependencies.
 | 
				
			||||||
 | 
					* (51) sharp: Jimp has fewer dependencies, but sharp is faster.
 | 
				
			||||||
* (0) try-to-catch: Not strictly necessary, but it does what I want and has no dependencies.
 | 
					* (0) try-to-catch: Not strictly necessary, but it does what I want and has no dependencies.
 | 
				
			||||||
* (1) turndown: I need an HTML-to-Markdown converter and this one looked suitable enough. It has some bugs that I've worked around, so I might switch away from it later.
 | 
					* (1) turndown: I need an HTML-to-Markdown converter and this one looked suitable enough. It has some bugs that I've worked around, so I might switch away from it later.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue