Compare commits
122 Commits
v0.14.0
...
dc0d37c71e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc0d37c71e | ||
|
|
0287c4d458 | ||
|
|
47be1061b7 | ||
|
|
1c79de2f37 | ||
|
|
9696cc7188 | ||
|
|
082dd8c1f2 | ||
|
|
43524dcdb1 | ||
|
|
45a0540edf | ||
|
|
2b784d1edc | ||
|
|
8650cf9a63 | ||
|
|
76c840dbaa | ||
|
|
e78de6f6de | ||
|
|
554edf5a27 | ||
|
|
6fca398a12 | ||
|
|
5bf3fbc10e | ||
|
|
d994c38219 | ||
|
|
3dccbfc797 | ||
|
|
c3d461f102 | ||
|
|
c0105889ab | ||
|
|
c6006b58d2 | ||
|
|
178f009458 | ||
|
|
9c24cf4aba | ||
|
|
af68498494 | ||
|
|
eac22d53d3 | ||
|
|
e5ac60c187 | ||
|
|
4b42a5fbda | ||
|
|
08a833f1cf | ||
|
|
c0f0dc5192 | ||
|
|
6452d0b357 | ||
|
|
9f6b815197 | ||
|
|
d8cbc0aaee | ||
|
|
b1f70ec36c | ||
|
|
311d2516ce | ||
|
|
5957873534 | ||
|
|
7524f304fe | ||
|
|
f1730ede97 | ||
|
|
7f9c8868fd | ||
|
|
72b484a480 | ||
|
|
c47c1dd4f4 | ||
|
|
3975c70de7 | ||
|
|
fd4e73e76c | ||
|
|
301fab5c17 | ||
|
|
2c90454244 | ||
|
|
2db99edeaf | ||
|
|
4fa471263f | ||
|
|
435f654cbe | ||
|
|
15b03d7561 | ||
|
|
219e6a29e4 | ||
|
|
a85edb6cee | ||
|
|
43081c5179 | ||
|
|
d390dce843 | ||
|
|
e5939aaa5d | ||
|
|
1db0c0f531 | ||
|
|
eefd33ac88 | ||
|
|
de10436c1a | ||
|
|
81f109f830 | ||
|
|
858ee28ef2 | ||
|
|
b89afe1b0c | ||
|
|
b4fedc1c1d | ||
|
|
d24e8b4027 | ||
|
|
93fbdbe0f3 | ||
|
|
068d9b8716 | ||
|
|
2295f23725 | ||
|
|
fed587b4a4 | ||
|
|
8f73f9c365 | ||
|
|
363efc2e7f | ||
|
|
cf93fed64b | ||
|
|
99c689657f | ||
|
|
394c98c65a | ||
|
|
8f93ac29dd | ||
|
|
5ffb7f4a01 | ||
|
|
f5f718a84a | ||
|
|
4c36a950a7 | ||
|
|
a9bc9d7e8d | ||
|
|
18fed70ddf | ||
|
|
dd9d117ab8 | ||
|
|
0e94fe354f | ||
|
|
3b99c79495 | ||
|
|
20e914c85b | ||
|
|
efc4b3f84c | ||
|
|
2bc6b52e99 | ||
|
|
feb59e560b | ||
|
|
17be8f3601 | ||
|
|
3b053e8222 | ||
|
|
9d5050d3ee | ||
|
|
1bd56e1d0e | ||
|
|
3f46abf261 | ||
|
|
6a248e17be | ||
|
|
8273a2a6a0 | ||
|
|
78f52c769d | ||
|
|
fe22b2f8fb | ||
|
|
482421f10e | ||
|
|
dcb15aee0e | ||
|
|
827f22e2fc | ||
|
|
bd36314f00 | ||
|
|
4ad7892eab | ||
|
|
31b7e62983 | ||
|
|
0f5ef2f49c | ||
|
|
2ce3fee70b | ||
|
|
33e5bee9fb | ||
|
|
b32f7dba5d | ||
|
|
2661acbadb | ||
|
|
d28c079a57 | ||
|
|
29a159c094 | ||
|
|
68dad51948 | ||
|
|
3bf82b5b86 | ||
|
|
e52e8c12cf | ||
|
|
8f7a7faa91 | ||
|
|
ff0edec652 | ||
|
|
bbcecf274f | ||
|
|
cc41be6856 | ||
|
|
761f56b869 | ||
|
|
d4e8eaadd7 | ||
|
|
9f2bdadde7 | ||
|
|
f789d9dfe3 | ||
|
|
ce41ee2387 | ||
|
|
01c8fad012 | ||
|
|
908e91cb91 | ||
|
|
f1c5cd9f6b | ||
|
|
6ea3058e66 | ||
|
|
a4e741cc0a | ||
|
|
33388cf209 |
1
.bun-version
Normal file
@@ -0,0 +1 @@
|
||||
1.2.2
|
||||
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,10 +1,9 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
@@ -12,10 +11,12 @@ A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Checklist:**
|
||||
|
||||
- [ ] I am accessing ConvertX over HTTPS or have `HTTP_ALLOWED=true`
|
||||
|
||||
26
.github/ISSUE_TEMPLATE/converter_request.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Converter request
|
||||
about: Suggest a converter for this project
|
||||
title: "[Converter Request]"
|
||||
labels: "converter request"
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**What file formats are missing?**
|
||||
|
||||
<!-- Provide an example of what you would like to convert -->
|
||||
|
||||
**What converter should be added**
|
||||
|
||||
<!-- It has to be free and preferably open source -->
|
||||
|
||||
**Are you willing to add it?**
|
||||
|
||||
<!-- Adding a converter is very easy just copy one of the existing and modify it -->
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
**Additional context**
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -3,8 +3,7 @@ name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
31
.github/workflows/check-lint.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Check Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Run linting checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.2.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Run lint
|
||||
run: bun run lint
|
||||
39
.github/workflows/docker-publish.yml
vendored
@@ -10,7 +10,6 @@ on:
|
||||
branches: ["main"]
|
||||
workflow_dispatch:
|
||||
env:
|
||||
GHCR_IMAGE: ghcr.io/c4illin/convertx
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
DOCKERHUB_USERNAME: c4illin
|
||||
|
||||
@@ -32,8 +31,7 @@ jobs:
|
||||
contents: write
|
||||
packages: write
|
||||
attestations: write
|
||||
checks: write
|
||||
actions: read
|
||||
id-token: write
|
||||
|
||||
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-24.04' || matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' }}
|
||||
|
||||
@@ -51,22 +49,26 @@ jobs:
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: downcase REPO
|
||||
run: |
|
||||
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Docker meta default
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.GHCR_IMAGE }}
|
||||
images: ghcr.io/${{ env.REPO }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
# here we only login to ghcr.io since the this only pushes internal images
|
||||
uses: docker/login-action@v3.4.0
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -74,7 +76,7 @@ jobs:
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
uses: docker/build-push-action@v6
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
with:
|
||||
@@ -82,7 +84,8 @@ jobs:
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
outputs: type=image,name=${{ env.GHCR_IMAGE }},push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=true
|
||||
outputs: type=image,name=ghcr.io/${{ env.REPO }},push-by-digest=true,name-canonical=true,oci-mediatypes=true
|
||||
push: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
cache-from: type=gha,scope=${{ matrix.platform }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
|
||||
|
||||
@@ -101,30 +104,36 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
name: Merge Docker manifests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
attestations: write
|
||||
contents: read
|
||||
contents: write
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: downcase REPO
|
||||
run: |
|
||||
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.GHCR_IMAGE }}
|
||||
ghcr.io/${{ env.REPO }}
|
||||
${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
@@ -157,8 +166,8 @@ jobs:
|
||||
--annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \
|
||||
--annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \
|
||||
--annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \
|
||||
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
|
||||
$(printf 'ghcr.io/${{ env.REPO }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect '${{ env.GHCR_IMAGE }}:${{ steps.meta.outputs.version }}'
|
||||
docker buildx imagetools inspect 'ghcr.io/${{ env.REPO }}:${{ steps.meta.outputs.version }}'
|
||||
|
||||
2
.github/workflows/dockerhub-description.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
dockerHubDescription:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v4
|
||||
|
||||
31
.github/workflows/run-bun-test.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Check Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.2.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Run tests
|
||||
run: bun test
|
||||
1
.gitignore
vendored
@@ -46,6 +46,7 @@ package-lock.json
|
||||
/output
|
||||
/db
|
||||
/data
|
||||
/dist
|
||||
/Bruno
|
||||
/tsconfig.tsbuildinfo
|
||||
/public/generated.css
|
||||
|
||||
35
CHANGELOG.md
@@ -1,24 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## [0.14.0](https://github.com/C4illin/ConvertX/compare/v0.13.0...v0.14.0) (2025-06-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add dvisvgm ([625e1a5](https://github.com/C4illin/ConvertX/commit/625e1a51f620fe9da79d0127eb6c95f468d9ea2b))
|
||||
* add ImageMagick ([b47e575](https://github.com/C4illin/ConvertX/commit/b47e5755f677056e8acecad54c0c2e28a5e137f3))
|
||||
* enhance job details display with file information ([50725ed](https://github.com/C4illin/ConvertX/commit/50725edd021bb9a7f58c85b79c1eab355ad22ced))
|
||||
* improve job details interaction and accessibility ([2a3b084](https://github.com/C4illin/ConvertX/commit/2a3b08487ec4bf215e1e80059dbdc1dcccea68c8))
|
||||
* improve job details interaction and accessibility ([29ba229](https://github.com/C4illin/ConvertX/commit/29ba229bc23d2019d2ee9829da7852f884ffa611))
|
||||
* show version in footer ([9a49ded](https://github.com/C4illin/ConvertX/commit/9a49dedacac7e67a432b6da0daf1967038d97d26))
|
||||
|
||||
## [0.14.1](https://github.com/C4illin/ConvertX/compare/v0.14.0...v0.14.1) (2025-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add av1 and h26X with containers ([af5c768](https://github.com/C4illin/ConvertX/commit/af5c768dc74b3124fd7ef4b29e27c83a5d19ad49))
|
||||
* progress bars on firefox ([ff2c005](https://github.com/C4illin/ConvertX/commit/ff2c0057e890b9ecb552df30914333349ea20eb7))
|
||||
* register button style ([b9bbf77](https://github.com/C4illin/ConvertX/commit/b9bbf7792f01fcaa77e3520925de107e856926f1))
|
||||
* switch from alpine to debian trixie ([4e4c029](https://github.com/C4illin/ConvertX/commit/4e4c029cb800df86affb99c3a82dda9e6708bdde))
|
||||
- change to baseline build ([6ea3058](https://github.com/C4illin/ConvertX/commit/6ea3058e66262f7a14633bddcecd5573948f524a)), closes [#311](https://github.com/C4illin/ConvertX/issues/311)
|
||||
|
||||
## [0.14.0](https://github.com/C4illin/ConvertX/compare/v0.13.0...v0.14.0) (2025-06-03)
|
||||
|
||||
### Features
|
||||
|
||||
- add dvisvgm ([625e1a5](https://github.com/C4illin/ConvertX/commit/625e1a51f620fe9da79d0127eb6c95f468d9ea2b))
|
||||
- add ImageMagick ([b47e575](https://github.com/C4illin/ConvertX/commit/b47e5755f677056e8acecad54c0c2e28a5e137f3)), closes [#295](https://github.com/C4illin/ConvertX/issues/295), closes [#269](https://github.com/C4illin/ConvertX/issues/269)
|
||||
- enhance job details display with file information ([50725ed](https://github.com/C4illin/ConvertX/commit/50725edd021bb9a7f58c85b79c1eab355ad22ced)), closes [#251](https://github.com/C4illin/ConvertX/issues/251)
|
||||
- improve job details interaction and accessibility ([29ba229](https://github.com/C4illin/ConvertX/commit/29ba229bc23d2019d2ee9829da7852f884ffa611))
|
||||
- show version in footer ([9a49ded](https://github.com/C4illin/ConvertX/commit/9a49dedacac7e67a432b6da0daf1967038d97d26))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- add av1 and h26X with containers ([af5c768](https://github.com/C4illin/ConvertX/commit/af5c768dc74b3124fd7ef4b29e27c83a5d19ad49)), closes [#287](https://github.com/C4illin/ConvertX/issues/287), closes [#293](https://github.com/C4illin/ConvertX/issues/293)
|
||||
- progress bars on firefox ([ff2c005](https://github.com/C4illin/ConvertX/commit/ff2c0057e890b9ecb552df30914333349ea20eb7))
|
||||
- register button style ([b9bbf77](https://github.com/C4illin/ConvertX/commit/b9bbf7792f01fcaa77e3520925de107e856926f1))
|
||||
- switch from alpine to debian trixie ([4e4c029](https://github.com/C4illin/ConvertX/commit/4e4c029cb800df86affb99c3a82dda9e6708bdde)), closes [#234](https://github.com/C4illin/ConvertX/issues/234), closes [#199](https://github.com/C4illin/ConvertX/issues/199)
|
||||
|
||||
## [0.13.0](https://github.com/C4illin/ConvertX/compare/v0.12.1...v0.13.0) (2025-05-14)
|
||||
|
||||
|
||||
43
Dockerfile
@@ -3,14 +3,22 @@ LABEL org.opencontainers.image.source="https://github.com/C4illin/ConvertX"
|
||||
WORKDIR /app
|
||||
|
||||
# install bun
|
||||
ENV BUN_INSTALL=/etc/.bun
|
||||
ENV PATH=$BUN_INSTALL/bin:$PATH
|
||||
ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=0
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.2"
|
||||
|
||||
# if architecture is arm64, use the arm64 version of bun
|
||||
RUN ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
curl -fsSL -o bun-linux-aarch64.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-aarch64.zip; \
|
||||
else \
|
||||
curl -fsSL -o bun-linux-x64-baseline.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-x64-baseline.zip; \
|
||||
fi
|
||||
|
||||
RUN unzip -j bun-linux-*.zip -d /usr/local/bin && \
|
||||
rm bun-linux-*.zip && \
|
||||
chmod +x /usr/local/bin/bun
|
||||
|
||||
# install dependencies into temp directory
|
||||
# this will cache them and speed up future builds
|
||||
@@ -48,8 +56,11 @@ RUN apt-get update && apt-get install -y \
|
||||
inkscape \
|
||||
libheif-examples \
|
||||
libjxl-tools \
|
||||
libreoffice \
|
||||
libva2 \
|
||||
libvips-tools \
|
||||
libemail-outlook-message-perl \
|
||||
lmodern \
|
||||
mupdf-tools \
|
||||
pandoc \
|
||||
poppler-utils \
|
||||
@@ -57,15 +68,35 @@ RUN apt-get update && apt-get install -y \
|
||||
python3-numpy \
|
||||
resvg \
|
||||
texlive \
|
||||
texlive-fonts-recommended \
|
||||
texlive-latex-extra \
|
||||
texlive-latex-recommended \
|
||||
texlive-xetex \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install VTracer binary
|
||||
RUN ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "aarch64" ]; then \
|
||||
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"; \
|
||||
else \
|
||||
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"; \
|
||||
fi && \
|
||||
curl -L -o /tmp/vtracer.tar.gz "https://github.com/visioncortex/vtracer/releases/download/0.6.4/${VTRACER_ASSET}" && \
|
||||
tar -xzf /tmp/vtracer.tar.gz -C /tmp/ && \
|
||||
mv /tmp/vtracer /usr/local/bin/vtracer && \
|
||||
chmod +x /usr/local/bin/vtracer && \
|
||||
rm /tmp/vtracer.tar.gz
|
||||
|
||||
COPY --from=install /temp/prod/node_modules node_modules
|
||||
COPY --from=prerelease /app/public/generated.css /app/public/
|
||||
COPY . .
|
||||
COPY --from=prerelease /app/dist /app/dist
|
||||
|
||||
# COPY . .
|
||||
RUN mkdir data
|
||||
|
||||
EXPOSE 3000/tcp
|
||||
# used for calibre
|
||||
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
|
||||
ENV NODE_ENV=production
|
||||
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
||||
ENTRYPOINT [ "bun", "run", "dist/src/index.js" ]
|
||||
67
README.md
@@ -25,22 +25,23 @@ A self-hosted online file converter. Supports over a thousand different formats.
|
||||
|
||||
## Converters supported
|
||||
|
||||
| Converter | Use case | Converts from | Converts to |
|
||||
| ------------------------------------------------ | ---------------- | ------------- | ----------- |
|
||||
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
|
||||
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
|
||||
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
|
||||
| [libheif](https://github.com/strukturag/libheif) | HEIF | 2 | 4 |
|
||||
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
|
||||
| [Calibre](https://calibre-ebook.com/) | E-books | 26 | 19 |
|
||||
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
|
||||
| [dvisvgm](https://dvisvgm.de/) | Vector images | 4 | 2 |
|
||||
| [ImageMagick](https://imagemagick.org/) | Images | 245 | 183 |
|
||||
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 167 | 130 |
|
||||
| [Inkscape](https://inkscape.org/) | Vector images | 7 | 17 |
|
||||
| [Assimp](https://github.com/assimp/assimp) | 3D Assets | 77 | 23 |
|
||||
| [FFmpeg](https://ffmpeg.org/) | Video | ~472 | ~199 |
|
||||
| [Potrace](https://potrace.sourceforge.net/) | Raster to vector | 4 | 11 |
|
||||
| Converter | Use case | Converts from | Converts to |
|
||||
| -------------------------------------------------- | ---------------- | ------------- | ----------- |
|
||||
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
|
||||
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
|
||||
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
|
||||
| [libheif](https://github.com/strukturag/libheif) | HEIF | 2 | 4 |
|
||||
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
|
||||
| [Calibre](https://calibre-ebook.com/) | E-books | 26 | 19 |
|
||||
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
|
||||
| [dvisvgm](https://dvisvgm.de/) | Vector images | 4 | 2 |
|
||||
| [ImageMagick](https://imagemagick.org/) | Images | 245 | 183 |
|
||||
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 167 | 130 |
|
||||
| [Inkscape](https://inkscape.org/) | Vector images | 7 | 17 |
|
||||
| [Assimp](https://github.com/assimp/assimp) | 3D Assets | 77 | 23 |
|
||||
| [FFmpeg](https://ffmpeg.org/) | Video | ~472 | ~199 |
|
||||
| [Potrace](https://potrace.sourceforge.net/) | Raster to vector | 4 | 11 |
|
||||
| [VTracer](https://github.com/visioncortex/vtracer) | Raster to vector | 8 | 1 |
|
||||
|
||||
<!-- many ffmpeg fileformats are duplicates -->
|
||||
|
||||
@@ -62,6 +63,7 @@ services:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() if unset
|
||||
# - HTTP_ALLOWED=true # uncomment this if accessing it over a non-https connection
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
```
|
||||
@@ -80,16 +82,18 @@ If you get unable to open database file run `chown -R $USER:$USER path` on the p
|
||||
|
||||
All are optional, JWT_SECRET is recommended to be set.
|
||||
|
||||
| Name | Default | Description |
|
||||
| ------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| JWT_SECRET | when unset it will use the value from randomUUID() | A long and secret string used to sign the JSON Web Token |
|
||||
| ACCOUNT_REGISTRATION | false | Allow users to register accounts |
|
||||
| HTTP_ALLOWED | false | Allow HTTP connections, only set this to true locally |
|
||||
| ALLOW_UNAUTHENTICATED | false | Allow unauthenticated users to use the service, only set this to true locally |
|
||||
| AUTO_DELETE_EVERY_N_HOURS | 24 | Checks every n hours for files older then n hours and deletes them, set to 0 to disable |
|
||||
| WEBROOT | | The address to the root path setting this to "/convert" will serve the website on "example.com/convert/" |
|
||||
| FFMPEG_ARGS | | Arguments to pass to ffmpeg, e.g. `-preset veryfast` |
|
||||
| HIDE_HISTORY | false | Hide the history page |
|
||||
| Name | Default | Description |
|
||||
| ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| JWT_SECRET | when unset it will use the value from randomUUID() | A long and secret string used to sign the JSON Web Token |
|
||||
| ACCOUNT_REGISTRATION | false | Allow users to register accounts |
|
||||
| HTTP_ALLOWED | false | Allow HTTP connections, only set this to true locally |
|
||||
| ALLOW_UNAUTHENTICATED | false | Allow unauthenticated users to use the service, only set this to true locally |
|
||||
| AUTO_DELETE_EVERY_N_HOURS | 24 | Checks every n hours for files older then n hours and deletes them, set to 0 to disable |
|
||||
| WEBROOT | | The address to the root path setting this to "/convert" will serve the website on "example.com/convert/" |
|
||||
| FFMPEG_ARGS | | Arguments to pass to ffmpeg, e.g. `-preset veryfast` |
|
||||
| HIDE_HISTORY | false | Hide the history page |
|
||||
| LANGUAGE | en | Language to format date strings in, specified as a [BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) |
|
||||
| UNAUTHENTICATED_USER_SHARING | false | Shares conversion history between all unauthenticated users |
|
||||
|
||||
### Docker images
|
||||
|
||||
@@ -129,19 +133,10 @@ Tutorial in chinese: <https://xzllll.com/24092901/>
|
||||
2. `bun install`
|
||||
3. `bun run dev`
|
||||
|
||||
Pull requests are welcome! See below and open issues for the list of todos.
|
||||
Pull requests are welcome! See open issues for the list of todos. The ones tagged with "converter request" are quite easy. Help with docs and cleaning up in issues are also very welcome!
|
||||
|
||||
Use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) for commit messages.
|
||||
|
||||
## Todo
|
||||
|
||||
- [ ] Add options for converters
|
||||
- [ ] Add tests
|
||||
- [ ] Make errors logs visible from the web ui
|
||||
- [ ] Add more converters:
|
||||
- [ ] [deark](https://github.com/jsummers/deark)
|
||||
- [ ] LibreOffice
|
||||
|
||||
## Contributors
|
||||
|
||||
<a href="https://github.com/C4illin/ConvertX/graphs/contributors">
|
||||
|
||||
277
bun.lock
@@ -4,35 +4,36 @@
|
||||
"": {
|
||||
"name": "convertx-frontend",
|
||||
"dependencies": {
|
||||
"@elysiajs/html": "^1.3.0",
|
||||
"@elysiajs/jwt": "^1.3.0",
|
||||
"@elysiajs/html": "^1.3.1",
|
||||
"@elysiajs/jwt": "^1.3.2",
|
||||
"@elysiajs/static": "^1.3.0",
|
||||
"@kitajs/html": "^4.2.9",
|
||||
"elysia": "^1.3.1",
|
||||
"elysia": "^1.3.8",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"tar": "^7.4.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
|
||||
"@kitajs/ts-html-plugin": "^4.1.1",
|
||||
"@tailwindcss/cli": "^4.1.7",
|
||||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.6.2",
|
||||
"@kitajs/ts-html-plugin": "^4.1.2",
|
||||
"@tailwindcss/cli": "^4.1.11",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@total-typescript/ts-reset": "^0.6.1",
|
||||
"@types/bun": "^1.2.14",
|
||||
"@types/node": "^22.15.21",
|
||||
"@typescript-eslint/parser": "^8.33.1",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-better-tailwindcss": "^3.0.0",
|
||||
"@types/bun": "latest",
|
||||
"@types/node": "^24.2.1",
|
||||
"@typescript-eslint/parser": "^8.39.1",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.4",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^16.1.0",
|
||||
"knip": "^5.57.2",
|
||||
"npm-run-all2": "^8.0.3",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"globals": "^16.3.0",
|
||||
"knip": "^5.62.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.2",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.39.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -61,35 +62,31 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.26.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg=="],
|
||||
|
||||
"@elysiajs/html": ["@elysiajs/html@1.3.0", "", { "dependencies": { "@kitajs/html": "^4.1.0", "@kitajs/ts-html-plugin": "^4.0.1" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-NpujllWwiEXdsX8GJhbBppOv7+aJr+OU7Gn3K8fVXpwieutwau0/B/M6vzjYXsh9OaoGByUTpL8U9rA/tVSn7w=="],
|
||||
"@elysiajs/html": ["@elysiajs/html@1.3.1", "", { "dependencies": { "@kitajs/html": "^4.1.0", "@kitajs/ts-html-plugin": "^4.0.1" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-jOWUfvL9vZ2Gs3uCx2w4Po+jxOwRD/sXW3JgvOAD3rEjX0NuygwcvixtbONSzAH8lFhaDBbHAtmCfpue46X9IQ=="],
|
||||
|
||||
"@elysiajs/jwt": ["@elysiajs/jwt@1.3.1", "", { "dependencies": { "jose": "^6.0.11" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-BVLAp0ER4839bR82ElgTsI7OoPxvFWP5u02KMgqpNoAM6xirJBYTKqANzY9ghuMfQoVEuD4B1/8lZwdPKAVg9Q=="],
|
||||
"@elysiajs/jwt": ["@elysiajs/jwt@1.3.2", "", { "dependencies": { "jose": "^6.0.11" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-1Ysb+THWmwy/AKqn9Q1SaBeYK6f499VEVV0E+YifKQjadJT5W+0qKhncOdfqrb4NufUtd65BxULdPQGKJwYo1Q=="],
|
||||
|
||||
"@elysiajs/static": ["@elysiajs/static@1.3.0", "", { "dependencies": { "node-cache": "^5.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-7mWlj2U/AZvH27IfRKqpUjDP1W9ZRldF9NmdnatFEtx0AOy7YYgyk0rt5hXrH6wPcR//2gO2Qy+k5rwswpEhJA=="],
|
||||
|
||||
"@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" } }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
||||
|
||||
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
|
||||
|
||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
||||
|
||||
"@eslint/config-array": ["@eslint/config-array@0.20.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ=="],
|
||||
"@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
|
||||
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.2.2", "", {}, "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg=="],
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
|
||||
|
||||
"@eslint/core": ["@eslint/core@0.14.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="],
|
||||
"@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
|
||||
|
||||
"@eslint/css-tree": ["@eslint/css-tree@3.6.3", "", { "dependencies": { "mdn-data": "2.21.0", "source-map-js": "^1.0.1" } }, "sha512-M9iq4Brt/MG+5/B4Jrla5XZqaCgaHjfZyMSUJM3KNpBU61u8gMYg4TTaNTP/mUGR/rnRrVV7RXmh5qI4pIk0Yw=="],
|
||||
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
||||
|
||||
"@eslint/js": ["@eslint/js@9.28.0", "", {}, "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg=="],
|
||||
"@eslint/js": ["@eslint/js@9.33.0", "", {}, "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A=="],
|
||||
|
||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
|
||||
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.1", "", { "dependencies": { "@eslint/core": "^0.14.0", "levn": "^0.4.1" } }, "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w=="],
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
|
||||
|
||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||
|
||||
@@ -99,7 +96,7 @@
|
||||
|
||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="],
|
||||
|
||||
"@ianvs/prettier-plugin-sort-imports": ["@ianvs/prettier-plugin-sort-imports@4.4.2", "", { "dependencies": { "@babel/generator": "^7.26.2", "@babel/parser": "^7.26.2", "@babel/traverse": "^7.25.9", "@babel/types": "^7.26.0", "semver": "^7.5.2" }, "peerDependencies": { "@vue/compiler-sfc": "2.7.x || 3.x", "prettier": "2 || 3 || ^4.0.0-0" }, "optionalPeers": ["@vue/compiler-sfc"] }, "sha512-KkVFy3TLh0OFzimbZglMmORi+vL/i2OFhEs5M07R9w0IwWAGpsNNyE4CY/2u0YoMF5bawKC2+8/fUH60nnNtjw=="],
|
||||
"@ianvs/prettier-plugin-sort-imports": ["@ianvs/prettier-plugin-sort-imports@4.6.2", "", { "dependencies": { "@babel/generator": "^7.26.2", "@babel/parser": "^7.26.2", "@babel/traverse": "^7.25.9", "@babel/types": "^7.26.0", "semver": "^7.5.2" }, "peerDependencies": { "@prettier/plugin-oxc": "^0.0.4", "@vue/compiler-sfc": "2.7.x || 3.x", "content-tag": "^4.0.0", "prettier": "2 || 3 || ^4.0.0-0", "prettier-plugin-ember-template-tag": "^2.1.0" }, "optionalPeers": ["@prettier/plugin-oxc", "@vue/compiler-sfc", "content-tag", "prettier-plugin-ember-template-tag"] }, "sha512-kHiL1IghIodo43clNQaJJU2rPqXEioPG+Ink4/T5za46A0ggSNvIx4NM3hGgciQ2VpDaR/X8cTJIZDKRurWjPw=="],
|
||||
|
||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
@@ -115,9 +112,9 @@
|
||||
|
||||
"@kitajs/html": ["@kitajs/html@4.2.9", "", { "dependencies": { "csstype": "^3.1.3" } }, "sha512-FDHHf5Mi5nR0D+Btq86IV1O9XfsePVCiC5rwU4PXjw2aHja16FmIiwLZBO0CS16rJxKkibjMldyRLAW2ni2mzA=="],
|
||||
|
||||
"@kitajs/ts-html-plugin": ["@kitajs/ts-html-plugin@4.1.1", "", { "dependencies": { "chalk": "^4.1.2", "tslib": "^2.8.1", "yargs": "^17.7.2" }, "peerDependencies": { "@kitajs/html": "^4.2.5", "typescript": "^5.6.2" }, "bin": { "ts-html-plugin": "dist/cli.js", "xss-scan": "dist/cli.js" } }, "sha512-wmjyV8hmJmDOnUM/ZyPkc0UBYgUYmf32/93rkW8wr8h+HiHVMU0tEKFnmRdBjTcy9jwoC9Bnt2NuzS9l67lq5g=="],
|
||||
"@kitajs/ts-html-plugin": ["@kitajs/ts-html-plugin@4.1.2", "", { "dependencies": { "chalk": "^4.1.2", "tslib": "^2.8.1", "yargs": "^17.7.2" }, "peerDependencies": { "@kitajs/html": "^4.2.5", "typescript": "^5.6.2" }, "bin": { "ts-html-plugin": "dist/cli.js", "xss-scan": "dist/cli.js" } }, "sha512-XE9iIe93TELBdQSvNC3xxXOPDhkcK7on4Oi2HUKhln3jAc5hzn1o33uzjHCYhLeW36r/LXCT70beoXRCFcuTxQ=="],
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="],
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" } }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
@@ -125,31 +122,31 @@
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@9.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MVyRgP2gzJJtAowjG/cHN3VQXwNLWnY+FpOEsyvDepJki1SdAX/8XDijM1yN6ESD1kr9uhBKjGelC6h3qtT+rA=="],
|
||||
"@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ruKLkS+Dm/YIJaUhzEB7zPI+jh3EXxu0QnNV8I7t9jf0lpD2VnltuyRbhrbJEkksklZj//xCMyFFsILGjiU2Mg=="],
|
||||
|
||||
"@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@9.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-7kV0EOFEZ3sk5Hjy4+bfA6XOQpCwbDiDkkHN4BHHyrBHsXxUR05EcEJPPL1WjItefg+9+8hrBmoK0xRoDs41+A=="],
|
||||
"@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0zhgNUm5bYezdSFOg3FYhtVP83bAq7FTV/3suGQDl/43MixfQG7+bl+hlrP4mz6WlD2SUb2u9BomnJWl1uey9w=="],
|
||||
|
||||
"@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@9.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6OvkEtRXrt8sJ4aVfxHRikjain9nV1clIsWtJ1J3J8NG1ZhjyJFgT00SCvqxbK+pzeWJq6XzHyTCN78ML+lY2w=="],
|
||||
"@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.2.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SHOxfCcZV1axeIGfyeD1BkdLvfQgjmPy18tO0OUXGElcdScxD6MqU5rj/AVtiuBT+51GtFfOKlwl1+BdVwhD1A=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@9.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-aYpNL6o5IRAUIdoweW21TyLt54Hy/ZS9tvzNzF6ya1ckOQ8DLaGVPjGpmzxdNja9j/bbV6aIzBH7lNcBtiOTkQ=="],
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mgEkYrJ+N90sgEDqEZ07zH+4I1D28WjqAhdzfW3aS2x2vynVpoY9jWfHuH8S62vZt3uATJrTKTRa8CjPWEsrdw=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@9.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGFW4vCfKMFEIzb9VCY0oWyyY9tR1/o+wDdNePhiUXZU4SVniRPQaZ1SJ0sUFI1k25pXZmzQmIP6cBmazi/Dew=="],
|
||||
"@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BhEzNLjn4HjP8+Q18D3/jeIDBxW7OgoJYIjw2CaaysnYneoTlij8hPTKxHfyqq4IGM3fFs9TLR/k338M3zkQ7g=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@9.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-lxx/PibBfzqYvut2Y8N2D0Ritg9H8pKO+7NUSJb9YjR/bfk2KRmP8iaUz3zB0JhPtf/W3REs65oKpWxgflGToA=="],
|
||||
"@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yxbMYUgRmN2V8x8XoxmD/Qq6aG7YIW3ToMDILfmcfeeRRVieEJ3DOWBT0JSE+YgrOy79OyFDH/1lO8VnqLmDQQ=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@9.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-yD28ptS/OuNhwkpXRPNf+/FvrO7lwURLsEbRVcL1kIE0GxNJNMtKgIE4xQvtKDzkhk6ZRpLho5VSrkkF+3ARTQ=="],
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.2.0", "", { "os": "linux", "cpu": "none" }, "sha512-QG1UfgC2N2qhW1tOnDCgB/26vn1RCshR5sYPhMeaxO1gMQ3kEKbZ3QyBXxrG1IX5qsXYj5hPDJLDYNYUjRcOpg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@9.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-WBwEJdspoga2w+aly6JVZeHnxuPVuztw3fPfWrei2P6rNM5hcKxBGWKKT6zO1fPMCB4sdDkFohGKkMHVV1eryQ=="],
|
||||
"@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-uqTDsQdi6mrkSV1gvwbuT8jf/WFl6qVDVjNlx7IPSaAByrNiJfPrhTmH8b+Do58Dylz7QIRZgxQ8CHIZSyBUdg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@9.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-a2z3/cbOOTUq0UTBG8f3EO/usFcdwwXnCejfXv42HmV/G8GjrT4fp5+5mVDoMByH3Ce3iVPxj1LmS6OvItKMYQ=="],
|
||||
"@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-GZdHXhJ7p6GaQg9MjRqLebwBf8BLvGIagccI6z5yMj4fV3LU4QuDfwSEERG+R6oQ/Su9672MBqWwncvKcKT68w=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@9.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-bHZF+WShYQWpuswB9fyxcgMIWVk4sZQT0wnwpnZgQuvGTZLkYJ1JTCXJMtaX5mIFHf69ngvawnwPIUA4Feil0g=="],
|
||||
"@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-YBAC3GOicYznReG2twE7oFPSeK9Z1f507z1EYWKg6HpGYRYRlJyszViu7PrhMT85r/MumDTs429zm+CNqpFWOA=="],
|
||||
|
||||
"@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@9.0.2", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.9" }, "cpu": "none" }, "sha512-I5cSgCCh5nFozGSHz+PjIOfrqW99eUszlxKLgoNNzQ1xQ2ou9ZJGzcZ94BHsM9SpyYHLtgHljmOZxCT9bgxYNA=="],
|
||||
"@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.2.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-+qlIg45CPVPy+Jn3vqU1zkxA/AAv6e/2Ax/ImX8usZa8Tr2JmQn/93bmSOOOnr9fXRV9d0n4JyqYzSWxWPYDEw=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@9.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-5IhoOpPr38YWDWRCA5kP30xlUxbIJyLAEsAK7EMyUgqygBHEYLkElaKGgS0X5jRXUQ6l5yNxuW73caogb2FYaw=="],
|
||||
"@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-AI4KIpS8Zf6vwfOPk0uQPSC0pQ1m5HU4hCbtrgL21JgJSlnJaeEu3/aoOBB45AXKiExBU9R+CDR7aSnW7uhc5A=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@9.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-Qc40GDkaad9rZksSQr2l/V9UubigIHsW69g94Gswc2sKYB3XfJXfIfyV8WTJ67u6ZMXsZ7BH1msSC6Aen75mCg=="],
|
||||
"@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-r19cQc7HaEJ76HFsMsbiKMTIV2YqFGSof8H5hB7e5Jkb/23Y8Isv1YrSzkDaGhcw02I/COsrPo+eEmjy35eFuA=="],
|
||||
|
||||
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
|
||||
|
||||
@@ -179,41 +176,41 @@
|
||||
|
||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
|
||||
|
||||
"@pkgr/core": ["@pkgr/core@0.1.1", "", {}, "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA=="],
|
||||
"@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
|
||||
|
||||
"@sinclair/typebox": ["@sinclair/typebox@0.34.33", "", {}, "sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g=="],
|
||||
|
||||
"@tailwindcss/cli": ["@tailwindcss/cli@4.1.8", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.8", "@tailwindcss/oxide": "4.1.8", "enhanced-resolve": "^5.18.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.8" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-+6lkjXSr/68zWiabK3mVYVHmOq/SAHjJ13mR8spyB4LgUWZbWzU9kCSErlAUo+gK5aVfgqe8kY6Ltz9+nz5XYA=="],
|
||||
"@tailwindcss/cli": ["@tailwindcss/cli@4.1.11", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "enhanced-resolve": "^5.18.1", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.11" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-7RAFOrVaXCFz5ooEG36Kbh+sMJiI2j4+Ozp71smgjnLfBRu7DTfoq8DsTvzse2/6nDeo2M3vS/FGaxfDgr3rtQ=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.8", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.8" } }, "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q=="],
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.8", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.8", "@tailwindcss/oxide-darwin-arm64": "4.1.8", "@tailwindcss/oxide-darwin-x64": "4.1.8", "@tailwindcss/oxide-freebsd-x64": "4.1.8", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", "@tailwindcss/oxide-linux-x64-musl": "4.1.8", "@tailwindcss/oxide-wasm32-wasi": "4.1.8", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A=="],
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
|
||||
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg=="],
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A=="],
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw=="],
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg=="],
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8", "", { "os": "linux", "cpu": "arm" }, "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ=="],
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q=="],
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ=="],
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g=="],
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg=="],
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.8", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA=="],
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.8", "", { "os": "win32", "cpu": "x64" }, "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ=="],
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
|
||||
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.8", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.8", "@tailwindcss/oxide": "4.1.8", "postcss": "^8.4.41", "tailwindcss": "4.1.8" } }, "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw=="],
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.11", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", "tailwindcss": "4.1.11" } }, "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA=="],
|
||||
|
||||
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
|
||||
|
||||
@@ -221,39 +218,39 @@
|
||||
|
||||
"@total-typescript/ts-reset": ["@total-typescript/ts-reset@0.6.1", "", {}, "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg=="],
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],
|
||||
"@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.15.29", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ=="],
|
||||
"@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
|
||||
|
||||
"@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.33.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.33.1", "@typescript-eslint/type-utils": "8.33.1", "@typescript-eslint/utils": "8.33.1", "@typescript-eslint/visitor-keys": "8.33.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.33.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-TDCXj+YxLgtvxvFlAvpoRv9MAncDLBV2oT9Bd7YBGC/b/sEURoOYuIwLI99rjWOfY3QtDzO+mk0n4AmdFExW8A=="],
|
||||
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.33.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.33.1", "@typescript-eslint/types": "8.33.1", "@typescript-eslint/typescript-estree": "8.33.1", "@typescript-eslint/visitor-keys": "8.33.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA=="],
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.39.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/type-utils": "8.39.1", "@typescript-eslint/utils": "8.39.1", "@typescript-eslint/visitor-keys": "8.39.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.39.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.33.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.33.1", "@typescript-eslint/types": "^8.33.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw=="],
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.39.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", "@typescript-eslint/typescript-estree": "8.39.1", "@typescript-eslint/visitor-keys": "8.39.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.33.1", "", { "dependencies": { "@typescript-eslint/types": "8.33.1", "@typescript-eslint/visitor-keys": "8.33.1" } }, "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA=="],
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.39.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.39.1", "@typescript-eslint/types": "^8.39.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw=="],
|
||||
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.33.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g=="],
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.39.1", "", { "dependencies": { "@typescript-eslint/types": "8.39.1", "@typescript-eslint/visitor-keys": "8.39.1" } }, "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.33.1", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.33.1", "@typescript-eslint/utils": "8.33.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-1cG37d9xOkhlykom55WVwG2QRNC7YXlxMaMzqw2uPeJixBFfKWZgaP/hjAObqMN/u3fr5BrTwTnc31/L9jQ2ww=="],
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.39.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.33.1", "", {}, "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg=="],
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.39.1", "", { "dependencies": { "@typescript-eslint/types": "8.39.1", "@typescript-eslint/typescript-estree": "8.39.1", "@typescript-eslint/utils": "8.39.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.33.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.33.1", "@typescript-eslint/tsconfig-utils": "8.33.1", "@typescript-eslint/types": "8.33.1", "@typescript-eslint/visitor-keys": "8.33.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.39.1", "", {}, "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.33.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.33.1", "@typescript-eslint/types": "8.33.1", "@typescript-eslint/typescript-estree": "8.33.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-52HaBiEQUaRYqAXpfzWSR2U3gxk92Kw006+xZpElaPMg3C4PgM+A5LqwoQI1f9E5aZ/qlxAZxzm42WX+vn92SQ=="],
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.39.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.39.1", "@typescript-eslint/tsconfig-utils": "8.39.1", "@typescript-eslint/types": "8.39.1", "@typescript-eslint/visitor-keys": "8.39.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.33.1", "", { "dependencies": { "@typescript-eslint/types": "8.33.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ=="],
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", "@typescript-eslint/typescript-estree": "8.39.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg=="],
|
||||
|
||||
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.39.1", "", { "dependencies": { "@typescript-eslint/types": "8.39.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
@@ -271,7 +268,7 @@
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
|
||||
"bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
@@ -303,27 +300,27 @@
|
||||
|
||||
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
||||
|
||||
"elysia": ["elysia@1.3.4", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.1.2", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": "^0.34.33", "openapi-types": "^12.1.3" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-kAfM3Zwovy3z255IZgTKVxBw91HbgKhYl3TqrGRdZqqr+Fd+4eKOfvxgaKij22+MZLczPzIHtscAmvfpI3+q/A=="],
|
||||
"elysia": ["elysia@1.3.8", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.1.3", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": "^0.34.33", "openapi-types": "^12.1.3" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-kxYFhegJbUEf5otzmisEvGt3R7d/dPBNVERO2nHo0kFqKBHyj5slArc90mSRKLfi1vamMtPcz67rL6Zeg5F2yg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"eslint": ["eslint@9.28.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.28.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ=="],
|
||||
"eslint": ["eslint@9.33.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.33.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA=="],
|
||||
|
||||
"eslint-plugin-better-tailwindcss": ["eslint-plugin-better-tailwindcss@3.1.0", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "postcss": "^8.5.4", "postcss-import": "^16.1.0", "synckit": "0.9.2" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "tailwindcss": "^3.3.0 || ^4.0.0" } }, "sha512-qaOnCBBvkxq5O1CPwzD8NWTNbBLY5RtjfpbOXiv0MtjX5GHnraj/cKBvjrfAPGH7FWrDeycR+kQ52aYqNWpujw=="],
|
||||
"eslint-plugin-better-tailwindcss": ["eslint-plugin-better-tailwindcss@3.7.4", "", { "dependencies": { "@eslint/css-tree": "^3.6.3", "enhanced-resolve": "^5.18.2", "jiti": "^2.5.1", "postcss": "^8.5.6", "postcss-import": "^16.1.1", "synckit": "^0.11.11", "tailwind-csstree": "^0.1.2", "tsconfig-paths-webpack-plugin": "^4.2.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "tailwindcss": "^3.3.0 || ^4.1.6" } }, "sha512-jlLHLoqrNbcqqROVjFojGDv1m2LGiJwFqynbARbyeRj9rc1Hmh46EeQhmYVQihhD4j+DSxG/bcwoA9PABIpLmw=="],
|
||||
|
||||
"eslint-plugin-simple-import-sort": ["eslint-plugin-simple-import-sort@12.1.1", "", { "peerDependencies": { "eslint": ">=5.0.0" } }, "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA=="],
|
||||
|
||||
"eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="],
|
||||
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
|
||||
|
||||
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
|
||||
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
|
||||
|
||||
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
|
||||
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
|
||||
|
||||
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
||||
|
||||
@@ -333,7 +330,7 @@
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"exact-mirror": ["exact-mirror@0.1.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-wFCPCDLmHbKGUb8TOi/IS7jLsgR8WVDGtDK3CzcB4Guf/weq7G+I+DkXiRSZfbemBFOxOINKpraM6ml78vo8Zw=="],
|
||||
"exact-mirror": ["exact-mirror@0.1.3", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-yI62LpSby0ItzPJF05C4DRycVAoknRiCIDOLOCCs9zaEKylOXQtOFM3flX54S44swpRz584vk3P70yWQodsLlg=="],
|
||||
|
||||
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
|
||||
|
||||
@@ -347,7 +344,7 @@
|
||||
|
||||
"fastq": ["fastq@1.19.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="],
|
||||
|
||||
"fd-package-json": ["fd-package-json@1.2.0", "", { "dependencies": { "walk-up-path": "^3.0.1" } }, "sha512-45LSPmWf+gC5tdCQMNH4s9Sr00bIkiD9aN7dc5hqkrEw1geRYyDQS1v1oMHAW3ysfxfndqGsrDREHHjNNbKUfA=="],
|
||||
"fd-package-json": ["fd-package-json@2.0.0", "", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="],
|
||||
|
||||
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||
|
||||
@@ -363,7 +360,7 @@
|
||||
|
||||
"flatted": ["flatted@3.3.2", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="],
|
||||
|
||||
"formatly": ["formatly@0.2.3", "", { "dependencies": { "fd-package-json": "^1.2.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-WH01vbXEjh9L3bqn5V620xUAWs32CmK4IzWRRY6ep5zpa/mrisL4d9+pRVuETORVDTQw8OycSO1WC68PL51RaA=="],
|
||||
"formatly": ["formatly@0.2.4", "", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-lIN7GpcvX/l/i24r/L9bnJ0I8Qn01qijWpQpDDvTLL29nKqSaJJu4h20+7VJ6m2CAhQ2/En/GbxDiHCzq/0MyA=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
@@ -371,7 +368,7 @@
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"globals": ["globals@16.2.0", "", {}, "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg=="],
|
||||
"globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
@@ -401,7 +398,7 @@
|
||||
|
||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
||||
"jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
|
||||
|
||||
"jose": ["jose@6.0.11", "", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
|
||||
|
||||
@@ -419,9 +416,11 @@
|
||||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"knip": ["knip@5.59.1", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.2.3", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "oxc-resolver": "^9.0.2", "picocolors": "^1.1.0", "picomatch": "^4.0.1", "smol-toml": "^1.3.1", "strip-json-comments": "5.0.1", "zod": "^3.22.4", "zod-validation-error": "^3.0.3" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-pOMBw6sLQhi/RfnpI6TwBY6NrAtKXDO5wkmMm+pCsSK5eWbVfDnDtPXbLDGNCoZPXiuAojb27y4XOpp4JPNxlA=="],
|
||||
"knip": ["knip@5.62.0", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.2.4", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "oxc-resolver": "^11.1.0", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.3.4", "strip-json-comments": "5.0.2", "zod": "^3.22.4", "zod-validation-error": "^3.0.3" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-hfTUVzmrMNMT1khlZfAYmBABeehwWUUrizLQoLamoRhSFkygsGIXWx31kaWKBgEaIVL77T3Uz7IxGvSw+CvQ6A=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
@@ -453,6 +452,8 @@
|
||||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
"mdn-data": ["mdn-data@2.21.0", "", {}, "sha512-+ZKPQezM5vYJIkCxaC+4DTnRrVZR1CgsKLu5zsQERQx6Tea8Y+wMx5A24rq8A8NepCeatIQufVAekKNgiBMsGQ=="],
|
||||
|
||||
"memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
@@ -489,7 +490,7 @@
|
||||
|
||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||
|
||||
"oxc-resolver": ["oxc-resolver@9.0.2", "", { "optionalDependencies": { "@oxc-resolver/binding-darwin-arm64": "9.0.2", "@oxc-resolver/binding-darwin-x64": "9.0.2", "@oxc-resolver/binding-freebsd-x64": "9.0.2", "@oxc-resolver/binding-linux-arm-gnueabihf": "9.0.2", "@oxc-resolver/binding-linux-arm64-gnu": "9.0.2", "@oxc-resolver/binding-linux-arm64-musl": "9.0.2", "@oxc-resolver/binding-linux-riscv64-gnu": "9.0.2", "@oxc-resolver/binding-linux-s390x-gnu": "9.0.2", "@oxc-resolver/binding-linux-x64-gnu": "9.0.2", "@oxc-resolver/binding-linux-x64-musl": "9.0.2", "@oxc-resolver/binding-wasm32-wasi": "9.0.2", "@oxc-resolver/binding-win32-arm64-msvc": "9.0.2", "@oxc-resolver/binding-win32-x64-msvc": "9.0.2" } }, "sha512-w838ygc1p7rF+7+h5vR9A+Y9Fc4imy6C3xPthCMkdFUgFvUWkmABeNB8RBDQ6+afk44Q60/UMMQ+gfDUW99fBA=="],
|
||||
"oxc-resolver": ["oxc-resolver@11.2.0", "", { "optionalDependencies": { "@oxc-resolver/binding-darwin-arm64": "11.2.0", "@oxc-resolver/binding-darwin-x64": "11.2.0", "@oxc-resolver/binding-freebsd-x64": "11.2.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.2.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.2.0", "@oxc-resolver/binding-linux-arm64-musl": "11.2.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.2.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.2.0", "@oxc-resolver/binding-linux-x64-gnu": "11.2.0", "@oxc-resolver/binding-linux-x64-musl": "11.2.0", "@oxc-resolver/binding-wasm32-wasi": "11.2.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.2.0", "@oxc-resolver/binding-win32-x64-msvc": "11.2.0" } }, "sha512-3iJYyIdDZMDoj0ZSVBrI1gUvPBMkDC4gxonBG+7uqUyK5EslG0mCwnf6qhxK8oEU7jLHjbRBNyzflPSd3uvH7Q=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
|
||||
@@ -513,15 +514,15 @@
|
||||
|
||||
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||
|
||||
"postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="],
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"postcss-import": ["postcss-import@16.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg=="],
|
||||
"postcss-import": ["postcss-import@16.1.1", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ=="],
|
||||
|
||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="],
|
||||
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||
|
||||
"prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="],
|
||||
|
||||
@@ -555,7 +556,7 @@
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.2", "", {}, "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA=="],
|
||||
|
||||
"smol-toml": ["smol-toml@1.3.1", "", {}, "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ=="],
|
||||
"smol-toml": ["smol-toml@1.3.4", "", {}, "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
@@ -563,7 +564,9 @@
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@5.0.1", "", {}, "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw=="],
|
||||
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@5.0.2", "", {}, "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g=="],
|
||||
|
||||
"strtok3": ["strtok3@10.2.2", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^7.0.0" } }, "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg=="],
|
||||
|
||||
@@ -571,11 +574,13 @@
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"synckit": ["synckit@0.9.2", "", { "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" } }, "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw=="],
|
||||
"synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="],
|
||||
|
||||
"tailwind-csstree": ["tailwind-csstree@0.1.3", "", {}, "sha512-LfOT807005OVfyxAjHpOajlIgoEaE894jqjkrhONC/HqBLS8OAhhNifnNs3Y5wD26eIdf0vk1zu9gja2oI3/1Q=="],
|
||||
|
||||
"tailwind-scrollbar": ["tailwind-scrollbar@4.0.2", "", { "dependencies": { "prism-react-renderer": "^2.4.1" }, "peerDependencies": { "tailwindcss": "4.x" } }, "sha512-wAQiIxAPqk0MNTPptVe/xoyWi27y+NRGnTwvn4PQnbvB9kp8QUBiGl/wsfoVBHnQxTmhXJSNt9NHTmcz9EivFA=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="],
|
||||
"tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
|
||||
|
||||
"tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
|
||||
|
||||
@@ -589,23 +594,27 @@
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="],
|
||||
|
||||
"tsconfig-paths-webpack-plugin": ["tsconfig-paths-webpack-plugin@4.2.0", "", { "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", "tapable": "^2.2.1", "tsconfig-paths": "^4.1.2" } }, "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.33.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.33.1", "@typescript-eslint/parser": "8.33.1", "@typescript-eslint/utils": "8.33.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-AgRnV4sKkWOiZ0Kjbnf5ytTJXMUZQ0qhSVdQtDNYLPLnjsATEYhaO94GlRQwi4t4gO8FfjM6NnikHeKjUm8D7A=="],
|
||||
"typescript-eslint": ["typescript-eslint@8.39.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.39.1", "@typescript-eslint/parser": "8.39.1", "@typescript-eslint/typescript-estree": "8.39.1", "@typescript-eslint/utils": "8.39.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg=="],
|
||||
|
||||
"uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
|
||||
|
||||
"walk-up-path": ["walk-up-path@3.0.1", "", {}, "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA=="],
|
||||
"walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
|
||||
|
||||
"which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
|
||||
|
||||
@@ -631,12 +640,20 @@
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@eslint/eslintrc/espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
|
||||
|
||||
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||
|
||||
"@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@tailwindcss/oxide/detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
@@ -645,7 +662,7 @@
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.10", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
@@ -667,8 +684,50 @@
|
||||
|
||||
"wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"@eslint/eslintrc/espree/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"@eslint/eslintrc/espree/eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@emnapi/core/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@emnapi/runtime/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@ services:
|
||||
# - WEBROOT=/convertx # the root path of the web interface, leave empty to disable
|
||||
# - HIDE_HISTORY=true # hides the history tab in the web interface, defaults to false
|
||||
- TZ=Europe/Stockholm # set your timezone, defaults to UTC
|
||||
# - UNAUTHENTICATED_USER_SHARING=true # for use with ALLOW_UNAUTHENTICATED=true to share history with all unauthenticated users / devices
|
||||
ports:
|
||||
- 3000:3000
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import js from "@eslint/js";
|
||||
import eslintParserTypeScript from "@typescript-eslint/parser";
|
||||
import type { Linter } from "eslint";
|
||||
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
|
||||
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
export default tseslint.config(
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
// ...tailwind.configs["flat/recommended"],
|
||||
tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
"simple-import-sort": simpleImportSortPlugin,
|
||||
"better-tailwindcss": eslintPluginBetterTailwindcss,
|
||||
},
|
||||
ignores: ["**/node_modules/**"],
|
||||
ignores: ["**/node_modules/**", "eslint.config.ts"],
|
||||
languageOptions: {
|
||||
parser: eslintParserTypeScript,
|
||||
parserOptions: {
|
||||
@@ -26,10 +24,9 @@ export default [
|
||||
},
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
files: ["**/*.{js,mjs,cjs,jsx,tsx,ts}"],
|
||||
files: ["**/*.{tsx,ts}"],
|
||||
settings: {
|
||||
"better-tailwindcss": {
|
||||
entryPoint: "src/main.css",
|
||||
@@ -39,7 +36,7 @@ export default [
|
||||
...(eslintPluginBetterTailwindcss.configs["recommended-warn"] ?? {}).rules,
|
||||
...(eslintPluginBetterTailwindcss.configs["stylistic-warn"] ?? {}).rules,
|
||||
// "tailwindcss/classnames-order": "off",
|
||||
"better-tailwindcss/multiline": [
|
||||
"better-tailwindcss/enforce-consistent-line-wrapping": [
|
||||
"warn",
|
||||
{
|
||||
group: "newLine",
|
||||
@@ -63,4 +60,13 @@ export default [
|
||||
],
|
||||
},
|
||||
},
|
||||
] as Linter.Config[];
|
||||
{
|
||||
files: ["**/*.{js,cjs,mjs,jsx}"],
|
||||
extends: [tseslint.configs.disableTypeChecked],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@5/schema.json",
|
||||
"entry": ["src/index.tsx"],
|
||||
"project": ["src/**/*.ts", "src/**/*.tsx", "src/main.css"],
|
||||
"project": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"],
|
||||
"tailwind": {
|
||||
"entry": ["src/main.css"]
|
||||
},
|
||||
|
||||
48
package.json
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "convertx-frontend",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.1",
|
||||
"scripts": {
|
||||
"dev": "bun run --watch src/index.tsx",
|
||||
"hot": "bun run --hot src/index.tsx",
|
||||
"format": "run-p 'format:*'",
|
||||
"format:eslint": "eslint --fix .",
|
||||
"format:prettier": "prettier --write .",
|
||||
"build": "bunx @tailwindcss/cli -i ./src/main.css -o ./public/generated.css",
|
||||
"build:js": "tsc",
|
||||
"build": "bun x @tailwindcss/cli -i ./src/main.css -o ./public/generated.css && bun run build:js",
|
||||
"lint": "run-p 'lint:*'",
|
||||
"lint:tsc": "tsc --noEmit",
|
||||
"lint:knip": "knip",
|
||||
@@ -15,12 +16,13 @@
|
||||
"lint:prettier": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@elysiajs/html": "^1.3.0",
|
||||
"@elysiajs/jwt": "^1.3.1",
|
||||
"@elysiajs/html": "^1.3.1",
|
||||
"@elysiajs/jwt": "^1.3.2",
|
||||
"@elysiajs/static": "^1.3.0",
|
||||
"@kitajs/html": "^4.2.9",
|
||||
"elysia": "^1.3.4",
|
||||
"sanitize-filename": "^1.6.3"
|
||||
"elysia": "^1.3.8",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"tar": "^7.4.3"
|
||||
},
|
||||
"module": "src/index.tsx",
|
||||
"type": "module",
|
||||
@@ -28,27 +30,27 @@
|
||||
"start": "bun run src/index.tsx"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.28.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
||||
"@kitajs/ts-html-plugin": "^4.1.1",
|
||||
"@tailwindcss/cli": "^4.1.8",
|
||||
"@tailwindcss/postcss": "^4.1.8",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.6.2",
|
||||
"@kitajs/ts-html-plugin": "^4.1.2",
|
||||
"@tailwindcss/cli": "^4.1.11",
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@total-typescript/ts-reset": "^0.6.1",
|
||||
"@types/bun": "^1.2.15",
|
||||
"@types/node": "^22.15.29",
|
||||
"@typescript-eslint/parser": "^8.33.1",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint-plugin-better-tailwindcss": "^3.1.0",
|
||||
"@types/bun": "latest",
|
||||
"@types/node": "^24.2.1",
|
||||
"@typescript-eslint/parser": "^8.39.1",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.4",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^16.2.0",
|
||||
"knip": "^5.59.1",
|
||||
"globals": "^16.3.0",
|
||||
"knip": "^5.62.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"postcss": "^8.5.4",
|
||||
"prettier": "^3.5.3",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.2",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
"tailwindcss": "^4.1.8",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.33.1"
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.39.1"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 405 B |
|
Before Width: | Height: | Size: 960 B After Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
1
public/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="512" height="512" style="cursor:default" viewBox="0 0 96.983 132.292"><g transform="translate(-56.568 -82.29)"><path d="M124.878 83.82h-60.91a5.86 5.86 0 0 0-5.87 5.87v117.496a5.855 5.855 0 0 0 5.87 5.866h82.182a5.855 5.855 0 0 0 5.87-5.866v-92.55z" style="display:inline;fill:#111827;stroke:#aeb9d0;stroke-width:3.06006;stroke-dasharray:none;stroke-opacity:1"/><circle cx="84.331" cy="128.904" r="6.653" style="fill:#84cc16;fill-opacity:1;stroke-width:2.13082"/><circle cx="105.059" cy="128.904" r="6.653" style="fill:#fff;fill-opacity:1;stroke-width:2.13082"/><circle cx="125.786" cy="128.904" r="6.653" style="fill:#84cc16;fill-opacity:1;stroke-width:2.13082"/><circle cx="84.331" cy="148.438" r="6.653" style="fill:#fff;fill-opacity:1;stroke-width:2.13082"/><circle cx="105.059" cy="148.438" r="6.653" style="display:inline;fill:#84cc16;fill-opacity:1;stroke-width:2.13082"/><circle cx="125.786" cy="148.438" r="6.653" style="fill:#fff;fill-opacity:1;stroke-width:2.13082"/><circle cx="84.331" cy="167.971" r="6.653" style="fill:#84cc16;fill-opacity:1;stroke-width:2.13082"/><circle cx="105.059" cy="167.971" r="6.653" style="fill:#fff;fill-opacity:1;stroke-width:2.13082"/><circle cx="125.786" cy="167.971" r="6.653" style="fill:#84cc16;fill-opacity:1;stroke-width:2.13082"/><path d="M119.124 161.326h13.287v13.287h-13.287z" style="fill:#84cc16;fill-opacity:1;stroke:none;stroke-width:3.04496;stroke-opacity:1"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,18 +1,4 @@
|
||||
const webroot = document.querySelector("meta[name='webroot']").content;
|
||||
|
||||
window.downloadAll = function () {
|
||||
// Get all download links
|
||||
const downloadLinks = document.querySelectorAll("a[download]");
|
||||
|
||||
// Trigger download for each link
|
||||
downloadLinks.forEach((link, index) => {
|
||||
// We add a delay for each download to prevent them from starting at the same time
|
||||
setTimeout(() => {
|
||||
const event = new MouseEvent("click");
|
||||
link.dispatchEvent(event);
|
||||
}, index * 100);
|
||||
});
|
||||
};
|
||||
const jobId = window.location.pathname.split("/").pop();
|
||||
const main = document.querySelector("main");
|
||||
let progressElem = document.querySelector("progress");
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true,
|
||||
"automerge": true
|
||||
}
|
||||
},
|
||||
"ignoreDeps": ["bun-types", "@types/bun"]
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export const BaseHtml = ({
|
||||
<link rel="icon" type="image/png" sizes="16x16" href={`${webroot}/favicon-16x16.png`} />
|
||||
<link rel="manifest" href={`${webroot}/site.webmanifest`} />
|
||||
</head>
|
||||
<body class="flex min-h-screen w-full flex-col bg-neutral-900 text-neutral-200">
|
||||
<body class={`flex min-h-screen w-full flex-col bg-neutral-900 text-neutral-200`}>
|
||||
{children}
|
||||
<footer class="w-full">
|
||||
<div class="p-4 text-center text-sm text-neutral-500">
|
||||
|
||||
@@ -91,7 +91,7 @@ export const Header = ({
|
||||
|
||||
return (
|
||||
<header class="w-full p-4">
|
||||
<nav class="mx-auto flex max-w-4xl justify-between rounded-sm bg-neutral-900 p-4">
|
||||
<nav class={`mx-auto flex max-w-4xl justify-between rounded-sm bg-neutral-900 p-4`}>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -116,8 +117,8 @@ export async function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("assimp", ["export", filePath, targetPath], (error, stdout, stderr) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -39,6 +40,7 @@ export const properties = {
|
||||
"fb2",
|
||||
"html",
|
||||
"htmlz",
|
||||
"kepub.epub",
|
||||
"lit",
|
||||
"lrf",
|
||||
"mobi",
|
||||
@@ -61,8 +63,8 @@ export async function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("ebook-convert", [filePath, targetPath], (error, stdout, stderr) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -14,8 +15,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
const inputArgs: string[] = [];
|
||||
if (fileType === "eps") {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
// This could be done dynamically by running `ffmpeg -formats` and parsing the output
|
||||
export const properties = {
|
||||
@@ -691,8 +692,8 @@ export async function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
let extraArgs: string[] = [];
|
||||
let message = "Done";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -313,8 +314,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("gm", ["convert", filePath, targetPath], (error, stdout, stderr) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
// declare possible conversions
|
||||
export const properties = {
|
||||
@@ -445,8 +446,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
let outputArgs: string[] = [];
|
||||
let inputArgs: string[] = [];
|
||||
@@ -460,6 +461,13 @@ export function convert(
|
||||
}
|
||||
}
|
||||
|
||||
// Handle EMF files specifically to avoid LibreOffice delegate issues
|
||||
if (fileType === "emf") {
|
||||
// Use direct conversion without delegates for EMF files
|
||||
inputArgs.push("-define", "emf:delegate=false", "-density", "300");
|
||||
outputArgs.push("-background", "white", "-alpha", "remove");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
"magick",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -32,8 +33,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("inkscape", [filePath, "-o", targetPath], (error, stdout, stderr) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "child_process";
|
||||
import { execFile as execFileOriginal } from "child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -14,8 +15,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("heif-convert", [filePath, targetPath], (error, stdout, stderr) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
// declare possible conversions
|
||||
export const properties = {
|
||||
@@ -17,8 +18,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
let tool = "";
|
||||
if (fileType === "jxl") {
|
||||
|
||||
177
src/converters/libreoffice.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
text: [
|
||||
"602",
|
||||
"abw",
|
||||
"csv",
|
||||
"cwk",
|
||||
"doc",
|
||||
"docm",
|
||||
"docx",
|
||||
"dot",
|
||||
"dotx",
|
||||
"dotm",
|
||||
"epub",
|
||||
"fb2",
|
||||
"fodt",
|
||||
"htm",
|
||||
"html",
|
||||
"hwp",
|
||||
"mcw",
|
||||
"mw",
|
||||
"mwd",
|
||||
"lwp",
|
||||
"lrf",
|
||||
"odt",
|
||||
"ott",
|
||||
"pages",
|
||||
"pdf",
|
||||
"psw",
|
||||
"rtf",
|
||||
"sdw",
|
||||
"stw",
|
||||
"sxw",
|
||||
"tab",
|
||||
"tsv",
|
||||
"txt",
|
||||
"wn",
|
||||
"wpd",
|
||||
"wps",
|
||||
"wpt",
|
||||
"wri",
|
||||
"xhtml",
|
||||
"xml",
|
||||
"zabw",
|
||||
],
|
||||
},
|
||||
to: {
|
||||
text: [
|
||||
"csv",
|
||||
"doc",
|
||||
"docm",
|
||||
"docx",
|
||||
"dot",
|
||||
"dotx",
|
||||
"dotm",
|
||||
"epub",
|
||||
"fodt",
|
||||
"htm",
|
||||
"html",
|
||||
"odt",
|
||||
"ott",
|
||||
"pdf",
|
||||
"rtf",
|
||||
"tab",
|
||||
"tsv",
|
||||
"txt",
|
||||
"wps",
|
||||
"wpt",
|
||||
"xhtml",
|
||||
"xml",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
type FileCategories = "text" | "calc";
|
||||
|
||||
const filters: Record<FileCategories, Record<string, string>> = {
|
||||
text: {
|
||||
"602": "T602Document",
|
||||
abw: "AbiWord",
|
||||
csv: "Text",
|
||||
doc: "MS Word 97",
|
||||
docm: "MS Word 2007 XML VBA",
|
||||
docx: "MS Word 2007 XML",
|
||||
dot: "MS Word 97 Vorlage",
|
||||
dotx: "MS Word 2007 XML Template",
|
||||
dotm: "MS Word 2007 XML Template",
|
||||
epub: "EPUB",
|
||||
fb2: "Fictionbook 2",
|
||||
fodt: "OpenDocument Text Flat XML",
|
||||
htm: "HTML (StarWriter)",
|
||||
html: "HTML (StarWriter)",
|
||||
hwp: "writer_MIZI_Hwp_97",
|
||||
mcw: "MacWrite",
|
||||
mw: "MacWrite",
|
||||
mwd: "Mariner_Write",
|
||||
lwp: "LotusWordPro",
|
||||
lrf: "BroadBand eBook",
|
||||
odt: "writer8",
|
||||
ott: "writer8_template",
|
||||
pages: "Apple Pages",
|
||||
// pdf: "writer_pdf_import",
|
||||
psw: "PocketWord File",
|
||||
rtf: "Rich Text Format",
|
||||
sdw: "StarOffice_Writer",
|
||||
stw: "writer_StarOffice_XML_Writer_Template",
|
||||
sxw: "StarOffice XML (Writer)",
|
||||
tab: "Text",
|
||||
tsv: "Text",
|
||||
txt: "Text",
|
||||
wn: "WriteNow",
|
||||
wpd: "WordPerfect",
|
||||
wps: "MS Word 97",
|
||||
wpt: "MS Word 97 Vorlage",
|
||||
wri: "MS_Write",
|
||||
xhtml: "HTML (StarWriter)",
|
||||
xml: "OpenDocument Text Flat XML",
|
||||
zabw: "AbiWord",
|
||||
},
|
||||
calc: {},
|
||||
};
|
||||
|
||||
const getFilters = (fileType: string, converto: string) => {
|
||||
if (fileType in filters.text && converto in filters.text) {
|
||||
return [filters.text[fileType], filters.text[converto]];
|
||||
} else if (fileType in filters.calc && converto in filters.calc) {
|
||||
return [filters.calc[fileType], filters.calc[converto]];
|
||||
}
|
||||
return [null, null];
|
||||
};
|
||||
|
||||
export function convert(
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal,
|
||||
): Promise<string> {
|
||||
const outputPath = targetPath.split("/").slice(0, -1).join("/").replace("./", "") ?? targetPath;
|
||||
|
||||
// Build arguments array
|
||||
const args: string[] = [];
|
||||
args.push("--headless");
|
||||
const [inFilter, outFilter] = getFilters(fileType, convertTo);
|
||||
|
||||
if (inFilter) {
|
||||
args.push(`--infilter="${inFilter}"`);
|
||||
}
|
||||
|
||||
if (outFilter) {
|
||||
args.push("--convert-to", `${convertTo}:${outFilter}`, "--outdir", outputPath, filePath);
|
||||
} else {
|
||||
args.push("--convert-to", convertTo, "--outdir", outputPath, filePath);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("soffice", args, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(`error: ${error}`);
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.error(`stderr: ${stderr}`);
|
||||
}
|
||||
|
||||
resolve("Done");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||
import { Cookie } from "elysia";
|
||||
import db from "../db/db";
|
||||
import { MAX_CONVERT_PROCESS } from "../helpers/env";
|
||||
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
||||
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
|
||||
import { convert as convertCalibre, properties as propertiesCalibre } from "./calibre";
|
||||
import { convert as convertDvisvgm, properties as propertiesDvisvgm } from "./dvisvgm";
|
||||
@@ -11,10 +14,13 @@ import { convert as convertImagemagick, properties as propertiesImagemagick } fr
|
||||
import { convert as convertInkscape, properties as propertiesInkscape } from "./inkscape";
|
||||
import { convert as convertLibheif, properties as propertiesLibheif } from "./libheif";
|
||||
import { convert as convertLibjxl, properties as propertiesLibjxl } from "./libjxl";
|
||||
import { convert as convertLibreOffice, properties as propertiesLibreOffice } from "./libreoffice";
|
||||
import { convert as convertMsgconvert, properties as propertiesMsgconvert } from "./msgconvert";
|
||||
import { convert as convertPandoc, properties as propertiesPandoc } from "./pandoc";
|
||||
import { convert as convertPotrace, properties as propertiesPotrace } from "./potrace";
|
||||
import { convert as convertresvg, properties as propertiesresvg } from "./resvg";
|
||||
import { convert as convertImage, properties as propertiesImage } from "./vips";
|
||||
import { convert as convertVtracer, properties as propertiesVtracer } from "./vtracer";
|
||||
import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex";
|
||||
|
||||
// This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular
|
||||
@@ -47,6 +53,11 @@ const properties: Record<
|
||||
) => unknown;
|
||||
}
|
||||
> = {
|
||||
// Prioritize Inkscape for EMF files as it handles them better than ImageMagick
|
||||
inkscape: {
|
||||
properties: propertiesInkscape,
|
||||
converter: convertInkscape,
|
||||
},
|
||||
libjxl: {
|
||||
properties: propertiesLibjxl,
|
||||
converter: convertLibjxl,
|
||||
@@ -71,10 +82,18 @@ const properties: Record<
|
||||
properties: propertiesCalibre,
|
||||
converter: convertCalibre,
|
||||
},
|
||||
libreoffice: {
|
||||
properties: propertiesLibreOffice,
|
||||
converter: convertLibreOffice,
|
||||
},
|
||||
pandoc: {
|
||||
properties: propertiesPandoc,
|
||||
converter: convertPandoc,
|
||||
},
|
||||
msgconvert: {
|
||||
properties: propertiesMsgconvert,
|
||||
converter: convertMsgconvert,
|
||||
},
|
||||
dvisvgm: {
|
||||
properties: propertiesDvisvgm,
|
||||
converter: convertDvisvgm,
|
||||
@@ -87,10 +106,6 @@ const properties: Record<
|
||||
properties: propertiesGraphicsmagick,
|
||||
converter: convertGraphicsmagick,
|
||||
},
|
||||
inkscape: {
|
||||
properties: propertiesInkscape,
|
||||
converter: convertInkscape,
|
||||
},
|
||||
assimp: {
|
||||
properties: propertiesassimp,
|
||||
converter: convertassimp,
|
||||
@@ -103,9 +118,63 @@ const properties: Record<
|
||||
properties: propertiesPotrace,
|
||||
converter: convertPotrace,
|
||||
},
|
||||
vtracer: {
|
||||
properties: propertiesVtracer,
|
||||
converter: convertVtracer,
|
||||
},
|
||||
};
|
||||
|
||||
export async function mainConverter(
|
||||
function chunks<T>(arr: T[], size: number): T[][] {
|
||||
if (size <= 0) {
|
||||
return [arr];
|
||||
}
|
||||
return Array.from({ length: Math.ceil(arr.length / size) }, (_: T, i: number) =>
|
||||
arr.slice(i * size, i * size + size),
|
||||
);
|
||||
}
|
||||
|
||||
export async function handleConvert(
|
||||
fileNames: string[],
|
||||
userUploadsDir: string,
|
||||
userOutputDir: string,
|
||||
convertTo: string,
|
||||
converterName: string,
|
||||
jobId: Cookie<string | undefined>,
|
||||
) {
|
||||
const query = db.query(
|
||||
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?1, ?2, ?3, ?4)",
|
||||
);
|
||||
|
||||
for (const chunk of chunks(fileNames, MAX_CONVERT_PROCESS)) {
|
||||
const toProcess: Promise<string>[] = [];
|
||||
for (const fileName of chunk) {
|
||||
const filePath = `${userUploadsDir}${fileName}`;
|
||||
const fileTypeOrig = fileName.split(".").pop() ?? "";
|
||||
const fileType = normalizeFiletype(fileTypeOrig);
|
||||
const newFileExt = normalizeOutputFiletype(convertTo);
|
||||
const newFileName = fileName.replace(
|
||||
new RegExp(`${fileTypeOrig}(?!.*${fileTypeOrig})`),
|
||||
newFileExt,
|
||||
);
|
||||
const targetPath = `${userOutputDir}${newFileName}`;
|
||||
toProcess.push(
|
||||
new Promise((resolve, reject) => {
|
||||
mainConverter(filePath, fileType, convertTo, targetPath, {}, converterName)
|
||||
.then((r) => {
|
||||
if (jobId.value) {
|
||||
query.run(jobId.value, fileName, newFileName, r);
|
||||
}
|
||||
resolve(r);
|
||||
})
|
||||
.catch((c) => reject(c));
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(toProcess);
|
||||
}
|
||||
}
|
||||
|
||||
async function mainConverter(
|
||||
inputFilePath: string,
|
||||
fileTypeOriginal: string,
|
||||
convertTo: string,
|
||||
|
||||
52
src/converters/msgconvert.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
email: ["msg"],
|
||||
},
|
||||
to: {
|
||||
email: ["eml"],
|
||||
},
|
||||
};
|
||||
|
||||
export function convert(
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal,
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fileType === "msg" && convertTo === "eml") {
|
||||
// Convert MSG to EML using msgconvert
|
||||
// msgconvert will output to the same directory as the input file with .eml extension
|
||||
// We need to use --outfile to specify the target path
|
||||
const args = ["--outfile", targetPath, filePath];
|
||||
|
||||
execFile("msgconvert", args, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(new Error(`msgconvert failed: ${error.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
// Log sanitized stderr to avoid exposing sensitive paths
|
||||
const sanitizedStderr = stderr.replace(/(\/[^\s]+)/g, "[REDACTED_PATH]");
|
||||
console.warn(
|
||||
`msgconvert stderr: ${sanitizedStderr.length > 200 ? sanitizedStderr.slice(0, 200) + "..." : sanitizedStderr}`,
|
||||
);
|
||||
}
|
||||
|
||||
resolve(targetPath);
|
||||
});
|
||||
} else {
|
||||
reject(
|
||||
new Error(
|
||||
`Unsupported conversion from ${fileType} to ${convertTo}. Only MSG to EML conversion is currently supported.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -124,8 +125,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal,
|
||||
): Promise<string> {
|
||||
// set xelatex here
|
||||
const xelatex = ["pdf", "latex"];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -26,8 +27,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("potrace", [filePath, "-o", targetPath, "-b", convertTo], (error, stdout, stderr) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -14,8 +15,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile("resvg", [filePath, targetPath], (error, stdout, stderr) => {
|
||||
|
||||
15
src/converters/types.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export type ExecFileFn = (
|
||||
cmd: string,
|
||||
args: string[],
|
||||
callback: (err: Error | null, stdout: string, stderr: string) => void,
|
||||
options?: import("child_process").ExecFileOptions,
|
||||
) => void;
|
||||
|
||||
export type ConvertFnWithExecFile = (
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
options: unknown,
|
||||
execFileOverride?: ExecFileFn,
|
||||
) => Promise<string>;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
// declare possible conversions
|
||||
export const properties = {
|
||||
@@ -94,8 +95,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal,
|
||||
): Promise<string> {
|
||||
// if (fileType === "svg") {
|
||||
// const scale = options.scale || 1;
|
||||
|
||||
80
src/converters/vtracer.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
images: ["jpg", "jpeg", "png", "bmp", "gif", "tiff", "tif", "webp"],
|
||||
},
|
||||
to: {
|
||||
images: ["svg"],
|
||||
},
|
||||
};
|
||||
|
||||
interface VTracerOptions {
|
||||
colormode?: string;
|
||||
hierarchical?: string;
|
||||
mode?: string;
|
||||
filter_speckle?: string | number;
|
||||
color_precision?: string | number;
|
||||
layer_difference?: string | number;
|
||||
corner_threshold?: string | number;
|
||||
length_threshold?: string | number;
|
||||
max_iterations?: string | number;
|
||||
splice_threshold?: string | number;
|
||||
path_precision?: string | number;
|
||||
}
|
||||
|
||||
export function convert(
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal, // to make it mockable
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Build vtracer arguments
|
||||
const args = ["--input", filePath, "--output", targetPath];
|
||||
|
||||
// Add optional parameter if provided
|
||||
if (options && typeof options === "object") {
|
||||
const opts = options as VTracerOptions;
|
||||
const validOptions: Array<keyof VTracerOptions> = [
|
||||
"colormode",
|
||||
"hierarchical",
|
||||
"mode",
|
||||
"filter_speckle",
|
||||
"color_precision",
|
||||
"layer_difference",
|
||||
"corner_threshold",
|
||||
"length_threshold",
|
||||
"max_iterations",
|
||||
"splice_threshold",
|
||||
"path_precision",
|
||||
];
|
||||
|
||||
for (const option of validOptions) {
|
||||
if (opts[option] !== undefined) {
|
||||
args.push(`--${option}`, String(opts[option]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execFile("vtracer", args, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(`error: ${error}${stderr ? `\nstderr: ${stderr}` : ""}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(`stdout: ${stdout}`);
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.log(`stderr: ${stderr}`);
|
||||
}
|
||||
|
||||
resolve("Done");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile as execFileOriginal } from "node:child_process";
|
||||
import { ExecFileFn } from "./types";
|
||||
|
||||
export const properties = {
|
||||
from: {
|
||||
@@ -14,8 +15,8 @@ export function convert(
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options?: unknown,
|
||||
execFile: ExecFileFn = execFileOriginal,
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
|
||||
|
||||
@@ -13,3 +13,13 @@ export const AUTO_DELETE_EVERY_N_HOURS = process.env.AUTO_DELETE_EVERY_N_HOURS
|
||||
export const HIDE_HISTORY = process.env.HIDE_HISTORY?.toLowerCase() === "true" || false;
|
||||
|
||||
export const WEBROOT = process.env.WEBROOT ?? "";
|
||||
|
||||
export const LANGUAGE = process.env.LANGUAGE?.toLowerCase() || "en";
|
||||
|
||||
export const MAX_CONVERT_PROCESS =
|
||||
process.env.MAX_CONVERT_PROCESS && Number(process.env.MAX_CONVERT_PROCESS) > 0
|
||||
? Number(process.env.MAX_CONVERT_PROCESS)
|
||||
: 0;
|
||||
|
||||
export const UNAUTHENTICATED_USER_SHARING =
|
||||
process.env.UNAUTHENTICATED_USER_SHARING?.toLowerCase() === "true" || false;
|
||||
|
||||
@@ -144,6 +144,26 @@ if (process.env.NODE_ENV === "production") {
|
||||
}
|
||||
});
|
||||
|
||||
exec("soffice --version", (error, stdout) => {
|
||||
if (error) {
|
||||
console.error("libreoffice is not installed");
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(stdout.split("\n")[0]);
|
||||
}
|
||||
});
|
||||
|
||||
exec("msgconvert --version", (error, stdout) => {
|
||||
if (error) {
|
||||
console.error("msgconvert (libemail-outlook-message-perl) is not installed");
|
||||
}
|
||||
|
||||
if (stdout) {
|
||||
console.log(stdout.split("\n")[0]);
|
||||
}
|
||||
});
|
||||
|
||||
exec("bun -v", (error, stdout) => {
|
||||
if (error) {
|
||||
console.error("Bun is not installed. wait what");
|
||||
|
||||
62
src/main.css
@@ -1,21 +1,22 @@
|
||||
@import "./theme/theme.css";
|
||||
@import "tailwindcss";
|
||||
|
||||
@plugin 'tailwind-scrollbar';
|
||||
@plugin "tailwind-scrollbar";
|
||||
|
||||
@theme {
|
||||
--color-contrast: rgba(var(--contrast));
|
||||
--color-neutral-900: rgba(var(--neutral-900));
|
||||
--color-neutral-800: rgba(var(--neutral-800));
|
||||
--color-neutral-700: rgba(var(--neutral-700));
|
||||
--color-neutral-600: rgba(var(--neutral-600));
|
||||
--color-neutral-500: rgba(var(--neutral-500));
|
||||
--color-neutral-400: rgba(var(--neutral-400));
|
||||
--color-neutral-300: rgba(var(--neutral-300));
|
||||
--color-neutral-200: rgba(var(--neutral-200));
|
||||
--color-neutral-100: rgba(var(--neutral-100));
|
||||
--color-accent-600: rgba(var(--accent-600));
|
||||
--color-accent-500: rgba(var(--accent-500));
|
||||
--color-accent-400: rgba(var(--accent-400));
|
||||
--color-contrast: var(--contrast);
|
||||
--color-neutral-900: var(--neutral-900);
|
||||
--color-neutral-800: var(--neutral-800);
|
||||
--color-neutral-700: var(--neutral-700);
|
||||
--color-neutral-600: var(--neutral-600);
|
||||
--color-neutral-500: var(--neutral-500);
|
||||
--color-neutral-400: var(--neutral-400);
|
||||
--color-neutral-300: var(--neutral-300);
|
||||
--color-neutral-200: var(--neutral-200);
|
||||
--color-neutral-100: var(--neutral-100);
|
||||
--color-accent-600: var(--accent-600);
|
||||
--color-accent-500: var(--accent-500);
|
||||
--color-accent-400: var(--accent-400);
|
||||
}
|
||||
|
||||
@utility article {
|
||||
@@ -29,36 +30,3 @@
|
||||
@utility btn-secondary {
|
||||
@apply bg-neutral-400 text-contrast rounded-sm p-2 sm:p-4 hover:bg-neutral-300 cursor-pointer transition-colors;
|
||||
}
|
||||
|
||||
:root {
|
||||
--contrast: 255, 255, 255;
|
||||
--neutral-900: 243, 244, 246;
|
||||
--neutral-800: 229, 231, 235;
|
||||
--neutral-700: 209, 213, 219;
|
||||
--neutral-600: 156, 163, 175;
|
||||
--neutral-500: 180, 180, 180;
|
||||
--neutral-400: 75, 85, 99;
|
||||
--neutral-300: 55, 65, 81;
|
||||
--neutral-200: 31, 41, 55;
|
||||
--neutral-100: 17, 24, 39;
|
||||
--accent-400: 132, 204, 22;
|
||||
--accent-500: 101, 163, 13;
|
||||
--accent-600: 77, 124, 15;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--contrast: 0, 0, 0;
|
||||
--neutral-900: 17, 24, 39;
|
||||
--neutral-800: 31, 41, 55;
|
||||
--neutral-700: 55, 65, 81;
|
||||
--neutral-600: 75, 85, 99;
|
||||
--neutral-500: 107, 114, 128;
|
||||
--neutral-300: 209, 213, 219;
|
||||
--neutral-400: 156, 163, 175;
|
||||
--neutral-200: 229, 231, 235;
|
||||
--accent-600: 101, 163, 13;
|
||||
--accent-500: 132, 204, 22;
|
||||
--accent-400: 163, 230, 53;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const chooseConverter = new Elysia().use(userService).post(
|
||||
>
|
||||
{Object.entries(getPossibleTargets(body.fileType)).map(([converter, targets]) => (
|
||||
<article
|
||||
class="convert_to_group flex w-full flex-col border-b border-neutral-700 p-4"
|
||||
class={`convert_to_group flex w-full flex-col border-b border-neutral-700 p-4`}
|
||||
data-converter={converter}
|
||||
>
|
||||
<header class="mb-2 w-full text-xl font-bold" safe>
|
||||
|
||||
@@ -2,11 +2,11 @@ import { mkdir } from "node:fs/promises";
|
||||
import { Elysia, t } from "elysia";
|
||||
import sanitize from "sanitize-filename";
|
||||
import { outputDir, uploadsDir } from "..";
|
||||
import { mainConverter } from "../converters/main";
|
||||
import { handleConvert } from "../converters/main";
|
||||
import db from "../db/db";
|
||||
import { Jobs } from "../db/types";
|
||||
import { WEBROOT } from "../helpers/env";
|
||||
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||
import { userService } from "./user";
|
||||
|
||||
export const convert = new Elysia().use(userService).post(
|
||||
@@ -46,6 +46,11 @@ export const convert = new Elysia().use(userService).post(
|
||||
|
||||
const convertTo = normalizeFiletype(body.convert_to.split(",")[0] ?? "");
|
||||
const converterName = body.convert_to.split(",")[1];
|
||||
|
||||
if (!converterName) {
|
||||
return redirect(`${WEBROOT}/`, 302);
|
||||
}
|
||||
|
||||
const fileNames = JSON.parse(body.file_names) as string[];
|
||||
|
||||
for (let i = 0; i < fileNames.length; i++) {
|
||||
@@ -61,36 +66,8 @@ export const convert = new Elysia().use(userService).post(
|
||||
jobId.value,
|
||||
);
|
||||
|
||||
const query = db.query(
|
||||
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?1, ?2, ?3, ?4)",
|
||||
);
|
||||
|
||||
// Start the conversion process in the background
|
||||
Promise.all(
|
||||
fileNames.map(async (fileName) => {
|
||||
const filePath = `${userUploadsDir}${fileName}`;
|
||||
const fileTypeOrig = fileName.split(".").pop() ?? "";
|
||||
const fileType = normalizeFiletype(fileTypeOrig);
|
||||
const newFileExt = normalizeOutputFiletype(convertTo);
|
||||
const newFileName = fileName.replace(
|
||||
new RegExp(`${fileTypeOrig}(?!.*${fileTypeOrig})`),
|
||||
newFileExt,
|
||||
);
|
||||
const targetPath = `${userOutputDir}${newFileName}`;
|
||||
|
||||
const result = await mainConverter(
|
||||
filePath,
|
||||
fileType,
|
||||
convertTo,
|
||||
targetPath,
|
||||
{},
|
||||
converterName,
|
||||
);
|
||||
if (jobId.value) {
|
||||
query.run(jobId.value, fileName, newFileName, result);
|
||||
}
|
||||
}),
|
||||
)
|
||||
handleConvert(fileNames, userUploadsDir, userOutputDir, convertTo, converterName, jobId)
|
||||
.then(() => {
|
||||
// All conversions are done, update the job status to 'completed'
|
||||
if (jobId.value) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import path from "node:path";
|
||||
import { Elysia } from "elysia";
|
||||
import sanitize from "sanitize-filename";
|
||||
import * as tar from "tar";
|
||||
import { outputDir } from "..";
|
||||
import db from "../db/db";
|
||||
import { WEBROOT } from "../helpers/env";
|
||||
@@ -35,8 +37,7 @@ export const download = new Elysia()
|
||||
return Bun.file(filePath);
|
||||
},
|
||||
)
|
||||
.get("/zip/:userId/:jobId", async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||
// TODO: Implement zip download
|
||||
.get("/archive/:userId/:jobId", async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||
if (!auth?.value) {
|
||||
return redirect(`${WEBROOT}/login`, 302);
|
||||
}
|
||||
@@ -54,9 +55,20 @@ export const download = new Elysia()
|
||||
return redirect(`${WEBROOT}/results`, 302);
|
||||
}
|
||||
|
||||
// const userId = decodeURIComponent(params.userId);
|
||||
// const jobId = decodeURIComponent(params.jobId);
|
||||
// const outputPath = `${outputDir}${userId}/`{jobId}/);
|
||||
const userId = decodeURIComponent(params.userId);
|
||||
const jobId = decodeURIComponent(params.jobId);
|
||||
const outputPath = `${outputDir}${userId}/${jobId}`;
|
||||
const outputTar = path.join(outputPath, `converted_files_${jobId}.tar`);
|
||||
|
||||
// return Bun.zip(outputPath);
|
||||
await tar.create(
|
||||
{
|
||||
file: outputTar,
|
||||
cwd: outputPath,
|
||||
filter: (path) => {
|
||||
return !path.match(".*\\.tar");
|
||||
},
|
||||
},
|
||||
["."],
|
||||
);
|
||||
return Bun.file(outputTar);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { BaseHtml } from "../components/base";
|
||||
import { Header } from "../components/header";
|
||||
import db from "../db/db";
|
||||
import { Filename, Jobs } from "../db/types";
|
||||
import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, WEBROOT } from "../helpers/env";
|
||||
import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, LANGUAGE, WEBROOT } from "../helpers/env";
|
||||
import { userService } from "./user";
|
||||
|
||||
export const history = new Elysia()
|
||||
@@ -133,7 +133,7 @@ export const history = new Elysia()
|
||||
/>
|
||||
</svg>
|
||||
</td>
|
||||
<td safe>{new Date(job.date_created).toLocaleTimeString()}</td>
|
||||
<td safe>{new Date(job.date_created).toLocaleTimeString(LANGUAGE)}</td>
|
||||
<td>{job.num_files}</td>
|
||||
<td class="max-sm:hidden">{job.finished_files}</td>
|
||||
<td safe>{job.status}</td>
|
||||
@@ -162,7 +162,7 @@ export const history = new Elysia()
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="mx-2 inline-block h-4 w-4 text-neutral-500"
|
||||
class={`mx-2 inline-block h-4 w-4 text-neutral-500`}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Html } from "@elysiajs/html";
|
||||
import { JWTPayloadSpec } from "@elysiajs/jwt";
|
||||
import { Elysia } from "elysia";
|
||||
import { BaseHtml } from "../components/base";
|
||||
import { Header } from "../components/header";
|
||||
@@ -8,10 +9,14 @@ import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
||||
import { userService } from "./user";
|
||||
|
||||
function ResultsArticle({
|
||||
user,
|
||||
job,
|
||||
files,
|
||||
outputPath,
|
||||
}: {
|
||||
user: {
|
||||
id: string;
|
||||
} & JWTPayloadSpec;
|
||||
job: Jobs;
|
||||
files: Filename[];
|
||||
outputPath: string;
|
||||
@@ -21,14 +26,19 @@ function ResultsArticle({
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h1 class="text-xl">Results</h1>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="float-right w-40 btn-primary"
|
||||
onclick="downloadAll()"
|
||||
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
||||
<a
|
||||
style={files.length !== job.num_files ? "pointer-events: none;" : ""}
|
||||
href={`${WEBROOT}/archive/${user.id}/${job.id}`}
|
||||
download={`converted_files_${job.id}.tar`}
|
||||
>
|
||||
{files.length === job.num_files ? "Download All" : "Converting..."}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="float-right w-40 btn-primary"
|
||||
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
||||
>
|
||||
{files.length === job.num_files ? "Download All" : "Converting..."}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<progress
|
||||
@@ -170,7 +180,7 @@ export const results = new Elysia()
|
||||
sm:px-4
|
||||
`}
|
||||
>
|
||||
<ResultsArticle job={job} files={files} outputPath={outputPath} />
|
||||
<ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />
|
||||
</main>
|
||||
<script src={`${WEBROOT}/results.js`} defer />
|
||||
</>
|
||||
@@ -211,5 +221,5 @@ export const results = new Elysia()
|
||||
.as(Filename)
|
||||
.all(params.jobId);
|
||||
|
||||
return <ResultsArticle job={job} files={files} outputPath={outputPath} />;
|
||||
return <ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />;
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ALLOW_UNAUTHENTICATED,
|
||||
HIDE_HISTORY,
|
||||
HTTP_ALLOWED,
|
||||
UNAUTHENTICATED_USER_SHARING,
|
||||
WEBROOT,
|
||||
} from "../helpers/env";
|
||||
import { FIRST_RUN, userService } from "./user";
|
||||
@@ -33,7 +34,9 @@ export const root = new Elysia()
|
||||
let user: ({ id: string } & JWTPayloadSpec) | false = false;
|
||||
if (ALLOW_UNAUTHENTICATED) {
|
||||
const newUserId = String(
|
||||
randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
|
||||
UNAUTHENTICATED_USER_SHARING
|
||||
? 0
|
||||
: randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
|
||||
);
|
||||
const accessToken = await jwt.sign({
|
||||
id: newUserId,
|
||||
@@ -182,7 +185,7 @@ export const root = new Elysia()
|
||||
<header class="mb-2 w-full text-xl font-bold" safe>
|
||||
{converter}
|
||||
</header>
|
||||
<ul class="convert_to_target flex flex-row flex-wrap gap-1">
|
||||
<ul class={`convert_to_target flex flex-row flex-wrap gap-1`}>
|
||||
{targets.map((target) => (
|
||||
<button
|
||||
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
||||
|
||||
47
src/theme/theme.css
Normal file
@@ -0,0 +1,47 @@
|
||||
:root {
|
||||
/* Light mode */
|
||||
--contrast: oklch(100% 0 0);
|
||||
/* Neutral colors - Gray */
|
||||
--neutral-950: oklch(98.5% 0.002 247.839);
|
||||
--neutral-900: oklch(96.7% 0.003 264.542);
|
||||
--neutral-800: oklch(92.8% 0.006 264.531);
|
||||
--neutral-700: oklch(87.2% 0.01 258.338);
|
||||
--neutral-600: oklch(70.7% 0.022 261.325);
|
||||
--neutral-500: oklch(55.1% 0.027 264.364);
|
||||
--neutral-400: oklch(44.6% 0.03 256.802);
|
||||
--neutral-300: oklch(37.3% 0.034 259.733);
|
||||
--neutral-200: oklch(26.9% 0 0);
|
||||
--neutral-100: oklch(21% 0.034 264.665);
|
||||
--neutral-50: oklch(13% 0.028 261.692);
|
||||
/* lime-700 */
|
||||
--accent-600: oklch(53.2% 0.157 131.589);
|
||||
/* lime-600 */
|
||||
--accent-500: oklch(64.8% 0.2 131.684);
|
||||
/* lime-500 */
|
||||
--accent-400: oklch(76.8% 0.233 130.85);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* Dark mode */
|
||||
:root {
|
||||
--contrast: oklch(0% 0 0);
|
||||
/* Neutral colors - Gray */
|
||||
--neutral-950: oklch(13% 0.028 261.692);
|
||||
--neutral-900: oklch(21% 0.034 264.665);
|
||||
--neutral-800: oklch(27.8% 0.033 256.848);
|
||||
--neutral-700: oklch(37.3% 0.034 259.733);
|
||||
--neutral-600: oklch(44.6% 0.03 256.802);
|
||||
--neutral-500: oklch(55.1% 0.027 264.364);
|
||||
--neutral-400: oklch(70.7% 0.022 261.325);
|
||||
--neutral-300: oklch(87.2% 0.01 258.338);
|
||||
--neutral-200: oklch(92.8% 0.006 264.531);
|
||||
--neutral-100: oklch(96.7% 0.003 264.542);
|
||||
--neutral-50: oklch(98.5% 0.002 247.839);
|
||||
/* lime-600 */
|
||||
--accent-600: oklch(64.8% 0.2 131.684);
|
||||
/* lime-500 */
|
||||
--accent-500: oklch(76.8% 0.233 130.85);
|
||||
/* lime-400 */
|
||||
--accent-400: oklch(84.1% 0.238 128.85);
|
||||
}
|
||||
}
|
||||
7
tests/converters/assimp.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/assimp";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
7
tests/converters/calibre.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/calibre";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
91
tests/converters/dvisvgm.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { ExecFileException } from "node:child_process";
|
||||
import { beforeEach, expect, test } from "bun:test";
|
||||
import { convert } from "../../src/converters/dvisvgm";
|
||||
import { ExecFileFn } from "../../src/converters/types";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
let calls: string[][] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
calls = [];
|
||||
});
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test("convert respects eps filetype", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.eps", "eps", "stl", "output.stl", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["--eps", "input.eps", "output.stl"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert respects pdf filetype", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.pdf", "pdf", "stl", "output.stl", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["--pdf", "input.pdf", "output.stl"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert respects svgz conversion target type", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.obj", "eps", "svgz", "output.svgz", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["-z", "input.obj", "output.svgz"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
181
tests/converters/ffmpeg.test.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import { beforeEach, expect, test } from "bun:test";
|
||||
import { convert } from "../../src/converters/ffmpeg";
|
||||
|
||||
let calls: string[][] = [];
|
||||
|
||||
function mockExecFile(
|
||||
_cmd: string,
|
||||
args: string[],
|
||||
callback: (err: Error | null, stdout: string, stderr: string) => void,
|
||||
) {
|
||||
calls.push(args);
|
||||
if (args.includes("fail.mov")) {
|
||||
callback(new Error("mock failure"), "", "Fake stderr: fail");
|
||||
} else {
|
||||
callback(null, "Fake stdout", "");
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
calls = [];
|
||||
delete process.env.FFMPEG_ARGS;
|
||||
});
|
||||
|
||||
test("converts a normal file", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const result = await convert("in.mp4", "mp4", "avi", "out.avi", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["-i", "in.mp4", "out.avi"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("adds resize for ico output", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const result = await convert("in.png", "png", "ico", "out.ico", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done: resized to 256x256");
|
||||
expect(calls[0]).toEqual(
|
||||
expect.arrayContaining(["-filter:v", expect.stringContaining("scale=")]),
|
||||
);
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("uses libaom-av1 for av1.mp4", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
await convert("in.mkv", "mkv", "av1.mp4", "out.mp4", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["-c:v", "libaom-av1"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("uses libx264 for h264.mp4", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
await convert("in.mkv", "mkv", "h264.mp4", "out.mp4", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["-c:v", "libx264"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("uses libx265 for h265.mp4", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
await convert("in.mkv", "mkv", "h265.mp4", "out.mp4", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["-c:v", "libx265"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("uses libx266 for h266.mp4", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
await convert("in.mkv", "mkv", "h266.mp4", "out.mp4", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["-c:v", "libx266"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("respects FFMPEG_ARGS", async () => {
|
||||
process.env.FFMPEG_ARGS = "-hide_banner -y";
|
||||
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
await convert("input.mov", "mov", "mp4", "output.mp4", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(calls[0]?.slice(0, 2)).toEqual(["-hide_banner", "-y"]);
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("fails on exec error", async () => {
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.error = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
expect(convert("fail.mov", "mov", "mp4", "output.mp4", undefined, mockExecFile)).rejects.toThrow(
|
||||
"mock failure",
|
||||
);
|
||||
|
||||
console.error = originalConsoleError;
|
||||
|
||||
expect(loggedMessage).toBe("stderr: Fake stderr: fail");
|
||||
});
|
||||
|
||||
test("logs stderr when execFile returns only stderr and no error", async () => {
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.error = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
// Mock execFile to call back with no error, no stdout, but with stderr
|
||||
const mockExecFileStderrOnly = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: Error | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(null, "", "Only stderr output");
|
||||
};
|
||||
|
||||
await convert("input.mov", "mov", "mp4", "output.mp4", undefined, mockExecFileStderrOnly);
|
||||
|
||||
console.error = originalConsoleError;
|
||||
|
||||
expect(loggedMessage).toBe("stderr: Only stderr output");
|
||||
});
|
||||
7
tests/converters/graphicsmagick.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/graphicsmagick";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
26
tests/converters/helpers/commonTests.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { test } from "bun:test";
|
||||
import { ConvertFnWithExecFile } from "../../../src/converters/types";
|
||||
import {
|
||||
runConvertFailTest,
|
||||
runConvertLogsStderror,
|
||||
runConvertLogsStderrorAndStdout,
|
||||
runConvertSuccessTest,
|
||||
} from "./converters";
|
||||
|
||||
export function runCommonTests(convert: ConvertFnWithExecFile) {
|
||||
test("convert resolves when execFile succeeds", async () => {
|
||||
await runConvertSuccessTest(convert);
|
||||
});
|
||||
|
||||
test("convert rejects when execFile fails", async () => {
|
||||
await runConvertFailTest(convert);
|
||||
});
|
||||
|
||||
test("convert logs stderr when present", async () => {
|
||||
await runConvertLogsStderror(convert);
|
||||
});
|
||||
|
||||
test("convert logs both stderr and stdout when present", async () => {
|
||||
await runConvertLogsStderrorAndStdout(convert);
|
||||
});
|
||||
}
|
||||
121
tests/converters/helpers/converters.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { ExecFileException } from "node:child_process";
|
||||
import { expect } from "bun:test";
|
||||
import { ConvertFnWithExecFile, ExecFileFn } from "../../../src/converters/types";
|
||||
|
||||
export async function runConvertSuccessTest(convertFn: ConvertFnWithExecFile) {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convertFn("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
}
|
||||
|
||||
export async function runConvertFailTest(convertFn: ConvertFnWithExecFile) {
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(new Error("Test error"), "", "");
|
||||
};
|
||||
|
||||
expect(
|
||||
convertFn("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile),
|
||||
).rejects.toMatch(/error: Error: Test error/);
|
||||
|
||||
// Test with error object lacking 'message' property
|
||||
const mockExecFileNoMessage: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
// Simulate a non-standard error object
|
||||
callback({ notMessage: true } as unknown as ExecFileException, "", "");
|
||||
};
|
||||
|
||||
expect(
|
||||
convertFn("input.obj", "obj", "stl", "output.stl", undefined, mockExecFileNoMessage),
|
||||
).rejects.toMatch(/error:/i);
|
||||
|
||||
// Test with a non-object error (e.g., a string)
|
||||
const mockExecFileStringError: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback("string error" as unknown as ExecFileException, "", "");
|
||||
};
|
||||
|
||||
expect(
|
||||
convertFn("input.obj", "obj", "stl", "output.stl", undefined, mockExecFileStringError),
|
||||
).rejects.toMatch(/error:/i);
|
||||
}
|
||||
|
||||
export async function runConvertLogsStderror(convertFn: ConvertFnWithExecFile) {
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.error = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: Error | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(null, "", "Fake stderr");
|
||||
};
|
||||
|
||||
await convertFn("file.obj", "obj", "stl", "out.stl", undefined, mockExecFile);
|
||||
|
||||
console.error = originalConsoleError;
|
||||
|
||||
expect(loggedMessage).toBe("stderr: Fake stderr");
|
||||
}
|
||||
|
||||
export async function runConvertLogsStderrorAndStdout(convertFn: ConvertFnWithExecFile) {
|
||||
const originalConsoleError = console.error;
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedError = "";
|
||||
let loggedMessage = "";
|
||||
console.error = (msg) => {
|
||||
loggedError = msg;
|
||||
};
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: Error | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(null, "Fake stdout", "Fake stderr");
|
||||
};
|
||||
|
||||
await convertFn("file.obj", "obj", "stl", "out.stl", undefined, mockExecFile);
|
||||
|
||||
console.error = originalConsoleError;
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(loggedError).toBe("stderr: Fake stderr");
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
}
|
||||
165
tests/converters/imagemagick.test.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import type { ExecFileException } from "node:child_process";
|
||||
import { beforeEach, expect, test } from "bun:test";
|
||||
import { convert } from "../../src/converters/imagemagick";
|
||||
import { ExecFileFn } from "../../src/converters/types";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
let calls: string[][] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
calls = [];
|
||||
});
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test("convert respects ico conversion target type", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.obj", "eps", "ico", "output.ico", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(
|
||||
expect.arrayContaining([
|
||||
"-define",
|
||||
"icon:auto-resize=256,128,64,48,32,16",
|
||||
"-background",
|
||||
"none",
|
||||
"input.obj",
|
||||
"output.ico",
|
||||
]),
|
||||
);
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert respects ico conversion target type with svg as input filetype", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.svg", "svg", "ico", "output.ico", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(
|
||||
expect.arrayContaining([
|
||||
"-define",
|
||||
"icon:auto-resize=256,128,64,48,32,16",
|
||||
"-background",
|
||||
"none",
|
||||
"-density",
|
||||
"512",
|
||||
"input.svg",
|
||||
"output.ico",
|
||||
]),
|
||||
);
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert respects ico conversion target type with emf as input filetype", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.emf", "emf", "ico", "output.ico", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(
|
||||
expect.arrayContaining([
|
||||
"-define",
|
||||
"icon:auto-resize=256,128,64,48,32,16",
|
||||
"-background",
|
||||
"none",
|
||||
"emf:delegate=false",
|
||||
"-density",
|
||||
"300",
|
||||
"white",
|
||||
"-alpha",
|
||||
"remove",
|
||||
"input.emf",
|
||||
"output.ico",
|
||||
]),
|
||||
);
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert respects emf as input filetype", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.emf", "emf", "obj", "output.obj", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(
|
||||
expect.arrayContaining([
|
||||
"-define",
|
||||
"emf:delegate=false",
|
||||
"-density",
|
||||
"300",
|
||||
"-background",
|
||||
"white",
|
||||
"-alpha",
|
||||
"remove",
|
||||
"input.emf",
|
||||
"output.obj",
|
||||
]),
|
||||
);
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
7
tests/converters/inkscape.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/inkscape";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
7
tests/converters/libheif.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/libheif";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
91
tests/converters/libjxl.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { ExecFileException } from "node:child_process";
|
||||
import { beforeEach, expect, test } from "bun:test";
|
||||
import { convert } from "../../src/converters/libjxl";
|
||||
import { ExecFileFn } from "../../src/converters/types";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
let command: string = "";
|
||||
|
||||
beforeEach(() => {
|
||||
command = "";
|
||||
});
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test("convert uses djxl with input filetype being jxl", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
command = _cmd;
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.jxl", "jxl", "png", "output.png", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(command).toEqual("djxl");
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert uses cjxl with output filetype being jxl", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
command = _cmd;
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.png", "png", "jxl", "output.jxl", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(command).toEqual("cjxl");
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert uses empty string as command with neither input nor output filetype being jxl", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
command = _cmd;
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.png", "png", "jpg", "output.jpg", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(command).toEqual("");
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
61
tests/converters/msgconvert.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { ExecFileException } from "node:child_process";
|
||||
import { expect, test } from "bun:test";
|
||||
import { convert } from "../../src/converters/msgconvert";
|
||||
import { ExecFileFn } from "../../src/converters/types";
|
||||
|
||||
test("convert rejects conversion if input filetype is not msg and output type is not eml", async () => {
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const expectedError = new Error(
|
||||
"Unsupported conversion from obj to stl. Only MSG to EML conversion is currently supported.",
|
||||
);
|
||||
|
||||
expect(convert("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile)).rejects.toEqual(
|
||||
expectedError,
|
||||
);
|
||||
});
|
||||
|
||||
test("convert rejects conversion on error", async () => {
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(new Error("Test error"), "", "");
|
||||
};
|
||||
|
||||
const expectedError = new Error("msgconvert failed: Test error");
|
||||
|
||||
expect(convert("input.msg", "msg", "eml", "output.eml", undefined, mockExecFile)).rejects.toEqual(
|
||||
expectedError,
|
||||
);
|
||||
});
|
||||
|
||||
test("convert logs stderr as warning", async () => {
|
||||
const originalConsoleWarn = console.warn;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.warn = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: Error | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
callback(null, "", "Fake stderr");
|
||||
};
|
||||
|
||||
await convert("file.msg", "msg", "eml", "out.eml", undefined, mockExecFile);
|
||||
|
||||
console.error = originalConsoleWarn;
|
||||
|
||||
expect(loggedMessage).toBe("msgconvert stderr: Fake stderr");
|
||||
});
|
||||
7
tests/converters/potrace.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/potrace";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
7
tests/converters/resvg.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/resvg";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
65
tests/converters/vips.test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import type { ExecFileException } from "node:child_process";
|
||||
import { beforeEach, expect, test } from "bun:test";
|
||||
import { ExecFileFn } from "../../src/converters/types";
|
||||
import { convert } from "../../src/converters/vips";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
let calls: string[][] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
calls = [];
|
||||
});
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test("convert uses action pdfload with filetype being pdf", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.pdf", "pdf", "obj", "output.obj", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["pdfload"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
|
||||
test("convert uses action copy with filetype being anything but pdf", async () => {
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
let loggedMessage = "";
|
||||
console.log = (msg) => {
|
||||
loggedMessage = msg;
|
||||
};
|
||||
|
||||
const mockExecFile: ExecFileFn = (
|
||||
_cmd: string,
|
||||
_args: string[],
|
||||
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
|
||||
) => {
|
||||
calls.push(_args);
|
||||
callback(null, "Fake stdout", "");
|
||||
};
|
||||
|
||||
const result = await convert("input.jpg", "jpg", "obj", "output.obj", undefined, mockExecFile);
|
||||
|
||||
console.log = originalConsoleLog;
|
||||
|
||||
expect(result).toBe("Done");
|
||||
expect(calls[0]).toEqual(expect.arrayContaining(["copy"]));
|
||||
expect(loggedMessage).toBe("stdout: Fake stdout");
|
||||
});
|
||||
7
tests/converters/xelatex.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { test } from "bun:test";
|
||||
import { convert } from "../../src/converters/xelatex";
|
||||
import { runCommonTests } from "./helpers/commonTests";
|
||||
|
||||
runCommonTests(convert);
|
||||
|
||||
test.skip("dummy - required to trigger test detection", () => {});
|
||||
@@ -5,8 +5,9 @@
|
||||
"target": "ES2021",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
// "allowImportingTsExtensions": true,
|
||||
"outDir": "dist",
|
||||
"noEmit": false,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
@@ -24,7 +25,10 @@
|
||||
// "noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true
|
||||
"noImplicitOverride": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true
|
||||
// "noImplicitReturns": true
|
||||
}
|
||||
},
|
||||
"include": ["src", "tests", "package.json", "reset.d.ts"]
|
||||
}
|
||||
|
||||