diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 2e546b8..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# .github/dependabot.yml -version: 2 -updates: - - package-ecosystem: "npm" - directory: "/lib" - schedule: - interval: "weekly" # Frecuencia: daily, weekly, monthly - open-pull-requests-limit: 5 - commit-message: - prefix: "fix" - allow: - - dependency-type: "direct" # Solo las dependencias directas diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb80f44..0d83562 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,5 @@ name: Build + on: push: branches: [main] @@ -6,148 +7,140 @@ on: branches: [main] paths: - 'core/**' + - 'lib/**' - '.github/workflows/**' concurrency: group: build-${{ github.ref }} cancel-in-progress: true -env: - OPENJPEG_VERSION: 2.5 - jobs: - build: - name: Build and Package - if: ${{ contains(github.event.head_commit.message, 'chore(main):') || github.event_name == 'pull_request' }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - include: - - os: ubuntu-latest - binary_name: convert-linux - - os: windows-latest - binary_name: convert-win.exe - - os: macos-latest - binary_name: convert-macos + build-wasm: + name: Build WASM + runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Set up build cache - uses: actions/cache@v3 + - name: Setup Emscripten + uses: mymindstorm/setup-emsdk@v14 with: - path: | - ~/.vcpkg - vcpkg - openjpeg/build - key: ${{ runner.os }}-build-${{ hashFiles('**/convert.c') }} - restore-keys: | - ${{ runner.os }}-build- - - - name: Create bin directory - run: mkdir -p bin - shell: bash - - # Linux setup - - name: Install dependencies (Linux) - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y libopenjp2-7-dev build-essential - shell: bash + version: 3.1.61 - # Windows setup - - name: Install vcpkg (Windows) - if: runner.os == 'Windows' - run: | - if (!(Test-Path vcpkg/.git)) { - git clone https://github.com/microsoft/vcpkg.git - cd vcpkg - .\bootstrap-vcpkg.bat - .\vcpkg.exe install openjpeg:x64-windows-static - } - shell: powershell - - - name: Setup MSVC for Windows - if: runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1 + - name: Cache OpenJPEG WASM build + uses: actions/cache@v4 + id: openjpeg-cache with: - arch: x64 + path: openjpeg-wasm + key: openjpeg-wasm-v2.5.2-${{ hashFiles('core/convert.c') }} + restore-keys: openjpeg-wasm-v2.5.2- - # MacOS setup - - name: Install dependencies (macOS) - if: runner.os == 'macOS' - run: | - brew update - brew install openjpeg cmake libtiff - shell: bash + - name: Clone OpenJPEG + if: steps.openjpeg-cache.outputs.cache-hit != 'true' + run: git clone --depth 1 --branch v2.5.2 https://github.com/uclouvain/openjpeg.git openjpeg-wasm - - name: Build static OpenJPEG (macOS) - if: runner.os == 'macOS' + - name: Build OpenJPEG to WASM + if: steps.openjpeg-cache.outputs.cache-hit != 'true' run: | - if [ ! -f "openjpeg/CMakeLists.txt" ]; then - rm -rf openjpeg - git clone --depth 1 https://github.com/uclouvain/openjpeg.git - fi - - cd openjpeg - mkdir -p build && cd build - cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DBUILD_CODEC=OFF -DBUILD_JPIP=OFF - make -j$(sysctl -n hw.ncpu) - sudo make install - shell: bash - - # Build steps for each platform - - name: Compile C binary (Linux) - if: runner.os == 'Linux' - run: | - gcc -I/usr/include/openjpeg-${{ env.OPENJPEG_VERSION }} core/convert.c -o bin/${{ matrix.binary_name }} -static -lopenjp2 -lm - shell: bash - - - name: Compile C binary (macOS) - if: runner.os == 'macOS' - run: | - clang core/convert.c -o bin/${{ matrix.binary_name }} -I/usr/local/include/openjpeg-${{ env.OPENJPEG_VERSION }} -L/usr/local/lib /usr/local/lib/libopenjp2.a -lm - shell: bash - - - name: Compile C binary (Windows) - if: runner.os == 'Windows' + mkdir -p openjpeg-wasm/build + cd openjpeg-wasm/build + emcmake cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_CODEC=OFF \ + -DBUILD_JPIP=OFF + make -j$(nproc) openjp2 + + - name: Build WASM module run: | - cl /O2 /I"$env:GITHUB_WORKSPACE\vcpkg\installed\x64-windows-static\include" /Fe:bin\${{ matrix.binary_name }} core\convert.c /link /LIBPATH:"$env:GITHUB_WORKSPACE\vcpkg\installed\x64-windows-static\lib" openjp2.lib - shell: powershell - - - name: Test binary + OPENJPEG_INCLUDE="openjpeg-wasm/src/lib/openjp2" + OPENJPEG_BUILD_INCLUDE="openjpeg-wasm/build/src/lib/openjp2" + OPENJPEG_LIB="openjpeg-wasm/build/bin/libopenjp2.a" + mkdir -p lib/src/wasm + emcc core/convert.c \ + -I "$OPENJPEG_INCLUDE" \ + -I "$OPENJPEG_BUILD_INCLUDE" \ + "$OPENJPEG_LIB" \ + -O3 \ + -s MODULARIZE=1 \ + -s ENVIRONMENT=node \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s EXPORTED_FUNCTIONS='["_decode_jp2","_free_buffer","_malloc","_free"]' \ + -s EXPORTED_RUNTIME_METHODS='["getValue","HEAPU8"]' \ + -o lib/src/wasm/openjp2.js + + - name: Verify WASM unchanged (drift check) run: | - ls -la bin/ - if [ -f "bin/${{ matrix.binary_name }}" ]; then - echo "Binary successfully built!" + if [ ! -f lib/src/wasm/openjp2.wasm ]; then + echo "::warning::No committed WASM found — this is the initial build. Commit lib/src/wasm/ to the repo." + elif git diff --quiet lib/src/wasm/; then + echo "WASM is up to date." else - echo "Binary build failed!" + echo "::error::Generated WASM differs from committed version. Run core/build-wasm.sh locally and commit the result." + git diff --stat lib/src/wasm/ exit 1 fi - shell: bash - - name: Upload binaries + - name: Upload WASM artifact uses: actions/upload-artifact@v4 with: - name: convert-${{ runner.os }} - path: bin/${{ matrix.binary_name }} + name: wasm-build + path: lib/src/wasm/ if-no-files-found: error + test: + name: Test (${{ matrix.os }}, Node ${{ matrix.node }}) + runs-on: ${{ matrix.os }} + needs: build-wasm + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + node: [18, 20, 22] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download WASM artifact + uses: actions/download-artifact@v4 + with: + name: wasm-build + path: lib/src/wasm/ + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + + - name: Enable Corepack and Setup Yarn + run: | + corepack enable + corepack prepare yarn@3.6.1 --activate + + - name: Install dependencies + run: cd lib && yarn install --immutable + + - name: Validate + run: cd lib && yarn validate + + - name: Test + run: cd lib && yarn test + release: name: Process Release runs-on: ubuntu-latest - needs: build + needs: [build-wasm, test] if: ${{ contains(github.event.head_commit.message, 'chore(main):') }} + steps: - - name: Checkout code + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - + - name: Process Release Please uses: googleapis/release-please-action@v4 id: release-please @@ -155,43 +148,38 @@ jobs: config-file: release-please-config.json manifest-file: .release-please-manifest.json token: ${{ secrets.GITHUB_TOKEN }} - - - name: Print release outputs for debugging + + - name: Print release outputs continue-on-error: true - run: | - echo "Release outputs:" - echo "${{ toJson(steps.release-please.outputs) }}" - - - name: Download artifacts + run: echo "${{ toJson(steps.release-please.outputs) }}" + + - name: Download WASM artifact if: ${{ steps.release-please.outputs.releases_created == 'true' }} uses: actions/download-artifact@v4 with: - pattern: convert-* - path: lib/src/bin/ - merge-multiple: true - - - name: Setup Node.js for NPM Publishing + name: wasm-build + path: lib/src/wasm/ + + - name: Setup Node.js if: ${{ steps.release-please.outputs.releases_created == 'true' }} uses: actions/setup-node@v4 with: node-version: 20 registry-url: 'https://registry.npmjs.org' - + - name: Enable Corepack and Setup Yarn if: ${{ steps.release-please.outputs.releases_created == 'true' }} run: | corepack enable corepack prepare yarn@3.6.1 --activate - yarn --version - + - name: Publish to NPM if: ${{ steps.release-please.outputs.releases_created == 'true' }} run: | cd lib yarn install --immutable yarn build - rm -rf src/bin/ yarn config set npmAuthToken ${NPM_AUTH_TOKEN} yarn npm publish --access public env: - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml deleted file mode 100644 index a027a91..0000000 --- a/.github/workflows/security-scan.yml +++ /dev/null @@ -1,73 +0,0 @@ -# .github/workflows/security-scan.yml -name: Security & Dependency Scanning - -on: - push: - branches: [ main ] - pull_request: - -permissions: - contents: read - security-events: write - -concurrency: - group: security-scan-${{ github.ref }} - cancel-in-progress: true - -jobs: - codeql: - name: 🔍 CodeQL Analysis - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: javascript - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - name: Analyze - uses: github/codeql-action/analyze@v3 - - grype: - name: 🧭 Grype Vulnerability Scan - runs-on: ubuntu-latest - needs: codeql - steps: - - uses: actions/checkout@v3 - - name: Enable Corepack and Setup Yarn - run: | - corepack enable - corepack prepare yarn@3.6.1 --activate - yarn --version - - name: Install dependencies with Yarn - run: | - cd lib - yarn install --immutable - - name: Run Grype scan on dependencies - id: grype - uses: anchore/scan-action@v6 - with: - path: "./lib" - output-format: sarif - output-file: grype-report.sarif - fail-build: true - severity-cutoff: critical - - name: Upload SARIF report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ./grype-report.sarif - - gitleaks: - name: 🔐 Gitleaks Secret Scan - runs-on: ubuntu-latest - needs: grype - steps: - - uses: actions/checkout@v3 - - name: Run Gitleaks - uses: zricethezav/gitleaks-action@v2 - with: - config: .github/.gitleaks.toml - enable-annotations: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index ede85ec..c191c1d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ target/ .classpath .settings/ convert -output.jpg \ No newline at end of file +output.jpg +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md index c331789..a3cce94 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,84 @@ # JPEG2000 Node Wrapper ## Overview -**JPEG2000 Node Wrapper** is a simple and efficient solution for converting JP2 images in TypeScript-based systems. It provides a seamless interface between Node.js and a C-based JPEG2000 converter, leveraging the performance and portability of native C code without the need for Java or other heavy runtimes. -This project consists of two main components: -1. **jp2-to-image** (Node.js Wrapper) - A TypeScript library that exposes a user-friendly API to convert JP2 images. -2. **core** (C Backend) - A C-based converter that performs the actual JP2 image processing using the OpenJPEG library. +**JPEG2000 Node Wrapper** is a lightweight solution for converting JP2 images in TypeScript/Node.js projects. It ships as a single WebAssembly module built from the OpenJPEG C library — no system dependencies, no compiler, no ImageMagick required. -This approach ensures compatibility across various environments, including Docker, without requiring additional heavyweight dependencies. +`npm install jp2-to-image` is all you need. Works identically on Linux (x64/ARM), macOS (Intel/Apple Silicon), Windows, Docker, and Lambda. --- ## Features -✅ Easy-to-use TypeScript API for JP2 image conversion -✅ Native performance using C and OpenJPEG -✅ No Java or JVM required -✅ Lightweight and production-ready for Docker environments -✅ Reliable and scalable solution for image processing -✅ Fully open-source and free for any use + +- Cross-platform with zero system dependencies (WASM runs everywhere V8 does) +- No Java, no ImageMagick, no `libopenjp2-7-dev` needed by end users +- Concurrency-safe (no temp files) +- Pure JS encoders for PNG and JPEG output +- Lightweight alternative to `sharp` for JP2-only workloads --- -## Prerequisites -Before using JPEG2000 Node Wrapper, ensure you have the following installed: +## Usage -- **Node.js** (version 16+ recommended) -- **A C compiler** (e.g., `gcc` or `clang`) -- **OpenJPEG development libraries** +```ts +import { JPEG2000NodeConverter } from 'jp2-to-image' -To install OpenJPEG on Ubuntu/Debian-based systems: -```sh -sudo apt-get update -sudo apt-get install libopenjp2-7-dev -``` +const converter = new JPEG2000NodeConverter() +const jp2Buffer = fs.readFileSync('image.jp2') -To check if the CLI converter is available: -```sh -opj_decompress -h +const png = await converter.convertImage(jp2Buffer, 'png') +const jpg = await converter.convertImage(jp2Buffer, 'jpg') ``` -Or if you're compiling your own C binary, make sure the `core/` folder contains the proper build scripts (`Makefile`, etc). +Supported output formats: `png`, `jpg` / `jpeg`. + +--- + +## Prerequisites (end users) + +- **Node.js 18+** + +That's it. --- ## Development -### Build the C Component -If you modify the C source code, rebuild the binary: +### Build the WASM module + +The WASM artifacts (`lib/src/wasm/openjp2.js` + `openjp2.wasm`) are pre-built and committed. If you modify `core/convert.c`, rebuild them: + ```sh -cd core/ -make +# Install Emscripten SDK first: https://emscripten.org/docs/getting_started/downloads.html +source /path/to/emsdk/emsdk_env.sh +./core/build-wasm.sh ``` -This will produce the executable used by the Node.js wrapper. + +Then commit the updated `lib/src/wasm/` files. CI will verify the committed WASM matches what it builds. ### Run Tests + ```sh -cd lib/ +cd lib +yarn install yarn test ``` --- -## License +## Architecture -This project is **completely free for any use** and is licensed under the **MIT License**. You can find the full license text in the `LICENSE` file in this repository. +``` +core/convert.c C source — decodes JP2 to raw RGB pixels via OpenJPEG +core/build-wasm.sh Emscripten build script → generates openjp2.js + openjp2.wasm +lib/src/wasm/ WASM artifacts (committed, generated by CI/build-wasm.sh) +lib/src/ TypeScript wrapper — uses pngjs/jpeg-js to encode output +``` -### Dependencies and Third-Party Licenses +--- -This project wraps the **OpenJPEG** library, which is licensed under the **BSD License**. +## License -### Important Notes +MIT. See `LICENSE`. -By using OpenJPEG and C, this project remains **lightweight**, **performant**, and **fully MIT-licensed**. 🚀 +This project uses [OpenJPEG](https://github.com/uclouvain/openjpeg) (BSD-2-Clause), [pngjs](https://github.com/pngjs/pngjs) (MIT), and [jpeg-js](https://github.com/jpeg-js/jpeg-js) (BSD-3-Clause). diff --git a/core/build-wasm.sh b/core/build-wasm.sh new file mode 100755 index 0000000..58048d0 --- /dev/null +++ b/core/build-wasm.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(dirname "$SCRIPT_DIR")" +OPENJPEG_DIR="$REPO_ROOT/openjpeg-wasm" +OUT_DIR="$REPO_ROOT/lib/src/wasm" + +echo "==> Checking Emscripten..." +if ! command -v emcc &>/dev/null; then + echo "Error: emcc not found. Activate the Emscripten SDK first:" + echo " source /path/to/emsdk/emsdk_env.sh" + exit 1 +fi +emcc --version + +# Build OpenJPEG as a static WASM library if not already built +if [ ! -f "$OPENJPEG_DIR/build/bin/libopenjp2.a" ]; then + echo "==> Cloning OpenJPEG v2.5.2..." + if [ ! -d "$OPENJPEG_DIR/.git" ]; then + git clone --depth 1 --branch v2.5.2 https://github.com/uclouvain/openjpeg.git "$OPENJPEG_DIR" + fi + + echo "==> Building OpenJPEG to WASM..." + mkdir -p "$OPENJPEG_DIR/build" + cd "$OPENJPEG_DIR/build" + emcmake cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_CODEC=OFF \ + -DBUILD_JPIP=OFF + make -j"$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)" openjp2 + cd "$REPO_ROOT" +else + echo "==> OpenJPEG WASM build found, skipping." +fi + +OPENJPEG_INCLUDE="$OPENJPEG_DIR/src/lib/openjp2" +OPENJPEG_BUILD_INCLUDE="$OPENJPEG_DIR/build/src/lib/openjp2" +OPENJPEG_LIB="$OPENJPEG_DIR/build/bin/libopenjp2.a" + +mkdir -p "$OUT_DIR" + +echo "==> Compiling convert.c to WASM..." +emcc "$SCRIPT_DIR/convert.c" \ + -I "$OPENJPEG_INCLUDE" \ + -I "$OPENJPEG_BUILD_INCLUDE" \ + "$OPENJPEG_LIB" \ + -O3 \ + -s MODULARIZE=1 \ + -s ENVIRONMENT=node \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s EXPORTED_FUNCTIONS='["_decode_jp2","_free_buffer","_malloc","_free"]' \ + -s EXPORTED_RUNTIME_METHODS='["getValue","HEAPU8"]' \ + -o "$OUT_DIR/openjp2.js" + +echo "==> Done. Generated:" +ls -lh "$OUT_DIR"/openjp2.* diff --git a/core/convert.c b/core/convert.c index 929bb3b..6010b27 100644 --- a/core/convert.c +++ b/core/convert.c @@ -2,213 +2,144 @@ #include #include #include - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - -#define TEMP_INPUT "temp_input.jp2" -#define TEMP_OUTPUT "temp_output.bin" - -// --- Callbacks --- -void error_callback(const char *msg, void *client_data) { (void)client_data; fprintf(stderr, "[ERROR] %s\n", msg); } -void warning_callback(const char *msg, void *client_data) { (void)client_data; fprintf(stderr, "[WARNING] %s\n", msg); } -void info_callback(const char *msg, void *client_data) { (void)client_data; fprintf(stderr, "[INFO] %s\n", msg); } - -static void write_to_file(void *context, void *data, int size) { - FILE *file = (FILE*)context; - fwrite(data, 1, size, file); - fflush(file); +#include + +#ifdef __EMSCRIPTEN__ +#include +#define EXPORT EMSCRIPTEN_KEEPALIVE +#else +#define EXPORT +#endif + +// --- OpenJPEG callbacks --- +static void error_callback(const char *msg, void *client_data) { (void)client_data; fprintf(stderr, "[ERROR] %s\n", msg); } +static void warning_callback(const char *msg, void *client_data) { (void)client_data; fprintf(stderr, "[WARNING] %s\n", msg); } + +// --- In-memory stream --- +typedef struct { + const uint8_t *data; + size_t length; + size_t position; +} mem_stream_t; + +static OPJ_SIZE_T mem_stream_read(void *buf, OPJ_SIZE_T nb, void *udata) { + mem_stream_t *ms = (mem_stream_t *)udata; + size_t remaining = ms->length - ms->position; + if (remaining == 0) return (OPJ_SIZE_T)-1; + OPJ_SIZE_T to_read = (nb < remaining) ? nb : (OPJ_SIZE_T)remaining; + memcpy(buf, ms->data + ms->position, to_read); + ms->position += to_read; + return to_read; } -int write_temp_input() { - FILE* f = fopen(TEMP_INPUT, "wb"); - if (!f) return -1; - - unsigned char buf[8192]; - size_t total = 0, read; - while ((read = fread(buf, 1, sizeof(buf), stdin)) > 0) { - fwrite(buf, 1, read, f); - total += read; - } - - fclose(f); - if (total == 0) return -2; - - fprintf(stderr, "Read %zu bytes from stdin\n", total); - return 0; +static OPJ_OFF_T mem_stream_skip(OPJ_OFF_T nb, void *udata) { + mem_stream_t *ms = (mem_stream_t *)udata; + size_t remaining = ms->length - ms->position; + if ((size_t)nb > remaining) nb = (OPJ_OFF_T)remaining; + ms->position += (size_t)nb; + return nb; } -int is_format_supported(const char* fmt) { - return strcmp(fmt, "png") == 0 || strcmp(fmt, "bmp") == 0 || strcmp(fmt, "jpg") == 0 || strcmp(fmt, "jpeg") == 0; +static OPJ_BOOL mem_stream_seek(OPJ_OFF_T nb, void *udata) { + mem_stream_t *ms = (mem_stream_t *)udata; + if ((size_t)nb > ms->length) return OPJ_FALSE; + ms->position = (size_t)nb; + return OPJ_TRUE; } -void cleanup_all(opj_image_t* img, opj_codec_t* codec, const char* reason) { - if (reason) fprintf(stderr, "Error: %s\n", reason); - if (img) opj_image_destroy(img); - if (codec) opj_destroy_codec(codec); - remove(TEMP_INPUT); - remove(TEMP_OUTPUT); -} +// --- Public API --- -int run_test() { - fprintf(stderr, "Running test...\n"); - int result = system("cat test.jp2 | ./convert jpg > output.jpg"); - - if (result != 0) { - fprintf(stderr, "Test failed with code %d\n", result); - return 1; - } - - FILE* f = fopen("output.jpg", "rb"); - if (!f) { - fprintf(stderr, "Test failed: output.jpg not created\n"); - return 1; - } - - fseek(f, 0, SEEK_END); - long size = ftell(f); - fclose(f); - - if (size <= 0) { - fprintf(stderr, "Test failed: output.jpg is empty\n"); - return 1; - } - - fprintf(stderr, "Test passed: output.jpg created (%ld bytes)\n", size); - return 0; -} - -int main(int argc, char *argv[]) { - // Run test mode - if (argc > 1 && strcmp(argv[1], "--test") == 0) { - return run_test(); - } - - const char* format = (argc > 1) ? argv[1] : "png"; - if (!is_format_supported(format)) { - fprintf(stderr, "Unsupported format: %s\nSupported: png, bmp, jpg, jpeg\n", format); - return 1; - } +/* + * Decodes a JP2 image from memory. + * Returns a malloc'd RGB(A) pixel buffer. Caller must free via free_buffer(). + * On error returns NULL. out_width/out_height/out_channels are set on success. + */ +EXPORT uint8_t *decode_jp2(const uint8_t *input, int input_len, + int *out_width, int *out_height, int *out_channels) { + if (!input || input_len <= 0) return NULL; - int input_status = write_temp_input(); - if (input_status != 0) { - fprintf(stderr, input_status == -1 ? "Could not create temp input\n" : "No data from stdin\n"); - return 1; - } + mem_stream_t ms = { input, (size_t)input_len, 0 }; - opj_codec_t* codec = opj_create_decompress(OPJ_CODEC_JP2); - if (!codec) { - remove(TEMP_INPUT); - fprintf(stderr, "Could not create decoder\n"); - return 1; - } + opj_codec_t *codec = opj_create_decompress(OPJ_CODEC_JP2); + if (!codec) return NULL; opj_set_error_handler(codec, error_callback, NULL); opj_set_warning_handler(codec, warning_callback, NULL); - - #ifdef DEBUG - opj_set_info_handler(codec, info_callback, NULL); - #endif opj_dparameters_t params; opj_set_default_decoder_parameters(¶ms); - if (!opj_setup_decoder(codec, ¶ms)) { - cleanup_all(NULL, codec, "setup decoder failed"); - return 1; + opj_destroy_codec(codec); + return NULL; } - opj_stream_t* stream = opj_stream_create_default_file_stream(TEMP_INPUT, OPJ_TRUE); + opj_stream_t *stream = opj_stream_create((OPJ_SIZE_T)input_len, OPJ_TRUE); if (!stream) { - cleanup_all(NULL, codec, "stream creation failed"); - return 1; + opj_destroy_codec(codec); + return NULL; } - opj_image_t* image = NULL; - if (!opj_read_header(stream, codec, &image) || !opj_decode(codec, stream, image) || !opj_end_decompress(codec, stream)) { - opj_stream_destroy(stream); - cleanup_all(image, codec, "decoding failed"); - return 1; - } + opj_stream_set_user_data(stream, &ms, NULL); + opj_stream_set_user_data_length(stream, (OPJ_UINT64)input_len); + opj_stream_set_read_function(stream, mem_stream_read); + opj_stream_set_skip_function(stream, mem_stream_skip); + opj_stream_set_seek_function(stream, mem_stream_seek); + + opj_image_t *image = NULL; + int ok = opj_read_header(stream, codec, &image) + && opj_decode(codec, stream, image) + && opj_end_decompress(codec, stream); opj_stream_destroy(stream); + opj_destroy_codec(codec); + + if (!ok) { + if (image) opj_image_destroy(image); + return NULL; + } if (image->numcomps < 3) { - cleanup_all(image, codec, "less than 3 components (RGB required)"); - return 1; + opj_image_destroy(image); + return NULL; } - int width = image->comps[0].w, height = image->comps[0].h; - for (int i = 1; i < 3; ++i) - if (image->comps[i].w != width || image->comps[i].h != height) { - cleanup_all(image, codec, "component size mismatch"); - return 1; + int width = (int)image->comps[0].w; + int height = (int)image->comps[0].h; + int channels = (image->numcomps >= 4) ? 4 : 3; + + for (int i = 1; i < channels; i++) { + if ((int)image->comps[i].w != width || (int)image->comps[i].h != height) { + opj_image_destroy(image); + return NULL; } + } - unsigned char* rgb = malloc(width * height * 3); - if (!rgb) { - cleanup_all(image, codec, "memory allocation failed"); - return 1; + uint8_t *pixels = (uint8_t *)malloc((size_t)(width * height * channels)); + if (!pixels) { + opj_image_destroy(image); + return NULL; } int max_val = (1 << image->comps[0].prec) - 1; - for (int i = 0; i < width * height; ++i) { - for (int c = 0; c < 3; ++c) { + for (int i = 0; i < width * height; i++) { + for (int c = 0; c < channels; c++) { int val = image->comps[c].data[i]; - if (image->comps[c].sgnd) val += max_val / 2; + if (image->comps[c].sgnd) val += (max_val + 1) / 2; val = (val * 255) / max_val; - rgb[i * 3 + c] = (unsigned char)(val < 0 ? 0 : val > 255 ? 255 : val); + if (val < 0) val = 0; + if (val > 255) val = 255; + pixels[i * channels + c] = (uint8_t)val; } } - FILE* fout = fopen(TEMP_OUTPUT, "wb"); - if (!fout) { - free(rgb); - cleanup_all(image, codec, "could not create temp file output"); - return 1; - } - - int success = 0; - if (strcmp(format, "png") == 0) - success = stbi_write_png_to_func(write_to_file, fout, width, height, 3, rgb, width * 3); - else if (strcmp(format, "bmp") == 0) - success = stbi_write_bmp_to_func(write_to_file, fout, width, height, 3, rgb); - else - success = stbi_write_jpg_to_func(write_to_file, fout, width, height, 3, rgb, 90); - - fclose(fout); - free(rgb); opj_image_destroy(image); - opj_destroy_codec(codec); - - if (!success) { - remove(TEMP_INPUT); remove(TEMP_OUTPUT); - fprintf(stderr, "image write failed\n"); - return 1; - } - FILE* out = fopen(TEMP_OUTPUT, "rb"); - if (!out) { - remove(TEMP_INPUT); remove(TEMP_OUTPUT); - fprintf(stderr, "could not open output file\n"); - return 1; - } - - unsigned char buf[8192]; - size_t r; - while ((r = fread(buf, 1, sizeof(buf), out)) > 0) { - if (fwrite(buf, 1, r, stdout) != r) { - fclose(out); - remove(TEMP_INPUT); remove(TEMP_OUTPUT); - fprintf(stderr, "stdout write failed\n"); - return 1; - } - } - fclose(out); - fflush(stdout); + *out_width = width; + *out_height = height; + *out_channels = channels; + return pixels; +} - remove(TEMP_INPUT); - remove(TEMP_OUTPUT); - fprintf(stderr, "Conversion to %s complete\n", format); - return 0; +EXPORT void free_buffer(uint8_t *ptr) { + free(ptr); } diff --git a/lib/eslint.config.js b/lib/eslint.config.js new file mode 100644 index 0000000..1325cb3 --- /dev/null +++ b/lib/eslint.config.js @@ -0,0 +1,55 @@ +const tsPlugin = require('@typescript-eslint/eslint-plugin') +const tsParser = require('@typescript-eslint/parser') +const importPlugin = require('eslint-plugin-import') +const prettierPlugin = require('eslint-plugin-prettier') +const eslintConfigPrettier = require('eslint-config-prettier') + +module.exports = [ + { + ignores: ['build/**', 'node_modules/**', 'eslint.config.js'], + }, + { + files: ['src/**/*.ts', 'tests/**/*.ts'], + plugins: { + '@typescript-eslint': tsPlugin, + import: importPlugin, + prettier: prettierPlugin, + }, + languageOptions: { + parser: tsParser, + parserOptions: { + project: 'tsconfig.eslint.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + }, + settings: { + 'import/resolver': { + typescript: { + project: 'tsconfig.eslint.json', + }, + }, + }, + rules: { + ...tsPlugin.configs.recommended.rules, + ...eslintConfigPrettier.rules, + 'prettier/prettier': 'error', + 'arrow-body-style': 'off', + 'prefer-arrow-callback': 'off', + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'import/no-cycle': 'error', + 'import/newline-after-import': ['error', { count: 1 }], + 'import/order': [ + 'error', + { + groups: ['type', ['builtin', 'external'], 'parent', 'sibling', 'index'], + alphabetize: { order: 'asc' }, + 'newlines-between': 'always', + }, + ], + }, + }, +] diff --git a/lib/package.json b/lib/package.json index 1c21e36..633c17f 100644 --- a/lib/package.json +++ b/lib/package.json @@ -21,7 +21,7 @@ "prettier": "prettier '**/*.+(js|json|ts|tsx|md|yml|yaml)'", "format": "yarn prettier --write", "check-format": "yarn prettier --list-different", - "build": "yarn run clean && yarn run compile && cpx 'src/bin/**/*' build/bin", + "build": "yarn run clean && yarn run compile && cpx 'src/wasm/**/*' build/wasm", "clean": "rimraf -rf ./build", "compile": "tsc -p tsconfig.json", "prepublishOnly": "yarn run build", @@ -36,17 +36,16 @@ "jp2" ], "files": [ - "build", - "bin" + "build" ], "publishConfig": { "access": "public" }, "devDependencies": { - "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.14", "@types/node": "^24.0.4", - "@typescript-eslint/eslint-plugin": "^6.0.0", + "@types/pngjs": "^6.0.5", + "@typescript-eslint/eslint-plugin": "^8.35.0", "@typescript-eslint/parser": "^8.35.0", "cpx": "^1.5.0", "eslint": "^9.30.0", @@ -67,6 +66,7 @@ }, "packageManager": "yarn@3.6.1", "dependencies": { - "fs-extra": "^11.3.0" + "jpeg-js": "^0.4.4", + "pngjs": "^7.0.0" } } diff --git a/lib/src/JPEG2000NodeConverter.ts b/lib/src/JPEG2000NodeConverter.ts index afc9996..7532493 100644 --- a/lib/src/JPEG2000NodeConverter.ts +++ b/lib/src/JPEG2000NodeConverter.ts @@ -1,74 +1,86 @@ -import { spawn } from 'child_process' -import * as fs from 'fs-extra' -import * as path from 'path' +import type { OpenjpegModule } from './wasm/openjp2' + +import * as jpeg from 'jpeg-js' +import { PNG } from 'pngjs' export class JPEG2000NodeConverter { - private binPath: string + private static modulePromise: Promise | null = null + + private static getModule(): Promise { + if (!JPEG2000NodeConverter.modulePromise) { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const createModule = require('./wasm/openjp2') as ( + options?: Record, + ) => Promise + JPEG2000NodeConverter.modulePromise = createModule() + } + return JPEG2000NodeConverter.modulePromise + } + + async convertImage(inputBuffer: Buffer, format: string = 'png'): Promise { + const mod = await JPEG2000NodeConverter.getModule() + return this._convert(mod, inputBuffer, format) + } + + private _convert(mod: OpenjpegModule, inputBuffer: Buffer, format: string): Buffer { + const inputPtr = mod._malloc(inputBuffer.length) + if (!inputPtr) throw new Error('WASM memory allocation failed for input') + + const widthPtr = mod._malloc(4) + const heightPtr = mod._malloc(4) + const channelsPtr = mod._malloc(4) - constructor() { - const platform = process.platform - let binFileName = 'convert' // base path + try { + mod.HEAPU8.set(inputBuffer, inputPtr) + + const pixelsPtr = mod._decode_jp2(inputPtr, inputBuffer.length, widthPtr, heightPtr, channelsPtr) + if (!pixelsPtr) throw new Error('JP2 decoding failed: invalid or unsupported image') - if (platform === 'win32') binFileName = 'convert-win.exe' - else if (platform === 'darwin') binFileName = 'convert-mac' - else if (platform === 'linux') binFileName = 'convert-linux' - else throw new Error(`Unsupported platform: ${platform}`) + const width = mod.getValue(widthPtr, 'i32') + const height = mod.getValue(heightPtr, 'i32') + const channels = mod.getValue(channelsPtr, 'i32') - this.binPath = path.resolve(__dirname, 'bin', binFileName) + const pixelCount = width * height * channels + const pixels = Buffer.from(mod.HEAPU8.buffer, pixelsPtr, pixelCount) + // Copy before freeing WASM memory + const pixelsCopy = Buffer.from(pixels) + mod._free_buffer(pixelsPtr) - if (platform !== 'win32') { - fs.ensureFileSync(this.binPath) - fs.chmodSync(this.binPath, 0o755) + return this._encode(pixelsCopy, width, height, channels, format) + } finally { + mod._free(inputPtr) + mod._free(widthPtr) + mod._free(heightPtr) + mod._free(channelsPtr) } } - /** - * Handles the conversion process - * @param inputBuffer The input image buffer - * @param format Target format - * @returns Converted image as a buffer - */ - async convertImage(inputBuffer: Buffer, format: string = 'png'): Promise { - try { - const convertedBuffer = await this._executeJar(inputBuffer, format) - return convertedBuffer - } catch (error) { - console.error(`Error converting to ${format}:`, error) - throw error + private _encode(pixels: Buffer, width: number, height: number, channels: number, format: string): Buffer { + const fmt = format.toLowerCase() + const rgba = channels === 4 ? pixels : this._rgbToRgba(pixels, width, height) + + if (fmt === 'png') { + const png = new PNG({ width, height }) + png.data = rgba + return PNG.sync.write(png) } + + if (fmt === 'jpg' || fmt === 'jpeg') { + const result = jpeg.encode({ data: rgba, width, height }, 90) + return result.data as Buffer + } + + throw new Error(`Unsupported format: "${format}". Supported formats: png, jpg, jpeg`) } - /** - * Executes the conversion binary, sending the image via stdin and receiving the result via stdout. - * - * @param inputBuffer The input image as a Buffer - * @param format The desired output format (e.g., "jpg", "png", "jp2") - * @returns The converted image as a Buffer - */ - private _executeJar(inputBuffer: Buffer, format: string): Promise { - return new Promise((resolve, reject) => { - const process = spawn(this.binPath, [format]) - - const outputBuffer: Buffer[] = [] - let errorOutput = '' - - // Capture stdout as Buffer - process.stdout.on('data', data => outputBuffer.push(data)) - - // Capture stderr - process.stderr.on('data', data => (errorOutput += data.toString())) - - process.on('close', code => { - if (code !== 0) { - reject(new Error(`Process failed with code ${code}: ${errorOutput}`)) - } else { - resolve(Buffer.concat(outputBuffer)) // Combine all received chunks - } - }) - - // Write image buffer to process and close stdin - process.stdin.write(inputBuffer) - process.stdin.end() - }) + private _rgbToRgba(rgb: Buffer, width: number, height: number): Buffer { + const rgba = Buffer.alloc(width * height * 4) + for (let i = 0; i < width * height; i++) { + rgba[i * 4] = rgb[i * 3] + rgba[i * 4 + 1] = rgb[i * 3 + 1] + rgba[i * 4 + 2] = rgb[i * 3 + 2] + rgba[i * 4 + 3] = 255 + } + return rgba } } diff --git a/lib/src/wasm/openjp2.d.ts b/lib/src/wasm/openjp2.d.ts new file mode 100644 index 0000000..26266d7 --- /dev/null +++ b/lib/src/wasm/openjp2.d.ts @@ -0,0 +1,21 @@ +// Type declarations for the Emscripten-generated WASM module (CommonJS format). +// The actual openjp2.js + openjp2.wasm are generated by core/build-wasm.sh. + +export interface OpenjpegModule { + _decode_jp2( + inputPtr: number, + inputLen: number, + widthPtr: number, + heightPtr: number, + channelsPtr: number, + ): number + _free_buffer(ptr: number): void + _malloc(size: number): number + _free(ptr: number): void + HEAPU8: Uint8Array + getValue(ptr: number, type: string): number +} + +declare function createOpenjpegModule(options?: Record): Promise + +export = createOpenjpegModule diff --git a/lib/tests/JPEG2000NodeConverter.test.ts b/lib/tests/JPEG2000NodeConverter.test.ts index fe6644f..2d47d01 100644 --- a/lib/tests/JPEG2000NodeConverter.test.ts +++ b/lib/tests/JPEG2000NodeConverter.test.ts @@ -3,20 +3,54 @@ import * as path from 'path' import { JPEG2000NodeConverter } from '../src' +const FIXTURE = path.join(__dirname, '..', 'assets', 'test.jp2') + describe('JPEG2000NodeConverter', () => { - const inputFilePath = path.join(__dirname, '..', 'assets', 'test.jp2') + let inputBuffer: Buffer - it('should convert test.jp2 to jpg', async () => { - const inputBuffer = fs.readFileSync(inputFilePath) + beforeAll(() => { + inputBuffer = fs.readFileSync(FIXTURE) + }) + it('converts test.jp2 to jpg', async () => { const start = Date.now() - const outputBuffer = await new JPEG2000NodeConverter().convertImage(inputBuffer, 'jpg') - const duration = Date.now() - start + const output = await new JPEG2000NodeConverter().convertImage(inputBuffer, 'jpg') + console.log(`JP2 → JPG took ${Date.now() - start}ms`) + + expect(output.length).toBeGreaterThan(0) + expect(output.toString('base64')).toMatch(/^([A-Za-z0-9+/=]+)$/) + // JPEG magic bytes: FF D8 FF + expect(output[0]).toBe(0xff) + expect(output[1]).toBe(0xd8) + expect(output[2]).toBe(0xff) + }) + + it('converts test.jp2 to png', async () => { + const output = await new JPEG2000NodeConverter().convertImage(inputBuffer, 'png') + + expect(output.length).toBeGreaterThan(0) + // PNG magic bytes: 89 50 4E 47 + expect(output[0]).toBe(0x89) + expect(output[1]).toBe(0x50) + expect(output[2]).toBe(0x4e) + expect(output[3]).toBe(0x47) + }) + + it('handles concurrent conversions correctly', async () => { + const results = await Promise.all( + Array.from({ length: 10 }, () => new JPEG2000NodeConverter().convertImage(inputBuffer, 'png')), + ) - console.log(`Conversion took ${duration}ms`) + // All results must be identical (regression for temp-file concurrency bug) + const reference = results[0].toString('base64') + for (const result of results) { + expect(result.toString('base64')).toBe(reference) + } + }) - const outputBase64 = outputBuffer.toString('base64') - expect(outputBase64).toMatch(/^([A-Za-z0-9+/=]+)$/) - expect(outputBase64.length).toBeGreaterThan(0) - }, 15000) + it('throws on unsupported format', async () => { + await expect(new JPEG2000NodeConverter().convertImage(inputBuffer, 'bmp')).rejects.toThrow( + /Unsupported format/, + ) + }) }) diff --git a/lib/yarn.lock b/lib/yarn.lock index c63735b..0539c25 100644 --- a/lib/yarn.lock +++ b/lib/yarn.lock @@ -421,7 +421,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": +"@eslint-community/eslint-utils@npm:^4.2.0": version: 4.6.1 resolution: "@eslint-community/eslint-utils@npm:4.6.1" dependencies: @@ -432,13 +432,31 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.5.1": +"@eslint-community/eslint-utils@npm:^4.9.1": + version: 4.9.1 + resolution: "@eslint-community/eslint-utils@npm:4.9.1" + dependencies: + eslint-visitor-keys: ^3.4.3 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 0a27c2d676c4be6b329ebb5dd8f6c5ef5fae9a019ff575655306d72874bb26f3ab20e0b241a5f086464bb1f2511ca26a29ff6f80c1e2b0b02eca4686b4dfe1b5 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.12.1": version: 4.12.1 resolution: "@eslint-community/regexpp@npm:4.12.1" checksum: 0d628680e204bc316d545b4993d3658427ca404ae646ce541fcc65306b8c712c340e5e573e30fb9f85f4855c0c5f6dca9868931f2fcced06417fbe1a0c6cd2d6 languageName: node linkType: hard +"@eslint-community/regexpp@npm:^4.12.2": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 1770bc81f676a72f65c7200b5675ff7a349786521f30e66125faaf767fde1ba1c19c3790e16ba8508a62a3933afcfc806a893858b3b5906faf693d862b9e4120 + languageName: node + linkType: hard + "@eslint/config-array@npm:^0.21.0": version: 0.21.0 resolution: "@eslint/config-array@npm:0.21.0" @@ -1070,16 +1088,6 @@ __metadata: languageName: node linkType: hard -"@types/fs-extra@npm:^11.0.4": - version: 11.0.4 - resolution: "@types/fs-extra@npm:11.0.4" - dependencies: - "@types/jsonfile": "*" - "@types/node": "*" - checksum: 242cb84157631f057f76495c8220707541882c00a00195b603d937fb55e471afecebcb089bab50233ed3a59c69fd68bf65c1f69dd7fafe2347e139cc15b9b0e5 - languageName: node - linkType: hard - "@types/graceful-fs@npm:^4.1.3": version: 4.1.9 resolution: "@types/graceful-fs@npm:4.1.9" @@ -1124,7 +1132,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.15": +"@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 @@ -1138,15 +1146,6 @@ __metadata: languageName: node linkType: hard -"@types/jsonfile@npm:*": - version: 6.1.4 - resolution: "@types/jsonfile@npm:6.1.4" - dependencies: - "@types/node": "*" - checksum: 309fda20eb5f1cf68f2df28931afdf189c5e7e6bec64ac783ce737bb98908d57f6f58757ad5da9be37b815645a6f914e2d4f3ac66c574b8fe1ba6616284d0e97 - languageName: node - linkType: hard - "@types/node@npm:*": version: 22.14.1 resolution: "@types/node@npm:22.14.1" @@ -1165,10 +1164,12 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.5.0": - version: 7.7.0 - resolution: "@types/semver@npm:7.7.0" - checksum: d488eaeddb23879a0a8a759bed667e1a76cb0dd4d23e3255538e24c189db387357953ca9e7a3bda2bb7f95e84cac8fe0db4fbe6b3456e893043337732d1d23cc +"@types/pngjs@npm:^6.0.5": + version: 6.0.5 + resolution: "@types/pngjs@npm:6.0.5" + dependencies: + "@types/node": "*" + checksum: 132fce25817d47a784ed48aa678332521b0f7e6edbaa76f3fa4e9ca1228078788ae712f99ad4d1a324d9ba0b14829958677eabf3ebef1fb6e120816f433f0cd8 languageName: node linkType: hard @@ -1195,28 +1196,23 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.0.0": - version: 6.21.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" +"@typescript-eslint/eslint-plugin@npm:^8.35.0": + version: 8.59.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.59.1" dependencies: - "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.21.0 - "@typescript-eslint/type-utils": 6.21.0 - "@typescript-eslint/utils": 6.21.0 - "@typescript-eslint/visitor-keys": 6.21.0 - debug: ^4.3.4 - graphemer: ^1.4.0 - ignore: ^5.2.4 + "@eslint-community/regexpp": ^4.12.2 + "@typescript-eslint/scope-manager": 8.59.1 + "@typescript-eslint/type-utils": 8.59.1 + "@typescript-eslint/utils": 8.59.1 + "@typescript-eslint/visitor-keys": 8.59.1 + ignore: ^7.0.5 natural-compare: ^1.4.0 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 + ts-api-utils: ^2.5.0 peerDependencies: - "@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 5ef2c502255e643e98051e87eb682c2a257e87afd8ec3b9f6274277615e1c2caf3131b352244cfb1987b8b2c415645eeacb9113fa841fc4c9b2ac46e8aed6efd + "@typescript-eslint/parser": ^8.59.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: d3caa173634d374fefa232047f19754a432d5ed3d181b19ba6220b1f403abe3d7cf994374a35e4ab4a7c7775329ea98baf63f263d1d9449b4c2ab9ecdcdb16c7 languageName: node linkType: hard @@ -1249,13 +1245,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/scope-manager@npm:6.21.0" +"@typescript-eslint/project-service@npm:8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/project-service@npm:8.59.1" dependencies: - "@typescript-eslint/types": 6.21.0 - "@typescript-eslint/visitor-keys": 6.21.0 - checksum: 71028b757da9694528c4c3294a96cc80bc7d396e383a405eab3bc224cda7341b88e0fc292120b35d3f31f47beac69f7083196c70616434072fbcd3d3e62d3376 + "@typescript-eslint/tsconfig-utils": ^8.59.1 + "@typescript-eslint/types": ^8.59.1 + debug: ^4.4.3 + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 6183c2328c1f8a65b462d70b3b7e7271ccd0964f343714f9a35c799299c883639c945d70e92eae19efb3e1846cebb1e6fe50fe6fbd5e42f2194496695be01de6 languageName: node linkType: hard @@ -1269,6 +1268,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/scope-manager@npm:8.59.1" + dependencies: + "@typescript-eslint/types": 8.59.1 + "@typescript-eslint/visitor-keys": 8.59.1 + checksum: 845c8a6f9cd19948c93946e030bebb6a81ec0f47453e407bbd060987191ff2c1f958933300fb895bf4ea1b6868ab46bd2f8a23c7a6c64cd25d8c94f29597ee2c + languageName: node + linkType: hard + "@typescript-eslint/tsconfig-utils@npm:8.35.0, @typescript-eslint/tsconfig-utils@npm:^8.35.0": version: 8.35.0 resolution: "@typescript-eslint/tsconfig-utils@npm:8.35.0" @@ -1278,27 +1287,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/type-utils@npm:6.21.0" - dependencies: - "@typescript-eslint/typescript-estree": 6.21.0 - "@typescript-eslint/utils": 6.21.0 - debug: ^4.3.4 - ts-api-utils: ^1.0.1 +"@typescript-eslint/tsconfig-utils@npm:8.59.1, @typescript-eslint/tsconfig-utils@npm:^8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.59.1" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 77025473f4d80acf1fafcce99c5c283e557686a61861febeba9c9913331f8a41e930bf5cd8b7a54db502a57b6eb8ea6d155cbd4f41349ed00e3d7aeb1f477ddc + typescript: ">=4.8.4 <6.1.0" + checksum: b5f1835aaf60a2d629f8c7e6eae964914edb56c853a9558a5887d6ac4ab38630540ff17b0967c749b8aea5df235b7dba8bff3f3c46f17b89b182b39d1ad07911 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/types@npm:6.21.0" - checksum: 9501b47d7403417af95fc1fb72b2038c5ac46feac0e1598a46bcb43e56a606c387e9dcd8a2a0abe174c91b509f2d2a8078b093786219eb9a01ab2fbf9ee7b684 +"@typescript-eslint/type-utils@npm:8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/type-utils@npm:8.59.1" + dependencies: + "@typescript-eslint/types": 8.59.1 + "@typescript-eslint/typescript-estree": 8.59.1 + "@typescript-eslint/utils": 8.59.1 + debug: ^4.4.3 + ts-api-utils: ^2.5.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 3261116b476d2b12c62b0919e7f701aed38eee41cacf7fd1dd03f5d2347c68d2aae8fc6223fbbc08fa2dfbf076b146d6a29f86fae2a004f3eeec0aa6858ec5cf languageName: node linkType: hard @@ -1309,22 +1319,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" - dependencies: - "@typescript-eslint/types": 6.21.0 - "@typescript-eslint/visitor-keys": 6.21.0 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - minimatch: 9.0.3 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 - peerDependenciesMeta: - typescript: - optional: true - checksum: dec02dc107c4a541e14fb0c96148f3764b92117c3b635db3a577b5a56fc48df7a556fa853fb82b07c0663b4bf2c484c9f245c28ba3e17e5cb0918ea4cab2ea21 +"@typescript-eslint/types@npm:8.59.1, @typescript-eslint/types@npm:^8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/types@npm:8.59.1" + checksum: 2b2033e844a3b5206971a13f27278b0a47657c39c9bed53c801a599c91b2d075943dbb4159aa2f91843a8993b9c5545c8e471e405c0584d44e3504c4f3b2c4bf languageName: node linkType: hard @@ -1348,30 +1346,37 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/utils@npm:6.21.0" - dependencies: - "@eslint-community/eslint-utils": ^4.4.0 - "@types/json-schema": ^7.0.12 - "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.21.0 - "@typescript-eslint/types": 6.21.0 - "@typescript-eslint/typescript-estree": 6.21.0 - semver: ^7.5.4 +"@typescript-eslint/typescript-estree@npm:8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.59.1" + dependencies: + "@typescript-eslint/project-service": 8.59.1 + "@typescript-eslint/tsconfig-utils": 8.59.1 + "@typescript-eslint/types": 8.59.1 + "@typescript-eslint/visitor-keys": 8.59.1 + debug: ^4.4.3 + minimatch: ^10.2.2 + semver: ^7.7.3 + tinyglobby: ^0.2.15 + ts-api-utils: ^2.5.0 peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: b129b3a4aebec8468259f4589985cb59ea808afbfdb9c54f02fad11e17d185e2bf72bb332f7c36ec3c09b31f18fc41368678b076323e6e019d06f74ee93f7bf2 + typescript: ">=4.8.4 <6.1.0" + checksum: 9ea9e7bf2bf5ca94dd1e4b0f92872b4969aff9a0789eaaf3d1410d1bd820f15b7a4c53a17f9f550a175b92fee1685bb9ad1e99d0c84f32b303da6c9e6825d949 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" +"@typescript-eslint/utils@npm:8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/utils@npm:8.59.1" dependencies: - "@typescript-eslint/types": 6.21.0 - eslint-visitor-keys: ^3.4.1 - checksum: 67c7e6003d5af042d8703d11538fca9d76899f0119130b373402819ae43f0bc90d18656aa7add25a24427ccf1a0efd0804157ba83b0d4e145f06107d7d1b7433 + "@eslint-community/eslint-utils": ^4.9.1 + "@typescript-eslint/scope-manager": 8.59.1 + "@typescript-eslint/types": 8.59.1 + "@typescript-eslint/typescript-estree": 8.59.1 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 0788a6d2e4ff0e4903bb5aa5604ff2e670b34ca967db932c8f3940a7da7f6ca4ce6a29f20618ed1e31ea361a07798f7c65c4a0ed945fc22b2aa8a3cc1455639b languageName: node linkType: hard @@ -1385,6 +1390,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.59.1": + version: 8.59.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.59.1" + dependencies: + "@typescript-eslint/types": 8.59.1 + eslint-visitor-keys: ^5.0.0 + checksum: 05abd1078037abf3e8b006d9abef0601032c4f7e510a2af2d61e9190076da3f33d6d2d7a7926b90a65b6bac7616a008818fbcdf43c76db2c4eb898cb96478396 + languageName: node + linkType: hard + "@unrs/resolver-binding-android-arm-eabi@npm:1.9.1": version: 1.9.1 resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.9.1" @@ -1727,13 +1742,6 @@ __metadata: languageName: node linkType: hard -"array-union@npm:^2.1.0": - version: 2.1.0 - resolution: "array-union@npm:2.1.0" - checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d - languageName: node - linkType: hard - "array-unique@npm:^0.2.1": version: 0.2.1 resolution: "array-unique@npm:0.2.1" @@ -1944,6 +1952,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0 + languageName: node + linkType: hard + "base@npm:^0.11.1": version: 0.11.2 resolution: "base@npm:0.11.2" @@ -1994,6 +2009,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.5": + version: 5.0.5 + resolution: "brace-expansion@npm:5.0.5" + dependencies: + balanced-match: ^4.0.2 + checksum: 4481b7ffa467b34c14e258167dbd8d9485a2d31d03060e8e8b38142dcde32cdc89c8f55b04d3ae7aae9304fa7eac1dfafd602787cf09c019cc45de3bb6950ffc + languageName: node + linkType: hard + "braces@npm:^1.8.2": version: 1.8.5 resolution: "braces@npm:1.8.5" @@ -2462,6 +2486,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.3": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: ^2.1.3 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 4805abd570e601acdca85b6aa3757186084a45cff9b2fa6eee1f3b173caa776b45f478b2a71a572d616d2010cea9211d0ac4a02a610e4c18ac4324bde3760834 + languageName: node + linkType: hard + "decode-uri-component@npm:^0.2.0": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" @@ -2566,15 +2602,6 @@ __metadata: languageName: node linkType: hard -"dir-glob@npm:^3.0.1": - version: 3.0.1 - resolution: "dir-glob@npm:3.0.1" - dependencies: - path-type: ^4.0.0 - checksum: fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 - languageName: node - linkType: hard - "doctrine@npm:^2.1.0": version: 2.1.0 resolution: "doctrine@npm:2.1.0" @@ -3009,7 +3036,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 @@ -3023,6 +3050,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^5.0.0": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: d6cc6830536ab4a808f25325686c2c27862f27aab0c1ffed39627293b06cee05d95187da113cafd366314ea5be803b456115de71ad625e365020f20e2a6af89b + languageName: node + linkType: hard + "eslint@npm:^9.30.0": version: 9.30.0 resolution: "eslint@npm:9.30.0" @@ -3261,7 +3295,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.3.2": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -3318,6 +3352,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: bd537daa9d3cd53887eed35efa0eab2dbb1ca408790e10e024120e7a36c6e9ae2b33710cb8381e35def01bc9c1d7eaba746f886338413e68ff6ebaee07b9a6e8 + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -3472,17 +3518,6 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.3.0": - version: 11.3.0 - resolution: "fs-extra@npm:11.3.0" - dependencies: - graceful-fs: ^4.2.0 - jsonfile: ^6.0.1 - universalify: ^2.0.0 - checksum: f983c706e0c22b0c0747a8e9c76aed6f391ba2d76734cf2757cd84da13417b402ed68fe25bace65228856c61d36d3b41da198f1ffbf33d0b34283a2f7a62c6e9 - languageName: node - linkType: hard - "fs-minipass@npm:^3.0.0": version: 3.0.3 resolution: "fs-minipass@npm:3.0.3" @@ -3766,20 +3801,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.1.0": - version: 11.1.0 - resolution: "globby@npm:11.1.0" - dependencies: - array-union: ^2.1.0 - dir-glob: ^3.0.1 - fast-glob: ^3.2.9 - ignore: ^5.2.0 - merge2: ^1.4.1 - slash: ^3.0.0 - checksum: b4be8885e0cfa018fc783792942d53926c35c50b3aefd3fdcfb9d22c627639dc26bd2327a40a0b74b074100ce95bb7187bfeae2f236856aa3de183af7a02aea6 - languageName: node - linkType: hard - "gopd@npm:^1.0.1, gopd@npm:^1.2.0": version: 1.2.0 resolution: "gopd@npm:1.2.0" @@ -3787,20 +3808,13 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 languageName: node linkType: hard -"graphemer@npm:^1.4.0": - version: 1.4.0 - resolution: "graphemer@npm:1.4.0" - checksum: bab8f0be9b568857c7bec9fda95a89f87b783546d02951c40c33f84d05bb7da3fd10f863a9beb901463669b6583173a8c8cc6d6b306ea2b9b9d5d3d943c3a673 - languageName: node - linkType: hard - "has-bigints@npm:^1.0.2": version: 1.1.0 resolution: "has-bigints@npm:1.1.0" @@ -3947,13 +3961,20 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.2.0, ignore@npm:^5.2.4": +"ignore@npm:^5.2.0": version: 5.3.2 resolution: "ignore@npm:5.3.2" checksum: 2acfd32a573260ea522ea0bfeff880af426d68f6831f973129e2ba7363f422923cf53aab62f8369cbf4667c7b25b6f8a3761b34ecdb284ea18e87a5262a865be languageName: node linkType: hard +"ignore@npm:^7.0.5": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: d0862bf64d3d58bf34d5fb0a9f725bec9ca5ce8cd1aecc8f28034269e8f69b8009ffd79ca3eda96962a6a444687781cd5efdb8c7c8ddc0a6996e36d31c217f14 + languageName: node + linkType: hard + "import-fresh@npm:^3.2.1": version: 3.3.1 resolution: "import-fresh@npm:3.3.1" @@ -5035,10 +5056,10 @@ __metadata: version: 0.0.0-use.local resolution: "jp2-to-image@workspace:." dependencies: - "@types/fs-extra": ^11.0.4 "@types/jest": ^29.5.14 "@types/node": ^24.0.4 - "@typescript-eslint/eslint-plugin": ^6.0.0 + "@types/pngjs": ^6.0.5 + "@typescript-eslint/eslint-plugin": ^8.35.0 "@typescript-eslint/parser": ^8.35.0 cpx: ^1.5.0 eslint: ^9.30.0 @@ -5046,8 +5067,9 @@ __metadata: eslint-import-resolver-typescript: ^4.4.3 eslint-plugin-import: ^2.31.0 eslint-plugin-prettier: ^5.0.0 - fs-extra: ^11.3.0 jest: ^29.7.0 + jpeg-js: ^0.4.4 + pngjs: ^7.0.0 prettier: ^3.2.5 rimraf: ^6.0.1 ts-jest: ^29.1.2 @@ -5057,6 +5079,13 @@ __metadata: languageName: unknown linkType: soft +"jpeg-js@npm:^0.4.4": + version: 0.4.4 + resolution: "jpeg-js@npm:0.4.4" + checksum: bd7cb61aa8df40a9ee2c2106839c3df6054891e56cfc22c0ac581402e06c6295f962a4754b0b2ac50a401789131b1c6dc9df8d24400f1352168be1894833c590 + languageName: node + linkType: hard + "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -5151,19 +5180,6 @@ __metadata: languageName: node linkType: hard -"jsonfile@npm:^6.0.1": - version: 6.1.0 - resolution: "jsonfile@npm:6.1.0" - dependencies: - graceful-fs: ^4.1.6 - universalify: ^2.0.0 - dependenciesMeta: - graceful-fs: - optional: true - checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 - languageName: node - linkType: hard - "keyv@npm:^4.5.4": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -5365,7 +5381,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": +"merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 @@ -5431,15 +5447,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.3": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: ^2.0.1 - checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 - languageName: node - linkType: hard - "minimatch@npm:^10.0.0": version: 10.0.1 resolution: "minimatch@npm:10.0.1" @@ -5449,6 +5456,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.2.2": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: ^5.0.5 + checksum: 000423875fecbc7da1d74bf63c9081363a71291ef2588c376c45647ac004582cb5bc8cc09ef84420b26bfb490f4d0818d328e78569c6228e20d90271283f73ba + languageName: node + linkType: hard + "minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -6014,13 +6030,6 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^4.0.0": - version: 4.0.0 - resolution: "path-type@npm:4.0.0" - checksum: 5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 - languageName: node - linkType: hard - "picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" @@ -6042,6 +6051,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.4": + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 76b387b5157951422fa6049a96bdd1695e39dd126cd99df34d343638dc5cdb8bcdc83fff288c23eddcf7c26657c35e3173d4d5f488c4f28b889b314472e0a662 + languageName: node + linkType: hard + "pirates@npm:^4.0.4": version: 4.0.7 resolution: "pirates@npm:4.0.7" @@ -6058,6 +6074,13 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^7.0.0": + version: 7.0.0 + resolution: "pngjs@npm:7.0.0" + checksum: b19a018930d27de26229c1b3ff250b3a25d09caa22cbb0b0459987d91eb0a560a18ab5d67da45a38ed7514140f26d1db58de83c31159ec101f2bb270a3c707f1 + languageName: node + linkType: hard + "posix-character-classes@npm:^0.1.0": version: 0.1.1 resolution: "posix-character-classes@npm:0.1.1" @@ -6501,6 +6524,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.3": + version: 7.7.4 + resolution: "semver@npm:7.7.4" + bin: + semver: bin/semver.js + checksum: 9b4a6a58e98b9723fafcafa393c9d4e8edefaa60b8dfbe39e30892a3604cf1f45f52df9cfb1ae1a22b44c8b3d57fec8a9bb7b3e1645431587cb272399ede152e + languageName: node + linkType: hard + "set-function-length@npm:^1.2.2": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -7038,6 +7070,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.15": + version: 0.2.16 + resolution: "tinyglobby@npm:0.2.16" + dependencies: + fdir: ^6.5.0 + picomatch: ^4.0.4 + checksum: db9d22ce1deb1095720a683c492cd5e80da0f71fed21ed697e2752f6f298edd8a1249dab197c86a26f001c180594a81bf532400fe519791ed2a2cb57b03bc337 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -7085,15 +7127,6 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.0.1": - version: 1.4.3 - resolution: "ts-api-utils@npm:1.4.3" - peerDependencies: - typescript: ">=4.2.0" - checksum: ea00dee382d19066b2a3d8929f1089888b05fec797e32e7a7004938eda1dccf2e77274ee2afcd4166f53fab9b8d7ee90ebb225a3183f9ba8817d636f688a148d - languageName: node - linkType: hard - "ts-api-utils@npm:^2.1.0": version: 2.1.0 resolution: "ts-api-utils@npm:2.1.0" @@ -7103,6 +7136,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.5.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 5b2a2db7aa041d60b040df691ee5e73d534fb4cb3cf4fd6d2c27c584a32836a7ca8272fb23d865e673559ea639fdba35f8623249bf931df22188f0aaef7f0075 + languageName: node + linkType: hard + "ts-jest@npm:^29.1.2": version: 29.4.0 resolution: "ts-jest@npm:29.4.0" @@ -7370,13 +7412,6 @@ __metadata: languageName: node linkType: hard -"universalify@npm:^2.0.0": - version: 2.0.1 - resolution: "universalify@npm:2.0.1" - checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 - languageName: node - linkType: hard - "unrs-resolver@npm:^1.7.11": version: 1.9.1 resolution: "unrs-resolver@npm:1.9.1" diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +