diff --git a/.gitignore b/.gitignore index 407e7b0..b141d12 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ node_modules/ /.parcel-cache # nvm it does now ok /webapp/.parcel-cache + +/jpeg-rs/target diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index eaa46f0..913f3ab 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/backend/package.json b/backend/package.json index 4d5b861..b38134b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,8 +17,8 @@ "dependencies": { "@computernewb/superqemu": "^0.1.0", "@fastify/websocket": "^10.0.1", + "@socketcomputer/jpeg-rs": "*", "@socketcomputer/shared": "*", - "canvas": "^2.11.2", "fastify": "^4.26.2", "mnemonist": "^0.39.8" }, diff --git a/backend/src/JPEGEncoder.ts b/backend/src/JPEGEncoder.ts new file mode 100644 index 0000000..faf82e3 --- /dev/null +++ b/backend/src/JPEGEncoder.ts @@ -0,0 +1,20 @@ +import * as jpeg from '@socketcomputer/jpeg-rs'; +import { Size, Rect } from '@computernewb/superqemu'; + +let gJpegQuality = 20; + +export class JPEGEncoder { + static SetQuality(quality: number) { + gJpegQuality = quality; + } + + static async Encode(canvas: Buffer, displaySize: Size, rect: Rect): Promise { + let offset = (rect.y * displaySize.width + rect.x) * 4; + return jpeg.jpegEncode({ + width: rect.width, + height: rect.height, + stride: displaySize.width, + buffer: canvas.subarray(offset) + }); + } +} diff --git a/backend/src/SocketVM.ts b/backend/src/SocketVM.ts index dbd304b..5b76012 100644 --- a/backend/src/SocketVM.ts +++ b/backend/src/SocketVM.ts @@ -1,21 +1,14 @@ import { EventEmitter } from 'node:events'; import { TurnQueue, UserTimeTuple, kTurnTimeSeconds } from './TurnQueue.js'; import { VMUser } from './VMUser.js'; -import { QemuDisplay, QemuVM, VMState } from '@computernewb/superqemu'; +import { QemuDisplay, QemuVM, Rect, Size, VMState } from '@computernewb/superqemu'; import { ExtendableTimer } from './ExtendableTimer.js'; import { kMaxUserNameLength } from '@socketcomputer/shared'; import * as Shared from '@socketcomputer/shared'; -import { Canvas } from 'canvas'; import pino from 'pino'; - -// for the maximum socket.io experience -const kCanvasJpegQuality = 0.25; - -function EncodeCanvas(canvas: Canvas): Buffer { - return canvas.toBuffer('image/jpeg', { quality: kCanvasJpegQuality }); -} +import { JPEGEncoder } from './JPEGEncoder.js'; export class SocketVM extends EventEmitter { private vm: QemuVM; @@ -195,52 +188,70 @@ export class SocketVM extends EventEmitter { } } - private async InsertCD(isoPath: string) { - await this.vm.ChangeRemovableMedia('vm.cd', isoPath); - } - private async VMRunning() { let self = this; - // Hook up the display - this.display?.on('resize', async (width: number, height: number) => { - if (self.display == null) return; - - let buffers = this.MakeFullScreenData(); - - for (let buffer of buffers) await self.BroadcastBuffer(buffer); - }); - - this.display?.on('rect', async (x: number, y: number, rect: ImageData) => { - let canvas = new Canvas(rect.width, rect.height); - canvas.getContext('2d').putImageData(rect, 0, 0); - - let buffer = EncodeCanvas(canvas); - - await this.BroadcastMessage((encoder: Shared.MessageEncoder) => { - encoder.Init(buffer.length + 8); - encoder.SetDisplayRectMessage(x, y, buffer); - return encoder.Finish(); - }); - }); + this.display = this.vm.GetDisplay(); + this.display?.on('resize', (size: Size) => self.OnDisplayResized(size)); + this.display?.on('rect', (rect: Rect) => self.OnDisplayRectangle(rect)); this.timer.Start(); } - private MakeFullScreenData(): Array { + private async OnDisplayRectangle(rect: Rect) { + let encoded = await this.MakeRectData(rect); + await this.BroadcastMessage((encoder: Shared.MessageEncoder) => { + encoder.Init(encoded.length + 8); + encoder.SetDisplayRectMessage(rect.x, rect.y, encoded); + return encoder.Finish(); + }); + } + + private async OnDisplayResized(size: Size) { + let arr = await this.MakeFullScreen(); + + for(let user of this.users) { + for(let msg of arr) { + await user.SendBuffer(msg); + } + } + } + + + private async MakeRectData(rect: Rect) { + let display = this.vm.GetDisplay(); + + // TODO: actually throw an error here + if (display == null) return Buffer.from('no'); + + let displaySize = display.Size(); + let encoded = await JPEGEncoder.Encode(display.Buffer(), displaySize, rect); + + return encoded; + } + + + private async MakeFullScreen() { + let disp = this.display; + if (disp == null) return []; + let dispSize = disp.Size(); + let arr: Array = []; const addMessage = (enc: (encoder: Shared.MessageEncoder) => ArrayBuffer) => { arr.push(Buffer.from(enc(new Shared.MessageEncoder()))); }; - if (this.display == null) return arr; - - let buffer = EncodeCanvas(this.display.GetCanvas()); + let buffer = await this.MakeRectData({ + x: 0, + y: 0, + width: dispSize.width, + height: dispSize.height + }); addMessage((encoder: Shared.MessageEncoder) => { encoder.Init(8); - encoder.SetDisplaySizeMessage(this.display!.Size().width, this.display!.Size().height); + encoder.SetDisplaySizeMessage(dispSize.width, dispSize.height); return encoder.Finish(); }); @@ -249,19 +260,17 @@ export class SocketVM extends EventEmitter { encoder.SetDisplayRectMessage(0, 0, buffer); return encoder.Finish(); }); - return arr; } + private async SendInitialScreen(user: VMUser) { - if (this.display == null) return; - - let buffers = this.MakeFullScreenData(); - - for (let buffer of buffers) await user.SendBuffer(buffer); + let arr = await this.MakeFullScreen(); + for (let buffer of arr) await user.SendBuffer(buffer); } private async VMStopped() { + this.display = null; await this.vm.Start(); } } diff --git a/jpeg-rs/Cargo.lock b/jpeg-rs/Cargo.lock new file mode 100644 index 0000000..0588f48 --- /dev/null +++ b/jpeg-rs/Cargo.lock @@ -0,0 +1,333 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "jpeg-rs" +version = "0.1.0" +dependencies = [ + "neon", + "once_cell", + "tokio", + "turbojpeg-sys", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "neon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d75440242411c87dc39847b0e33e961ec1f10326a9d8ecf9c1ea64a3b3c13dc" +dependencies = [ + "getrandom", + "libloading", + "neon-macros", + "once_cell", + "semver", + "send_wrapper", + "smallvec", +] + +[[package]] +name = "neon-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" +dependencies = [ + "quote", + "syn", + "syn-mid", +] + +[[package]] +name = "object" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-mid" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +dependencies = [ + "backtrace", + "pin-project-lite", +] + +[[package]] +name = "turbojpeg-sys" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa6daade3b979fb7454cce5ebcb9772ce7a1cf476ea27ed20ed06e13d9bc983" +dependencies = [ + "anyhow", + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/jpeg-rs/Cargo.toml b/jpeg-rs/Cargo.toml new file mode 100644 index 0000000..9b8ce08 --- /dev/null +++ b/jpeg-rs/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "jpeg-rs" +description = "JPEG utility library for socket2" +version = "0.1.0" +edition = "2021" +exclude = ["index.node"] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +neon = "1" + +# Required for JPEG +once_cell = "1.19.0" +tokio = { version = "1.38.0", features = [ "rt", "rt-multi-thread" ] } +turbojpeg-sys = "1.0.0" diff --git a/jpeg-rs/index.d.ts b/jpeg-rs/index.d.ts new file mode 100644 index 0000000..875c97c --- /dev/null +++ b/jpeg-rs/index.d.ts @@ -0,0 +1,15 @@ +// + +interface JpegInputArgs { + width: number, + height: number, + stride: number, // The width of your input framebuffer OR your image width (if encoding a full image) + buffer: Buffer + + // TODO: Allow different formats, or export a boxed ffi object which can store a format + // (i.e: new JpegEncoder(FORMAT_xxx)). +} + +/// Performs JPEG encoding. +export function jpegEncode(input: JpegInputArgs) : Promise; + diff --git a/jpeg-rs/index.js b/jpeg-rs/index.js new file mode 100644 index 0000000..fc02f40 --- /dev/null +++ b/jpeg-rs/index.js @@ -0,0 +1,6 @@ +// *sigh* +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +export let {guacDecode, guacEncode, jpegEncode} = require('./index.node'); + diff --git a/jpeg-rs/index.node b/jpeg-rs/index.node new file mode 100755 index 0000000..0edf5d7 Binary files /dev/null and b/jpeg-rs/index.node differ diff --git a/jpeg-rs/package.json b/jpeg-rs/package.json new file mode 100644 index 0000000..6fd8965 --- /dev/null +++ b/jpeg-rs/package.json @@ -0,0 +1,16 @@ +{ + "name": "@socketcomputer/jpeg-rs", + "version": "0.1.0", + "packageManager": "yarn@4.3.1", + "type": "module", + "main": "index.js", + "types": "index.d.ts", + "scripts": { + "build": "cargo-cp-artifact -nc index.node -- cargo build --release --message-format=json-render-diagnostics", + "install": "yarn build", + "test": "cargo test" + }, + "devDependencies": { + "cargo-cp-artifact": "^0.1" + } +} diff --git a/jpeg-rs/src/jpeg_compressor.rs b/jpeg-rs/src/jpeg_compressor.rs new file mode 100644 index 0000000..56248c4 --- /dev/null +++ b/jpeg-rs/src/jpeg_compressor.rs @@ -0,0 +1,82 @@ +use turbojpeg_sys::*; + +pub struct Image<'a> { + pub buffer: &'a [u8], + pub width: u32, + pub height: u32, + pub stride: u32, + pub format: TJPF, +} + +pub struct JpegCompressor { + handle: tjhandle, + subsamp: TJSAMP, + quality: u32, +} + +unsafe impl Send for JpegCompressor {} + +impl JpegCompressor { + pub fn new() -> Self { + unsafe { + let init = Self { + handle: tjInitCompress(), + subsamp: TJSAMP_TJSAMP_422, + quality: 95, + }; + return init; + } + } + + pub fn set_quality(&mut self, quality: u32) { + self.quality = quality; + } + + pub fn set_subsamp(&mut self, samp: TJSAMP) { + self.subsamp = samp; + } + + pub fn compress_buffer<'a>(&self, image: &Image<'a>) -> Vec { + unsafe { + let size: usize = + tjBufSize(image.width as i32, image.height as i32, self.subsamp) as usize; + let mut vec = Vec::with_capacity(size); + + vec.resize(size, 0); + + let mut ptr: *mut u8 = vec.as_mut_ptr(); + let mut size: u64 = 0; + + let res = tjCompress2( + self.handle, + image.buffer.as_ptr(), + image.width as i32, + image.stride as i32, + image.height as i32, + image.format, + std::ptr::addr_of_mut!(ptr), + std::ptr::addr_of_mut!(size), + self.subsamp, + self.quality as i32, + (TJFLAG_NOREALLOC) as i32, + ); + + // TODO: Result sex so we can actually notify failure + if res == -1 { + return Vec::new(); + } + + // Truncate down to the size we're given back + vec.truncate(size as usize); + return vec; + } + } +} + +impl Drop for JpegCompressor { + fn drop(&mut self) { + unsafe { + tjDestroy(self.handle); + } + } +} diff --git a/jpeg-rs/src/jpeg_js.rs b/jpeg-rs/src/jpeg_js.rs new file mode 100644 index 0000000..de87f9b --- /dev/null +++ b/jpeg-rs/src/jpeg_js.rs @@ -0,0 +1,87 @@ +use std::sync::{Arc, Mutex}; + +use neon::prelude::*; +use neon::types::buffer::TypedArray; + +use once_cell::sync::OnceCell; +use tokio::runtime::Runtime; + +use std::cell::RefCell; + +use crate::jpeg_compressor::*; + +/// Gives a static Tokio runtime. We should replace this with +/// rayon or something, but for now tokio works. +fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> { + static RUNTIME: OnceCell = OnceCell::new(); + + RUNTIME + .get_or_try_init(Runtime::new) + .or_else(|err| cx.throw_error(&err.to_string())) +} + +thread_local! { + static COMPRESSOR: RefCell = RefCell::new(JpegCompressor::new()); +} + +fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> { + let input = cx.argument::(0)?; + + // Get our input arguments here + let width: u64 = input.get::(cx, "width")?.value(cx) as u64; + let height: u64 = input.get::(cx, "height")?.value(cx) as u64; + let stride: u64 = input.get::(cx, "stride")?.value(cx) as u64; + let buffer: Handle = input.get(cx, "buffer")?; + + let (deferred, promise) = cx.promise(); + let channel = cx.channel(); + let runtime = runtime(cx)?; + + let buf = buffer.as_slice(cx); + + let copy: Arc>> = Arc::new(Mutex::new(Vec::with_capacity(buf.len()))); + + // Copy from the node buffer to our temporary buffer + { + let mut locked = copy.lock().unwrap(); + let cap = locked.capacity(); + locked.resize(cap, 0); + locked.copy_from_slice(buf); + } + + // Spawn off a tokio blocking pool thread that will do the work for us + runtime.spawn_blocking(move || { + let clone = Arc::clone(©); + let locked = clone.lock().unwrap(); + + let image: Image = Image { + buffer: locked.as_slice(), + width: width as u32, + height: height as u32, + + stride: (stride * 4u64) as u32, // I think? + format: turbojpeg_sys::TJPF_TJPF_RGBA, + }; + + let vec = COMPRESSOR.with(|lazy| { + let mut b = lazy.borrow_mut(); + b.set_quality(35); + b.set_subsamp(turbojpeg_sys::TJSAMP_TJSAMP_420); + b.compress_buffer(&image) + }); + + // Fulfill the Javascript promise with our encoded buffer + deferred.settle_with(&channel, move |mut cx| { + let mut buf = cx.buffer(vec.len())?; + let slice = buf.as_mut_slice(&mut cx); + slice.copy_from_slice(vec.as_slice()); + Ok(buf) + }); + }); + + Ok(promise) +} + +pub fn jpeg_encode(mut cx: FunctionContext) -> JsResult { + jpeg_encode_impl(&mut cx) +} diff --git a/jpeg-rs/src/lib.rs b/jpeg-rs/src/lib.rs new file mode 100644 index 0000000..e045232 --- /dev/null +++ b/jpeg-rs/src/lib.rs @@ -0,0 +1,13 @@ +mod jpeg_compressor; +mod jpeg_js; + + +use neon::prelude::*; + + +#[neon::main] +fn main(mut cx: ModuleContext) -> NeonResult<()> { + // Mostly transitionary, later on API should change + cx.export_function("jpegEncode", jpeg_js::jpeg_encode)?; + Ok(()) +} diff --git a/package.json b/package.json index 2871a23..aac4286 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "socketcomputer-repo", "workspaces": [ "shared", + "jpeg-rs", "backend", "webapp" ], @@ -11,7 +12,6 @@ "build:frontend": "npm -w shared run build && npm -w webapp run build" }, "dependencies": { - "canvas": "^2.11.2", "pino": "^9.3.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 7454d24..bd7ea02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -171,25 +171,6 @@ __metadata: languageName: node linkType: hard -"@mapbox/node-pre-gyp@npm:^1.0.0": - version: 1.0.11 - resolution: "@mapbox/node-pre-gyp@npm:1.0.11" - dependencies: - detect-libc: "npm:^2.0.0" - https-proxy-agent: "npm:^5.0.0" - make-dir: "npm:^3.1.0" - node-fetch: "npm:^2.6.7" - nopt: "npm:^5.0.0" - npmlog: "npm:^5.0.1" - rimraf: "npm:^3.0.2" - semver: "npm:^7.3.5" - tar: "npm:^6.1.11" - bin: - node-pre-gyp: bin/node-pre-gyp - checksum: 10c0/2b24b93c31beca1c91336fa3b3769fda98e202fb7f9771f0f4062588d36dcc30fcf8118c36aa747fa7f7610d8cf601872bdaaf62ce7822bb08b545d1bbe086cc - languageName: node - linkType: hard - "@mischnic/json-sourcemap@npm:^0.1.0": version: 0.1.1 resolution: "@mischnic/json-sourcemap@npm:0.1.1" @@ -1159,15 +1140,23 @@ __metadata: dependencies: "@computernewb/superqemu": "npm:^0.1.0" "@fastify/websocket": "npm:^10.0.1" + "@socketcomputer/jpeg-rs": "npm:*" "@socketcomputer/shared": "npm:*" "@types/ws": "npm:^8.5.10" - canvas: "npm:^2.11.2" fastify: "npm:^4.26.2" mnemonist: "npm:^0.39.8" parcel: "npm:^2.12.0" languageName: unknown linkType: soft +"@socketcomputer/jpeg-rs@npm:*, @socketcomputer/jpeg-rs@workspace:jpeg-rs": + version: 0.0.0-use.local + resolution: "@socketcomputer/jpeg-rs@workspace:jpeg-rs" + dependencies: + cargo-cp-artifact: "npm:^0.1" + languageName: unknown + linkType: soft + "@socketcomputer/shared@npm:*, @socketcomputer/shared@workspace:shared": version: 0.0.0-use.local resolution: "@socketcomputer/shared@workspace:shared" @@ -1350,13 +1339,6 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:1": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: 10c0/3f762677702acb24f65e813070e306c61fafe25d4b2583f9dfc935131f774863f3addd5741572ed576bd69cabe473c5af18e1e108b829cb7b6b4747884f726e6 - languageName: node - linkType: hard - "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -1387,15 +1369,6 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: "npm:4" - checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261 - languageName: node - linkType: hard - "agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": version: 7.1.1 resolution: "agent-base@npm:7.1.1" @@ -1490,13 +1463,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 10c0/d06e26384a8f6245d8c8896e138c0388824e259a329e0c9f196b4fa533c82502a6fd449586e3604950a0c42921832a458bb3aa0aa9f0ba449cfd4f50fd0d09b5 - languageName: node - linkType: hard - "archy@npm:^1.0.0": version: 1.0.0 resolution: "archy@npm:1.0.0" @@ -1504,16 +1470,6 @@ __metadata: languageName: node linkType: hard -"are-we-there-yet@npm:^2.0.0": - version: 2.0.0 - resolution: "are-we-there-yet@npm:2.0.0" - dependencies: - delegates: "npm:^1.0.0" - readable-stream: "npm:^3.6.0" - checksum: 10c0/375f753c10329153c8d66dc95e8f8b6c7cc2aa66e05cb0960bd69092b10dae22900cacc7d653ad11d26b3ecbdbfe1e8bfb6ccf0265ba8077a7d979970f16b99c - languageName: node - linkType: hard - "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -1577,16 +1533,6 @@ __metadata: languageName: node linkType: hard -"brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668 - languageName: node - linkType: hard - "brace-expansion@npm:^2.0.1": version: 2.0.1 resolution: "brace-expansion@npm:2.0.1" @@ -1670,15 +1616,12 @@ __metadata: languageName: node linkType: hard -"canvas@npm:^2.11.2": - version: 2.11.2 - resolution: "canvas@npm:2.11.2" - dependencies: - "@mapbox/node-pre-gyp": "npm:^1.0.0" - nan: "npm:^2.17.0" - node-gyp: "npm:latest" - simple-get: "npm:^3.0.3" - checksum: 10c0/943368798ad1b66b18633aa34b6181e1038dac5433fc9727cd07be35f0a633f572b60d9edb95f5ff90b6a9128e86d5312035f91a2934101c73185b15d906230a +"cargo-cp-artifact@npm:^0.1": + version: 0.1.9 + resolution: "cargo-cp-artifact@npm:0.1.9" + bin: + cargo-cp-artifact: bin/cargo-cp-artifact.js + checksum: 10c0/60eb1845917cfb021920fcf600a72379890b385396f9c69107face3b16b347960b66cd3d82cc169c6ac8b1212cf0706584125bc36fbc08353b033310c17ca0a6 languageName: node linkType: hard @@ -1782,15 +1725,6 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.2": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 10c0/8ffeaa270a784dc382f62d9be0a98581db43e11eee301af14734a6d089bd456478b1a8b3e7db7ca7dc5b18a75f828f775c44074020b51c05fc00e6d0992b1cc6 - languageName: node - linkType: hard - "commander@npm:^7.0.0, commander@npm:^7.2.0": version: 7.2.0 resolution: "commander@npm:7.2.0" @@ -1798,20 +1732,6 @@ __metadata: languageName: node linkType: hard -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f - languageName: node - linkType: hard - -"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 10c0/7ab51d30b52d461412cd467721bb82afe695da78fff8f29fe6f6b9cbaac9a2328e27a22a966014df9532100f6dd85370460be8130b9c677891ba36d96a343f50 - languageName: node - linkType: hard - "cookie@npm:^0.6.0": version: 0.6.0 resolution: "cookie@npm:0.6.0" @@ -1910,22 +1830,6 @@ __metadata: languageName: node linkType: hard -"decompress-response@npm:^4.2.0": - version: 4.2.1 - resolution: "decompress-response@npm:4.2.1" - dependencies: - mimic-response: "npm:^2.0.0" - checksum: 10c0/5e4821be332e80e3639acee2441c41d245fc07ac3ee85a6f28893c10c079d66d9bf09e8d84bffeae5656a4625e09e9b93fb4a5705adbe6b07202eea64fae1c8d - languageName: node - linkType: hard - -"delegates@npm:^1.0.0": - version: 1.0.0 - resolution: "delegates@npm:1.0.0" - checksum: 10c0/ba05874b91148e1db4bf254750c042bf2215febd23a6d3cda2e64896aef79745fbd4b9996488bd3cafb39ce19dbce0fd6e3b6665275638befffe1c9b312b91b5 - languageName: node - linkType: hard - "detect-libc@npm:^1.0.3": version: 1.0.3 resolution: "detect-libc@npm:1.0.3" @@ -1935,7 +1839,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1": +"detect-libc@npm:^2.0.1": version: 2.0.3 resolution: "detect-libc@npm:2.0.3" checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 @@ -2295,13 +2199,6 @@ __metadata: languageName: node linkType: hard -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 - languageName: node - linkType: hard - "fsevents@npm:~2.3.2": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -2321,23 +2218,6 @@ __metadata: languageName: node linkType: hard -"gauge@npm:^3.0.0": - version: 3.0.2 - resolution: "gauge@npm:3.0.2" - dependencies: - aproba: "npm:^1.0.3 || ^2.0.0" - color-support: "npm:^1.1.2" - console-control-strings: "npm:^1.0.0" - has-unicode: "npm:^2.0.1" - object-assign: "npm:^4.1.1" - signal-exit: "npm:^3.0.0" - string-width: "npm:^4.2.3" - strip-ansi: "npm:^6.0.1" - wide-align: "npm:^1.1.2" - checksum: 10c0/75230ccaf216471e31025c7d5fcea1629596ca20792de50c596eb18ffb14d8404f927cd55535aab2eeecd18d1e11bd6f23ec3c2e9878d2dda1dc74bccc34b913 - languageName: node - linkType: hard - "get-port@npm:^4.2.0": version: 4.2.0 resolution: "get-port@npm:4.2.0" @@ -2377,20 +2257,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.3": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: "npm:^1.0.0" - inflight: "npm:^1.0.4" - inherits: "npm:2" - minimatch: "npm:^3.1.1" - once: "npm:^1.3.0" - path-is-absolute: "npm:^1.0.0" - checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe - languageName: node - linkType: hard - "globals@npm:^13.2.0": version: 13.24.0 resolution: "globals@npm:13.24.0" @@ -2421,13 +2287,6 @@ __metadata: languageName: node linkType: hard -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 10c0/ebdb2f4895c26bb08a8a100b62d362e49b2190bcfd84b76bc4be1a3bd4d254ec52d0dd9f2fbcc093fc5eb878b20c52146f9dfd33e2686ed28982187be593b47c - languageName: node - linkType: hard - "htmlnano@npm:^2.0.0": version: 2.1.0 resolution: "htmlnano@npm:2.1.0" @@ -2494,16 +2353,6 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: "npm:6" - debug: "npm:4" - checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1 - languageName: node - linkType: hard - "https-proxy-agent@npm:^7.0.1": version: 7.0.5 resolution: "https-proxy-agent@npm:7.0.5" @@ -2568,17 +2417,7 @@ __metadata: languageName: node linkType: hard -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: "npm:^1.3.0" - wrappy: "npm:1" - checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 - languageName: node - linkType: hard - -"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4": +"inherits@npm:^2.0.3, inherits@npm:^2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 @@ -2922,15 +2761,6 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^3.1.0": - version: 3.1.0 - resolution: "make-dir@npm:3.1.0" - dependencies: - semver: "npm:^6.0.0" - checksum: 10c0/56aaafefc49c2dfef02c5c95f9b196c4eb6988040cf2c712185c7fe5c99b4091591a7fc4d4eafaaefa70ff763a26f6ab8c3ff60b9e75ea19876f49b18667ecaa - languageName: node - linkType: hard - "make-fetch-happen@npm:^13.0.0": version: 13.0.1 resolution: "make-fetch-happen@npm:13.0.1" @@ -2982,22 +2812,6 @@ __metadata: languageName: node linkType: hard -"mimic-response@npm:^2.0.0": - version: 2.1.0 - resolution: "mimic-response@npm:2.1.0" - checksum: 10c0/717475c840f20deca87a16cb2f7561f9115f5de225ea2377739e09890c81aec72f43c81fd4984650c4044e66be5a846fa7a517ac7908f01009e1e624e19864d5 - languageName: node - linkType: hard - -"minimatch@npm:^3.1.1": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 - languageName: node - linkType: hard - "minimatch@npm:^9.0.4": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -3159,15 +2973,6 @@ __metadata: languageName: node linkType: hard -"nan@npm:^2.17.0": - version: 2.19.0 - resolution: "nan@npm:2.19.0" - dependencies: - node-gyp: "npm:latest" - checksum: 10c0/b8d05d75f92ee9d94affa50d0aa41b6c698254c848529452d7ab67c2e0d160a83f563bfe2cbd53e077944eceb48c757f83c93634c7c9ff404c9ec1ed4e5ced1a - languageName: node - linkType: hard - "negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" @@ -3193,20 +2998,6 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.7": - version: 2.7.0 - resolution: "node-fetch@npm:2.7.0" - dependencies: - whatwg-url: "npm:^5.0.0" - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 - languageName: node - linkType: hard - "node-gyp-build-optional-packages@npm:5.0.7": version: 5.0.7 resolution: "node-gyp-build-optional-packages@npm:5.0.7" @@ -3258,17 +3049,6 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^5.0.0": - version: 5.0.0 - resolution: "nopt@npm:5.0.0" - dependencies: - abbrev: "npm:1" - bin: - nopt: bin/nopt.js - checksum: 10c0/fc5c4f07155cb455bf5fc3dd149fac421c1a40fd83c6bfe83aa82b52f02c17c5e88301321318adaa27611c8a6811423d51d29deaceab5fa158b585a61a551061 - languageName: node - linkType: hard - "nopt@npm:^7.0.0": version: 7.2.1 resolution: "nopt@npm:7.2.1" @@ -3296,18 +3076,6 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^5.0.1": - version: 5.0.1 - resolution: "npmlog@npm:5.0.1" - dependencies: - are-we-there-yet: "npm:^2.0.0" - console-control-strings: "npm:^1.1.0" - gauge: "npm:^3.0.0" - set-blocking: "npm:^2.0.0" - checksum: 10c0/489ba519031013001135c463406f55491a17fc7da295c18a04937fe3a4d523fd65e88dd418a28b967ab743d913fdeba1e29838ce0ad8c75557057c481f7d49fa - languageName: node - linkType: hard - "nth-check@npm:^2.0.1": version: 2.1.1 resolution: "nth-check@npm:2.1.1" @@ -3324,13 +3092,6 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 - languageName: node - linkType: hard - "obliterator@npm:^2.0.1": version: 2.0.4 resolution: "obliterator@npm:2.0.4" @@ -3345,7 +3106,7 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": +"once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -3431,13 +3192,6 @@ __metadata: languageName: node linkType: hard -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 - languageName: node - linkType: hard - "path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -3770,17 +3524,6 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" - dependencies: - glob: "npm:^7.1.3" - bin: - rimraf: bin.js - checksum: 10c0/9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8 - languageName: node - linkType: hard - "safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" @@ -3831,15 +3574,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0": - version: 6.3.1 - resolution: "semver@npm:6.3.1" - bin: - semver: bin/semver.js - checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d - languageName: node - linkType: hard - "semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.4": version: 7.6.0 resolution: "semver@npm:7.6.0" @@ -3851,13 +3585,6 @@ __metadata: languageName: node linkType: hard -"set-blocking@npm:^2.0.0": - version: 2.0.0 - resolution: "set-blocking@npm:2.0.0" - checksum: 10c0/9f8c1b2d800800d0b589de1477c753492de5c1548d4ade52f57f1d1f5e04af5481554d75ce5e5c43d4004b80a3eb714398d6907027dc0534177b7539119f4454 - languageName: node - linkType: hard - "set-cookie-parser@npm:^2.4.1": version: 2.6.0 resolution: "set-cookie-parser@npm:2.6.0" @@ -3881,13 +3608,6 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 - languageName: node - linkType: hard - "signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" @@ -3895,24 +3615,6 @@ __metadata: languageName: node linkType: hard -"simple-concat@npm:^1.0.0": - version: 1.0.1 - resolution: "simple-concat@npm:1.0.1" - checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 - languageName: node - linkType: hard - -"simple-get@npm:^3.0.3": - version: 3.1.1 - resolution: "simple-get@npm:3.1.1" - dependencies: - decompress-response: "npm:^4.2.0" - once: "npm:^1.3.1" - simple-concat: "npm:^1.0.0" - checksum: 10c0/438c78844ea1b1e7268d13ee0b3a39c7d644183367aec916aed3b676b45d3037a61d9f975c200a49b42eb851f29f03745118af1e13c01e60a7b4044f2fd60be7 - languageName: node - linkType: hard - "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -3928,7 +3630,6 @@ __metadata: "@parcel/transformer-sass": "npm:^2.12.0" "@parcel/transformer-typescript-types": "npm:2.12.0" "@types/node": "npm:^20.12.2" - canvas: "npm:^2.11.2" parcel: "npm:^2.12.0" pino: "npm:^9.3.1" prettier: "npm:^3.2.5" @@ -4046,7 +3747,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -4199,13 +3900,6 @@ __metadata: languageName: node linkType: hard -"tr46@npm:~0.0.3": - version: 0.0.3 - resolution: "tr46@npm:0.0.3" - checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 - languageName: node - linkType: hard - "tslib@npm:^2.4.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" @@ -4309,23 +4003,6 @@ __metadata: languageName: node linkType: hard -"webidl-conversions@npm:^3.0.0": - version: 3.0.1 - resolution: "webidl-conversions@npm:3.0.1" - checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db - languageName: node - linkType: hard - -"whatwg-url@npm:^5.0.0": - version: 5.0.0 - resolution: "whatwg-url@npm:5.0.0" - dependencies: - tr46: "npm:~0.0.3" - webidl-conversions: "npm:^3.0.0" - checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 - languageName: node - linkType: hard - "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -4348,15 +4025,6 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.2": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" - dependencies: - string-width: "npm:^1.0.2 || 2 || 3 || 4" - checksum: 10c0/1d9c2a3e36dfb09832f38e2e699c367ef190f96b82c71f809bc0822c306f5379df87bab47bed27ea99106d86447e50eb972d3c516c2f95782807a9d082fbea95 - languageName: node - linkType: hard - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0"