Compare commits

..
7 Commits
24 changed files with 388 additions and 639 deletions
+2 -2
View File
@@ -4,5 +4,5 @@ TRAEFIK_NETWORK=internal
TRAEFIK_CERT_RESOLVER=netcup TRAEFIK_CERT_RESOLVER=netcup
# Keep these two values paired when deploying a newer published toolbox release. # Keep these two values paired when deploying a newer published toolbox release.
TOOLBOX_RELEASE_VERSION=0.6.0 TOOLBOX_RELEASE_VERSION=0.10.0
TOOLBOX_RELEASE_SHA256=aa01a0dfd6681c6d94f00c5a83ddf9898f7393e3dc476d491680884d806fbaa0 TOOLBOX_RELEASE_SHA256=de6f1afc3f0ec08ee1a8afd80d874832cbf905d0d50f62060e6985be9f5e0019
+1 -1
View File
@@ -1 +1 @@
@add-ideas:registry=https://git.add-ideas.de/api/packages/zemion/npm/ @add-ideas:registry=https://git.add-ideas.de/api/packages/lotobo/npm/
+4 -3
View File
@@ -2,9 +2,9 @@ ARG NGINX_IMAGE=nginxinc/nginx-unprivileged:1.31.3-alpine@sha256:18d67281256ded3
FROM ${NGINX_IMAGE} AS release FROM ${NGINX_IMAGE} AS release
ARG TOOLBOX_RELEASE_VERSION=0.6.0 ARG TOOLBOX_RELEASE_VERSION=0.10.0
ARG TOOLBOX_RELEASE_SHA256=aa01a0dfd6681c6d94f00c5a83ddf9898f7393e3dc476d491680884d806fbaa0 ARG TOOLBOX_RELEASE_SHA256=de6f1afc3f0ec08ee1a8afd80d874832cbf905d0d50f62060e6985be9f5e0019
ARG TOOLBOX_RELEASE_BASE_URL=https://git.add-ideas.de/zemion/toolbox-portal/releases/download ARG TOOLBOX_RELEASE_BASE_URL=https://git.add-ideas.de/lotobo/toolbox-portal/releases/download
RUN set -eu; \ RUN set -eu; \
archive="add-ideas-toolbox-${TOOLBOX_RELEASE_VERSION}.zip"; \ archive="add-ideas-toolbox-${TOOLBOX_RELEASE_VERSION}.zip"; \
@@ -23,6 +23,7 @@ RUN set -eu; \
test -f /tmp/toolbox/apps/onenote/toolbox-app.json; \ test -f /tmp/toolbox/apps/onenote/toolbox-app.json; \
test -f /tmp/toolbox/apps/av/toolbox-app.json; \ test -f /tmp/toolbox/apps/av/toolbox-app.json; \
test -f /tmp/toolbox/apps/regex/toolbox-app.json; \ test -f /tmp/toolbox/apps/regex/toolbox-app.json; \
test -f /tmp/toolbox/apps/svg/toolbox-app.json; \
rm "/tmp/${archive}" rm "/tmp/${archive}"
FROM ${NGINX_IMAGE} FROM ${NGINX_IMAGE}
+41 -38
View File
@@ -1,7 +1,7 @@
# add·ideas Toolbox Portal # add·ideas Toolbox Portal
`toolbox-portal` is the static launcher and release assembler for the add·ideas `toolbox-portal` is the static launcher and release assembler for the add·ideas
browser toolbox. Version `0.2.5` reads one same-origin catalogue, shows each app browser toolbox. Version `0.2.12` reads one same-origin catalogue, shows each app
as a compact launch tile, and keeps personal pins, drag-and-drop ordering, as a compact launch tile, and keeps personal pins, drag-and-drop ordering,
visibility, and appearance in the current browser. Pinned tools have their own visibility, and appearance in the current browser. Pinned tools have their own
section; search, category, and clickable tag filters stay close to the tool section; search, category, and clickable tag filters stay close to the tool
@@ -30,14 +30,14 @@ npm run build
npm run dev npm run dev
``` ```
The package lock resolves the `^0.2.2` SDK ranges to the published `0.2.2` The package lock resolves the `^0.2.3` SDK ranges to the published `0.2.3`
packages. A sibling SDK checkout is only needed when intentionally developing packages. A sibling SDK checkout is only needed when intentionally developing
the SDK and portal together. the SDK and portal together.
Vite uses `base: './'`, so the built portal works at `/` or a nested static path. Vite uses `base: './'`, so the built portal works at `/` or a nested static path.
The development catalogue at `public/toolbox.catalog.json` points to assembled The development catalogue at `public/toolbox.catalog.json` points to assembled
paths (`./apps/pdf/`, `./apps/xslt/`, `./apps/onenote/`, `./apps/av/`, and paths (`./apps/pdf/`, `./apps/xslt/`, `./apps/onenote/`, `./apps/av/`,
`./apps/regex/`). `./apps/regex/`, and `./apps/svg/`).
Those manifests will correctly appear as unavailable until an assembled release Those manifests will correctly appear as unavailable until an assembled release
is being served. is being served.
@@ -69,7 +69,7 @@ privacy and executable-code warning. Light, dark, and system modes are supported
## Production deployment behind Traefik ## Production deployment behind Traefik
The committed `compose.yaml` is the shortest production path. Its release The committed `compose.yaml` is the shortest production path. Its release
container downloads the immutable Toolbox 0.6.0 ZIP during the image build, container downloads the immutable Toolbox 0.10.0 ZIP during the image build,
verifies SHA-256 before extracting it, and then copies only the verified static verifies SHA-256 before extracting it, and then copies only the verified static
files into the pinned unprivileged nginx image. Node.js and a local portal files into the pinned unprivileged nginx image. Node.js and a local portal
assembly are not required on the deployment host. assembly are not required on the deployment host.
@@ -78,7 +78,7 @@ Prerequisites:
- Docker with the Compose plugin; - Docker with the Compose plugin;
- Traefik attached to the existing external Docker network `internal`; - Traefik attached to the existing external Docker network `internal`;
- a Traefik HTTPS entrypoint named `websecure`; - Traefik HTTP and HTTPS entrypoints named `web` and `websecure`;
- a certificate resolver named `netcup`; - a certificate resolver named `netcup`;
- DNS for `toolbox.add-ideas.de` pointing to that Traefik instance; and - DNS for `toolbox.add-ideas.de` pointing to that Traefik instance; and
- outbound HTTPS access to `git.add-ideas.de` while the image is built. - outbound HTTPS access to `git.add-ideas.de` while the image is built.
@@ -86,14 +86,15 @@ Prerequisites:
Deploy a fresh clone with: Deploy a fresh clone with:
```sh ```sh
git clone https://git.add-ideas.de/zemion/toolbox-portal.git git clone https://git.add-ideas.de/lotobo/toolbox-portal.git
cd toolbox-portal cd toolbox-portal
docker compose up -d --build docker compose up -d --build
docker compose ps docker compose ps
``` ```
No host port is published. Traefik routes No host port is published. Traefik permanently redirects
`https://toolbox.add-ideas.de` to nginx on the shared network at port 8080 and `http://toolbox.add-ideas.de` to HTTPS, routes
`https://toolbox.add-ideas.de` to nginx on the shared network at port 8080, and
obtains its TLS certificate through `netcup`. The hostname, network, resolver, obtains its TLS certificate through `netcup`. The hostname, network, resolver,
release version, and matching checksum can be overridden through environment release version, and matching checksum can be overridden through environment
variables; copy `.env.example` to `.env` only when an override is needed. For variables; copy `.env.example` to `.env` only when an override is needed. For
@@ -120,11 +121,9 @@ The app release should also publish a matching `.sha256` sidecar. The manifest
must declare the pinned reverse-DNS id and version. Application and portal must declare the pinned reverse-DNS id and version. Application and portal
versions are independent. versions are independent.
`release/toolbox.lock.json` is the reviewed v0.6.0 lock. Its URLs and checksums `release/toolbox.lock.json` is the reviewed v0.10.0 lock. Its URLs and checksums
pin the published PDF Tools 0.4.3, XSLT Tools 0.4.2, OneNote Tools 0.3.3, and pin the published PDF Tools 0.4.4, XSLT Tools 0.4.3, OneNote Tools 0.3.4,
Audio & Video Tools 0.1.0 and Regex Tools 0.1.0 release bytes. Regex Tools Audio & Video Tools 0.3.0, Regex Tools 0.4.2, and SVG Tools 0.1.0 release bytes. Use
v0.1.0 is the open-source, ECMAScript-only first flavour; later regex flavours
remain separate roadmap work. Use
`release/toolbox.lock.example.json` as the template for a future release and `release/toolbox.lock.example.json` as the template for a future release and
verify every downloaded asset before committing updated values. Artifacts may be: verify every downloaded asset before committing updated values. Artifacts may be:
@@ -145,7 +144,7 @@ npm run assemble -- \
--lock release/toolbox.lock.json \ --lock release/toolbox.lock.json \
--portal-dist dist \ --portal-dist dist \
--output build/toolbox \ --output build/toolbox \
--archive build/add-ideas-toolbox-0.6.0.zip --archive build/add-ideas-toolbox-0.10.0.zip
``` ```
Existing output is refused. Pass `--force` only when replacing those exact Existing output is refused. Pass `--force` only when replacing those exact
@@ -165,10 +164,11 @@ build/toolbox/
├── xslt/ ├── xslt/
├── onenote/ ├── onenote/
├── av/ ├── av/
── regex/ ── regex/
└── svg/
build/add-ideas-toolbox-0.6.0.zip build/add-ideas-toolbox-0.10.0.zip
build/add-ideas-toolbox-0.6.0.zip.sha256 build/add-ideas-toolbox-0.10.0.zip.sha256
``` ```
The assembler verifies SHA-256 before opening an artifact, validates every app The assembler verifies SHA-256 before opening an artifact, validates every app
@@ -189,7 +189,7 @@ directory separately:
```sh ```sh
npm run package:static -- \ npm run package:static -- \
--input build/toolbox \ --input build/toolbox \
--output artifacts/add-ideas-toolbox-0.6.0.zip --output artifacts/add-ideas-toolbox-0.10.0.zip
``` ```
This also emits a `.sha256` sidecar. This also emits a `.sha256` sidecar.
@@ -197,9 +197,12 @@ This also emits a `.sha256` sidecar.
## Local assembled container and publication ## Local assembled container and publication
`Containerfile` serves only the assembled files with unprivileged nginx on port 8080. It adds restrictive browser headers, same-origin isolation, explicit `Containerfile` serves only the assembled files with unprivileged nginx on port 8080. It adds restrictive browser headers, same-origin isolation, explicit
`application/wasm`, immutable caching only for Vite-hashed assets and narrowly `application/javascript` for `.mjs` engine modules and `application/wasm`,
matched semver-versioned FFmpeg core files, and revalidation for all other immutable caching only for Vite-hashed assets and narrowly matched
files. Assemble first, then: semver-versioned FFmpeg core files, and revalidation for all other files. The
opaque-origin SVG preview iframe loads its packaged controller with a URI-exact
CORS and cross-origin resource-policy exception; all other files retain the
same-origin policy and every normal security header. Assemble first, then:
The packaged policy intentionally does not grant CSP `unsafe-eval`. SaxonJS's The packaged policy intentionally does not grant CSP `unsafe-eval`. SaxonJS's
`ixsl:eval()` extension is therefore unsupported in this deployment profile; `ixsl:eval()` extension is therefore unsupported in this deployment profile;
@@ -207,9 +210,9 @@ normal local XML/XSLT transformations do not require that permission.
```sh ```sh
podman build -f Containerfile \ podman build -f Containerfile \
-t git.add-ideas.de/zemion/toolbox:0.6.0 . -t git.add-ideas.de/lotobo/toolbox:0.10.0 .
podman run --rm -p 8080:8080 \ podman run --rm -p 8080:8080 \
git.add-ideas.de/zemion/toolbox:0.6.0 git.add-ideas.de/lotobo/toolbox:0.10.0
``` ```
For a direct-port local deployment after assembly, use the separate example: For a direct-port local deployment after assembly, use the separate example:
@@ -226,8 +229,8 @@ docker login git.add-ideas.de
docker buildx build \ docker buildx build \
--platform linux/amd64,linux/arm64 \ --platform linux/amd64,linux/arm64 \
--file Containerfile.release \ --file Containerfile.release \
--tag git.add-ideas.de/zemion/toolbox:0.6.0 \ --tag git.add-ideas.de/lotobo/toolbox:0.10.0 \
--tag git.add-ideas.de/zemion/toolbox:0.6 \ --tag git.add-ideas.de/lotobo/toolbox:0.10 \
--push . --push .
``` ```
@@ -240,7 +243,7 @@ notes.
No credentials belong in this repository. The publishing workstation or runner No credentials belong in this repository. The publishing workstation or runner
needs permission to push code, releases, and container packages to needs permission to push code, releases, and container packages to
`zemion/toolbox-portal`. `lotobo/toolbox-portal`.
1. Run `npm ci`, `npm test`, `npm run lint`, and `npm run build` from a clean 1. Run `npm ci`, `npm test`, `npm run lint`, and `npm run build` from a clean
checkout of the intended portal tag. checkout of the intended portal tag.
@@ -249,11 +252,11 @@ needs permission to push code, releases, and container packages to
3. Verify downloaded app assets with `sha256sum -c <asset>.sha256`; copy those 3. Verify downloaded app assets with `sha256sum -c <asset>.sha256`; copy those
exact values into a reviewed toolbox lock. exact values into a reviewed toolbox lock.
4. Run the assembler and serve `build/toolbox` from a nested test path. Open 4. Run the assembler and serve `build/toolbox` from a nested test path. Open
all five apps, switch between them, and confirm the encoded `toolbox` all six apps, switch between them, and confirm the encoded `toolbox`
context. context.
5. Verify the distribution with 5. Verify the distribution with
`(cd build && sha256sum -c add-ideas-toolbox-0.6.0.zip.sha256)`. `(cd build && sha256sum -c add-ideas-toolbox-0.10.0.zip.sha256)`.
6. Commit the reviewed lock, tag the toolbox release (for example `v0.6.0`), and 6. Commit the reviewed lock, tag the toolbox release (for example `v0.10.0`), and
push the branch and tag to Gitea. push the branch and tag to Gitea.
7. In Gitea, open **Releases → New release**, select the tag, and upload the 7. In Gitea, open **Releases → New release**, select the tag, and upload the
static ZIP plus its `.sha256` file. Do not use a mutable “latest” URL in a static ZIP plus its `.sha256` file. Do not use a mutable “latest” URL in a
@@ -268,13 +271,14 @@ must not re-resolve app versions or substitute a newer release.
## Existing tools ## Existing tools
| Tool | State and boundary | Principal workflows | Important concrete scope | Critical considerations and integrations | | Tool | State and boundary | Principal workflows | Important concrete scope | Critical considerations and integrations |
| ------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`pdf-tools`** | Existing; dedicated repository | Merge, split, reorder, rotate and export PDF pages | Thumbnail workspace; multi-document operations; ZIP and PDF output; saved local workspace | Workers and IndexedDB; large-document memory control; future signature inspection through `crypto-tools`; metadata and safe-sharing through `privacy-tools` | | **`pdf-tools`** | Existing; dedicated repository | Merge, split, reorder, rotate and export PDF pages | Thumbnail workspace; multi-document operations; ZIP and PDF output; saved local workspace | Workers and IndexedDB; large-document memory control; future signature inspection through `crypto-tools`; metadata and safe-sharing through `privacy-tools` |
| **`xslt-tools`** | Existing; dedicated repository | Develop, test and run XSLT transformations | XML/XSLT editors; transformation results; local files; saved projects; validation and diagnostics | Lazy SaxonJS loading; relocatable assets; useful handoffs to `data-tools`, `schema-tools` and `diff-tools` | | **`xslt-tools`** | Existing; dedicated repository | Develop, test and run XSLT transformations | XML/XSLT editors; transformation results; local files; saved projects; validation and diagnostics | Lazy SaxonJS loading; relocatable assets; useful handoffs to `data-tools`, `schema-tools` and `diff-tools` |
| **`onenote-tools`** | Existing rich-reader release; dedicated repository | Open, browse, inspect and export OneNote sections and packages locally | Desktop/unfragmented FSSHTTPB `.one`/`.onetoc2`; none/LZX/MSZIP/Quantum and multi-cabinet `.onepkg`; trees, rich text, images, tables, ink, attachments and export | Native TypeScript worker with no Wasm; bounded parsing, rendering and export; fragmented FSSHTTP fails safely; no editing or pixel-perfect fidelity | | **`onenote-tools`** | Existing rich-reader release; dedicated repository | Open, browse, inspect and export OneNote sections and packages locally | Desktop/unfragmented FSSHTTPB `.one`/`.onetoc2`; none/LZX/MSZIP/Quantum and multi-cabinet `.onepkg`; trees, rich text, images, tables, ink, attachments and export | Native TypeScript worker with no Wasm; bounded parsing, rendering and export; fragmented FSSHTTP fails safely; no editing or pixel-perfect fidelity |
| **`av-tools`** | Existing local-first v0.1.0 release; dedicated repository | Convert and lightly edit audio and video locally in the browser | Quick conversion; trim, split and concatenate; crop, resize, normalize, fades, waveform, metadata, chapters, subtitles, thumbnails, contact sheets, presets and export | Self-hosted ffmpeg.wasm 0.12.10 ST/MT cores; bounded queue, temporary storage and resource policy; isolation enables MT with automatic ST fallback; semver-versioned core assets are immutable | | **`av-tools`** | Existing local-first v0.3.0 release; dedicated repository | Convert and lightly edit audio and video locally in the browser | Immediate native playback and basic file information; progressive stream inspection on demand; quick conversion; trim, split and crossfade concatenate; crop, resize, normalize, fades, waveform, metadata, chapters, subtitles, scene thumbnails/contact sheets, presets and export | Reviewed ffmpeg.wasm 0.12.10 ST/MT build with verified Opus and bundled label font; bounded queue, temporary storage and resource policy; isolation enables MT with automatic ST fallback; versioned core assets are immutable |
| **`regex-tools`** | Existing local-first v0.1.0 release; dedicated repository | Develop, explain, test and apply ECMAScript regular expressions | Deterministic syntax tree; native matching and exact captures; replacement and list previews; unit-test suites; project import, export and optional local persistence | Open-source regexpp parser and native browser engine in killable workers; explicit resource/presentation limits; ECMAScript-only first flavour; later engines remain roadmap work | | **`regex-tools`** | Current local-first v0.4.2 release; dedicated repository | Develop, explain, test and apply JavaScript, PCRE2, PHP, Perl, Python, Ruby, Java, C++, Go, .NET, Rust and Scala/JVM-compatible regular expressions | Deterministic syntax trees; engine-native matching, captures and bounded replacement; stable double-buffered live results; PCRE2 tracing and C17 generation; comparison; corpus apply; tests; risk/growth/benchmark analysis; generated cases; bounded minimization; validated ECMAScript formatting; project import/export and optional local persistence | Open-source regexpp and pinned local WebAssembly runtimes for PCRE2, PHP preg, legacy Perl, CPython, CRuby, TeaVM Java/Scala compatibility, libc++ C++, Go, .NET and Rust in killable workers; explicit source/licence inventories, resource limits and engine-specific offset semantics |
| **`svg-tools`** | Initial local-first v0.1.0 release; dedicated repository | Inspect and edit SVG source, structure, geometry, references and accessibility locally | Synchronized source/tree/canvas/inspector; path and transform tools; reference analysis; optimization; CSS animation preview; exact, sanitized, raster and project export | Untrusted SVG is sanitized into an opaque-origin sandbox; bounded parsing and decompression, explicit security findings and a URI-scoped controller header exception; later milestone slices remain intentionally tracked in the app repository |
## Planned tools ## Planned tools
@@ -282,7 +286,6 @@ must not re-resolve app versions or substitute a newer release.
| -------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`flow-tools`** | Later platform layer | Compose local processing operations across tools | Example: CSV → filter → QR generation → SVG template → PDF → ZIP; reusable recipes; typed inputs and outputs; progress and cancellation | Do not load complete React applications into one runtime; expose separate worker-safe operation packages only after several apps have stable operations | | **`flow-tools`** | Later platform layer | Compose local processing operations across tools | Example: CSV → filter → QR generation → SVG template → PDF → ZIP; reusable recipes; typed inputs and outputs; progress and cancellation | Do not load complete React applications into one runtime; expose separate worker-safe operation packages only after several apps have stable operations |
| **`file-tools`** | High priority; dedicated repository | Identify, inspect and organize local files | Magic-byte detection; hex view; decoded strings; checksums; directory manifests; split/join; duplicate detection; trailing-data detection; batch-rename preview | All inputs are untrusted; format inspectors require strict size and nesting limits; natural entry point to archive, privacy, crypto and package tools | | **`file-tools`** | High priority; dedicated repository | Identify, inspect and organize local files | Magic-byte detection; hex view; decoded strings; checksums; directory manifests; split/join; duplicate detection; trailing-data detection; batch-rename preview | All inputs are untrusted; format inspectors require strict size and nesting limits; natural entry point to archive, privacy, crypto and package tools |
| **`svg-tools`** | Planned; dedicated repository | Source and visual SVG editing | Synchronized DOM tree, code and canvas; optimization; path simplification; symbols; accessibility; animation; sanitization | SVG is potentially active content; previews require sanitization or sandboxing; integrate with `colour-tools`, `token-tools`, `image-tools` and `label-tools` |
| **`image-tools`** | High priority; dedicated repository | Edit and batch-process raster images | Crop, resize, rotate, compress, convert, contact sheets, sprites, responsive sets, favicon generation, metadata removal and visual quality comparison | Use workers and off-main-thread rendering; preserve or deliberately remove colour profiles; integrate with colour, SVG, PDF and privacy tools | | **`image-tools`** | High priority; dedicated repository | Edit and batch-process raster images | Crop, resize, rotate, compress, convert, contact sheets, sprites, responsive sets, favicon generation, metadata removal and visual quality comparison | Use workers and off-main-thread rendering; preserve or deliberately remove colour profiles; integrate with colour, SVG, PDF and privacy tools |
| **`subtitle-tools`** | Medium priority; dedicated repository | Edit, validate and synchronize captions | SRT, WebVTT and ASS; timing shifts; stretching; frame-rate conversion; overlap and reading-speed checks; waveform synchronization; revision comparison | Direct integration with `av-tools`; preserve style information when supported and disclose conversion losses | | **`subtitle-tools`** | Medium priority; dedicated repository | Edit, validate and synchronize captions | SRT, WebVTT and ASS; timing shifts; stretching; frame-rate conversion; overlap and reading-speed checks; waveform synchronization; revision comparison | Direct integration with `av-tools`; preserve style information when supported and disclose conversion losses |
| **`midi-tools`** | Medium specialist app | MIDI event inspection and editing; transpose, quantize, tempo maps and controller data; device access should remain optional | | | **`midi-tools`** | Medium specialist app | MIDI event inspection and editing; transpose, quantize, tempo maps and controller data; device access should remain optional | |
+1 -1
View File
@@ -3,7 +3,7 @@ services:
build: build:
context: . context: .
dockerfile: Containerfile dockerfile: Containerfile
image: git.add-ideas.de/zemion/toolbox:0.6.0 image: git.add-ideas.de/lotobo/toolbox:0.10.0
restart: unless-stopped restart: unless-stopped
read_only: true read_only: true
ports: ports:
+10 -3
View File
@@ -6,9 +6,9 @@ services:
context: . context: .
dockerfile: Containerfile.release dockerfile: Containerfile.release
args: args:
TOOLBOX_RELEASE_VERSION: "${TOOLBOX_RELEASE_VERSION:-0.6.0}" TOOLBOX_RELEASE_VERSION: "${TOOLBOX_RELEASE_VERSION:-0.10.0}"
TOOLBOX_RELEASE_SHA256: "${TOOLBOX_RELEASE_SHA256:-aa01a0dfd6681c6d94f00c5a83ddf9898f7393e3dc476d491680884d806fbaa0}" TOOLBOX_RELEASE_SHA256: "${TOOLBOX_RELEASE_SHA256:-de6f1afc3f0ec08ee1a8afd80d874832cbf905d0d50f62060e6985be9f5e0019}"
image: "git.add-ideas.de/zemion/toolbox:${TOOLBOX_RELEASE_VERSION:-0.6.0}" image: "git.add-ideas.de/lotobo/toolbox:${TOOLBOX_RELEASE_VERSION:-0.10.0}"
restart: unless-stopped restart: unless-stopped
read_only: true read_only: true
tmpfs: tmpfs:
@@ -30,6 +30,13 @@ services:
traefik.http.routers.addideas-toolbox.tls: "true" traefik.http.routers.addideas-toolbox.tls: "true"
traefik.http.routers.addideas-toolbox.tls.certresolver: "${TRAEFIK_CERT_RESOLVER:-netcup}" traefik.http.routers.addideas-toolbox.tls.certresolver: "${TRAEFIK_CERT_RESOLVER:-netcup}"
traefik.http.routers.addideas-toolbox.service: addideas-toolbox traefik.http.routers.addideas-toolbox.service: addideas-toolbox
traefik.http.routers.addideas-toolbox-http.rule: "Host(`${TOOLBOX_HOST:-toolbox.add-ideas.de}`)"
traefik.http.routers.addideas-toolbox-http.priority: 900
traefik.http.routers.addideas-toolbox-http.entrypoints: web
traefik.http.routers.addideas-toolbox-http.middlewares: addideas-toolbox-https-redirect
traefik.http.routers.addideas-toolbox-http.service: addideas-toolbox
traefik.http.middlewares.addideas-toolbox-https-redirect.redirectscheme.scheme: https
traefik.http.middlewares.addideas-toolbox-https-redirect.redirectscheme.permanent: "true"
traefik.http.services.addideas-toolbox.loadbalancer.server.port: "8080" traefik.http.services.addideas-toolbox.loadbalancer.server.port: "8080"
networks: networks:
+20 -2
View File
@@ -1,7 +1,17 @@
map $uri $toolbox_cache_control { map $uri $toolbox_cache_control {
default "no-cache"; default "no-cache";
"~^/(?:.*/)?assets/.*-[A-Za-z0-9_-]{8,}\.[^/]+$" "public, max-age=31536000, immutable"; "~^/(?:.*/)?assets/.*-[A-Za-z0-9_-]{8,}\.[^/]+$" "public, max-age=31536000, immutable";
"~^/apps/[a-z0-9][a-z0-9-]*/vendor/ffmpeg/(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)/(?:version\.json|(?:st|mt)/ffmpeg-core\.(?:js|wasm)|mt/ffmpeg-core\.worker\.js)$" "public, max-age=31536000, immutable"; "~^/apps/[a-z0-9][a-z0-9-]*/vendor/ffmpeg/(?:(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)|0\.12\.10-reviewed-stack5m\.1)/(?:version\.json|(?:st|mt)/ffmpeg-core\.(?:js|wasm)|mt/ffmpeg-core\.worker\.js)$" "public, max-age=31536000, immutable";
}
map $uri $toolbox_resource_policy {
default "same-origin";
"/apps/svg/canvas-frame-controller.js" "cross-origin";
}
map $uri $toolbox_access_control_allow_origin {
default "";
"/apps/svg/canvas-frame-controller.js" "*";
} }
server { server {
@@ -19,7 +29,8 @@ server {
add_header X-Frame-Options "DENY" always; add_header X-Frame-Options "DENY" always;
add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always; add_header Cross-Origin-Resource-Policy $toolbox_resource_policy always;
add_header Access-Control-Allow-Origin $toolbox_access_control_allow_origin always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; connect-src 'self'; worker-src 'self' blob:; manifest-src 'self'" always; add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; connect-src 'self'; worker-src 'self' blob:; manifest-src 'self'" always;
add_header Cache-Control $toolbox_cache_control always; add_header Cache-Control $toolbox_cache_control always;
@@ -40,6 +51,13 @@ server {
try_files $uri =404; try_files $uri =404;
} }
location ~* \.mjs$ {
types {
application/javascript mjs;
}
try_files $uri =404;
}
location ~* \.(?:css|js|mjs|woff2?|png|jpe?g|gif|webp|svg|ico|webmanifest)$ { location ~* \.(?:css|js|mjs|woff2?|png|jpe?g|gif|webp|svg|ico|webmanifest)$ {
try_files $uri =404; try_files $uri =404;
} }
+54 -521
View File
@@ -1,16 +1,16 @@
{ {
"name": "@add-ideas/toolbox-portal", "name": "@add-ideas/toolbox-portal",
"version": "0.2.5", "version": "0.2.12",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@add-ideas/toolbox-portal", "name": "@add-ideas/toolbox-portal",
"version": "0.2.5", "version": "0.2.12",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@add-ideas/toolbox-contract": "^0.2.2", "@add-ideas/toolbox-contract": "^0.2.3",
"@add-ideas/toolbox-shell-react": "^0.2.2", "@add-ideas/toolbox-shell-react": "^0.2.3",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@@ -26,7 +26,7 @@
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2", "@vitejs/plugin-react": "^6.0.2",
"archiver": "^7.0.1", "archiver": "^8.0.0",
"eslint": "^10.4.0", "eslint": "^10.4.0",
"eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2", "eslint-plugin-react-refresh": "^0.5.2",
@@ -44,18 +44,18 @@
} }
}, },
"node_modules/@add-ideas/toolbox-contract": { "node_modules/@add-ideas/toolbox-contract": {
"version": "0.2.2", "version": "0.2.3",
"resolved": "https://git.add-ideas.de/api/packages/zemion/npm/%40add-ideas%2Ftoolbox-contract/-/0.2.2/toolbox-contract-0.2.2.tgz", "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.2.3/toolbox-contract-0.2.3.tgz",
"integrity": "sha512-VcZ8j4O2PgFaIYgxs6r9u0uPxA+hHKwhCW+omfeVCtbjAAYhH2KhRlBGm0ERVVYcK5kchyzZLXrC4LKWRotVzg==", "integrity": "sha512-T0PVSuMT40GjTDfQJhEEY3ZawQq8zz1/ry95JdKI6W39CdLacaRXdGnEpDCMHt+jUbf1Jz7Nat/M5dFCgKVM9A==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@add-ideas/toolbox-shell-react": { "node_modules/@add-ideas/toolbox-shell-react": {
"version": "0.2.2", "version": "0.2.3",
"resolved": "https://git.add-ideas.de/api/packages/zemion/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.2.2/toolbox-shell-react-0.2.2.tgz", "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.2.3/toolbox-shell-react-0.2.3.tgz",
"integrity": "sha512-w/xbLCd50a2Jq7vQ9Z9ygUOuqlOCRkIYlk4vSJx0mf4REJNeMYi3PE2MbaLUC4DkQWyrmatsa5HNT89o0l91BA==", "integrity": "sha512-DT5lQDH48BFkFcmFLZnQh7+Cm73JzBPcmp5WzUXypfkUXpEyDYHzaXgmW4kZ0edSwh4RK4sPmx+JPtK0X4aKCQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@add-ideas/toolbox-contract": "0.2.2" "@add-ideas/toolbox-contract": "0.2.3"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=18 <20", "react": ">=18 <20",
@@ -822,24 +822,6 @@
"url": "https://github.com/sponsors/nzakas" "url": "https://github.com/sponsors/nzakas"
} }
}, },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
"strip-ansi": "^7.0.1",
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
"wrap-ansi": "^8.1.0",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -919,17 +901,6 @@
"url": "https://github.com/sponsors/Boshen" "url": "https://github.com/sponsors/Boshen"
} }
}, },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=14"
}
},
"node_modules/@rolldown/binding-android-arm64": { "node_modules/@rolldown/binding-android-arm64": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
@@ -1838,6 +1809,7 @@
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -1857,41 +1829,24 @@
} }
}, },
"node_modules/archiver": { "node_modules/archiver": {
"version": "7.0.1", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", "resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz",
"integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"archiver-utils": "^5.0.2",
"async": "^3.2.4", "async": "^3.2.4",
"buffer-crc32": "^1.0.0", "buffer-crc32": "^1.0.0",
"readable-stream": "^4.0.0", "is-stream": "^4.0.0",
"readdir-glob": "^1.1.2",
"tar-stream": "^3.0.0",
"zip-stream": "^6.0.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/archiver-utils": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
"integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
"dev": true,
"license": "MIT",
"dependencies": {
"glob": "^10.0.0",
"graceful-fs": "^4.2.0",
"is-stream": "^2.0.1",
"lazystream": "^1.0.0", "lazystream": "^1.0.0",
"lodash": "^4.17.15",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"readable-stream": "^4.0.0" "readable-stream": "^4.0.0",
"readdir-glob": "^3.0.0",
"tar-stream": "^3.0.0",
"zip-stream": "^7.0.2"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">=18"
} }
}, },
"node_modules/aria-query": { "node_modules/aria-query": {
@@ -2076,16 +2031,16 @@
} }
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "5.0.7", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^4.0.2" "balanced-match": "^4.0.2"
}, },
"engines": { "engines": {
"node": "18 || 20 || >=22" "node": "20 || >=22"
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
@@ -2188,41 +2143,21 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/compress-commons": { "node_modules/compress-commons": {
"version": "6.0.2", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz",
"integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"crc-32": "^1.2.0", "crc-32": "^1.2.0",
"crc32-stream": "^6.0.0", "crc32-stream": "^7.0.1",
"is-stream": "^2.0.1", "is-stream": "^4.0.0",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"readable-stream": "^4.0.0" "readable-stream": "^4.0.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">=18"
} }
}, },
"node_modules/convert-source-map": { "node_modules/convert-source-map": {
@@ -2253,9 +2188,9 @@
} }
}, },
"node_modules/crc32-stream": { "node_modules/crc32-stream": {
"version": "6.0.0", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz",
"integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2263,7 +2198,7 @@
"readable-stream": "^4.0.0" "readable-stream": "^4.0.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">=18"
} }
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
@@ -2383,13 +2318,6 @@
"license": "MIT", "license": "MIT",
"peer": true "peer": true
}, },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true,
"license": "MIT"
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.393", "version": "1.5.393",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz",
@@ -2397,13 +2325,6 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"license": "MIT"
},
"node_modules/entities": { "node_modules/entities": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
@@ -2779,23 +2700,6 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"dev": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.6",
"signal-exit": "^4.0.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2821,28 +2725,6 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/glob": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": { "node_modules/glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2856,39 +2738,6 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/glob/node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/globals": { "node_modules/globals": {
"version": "17.7.0", "version": "17.7.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
@@ -2902,13 +2751,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/hermes-estree": { "node_modules/hermes-estree": {
"version": "0.25.1", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
@@ -3007,16 +2849,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": { "node_modules/is-glob": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -3038,13 +2870,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/is-stream": { "node_modules/is-stream": {
"version": "2.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=18"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@@ -3064,22 +2896,6 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3544,13 +3360,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -3615,16 +3424,6 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/minipass": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3742,13 +3541,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/parse5": { "node_modules/parse5": {
"version": "8.0.1", "version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
@@ -3782,30 +3574,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/pathe": { "node_modules/pathe": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -3985,43 +3753,19 @@
} }
}, },
"node_modules/readdir-glob": { "node_modules/readdir-glob": {
"version": "1.1.3", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz",
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"minimatch": "^5.1.0" "minimatch": "^10.2.2"
}
},
"node_modules/readdir-glob/node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/readdir-glob/node_modules/brace-expansion": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/readdir-glob/node_modules/minimatch": {
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/yqnn"
} }
}, },
"node_modules/redent": { "node_modules/redent": {
@@ -4162,19 +3906,6 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -4221,103 +3952,6 @@
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
} }
}, },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string-width-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/string-width-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.2.2"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi/node_modules/ansi-regex": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/strip-indent": { "node_modules/strip-indent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@@ -4855,107 +4489,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/xml-name-validator": { "node_modules/xml-name-validator": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
@@ -5007,18 +4540,18 @@
} }
}, },
"node_modules/zip-stream": { "node_modules/zip-stream": {
"version": "6.0.1", "version": "7.0.5",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz",
"integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"archiver-utils": "^5.0.0", "compress-commons": "^7.0.0",
"compress-commons": "^6.0.2", "normalize-path": "^3.0.0",
"readable-stream": "^4.0.0" "readable-stream": "^4.0.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">=18"
} }
}, },
"node_modules/zod": { "node_modules/zod": {
+12 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@add-ideas/toolbox-portal", "name": "@add-ideas/toolbox-portal",
"version": "0.2.5", "version": "0.2.12",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"private": true, "private": true,
"type": "module", "type": "module",
@@ -21,8 +21,8 @@
"package:static": "node scripts/package-static.mjs" "package:static": "node scripts/package-static.mjs"
}, },
"dependencies": { "dependencies": {
"@add-ideas/toolbox-contract": "^0.2.2", "@add-ideas/toolbox-contract": "^0.2.3",
"@add-ideas/toolbox-shell-react": "^0.2.2", "@add-ideas/toolbox-shell-react": "^0.2.3",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@@ -38,7 +38,7 @@
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2", "@vitejs/plugin-react": "^6.0.2",
"archiver": "^7.0.1", "archiver": "^8.0.0",
"eslint": "^10.4.0", "eslint": "^10.4.0",
"eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2", "eslint-plugin-react-refresh": "^0.5.2",
@@ -53,5 +53,13 @@
}, },
"engines": { "engines": {
"node": ">=22" "node": ">=22"
},
"repository": {
"type": "git",
"url": "git+https://git.add-ideas.de/lotobo/toolbox-portal.git"
},
"homepage": "https://git.add-ideas.de/lotobo/toolbox-portal",
"bugs": {
"url": "https://git.add-ideas.de/lotobo/toolbox-portal/issues"
} }
} }
+2 -2
View File
@@ -1,8 +1,8 @@
# Corresponding source # Corresponding source
The source code corresponding to add·ideas Toolbox Portal 0.2.5 is available at: The source code corresponding to add·ideas Toolbox Portal 0.2.12 is available at:
https://git.add-ideas.de/zemion/toolbox-portal/src/tag/v0.6.0 https://git.add-ideas.de/lotobo/toolbox-portal/src/tag/v0.10.0
Each bundled application contains its own `SOURCE.md`, license, and third-party Each bundled application contains its own `SOURCE.md`, license, and third-party
license materials. The application sources are also linked from the portal. license materials. The application sources are also linked from the portal.
+5 -1
View File
@@ -1,5 +1,5 @@
{ {
"$schema": "https://git.add-ideas.de/zemion/toolbox-sdk/raw/branch/main/schemas/toolbox-catalog.v1.schema.json", "$schema": "https://git.add-ideas.de/lotobo/toolbox-sdk/raw/branch/main/schemas/toolbox-catalog.v1.schema.json",
"schemaVersion": 1, "schemaVersion": 1,
"id": "de.add-ideas.toolbox", "id": "de.add-ideas.toolbox",
"name": "add·ideas Toolbox", "name": "add·ideas Toolbox",
@@ -28,6 +28,10 @@
{ {
"manifest": "./apps/regex/toolbox-app.json", "manifest": "./apps/regex/toolbox-app.json",
"enabled": true "enabled": true
},
{
"manifest": "./apps/svg/toolbox-app.json",
"enabled": true
} }
] ]
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.add-ideas.de/zemion/toolbox-portal/raw/branch/main/release/toolbox-release-lock.schema.json", "$id": "https://git.add-ideas.de/lotobo/toolbox-portal/raw/branch/main/release/toolbox-release-lock.schema.json",
"title": "add·ideas Toolbox release lock v1", "title": "add·ideas Toolbox release lock v1",
"description": "Pins the exact portal and checksummed application archives used to assemble one static toolbox release.", "description": "Pins the exact portal and checksummed application archives used to assemble one static toolbox release.",
"type": "object", "type": "object",
+24 -17
View File
@@ -1,8 +1,8 @@
{ {
"$schema": "./toolbox-release-lock.schema.json", "$schema": "./toolbox-release-lock.schema.json",
"schemaVersion": 1, "schemaVersion": 1,
"releaseVersion": "0.6.0", "releaseVersion": "0.10.0",
"portalVersion": "0.2.5", "portalVersion": "0.2.12",
"catalogue": { "catalogue": {
"id": "de.add-ideas.toolbox", "id": "de.add-ideas.toolbox",
"name": "add·ideas Toolbox", "name": "add·ideas Toolbox",
@@ -15,38 +15,45 @@
"apps": [ "apps": [
{ {
"id": "de.add-ideas.pdf-tools", "id": "de.add-ideas.pdf-tools",
"version": "0.4.3", "version": "0.4.4",
"artifact": "https://git.add-ideas.de/zemion/pdf-tools/releases/download/v0.4.3/pdf-tools-0.4.3.zip", "artifact": "https://git.add-ideas.de/lotobo/pdf-tools/releases/download/v0.4.4/pdf-tools-0.4.4.zip",
"sha256": "f2dfd3bade49a4b8be9c0c54c6c2bf1bbb153fc0505a38af14de5ab52d5183cf", "sha256": "fa93c4c004be74d6a1f68f62a0f64f44c99218f387441ca3630a7f5af3c09d7d",
"target": "pdf" "target": "pdf"
}, },
{ {
"id": "de.add-ideas.xslt-tools", "id": "de.add-ideas.xslt-tools",
"version": "0.4.2", "version": "0.4.3",
"artifact": "https://git.add-ideas.de/zemion/xslt-tools/releases/download/v0.4.2/xslt-tools-0.4.2.zip", "artifact": "https://git.add-ideas.de/lotobo/xslt-tools/releases/download/v0.4.3/xslt-tools-0.4.3.zip",
"sha256": "daaa821c10c6c6285f97128ba3d923e48efa24449f1fb0a21bf478abaf27499f", "sha256": "e44012af214d24e3d97d10a6017fa3d9dbf063de36673ab04dd15a0a8643afcc",
"target": "xslt" "target": "xslt"
}, },
{ {
"id": "de.add-ideas.onenote-tools", "id": "de.add-ideas.onenote-tools",
"version": "0.3.3", "version": "0.3.4",
"artifact": "https://git.add-ideas.de/zemion/onenote-tools/releases/download/v0.3.3/onenote-tools-0.3.3.zip", "artifact": "https://git.add-ideas.de/lotobo/onenote-tools/releases/download/v0.3.4/onenote-tools-0.3.4.zip",
"sha256": "37c0ef7cdc7f435d0d4f5a341523f4e522118d5b258c374b5a7ef7b8e6c27cec", "sha256": "c0ed791d1ae64fdf6a90bceebf97a7a3252989f2434204d009f87f5a2d1cc341",
"target": "onenote" "target": "onenote"
}, },
{ {
"id": "de.add-ideas.av-tools", "id": "de.add-ideas.av-tools",
"version": "0.1.0", "version": "0.3.0",
"artifact": "https://git.add-ideas.de/zemion/av-tools/releases/download/v0.1.0/av-tools-0.1.0.zip", "artifact": "https://git.add-ideas.de/lotobo/av-tools/releases/download/v0.3.0/av-tools-0.3.0.zip",
"sha256": "a10889f80f8e30a71737125d8c05d3ff0264249726845700ab95bf37feb64595", "sha256": "3476c5d43ae43ce66b5aeebe8c11334f7cd4d153586348b72f10bfbdbada9be7",
"target": "av" "target": "av"
}, },
{ {
"id": "de.add-ideas.regex-tools", "id": "de.add-ideas.regex-tools",
"version": "0.1.0", "version": "0.4.2",
"artifact": "https://git.add-ideas.de/zemion/regex-tools/releases/download/v0.1.0/regex-tools-0.1.0.zip", "artifact": "https://git.add-ideas.de/lotobo/regex-tools/releases/download/v0.4.2/regex-tools-0.4.2.zip",
"sha256": "6d93fbc4baaad0decce939c020afc2edc011038d8f998b333373d55634e5487c", "sha256": "a3e7342d1f0746aa9c88e6568e7fbc04ab429c94604ba5fb1094ffdb096a54fe",
"target": "regex" "target": "regex"
},
{
"id": "de.add-ideas.svg-tools",
"version": "0.1.0",
"artifact": "https://git.add-ideas.de/lotobo/svg-tools/releases/download/v0.1.0/svg-tools-0.1.0.zip",
"sha256": "364f14c88c4934d9c847bf76de70b89dcf564a405afd58c6703363a95fbfb6ae",
"target": "svg"
} }
] ]
} }
+24 -17
View File
@@ -1,8 +1,8 @@
{ {
"$schema": "./toolbox-release-lock.schema.json", "$schema": "./toolbox-release-lock.schema.json",
"schemaVersion": 1, "schemaVersion": 1,
"releaseVersion": "0.6.0", "releaseVersion": "0.10.0",
"portalVersion": "0.2.5", "portalVersion": "0.2.12",
"catalogue": { "catalogue": {
"id": "de.add-ideas.toolbox", "id": "de.add-ideas.toolbox",
"name": "add·ideas Toolbox", "name": "add·ideas Toolbox",
@@ -15,38 +15,45 @@
"apps": [ "apps": [
{ {
"id": "de.add-ideas.pdf-tools", "id": "de.add-ideas.pdf-tools",
"version": "0.4.3", "version": "0.4.4",
"artifact": "https://git.add-ideas.de/zemion/pdf-tools/releases/download/v0.4.3/pdf-tools-0.4.3.zip", "artifact": "https://git.add-ideas.de/lotobo/pdf-tools/releases/download/v0.4.4/pdf-tools-0.4.4.zip",
"sha256": "f2dfd3bade49a4b8be9c0c54c6c2bf1bbb153fc0505a38af14de5ab52d5183cf", "sha256": "fa93c4c004be74d6a1f68f62a0f64f44c99218f387441ca3630a7f5af3c09d7d",
"target": "pdf" "target": "pdf"
}, },
{ {
"id": "de.add-ideas.xslt-tools", "id": "de.add-ideas.xslt-tools",
"version": "0.4.2", "version": "0.4.3",
"artifact": "https://git.add-ideas.de/zemion/xslt-tools/releases/download/v0.4.2/xslt-tools-0.4.2.zip", "artifact": "https://git.add-ideas.de/lotobo/xslt-tools/releases/download/v0.4.3/xslt-tools-0.4.3.zip",
"sha256": "daaa821c10c6c6285f97128ba3d923e48efa24449f1fb0a21bf478abaf27499f", "sha256": "e44012af214d24e3d97d10a6017fa3d9dbf063de36673ab04dd15a0a8643afcc",
"target": "xslt" "target": "xslt"
}, },
{ {
"id": "de.add-ideas.onenote-tools", "id": "de.add-ideas.onenote-tools",
"version": "0.3.3", "version": "0.3.4",
"artifact": "https://git.add-ideas.de/zemion/onenote-tools/releases/download/v0.3.3/onenote-tools-0.3.3.zip", "artifact": "https://git.add-ideas.de/lotobo/onenote-tools/releases/download/v0.3.4/onenote-tools-0.3.4.zip",
"sha256": "37c0ef7cdc7f435d0d4f5a341523f4e522118d5b258c374b5a7ef7b8e6c27cec", "sha256": "c0ed791d1ae64fdf6a90bceebf97a7a3252989f2434204d009f87f5a2d1cc341",
"target": "onenote" "target": "onenote"
}, },
{ {
"id": "de.add-ideas.av-tools", "id": "de.add-ideas.av-tools",
"version": "0.1.0", "version": "0.3.0",
"artifact": "https://git.add-ideas.de/zemion/av-tools/releases/download/v0.1.0/av-tools-0.1.0.zip", "artifact": "https://git.add-ideas.de/lotobo/av-tools/releases/download/v0.3.0/av-tools-0.3.0.zip",
"sha256": "a10889f80f8e30a71737125d8c05d3ff0264249726845700ab95bf37feb64595", "sha256": "3476c5d43ae43ce66b5aeebe8c11334f7cd4d153586348b72f10bfbdbada9be7",
"target": "av" "target": "av"
}, },
{ {
"id": "de.add-ideas.regex-tools", "id": "de.add-ideas.regex-tools",
"version": "0.1.0", "version": "0.4.2",
"artifact": "https://git.add-ideas.de/zemion/regex-tools/releases/download/v0.1.0/regex-tools-0.1.0.zip", "artifact": "https://git.add-ideas.de/lotobo/regex-tools/releases/download/v0.4.2/regex-tools-0.4.2.zip",
"sha256": "6d93fbc4baaad0decce939c020afc2edc011038d8f998b333373d55634e5487c", "sha256": "a3e7342d1f0746aa9c88e6568e7fbc04ab429c94604ba5fb1094ffdb096a54fe",
"target": "regex" "target": "regex"
},
{
"id": "de.add-ideas.svg-tools",
"version": "0.1.0",
"artifact": "https://git.add-ideas.de/lotobo/svg-tools/releases/download/v0.1.0/svg-tools-0.1.0.zip",
"sha256": "364f14c88c4934d9c847bf76de70b89dcf564a405afd58c6703363a95fbfb6ae",
"target": "svg"
} }
] ]
} }
+2 -2
View File
@@ -2,7 +2,7 @@ import { createReadStream, createWriteStream } from 'node:fs';
import { mkdir, readdir, stat } from 'node:fs/promises'; import { mkdir, readdir, stat } from 'node:fs/promises';
import path from 'node:path'; import path from 'node:path';
import { pipeline } from 'node:stream/promises'; import { pipeline } from 'node:stream/promises';
import archiver from 'archiver'; import { ZipArchive } from 'archiver';
import { import {
canonicalExistingPath, canonicalExistingPath,
canonicalPathsOverlap, canonicalPathsOverlap,
@@ -56,7 +56,7 @@ export async function createStaticArchive(inputDirectory, outputFile) {
throw new Error(`Archive input is not a directory: ${source}`); throw new Error(`Archive input is not a directory: ${source}`);
await mkdir(path.dirname(output), { recursive: true }); await mkdir(path.dirname(output), { recursive: true });
const archive = archiver('zip', { zlib: { level: 9 } }); const archive = new ZipArchive({ zlib: { level: 9 } });
const destination = createWriteStream(output, { flags: 'wx' }); const destination = createWriteStream(output, { flags: 'wx' });
archive.on('warning', (error) => { archive.on('warning', (error) => {
if (error.code !== 'ENOENT') destination.destroy(error); if (error.code !== 'ENOENT') destination.destroy(error);
+4 -4
View File
@@ -14,7 +14,7 @@ import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
import { pipeline } from 'node:stream/promises'; import { pipeline } from 'node:stream/promises';
import { pathToFileURL } from 'node:url'; import { pathToFileURL } from 'node:url';
import archiver from 'archiver'; import { ZipArchive } from 'archiver';
import { afterEach, describe, expect, it, vi } from 'vitest'; import { afterEach, describe, expect, it, vi } from 'vitest';
import { assembleRelease } from './assemble.mjs'; import { assembleRelease } from './assemble.mjs';
import { createStaticArchive } from './archive.mjs'; import { createStaticArchive } from './archive.mjs';
@@ -46,7 +46,7 @@ async function zipEntries(
output: string, output: string,
entries: Array<{ name: string; content: string }> entries: Array<{ name: string; content: string }>
) { ) {
const archive = archiver('zip', { zlib: { level: 9 } }); const archive = new ZipArchive({ zlib: { level: 9 } });
const writing = pipeline(archive, createWriteStream(output, { flags: 'wx' })); const writing = pipeline(archive, createWriteStream(output, { flags: 'wx' }));
for (const entry of entries) for (const entry of entries)
archive.append(entry.content, { name: entry.name }); archive.append(entry.content, { name: entry.name });
@@ -83,7 +83,7 @@ function makeLock(artifact: string, sha256: string) {
return { return {
schemaVersion: 1, schemaVersion: 1,
releaseVersion: '0.1.0', releaseVersion: '0.1.0',
portalVersion: '0.2.5', portalVersion: '0.2.12',
catalogue: { catalogue: {
id: 'de.add-ideas.toolbox', id: 'de.add-ideas.toolbox',
name: 'Test Toolbox', name: 'Test Toolbox',
@@ -238,7 +238,7 @@ describe('release assembly integrity', () => {
it('blocks symbolic links before extraction', async () => { it('blocks symbolic links before extraction', async () => {
const directory = await temporaryDirectory(); const directory = await temporaryDirectory();
const artifact = path.join(directory, 'symlink.zip'); const artifact = path.join(directory, 'symlink.zip');
const archive = archiver('zip', { zlib: { level: 9 } }); const archive = new ZipArchive({ zlib: { level: 9 } });
const writing = pipeline( const writing = pipeline(
archive, archive,
createWriteStream(artifact, { flags: 'wx' }) createWriteStream(artifact, { flags: 'wx' })
+74 -3
View File
@@ -62,11 +62,14 @@ describe('production deployment', () => {
/^TOOLBOX_RELEASE_SHA256=([a-f0-9]{64})$/mu /^TOOLBOX_RELEASE_SHA256=([a-f0-9]{64})$/mu
)?.[1]; )?.[1];
const localImageVersion = composeExample.match( const localImageVersion = composeExample.match(
/^\s+image: git\.add-ideas\.de\/zemion\/toolbox:(\S+)$/mu /^\s+image: git\.add-ideas\.de\/lotobo\/toolbox:(\S+)$/mu
)?.[1]; )?.[1];
expect(containerVersion).toBe(releaseLock.releaseVersion); expect(containerVersion).toBe(releaseLock.releaseVersion);
expect(composeVersion).toBe(containerVersion); expect(composeVersion).toBe(containerVersion);
expect(containerChecksum).toMatch(/^[a-f0-9]{64}$/u);
expect(composeChecksum).toMatch(/^[a-f0-9]{64}$/u);
expect(environmentChecksum).toMatch(/^[a-f0-9]{64}$/u);
expect(composeChecksum).toBe(containerChecksum); expect(composeChecksum).toBe(containerChecksum);
expect(environmentVersion).toBe(containerVersion); expect(environmentVersion).toBe(containerVersion);
expect(environmentChecksum).toBe(containerChecksum); expect(environmentChecksum).toBe(containerChecksum);
@@ -82,7 +85,7 @@ describe('production deployment', () => {
expect(containerfile).toContain( expect(containerfile).toContain(
'COPY --from=release --chown=101:101 /tmp/toolbox/' 'COPY --from=release --chown=101:101 /tmp/toolbox/'
); );
for (const app of ['pdf', 'xslt', 'onenote', 'av', 'regex']) { for (const app of ['pdf', 'xslt', 'onenote', 'av', 'regex', 'svg']) {
expect(containerfile).toContain( expect(containerfile).toContain(
`test -f /tmp/toolbox/apps/${app}/toolbox-app.json` `test -f /tmp/toolbox/apps/${app}/toolbox-app.json`
); );
@@ -90,7 +93,7 @@ describe('production deployment', () => {
expect(containerfile).toMatch(/\nUSER 101:101\nEXPOSE 8080\n/u); expect(containerfile).toMatch(/\nUSER 101:101\nEXPOSE 8080\n/u);
}); });
it('uses only the requested external Traefik network and HTTPS router', () => { it('uses only the requested external Traefik network and redirects HTTP to HTTPS', () => {
expect(compose).toContain('dockerfile: Containerfile.release'); expect(compose).toContain('dockerfile: Containerfile.release');
expect(compose).toContain('traefik.enable: "true"'); expect(compose).toContain('traefik.enable: "true"');
expect(compose).toContain( expect(compose).toContain(
@@ -105,6 +108,27 @@ describe('production deployment', () => {
expect(compose).toContain( expect(compose).toContain(
'traefik.http.routers.addideas-toolbox.tls.certresolver: "${TRAEFIK_CERT_RESOLVER:-netcup}"' 'traefik.http.routers.addideas-toolbox.tls.certresolver: "${TRAEFIK_CERT_RESOLVER:-netcup}"'
); );
expect(compose).toContain(
'traefik.http.routers.addideas-toolbox-http.rule: "Host(`${TOOLBOX_HOST:-toolbox.add-ideas.de}`)"'
);
expect(compose).toContain(
'traefik.http.routers.addideas-toolbox-http.entrypoints: web'
);
expect(compose).toContain(
'traefik.http.routers.addideas-toolbox-http.priority: 900'
);
expect(compose).toContain(
'traefik.http.routers.addideas-toolbox-http.middlewares: addideas-toolbox-https-redirect'
);
expect(compose).toContain(
'traefik.http.routers.addideas-toolbox-http.service: addideas-toolbox'
);
expect(compose).toContain(
'traefik.http.middlewares.addideas-toolbox-https-redirect.redirectscheme.scheme: https'
);
expect(compose).toContain(
'traefik.http.middlewares.addideas-toolbox-https-redirect.redirectscheme.permanent: "true"'
);
expect(compose).toContain('external: true'); expect(compose).toContain('external: true');
expect(compose).not.toMatch(/^\s+ports:/mu); expect(compose).not.toMatch(/^\s+ports:/mu);
}); });
@@ -127,6 +151,12 @@ describe('production deployment', () => {
'/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.js', '/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.js',
'/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.wasm', '/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.worker.js', '/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.worker.js',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/version.json',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/st/ffmpeg-core.js',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/st/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/mt/ffmpeg-core.js',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/mt/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/mt/ffmpeg-core.worker.js',
]) { ]) {
expect(isImmutable(uri), uri).toBe(true); expect(isImmutable(uri), uri).toBe(true);
} }
@@ -147,6 +177,11 @@ describe('production deployment', () => {
'/apps/av/vendor/ffmpeg/0.12.10/st/other.wasm', '/apps/av/vendor/ffmpeg/0.12.10/st/other.wasm',
'/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.worker.wasm', '/apps/av/vendor/ffmpeg/0.12.10/mt/ffmpeg-core.worker.wasm',
'/apps/av/vendor/ffmpeg/0.12.10/ffmpeg-core.wasm', '/apps/av/vendor/ffmpeg/0.12.10/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-/st/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed_/st/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-latest/st/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-SNAPSHOT/st/ffmpeg-core.wasm',
'/apps/av/vendor/ffmpeg/0.12.10-reviewed-stack5m.2/st/ffmpeg-core.wasm',
]) { ]) {
expect(isImmutable(uri), uri).toBe(false); expect(isImmutable(uri), uri).toBe(false);
} }
@@ -161,10 +196,46 @@ describe('production deployment', () => {
); );
}); });
it('serves ES modules as JavaScript before the broad static location', () => {
const moduleLocation =
/location ~\* \\\.mjs\$\s*\{\s*types\s*\{\s*application\/javascript mjs;\s*\}\s*try_files \$uri =404;\s*\}/mu;
const moduleLocationIndex = nginxConfig.search(moduleLocation);
const staticLocationIndex = nginxConfig.search(
/location ~\* \\\.\(\?:css\|js\|mjs\|/u
);
expect(moduleLocationIndex).toBeGreaterThanOrEqual(0);
expect(staticLocationIndex).toBeGreaterThan(moduleLocationIndex);
});
it('allows same-origin WebAssembly workers and local blob media previews', () => { it('allows same-origin WebAssembly workers and local blob media previews', () => {
expect(nginxConfig).toContain("script-src 'self' 'wasm-unsafe-eval'"); expect(nginxConfig).toContain("script-src 'self' 'wasm-unsafe-eval'");
expect(nginxConfig).toContain("worker-src 'self' blob:"); expect(nginxConfig).toContain("worker-src 'self' blob:");
expect(nginxConfig).toContain("media-src 'self' blob:"); expect(nginxConfig).toContain("media-src 'self' blob:");
expect(nginxConfig).not.toContain("'unsafe-eval'"); expect(nginxConfig).not.toContain("'unsafe-eval'");
}); });
it('grants only the sandboxed SVG canvas controller cross-origin loading headers', () => {
expect(nginxConfig).toMatch(
/map \$uri \$toolbox_resource_policy\s*\{\s*default "same-origin";\s*"\/apps\/svg\/canvas-frame-controller\.js" "cross-origin";\s*\}/mu
);
expect(nginxConfig).toMatch(
/map \$uri \$toolbox_access_control_allow_origin\s*\{\s*default "";\s*"\/apps\/svg\/canvas-frame-controller\.js" "\*";\s*\}/mu
);
expect(nginxConfig).toContain(
'add_header Cross-Origin-Resource-Policy $toolbox_resource_policy always;'
);
expect(nginxConfig).toContain(
'add_header Access-Control-Allow-Origin $toolbox_access_control_allow_origin always;'
);
expect(nginxConfig).not.toMatch(
/location[^\n{]*canvas-frame-controller[^\n{]*\{/u
);
expect(
nginxConfig.match(/^\s*add_header X-Content-Type-Options /gmu)
).toHaveLength(1);
expect(
nginxConfig.match(/^\s*add_header Content-Security-Policy /gmu)
).toHaveLength(1);
});
}); });
+1 -1
View File
@@ -131,7 +131,7 @@ export function buildCatalogue(lock) {
}; };
return { return {
$schema: $schema:
'https://git.add-ideas.de/zemion/toolbox-sdk/raw/branch/main/schemas/toolbox-catalog.v1.schema.json', 'https://git.add-ideas.de/lotobo/toolbox-sdk/raw/branch/main/schemas/toolbox-catalog.v1.schema.json',
...parseToolboxCatalog(catalogue), ...parseToolboxCatalog(catalogue),
}; };
} }
+9 -5
View File
@@ -42,6 +42,9 @@ describe('portal UI', () => {
expect( expect(
screen.getByTestId('app-card-de.add-ideas.onenote-tools') screen.getByTestId('app-card-de.add-ideas.onenote-tools')
).toHaveTextContent('OneNote'); ).toHaveTextContent('OneNote');
expect(
screen.getByTestId('app-card-de.add-ideas.svg-tools')
).toHaveTextContent('SVG');
expect( expect(
screen.queryByText( screen.queryByText(
'Page-level PDF operations performed locally in the browser.' 'Page-level PDF operations performed locally in the browser.'
@@ -143,9 +146,7 @@ describe('portal UI', () => {
expect(within(card).getByRole('heading', { level: 3 })).toHaveTextContent( expect(within(card).getByRole('heading', { level: 3 })).toHaveTextContent(
'Regex' 'Regex'
); );
expect( expect(within(card).getByText('Explain and test locally.')).toBeVisible();
within(card).getByText('Explain and test regex locally.')
).toBeVisible();
}); });
it('moves the full disclosure into an accessible modal', async () => { it('moves the full disclosure into an accessible modal', async () => {
@@ -181,7 +182,7 @@ describe('portal UI', () => {
}); });
expect(sourceLink).toHaveAttribute( expect(sourceLink).toHaveAttribute(
'href', 'href',
'https://git.add-ideas.de/zemion/pdf-tools' 'https://git.add-ideas.de/lotobo/pdf-tools'
); );
expect(sourceLink).toHaveAttribute('rel', 'noopener noreferrer'); expect(sourceLink).toHaveAttribute('rel', 'noopener noreferrer');
expect( expect(
@@ -409,6 +410,7 @@ describe('portal UI', () => {
'de.add-ideas.pdf-tools', 'de.add-ideas.pdf-tools',
'de.add-ideas.xslt-tools', 'de.add-ideas.xslt-tools',
'de.add-ideas.onenote-tools', 'de.add-ideas.onenote-tools',
'de.add-ideas.svg-tools',
], ],
hidden: [], hidden: [],
theme: 'light', theme: 'light',
@@ -426,6 +428,7 @@ describe('portal UI', () => {
screen.getByTestId('app-card-de.add-ideas.pdf-tools'), screen.getByTestId('app-card-de.add-ideas.pdf-tools'),
screen.getByTestId('app-card-de.add-ideas.xslt-tools'), screen.getByTestId('app-card-de.add-ideas.xslt-tools'),
screen.getByTestId('app-card-de.add-ideas.onenote-tools'), screen.getByTestId('app-card-de.add-ideas.onenote-tools'),
screen.getByTestId('app-card-de.add-ideas.svg-tools'),
]; ];
cards.forEach((card, index) => { cards.forEach((card, index) => {
const rect = { const rect = {
@@ -456,7 +459,7 @@ describe('portal UI', () => {
within(tools) within(tools)
.getAllByRole('heading', { level: 3 }) .getAllByRole('heading', { level: 3 })
.map((heading) => heading.textContent) .map((heading) => heading.textContent)
).toEqual(['XSLT', 'PDF', 'OneNote']); ).toEqual(['XSLT', 'PDF', 'OneNote', 'SVG']);
}); });
expect( expect(
JSON.parse(localStorage.getItem(PREFERENCES_KEY) ?? '{}').order JSON.parse(localStorage.getItem(PREFERENCES_KEY) ?? '{}').order
@@ -464,6 +467,7 @@ describe('portal UI', () => {
'de.add-ideas.xslt-tools', 'de.add-ideas.xslt-tools',
'de.add-ideas.pdf-tools', 'de.add-ideas.pdf-tools',
'de.add-ideas.onenote-tools', 'de.add-ideas.onenote-tools',
'de.add-ideas.svg-tools',
]); ]);
}); });
+3 -3
View File
@@ -28,7 +28,7 @@ import type { LoadedCatalogue, ResolvedApp } from './types';
import { usePreferences } from './usePreferences'; import { usePreferences } from './usePreferences';
const CATALOGUE_HREF = './toolbox.catalog.json'; const CATALOGUE_HREF = './toolbox.catalog.json';
const PORTAL_SOURCE = 'https://git.add-ideas.de/zemion/toolbox-portal'; const PORTAL_SOURCE = 'https://git.add-ideas.de/lotobo/toolbox-portal';
type LoadState = type LoadState =
| { status: 'loading' } | { status: 'loading' }
@@ -489,9 +489,9 @@ export default function App() {
</span> </span>
</p> </p>
<span> <span>
Portal v0.2.5 ·{' '} Portal v0.2.12 ·{' '}
<a <a
href="https://git.add-ideas.de/zemion/toolbox-portal/src/tag/v0.6.0" href="https://git.add-ideas.de/lotobo/toolbox-portal/src/tag/v0.10.0"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
+3 -2
View File
@@ -10,12 +10,12 @@ describe('catalogue loading', () => {
const loaded = await loadCatalogue('/toolbox.catalog.json'); const loaded = await loadCatalogue('/toolbox.catalog.json');
expect(loaded.catalogue.name).toBe('add·ideas Toolbox'); expect(loaded.catalogue.name).toBe('add·ideas Toolbox');
expect(loaded.homeUrl).toBe('http://localhost:3000/'); expect(loaded.homeUrl).toBe('http://localhost:3000/');
expect(loaded.apps).toHaveLength(3); expect(loaded.apps).toHaveLength(4);
expect(loaded.apps[0]).toMatchObject({ expect(loaded.apps[0]).toMatchObject({
id: 'de.add-ideas.pdf-tools', id: 'de.add-ideas.pdf-tools',
name: 'PDF Workbench', name: 'PDF Workbench',
entryUrl: 'http://localhost:3000/apps/pdf/', entryUrl: 'http://localhost:3000/apps/pdf/',
sourceUrl: 'https://git.add-ideas.de/zemion/pdf-tools', sourceUrl: 'https://git.add-ideas.de/lotobo/pdf-tools',
}); });
}); });
@@ -40,6 +40,7 @@ describe('catalogue loading', () => {
'de.add-ideas.pdf-tools', 'de.add-ideas.pdf-tools',
'de.add-ideas.xslt-tools', 'de.add-ideas.xslt-tools',
'de.add-ideas.onenote-tools', 'de.add-ideas.onenote-tools',
'de.add-ideas.svg-tools',
]); ]);
expect(loaded.unavailable).toHaveLength(1); expect(loaded.unavailable).toHaveLength(1);
expect(loaded.unavailable[0]?.reason).toMatch(/HTTP 404/); expect(loaded.unavailable[0]?.reason).toMatch(/HTTP 404/);
+40 -5
View File
@@ -30,7 +30,7 @@ export const pdfManifest: ToolboxAppManifest = {
telemetry: false, telemetry: false,
}, },
source: { source: {
repository: 'https://git.add-ideas.de/zemion/pdf-tools', repository: 'https://git.add-ideas.de/lotobo/pdf-tools',
license: 'AGPL-3.0-only', license: 'AGPL-3.0-only',
}, },
}; };
@@ -45,7 +45,7 @@ export const xsltManifest: ToolboxAppManifest = {
categories: ['documents', 'developer'], categories: ['documents', 'developer'],
tags: ['transform', 'xml'], tags: ['transform', 'xml'],
source: { source: {
repository: 'https://git.add-ideas.de/zemion/xslt-tools', repository: 'https://git.add-ideas.de/lotobo/xslt-tools',
license: 'AGPL-3.0-only', license: 'AGPL-3.0-only',
}, },
}; };
@@ -60,7 +60,7 @@ export const onenoteManifest: ToolboxAppManifest = {
categories: ['documents', 'notes'], categories: ['documents', 'notes'],
tags: ['onepkg', 'import'], tags: ['onepkg', 'import'],
source: { source: {
repository: 'https://git.add-ideas.de/zemion/onenote-tools', repository: 'https://git.add-ideas.de/lotobo/onenote-tools',
license: 'AGPL-3.0-only', license: 'AGPL-3.0-only',
}, },
}; };
@@ -69,7 +69,7 @@ export const regexManifest: ToolboxAppManifest = {
...pdfManifest, ...pdfManifest,
id: 'de.add-ideas.regex-tools', id: 'de.add-ideas.regex-tools',
name: 'Regex Tools', name: 'Regex Tools',
version: '0.1.0', version: '0.4.1',
description: description:
'Develop, explain, test and apply regular expressions locally in the browser.', 'Develop, explain, test and apply regular expressions locally in the browser.',
icon: './regex.svg', icon: './regex.svg',
@@ -82,11 +82,43 @@ export const regexManifest: ToolboxAppManifest = {
crossOriginIsolated: false, crossOriginIsolated: false,
}, },
source: { source: {
repository: 'https://git.add-ideas.de/zemion/regex-tools', repository: 'https://git.add-ideas.de/lotobo/regex-tools',
license: 'GPL-3.0-or-later', license: 'GPL-3.0-or-later',
}, },
}; };
export const svgManifest: ToolboxAppManifest = {
...pdfManifest,
id: 'de.add-ideas.svg-tools',
name: 'SVG Tools',
version: '0.1.0',
description:
'Inspect, edit, optimize and transform SVG documents locally in the browser.',
icon: './favicon.svg',
categories: ['graphics', 'design', 'developer'],
tags: [
'svg',
'vector',
'path',
'xml',
'transform',
'optimize',
'accessibility',
],
requirements: {
secureContext: false,
workers: true,
indexedDb: false,
crossOriginIsolated: false,
topLevelContext: false,
},
source: {
repository: 'https://git.add-ideas.de/lotobo/svg-tools',
license: 'GPL-3.0-or-later',
},
assets: ['./canvas-frame-controller.js'],
};
export const catalogue: ToolboxCatalog = { export const catalogue: ToolboxCatalog = {
schemaVersion: 1, schemaVersion: 1,
id: 'de.add-ideas.toolbox', id: 'de.add-ideas.toolbox',
@@ -97,6 +129,7 @@ export const catalogue: ToolboxCatalog = {
{ manifest: './apps/pdf/toolbox-app.json', enabled: true }, { manifest: './apps/pdf/toolbox-app.json', enabled: true },
{ manifest: './apps/xslt/toolbox-app.json', enabled: true }, { manifest: './apps/xslt/toolbox-app.json', enabled: true },
{ manifest: './apps/onenote/toolbox-app.json', enabled: true }, { manifest: './apps/onenote/toolbox-app.json', enabled: true },
{ manifest: './apps/svg/toolbox-app.json', enabled: true },
], ],
}; };
@@ -118,6 +151,8 @@ export function catalogueFetch(overrides: Record<string, Response> = {}) {
return Response.json(onenoteManifest); return Response.json(onenoteManifest);
if (url.pathname.endsWith('/apps/regex/toolbox-app.json')) if (url.pathname.endsWith('/apps/regex/toolbox-app.json'))
return Response.json(regexManifest); return Response.json(regexManifest);
if (url.pathname.endsWith('/apps/svg/toolbox-app.json'))
return Response.json(svgManifest);
return new Response('Not found', { status: 404 }); return new Response('Not found', { status: 404 });
}; };
} }
+42
View File
@@ -0,0 +1,42 @@
import { describe, expect, it } from 'vitest';
import { shortToolDescription, shortToolTitle } from './toolPresentation';
import type { ResolvedApp } from './types';
function app(id: string, name: string, description: string): ResolvedApp {
return { id, name, description } as ResolvedApp;
}
describe('compact tool presentation', () => {
it('uses the requested Audio & Video tile copy', () => {
const audioVideo = app(
'de.add-ideas.av-tools',
'Audio & Video Tools',
'Convert and lightly edit audio and video locally in the browser.'
);
expect(shortToolTitle(audioVideo)).toBe('Audio & Video');
expect(shortToolDescription(audioVideo)).toBe('Convert and edit locally.');
});
it('uses the requested Regex tile copy', () => {
const regex = app(
'de.add-ideas.regex-tools',
'Regex Tools',
'Develop, explain, test and apply regular expressions locally in the browser.'
);
expect(shortToolTitle(regex)).toBe('Regex');
expect(shortToolDescription(regex)).toBe('Explain and test locally.');
});
it('uses the compact SVG tile copy', () => {
const svg = app(
'de.add-ideas.svg-tools',
'SVG Tools',
'Inspect, edit, optimize and transform SVG documents locally in the browser.'
);
expect(shortToolTitle(svg)).toBe('SVG');
expect(shortToolDescription(svg)).toBe('Inspect and edit vectors locally.');
});
});
+9 -1
View File
@@ -13,9 +13,17 @@ const PRESENTATION: Record<string, { title: string; description: string }> = {
title: 'OneNote', title: 'OneNote',
description: 'Open OneNote files locally.', description: 'Open OneNote files locally.',
}, },
'de.add-ideas.av-tools': {
title: 'Audio & Video',
description: 'Convert and edit locally.',
},
'de.add-ideas.regex-tools': { 'de.add-ideas.regex-tools': {
title: 'Regex', title: 'Regex',
description: 'Explain and test regex locally.', description: 'Explain and test locally.',
},
'de.add-ideas.svg-tools': {
title: 'SVG',
description: 'Inspect and edit vectors locally.',
}, },
}; };