Scenario generator for vore roleplay and story ideas.
https://scenario-generator.deliciousreya.net/responses
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
3.0 KiB
112 lines
3.0 KiB
import { collapseWhiteSpace } from 'collapse-white-space';
|
|
import { isSnowflake, type Snowflake } from 'discord-snowflake';
|
|
|
|
const VALID_URL_PATTERN = new URLPattern({
|
|
protocol: '(http(?:s)?|mailto)'
|
|
});
|
|
|
|
export function typeOf(data: unknown): string {
|
|
if (data === null) {
|
|
return 'null'
|
|
}
|
|
if (Array.isArray(data)) {
|
|
return 'array'
|
|
}
|
|
return typeof data
|
|
}
|
|
|
|
export function string(data: string|undefined, trim?: boolean): string {
|
|
if (typeof data !== 'string') {
|
|
throw Error(`expected string, but was ${typeOf(data)}`)
|
|
}
|
|
return collapseWhiteSpace(data, {
|
|
style: 'js',
|
|
trim: trim ?? true,
|
|
preserveLineEndings: false
|
|
})
|
|
}
|
|
|
|
export function discordSnowflake(data: string|Snowflake|undefined): Snowflake {
|
|
const text = string(data)
|
|
if (!isSnowflake(text)) {
|
|
throw Error(`expected Discord snowflake, but was ${typeOf(data)}`)
|
|
}
|
|
return text
|
|
}
|
|
|
|
export function substring(data: string|undefined): string {
|
|
const text = string(data, false)
|
|
if (text.length === 0) {
|
|
return '%';
|
|
}
|
|
return '%' + text.replaceAll('\\', '\\\\')
|
|
.replaceAll('_', '\\_')
|
|
.replaceAll('%', '\\%') + '%';
|
|
}
|
|
|
|
export function tableIdentifierOrId(data: string|undefined): string
|
|
export function tableIdentifierOrId(data: string|number|undefined): string|number
|
|
export function tableIdentifierOrId(data: string|number|undefined): string|number {
|
|
if (typeof data === 'number') {
|
|
return integer(data)
|
|
} else {
|
|
return string(data).toLowerCase()
|
|
}
|
|
}
|
|
|
|
export function tableIdentifierSubstring(data: string|undefined): string {
|
|
return substring(tableIdentifierOrId(data))
|
|
}
|
|
|
|
export function URL(data: string|undefined): string {
|
|
const url = string(data)
|
|
if (!VALID_URL_PATTERN.test(url)) {
|
|
throw Error('url must be a valid HTTP, HTTPS, or MAILTO URL');
|
|
}
|
|
return url
|
|
}
|
|
|
|
export function boolean(data: boolean|undefined): number {
|
|
if (typeof data !== 'boolean') {
|
|
throw Error(`expected boolean but was ${typeof(data)}`)
|
|
}
|
|
return data ? 1 : 0
|
|
}
|
|
|
|
export function integer(data: number|undefined): number {
|
|
const num = number(data)
|
|
if (!Number.isInteger(num)) {
|
|
throw Error(`expected integer but was ${data}`)
|
|
}
|
|
return num
|
|
}
|
|
|
|
export function number(data: number|undefined): number {
|
|
if (typeof data !== 'number') {
|
|
throw Error(`expected number but was ${typeof(data)}`)
|
|
}
|
|
return data
|
|
}
|
|
|
|
export function timestamp(data: number|undefined): number {
|
|
return integer(data)
|
|
}
|
|
|
|
export function jsonObject(data: object|undefined): string {
|
|
if (typeof data !== 'object' || typeOf(data) !== 'object') {
|
|
throw Error(`expected object but was ${typeof(data)}`)
|
|
}
|
|
return JSON.stringify(data)
|
|
}
|
|
|
|
export function jsonArray(data: (readonly unknown[])|undefined): string {
|
|
if (!Array.isArray(data)) {
|
|
throw Error(`expected object but was ${typeof(data)}`)
|
|
}
|
|
return JSON.stringify(data)
|
|
}
|
|
|
|
export function nullable<T extends (value: undefined) => string|number>(transformer: T): (value: Parameters<T>[0]|null) => ReturnType<T>|null {
|
|
return (value: Parameters<T>[0]|null): ReturnType<T>|null => value === null ? null : transformer(value) as ReturnType<T>
|
|
}
|
|
|
|
|