From 995b28dd41be340e8a3b0b0ec20070a4410cb6f9 Mon Sep 17 00:00:00 2001 From: Cristobal Contreras Rubio Date: Fri, 28 Jun 2024 20:03:12 +0200 Subject: [PATCH 1/4] All code refactored to use new API and hardening of Validators --- src/constants.ts | 8 +- src/errors.ts | 14 +- src/image.ts | 148 ++++++++----- src/imageSharp.ts | 101 +++++++++ src/index.ts | 6 +- src/output.ts | 26 --- src/schemas.ts | 268 +++++++++-------------- src/splitea.ts | 134 ++++++++++-- src/tiles.ts | 83 -------- src/types.ts | 100 ++++----- src/utils.ts | 31 +-- tests/chess_horizontal.png | Bin 0 -> 2491 bytes tests/chess_vertical.png | Bin 0 -> 7313 bytes tests/image.test.ts | 205 ++++++++++-------- tests/output.test.ts | 63 ------ tests/schemas.test.ts | 421 ++++++++++++------------------------- tests/splitea.test.ts | 244 +++++++++++++++------ tests/tiles.test.ts | 92 -------- tests/utils.test.ts | 8 +- tsconfig.json | 44 ++-- 20 files changed, 946 insertions(+), 1050 deletions(-) create mode 100644 src/imageSharp.ts delete mode 100644 src/output.ts delete mode 100644 src/tiles.ts create mode 100644 tests/chess_horizontal.png create mode 100644 tests/chess_vertical.png delete mode 100644 tests/output.test.ts delete mode 100644 tests/tiles.test.ts diff --git a/src/constants.ts b/src/constants.ts index 7eb91b1..4e84f27 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,8 @@ -export const MODES = ['horizontal', 'vertical', 'grid'] as const +// export const MODES = ['horizontal', 'vertical', 'grid'] as const + +export const EXTENSIONS = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'tiff'] as const + +export const UNIQUE_REQUIREMENTS = ['all', 'distance', 'difference'] as const export const MAX_DISTANCE = 0.01 -export const MAX_DIFFERENCE = 0.01 \ No newline at end of file +export const MAX_DIFFERENCE = 0.01 diff --git a/src/errors.ts b/src/errors.ts index 9e02c4d..0ab3abe 100755 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,19 +1,19 @@ -import { ValiError } from "valibot" +import { ValiError } from 'valibot' export class SpliteaError extends Error { - constructor(msg: string) { + constructor (msg: string) { super(msg) this.name = 'SpliteaError' } } -export const ThrowSpliteaError = (error: any, msg: string): SpliteaError => { - if (error instanceof SpliteaError) { return error } +export const ThrowSpliteaError = (error: any, msg: string): never => { + if (error instanceof SpliteaError) { throw error } if (error instanceof ValiError) { - const err = error.issues[0].message + const err: string = error.issues[0].message console.error(`Error: ${msg} -> ${err}`) - return new SpliteaError(msg + '\n' + err) + throw new SpliteaError(msg + '\n' + err) } console.error(error) - return new SpliteaError(msg + '\n' + String(error)) + throw new SpliteaError(msg + '\n' + String(error)) } diff --git a/src/image.ts b/src/image.ts index dd9a9b1..afe9a2f 100644 --- a/src/image.ts +++ b/src/image.ts @@ -1,45 +1,31 @@ import * as Path from 'node:path' -import * as v from 'valibot' -import Jimp from "jimp" -import { SpliteaError, ThrowSpliteaError } from "./errors" -import { Image, Size, TileCoordinates, Unique } from "./types" -import { ImageSchema, SizeSchema } from './schemas' +import Jimp from 'jimp' +import { SpliteaError, ThrowSpliteaError } from './errors' +import type { CropData, Image, Natural, Size, StoreOptions, UniqueImagesOptions, WriteOptions } from './types' -export const getSize = (image: Jimp): Size => v.parse(SizeSchema, { width: image.bitmap.width, height: image.bitmap.height}) - -export const getImage = async (image: Image): Promise<[Jimp, Size]> => { +// @ts-expect-error +export const getImage = async (image: Image): Promise => { try { - const i = v.parse(ImageSchema, image) - // @ts-ignore - const img = await Jimp.read(i) - const size: Size = getSize(img) - return [img, size] + // @ts-expect-error + return await Jimp.read(image) } catch (error) { - throw ThrowSpliteaError(error, `Error reading image ${image}`) + ThrowSpliteaError(error, `Error reading image ${image.toString()}`) } } -const getSplitImage = (image: Jimp, size: Size, tileCoordinates: TileCoordinates): Jimp => { - try { - const { width, height } = size - const { x, y, width: w, height: h } = tileCoordinates - if (x === 0 && w === width && y === 0 && h === height) return image - if ((x + w) > width) throw new SpliteaError(`Can't have an image of ${w}x${h}px from (${x}, ${y}) because max x value is ${width - 1}`) - if ((y + h) > height) throw new SpliteaError(`Can't have an image of ${w}x${h}px from (${x}, ${y}) because max y value is ${height - 1}`) - return image.clone().crop(x, y, w, h) - } catch (error) { - throw ThrowSpliteaError(error, 'Problem spliting image') - } -} +export const getSize = (image: Jimp): Size => ({ width: image.bitmap.width, height: image.bitmap.height }) -export const areEqualImages = (img1: Jimp, img2: Jimp, unique: Unique): boolean => { - const { requirement, distance, difference } = unique +const areEqualImages = (img1: Jimp, img2: Jimp, options: UniqueImagesOptions): boolean => { + const { requirement, distance, difference } = options try { + // Distance + if (requirement === 'distance') { return Jimp.distance(img1, img2) <= distance } + // Difference + if (requirement === 'difference') { return Jimp.diff(img1, img2).percent <= difference } + // Distance + Difference const dist = Jimp.distance(img1, img2) const diff = Jimp.diff(img1, img2).percent - return ( requirement === 'both') - ? (dist < distance && diff < difference) - : (dist < distance || diff < difference) + return (dist <= distance && diff <= difference) } catch (error) { console.log(error) ThrowSpliteaError(error, 'Error comparing images') @@ -47,50 +33,114 @@ export const areEqualImages = (img1: Jimp, img2: Jimp, unique: Unique): boolean return false } -export const getUniqueImages = (images: Jimp[], unique: Unique): Jimp[] => { +const getUniqueTiles = (images: Jimp[], options: UniqueImagesOptions): Jimp[] => { if (images.length < 2) return images let array = [...images] - let uniques: Jimp[] = [] + const uniques: Jimp[] = [] do { const image: Jimp = array.shift() as Jimp uniques.push(image) - array = array.filter(elem => !areEqualImages(image, elem, unique)) - } while (array.length > 0) + array = array.filter(elem => !areEqualImages(image, elem, options)) + } while (array.length > 0) return uniques } -export const getSplitImages = (image: Jimp, size: Size, tilesCoordinate: TileCoordinates[], unique: Unique | undefined): Jimp[] => { - const images = tilesCoordinate.map(tileCoordinates => getSplitImage(image, size, tileCoordinates)) - if (unique && images.length > 1) { return getUniqueImages(images, unique) } - return images +const getTile = (image: Jimp, { x, y, w, h }: CropData): Jimp => image.clone().crop(x, y, w, h) + +export const getHorizontalTiles = (image: Jimp, size: Size, width: Natural): Jimp[] => { + if (size.width === width) return [image] + const tiles = [] + const y = 0 + const w = width + const h = size.height + for (let x = 0; x < size.width; x += width) { + try { + tiles.push(getTile(image, { x, y, w, h })) + } catch (error) { + ThrowSpliteaError(error, 'Cannot get Horizontal tiles') + } + } + return tiles +} + +export const getUniqueHorizontalTiles = (image: Jimp, size: Size, width: Natural, options: UniqueImagesOptions): Jimp[] => { + const tiles = getHorizontalTiles(image, size, width) + return getUniqueTiles(tiles, options) } -const writeImage = async (image: Jimp, path: string, name: string, index: number | string, extension: string): Promise => { - const filename = `${name}_${(index).toString().padStart(3, '0')}.${extension}` +export const getVerticalTiles = (image: Jimp, size: Size, height: Natural): Jimp[] => { + if (size.height === height) return [image] + const tiles = [] + const x = 0 + const w = size.width + const h = height + for (let y = 0; y < size.height; y += height) { + try { + tiles.push(getTile(image, { x, y, w, h })) + } catch (error) { + ThrowSpliteaError(error, 'Cannot get Vertical tiles') + } + } + return tiles +} + +export const getUniqueVerticalTiles = (image: Jimp, size: Size, height: Natural, options: UniqueImagesOptions): Jimp[] => { + const tiles = getVerticalTiles(image, size, height) + return getUniqueTiles(tiles, options) +} + +export const getGridTiles = (image: Jimp, size: Size, width: Natural, height: Natural): Jimp[] => { + if (size.width === width) return [image] + const tiles = [] + const w = width + const h = height + for (let x = 0; x < size.width; x += width) { + for (let y = 0; y < size.height; y += height) { + try { + tiles.push(getTile(image, { x, y, w, h })) + } catch (error) { + ThrowSpliteaError(error, 'Cannot get Grid tiles') + } + } + } + return tiles +} + +export const getUniqueGridTiles = (image: Jimp, size: Size, width: Natural, height: Natural, options: UniqueImagesOptions): Jimp[] => { + const tiles = getGridTiles(image, size, width, height) + return getUniqueTiles(tiles, options) +} + +const writeImage = async (image: Jimp, options: WriteOptions): Promise => { + const { path, filename: name, extension, index, pad } = options + const filename = `${name}_${(index).toString().padStart(pad, '0')}.${extension}` const file = Path.join(path, filename) await image.writeAsync(file) return file } -export const writeImages = async (images: Jimp[], path: string, name: string, extension: string): Promise => { +export const writeImages = async (images: Jimp[], storeOptions: StoreOptions): Promise => { if (images.length < 1) throw new SpliteaError('Impossible to write no images') + const { path, filename, extension } = storeOptions + const pad = Math.floor(Math.log10(images.length)) + 1 if (images.length === 1) { - const filenames = await writeImage(images[0], path, name, '', extension) + const filenames = await writeImage(images[0], { path, filename, extension, index: '', pad }) return [filenames] } - return Promise.all( + return await Promise.all( images.map( - async (image: Jimp, index: number) => await writeImage(image, path, name, index, extension) + async (image: Jimp, index: number) => await writeImage(image, { path, filename, extension, index: index.toString(), pad }) ) ) } +// @ts-expect-error export const getBufferImages = async (images: Jimp[]): Promise => { try { - const buffers = await Promise.all(images.map(async (image: Jimp) => await image.getBufferAsync(image.getMIME()))) - return buffers + return await Promise.all( + images.map(async (image: Jimp) => await image.getBufferAsync(image.getMIME())) + ) } catch (error) { ThrowSpliteaError(error, 'Impossible to get buffer from images') } - return Promise.resolve([]) -} \ No newline at end of file +} diff --git a/src/imageSharp.ts b/src/imageSharp.ts new file mode 100644 index 0000000..536db77 --- /dev/null +++ b/src/imageSharp.ts @@ -0,0 +1,101 @@ +// phash with sharp -> https://www.npmjs.com/package/sharp-phash +// phash -> https://www.npmjs.com/search?q=phash + +// import * as Path from 'node:path' +// import Jimp from "jimp" +// import sharp, { Sharp } from 'sharp' +// import { SpliteaError, ThrowSpliteaError } from "./errors" +// import { Image, ImageSchema, Size, SizeSchema, TileCoordinates } from "./types" + +// export const getSize = async (image: Sharp): Promise => { +// const { width, height } = await image.metadata() +// return SizeSchema.parse({ width, height }) +// } + +// export const getImage = async (image: Image): Promise<[Sharp, Size]> => { +// try { +// ImageSchema.parse(image) +// const img = sharp(image) +// const { width, height } = await img.metadata() +// const size: Size = SizeSchema.parse({ width, height }) +// return [img, size] +// } catch (error) { +// throw ThrowSpliteaError(error, `Error reading image ${image}`) +// } +// } + +// const getSplitImage = (image: Sharp, size: Size, tileCoordinates: TileCoordinates): Sharp => { +// try { +// const { width, height } = size +// const { x, y, width: w, height: h } = tileCoordinates +// if (x === 0 && w === width && y === 0 && h === height) return image +// if ((x + w) > width) throw new SpliteaError(`Can't have an image of ${w}x${h}px from (${x}, ${y}) because max x value is ${width - 1}`) +// if ((y + h) > height) throw new SpliteaError(`Can't have an image of ${w}x${h}px from (${x}, ${y}) because max y value is ${height - 1}`) +// return image.clone().extract({ left: x, top: y, width: w, height: h }) +// } catch (error) { +// throw ThrowSpliteaError(error, 'Problem spliting image') +// } +// } + +// export const getSplitImages = (image: Sharp, size: Size, tilesCoordinate: TileCoordinates[], unique: boolean = false): Sharp[] => { +// const images = tilesCoordinate.map(tileCoordinates => getSplitImage(image, size, tileCoordinates)) +// if (unique && images.length > 1) { return getUniqueImages(images) } +// return images +// } + +// export const areEqualImages = (img1: Sharp, img2: Sharp): boolean => { +// try { +// const distance = Jimp.distance(img1, img2) +// const diff = Jimp.diff(img1, img2) +// if (distance < 0.15 && diff.percent < 0.15) { +// console.debug(`distance = ${distance} | diff = ${diff.percent}`) +// return true +// } +// } catch (error) { +// console.log(error) +// ThrowSpliteaError(error, 'Error comparing images') +// } +// return false +// } + +// export const getUniqueImages = (images: Sharp[]): Sharp[] => { +// let array = [...images] +// let image: Sharp +// let uniqueArray: Sharp[] = [] +// while (array.length) { +// image = array[0] +// uniqueArray.push(image) +// array = array.filter(elem => !areEqualImages(image, elem)) +// } +// return uniqueArray +// } + +// const writeImage = async (image: Jimp, path: string, name: string, index: number | string, extension: string): Promise => { +// const filename = `${name}_${index}_${new Date().getTime()}.${extension}` +// const file = Path.join(path, filename) +// await image.writeAsync(file) +// return file +// } + +// export const writeImages = async (images: Jimp[], path: string, name: string, extension: string): Promise => { +// if (images.length < 1) throw new SpliteaError('Impossible to write no images') +// if (images.length === 1) { +// const filenames = await writeImage(images[0], path, name, '', extension) +// return [filenames] +// } +// return Promise.all( +// images.map( +// async (image: Jimp, index: number) => await writeImage(image, path, name, index, extension) +// ) +// ) +// } + +// export const getBufferImages = async (images: Jimp[]): Promise => { +// try { +// const buffers = await Promise.all(images.map(async (image: Jimp) => await image.getBufferAsync(image.getMIME()))) +// return buffers +// } catch (error) { +// ThrowSpliteaError(error, 'Impossible to get buffer from images') +// } +// return Promise.resolve([]) +// } diff --git a/src/index.ts b/src/index.ts index cc5d26d..33900a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ -import Splitea from './splitea' - -export { Splitea } +export * from './splitea' +export * from './constants' +export * from './types' diff --git a/src/output.ts b/src/output.ts deleted file mode 100644 index feff865..0000000 --- a/src/output.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as v from 'valibot' -import { Image, Output } from "./types" -import Jimp from 'jimp' -import { getBufferImages, writeImages } from './image' -import { ExtensionSchema, OutputSchema } from './schemas' - - -export const checkOutput = (output: Output): void => { - v.parse(OutputSchema, output) -} - -export const getOutput = async (images: Jimp[], output: Output): Promise => { - if (images.length === 0) return [] - const { response, store } = output - // Storage if neccessary - if (store) { - const { path, name, extension: ext } = store - const extension = ext ?? v.parse(ExtensionSchema, images[0].getExtension()) - const paths = await writeImages(images, path, name, extension) - // Response are paths - if (response === 'path') return paths - } - // Luego devolver si es buffer o path - const buffers = await getBufferImages(images) - return buffers -} \ No newline at end of file diff --git a/src/schemas.ts b/src/schemas.ts index 61d3a3f..ddc3027 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -1,147 +1,32 @@ import fs from 'node:fs' import * as v from 'valibot' -import Jimp from 'jimp' -import { MAX_DIFFERENCE, MAX_DISTANCE, MODES } from './constants' -import { greaterThanZero, invalidFilename, isSubmultiple, xor } from './utils' +import { ValibotValidator } from '@schemasjs/validator' +import { UnsignedIntegerSchema as ValibotNaturalSchema, type UnsignedInteger, Float32Schema as ValibotFloat32Schema } from '@schemasjs/valibot-numbers' +import { EXTENSIONS, MAX_DIFFERENCE, MAX_DISTANCE, UNIQUE_REQUIREMENTS } from './constants' +import { validFilename } from './utils' -export const BooleanSchema = v.boolean() -export const StringSchema = v.string() -export const NaturalSchema = v.number([v.integer(), v.minValue(0)]) +// PRIMITIVES --------------------------------------------------------------------------------------------------------- +const ValibotBooleanSchema = v.boolean() -// Image ---------------------------------------------------------------------- -export const BufferSchema = v.instance(Buffer) -export const JimpSchema = v.instance(Jimp) +export const NaturalSchema = ValibotValidator(ValibotNaturalSchema) -export const ImageExistsSchema = v.string([ - v.custom((file) => fs.existsSync(file), 'This image does not exists') -]) - -export const ImageSchema = v.union([ImageExistsSchema, BufferSchema]) - -export const SizeSchema = v.object({ - width: NaturalSchema, - height: NaturalSchema, -}) -/** Tiles --------------------------------------------------------------------- - * 1. Slice Mode -> mode => "grid" | "vertical" | "horizontal" - * 2. Options for the slices - * 2.1 Grid tiles -> rows + columns | width + height - * 2.2 Vertical tiles -> rows | height - * 2.3 Horizontal tiles -> columns | width - * 3. Non-repeated tiles -> unique => false (default) | true -*/ -export const ModeSchema = v.picklist(MODES) - -export const UniqueSchema = v.object({ - // enable: v.fallback(BooleanSchema, false), - distance: v.fallback(v.number([v.minValue(0), v.maxValue(1)]), MAX_DISTANCE), - difference: v.fallback(v.number([v.minValue(0), v.maxValue(1)]), MAX_DIFFERENCE), - requirement: v.fallback(v.picklist(['one', 'both']), 'both') -}) - -export const TilesSchema = v.object({ - mode: ModeSchema, - rows: v.optional(NaturalSchema), - columns: v.optional(NaturalSchema), - width: v.optional(NaturalSchema), - height: v.optional(NaturalSchema), - unique: v.optional(UniqueSchema), -}) - -export const HorizontalTilesSchema = v.object( - { - columns: v.fallback(NaturalSchema, 0), - width: v.fallback(NaturalSchema, 0), - size: SizeSchema - }, - [ - v.custom(({ columns, width }) => [columns, width].some(greaterThanZero), 'Provide columns or width (px)'), - v.custom(({ columns, width }) => xor(Boolean(columns), Boolean(width)), 'Provide columns or width (px), not both'), - v.custom(({ columns, width, size }) => { - if (columns > 0) return isSubmultiple(size.width, columns) - if (width > 0) return isSubmultiple(size.width, width) - return false - }, 'Provided columns or width has to be a submultiple of image width'), - ] -) - -export const VerticalTilesSchema = v.object( - { - rows: v.fallback(NaturalSchema, 0), - height: v.fallback(NaturalSchema, 0), - size: SizeSchema - }, - [ - v.custom(({ rows, height }) => [rows, height].some(greaterThanZero), 'Provide rows or height (px)'), - v.custom(({ rows, height }) => xor(Boolean(rows), Boolean(height)), 'Provide rows or height (px), not both'), - v.custom(({ rows, height, size }) => { - if (rows > 0) return isSubmultiple(size.height, rows) - if (height > 0) return isSubmultiple(size.height, height) - return false - }, 'Provided rows or height has to be a submultiple of image height'), - ] -) - -export const GridTilesSchema = v.object( - { - columns: v.fallback(NaturalSchema, 0), - rows: v.fallback(NaturalSchema, 0), - width: v.fallback(NaturalSchema, 0), - height: v.fallback(NaturalSchema, 0), - size: SizeSchema - }, - [ - v.custom(({ columns, rows, width, height }) => [columns, rows, width, height].some(greaterThanZero), 'Provide columns-rows or height-width (px)'), - v.custom(({ columns, rows, width, height }) => xor( [columns, rows].some(greaterThanZero), [width, height].some(greaterThanZero) ) , 'Provide columns-rows or height-width (px), not both'), - v.custom(({ columns, rows, size }) => { - if ([columns, rows].every(greaterThanZero)) return isSubmultiple(size.width, columns) && isSubmultiple(size.height, rows) - return true - }, 'Provided columns-rows have to be a submultiple of image width-height'), - v.custom(({ width, height, size }) => { - if ([width, height].every(greaterThanZero)) return isSubmultiple(size.width, width) && isSubmultiple(size.height, height) - return true - }, 'Provided width-height have to be a submultiple of image width-height'), - // v.custom(({ columns, rows, width, height, size }) => { - // if ([columns, rows].every(greaterThanZero)) return isSubmultiple(size.width, columns) && isSubmultiple(size.height, rows) - // if ([width, height].every(greaterThanZero)) return isSubmultiple(size.width, width) && isSubmultiple(size.height, height) - // return false - // }, 'Provided columns-rows or width-height have to be a submultiple of image width-height'), - ] -) - -export const TilesCutSchema = v.object( - { - tileWidth: NaturalSchema, - tileHeight: NaturalSchema, - imageWidth: NaturalSchema, - imageHeight: NaturalSchema, - }, - [ - v.custom(({ tileWidth, imageWidth }) => tileWidth <= imageWidth, 'tileWidth cannot be bigger than imageWidth'), - v.custom(({ tileWidth, imageWidth }) => isSubmultiple(imageWidth, tileWidth), 'tileWidth has to be a submultiple of imageWidth'), - v.custom(({ tileHeight, imageHeight }) => tileHeight <= imageHeight, 'tileHeight cannot be bigger than imageHeight'), - v.custom(({ tileHeight, imageHeight }) => isSubmultiple(imageHeight, tileHeight), 'tileHeight has to be a submultiple of imageHeight'), - ] +const ValibotBufferSchema = v.instance(Buffer) +// IMAGE -------------------------------------------------------------------------------------------------------------- +const ValibotImageExistsSchema = v.pipe( + v.string(), + v.check((file: string) => fs.existsSync(file), 'This image does not exists') ) -export const TileCoordinatesSchema = v.object({ - x: NaturalSchema, - y: NaturalSchema, - width: NaturalSchema, - height: NaturalSchema -}) -/** Output -------------------------------------------------------------------- - * 1. response = data to return -> data => "buffer" | "path" - * 2. If response = "path" or you want to store the slices it needs to provide path + name (extension is optional) - * 2.1 Local path to store slices -> path - * 2.2 Name preffix to slices -> name - * 2.3 Extension to store tiles -> extension => "jpg" | "png" | "bmp" | "gif" | "tiff" -**/ -export const ResponseSchema = v.picklist(['buffer', 'path']) +const ValibotImageSchema = v.union([ValibotImageExistsSchema, ValibotBufferSchema]) +export const ImageSchema = ValibotValidator>(ValibotImageSchema) +// OPTIONS ------------------------------------------------------------------------------------------------------------ +const ValibotResponseSchema = v.picklist(['buffer', 'file']) +export const ResponseSchema = ValibotValidator>(ValibotResponseSchema) -export const PathSchema = v.string([ +const ValibotPathSchema = v.pipe( + v.string(), // v.custom((input: string) => !invalidFilename(input), 'Invalid name for a path'), - v.custom( + v.check( (input: string) => { try { if (!fs.existsSync(input)) { @@ -152,9 +37,9 @@ export const PathSchema = v.string([ return false } }, - 'Cannot create the path provided' + 'Cannot create the provided path' ), - v.custom( + v.check( (input: string) => { try { fs.accessSync(input, fs.constants.W_OK) @@ -163,39 +48,88 @@ export const PathSchema = v.string([ return false } }, - 'path provided doesn\'t have write permissions' + 'provided path doesn\'t have write permissions' ) -]) +) +export const PathSchema = ValibotValidator>(ValibotPathSchema) -export const FilenameSchema = v.string([ - v.custom((input: string) => !invalidFilename(input), 'Invalid filename'), -]) +const ValibotFilenameSchema = v.pipe( + v.string(), + v.check((input: string) => validFilename(input), 'Invalid filename') +) +export const FilenameSchema = ValibotValidator>(ValibotFilenameSchema) -export const ExtensionSchema = v.picklist([ - 'jpg', 'jpeg', - 'png', 'bmp', - 'gif', 'tiff' -]) +// const ValibotExtensionSchema = v.picklist(EXTENSIONS) +const ValibotExtensionSchema = v.pipe( + v.string(), + v.check(input => EXTENSIONS.filter(ext => ext === input.toLowerCase()).length > 0), + v.transform(input => input.toLowerCase()) +) +export const ExtensionSchema = ValibotValidator>(ValibotExtensionSchema) + +const ValibotUniqueRequirementSchema = v.picklist(UNIQUE_REQUIREMENTS) +export const UniqueRequirementSchema = ValibotValidator>(ValibotUniqueRequirementSchema) + +const ValibotDistanceSchema = v.pipe(ValibotFloat32Schema, v.minValue(0), v.maxValue(1)) +export const DistanceSchema = ValibotValidator>(ValibotDistanceSchema) + +const ValibotDifferenceSchema = ValibotDistanceSchema +export const DifferenceSchema = ValibotValidator>(ValibotDifferenceSchema) + +const ValibotOptionsSchema = v.pipe( + v.object({ + response: v.optional(ValibotResponseSchema, 'buffer'), + path: v.optional(ValibotPathSchema), + filename: v.optional(ValibotFilenameSchema, 'tile'), + extension: v.optional(ValibotExtensionSchema), + unique: v.optional(ValibotBooleanSchema, false), + uniqueRequirement: v.optional(ValibotUniqueRequirementSchema, 'all'), + distance: v.optional(ValibotDistanceSchema, MAX_DISTANCE), + difference: v.optional(ValibotDifferenceSchema, MAX_DIFFERENCE) + }), + v.forward( + v.partialCheck( + [['response'], ['path']], + ({ response, path }) => (response === 'file') ? path !== undefined : true, + 'If response is "file" a valid "path" has to be provided'), + ['path'] + ) +) +export const OptionsSchema = ValibotValidator>(ValibotOptionsSchema) +// HORIZONTAL --------------------------------------------------------------------------------------------------------- +const ValibotColumnsWidthSchema = v.pipe( + v.object({ + columns: v.optional(ValibotNaturalSchema), + width: v.optional(ValibotNaturalSchema) + }), + v.check( + ({ columns, width }) => !(columns !== undefined && width !== undefined) && !(columns === undefined && width === undefined), + 'columns or width has to be provided, not both' + ) +) -export const StoreSchema = v.object({ - path: PathSchema, - name: FilenameSchema, - extension: v.optional(ExtensionSchema) -}) +const ValibotHorizontalOptionsSchema = v.intersect([ValibotColumnsWidthSchema, ValibotOptionsSchema]) +export const HorizontalOptionsSchema = ValibotValidator>(ValibotHorizontalOptionsSchema) +// VERTICAL ----------------------------------------------------------------------------------------------------------- +const ValibotRowsHeightSchema = v.pipe( + v.object({ + rows: v.optional(ValibotNaturalSchema), + height: v.optional(ValibotNaturalSchema) + }), + v.check( + ({ rows, height }) => !(rows !== undefined && height !== undefined) && !(rows === undefined && height === undefined), + 'rows or width has to be provided, not both' + ) +) -export const OutputSchema = v.object( - { - response: v.fallback(ResponseSchema, 'buffer'), - store: v.optional(StoreSchema) - }, - [ - v.custom( - ({ response, store }) => (response === 'path') ? store !== undefined : true - , 'To response with path, store argument has to be passed' - ), - v.custom( - ({ store }) => (store !== undefined) ? v.safeParse(StoreSchema, store).success : true - , 'Invalid store property' - ), - ] +const ValibotVerticalOptionsSchema = v.intersect([ValibotRowsHeightSchema, ValibotOptionsSchema]) +export const VerticalOptionsSchema = ValibotValidator>(ValibotVerticalOptionsSchema) +// GRID --------------------------------------------------------------------------------------------------------------- +const ValibotGridOptionsSchema = v.pipe( + v.intersect([ValibotColumnsWidthSchema, ValibotRowsHeightSchema, ValibotOptionsSchema]), + v.check( + input => (input.rows !== undefined && input.columns !== undefined) || (input.width !== undefined && input.height !== undefined), + 'It must be provided the pair rows-columns or width-height' + ) ) +export const GridOptionsSchema = ValibotValidator>(ValibotGridOptionsSchema) diff --git a/src/splitea.ts b/src/splitea.ts index 591ba88..78f1953 100755 --- a/src/splitea.ts +++ b/src/splitea.ts @@ -1,20 +1,120 @@ -import { Tiles, Output, Image } from './types' -import { getImage, getSplitImages } from './image' -import { checkTiles, getTilesCoordinates } from './tiles' -import { checkOutput, getOutput } from './output' +import type { Difference, Distance, Filename, GridOptions, HorizontalOptions, Image, Output, StoreOptions, UniqueImagesOptions, UniqueRequirement, VerticalOptions } from './types' +import { getBufferImages, getGridTiles, getHorizontalTiles, getImage, getSize, getUniqueGridTiles, getUniqueHorizontalTiles, getUniqueVerticalTiles, getVerticalTiles, writeImages } from './image' +import { GridOptionsSchema, HorizontalOptionsSchema, ImageSchema, VerticalOptionsSchema } from './schemas' +import { isSubmultiple } from './utils' +import { SpliteaError } from './errors' -const Splitea = async (image: Image, tiles: Tiles, output: Output): Promise => { - // 1. Check Image + Get the image - const [img, size] = await getImage(image) - // 2. Check arguments -> Tiles, Outputs - checkTiles(tiles, size) - checkOutput(output) - // 3. everything is fine so get tiles and output - const coordinates = getTilesCoordinates(size, tiles) - const newTiles = getSplitImages(img, size, coordinates, tiles?.unique) - const newOutput = await getOutput(newTiles, output) - // 4. Return solution - return newOutput +export const horizontalTiles = async (image: Image, options: HorizontalOptions): Promise => { + // 1. Check Image + Get the image + size + const img = await getImage(ImageSchema.parse(image)) + const size = getSize(img) + // 2. Check Options + const opt = HorizontalOptionsSchema.parse(options) + if (opt.columns !== undefined && !isSubmultiple(size.width, opt.columns)) { + throw new SpliteaError(`columns (${opt.columns}) is not a submultiple of the image width (${size.width} px)`) + } + if (opt.width !== undefined && !isSubmultiple(size.width, opt.width)) { + throw new SpliteaError(`width (${opt.width} px) is not a submultiple of the image width (${size.width} px)`) + } + // 3. Get the tiles + const tileWidth = (opt.columns !== undefined) ? size.width / opt.columns : opt.width as number + const uniqueOptions = { + requirement: opt.uniqueRequirement as UniqueRequirement, + distance: opt.distance as Distance, + difference: opt.difference as Difference + } satisfies UniqueImagesOptions + const tiles = (opt.unique as boolean) + ? getUniqueHorizontalTiles(img, size, tileWidth, uniqueOptions) + : getHorizontalTiles(img, size, tileWidth) + // 4. Not store -> return Buffer[] + if (opt.path === undefined) { return await getBufferImages(tiles) } + // 5. Store tiles + const storeOptions = { + path: opt.path, + filename: opt.filename as Filename, + extension: opt.extension ?? img.getExtension() + } satisfies StoreOptions + const files = await writeImages(tiles, storeOptions) + // 6. Return buffer[] | string[] + if (opt.response === 'file') return files + return await getBufferImages(tiles) } -export default Splitea \ No newline at end of file +export const verticalTiles = async (image: Image, options: VerticalOptions): Promise => { + // 1. Check Image + Get the image + size + const img = await getImage(ImageSchema.parse(image)) + const size = getSize(img) + // 2. Check Options + const opt = VerticalOptionsSchema.parse(options) + if (opt.rows !== undefined && !isSubmultiple(size.height, opt.rows)) { + throw new SpliteaError(`rows (${opt.rows}) is not a submultiple of the image height (${size.height} px)`) + } + if (opt.height !== undefined && !isSubmultiple(size.height, opt.height)) { + throw new SpliteaError(`height (${opt.height} px) is not a submultiple of the image height (${size.height} px)`) + } + // 3. Get the tiles + const tileHeight = (opt.rows !== undefined) ? size.height / opt.rows : opt.height as number + const uniqueOptions = { + requirement: opt.uniqueRequirement as UniqueRequirement, + distance: opt.distance as Distance, + difference: opt.difference as Difference + } satisfies UniqueImagesOptions + const tiles = (opt.unique as boolean) + ? getUniqueVerticalTiles(img, size, tileHeight, uniqueOptions) + : getVerticalTiles(img, size, tileHeight) + // 4. Not store -> return Buffer[] + if (opt.path === undefined) { return await getBufferImages(tiles) } + // 5. Store tiles + const storeOptions = { + path: opt.path, + filename: opt.filename as Filename, + extension: opt.extension ?? img.getExtension() + } satisfies StoreOptions + const files = await writeImages(tiles, storeOptions) + // 6. Return buffer[] | string[] + if (opt.response === 'file') return files + return await getBufferImages(tiles) +} + +export const gridTiles = async (image: Image, options: GridOptions): Promise => { + // 1. Check Image + Get the image + size + const img = await getImage(ImageSchema.parse(image)) + const size = getSize(img) + // 2. Check Options + const opt = GridOptionsSchema.parse(options) + if (opt.columns !== undefined && !isSubmultiple(size.width, opt.columns)) { + throw new SpliteaError(`columns (${opt.columns}) is not a submultiple of the image width (${size.width} px)`) + } + if (opt.rows !== undefined && !isSubmultiple(size.height, opt.rows)) { + throw new SpliteaError(`rows (${opt.rows}) is not a submultiple of the image height (${size.height} px)`) + } + if (opt.width !== undefined && !isSubmultiple(size.width, opt.width)) { + throw new SpliteaError(`width (${opt.width} px) is not a submultiple of the image width (${size.width} px)`) + } + if (opt.height !== undefined && !isSubmultiple(size.height, opt.height)) { + throw new SpliteaError(`height (${opt.height} px) is not a submultiple of the image height (${size.height} px)`) + } + // 3. Get the tiles + const tileWidth = (opt.columns !== undefined) ? size.width / opt.columns : opt.width as number + const tileHeight = (opt.rows !== undefined) ? size.height / opt.rows : opt.height as number + const uniqueOptions = { + requirement: opt.uniqueRequirement as UniqueRequirement, + distance: opt.distance as Distance, + difference: opt.difference as Difference + } satisfies UniqueImagesOptions + const tiles = (opt.unique as boolean) + ? getUniqueGridTiles(img, size, tileWidth, tileHeight, uniqueOptions) + : getGridTiles(img, size, tileWidth, tileHeight) + // 4. Not store -> return Buffer[] + if (opt.path === undefined) { return await getBufferImages(tiles) } + // 5. Store tiles + const storeOptions = { + path: opt.path, + filename: opt.filename as Filename, + extension: opt.extension ?? img.getExtension() + } satisfies StoreOptions + const files = await writeImages(tiles, storeOptions) + // 6. Return buffer[] | string[] + if (opt.response === 'file') return files + return await getBufferImages(tiles) +} diff --git a/src/tiles.ts b/src/tiles.ts deleted file mode 100644 index 65c7517..0000000 --- a/src/tiles.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as v from 'valibot' -import { - Size, - Tiles, - HorizontalTiles, - VerticalTiles, - GridTiles, - TilesCut, - TileCoordinates, -} from "./types" -import { ThrowSpliteaError } from "./errors" -import { GridTilesSchema, HorizontalTilesSchema, TileCoordinatesSchema, TilesCutSchema, TilesSchema, VerticalTilesSchema } from './schemas' - -export const checkTiles = (tiles: Tiles, size: Size): void => { - const { mode } = v.parse(TilesSchema, tiles) - const parser = { - // Mode horizontal -> Columns || Width - 'horizontal': HorizontalTilesSchema, - // Mode Vertical -> Rows || Height - 'vertical': VerticalTilesSchema, - // Mode Grid -> rows + columns || width + height - 'grid': GridTilesSchema - } - v.parse(parser[mode], { ...tiles, size }) -} - -const getCoordinates = (tilesCut: TilesCut): TileCoordinates[] => { - try { - const { tileWidth, tileHeight, imageWidth, imageHeight } = tilesCut - const arrayX = new Array(imageWidth / tileWidth).fill(0) - const arrayY = new Array(imageHeight / tileHeight).fill(0) - // Move by row - return arrayY.map((_y, indexY) => { - const y = tileHeight * indexY - // Move by column - return arrayX.map((_x, indexX) => { - const x = tileWidth * indexX - return v.parse(TileCoordinatesSchema, { x, y, width: tileWidth, height: tileHeight }) - }) - }).flat() - } catch (error) { - throw ThrowSpliteaError(error, 'Problem with getting tiles') - } -} - -const getHorizontalTiles = (tiles: HorizontalTiles): TileCoordinates[] => { - const { width , columns, size } = v.parse(HorizontalTilesSchema, tiles) - const tilesCut = v.parse(TilesCutSchema, { - tileWidth: (width > 0) ? width : size.width / columns, - tileHeight: size.height, - imageWidth: size.width, - imageHeight: size.height, - }) - return getCoordinates(tilesCut) -} - -const getVerticalTiles = (tiles: VerticalTiles): TileCoordinates[] => { - const { height, rows, size } = v.parse(VerticalTilesSchema, tiles) - const tilesCut = v.parse(TilesCutSchema, { - tileWidth: size.width, - tileHeight: (height > 0) ? height : size.height / rows, - imageWidth: size.width, - imageHeight: size.height, - }) - return getCoordinates(tilesCut) -} - -const getGridTiles = (tiles: GridTiles): TileCoordinates[] => { - const { width, height, rows, columns, size } = v.parse(GridTilesSchema, tiles) - const tilesCut = v.parse(TilesCutSchema, { - tileWidth: (width > 0) ? width : size.width / columns, - tileHeight: (height > 0) ? height : size.height / rows, - imageWidth: size.width, - imageHeight: size.height, - }) - return getCoordinates(tilesCut) -} - -export const getTilesCoordinates = (size: Size, tiles: Tiles): TileCoordinates[] => { - if (tiles.mode === 'horizontal') return getHorizontalTiles({ ...tiles, size } as HorizontalTiles) - if (tiles.mode === 'vertical') return getVerticalTiles({ ...tiles, size } as VerticalTiles) - return getGridTiles({ ...tiles, size } as GridTiles) -} diff --git a/src/types.ts b/src/types.ts index 732286b..5b865b5 100755 --- a/src/types.ts +++ b/src/types.ts @@ -1,48 +1,52 @@ -import * as v from 'valibot' -import { ExtensionSchema, FilenameSchema, GridTilesSchema, HorizontalTilesSchema, ImageSchema, ModeSchema, NaturalSchema, OutputSchema, PathSchema, ResponseSchema, SizeSchema, StoreSchema, TileCoordinatesSchema, TilesCutSchema, TilesSchema, UniqueSchema, VerticalTilesSchema } from './schemas' - -export type Natural = v.Input -// Image ---------------------------------------------------------------------- -export type Image = v.Input -export type Size = v.Input -/** Tiles --------------------------------------------------------------------- - * 1. Slice Mode -> mode => "grid" | "vertical" | "horizontal" - * 2. Options for the slices - * 2.1 Grid tiles -> rows + columns | width + height - * 2.2 Vertical tiles -> rows | height - * 2.3 Horizontal tiles -> columns | width - * 3. Non-repeated tiles -> unique => false (default) | true -*/ -export type Mode = v.Input - -export type Unique = v.Output - -export type Tiles = v.Output - -export type HorizontalTiles = v.Output - -export type VerticalTiles = v.Output - -export type GridTiles = v.Output - -export type TilesCut = v.Output - -export type TileCoordinates = v.Output -/** Output -------------------------------------------------------------------- - * 1. response = data to return -> data => "buffer" | "path" - * 2. If response = "path" or you want to store the slices it needs to provide path + name (extension is optional) - * 2.1 Local path to store slices -> path - * 2.2 Name preffix to slices -> name - * 2.3 Extension to store tiles -> extension => "jpg" | "png" | "bmp" | "gif" | "tiff" - **/ -export type Response = v.Input - -export type Path = v.Input - -export type Filename = v.Input - -export type Extension = v.Input - -export type Store = v.Input - -export type Output = v.Output +import { DifferenceSchema, DistanceSchema, ExtensionSchema, FilenameSchema, GridOptionsSchema, HorizontalOptionsSchema, ImageSchema, NaturalSchema, OptionsSchema, PathSchema, ResponseSchema, UniqueRequirementSchema, VerticalOptionsSchema } from './schemas' + +// PRIMITIVES ----------------------------------------------------------------- +export type Natural = ReturnType +// IMAGE ---------------------------------------------------------------------- +export type Image = ReturnType +export interface Size { + width: Natural + height: Natural +} +// OPTIONS -------------------------------------------------------------------- +export type Response = ReturnType +export type Path = ReturnType +export type Filename = ReturnType +export type Extension = ReturnType +export type UniqueRequirement = ReturnType +export type Distance = ReturnType +export type Difference = ReturnType + +export type Options = ReturnType +export type HorizontalOptions = ReturnType +export type VerticalOptions = ReturnType +export type GridOptions = ReturnType + +export interface UniqueImagesOptions { + requirement: UniqueRequirement + distance: Distance + difference: Difference +} + +export interface StoreOptions { + path: Path + filename: Filename + extension: Extension +} + +export interface CropData { + x: Natural + y: Natural + w: Natural + h: Natural +} + +export interface WriteOptions { + path: string + filename: string + extension: string + index: string + pad: number +} + +export type Output = string | Buffer diff --git a/src/utils.ts b/src/utils.ts index af534ad..8cd9b11 100755 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,30 +1,7 @@ -import * as v from 'valibot' -import { NaturalSchema } from './schemas' -import { Natural } from './types' -// import isValidFilename from 'valid-filename' +import isValidFilename from 'valid-filename' -export const greaterThanZero = (num: Natural): boolean => num > 0 -export const xor = (a: boolean, b: boolean): boolean => ( a && !b ) || ( !a && b ) +export const xor = (a: boolean, b: boolean): boolean => (a && !b) || (!a && b) -export const isSubmultiple = (numerator: number, denominator: number): boolean => (v.parse(NaturalSchema, numerator) % v.parse(NaturalSchema, denominator)) === 0 +export const isSubmultiple = (numerator: number, denominator: number): boolean => numerator % denominator === 0 -export const invalidWindowsFilename = (name: string): boolean => { - // Windows Regex = /^(con|prn|aux|nul|com\d|lpt\d)$/i - const windowsReservedNameRegex = /^(con|prn|aux|nul|com\d|lpt\d)$/ - const invalidWindowsFilename = new RegExp(windowsReservedNameRegex, 'i') - const result = invalidWindowsFilename.test(name) - if (result) console.error(`Invalid windows filename "${name}"`) - return result -} - -export const invalidUnixFilename = (name: string): boolean => { - // All OS except Windows Regex = /[<>:"/\\|?*\u0000-\u001F]/g - const filenameReservedRegex = /[<>:"/\\|?*\u0000-\u001F]/ - const invalidFilename = new RegExp(filenameReservedRegex, 'g') - const result = invalidFilename.test(name) - if (result) console.error(`Invalid unix filename "${name}"`) - return result -} - -export const invalidFilename = (name: string): boolean => invalidUnixFilename(name) || invalidWindowsFilename(name) -// export const invalidFilename = (name: string): boolean => !isValidFilename(name) \ No newline at end of file +export const validFilename = (name: string): boolean => isValidFilename(name) diff --git a/tests/chess_horizontal.png b/tests/chess_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..de95dd09106bc94a95a344ebb4529ef8e600553c GIT binary patch literal 2491 zcmeAS@N?(olHy`uVBq!ia0y~yV7dTgM{%$L$%&5>xfvKZn><|{Ln`9lUNhuva1dcV zcvv+^Lql^ylU&Oj)&nfjXC-%}Y^wZudd_#_J@;A9z5el+U%qGKTI)6khx3e|zPHYk zPPa8bSCu#C>m3Vyz87p!7kW20f_blZ7G5iUyyp3_$n(9MnRgY+HRUbfErAKZ^~nM? zFk2OwUEtkT?^|4%IVB^9i(4?O&QfR8W5u?0+^*nvj((m84ZZhfIzKnMpMLS fiWpKU;wNjpIo|=!1?*FRjWPyLS3j3^P6Agw^=}3zpfgnhgCV|j{QluuK zHv#F9P?TN-9>4eQyqWvv-8*mQ&Afl^`)Bve?zd-l&+ggto!!_cI_mVaY_tFXfL`N~ zivDG71^|%asVOg|s~q+j004)nh6>cs&vFxSCB>)<(H*$_@J%~lCpGF;2{ZZlH>Eo& ztPj(hXjKwPW4CzP>d0B@GWAP_hHv9NG)XvxS*+g13NFfqW;UtVY74TMW<)^}9}B0T z>G;UQps9!pZ`}U()ww_}^eK_+OlQn69Nml=!R*dn93L*_n2+PD*SX_jyh&}zoe}P8 zp~(9=G;Ez&NE$X(Kw-xXZ9ESt&rO+ZB$ON?;Ux);pboVmz``jR0loMk@-xveGAdwM zYv7^nxzF}o+A&fSu2c5%8!!vAv*W|_?5Ayuub1;C@$q>a=NbZ}uZ@toEl&Z_7KXX6 zWs&JJKo)@tr?=b25^rAQrdyO;O?LtS3%ewSORMw|rpj8`q2~Jy%c* zo5V6X0YpL?aCV}-zCQsk^_@G8I5{D9fFY|mj5+-cJesGF-KD|Yd9u1uxHB#;X&r>g zvZx-wBIq3#I~~AzXqVZ?^z2fG+Sy)udcg*EF`?%x;;u%CiGjPhwbEv7Cs9c+ygR(- zf_7uWom-AqJy@j8#QED<#a|CFdmAcY6G3y=3fLbH=J>zz6uyy)cB;8DiwWW0Clvgae1UirrgRmr z2SO>iJZtnw%)VdXwn2Yb`h>cVHLqj8%>Wr6?EP^hiiah>qRIV^i1?G8Vs1TF5uI-R zxU`6-tLo}2Ux%lzh~4m33E?-0eY_d|*kJ~;^20JzYT+HqnfXMp(7Eb~JT-xzx7~}X zV49C0tQ4(~+_yyE`~1LLWj002TTolJYxt|=CYYX6W5+rT5kE5SOY4=&XvrZh*w4(a zmpvNxoo*#^Neh^@VVYC_q{9`=dV^5%Oa^#k#P$hTk{Lx8Rn@YCK`emZ^^^x{q)mUFfd49XC z0(sqs4>8OQ1sy^>4)d9!vWwA;seQeU{`pa1sccD4r*0G8tsNnR)$quE0<+iXYx)to z?!;3N;d;ccV_CAz7w2RTtKQ;xu#o1SQj0=w><$4k*_FZpUOo_PjW@dq;>`OfKN^QN z$>-Yw@~5@yHK(?aYn=x`OT66!8JC!zh1ukL)qOg!RP+oIJCKe_baryg^{w|g05=;X)#Al zh8K%obh|9(0tjyRnPVdU19u}%8LBS7`usu2_)Jp326nFz0&V69ApXFERD@xyp7^Mo zSguRVsT&_%dqK?N6TDc=TvP3kvb!*BugrIpUUYuRL(hBoHIM>vz>m4IPtE4VvViPx zO(WUAuiiao{_0Z%&@%dS&@pYD%j`*yuPgFL|GgkQlEiVjXCVb%Xi6ap!gR%lSpBl# z6fV(|NexTjJRNDlrK*o5o;I@JB}OaDvWba-OJA?TO-$xr*alX)<4KQ;-j}udy7L|_ zZmDyXN&shsVX@x~im{IFI#^Jmj6K$?px;6bdU~&v8>FR@@t{@uIUUM18Lb+YaPoa` z$-NtLT7CTnh4{}k!D9WyumcA>vDaRVn`E0EOc}S^p|XPZM+spH+byrO4oBh- z;#wQ-_0lkTk&A}wyeMYsWAdW;m1-zJ;72_cCUO#(PIg*L95?KaT9HOdLNra}ei~&= zUQA{J=>i0%9|EP1@fxsAL6q@yqTZxLW{}a zC^o2&K;~z;{}b6P9?*(jKu)*XVop51LB~)RZbiw?GiZH8Aj-fZ=~b1Ck24%K|7*uG zVy?zIZyL3#S?6nSpm&e68Eq6C<7F&=;u}hqAVLrzR56@cR9LhbY)&^Qx+qGlH0{5t zqDCZ$qvuC(ln`|GB8-);TMbH`6aWITv<5Z##2M_Gye5_IyoGo>48a zVu1w~+Z*EVYrl~uZu~38-uv*5T_8o%R+sjA`+QsPoyYQh$qpaY5{TxG54zU_$)*e6 zlgTJFPP60py_-ify#o`$kJw~fo~S=`Qvnq?7da2G<3-?38VCpGen@m8SWJ|?V84aL zSwev-GAM3@+<6oo#lA}^sZ2$P+qI)69oz$wo2^Q(Uw78G0%iFR7kJM|JbV#k<+>`@ z5hDjdxnL?p;Xl-E%o4oK|Pn( z9pW2H0yeJDer@-qGVzznz_xkP=6MZ7YZFbt%>vuH=JVucrWiW;l*iOJkJ4N$E0O{k zW%y$(F&^j)hhdVACcn3uvQ(mcUr!W+-b!nAYE$v&(8+C@uJxN?p_tQw=!SvEcYMcL z&NR!c9BtQ-&I8KCCqeJvKEUbDgK%N=arId{7{*@fBjd+?-2oACU08w@u|3&Le7<3* ztKXC~yW{G!XB{?v1s~ORqH&!p$m}1R$8U`p8qIFVr`dk=^}QdF!s$uR-H7f(-d*m^nigU67zMO}4;2xURy*iC}6VLPNK`kpUczbNVr@DALnNkoF1;Nr=U ztvynp*|gS4y%*HhfM1~m+ZZy~gX^#11<`BW6rAavbqH2kna$O%!g~dM&69LAWTN?t zOCa>q8R2UbCKu17#&d!!#Z867LJ#OY0Gu>4GBAUJ*21JjbFPPRDB$!;qPUn-C;jjp z*odxK7X=)}u}C9Ujx-N{uji0wgcr=Me!PxFtvFXYe%qYDOS2qVLG*OiInBUsb{Ka$|iwXYDSPsIB;s(PQjTTMZEXpy#h3%k{WtV z@(tiobdn(KDoF|*=ihh|8%0yTbw<``c!|H+u>Zvj&C3uxF> z0Q}18|E-FDK(+qm1{Oec-z$b;ycj|))bN15K_>FV1uZ-emRuVzOv=OA{r@KYyOZ}% zj?L{?Fw-upCaQKp7{KN&&;J|le`>hDchn-M=|41oJ_Z7u!YLuwX~O^B^Z$wc|6a|1 zy)ypg2KKSB8UKcFkyO*L0iN@e-IyS7w0Iy&-^5S_@6(r;(1|JlV=Cy4s*UPi-v?iXiQMo+gdj^E^Z zZB84}bIF-{6Y#vdQ*FVgqtkV--YaW#Fxujzm(EE0RzNVZ;?Q;&1iqYHF<0P5O?dt-+VIF!Vim@cQ_k<6f#EP_s&+e}%;L`(vusKW!M&@**wzl$s z*I1S+czDa;j@RY#y_RTV&K0CFyNI;VdG~~1{l@kGEFt)LL zx%IuVRQ6h>U%x2YW=%2Z21=T$0_T3$vPJqg-W($#!OSh|<8+|-AlXGN-=0g*QiVEpj$Sc|do6>{KaCx>(sIql6zJ-!>wHAPAW!rk$&#Ctx=(=np{Vu02 zxIPoO-CEo^_$4y+J9%D!W9G;oFd>u`Y5T&VJ7KD<#$6XU$^Z0`bU!s)X$hgw4%?D3 zQkbfayL=sK(KKNyr||j?c4#DdEW!&GP%@1C|Ge9_B1qjy%e70=02RkB4593 z?(kM(ajU$tsdMUUMH)UonVKT9bLIEaH+#WNZLPGv#V;RJ<3InFY2c|;dt(`@@Ut*Y z0)%O>zO}`v|26hIhe?H5M#WAmX@zdG!qJb2*ary>)dJTV&kkm4yDYTfGVZ9o>2Z#j z*Cat%yKakG{=ahmr&&@0_qe3gnPfa)H&IVUKz{a0U5fAvSYF?$n0b+7m*th;* zrKr9QDV}|@G!uQZ3QD{$+e;d%u&^y77)Wn6ZgE)#iG+Q>G6A<%#z6k+!$^@7`LI`asFz@iIrw-vb+oAx4=AAy>Lh zEab)4!n6%iBH~jR>N_{e%6fHNQrz&Lg-`Lyx@+(B55CWaT$~BWKJ{&CZ(B-|P4c7s zT|ZM9DL$+OU5m&OOpK@HJXs5v7RoEI?tl>eJxZrk^(O#;!Y@qTb;*3MS9-dx)oAYF zMfO5VQUY(Djwob9AkU!snbYB4J|{O z##hS{E}Mk!VbAFd088}+t}QiSGZdxFrlZfZM@4=4#yD%F{lGixxWpkJJ8TZkR1tv0 zIX$7P+i8o6mw=DrgT;#hL9@4@5tFDJH(%u!bX^4?g#Qs`5BOVL-;fBWeBzSH1EaGK zr*s#Dg{v_Fe%@32=PW%$^T(4BYi~lk_1NCn0>@qFWS5WA>7a``XNs>Gmff1_S0-`i zEt;H@zNVYUi=i^8i*41w$)Yp8vXPpVvKD0#??1^+{xn>`lX7|OGWQvhI{URSx*oES z{4gr)fw=A0yhJ7-#8H{{}EVQb)`uS3;$&cAqls32y2wA27wCA>XVtG+o|!?(L0 zyE%il@6$Znd1>~o?Fo~Q&W$;3PEfT$*%fwlsu7-eQOtWqOELeNuAJWfCG?n;*nG!- z;}&47VtXj7>S)zF@PSd`_g$Q#6-seOq@Vlsk#$jmCB`0?n{}AkH)8T$%2_`zXh&nN zewmd12wv&lC?ICff@%PBkQR=l32OKKf!L_3svg*^urP_Y9lcnc|B~oDQB);y!S&EF zJ>W+eRh-Y=XI4r|2AQXu2X{26!f z-cSy_E*kfHSW|qEU2&=9!pf34u!A1NF=R_)q?viQH~vFgo27-%lqd88MWl}Zom^KKk=s@LKc5wx4Sy%A5va#8Z;q})$Km4%QiRnM&X|x?AcHGnct;j zJ~?5HBU$?JU8BTAR^~OwyLJ+eq1sMgB|$xRa17UAmTYvEB$?h2NUgH zLCD4VAO@QiLVk1^jBRRfHijiN?Hs)ulCro@L=r9M-f_}*_wo#T^Z8e}72UeMaua^W z&szRBwR1gO zwkN$um{C9(IXdx7myh6M{q@L^UWEvmGHCIk@W;lUrg12-MdUz z6JICen1IbAE^e*f$0Re#+dud}#Uw!<<-O0OIg7m}WG9%gGf2payqjh$0eRZsWWyn(y7x; z)++D$eVMBhBl1~&ato2yKJA?Ejp)`EKo~#E8>~?f_~S{62n9mNt$>PT=A{7;oR*_i zfj|K>@#KElozT0An~#5Q?U(*0XHXe8{hKBiMxgVwYw?mq-(xj7>5)3zB6E|ZnwNXxccQb~L z=fP`Q8yVZuS7esl7hDG~xi6CJT+q|@*OKC4yh(_byHiiP% zV9`^>KSk*SjH_T9ttg22#(+#U*yn`oF2U~ zpN=oszPQIyo)`+;UOfDnKZvS!z6-oTvPs;XbAZlFWP~B=|xde z6kVN&h{(vnP<%jLT^(5buH^khM>(}XXx|TwcWkJuUY)AEu!u_0@pKa;G}vWGLQP3M lUJxMT{|_=z>b(mRlo)01t=BOJmtTzl4OJbLN~P!F{|0&LPQCyD literal 0 HcmV?d00001 diff --git a/tests/image.test.ts b/tests/image.test.ts index 2eb26f1..d273f1b 100644 --- a/tests/image.test.ts +++ b/tests/image.test.ts @@ -1,12 +1,11 @@ import { Buffer } from 'node:buffer' -import fs from 'node:fs/promises' +import fs from 'node:fs' import path from 'node:path' -import * as v from 'valibot' import { describe, test, expect } from 'vitest' import { SpliteaError } from '../src/errors' -import { TileCoordinates } from '../src/types' -import { areEqualImages, getSplitImages, getUniqueImages, getImage, getBufferImages, writeImages } from '../src/image' -import { UniqueSchema } from '../src/schemas' +import { getBufferImages, getGridTiles, getHorizontalTiles, getImage, getSize, getUniqueGridTiles, getUniqueHorizontalTiles, getUniqueVerticalTiles, getVerticalTiles, writeImages } from '../src/image' +import { StoreOptions, UniqueImagesOptions } from '../src/types' +import { MAX_DIFFERENCE, MAX_DISTANCE } from '../src/constants' const IMG_FOLDER = path.join(__dirname) @@ -17,6 +16,12 @@ const forest = { bad: path.join(IMG_FOLDER, 'forestmapp.png'), } +const bad = { + path: path.join(IMG_FOLDER, 'forestmapp.png'), + width: 320, + height: 224, +} + const satie = { path: path.join(IMG_FOLDER, 'Ericsatie.jpg'), width: 2651, @@ -29,120 +34,136 @@ const chess = { height: 720, } -describe('Test getImage()', () => { +const horizontal = { + path: path.join(IMG_FOLDER, 'chess_horizontal.png'), + width: 720, + height: 90, +} + +const vertical = { + path: path.join(IMG_FOLDER, 'chess_vertical.png'), + width: 90, + height: 720, +} + +describe.concurrent('Test getImage + getSize', () => { test('Correct local jpg', async () => { Promise.all([forest, satie, chess].map(async(img) => { - const [_, size] = await getImage(img.path) + const image = await getImage(img.path) + const size = getSize(image) expect(size.width).toBe(img.width) expect(size.height).toBe(img.height) })) }) test('Incorrect local jpg', async () => { - await expect(getImage(forest.bad)).rejects.toThrow(SpliteaError) + await expect(getImage(bad.path)).rejects.toThrowError() }) }) -describe('Test getSplitImages()', () => { - const unique = v.parse(UniqueSchema, {}) - - test('Correct splits', async () => { - const [img, size] = await getImage(chess.path) - const tile: TileCoordinates = { x: 0, y: 0, width: size.width, height: size.height } - let tiles = [tile] - let image = getSplitImages(img, size, tiles, unique)[0] - expect(image.bitmap.width).toBe(tile.width) - expect(image.bitmap.height).toBe(tile.height) - tile.x = size.width / 2 - tile.width = size.width / 2 - tile.y = size.height / 2 - tile.height = size.height / 2 - tiles = [tile] - image = getSplitImages(img, size, tiles, unique)[0] - expect(image.bitmap.width).toBe(tile.width) - expect(image.bitmap.height).toBe(tile.height) - }) +test.concurrent('getHorizontalTiles', async () => { + const img = await getImage(horizontal.path) + const size = getSize(img) + const width = size.width / 8 + const tiles = getHorizontalTiles(img, size, width) + expect(tiles).toHaveLength(8) +}) - test.skip('Incorrect splits', async () => { - const [img, size] = await getImage(chess.path) - const tile: TileCoordinates = { x: 1, y: 1, width: size.width, height: size.height } - let tiles = [tile] - expect(() => getSplitImages(img, size, tiles, unique)).toThrow(SpliteaError) - tile.x = (size.width / 2) + 1 - tile.width = size.width / 2 - tile.y = size.height / 2 - tile.height = size.height / 2 - tiles = [tile] - expect(() => getSplitImages(img, size, tiles, unique)).toThrow(SpliteaError) - tile.x = size.width / 2 - // w = size.width / 2 - tile.y = (size.height / 2) + 1 - // h = size.height / 2 - tiles = [tile] - expect(() => getSplitImages(img, size, tiles, unique)).toThrow(SpliteaError) - }) +test.concurrent('getUniqueHorizontalTiles', async () => { + const img = await getImage(horizontal.path) + const size = getSize(img) + const width = size.width / 8 + const options: UniqueImagesOptions = { + requirement: 'all', + distance: MAX_DISTANCE, + difference: MAX_DIFFERENCE + } + const tiles = getUniqueHorizontalTiles(img, size, width, options) + // Promise.all(tiles.map(async (tile, idx) => { + // const extension = tile.getExtension() + // const filename = path.join(__dirname, `horizontal_${idx}.${extension}`) + // const buffer = await tile.getBufferAsync(tile.getMIME()) + // fs.writeFileSync(filename, buffer) + // })) + expect(tiles).toHaveLength(2) }) -describe('Test areEqualImages()', () => { - const unique = v.parse(UniqueSchema, {}) - - test('Checking Equal Images', async () => { - const [ [img, _], [imgSatie, __] ] = await Promise.all([getImage(forest.path), getImage(satie.path)]) - // const [img, _] = await getImage(forest.path) - // const [imgSatie, __] = await getImage(satie.path) - // Equals - expect(areEqualImages(img, img, unique)).toBeTruthy() - expect(areEqualImages(imgSatie, imgSatie, unique)).toBeTruthy() - // Differents - expect(areEqualImages(img, imgSatie, unique)).toBeFalsy() - expect(areEqualImages(imgSatie, img, unique)).toBeFalsy() - }) +test.concurrent('getVerticalTiles', async () => { + const img = await getImage(vertical.path) + const size = getSize(img) + const height = size.height / 8 + const tiles = getVerticalTiles(img, size, height) + expect(tiles).toHaveLength(8) }) -describe('Test getUniqueImages()', () => { - const unique = v.parse(UniqueSchema, { enable: true }) - - test('Unique images', async () => { - const [ [iforest, _i1], [imgSatie, _is1] ] = await Promise.all([getImage(forest.path), getImage(satie.path)]) - // const [iforest, _i1] = await getImage(forest.path) - // const [imgSatie, _is1] = await getImage(satie.path) - const load = [iforest, imgSatie, iforest, imgSatie] - const images = getUniqueImages(load, unique) - expect(images.length).toBe(2) - const [if11, is11] = images - expect(areEqualImages(if11, iforest, unique)).toBeTruthy() - expect(areEqualImages(is11, imgSatie, unique)).toBeTruthy() - }) +test.concurrent('getUniqueVerticalTiles', async () => { + const img = await getImage(vertical.path) + const size = getSize(img) + const height = size.height / 8 + const options: UniqueImagesOptions = { + requirement: 'all', + distance: MAX_DISTANCE, + difference: MAX_DIFFERENCE + } + const tiles = getUniqueVerticalTiles(img, size, height, options) + expect(tiles).toHaveLength(6) }) -describe('Test writeImages()', () => { - test('checking writting', async () => { - const image1 = forest.path - const image2 = satie.path - const [[jimp1, _size1], [jimp2, _size2]] = await Promise.all([getImage(image1), getImage(image2)]) - // const [jimp1, _size1] = await getImage(image1) - // const [jimp2, _size2] = await getImage(image2) +test.concurrent('getGridTiles', async () => { + const img = await getImage(chess.path) + const size = getSize(img) + const width = size.width / 8 + const height = size.height / 8 + const tiles = getGridTiles(img, size, width, height) + expect(tiles).toHaveLength(8 * 8) +}) - const data1 = { path: __dirname, name: 'test', extension: jimp1.getExtension() } +test.concurrent('getUniqueGridTiles', async () => { + const img = await getImage(chess.path) + const size = getSize(img) + const width = size.width / 8 + const height = size.height / 8 + const options: UniqueImagesOptions = { + requirement: 'all', + distance: MAX_DISTANCE, + difference: MAX_DIFFERENCE + } + const tiles = getUniqueGridTiles(img, size, width, height, options) + // Promise.all(tiles.map(async (tile, idx) => { + // const extension = tile.getExtension() + // const filename = path.join(__dirname, `horizontal_${idx}.${extension}`) + // const buffer = await tile.getBufferAsync(tile.getMIME()) + // fs.writeFileSync(filename, buffer) + // })) + expect(tiles).toHaveLength(22) +}) - const paths = await writeImages([jimp1, jimp2], data1.path, data1.name, data1.extension) - paths.forEach(async (path) => { - expect(() => fs.stat(path)).not.toThrowError() - await fs.rm(path) - }) - }) +test.concurrent('writeImages', { timeout: 50000 }, async () => { + const options: StoreOptions = { + path: __dirname, + filename: 'horizontal_test', + extension: 'jpg' + } + await expect(writeImages([], options)).rejects.toThrow(SpliteaError) + const [img1, img2, img3] = await Promise.all([getImage(chess.path), getImage(satie.path), getImage(forest.path)]) + await expect(writeImages([img1], options)).resolves.toHaveLength(1) + await expect(writeImages([img1, img2, img3], options)).resolves.toHaveLength(3) + fs.readdirSync(__dirname).forEach(item => { + if (item.includes(options.filename)) { + const file = path.join(__dirname, item) + fs.rmSync(file) + } + } ) }) -describe('Test getBufferImages()', () => { +describe.concurrent('Test getBufferImages()', { timeout: 50000 }, () => { test('checking buffers', async () => { const image1 = forest.path const image2 = satie.path - const [[jimp1, _size1], [jimp2, _size2]] = await Promise.all([getImage(image1), getImage(image2)]) - // const [jimp1, _size1] = await getImage(image1) - // const [jimp2, _size2] = await getImage(image2) + const [jimp1, jimp2] = await Promise.all([getImage(image1), getImage(image2)]) const buffers = await getBufferImages([jimp1, jimp2]) buffers.forEach(async (buffer) => { expect(buffer).toBeInstanceOf(Buffer) }) }) -}) \ No newline at end of file +}) diff --git a/tests/output.test.ts b/tests/output.test.ts deleted file mode 100644 index 47da32c..0000000 --- a/tests/output.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { rmSync } from 'node:fs' -import path from 'node:path' -import * as v from 'valibot' -import { describe, test, expect } from 'vitest' -import { getOutput } from '../src/output' -import { Output } from '../src/types' -import { getImage } from '../src/image' -import { OutputSchema } from '../src/schemas' - -const IMG_FOLDER = path.join(__dirname) - -const forest = { - path: path.join(IMG_FOLDER, 'forestmap.png'), - width: 320, - height: 224, - bad: path.join(IMG_FOLDER, 'forestmapp.png'), -} - -const satie = { - path: path.join(IMG_FOLDER, 'Ericsatie.jpg'), - width: 2651, - height: 3711, -} - -const chess = { - path: path.join(IMG_FOLDER, 'chess.png'), - width: 720, - height: 720, -} - -describe('getOutput', async () => { - const images = await Promise.all([ - getImage(forest.path), - getImage(satie.path), - getImage(chess.path) - ]).then(response => [response[0][0], response[1][0], response[2][0]]) - const output: Output = v.parse(OutputSchema, {}) - - test('response = "buffer", store = undefined', async () => { - const buffers = await getOutput(images, output) - expect(buffers).toHaveLength(images.length) - buffers.forEach(buff => expect(buff).toBeInstanceOf(Buffer)) - }) - - test('response = "buffer", store != undefined', async () => { - const store = { path: 'outputTest', name: 'testStore'} - output.store = store - const buffers = await getOutput(images, output) - expect(buffers).toHaveLength(images.length) - buffers.forEach(buff => expect(buff).toBeInstanceOf(Buffer)) - rmSync(store.path, { recursive: true }) - }) - - test('response = "path", store != undefined', async () => { - output.response = 'path' - const store = { path: 'outputTest', name: 'testStore'} - output.store = store - const paths = await getOutput(images, output) - expect(paths).toHaveLength(images.length) - paths.forEach(path => expect(typeof path).toBe('string')) - rmSync(store.path, { recursive: true }) - }) -}) diff --git a/tests/schemas.test.ts b/tests/schemas.test.ts index 875e5a3..9e78f64 100644 --- a/tests/schemas.test.ts +++ b/tests/schemas.test.ts @@ -1,302 +1,155 @@ -import { rmdirSync, existsSync } from 'node:fs' -import * as v from 'valibot' +import fs from 'node:fs' +import path from 'node:path' import { describe, test, expect } from 'vitest' -import { Size, Tiles, Output, TilesCut } from '../src/types' -import { MAX_DIFFERENCE, MAX_DISTANCE, MODES } from '../src/constants' -import { GridTilesSchema, HorizontalTilesSchema, OutputSchema, SizeSchema, TilesCutSchema, TilesSchema, VerticalTilesSchema } from '../src/schemas' +import type { GridOptions, HorizontalOptions, Options, VerticalOptions } from '../src/types' +import { ExtensionSchema, GridOptionsSchema, HorizontalOptionsSchema, ImageSchema, OptionsSchema, PathSchema, ResponseSchema, VerticalOptionsSchema } from '../src/schemas' +import { EXTENSIONS, MAX_DIFFERENCE, MAX_DISTANCE } from '../src' -describe('Size', () => { - test('test Size', () => { - const size: Size = {} as Size - expect(() => v.parse(SizeSchema, size)).toThrowError() - size.width = -1 - size.height = 5.0 - expect(() => v.parse(SizeSchema, size)).toThrowError() - size.width = 1.2 - expect(() => v.parse(SizeSchema, size)).toThrowError() - size.width = 1 - expect(() => v.parse(SizeSchema, size)).not.toThrowError() - expect(v.parse(SizeSchema, size)).toStrictEqual({ width: 1, height: 5}) - }) -}) +const IMG_FOLDER = path.join(__dirname) -describe('Tiles', () => { - const tilesModel: Tiles = { - mode: 'horizontal', - rows: 0, columns: 0, - width: 0, height: 0, - unique: { - distance: MAX_DISTANCE, - difference: MAX_DIFFERENCE, - requirement: 'both' - } - } - const sizeModel: Size = { width: 20, height: 30 } +const forest = { + file: path.join(IMG_FOLDER, 'forestmap.png'), + width: 320, + height: 224, +} - test('test Tiles', () => { - const tiles : Tiles = {} as Tiles - let result = v.safeParse(TilesSchema, tiles) - expect(result.success).toBeFalsy(); - ['horizontal', 'vertical', 'grid'].forEach(mode => { - tiles.mode = mode as typeof MODES[number] - result = v.safeParse(TilesSchema, tiles) - const expected = { ...tilesModel, mode } - expect(result.success).toBeTruthy() - if (result.success) - expect(expected).toMatchObject(result.output) - }); +const satie = { + file: path.join(IMG_FOLDER, 'Ericsatie.jpg'), + width: 2651, + height: 3711, +} + +const chess = { + file: path.join(IMG_FOLDER, 'chess.png'), + width: 720, + height: 720, +} + +const bad = { + file: path.join(IMG_FOLDER, 'forestmapp.png'), + width: 320, + height: 224, +} +// ImageSchema ---------------------------------------------------------------- +describe('ImageSchema', () => { + test('input non exists image throw an exception', () => { + expect(() => ImageSchema.parse(bad.file)).toThrow() }) - - test('test Horizontal', () => { - const mode = 'horizontal' - const size = {...sizeModel } - const tiles: Tiles = { mode } as Tiles - // const horizontal: HorizontalTiles = {...tiles, size} - const horizontal = {...tiles, size} - // Empty columns width - let result = v.safeParse(HorizontalTilesSchema, horizontal) - expect(result.success).toBeFalsy() - // Both columns width - horizontal.columns = 1 - horizontal.width = 1 - result = v.safeParse(HorizontalTilesSchema, horizontal) - expect(result.success).toBeFalsy() - // Just columns - horizontal.columns = 10 - horizontal.width = 0 - result = v.safeParse(HorizontalTilesSchema, horizontal) - expect(result.success).toBeTruthy() - // Just width - horizontal.columns = 0 - horizontal.width = 5 - result = v.safeParse(HorizontalTilesSchema, horizontal) - expect(result.success).toBeTruthy() - // Does not mind rows and height - horizontal.rows = 1 - horizontal.height = 1 - result = v.safeParse(HorizontalTilesSchema, horizontal) - expect(result.success).toBeTruthy() + + test('input image exists', async () => { + expect(ImageSchema.parse(satie.file)).toBeTypeOf('string') }) - - test('test Vertical', () => { - const mode = 'vertical' - const size = {...sizeModel } - const tiles: Tiles = { mode } as Tiles - // const vertical: VerticalTiles = {...tiles, size} - const vertical = {...tiles, size} - // Empty rows height - let result = v.safeParse(VerticalTilesSchema, vertical) - expect(result.success).toBeFalsy() - // Both rows height - vertical.rows = 1 - vertical.height = 1 - result = v.safeParse(VerticalTilesSchema, vertical) - expect(result.success).toBeFalsy() - // Just rows - vertical.rows = 10 - vertical.height = 0 - result = v.safeParse(VerticalTilesSchema, vertical) - expect(result.success).toBeTruthy() - // Just height - vertical.rows = 0 - vertical.height = 5 - result = v.safeParse(VerticalTilesSchema, vertical) - expect(result.success).toBeTruthy() - // Does not mind columns and width - vertical.columns = 1 - vertical.width = 1 - result = v.safeParse(VerticalTilesSchema, vertical) - expect(result.success).toBeTruthy() + + test('input buffer', () => { + const buffer = fs.readFileSync(forest.file) + expect(ImageSchema.safeParse(buffer).success).toBeTruthy() }) - - test('test Grid', () => { - const mode = 'grid' - const size = {...sizeModel } - const tiles: Tiles = { mode } as Tiles - // const grid: GridTiles = {...tiles, size} - const grid = {...tiles, size} - // Empty rows+columns + width+height - let result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeFalsy() - // Both rows+columns + width+height - grid.rows = 1 - grid.columns = 1 - grid.width = 1 - grid.height = 1 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeFalsy() - // Just rows+columns - grid.rows = 10 - grid.columns = 5 - grid.width = 0 - grid.height = 0 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeTruthy() - // Just width+height - grid.rows = 0 - grid.columns = 0 - grid.width = 10 - grid.height = 5 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeTruthy() - // Just rows+columns not width or height - grid.rows = 10 - grid.columns = 5 - grid.width = 1 - grid.height = 0 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeFalsy() - grid.width = 0 - grid.height = 1 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeFalsy() - // Just width+height not rows or columns - grid.width = 10 - grid.height = 5 - grid.rows = 1 - grid.columns = 0 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeFalsy() - grid.rows = 0 - grid.columns = 1 - result = v.safeParse(GridTilesSchema, grid) - expect(result.success).toBeFalsy() +}) +// ResponseSchema ------------------------------------------------------------- +test('ResponseSchema', () => { + ['buffer', 'file'].forEach(el => { + expect(ResponseSchema.parse(el)).toBe(el) + }) + ;['Buffer', 'File', {}, false].forEach(el => { + expect(() => ResponseSchema.parse(el)).toThrow() }) }) +// PathSchema ----------------------------------------------------------------- +describe('PathSchema', () => { + test('Cannot create path', () => { + const badPath = path.join(IMG_FOLDER, '\0') + const parsed = PathSchema.safeParse(badPath) + if (parsed.success) { + fs.rmdirSync(badPath) + } + expect(parsed.success).toBeFalsy() + }) -describe('Tiles Cut', () => { - test('test Tiles Cut', () => { - const tilesCut: TilesCut = {} as TilesCut - // Empty - let result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() - tilesCut.imageWidth = 20 - tilesCut.imageHeight = 30 - tilesCut.tileWidth = 2 - tilesCut.tileHeight = 3 - // Correct - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeTruthy() - // Bigger than limits - tilesCut.tileWidth = tilesCut.imageWidth + 1 - tilesCut.tileHeight = 10 - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() - tilesCut.tileWidth = 10 - tilesCut.tileHeight = tilesCut.imageHeight + 1 - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() - // Not submultiples - tilesCut.tileWidth = 0 - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() - tilesCut.tileWidth = 3 - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() - tilesCut.tileWidth = 10 - tilesCut.tileHeight = 0 - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() - tilesCut.tileHeight = 9 - result = v.safeParse(TilesCutSchema, tilesCut) - expect(result.success).toBeFalsy() + test('Not write permissions', () => { + const badPath = path.join(IMG_FOLDER, 'bad') + if (!fs.existsSync(badPath)) { + fs.mkdirSync(badPath, { mode: 444 }) + } + fs.chmodSync(badPath, 0o444) + const parsed = PathSchema.safeParse(badPath) + fs.rmdirSync(badPath) + expect(parsed.success).toBeFalsy() }) }) - -describe('Output', () => { - test('Response empty', () => { - const output: Output = {} as Output - // Empty => buffer response - let result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - if (result.success) - expect(result.output.response).toStrictEqual('buffer') +// FilenameSchema ------------------------------------------------------------- +// ExtensionSchema ------------------------------------------------------------ +test('ExtensionSchema', () => { + EXTENSIONS.forEach(ext => expect(ExtensionSchema.parse(ext)).toBe(ext)) + ;EXTENSIONS.forEach(ext => expect(ExtensionSchema.parse(ext.toLocaleUpperCase())).toBe(ext)) + ;expect(() => ExtensionSchema.parse('peneg')).toThrow() +}) +// UniqueRequirementSchema ---------------------------------------------------- +// DistanceSchema ------------------------------------------------------------- +// DifferenceSchema ----------------------------------------------------------- +// OptionsSchema -------------------------------------------------------------- +describe('OptionsSchema', () => { + test('Default object', () => { + const options = {} + expect(OptionsSchema.parse(options)).toEqual({ + response: 'buffer', + filename: 'tile', + unique: false, + uniqueRequirement: 'all', + distance: MAX_DISTANCE, + difference: MAX_DIFFERENCE + }) }) - test('Response buffer', () => { - const output: Output = { response: 'buffer'} as Output - let result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - if (result.success) - expect(result.output.response).toStrictEqual('buffer') - // Add store - output.store = { path: './', name: 'store' } - result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - - output.store.path = 'storeTest' - result = v.safeParse(OutputSchema, output) - rmdirSync(output.store.path, { recursive: true }) - expect(result.success).toBeTruthy() + test('If response is "file" then path must be provided', () => { + const options = { response: 'file' } satisfies Options + expect(() => OptionsSchema.parse(options)).toThrow() }) - - test('Response path', () => { - const output: Output = { response: 'path' } as Output - // Response = path No store - let result = v.safeParse(OutputSchema, output) - expect(result.success).toBeFalsy() - // Response = path + Store - output.store = { path: './', name: 'store' } - result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - - output.store.path = 'storeTest' - result = v.safeParse(OutputSchema, output) - rmdirSync(output.store.path) - expect(result.success).toBeTruthy() +}) +// HorizontalOptionsSchema ---------------------------------------------------- +describe('HorizontalOptionsSchema', () => { + test('Just provide at least columns or width but not both', () => { + const columns = 4 + const width = 40 + const options = { columns, width } satisfies HorizontalOptions + expect(() => HorizontalOptionsSchema.parse(options)).toThrow() + expect(() => HorizontalOptionsSchema.parse({})).toThrow() + const opt1 = { columns } + expect(HorizontalOptionsSchema.safeParse(opt1).success).toBeTruthy() + const opt2 = { width } + expect(HorizontalOptionsSchema.safeParse(opt2).success).toBeTruthy() }) - - test('Invalid paths - filenames', () => { - const response = 'path' - const store = { path: './', name: 'storeTest' } - let output = v.parse(OutputSchema, { response, store }) - let result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - // Invalid filenames - const invalidFiles = ['foo/bar', 'foo\u0000bar', 'foo\u001Fbar', 'foo*bar', 'foo:bar', 'AUX', 'com1', 'foo\\bar'] - invalidFiles.forEach(pattern => { - store.name = pattern - output = { response, store } - result = v.safeParse(OutputSchema, output) - expect(result.success).toBeFalsy() - }) - // Invalid paths - const invalidPaths = ['foo/bar', 'foo\u0000bar', 'foo\u001Fbar', 'foo*bar', 'foo:bar', 'AUX', 'com1', 'foo\\bar'] - invalidPaths.forEach(pattern => { - store.path = pattern - output = { response, store } - result = v.safeParse(OutputSchema, output) - if (existsSync(store.path)) { - rmdirSync(store.path, {recursive: true}) - } - expect(result.success).toBeFalsy() - }) +}) +// VerticalOptionsSchema ------------------------------------------------------ +describe('VerticalOptionsSchema', () => { + test('Just provide at least rows or height but not both', () => { + const rows = 4 + const height = 40 + const options = { rows, height } satisfies VerticalOptions + expect(() => VerticalOptionsSchema.parse(options)).toThrow() + expect(() => VerticalOptionsSchema.parse({})).toThrow() + const opt1 = { rows } + expect(VerticalOptionsSchema.safeParse(opt1).success).toBeTruthy() + const opt2 = { height } + expect(VerticalOptionsSchema.safeParse(opt2).success).toBeTruthy() }) - - test('Valid paths - filenames', () => { - const response = 'path' - const store = { path: 'storePath', name: 'storeFile' } - let output = v.parse(OutputSchema, { response, store }) - let result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - rmdirSync(store.path, { recursive: true }) - // Valid filenames - const validFiles = ['foo-bar', 'hola.txt'] - validFiles.forEach(pattern => { - store.name = pattern - output = { response, store } - result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - }) - rmdirSync(store.path) - // Valid filenames - const validPaths = ['foo-bar', 'helloWorld'] - validPaths.forEach(pattern => { - store.path = pattern - output = { response, store } - result = v.safeParse(OutputSchema, output) - expect(result.success).toBeTruthy() - rmdirSync(store.path, {recursive: true}) - }) +}) +// GridOptionsSchema ---------------------------------------------------------- +describe('GridOptionsSchema', () => { + test('Just provide at least columns-rows or width-height but not both', () => { + const rows = 4 + const columns = 4 + const width = 40 + const height = 40 + const options = { rows, columns, width, height } satisfies GridOptions + expect(() => GridOptionsSchema.parse(options)).toThrow() + expect(() => GridOptionsSchema.parse({})).toThrow() + const opt1 = { rows, width } + expect(() => GridOptionsSchema.parse(opt1)).toThrow() + const opt2 = { columns, height } + expect(() => GridOptionsSchema.parse(opt2)).toThrow() + const opt3 = { rows, columns } + expect(GridOptionsSchema.safeParse(opt3).success).toBeTruthy() + const opt4 = { width, height } + expect(GridOptionsSchema.safeParse(opt4).success).toBeTruthy() }) -}) \ No newline at end of file +}) diff --git a/tests/splitea.test.ts b/tests/splitea.test.ts index 7bb2b53..cf39595 100755 --- a/tests/splitea.test.ts +++ b/tests/splitea.test.ts @@ -1,10 +1,8 @@ -import fs from 'node:fs/promises' +import fs from 'node:fs' import path from 'node:path' -import * as v from 'valibot' import { describe, test, expect } from 'vitest' -import { Image } from '../src/types' -import { Splitea } from '../src' -import { OutputSchema, TilesSchema, UniqueSchema } from '../src/schemas' +import { HorizontalOptions, VerticalOptions, verticalTiles, horizontalTiles, GridOptions, gridTiles } from '../src' +import { SpliteaError } from '../src/errors' const IMG_FOLDER = path.join(__dirname) @@ -15,6 +13,12 @@ const forest = { bad: path.join(IMG_FOLDER, 'forestmapp.png'), } +const bad = { + path: path.join(IMG_FOLDER, 'forestmapp.png'), + width: 320, + height: 224, +} + const satie = { path: path.join(IMG_FOLDER, 'Ericsatie.jpg'), width: 2651, @@ -27,64 +31,180 @@ const chess = { height: 720, } -describe('Splitea Grid', () => { - const image: Image = chess.path - const tiles = v.parse(TilesSchema, { - mode: 'grid', - rows: 8, columns: 8, +const horizontal = { + path: path.join(IMG_FOLDER, 'chess_horizontal.png'), + width: 720, + height: 90, +} + +const vertical = { + path: path.join(IMG_FOLDER, 'chess_vertical.png'), + width: 90, + height: 720, +} + +describe.concurrent('Horizontal', () => { + + test('Not unique tiles', async () => { + const image = horizontal.path + const options: HorizontalOptions = { columns: 8 } + await expect(horizontalTiles(image, options)).resolves.toHaveLength(8) + delete options.columns + options.width = horizontal.width / 8 + await expect(horizontalTiles(image, options)).resolves.toHaveLength(8) + }) + + test('Unique tiles', async () => { + const image = horizontal.path + const options: HorizontalOptions = { columns: 8, unique: true } + await expect(horizontalTiles(image, options)).resolves.toHaveLength(2) + delete options.columns + options.width = horizontal.width / 8 + await expect(horizontalTiles(image, options)).resolves.toHaveLength(2) + }) + + test('columns or width are not submultiple of image.width', async () => { + const image = horizontal.path + const options: HorizontalOptions = { columns: 7 } + await expect(horizontalTiles(image, options)).rejects.toThrow(SpliteaError) + delete options.columns + options.width = 111 + await expect(horizontalTiles(image, options)).rejects.toThrow(SpliteaError) + }) + + test('store and return buffer', async () => { + const image = horizontal.path + const directory = path.join(__dirname, 'horizontal-test-buffer') + // const filename = 'horizontalTest' + const options: HorizontalOptions = { columns: 8, path: directory } + const tiles = await horizontalTiles(image, options) + expect(tiles).toHaveLength(8) + expect(tiles[1]).toBeInstanceOf(Buffer) + fs.rmSync(directory, { recursive: true }) + }) + + test('store and return file', async () => { + const image = horizontal.path + const directory = path.join(__dirname, 'horizontal-test-file') + const options: HorizontalOptions = { columns: 8, response: 'file', path: directory } + const tiles = await horizontalTiles(image, options) + expect(tiles).toHaveLength(8) + tiles.forEach(tile => expect(fs.existsSync(tile)).toBeTruthy()) + fs.rmSync(directory, { recursive: true }) + }) +}) + +describe.concurrent('Vertical', () => { + + test('Not unique tiles', async () => { + const image = vertical.path + const options: VerticalOptions = { rows: 8 } + await expect(verticalTiles(image, options)).resolves.toHaveLength(8) + delete options.rows + options.height = vertical.height / 8 + await expect(verticalTiles(image, options)).resolves.toHaveLength(8) + }) + + test('Unique tiles', async () => { + const image = vertical.path + const options: VerticalOptions = { rows: 8, unique: true } + await expect(verticalTiles(image, options)).resolves.toHaveLength(6) + delete options.rows + options.height = vertical.height / 8 + await expect(verticalTiles(image, options)).resolves.toHaveLength(6) }) - const store = { - path: path.join(IMG_FOLDER, 'chessFolder'), - name: 'chessTest', - } - const output = v.parse(OutputSchema, {}) - output.response = 'path' - output.store = store - - test('chess grid', async () => { - const imgs = await Splitea(image, tiles, output) - const rows = tiles.rows as number - const columns = tiles.columns as number - expect(imgs).toHaveLength(rows * columns) - await Promise.all(imgs.map(async (img) => { - expect(() => fs.stat(img)).not.toThrowError() - // await fs.rm(img) - })) - await fs.rm(store.path, {recursive: true}) + + test('columns or width are not submultiple of image.width', async () => { + const image = vertical.path + const options: VerticalOptions = { rows: 7 } + await expect(verticalTiles(image, options)).rejects.toThrow(SpliteaError) + delete options.rows + options.height = 111 + await expect(verticalTiles(image, options)).rejects.toThrow(SpliteaError) }) - test('chess grid unique', async () => { - tiles.unique = v.parse(UniqueSchema, {}) - const imgs = await Splitea(image, tiles, output) - expect(imgs).toHaveLength(22) - await Promise.all(imgs.map(async (img) => { - expect(() => fs.stat(img)).not.toThrowError() - await fs.rm(img) - })) - await fs.rm(store.path, {recursive: true}) - }, 100000) - - test('chess grid unique tunning requirement', async () => { - tiles.unique = v.parse(UniqueSchema, { - difference: 0.15, - distance: 0.15, - requirement: 'both', - }) - let imgs = await Splitea(image, tiles, output) - expect(imgs).toHaveLength(11) - await Promise.all(imgs.map(async (img) => { - expect(() => fs.stat(img)).not.toThrowError() - await fs.rm(img) - })) - await fs.rm(store.path, {recursive: true}) - - tiles.unique.requirement = 'one' - imgs = await Splitea(image, tiles, output) - expect(imgs).toHaveLength(3) - await Promise.all(imgs.map(async (img) => { - expect(() => fs.stat(img)).not.toThrowError() - await fs.rm(img) - })) - await fs.rm(store.path, {recursive: true}) - }, 100000) + test('store and return buffer', async () => { + const image = vertical.path + const directory = path.join(__dirname, 'vertical-test-buffer') + // const filename = 'horizontalTest' + const options: VerticalOptions = { rows: 8, path: directory } + const tiles = await verticalTiles(image, options) + expect(tiles).toHaveLength(8) + expect(tiles[1]).toBeInstanceOf(Buffer) + fs.rmSync(directory, { recursive: true }) + }) + + test('store and return file', async () => { + const image = vertical.path + const directory = path.join(__dirname, 'vertical-test-file') + const options: VerticalOptions = { rows: 8, response: 'file', path: directory } + const tiles = await verticalTiles(image, options) + expect(tiles).toHaveLength(8) + tiles.forEach(tile => expect(fs.existsSync(tile)).toBeTruthy()) + fs.rmSync(directory, { recursive: true }) + }) }) + +describe.concurrent('Grid', { timeout: 50000 }, () => { + + test('Not unique tiles', async () => { + const image = chess.path + const options: GridOptions = { rows: 8, columns: 8 } + await expect(gridTiles(image, options)).resolves.toHaveLength(8 * 8) + delete options.rows + delete options.columns + options.width = horizontal.width / 8 + options.height = vertical.height / 8 + await expect(gridTiles(image, options)).resolves.toHaveLength(8 * 8) + }) + + test('Unique tiles', async () => { + const image = chess.path + const options: GridOptions = { rows: 8, columns: 8, unique: true } + await expect(gridTiles(image, options)).resolves.toHaveLength(22) + delete options.rows + delete options.columns + options.width = horizontal.width / 8 + options.height = vertical.height / 8 + await expect(gridTiles(image, options)).resolves.toHaveLength(22) + }) + + test('rows-columns or width-height are not submultiple of image.width-image.size', async () => { + const image = chess.path + const options: GridOptions = { rows: 7, columns: 8 } + await expect(gridTiles(image, options)).rejects.toThrow(SpliteaError) + options.rows = 8 + options.columns = 7 + await expect(gridTiles(image, options)).rejects.toThrow(SpliteaError) + delete options.rows + delete options.columns + options.width = 21 + options.height = 20 + await expect(gridTiles(image, options)).rejects.toThrow(SpliteaError) + options.width = 20 + options.height = 21 + await expect(gridTiles(image, options)).rejects.toThrow(SpliteaError) + }) + + test('store and return buffer', async () => { + const image = chess.path + const directory = path.join(__dirname, 'grid-test-buffer') + // const filename = 'gridTest' + const options: GridOptions = { rows: 8, columns: 8, path: directory } + const tiles = await gridTiles(image, options) + expect(tiles).toHaveLength(8 * 8) + expect(tiles[1]).toBeInstanceOf(Buffer) + fs.rmSync(directory, { recursive: true }) + }) + + test('store and return file', async () => { + const image = chess.path + const directory = path.join(__dirname, 'grid-test-file') + const options: GridOptions = { rows: 8, columns: 8, response: 'file', path: directory } + const tiles = await gridTiles(image, options) + expect(tiles).toHaveLength(8 * 8) + tiles.forEach(tile => expect(fs.existsSync(tile)).toBeTruthy()) + fs.rmSync(directory, { recursive: true }) + }) +}) + diff --git a/tests/tiles.test.ts b/tests/tiles.test.ts deleted file mode 100644 index 3ad2bd0..0000000 --- a/tests/tiles.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { describe, test, expect } from 'vitest' -import * as v from 'valibot' -import { Size,Tiles } from '../src/types' -import { getTilesCoordinates } from '../src/tiles' -import { TilesSchema } from '../src/schemas' - -const forest = { width: 320, height: 224 } - -const satie = { width: 2651, height: 3711 } - -const chess = { width: 720, height: 720 } - -const tilesTest: Tiles = v.parse(TilesSchema, { mode: 'grid' }) - -describe('Get Horizontal Tiles', () => { - const tilesHorizontal: Tiles = { ...tilesTest, mode: 'horizontal' } - - test('Providing width', async () => { - const size: Size = { width: chess.width, height: chess.height } - const tiles = { ...tilesHorizontal } - tiles.width = 360 - expect(getTilesCoordinates(size, tiles).length).toBe(size.width / tiles.width) - tiles.width = size.width - expect(getTilesCoordinates(size, tiles).length).toBe(size.width / tiles.width) - }) - - test('Providing columns', async () => { - const size: Size = { width: chess.width, height: chess.height } - const tiles = { ...tilesHorizontal } - tiles.columns = 2 - expect(getTilesCoordinates(size, tiles).length).toBe(tiles.columns) - tiles.columns = size.width / 2 - expect(getTilesCoordinates(size, tiles).length).toBe(tiles.columns) - }) -}) - -describe('Get Vertical Tiles', () => { - const tilesVertical: Tiles = { ...tilesTest, mode: 'vertical' } - - test('Providing height', async () => { - const size: Size = { width: chess.width, height: chess.height } - const tiles = { ...tilesVertical } - tiles.height = 360 - expect(getTilesCoordinates(size, tiles).length).toBe(size.height / tiles.height) - tiles.height = size.height - expect(getTilesCoordinates(size, tiles).length).toBe(size.height / tiles.height) - }) - - test('Providing rows', async () => { - const size: Size = { width: chess.width, height: chess.height } - const tiles = { ...tilesVertical } - tiles.rows = 2 - let coordinates = getTilesCoordinates(size, tiles) - expect(coordinates.length).toBe(tiles.rows) - tiles.rows = size.height / 2 - expect(getTilesCoordinates(size, tiles).length).toBe(tiles.rows) - }) -}) - -describe('Get Grid Tiles', () => { - const tilesGrid: Tiles = { ...tilesTest, mode: 'grid' } - - test('Providing width + height', async () => { - const size: Size = { width: chess.width, height: chess.height } - const tiles = { ...tilesGrid } - tiles.width = 240 - tiles.height = 360 - let rows = size.height / tiles.height - let columns = size.width / tiles.width - expect(getTilesCoordinates(size, tiles).length).toBe(rows * columns) - tiles.width = 360 - tiles.height = 240 - rows = size.height / tiles.height - columns = size.width / tiles.width - expect(getTilesCoordinates(size, tiles).length).toBe(rows * columns) - }) - - test('Providing rows + columns', async () => { - const size: Size = { width: chess.width, height: chess.height } - const tiles = { ...tilesGrid } - tiles.rows = 3 - tiles.columns = 2 - let rows = tiles.rows - let columns = tiles.columns - expect(getTilesCoordinates(size, tiles).length).toBe(rows * columns) - tiles.rows = 2 - tiles.columns = 3 - rows = tiles.rows - columns = tiles.columns - expect(getTilesCoordinates(size, tiles).length).toBe(rows * columns) - }) -}) diff --git a/tests/utils.test.ts b/tests/utils.test.ts index f0debdb..56cff57 100755 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'vitest' -import { invalidFilename, isSubmultiple } from '../src/utils' +import { validFilename, isSubmultiple } from '../src/utils' describe('Test Utils Module', () => { test('isSubmultiple', () => { @@ -9,16 +9,16 @@ describe('Test Utils Module', () => { expect(isSubmultiple(10, 4)).toBeFalsy() }) - test('invalidFilename', () => { + test('validFilename', () => { // INVALID const invalidFiles = ['foo/bar', 'foo\u0000bar', 'foo\u001Fbar', 'foo*bar', 'foo:bar', 'AUX', 'com1', 'foo\\bar'] invalidFiles.forEach(file => { - expect(invalidFilename(file)).toBeTruthy() + expect(validFilename(file)).toBeFalsy() }) // VALID const validFiles = ['chess.txt', 'chess.jpg', 'chessFolder', 'chessfolder'] validFiles.forEach(file => { - expect(invalidFilename(file)).toBeFalsy() + expect(validFilename(file)).toBeTruthy() }) }) }) diff --git a/tsconfig.json b/tsconfig.json index 7b1643c..7616d8e 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Projects */ // "incremental": true, /* Enable incremental compilation */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ @@ -9,9 +8,8 @@ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ @@ -22,11 +20,10 @@ // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - /* Modules */ - "module": "ESNext", /* Specify what module code is generated. */ + "module": "ESNext", /* Specify what module code is generated. */ "rootDir": "./src", /* Specify the root folder within your source files. */ - "moduleResolution": "Node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "Node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ @@ -35,19 +32,17 @@ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "resolveJsonModule": true, /* Enable importing .json files */ // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ @@ -65,16 +60,14 @@ // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ @@ -83,21 +76,24 @@ // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": ["src"], - "exclude": ["tests"] -} + "include": [ + "src" + ], + "exclude": [ + "tests" + ] +} \ No newline at end of file From 44b61d71df9b2c71067e4bae524865a4547f6f98 Mon Sep 17 00:00:00 2001 From: Cristobal Contreras Rubio Date: Fri, 28 Jun 2024 21:44:28 +0200 Subject: [PATCH 2/4] v1.0.0 --- README.md | 283 +++++++++-- package-lock.json | 1208 ++++++++++++++++++++++++--------------------- package.json | 34 +- 3 files changed, 894 insertions(+), 631 deletions(-) diff --git a/README.md b/README.md index ccef19b..f0a407c 100755 --- a/README.md +++ b/README.md @@ -1,65 +1,250 @@ -# splitea +# SPLITEA -[![build](https://github.com/crisconru/splitea/actions/workflows/publish.yml/badge.svg)](https://github.com/crisconru/splitea/actions/workflows/publish.yml) +[![version](https://img.shields.io/npm/v/splitea)](https://www.npmjs.com/package/splitea) [![Bundle size minified](https://img.shields.io/bundlephobia/min/splitea/latest)](https://img.shields.io/bundlephobia/min/splitea/latest) [![build](https://github.com/crisconru/splitea/actions/workflows/publish.yml/badge.svg)](https://github.com/crisconru/splitea/actions/workflows/publish.yml) -It is a tool to split images into tiles or pieces. The idea is based on the project [image-splitter](https://github.com/achimoraites/image-splitter). +It is a tool to split images into pieces or tiles. Images can be splitted in three ways: -It has just one entry point with three arguments and the response is an array of tiles: +- Horizontal + + ```typescript + import { horizontalTiles } from 'splitea' -- `image`: The image to split. It is a string path or a buffer -- `tiles`: All the information to create the tiles - - `mode`: How to cut the images - - `rows` / `height`: Number of rows to be cut or height in px of each tile - - `columns` / `width`: Number of columns to be cut or width in px of each tile - - `unique`: Options to specify if you just want tiles not repeated - - `distance`: Max distance between tiles to be filtered - - `difference`: Max difference between tiles to be filtered - - `requirement`: Condition to be filtered, one of them or both -- `output`: Information for the created tiles - - `response`: Type of output, an array of Buffer (default) or the path of each tile - - `store`: Information if you want to store the tiles in the computer or not - - `path`: Where to store - - `name`: Filename pattern - - `extension`: Extension of the tiles + const tiles = await horizontalTiles(image, options) + ``` -For the tiles there are only these possible combinations: +- Vertical + + ```typescript + import { verticalTiles } from 'splitea' -- `mode` = `"horizontal"` + (`columns` | `width`) -- `mode` = `"vertical"` + (`rows` | `height`) -- `mode` = `"grid"` + (`columns` + `rows` | `width` + `height`) + const tiles = await verticalTiles(image, options) + ``` -`columns` and `rows` are natural number while `width` and `height` are in pixels. +- Grid + + ```typescript + import { gridTiles } from 'splitea' -For the output the default type is buffer and it is not stored. If you want to store the tiles in the computer you'll have to add store object. + const tiles = await gridTiles(image, options) + ``` -If you select response as `"path"` you have to add store object too. +All the functions has two parameters: + +1. `image`: A `string` with the path of the image or a `Buffer` with the content of the image. +2. `options`: An `object` with all the options available. + +Below there are sections related to each split mode in a simple way. +If you want to know more details about `options` go to the last section. + +## Horizontal + +Image can be split by columns or width, but not both. + +Get tiles by columns + +```typescript +import { horizontalTiles } from 'splitea' + +const numberOfColumns: number +// ... +const tiles = await horizontalTiles(image, { columns: numberOfColumns }) +``` + +Get tiles by width + +```typescript +import { horizontalTiles } from 'splitea' + +const tilesWidth: number +// ... +const tiles = await horizontalTiles(image, { width: tilesWidth }) +``` + +Related to options ```typescript -import { Splitea } from splitea - -type Image = string | Buffer - -type Tiles = { - mode: "horizontal" | "vertical" | "grid", - rows?: number, - columns?: number, - width?: number, - height?: number, - unique?: { - distance: number, - difference: number, - requirement: "one" | "both" - } +interface HorizontalOptions extends Options { + columns?: number + width?: number } +``` + +## Vertical -type Output = { - response: "buffer" | "path", - store?: { - path: string, - name: string, - extension?: "jpg" | "jpeg" | "png" | "bmp" | "gif" | "tiff" - } +Image can be split by rows or height, but not both. + +Get tiles by rows + +```typescript +import { verticalTiles } from 'splitea' + +const numberOfRows: number +// ... +const tiles = await verticalTiles(image, { rows: numberOfRows }) +``` + +Get tiles by width + +```typescript +import { verticalTiles } from 'splitea' + +const tilesHeight: number +// ... +const tiles = await verticalTiles(image, { height: tilesHeight }) +``` + +Related to options + +```typescript +interface VerticalOptions extends Options { + rows?: number + height?: number } +``` + +## Grid + +Image can be split by rows & columns or width & height, but not both. + +Get tiles by rows & columns + +```typescript +import { gridTiles } from 'splitea' + +const numberOfRows: number +const numberOfColumns: number +// ... +const tiles = await gridTiles(image, { rows: numberOfRows, columns: numberOfColumns }) +``` + +Get tiles by width & height + +```typescript +import { gridTiles } from 'splitea' + +const tilesWidth: number +const tilesHeight: number +// ... +const tiles = await gridTiles(image, { width: tilesWidth, height: tilesHeight }) +``` + +Related to options + +```typescript +interface GridOptions extends Options { + rows?: number + columns?: number + width?: number + height?: number +} +``` + +## Options + +It is an object which gives you superpowers + +```typescript +interface Options { + // Output Options + response: 'buffer' | 'file' // 'buffer' by default + // Store options + path?: string + filename?: string + extension?: 'png' | 'jpg' | 'jpeg' | 'giff' | 'tiff' + // Unique options + unique?: boolean // false by default + uniqueRequirement: 'all' | 'distance' | 'difference' // 'all' by default + distance?: number // Float between 0 - 1 + difference?: number // Float between 0 - 1 +} +``` + +It can be divided in 3 cathegories: + +1. Response: What to return. +2. Store Options: If you want to store the tiles in your computer. +3. Unique Options: If you want all tiles or non repeated tiles. + +### Output Options + +```typescript +interface Options { + response: 'buffer' | 'file' // 'buffer' by default +} +``` + +The output is an array, `Buffer[]` or `string[]`. + +The property for that is `response`. It can have just two values: `'buffer'` or `'file'`. + +- `response = 'buffer'` then the output is `Buffer[]`. +- `response = 'file'` then the output is `string[]` where each element is the absolute path to each tile. + +If `response = 'file'` then splitea needs to store in disk, so this implies to enter +at `path` property of the Store Options (check in the next section). + +### Store Options + +```typescript +interface Options { + path?: string + filename?: string + extension?: 'png' | 'jpg' | 'jpeg' | 'giff' | 'tiff' +} +``` + +Splitea can store in disk all the tiles. If the output are the images in disk (`response = 'file'`), at least `path` is mandatory. + +- `path`: Absolute path where tiles must be stored. +- `filename`: The main name of each tile. If none is provided, it has the value `'tile'`. +- `extension`: Image extension for all tiles. If none is provided, it has the value of the image extension. + +### Unique Options + +```typescript +interface Options { + unique?: boolean // false by default + uniqueRequirement: 'all' | 'distance' | 'difference' // 'all' by default + distance?: number // Float between 0 - 1, 0.01 by default + difference?: number // Float between 0 - 1, 0.01 by default +} +``` + +Splitea give you all the tiles by default. But it can give you only the non repeated tiles too. +To get that `unique = false`. + +To check if the images are non repeated it uses two concepts: + +- Distance means the Hamming distance of the pHash (percentual Hash algorithm). +- Difference means the difference between pixels using PixelMatch. + +- `unique`: By default to false, if you need non repeated it needs to be true. +- `uniqueRequirement`: By default is `'all'`, which means to use distance and difference. If you only want to use one of them, the value should be `'distance'` or `'difference'`. +- `distance`: It is a float number between 0 and 1. It has `0.01` by default. +- `difference`: It is a float number between 0 and 1. It has `0.01` by default. + +## Examples + +```typescript +import { horizontalTiles, VerticalTiles, gridTiles } from 'splitea' +// ... +import fs from 'node:fs' + +const imagePath = '/path/to/my/image' +const imageBuffer = fs.readFileSync(imagePath) +// ... +// Horizontal Tiles: 4 tiles +const horizontal = horizontalTiles(imagePath, { columns: 4 }) +// Vertical Tiles: 5 tiles non-repeated +const vertical = verticalTiles(imageBuffer, { rows: 4, unique: true }) +// Grid Tiles: Tiles of 40 x 60 px and stored in disk +const gridNoUniqueBuffer = gridTiles( + imagePath, + { width: 40, height: 60, path: '/store/path/grid', img: 'images' } +) +const gridUniqueImagesPath = gridTiles( + imageBuffer, + { width: 40, height: 60 , response: 'file', path: '/store/path/gridUnique', img: 'gridtiles' } +) -const tiles: Promise = await Splitea(image: Image, tiles: Tiles, output: Output) ``` diff --git a/package-lock.json b/package-lock.json index e670773..a227b04 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,27 @@ { "name": "splitea", - "version": "0.0.2", + "version": "0.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "splitea", - "version": "0.0.2", + "version": "0.0.5", "license": "MIT", "dependencies": { - "jimp": "^0.22.10", - "valibot": "^0.28.1", + "@schemasjs/valibot-numbers": "^1.0.12", + "@schemasjs/validator": "^1.0.0", + "jimp": "^0.22.12", + "valibot": "^0.35.0", "valid-filename": "^4.0.0" }, "devDependencies": { "@vitest/coverage-v8": "^1.6.0", "ts-node-dev": "^2.0.0", "ts-standard": "^12.0.2", - "tsup": "^8.0.2", - "typescript": "^5.3.3", - "vitest": "^1.3.0" + "tsup": "^8.1.0", + "typescript": "^5.5.2", + "vitest": "^1.6.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -669,11 +671,11 @@ } }, "node_modules/@jimp/bmp": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.10.tgz", - "integrity": "sha512-1UXRl1Nw1KptZ1r0ANqtXOst9vGH51dq7keVKQzyyTO2lz4dOaezS9StuSTNh+RmiHg/SVPaFRpPfB0S/ln4Kg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.12.tgz", + "integrity": "sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "bmp-js": "^0.1.0" }, "peerDependencies": { @@ -681,11 +683,11 @@ } }, "node_modules/@jimp/core": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.10.tgz", - "integrity": "sha512-ZKyrehVy6wu1PnBXIUpn/fXmyMRQiVSbvHDubgXz4bfTOao3GiOurKHjByutQIgozuAN6ZHWiSge1dKA+dex3w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.12.tgz", + "integrity": "sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "any-base": "^1.1.0", "buffer": "^5.2.0", "exif-parser": "^0.1.12", @@ -696,19 +698,19 @@ } }, "node_modules/@jimp/custom": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.10.tgz", - "integrity": "sha512-sPZkUYe1hu0iIgNisjizxPJqq2vaaKvkCkPoXq2U6UV3ZA1si/WVdrg25da3IcGIEV+83AoHgM8TvqlLgrCJsg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz", + "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==", "dependencies": { - "@jimp/core": "^0.22.10" + "@jimp/core": "^0.22.12" } }, "node_modules/@jimp/gif": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.10.tgz", - "integrity": "sha512-yEX2dSpamvkSx1PPDWGnKeWDrBz0vrCKjVG/cn4Zr68MRRT75tbZIeOrBa+RiUpY3ho5ix7d36LkYvt3qfUIhQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.12.tgz", + "integrity": "sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "gifwrap": "^0.10.1", "omggif": "^1.0.9" }, @@ -717,11 +719,11 @@ } }, "node_modules/@jimp/jpeg": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.10.tgz", - "integrity": "sha512-6bu98pAcVN4DY2oiDLC4TOgieX/lZrLd1tombWZOFCN5PBmqaHQxm7IUmT+Wj4faEvh8QSHgVLSA+2JQQRJWVA==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.12.tgz", + "integrity": "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "jpeg-js": "^0.4.4" }, "peerDependencies": { @@ -729,44 +731,44 @@ } }, "node_modules/@jimp/plugin-blit": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.10.tgz", - "integrity": "sha512-6EI8Sl+mxYHEIy6Yteh6eknD+EZguKpNdr3sCKxNezmLR0+vK99vHcllo6uGSjXXiwtwS67Xqxn8SsoatL+UJQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz", + "integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-blur": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.10.tgz", - "integrity": "sha512-4XRTWuPVdMXJeclJMisXPGizeHtTryVaVV5HnuQXpKqIZtzXReCCpNGH8q/i0kBQOQMXhGWS3mpqOEwtpPePKw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.12.tgz", + "integrity": "sha512-S0vJADTuh1Q9F+cXAwFPlrKWzDj2F9t/9JAbUvaaDuivpyWuImEKXVz5PUZw2NbpuSHjwssbTpOZ8F13iJX4uw==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-circle": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.10.tgz", - "integrity": "sha512-mhcwTO1ywRxiCgtLGge6tDDIDPlX6qkI3CY+BjgGG/XhVHccCddXgOGLdlf+5OuKIEF2Nqs0V01LQEQIJFTmEw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.12.tgz", + "integrity": "sha512-SWVXx1yiuj5jZtMijqUfvVOJBwOifFn0918ou4ftoHgegc5aHWW5dZbYPjvC9fLpvz7oSlptNl2Sxr1zwofjTg==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-color": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.10.tgz", - "integrity": "sha512-e4t3L7Kedd96E0x1XjsTM6NcgulKUU66HdFTao7Tc9FYJRFSlttARZ/C6LEryGDm/i69R6bJEpo7BkNz0YL55Q==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz", + "integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "tinycolor2": "^1.6.0" }, "peerDependencies": { @@ -774,11 +776,11 @@ } }, "node_modules/@jimp/plugin-contain": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.10.tgz", - "integrity": "sha512-eP8KrzctuEoqibQAxi9WhbnoRosydhiwg+IYya3dKuKDBTrD9UHt+ERlPQ/lTNWHzV/l4S1ntV3r9s9saJgsXA==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.12.tgz", + "integrity": "sha512-Eo3DmfixJw3N79lWk8q/0SDYbqmKt1xSTJ69yy8XLYQj9svoBbyRpSnHR+n9hOw5pKXytHwUW6nU4u1wegHNoQ==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -788,11 +790,11 @@ } }, "node_modules/@jimp/plugin-cover": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.10.tgz", - "integrity": "sha512-kJCwL5T1igfa0InCfkE7bBeqg26m46aoRt10ug+rvm11P6RrvRMGrgINFyIKB+mnB7CiyBN/MOula1CvLhSInQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.12.tgz", + "integrity": "sha512-z0w/1xH/v/knZkpTNx+E8a7fnasQ2wHG5ze6y5oL2dhH1UufNua8gLQXlv8/W56+4nJ1brhSd233HBJCo01BXA==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -802,55 +804,55 @@ } }, "node_modules/@jimp/plugin-crop": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.10.tgz", - "integrity": "sha512-BOZ+YGaZlhU7c5ye65RxikicXH0Ki0It6/XHISvipR5WZrfjLjL2Ke20G+AGnwBQc76gKenVcMXVUCnEjtZV+Q==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz", + "integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-displace": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.10.tgz", - "integrity": "sha512-llNiWWMTKISDXt5+cXI0GaFmZWAjlT+4fFLYf4eXquuL/9wZoQsEBhv2GdGd48mkiS8jZq1Nnb2Q4ehEPTvrzw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.12.tgz", + "integrity": "sha512-qpRM8JRicxfK6aPPqKZA6+GzBwUIitiHaZw0QrJ64Ygd3+AsTc7BXr+37k2x7QcyCvmKXY4haUrSIsBug4S3CA==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-dither": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.10.tgz", - "integrity": "sha512-05WLmeV5M+P/0FS+bWf13hMew2X0oa8w9AtmevL2UyA/5GqiyvP2Xm5WfGQ8oFiiMvpnL6RFomJQOZtWca0C2w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.12.tgz", + "integrity": "sha512-jYgGdSdSKl1UUEanX8A85v4+QUm+PE8vHFwlamaKk89s+PXQe7eVE3eNeSZX4inCq63EHL7cX580dMqkoC3ZLw==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-fisheye": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.10.tgz", - "integrity": "sha512-InjiXvc7Gkzrx8VWtU97kDqV7ENnhHGPULymJWeZaF2aicud9Fpk4iCtd/DcZIrk7Cbe60A8RwNXN00HXIbSCg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.12.tgz", + "integrity": "sha512-LGuUTsFg+fOp6KBKrmLkX4LfyCy8IIsROwoUvsUPKzutSqMJnsm3JGDW2eOmWIS/jJpPaeaishjlxvczjgII+Q==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-flip": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.10.tgz", - "integrity": "sha512-42GkGtTHWnhnwTMPVK/kXObZbkYIpQWfuIfy5EMEMk6zRj05zpv4vsjkKWfuemweZINwfvD7wDJF7FVFNNcZZg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.12.tgz", + "integrity": "sha512-m251Rop7GN8W0Yo/rF9LWk6kNclngyjIJs/VXHToGQ6EGveOSTSQaX2Isi9f9lCDLxt+inBIb7nlaLLxnvHX8Q==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -858,55 +860,55 @@ } }, "node_modules/@jimp/plugin-gaussian": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.10.tgz", - "integrity": "sha512-ykrG/6lTp9Q5YA8jS5XzwMHtRxb9HOFMgtmnrUZ8kU+BK8REecfy9Ic5BUEOjCYvS1a/xLsnrZQU07iiYxBxFg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.12.tgz", + "integrity": "sha512-sBfbzoOmJ6FczfG2PquiK84NtVGeScw97JsCC3rpQv1PHVWyW+uqWFF53+n3c8Y0P2HWlUjflEla2h/vWShvhg==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-invert": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.10.tgz", - "integrity": "sha512-d8j9BlUJYs/c994t4azUWSWmQq4LLPG4ecm8m6SSNqap+S/HlVQGqjYhJEBbY9EXkOTYB9vBL9bqwSM1Rr6paA==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.12.tgz", + "integrity": "sha512-N+6rwxdB+7OCR6PYijaA/iizXXodpxOGvT/smd/lxeXsZ/empHmFFFJ/FaXcYh19Tm04dGDaXcNF/dN5nm6+xQ==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-mask": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.10.tgz", - "integrity": "sha512-yRBs1230XZkz24uFTdTcSlZ0HXZpIWzM3iFQN56MzZ7USgdVZjPPDCQ8I9RpqfZ36nDflQkUO0wV7ucsi4ogow==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.12.tgz", + "integrity": "sha512-4AWZg+DomtpUA099jRV8IEZUfn1wLv6+nem4NRJC7L/82vxzLCgXKTxvNvBcNmJjT9yS1LAAmiJGdWKXG63/NA==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-normalize": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.10.tgz", - "integrity": "sha512-Wk9GX6eJMchX/ZAazVa70Fagu+OXMvHiPY+HrcEwcclL+p1wo8xAHEsf9iKno7Ja4EU9lLhbBRY5hYJyiKMEkg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.12.tgz", + "integrity": "sha512-0So0rexQivnWgnhacX4cfkM2223YdExnJTTy6d06WbkfZk5alHUx8MM3yEzwoCN0ErO7oyqEWRnEkGC+As1FtA==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-print": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.10.tgz", - "integrity": "sha512-1U3VloIR+beE1kWPdGEJMiE2h1Do29iv3w8sBbvPyRP4qXxRFcDpmCGtctsrKmb1krlBFlj8ubyAY90xL+5n9w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.12.tgz", + "integrity": "sha512-c7TnhHlxm87DJeSnwr/XOLjJU/whoiKYY7r21SbuJ5nuH+7a78EW1teOaj5gEr2wYEd7QtkFqGlmyGXY/YclyQ==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "load-bmfont": "^1.4.1" }, "peerDependencies": { @@ -915,22 +917,22 @@ } }, "node_modules/@jimp/plugin-resize": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.10.tgz", - "integrity": "sha512-ixomxVcnAONXDgaq0opvAx4UAOiEhOA/tipuhFFOvPKFd4yf1BAnEviB5maB0SBHHkJXPUSzDp/73xVTMGSe7g==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz", + "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "node_modules/@jimp/plugin-rotate": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.10.tgz", - "integrity": "sha512-eeFX8dnRyf3LAdsdXWKWuN18hLRg8zy1cP0cP9rHzQVWRK7ck/QsLxK1vHq7MADGwQalNaNTJ9SQxH6c8mz6jw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz", + "integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -940,11 +942,11 @@ } }, "node_modules/@jimp/plugin-scale": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.10.tgz", - "integrity": "sha512-TG/H0oUN69C9ArBCZg4PmuoixFVKIiru8282KzSB/Tp1I0xwX0XLTv3dJ5pobPlIgPcB+TmD4xAIdkCT4rtWxg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz", + "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -952,11 +954,11 @@ } }, "node_modules/@jimp/plugin-shadow": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.10.tgz", - "integrity": "sha512-TN9xm6fI7XfxbMUQqFPZjv59Xdpf0tSiAQdINB4g6pJMWiVANR/74OtDONoy3KKpenu5Y38s+FkrtID/KcQAhw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.12.tgz", + "integrity": "sha512-FX8mTJuCt7/3zXVoeD/qHlm4YH2bVqBuWQHXSuBK054e7wFRnRnbSLPUqAwSeYP3lWqpuQzJtgiiBxV3+WWwTg==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -965,11 +967,11 @@ } }, "node_modules/@jimp/plugin-threshold": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.10.tgz", - "integrity": "sha512-DA2lSnU0TgIRbAgmXaxroYw3Ad6J2DOFEoJp0NleSm2h3GWbZEE5yW9U2B6hD3iqn4AenG4E2b2WzHXZyzSutw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.12.tgz", + "integrity": "sha512-4x5GrQr1a/9L0paBC/MZZJjjgjxLYrqSmWd+e+QfAEPvmRxdRoQ5uKEuNgXnm9/weHQBTnQBQsOY2iFja+XGAw==", "dependencies": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5", @@ -978,31 +980,31 @@ } }, "node_modules/@jimp/plugins": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.10.tgz", - "integrity": "sha512-KDMZyM6pmvS8freB+UBLko1TO/k4D7URS/nphCozuH+P7i3UMe7NdckXKJ8u+WD6sqN0YFYvBehpkpnUiw/91w==", - "dependencies": { - "@jimp/plugin-blit": "^0.22.10", - "@jimp/plugin-blur": "^0.22.10", - "@jimp/plugin-circle": "^0.22.10", - "@jimp/plugin-color": "^0.22.10", - "@jimp/plugin-contain": "^0.22.10", - "@jimp/plugin-cover": "^0.22.10", - "@jimp/plugin-crop": "^0.22.10", - "@jimp/plugin-displace": "^0.22.10", - "@jimp/plugin-dither": "^0.22.10", - "@jimp/plugin-fisheye": "^0.22.10", - "@jimp/plugin-flip": "^0.22.10", - "@jimp/plugin-gaussian": "^0.22.10", - "@jimp/plugin-invert": "^0.22.10", - "@jimp/plugin-mask": "^0.22.10", - "@jimp/plugin-normalize": "^0.22.10", - "@jimp/plugin-print": "^0.22.10", - "@jimp/plugin-resize": "^0.22.10", - "@jimp/plugin-rotate": "^0.22.10", - "@jimp/plugin-scale": "^0.22.10", - "@jimp/plugin-shadow": "^0.22.10", - "@jimp/plugin-threshold": "^0.22.10", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.12.tgz", + "integrity": "sha512-yBJ8vQrDkBbTgQZLty9k4+KtUQdRjsIDJSPjuI21YdVeqZxYywifHl4/XWILoTZsjTUASQcGoH0TuC0N7xm3ww==", + "dependencies": { + "@jimp/plugin-blit": "^0.22.12", + "@jimp/plugin-blur": "^0.22.12", + "@jimp/plugin-circle": "^0.22.12", + "@jimp/plugin-color": "^0.22.12", + "@jimp/plugin-contain": "^0.22.12", + "@jimp/plugin-cover": "^0.22.12", + "@jimp/plugin-crop": "^0.22.12", + "@jimp/plugin-displace": "^0.22.12", + "@jimp/plugin-dither": "^0.22.12", + "@jimp/plugin-fisheye": "^0.22.12", + "@jimp/plugin-flip": "^0.22.12", + "@jimp/plugin-gaussian": "^0.22.12", + "@jimp/plugin-invert": "^0.22.12", + "@jimp/plugin-mask": "^0.22.12", + "@jimp/plugin-normalize": "^0.22.12", + "@jimp/plugin-print": "^0.22.12", + "@jimp/plugin-resize": "^0.22.12", + "@jimp/plugin-rotate": "^0.22.12", + "@jimp/plugin-scale": "^0.22.12", + "@jimp/plugin-shadow": "^0.22.12", + "@jimp/plugin-threshold": "^0.22.12", "timm": "^1.6.1" }, "peerDependencies": { @@ -1010,11 +1012,11 @@ } }, "node_modules/@jimp/png": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.10.tgz", - "integrity": "sha512-RYinU7tZToeeR2g2qAMn42AU+8OUHjXPKZZ9RkmoL4bguA1xyZWaSdr22/FBkmnHhOERRlr02KPDN1OTOYHLDQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.12.tgz", + "integrity": "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==", "dependencies": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "pngjs": "^6.0.0" }, "peerDependencies": { @@ -1022,9 +1024,9 @@ } }, "node_modules/@jimp/tiff": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.10.tgz", - "integrity": "sha512-OaivlSYzpNTHyH/h7pEtl3A7F7TbsgytZs52GLX/xITW92ffgDgT6PkldIrMrET6ERh/hdijNQiew7IoEEr2og==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.12.tgz", + "integrity": "sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg==", "dependencies": { "utif2": "^4.0.1" }, @@ -1033,15 +1035,15 @@ } }, "node_modules/@jimp/types": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.10.tgz", - "integrity": "sha512-u/r+XYzbCx4zZukDmxx8S0er3Yq3iDPI6+31WKX0N18i2qPPJYcn8qwIFurfupRumGvJ8SlGLCgt/T+Y8zzUIw==", - "dependencies": { - "@jimp/bmp": "^0.22.10", - "@jimp/gif": "^0.22.10", - "@jimp/jpeg": "^0.22.10", - "@jimp/png": "^0.22.10", - "@jimp/tiff": "^0.22.10", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.12.tgz", + "integrity": "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==", + "dependencies": { + "@jimp/bmp": "^0.22.12", + "@jimp/gif": "^0.22.12", + "@jimp/jpeg": "^0.22.12", + "@jimp/png": "^0.22.12", + "@jimp/tiff": "^0.22.12", "timm": "^1.6.1" }, "peerDependencies": { @@ -1049,9 +1051,9 @@ } }, "node_modules/@jimp/utils": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.10.tgz", - "integrity": "sha512-ztlOK9Mm2iLG2AMoabzM4i3WZ/FtshcgsJCbZCRUs/DKoeS2tySRJTnQZ1b7Roq0M4Ce+FUAxnCAcBV0q7PH9w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.12.tgz", + "integrity": "sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==", "dependencies": { "regenerator-runtime": "^0.13.3" } @@ -1357,6 +1359,29 @@ "win32" ] }, + "node_modules/@schemasjs/valibot-numbers": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@schemasjs/valibot-numbers/-/valibot-numbers-1.0.12.tgz", + "integrity": "sha512-DxZB0wnPk+gcxe3r195AYnRy0Eodrxnw7DYCOjKlvuZE+QGNwyQz3kYa5gaIeDhfK/rsVQYtk93CLzEoCVzkkw==", + "peerDependencies": { + "valibot": "^0.33.3" + } + }, + "node_modules/@schemasjs/validator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@schemasjs/validator/-/validator-1.0.0.tgz", + "integrity": "sha512-IrRWRAUIFLMdQ0s2ZBgebVMOG86MuOMMsSYI9U0+hkzE2OL5WfDw00sneoI7yhQs79EbtqYrkF+GgHNk3Iz4lQ==", + "optionalDependencies": { + "valibot": "^0.32.0", + "zod": "^3.23.8" + } + }, + "node_modules/@schemasjs/validator/node_modules/valibot": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.32.0.tgz", + "integrity": "sha512-FXBnJl4bNOmeg7lQv+jfvo/wADsRBN8e9C3r+O77Re3dEnDma8opp7p4hcIbF7XJJ30h/5SVohdjer17/sHOsQ==", + "optional": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2049,12 +2074,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3309,9 +3334,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4198,13 +4223,13 @@ } }, "node_modules/jimp": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.10.tgz", - "integrity": "sha512-lCaHIJAgTOsplyJzC1w/laxSxrbSsEBw4byKwXgUdMmh+ayPsnidTblenQm+IvhIs44Gcuvlb6pd2LQ0wcKaKg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.12.tgz", + "integrity": "sha512-R5jZaYDnfkxKJy1dwLpj/7cvyjxiclxU3F4TrI/J4j2rS0niq6YDUMoPn5hs8GDpO+OZGo7Ky057CRtWesyhfg==", "dependencies": { - "@jimp/custom": "^0.22.10", - "@jimp/plugins": "^0.22.10", - "@jimp/types": "^0.22.10", + "@jimp/custom": "^0.22.12", + "@jimp/plugins": "^0.22.12", + "@jimp/types": "^0.22.12", "regenerator-runtime": "^0.13.3" } }, @@ -4609,9 +4634,9 @@ "dev": true }, "node_modules/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -4879,12 +4904,12 @@ "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==" }, "node_modules/parse-bmfont-xml": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", - "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", "dependencies": { "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.4.5" + "xml2js": "^0.5.0" } }, "node_modules/parse-headers": { @@ -4989,7 +5014,8 @@ "node_modules/phin": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", - "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." }, "node_modules/picocolors": { "version": "1.0.0", @@ -5553,9 +5579,9 @@ } }, "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/semver": { "version": "7.5.4", @@ -6402,16 +6428,16 @@ "dev": true }, "node_modules/tsup": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.0.2.tgz", - "integrity": "sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.1.0.tgz", + "integrity": "sha512-UFdfCAXukax+U6KzeTNO2kAARHcWxmKsnvSPXUcfA1D+kU05XDccCrkffCQpFaWDsZfV0jMyTsxU39VfCp6EOg==", "dev": true, "dependencies": { "bundle-require": "^4.0.0", "cac": "^6.7.12", "chokidar": "^3.5.1", "debug": "^4.3.1", - "esbuild": "^0.19.2", + "esbuild": "^0.21.4", "execa": "^5.0.0", "globby": "^11.0.3", "joycon": "^3.0.1", @@ -6451,9 +6477,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -6467,9 +6493,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -6483,9 +6509,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -6499,9 +6525,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -6515,9 +6541,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -6531,9 +6557,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -6547,9 +6573,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -6563,9 +6589,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -6579,9 +6605,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -6595,9 +6621,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -6611,9 +6637,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -6627,9 +6653,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -6643,9 +6669,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -6659,9 +6685,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -6675,9 +6701,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -6691,9 +6717,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -6707,9 +6733,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -6723,9 +6749,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -6739,9 +6765,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -6755,9 +6781,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -6771,9 +6797,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -6787,9 +6813,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -6803,9 +6829,9 @@ } }, "node_modules/tsup/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -6819,9 +6845,9 @@ } }, "node_modules/tsup/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -6831,29 +6857,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/tsup/node_modules/execa": { @@ -7103,9 +7129,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -7172,9 +7198,9 @@ "dev": true }, "node_modules/valibot": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.28.1.tgz", - "integrity": "sha512-zQnjwNJuXk6362Leu0+4eFa/SMwRom3/hEvH6s1EGf3oXIPbo2WFKDra9ymnbVh3clLRvd8hw4sKF5ruI2Lyvw==" + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.35.0.tgz", + "integrity": "sha512-+i2aCRkReTrd5KBN/dW2BrPOvFnU5LXTV2xjZnjnqUIO8YUx6P2+MgRrkwF2FhkexgyKq/NIZdPdknhHf5A/Ww==" }, "node_modules/valid-filename": { "version": "4.0.0", @@ -7338,9 +7364,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-fetch": { - "version": "3.6.17", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", - "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==" + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" }, "node_modules/whatwg-url": { "version": "5.0.0", @@ -7543,9 +7569,9 @@ "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==" }, "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -7608,6 +7634,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -7975,20 +8010,20 @@ } }, "@jimp/bmp": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.10.tgz", - "integrity": "sha512-1UXRl1Nw1KptZ1r0ANqtXOst9vGH51dq7keVKQzyyTO2lz4dOaezS9StuSTNh+RmiHg/SVPaFRpPfB0S/ln4Kg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.12.tgz", + "integrity": "sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "bmp-js": "^0.1.0" } }, "@jimp/core": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.10.tgz", - "integrity": "sha512-ZKyrehVy6wu1PnBXIUpn/fXmyMRQiVSbvHDubgXz4bfTOao3GiOurKHjByutQIgozuAN6ZHWiSge1dKA+dex3w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.12.tgz", + "integrity": "sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "any-base": "^1.1.0", "buffer": "^5.2.0", "exif-parser": "^0.1.12", @@ -7999,265 +8034,265 @@ } }, "@jimp/custom": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.10.tgz", - "integrity": "sha512-sPZkUYe1hu0iIgNisjizxPJqq2vaaKvkCkPoXq2U6UV3ZA1si/WVdrg25da3IcGIEV+83AoHgM8TvqlLgrCJsg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz", + "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==", "requires": { - "@jimp/core": "^0.22.10" + "@jimp/core": "^0.22.12" } }, "@jimp/gif": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.10.tgz", - "integrity": "sha512-yEX2dSpamvkSx1PPDWGnKeWDrBz0vrCKjVG/cn4Zr68MRRT75tbZIeOrBa+RiUpY3ho5ix7d36LkYvt3qfUIhQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.12.tgz", + "integrity": "sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "gifwrap": "^0.10.1", "omggif": "^1.0.9" } }, "@jimp/jpeg": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.10.tgz", - "integrity": "sha512-6bu98pAcVN4DY2oiDLC4TOgieX/lZrLd1tombWZOFCN5PBmqaHQxm7IUmT+Wj4faEvh8QSHgVLSA+2JQQRJWVA==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.12.tgz", + "integrity": "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "jpeg-js": "^0.4.4" } }, "@jimp/plugin-blit": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.10.tgz", - "integrity": "sha512-6EI8Sl+mxYHEIy6Yteh6eknD+EZguKpNdr3sCKxNezmLR0+vK99vHcllo6uGSjXXiwtwS67Xqxn8SsoatL+UJQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz", + "integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-blur": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.10.tgz", - "integrity": "sha512-4XRTWuPVdMXJeclJMisXPGizeHtTryVaVV5HnuQXpKqIZtzXReCCpNGH8q/i0kBQOQMXhGWS3mpqOEwtpPePKw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.12.tgz", + "integrity": "sha512-S0vJADTuh1Q9F+cXAwFPlrKWzDj2F9t/9JAbUvaaDuivpyWuImEKXVz5PUZw2NbpuSHjwssbTpOZ8F13iJX4uw==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-circle": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.10.tgz", - "integrity": "sha512-mhcwTO1ywRxiCgtLGge6tDDIDPlX6qkI3CY+BjgGG/XhVHccCddXgOGLdlf+5OuKIEF2Nqs0V01LQEQIJFTmEw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.12.tgz", + "integrity": "sha512-SWVXx1yiuj5jZtMijqUfvVOJBwOifFn0918ou4ftoHgegc5aHWW5dZbYPjvC9fLpvz7oSlptNl2Sxr1zwofjTg==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-color": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.10.tgz", - "integrity": "sha512-e4t3L7Kedd96E0x1XjsTM6NcgulKUU66HdFTao7Tc9FYJRFSlttARZ/C6LEryGDm/i69R6bJEpo7BkNz0YL55Q==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz", + "integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "tinycolor2": "^1.6.0" } }, "@jimp/plugin-contain": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.10.tgz", - "integrity": "sha512-eP8KrzctuEoqibQAxi9WhbnoRosydhiwg+IYya3dKuKDBTrD9UHt+ERlPQ/lTNWHzV/l4S1ntV3r9s9saJgsXA==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.12.tgz", + "integrity": "sha512-Eo3DmfixJw3N79lWk8q/0SDYbqmKt1xSTJ69yy8XLYQj9svoBbyRpSnHR+n9hOw5pKXytHwUW6nU4u1wegHNoQ==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-cover": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.10.tgz", - "integrity": "sha512-kJCwL5T1igfa0InCfkE7bBeqg26m46aoRt10ug+rvm11P6RrvRMGrgINFyIKB+mnB7CiyBN/MOula1CvLhSInQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.12.tgz", + "integrity": "sha512-z0w/1xH/v/knZkpTNx+E8a7fnasQ2wHG5ze6y5oL2dhH1UufNua8gLQXlv8/W56+4nJ1brhSd233HBJCo01BXA==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-crop": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.10.tgz", - "integrity": "sha512-BOZ+YGaZlhU7c5ye65RxikicXH0Ki0It6/XHISvipR5WZrfjLjL2Ke20G+AGnwBQc76gKenVcMXVUCnEjtZV+Q==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz", + "integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-displace": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.10.tgz", - "integrity": "sha512-llNiWWMTKISDXt5+cXI0GaFmZWAjlT+4fFLYf4eXquuL/9wZoQsEBhv2GdGd48mkiS8jZq1Nnb2Q4ehEPTvrzw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.12.tgz", + "integrity": "sha512-qpRM8JRicxfK6aPPqKZA6+GzBwUIitiHaZw0QrJ64Ygd3+AsTc7BXr+37k2x7QcyCvmKXY4haUrSIsBug4S3CA==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-dither": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.10.tgz", - "integrity": "sha512-05WLmeV5M+P/0FS+bWf13hMew2X0oa8w9AtmevL2UyA/5GqiyvP2Xm5WfGQ8oFiiMvpnL6RFomJQOZtWca0C2w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.12.tgz", + "integrity": "sha512-jYgGdSdSKl1UUEanX8A85v4+QUm+PE8vHFwlamaKk89s+PXQe7eVE3eNeSZX4inCq63EHL7cX580dMqkoC3ZLw==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-fisheye": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.10.tgz", - "integrity": "sha512-InjiXvc7Gkzrx8VWtU97kDqV7ENnhHGPULymJWeZaF2aicud9Fpk4iCtd/DcZIrk7Cbe60A8RwNXN00HXIbSCg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.12.tgz", + "integrity": "sha512-LGuUTsFg+fOp6KBKrmLkX4LfyCy8IIsROwoUvsUPKzutSqMJnsm3JGDW2eOmWIS/jJpPaeaishjlxvczjgII+Q==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-flip": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.10.tgz", - "integrity": "sha512-42GkGtTHWnhnwTMPVK/kXObZbkYIpQWfuIfy5EMEMk6zRj05zpv4vsjkKWfuemweZINwfvD7wDJF7FVFNNcZZg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.12.tgz", + "integrity": "sha512-m251Rop7GN8W0Yo/rF9LWk6kNclngyjIJs/VXHToGQ6EGveOSTSQaX2Isi9f9lCDLxt+inBIb7nlaLLxnvHX8Q==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-gaussian": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.10.tgz", - "integrity": "sha512-ykrG/6lTp9Q5YA8jS5XzwMHtRxb9HOFMgtmnrUZ8kU+BK8REecfy9Ic5BUEOjCYvS1a/xLsnrZQU07iiYxBxFg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.12.tgz", + "integrity": "sha512-sBfbzoOmJ6FczfG2PquiK84NtVGeScw97JsCC3rpQv1PHVWyW+uqWFF53+n3c8Y0P2HWlUjflEla2h/vWShvhg==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-invert": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.10.tgz", - "integrity": "sha512-d8j9BlUJYs/c994t4azUWSWmQq4LLPG4ecm8m6SSNqap+S/HlVQGqjYhJEBbY9EXkOTYB9vBL9bqwSM1Rr6paA==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.12.tgz", + "integrity": "sha512-N+6rwxdB+7OCR6PYijaA/iizXXodpxOGvT/smd/lxeXsZ/empHmFFFJ/FaXcYh19Tm04dGDaXcNF/dN5nm6+xQ==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-mask": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.10.tgz", - "integrity": "sha512-yRBs1230XZkz24uFTdTcSlZ0HXZpIWzM3iFQN56MzZ7USgdVZjPPDCQ8I9RpqfZ36nDflQkUO0wV7ucsi4ogow==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.12.tgz", + "integrity": "sha512-4AWZg+DomtpUA099jRV8IEZUfn1wLv6+nem4NRJC7L/82vxzLCgXKTxvNvBcNmJjT9yS1LAAmiJGdWKXG63/NA==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-normalize": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.10.tgz", - "integrity": "sha512-Wk9GX6eJMchX/ZAazVa70Fagu+OXMvHiPY+HrcEwcclL+p1wo8xAHEsf9iKno7Ja4EU9lLhbBRY5hYJyiKMEkg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.12.tgz", + "integrity": "sha512-0So0rexQivnWgnhacX4cfkM2223YdExnJTTy6d06WbkfZk5alHUx8MM3yEzwoCN0ErO7oyqEWRnEkGC+As1FtA==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-print": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.10.tgz", - "integrity": "sha512-1U3VloIR+beE1kWPdGEJMiE2h1Do29iv3w8sBbvPyRP4qXxRFcDpmCGtctsrKmb1krlBFlj8ubyAY90xL+5n9w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.12.tgz", + "integrity": "sha512-c7TnhHlxm87DJeSnwr/XOLjJU/whoiKYY7r21SbuJ5nuH+7a78EW1teOaj5gEr2wYEd7QtkFqGlmyGXY/YclyQ==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "load-bmfont": "^1.4.1" } }, "@jimp/plugin-resize": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.10.tgz", - "integrity": "sha512-ixomxVcnAONXDgaq0opvAx4UAOiEhOA/tipuhFFOvPKFd4yf1BAnEviB5maB0SBHHkJXPUSzDp/73xVTMGSe7g==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz", + "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-rotate": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.10.tgz", - "integrity": "sha512-eeFX8dnRyf3LAdsdXWKWuN18hLRg8zy1cP0cP9rHzQVWRK7ck/QsLxK1vHq7MADGwQalNaNTJ9SQxH6c8mz6jw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz", + "integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-scale": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.10.tgz", - "integrity": "sha512-TG/H0oUN69C9ArBCZg4PmuoixFVKIiru8282KzSB/Tp1I0xwX0XLTv3dJ5pobPlIgPcB+TmD4xAIdkCT4rtWxg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz", + "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-shadow": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.10.tgz", - "integrity": "sha512-TN9xm6fI7XfxbMUQqFPZjv59Xdpf0tSiAQdINB4g6pJMWiVANR/74OtDONoy3KKpenu5Y38s+FkrtID/KcQAhw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.12.tgz", + "integrity": "sha512-FX8mTJuCt7/3zXVoeD/qHlm4YH2bVqBuWQHXSuBK054e7wFRnRnbSLPUqAwSeYP3lWqpuQzJtgiiBxV3+WWwTg==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugin-threshold": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.10.tgz", - "integrity": "sha512-DA2lSnU0TgIRbAgmXaxroYw3Ad6J2DOFEoJp0NleSm2h3GWbZEE5yW9U2B6hD3iqn4AenG4E2b2WzHXZyzSutw==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.12.tgz", + "integrity": "sha512-4x5GrQr1a/9L0paBC/MZZJjjgjxLYrqSmWd+e+QfAEPvmRxdRoQ5uKEuNgXnm9/weHQBTnQBQsOY2iFja+XGAw==", "requires": { - "@jimp/utils": "^0.22.10" + "@jimp/utils": "^0.22.12" } }, "@jimp/plugins": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.10.tgz", - "integrity": "sha512-KDMZyM6pmvS8freB+UBLko1TO/k4D7URS/nphCozuH+P7i3UMe7NdckXKJ8u+WD6sqN0YFYvBehpkpnUiw/91w==", - "requires": { - "@jimp/plugin-blit": "^0.22.10", - "@jimp/plugin-blur": "^0.22.10", - "@jimp/plugin-circle": "^0.22.10", - "@jimp/plugin-color": "^0.22.10", - "@jimp/plugin-contain": "^0.22.10", - "@jimp/plugin-cover": "^0.22.10", - "@jimp/plugin-crop": "^0.22.10", - "@jimp/plugin-displace": "^0.22.10", - "@jimp/plugin-dither": "^0.22.10", - "@jimp/plugin-fisheye": "^0.22.10", - "@jimp/plugin-flip": "^0.22.10", - "@jimp/plugin-gaussian": "^0.22.10", - "@jimp/plugin-invert": "^0.22.10", - "@jimp/plugin-mask": "^0.22.10", - "@jimp/plugin-normalize": "^0.22.10", - "@jimp/plugin-print": "^0.22.10", - "@jimp/plugin-resize": "^0.22.10", - "@jimp/plugin-rotate": "^0.22.10", - "@jimp/plugin-scale": "^0.22.10", - "@jimp/plugin-shadow": "^0.22.10", - "@jimp/plugin-threshold": "^0.22.10", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.12.tgz", + "integrity": "sha512-yBJ8vQrDkBbTgQZLty9k4+KtUQdRjsIDJSPjuI21YdVeqZxYywifHl4/XWILoTZsjTUASQcGoH0TuC0N7xm3ww==", + "requires": { + "@jimp/plugin-blit": "^0.22.12", + "@jimp/plugin-blur": "^0.22.12", + "@jimp/plugin-circle": "^0.22.12", + "@jimp/plugin-color": "^0.22.12", + "@jimp/plugin-contain": "^0.22.12", + "@jimp/plugin-cover": "^0.22.12", + "@jimp/plugin-crop": "^0.22.12", + "@jimp/plugin-displace": "^0.22.12", + "@jimp/plugin-dither": "^0.22.12", + "@jimp/plugin-fisheye": "^0.22.12", + "@jimp/plugin-flip": "^0.22.12", + "@jimp/plugin-gaussian": "^0.22.12", + "@jimp/plugin-invert": "^0.22.12", + "@jimp/plugin-mask": "^0.22.12", + "@jimp/plugin-normalize": "^0.22.12", + "@jimp/plugin-print": "^0.22.12", + "@jimp/plugin-resize": "^0.22.12", + "@jimp/plugin-rotate": "^0.22.12", + "@jimp/plugin-scale": "^0.22.12", + "@jimp/plugin-shadow": "^0.22.12", + "@jimp/plugin-threshold": "^0.22.12", "timm": "^1.6.1" } }, "@jimp/png": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.10.tgz", - "integrity": "sha512-RYinU7tZToeeR2g2qAMn42AU+8OUHjXPKZZ9RkmoL4bguA1xyZWaSdr22/FBkmnHhOERRlr02KPDN1OTOYHLDQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.12.tgz", + "integrity": "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==", "requires": { - "@jimp/utils": "^0.22.10", + "@jimp/utils": "^0.22.12", "pngjs": "^6.0.0" } }, "@jimp/tiff": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.10.tgz", - "integrity": "sha512-OaivlSYzpNTHyH/h7pEtl3A7F7TbsgytZs52GLX/xITW92ffgDgT6PkldIrMrET6ERh/hdijNQiew7IoEEr2og==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.12.tgz", + "integrity": "sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg==", "requires": { "utif2": "^4.0.1" } }, "@jimp/types": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.10.tgz", - "integrity": "sha512-u/r+XYzbCx4zZukDmxx8S0er3Yq3iDPI6+31WKX0N18i2qPPJYcn8qwIFurfupRumGvJ8SlGLCgt/T+Y8zzUIw==", - "requires": { - "@jimp/bmp": "^0.22.10", - "@jimp/gif": "^0.22.10", - "@jimp/jpeg": "^0.22.10", - "@jimp/png": "^0.22.10", - "@jimp/tiff": "^0.22.10", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.12.tgz", + "integrity": "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==", + "requires": { + "@jimp/bmp": "^0.22.12", + "@jimp/gif": "^0.22.12", + "@jimp/jpeg": "^0.22.12", + "@jimp/png": "^0.22.12", + "@jimp/tiff": "^0.22.12", "timm": "^1.6.1" } }, "@jimp/utils": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.10.tgz", - "integrity": "sha512-ztlOK9Mm2iLG2AMoabzM4i3WZ/FtshcgsJCbZCRUs/DKoeS2tySRJTnQZ1b7Roq0M4Ce+FUAxnCAcBV0q7PH9w==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.12.tgz", + "integrity": "sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==", "requires": { "regenerator-runtime": "^0.13.3" } @@ -8446,6 +8481,29 @@ "dev": true, "optional": true }, + "@schemasjs/valibot-numbers": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@schemasjs/valibot-numbers/-/valibot-numbers-1.0.12.tgz", + "integrity": "sha512-DxZB0wnPk+gcxe3r195AYnRy0Eodrxnw7DYCOjKlvuZE+QGNwyQz3kYa5gaIeDhfK/rsVQYtk93CLzEoCVzkkw==", + "requires": {} + }, + "@schemasjs/validator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@schemasjs/validator/-/validator-1.0.0.tgz", + "integrity": "sha512-IrRWRAUIFLMdQ0s2ZBgebVMOG86MuOMMsSYI9U0+hkzE2OL5WfDw00sneoI7yhQs79EbtqYrkF+GgHNk3Iz4lQ==", + "requires": { + "valibot": "^0.32.0", + "zod": "^3.23.8" + }, + "dependencies": { + "valibot": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.32.0.tgz", + "integrity": "sha512-FXBnJl4bNOmeg7lQv+jfvo/wADsRBN8e9C3r+O77Re3dEnDma8opp7p4hcIbF7XJJ30h/5SVohdjer17/sHOsQ==", + "optional": true + } + } + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -8929,12 +8987,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "buffer": { @@ -9846,9 +9904,9 @@ "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==" }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -10471,13 +10529,13 @@ } }, "jimp": { - "version": "0.22.10", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.10.tgz", - "integrity": "sha512-lCaHIJAgTOsplyJzC1w/laxSxrbSsEBw4byKwXgUdMmh+ayPsnidTblenQm+IvhIs44Gcuvlb6pd2LQ0wcKaKg==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.12.tgz", + "integrity": "sha512-R5jZaYDnfkxKJy1dwLpj/7cvyjxiclxU3F4TrI/J4j2rS0niq6YDUMoPn5hs8GDpO+OZGo7Ky057CRtWesyhfg==", "requires": { - "@jimp/custom": "^0.22.10", - "@jimp/plugins": "^0.22.10", - "@jimp/types": "^0.22.10", + "@jimp/custom": "^0.22.12", + "@jimp/plugins": "^0.22.12", + "@jimp/types": "^0.22.12", "regenerator-runtime": "^0.13.3" } }, @@ -10792,9 +10850,9 @@ "dev": true }, "node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "requires": { "whatwg-url": "^5.0.0" } @@ -10984,12 +11042,12 @@ "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==" }, "parse-bmfont-xml": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", - "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", "requires": { "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.4.5" + "xml2js": "^0.5.0" } }, "parse-headers": { @@ -11416,9 +11474,9 @@ } }, "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "semver": { "version": "7.5.4", @@ -12026,16 +12084,16 @@ "dev": true }, "tsup": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.0.2.tgz", - "integrity": "sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.1.0.tgz", + "integrity": "sha512-UFdfCAXukax+U6KzeTNO2kAARHcWxmKsnvSPXUcfA1D+kU05XDccCrkffCQpFaWDsZfV0jMyTsxU39VfCp6EOg==", "dev": true, "requires": { "bundle-require": "^4.0.0", "cac": "^6.7.12", "chokidar": "^3.5.1", "debug": "^4.3.1", - "esbuild": "^0.19.2", + "esbuild": "^0.21.4", "execa": "^5.0.0", "globby": "^11.0.3", "joycon": "^3.0.1", @@ -12048,195 +12106,195 @@ }, "dependencies": { "@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "dev": true, "optional": true }, "@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "dev": true, "optional": true }, "esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "requires": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "execa": { @@ -12419,9 +12477,9 @@ } }, "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true }, "ufo": { @@ -12478,9 +12536,9 @@ "dev": true }, "valibot": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.28.1.tgz", - "integrity": "sha512-zQnjwNJuXk6362Leu0+4eFa/SMwRom3/hEvH6s1EGf3oXIPbo2WFKDra9ymnbVh3clLRvd8hw4sKF5ruI2Lyvw==" + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.35.0.tgz", + "integrity": "sha512-+i2aCRkReTrd5KBN/dW2BrPOvFnU5LXTV2xjZnjnqUIO8YUx6P2+MgRrkwF2FhkexgyKq/NIZdPdknhHf5A/Ww==" }, "valid-filename": { "version": "4.0.0", @@ -12549,9 +12607,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "whatwg-fetch": { - "version": "3.6.17", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", - "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==" + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" }, "whatwg-url": { "version": "5.0.0", @@ -12700,9 +12758,9 @@ "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==" }, "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "requires": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -12741,6 +12799,12 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "optional": true } } } diff --git a/package.json b/package.json index 0179e40..de049e8 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "splitea", - "version": "0.0.5", + "version": "1.0.0", "description": "Splitea is a utility to split images in different sizes", "author": "Cristóbal Contreras Rubio", "license": "MIT", @@ -14,8 +14,8 @@ }, "keywords": [ "split", - "image", - "typescript" + "tiles", + "images" ], "type": "module", "main": "dist/index.js", @@ -38,24 +38,38 @@ ], "scripts": { "start": "node dist/index.js", + "lint": "ts-standard", + "format": "ts-standard --fix", "dev": "ts-node-dev src/index.ts", "test": "vitest", "test:coverage": "vitest run --coverage", - "lint": "ts-standard", - "lint-fix": "ts-standard --fix", "build": "tsup && cp src/filename-reserved-regex.d.ts dist" }, "dependencies": { - "jimp": "^0.22.10", - "valibot": "^0.28.1", + "@schemasjs/valibot-numbers": "^1.0.12", + "@schemasjs/validator": "^1.0.0", + "jimp": "^0.22.12", + "valibot": "^0.35.0", "valid-filename": "^4.0.0" }, "devDependencies": { "@vitest/coverage-v8": "^1.6.0", "ts-node-dev": "^2.0.0", "ts-standard": "^12.0.2", - "tsup": "^8.0.2", - "typescript": "^5.3.3", - "vitest": "^1.3.0" + "tsup": "^8.1.0", + "typescript": "^5.5.2", + "vitest": "^1.6.0" + }, + "ts-standard": { + "ignore": [ + "tests", + "vitest.config.ts", + "tsup.config.ts" + ] + }, + "eslintConfig": { + "parserOptions": { + "project": "./tsconfig.json" + } } } From 51d82a2a34255ed3eabf1a05578e6a60c1f617b5 Mon Sep 17 00:00:00 2001 From: Cristobal Contreras Rubio Date: Fri, 28 Jun 2024 21:45:37 +0200 Subject: [PATCH 3/4] renamed github actions --- .github/workflows/{publish.yml => ci.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{publish.yml => ci.yml} (98%) diff --git a/.github/workflows/publish.yml b/.github/workflows/ci.yml similarity index 98% rename from .github/workflows/publish.yml rename to .github/workflows/ci.yml index f2378f5..736f3d5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages -name: publish +name: ci on: push From dfbfdd86506ae5c9d474cd15b2380620b3957523 Mon Sep 17 00:00:00 2001 From: Cristobal Contreras Rubio Date: Fri, 28 Jun 2024 21:48:00 +0200 Subject: [PATCH 4/4] Fix deps of peerdeps --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index a227b04..261f7b0 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "splitea", - "version": "0.0.5", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "splitea", - "version": "0.0.5", + "version": "1.0.0", "license": "MIT", "dependencies": { "@schemasjs/valibot-numbers": "^1.0.12", "@schemasjs/validator": "^1.0.0", "jimp": "^0.22.12", - "valibot": "^0.35.0", + "valibot": "^0.33.3", "valid-filename": "^4.0.0" }, "devDependencies": { @@ -7198,9 +7198,9 @@ "dev": true }, "node_modules/valibot": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.35.0.tgz", - "integrity": "sha512-+i2aCRkReTrd5KBN/dW2BrPOvFnU5LXTV2xjZnjnqUIO8YUx6P2+MgRrkwF2FhkexgyKq/NIZdPdknhHf5A/Ww==" + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.33.3.tgz", + "integrity": "sha512-/fuY1DlX8uiQ7aphlzrrI2DbG0YJk84JMgvz2qKpUIdXRNsS53varfo4voPjSrjUr5BSV2K0miSEJUOlA5fQFg==" }, "node_modules/valid-filename": { "version": "4.0.0", @@ -12536,9 +12536,9 @@ "dev": true }, "valibot": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.35.0.tgz", - "integrity": "sha512-+i2aCRkReTrd5KBN/dW2BrPOvFnU5LXTV2xjZnjnqUIO8YUx6P2+MgRrkwF2FhkexgyKq/NIZdPdknhHf5A/Ww==" + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.33.3.tgz", + "integrity": "sha512-/fuY1DlX8uiQ7aphlzrrI2DbG0YJk84JMgvz2qKpUIdXRNsS53varfo4voPjSrjUr5BSV2K0miSEJUOlA5fQFg==" }, "valid-filename": { "version": "4.0.0", diff --git a/package.json b/package.json index de049e8..216dd7a 100755 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@schemasjs/valibot-numbers": "^1.0.12", "@schemasjs/validator": "^1.0.0", "jimp": "^0.22.12", - "valibot": "^0.35.0", + "valibot": "^0.33.3", "valid-filename": "^4.0.0" }, "devDependencies": {