Utilities related to "Make it a Quote".
The ordinary, domain-agnostic pieces shared by makeitaquote,
@makeitaquote/voids and
@makeitaquote/miqx — an HTTP client, shared error
classes, input-validation primitives, a deprecation-warning helper, Discord mention/markdown/message
helpers, plain-text Markdown/MFM/Twitter-text normalization, and a Twitter v2 author-matching helper.
Domain-specific logic (how each package builds its own quote shape, its own API-specific error
subclasses, its own canvas rendering) stays in each package and is not part of this library.
npm install @makeitaquote/utilsEvery export is available from a subpath, so a consumer only pays for the dependencies it actually
uses — importing @makeitaquote/utils/http never pulls in markdown-it or mfm-js.
import { createClient, HTTPError, TimeoutError } from '@makeitaquote/utils/http'
const client = createClient({ timeout: 10_000, retry: 2 })
const response = await client.get('https://example.com/')
const buffer = await client.getBuffer('https://example.com/image.png')import { errorMessage, MiQError, ValidationError } from '@makeitaquote/utils/errors'
try {
throw new ValidationError('text is required', { field: 'text' })
} catch (cause) {
if (cause instanceof MiQError) console.error(errorMessage(cause))
}import {
avatarURL,
formatUsername,
globalName,
guildName,
resolveMentions,
stripDiscordMarkdown,
} from '@makeitaquote/utils/discord'
const text = resolveMentions(message.content, message)
const plain = stripDiscordMarkdown(text)
const name = guildName(message) ?? globalName(message) ?? formatUsername(message.author)
const avatar = message.member ? avatarURL(message.member) : avatarURL(message.author)import { stripMarkdown } from '@makeitaquote/utils/markdown'
import { stripMfm } from '@makeitaquote/utils/mfm'
stripMarkdown('**bold** text') // 'bold text'
stripMfm('$[jelly ぷりん]') // 'ぷりん'import { findTweetV2Author, stripTwitterText } from '@makeitaquote/utils/twitter'
const { data: tweet, includes } = await client.v2.singleTweet(id, {
expansions: ['author_id'],
})
const author = findTweetV2Author(tweet, includes)
stripTwitterText('𝗕𝗼𝗹𝗱 𝟭𝟮𝟯') // 'Bold 123' — X's "bold/italic" is Unicode, not markupimport { normalizeAvatarSource, normalizeString } from '@makeitaquote/utils/validation'
const text = normalizeString(input.text, 'text', 4000) // throws ValidationError if not a string, or too long
const avatar = normalizeAvatarSource(input.avatar, 'avatar') // string | URL | Uint8Array | nullimport { deprecate } from '@makeitaquote/utils'
if (options.stripDiscordMarkdown !== undefined) {
deprecate('MIQ001', 'stripDiscordMarkdown is deprecated, use markdown instead')
}The root @makeitaquote/utils entry point re-exports ./http and ./errors, plus deprecate()/
resetDeprecationsForTests(), for convenience — none of them pulls in a dependency the others don't
already share.
- Node.js >= 18
http— a sharedofetchwrapper (createClient,HTTPError,TimeoutError) withget/post/head/getBuffer, retry, timeout andthrowHttpErrorshandling.errors— theMiQError/ValidationErrorbase classes every package's own errors extend, pluserrorMessage(). Each package keeps its own additional error subclasses (RenderError,VoidsApiError,MiQXApiError, …) locally.- root (
deprecate) —deprecate()/resetDeprecationsForTests(), a warn-once-per-processDeprecationWarninghelper (process.emitWarningunder the hood) for retiring an option in favor of a new one, the waystripDiscordMarkdown: trueis deprecated in favor ofmarkdown: false. validation— the input-normalization primitives every package's ownquote.tsvalidation independently re-implemented:assertString(),assertBoolean(),assertMaxLength(),assertNonEmpty(),normalizeString()(the two asserts combined) andnormalizeAvatarSource()(a URL string /URL/ bytes /null, optionally also aBlob).discord—resolveMentions()(expands<@id>,<#id>,<t:…>and the rest of Discord's raw message tokens),stripDiscordMarkdown(), and the message-reading helpersavatarURL(),formatUsername(),guildName()andglobalName().markdown/mfm—stripMarkdown()(CommonMark + GFM extras) andstripMfm()(Misskey Flavoured Markup) down to plain text, plusresolveNoteText()(mfm), which picks a Misskey note'stextorcwto quote.twitter—findTweetV2Author(), theincludes.userslookup every package's ownfromTwitterApiV2Tweet()adapter shares, andstripTwitterText(), which normalizes "Twitter bold/italic" (Unicode Mathematical Alphanumeric Symbols) back to plain ASCII.
Not in scope, and intentionally kept local to each package: how a quote's own fields are built
(QuoteData differs per package), each package's client class, and anything tied to
makeitaquote's canvas rendering pipeline — including its own stripMarkdown/stripMfm/
stripDiscordMarkdown, which return styled runs for the renderer rather than plain strings, and are
a different implementation from the ones this package ships.
Contributions are welcome! See CONTRIBUTING.md for details.
Distributed under the MIT License.