From 76f03eb046a23cfc5d82b9991a16c9d818aa121c Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 20 Sep 2026 14:22:47 +0800 Subject: [PATCH] feat: convert editor package to TypeScript --- .gitea/workflows/ci.yml | 2 + .gitignore | 1 + README.md | 4 +- package-lock.json | 2045 +++++++++++++++++++++++++++++++++++++++ package.json | 26 +- src/index.js | 136 --- src/index.ts | 31 + src/renderer.js | 65 -- src/renderer.ts | 17 + src/types.ts | 46 + src/xhs.ts | 2 + test/editor.test.js | 26 - test/editor.test.mjs | 9 + tsconfig.json | 18 + 14 files changed, 2192 insertions(+), 236 deletions(-) create mode 100644 package-lock.json delete mode 100644 src/index.js create mode 100644 src/index.ts delete mode 100644 src/renderer.js create mode 100644 src/renderer.ts create mode 100644 src/types.ts create mode 100644 src/xhs.ts delete mode 100644 test/editor.test.js create mode 100644 test/editor.test.mjs create mode 100644 tsconfig.json diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2987258..d58d0fa 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -27,5 +27,7 @@ jobs: with tarfile.open(fileobj=io.BytesIO(data),mode='r:gz') as archive:archive.extractall(directory,filter='data') shutil.copytree(next(Path(directory).iterdir()),Path.cwd(),dirs_exist_ok=True) PY + - run: npm ci - run: npm run check - run: npm test + - run: npm run build diff --git a/.gitignore b/.gitignore index 31e1c0e..edc5d77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ +dist/ *.tgz coverage/ diff --git a/README.md b/README.md index 0097e85..8953bdb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @entroia/poster-editor -跨 Web 与小程序的 Schema 驱动图片编辑器核心。仓库名是工程容器,实际包名为 `@entroia/poster-editor`。 +跨 Web 与小程序的 Schema 驱动图片编辑器核心。仓库名是工程容器,实际包名为 `@entroia/poster-editor`。公共源码、渲染器、小程序类型入口和声明文件均由 TypeScript 构建,包同时提供 ESM、CommonJS 与 `.d.ts`。 核心采用非破坏性 `PosterDocument`:背景、分组、图片、矢量、文字、贴纸和笔刷均为可独立绑定工具与效果的图层。业务图谱先转换为通用 vector/group 节点,编辑器不包含 PK 规则。 @@ -11,3 +11,5 @@ 花字按 `documentSeed + layerId + 字符逻辑位置` 哈希出受 `maxAngle` 限制的角度,因此重绘、导出和服务端复现时保持一致;背景色块可直接随测量宽度增长,图片背景使用 `capInsets` 九宫格拉伸,边角和装饰不会变形。 花花笔刷属于 Stamp Brush:沿采样后的路径重复绘制透明花朵素材,而不是一条特殊颜色的线。每个落点可保存尺寸、角度和素材 ID,因此撤销、重放与跨端导出是确定的。 + +开发时使用 `npm install`、`npm run check`、`npm test` 和 `npm run build`。构建产物位于 `dist/`;现有小程序若尚未接入包构建流程,可继续使用 `./xhs-legacy` 兼容入口。 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..965a2ba --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2045 @@ +{ + "name": "@entroia/poster-editor", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@entroia/poster-editor", + "version": "0.1.0", + "license": "UNLICENSED", + "devDependencies": { + "@types/node": "^24.7.0", + "tsup": "^8.5.0", + "typescript": "^5.9.3" + }, + "engines": { + "node": ">=20.9" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.4.tgz", + "integrity": "sha512-I+BSHzTAhKN2n7ZwGZsegGcZjDpLqFOMAtJz/u6uFGe0pUFbq56dEHjqJV/ZUdRJtNXNxA+hREUatZBvMR3Oiw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.4.tgz", + "integrity": "sha512-pu3BdjS2LtEzRu2elmGzS3fIeWSZy4BMDIaLNwjorO76+k2d0LMluijhsDx3KQyQBQ/lLUZCQA9/s6csvUfuhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.4.tgz", + "integrity": "sha512-xfSrj9MHnWK9GaSqT9U0ImHtH/N8WZlHLx4cZHiuLcqs640hvZ3hLPd5UR2AZS57FaE8HrRUSpltbZdWRxHiDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.4.tgz", + "integrity": "sha512-bqU99PLJb/dqb3S0GIMdeuyAEETSUgZBoqXYd3Sd+WCsV+MmPhnN6JrotWyir31+QgH7EvvE5/mwGJlEoci8Fw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.4.tgz", + "integrity": "sha512-JinsFZ5G40oXQb+sUuiA5x689vhr6dDYK0H0NL+rwKdL6CqnmYN8PE4ZwfRSoIjrCxqTQG/SLfTtSvHeGxoVlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.4.tgz", + "integrity": "sha512-GAdA4UxpiNm27cLHr2GqXBpAD0x9FqwYBY7/YSP0Ss0/PNi4k8gbviqpIpYbVSRBaS2ZcegXEzgTQMbRNCwxCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.4.tgz", + "integrity": "sha512-qDd6NoA1znaLjp4jR5U/KWCdLAKDJNB8W9ChbbDaKbo0xA+Atln5HK6LFCZ4oJQpemtRZA288DCirFRjrspptw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.4.tgz", + "integrity": "sha512-WtB5Tz5KTNINb8ZA+8sQ7bmjuS1JrRT7YverYIhUGdWWDlpzVWmIwuZE+jidkEXUn1l0zrEkaIMa8dHF3NGcsA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.4.tgz", + "integrity": "sha512-VcQ3L1tjnkKzWjryAVaFhHEWcqOfICX9uxVVoDzm2t0DpgKRHd2zOpVrJc0xsWeBZcBFyYROCIBdyR/fS174pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.4.tgz", + "integrity": "sha512-6+ZQX6P5s0cMDN2Ypb8Lbm2+/sZYmZjdaYny992ujUU9UKi/4CWoJWsl1pNvjWJHNHGK51m+jKGLlh1ylb2ifQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.4.tgz", + "integrity": "sha512-D72ZnvkFkBXOfzMMQLcwfPLyGkKb7HZ9/mf97B7v6/P5Lbv4oFOtSY/uHbS8lH6uKUOxoKiuokdb50XZSzzbJw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.4.tgz", + "integrity": "sha512-piU6BxeqA3O9KSu3kRCIQQtNqFFaTu21SEV4FwaRZowpnj3bLaWPZHw+xFqCs0XlJ+aOH3PTRWGoglH+mKA/OA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.4.tgz", + "integrity": "sha512-/5PGpHwqt2EEEOUs1XwzubE/ucr0dWDQ+to3zqi4Ds7EWpwtQ79wXc4JBoxqj/OwpawTsKWzJxHfSuBOq3DrWA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.4.tgz", + "integrity": "sha512-cX3beZDLWt7G2oJF+nhChiT+qtaihs+S2xi7ziGmVB+2pwPng6D0Ed0HmElQOgv2UsUmSJJLGwpBao/3TDx3VA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.4.tgz", + "integrity": "sha512-1uz2mGWHyptR7DgHHrlbdRAjXK7v7elGZ9lMja910/RP+ZYbX6xAmCiU9UZSX4hqmgtHMv6lr5l3kq1HIOpcag==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.4.tgz", + "integrity": "sha512-nLS8topojxyz7SRpKR2IODRpQ0XPZ+xaOXvT3+hqK/Uy8Lo5HFgkkIBiIrCu5tL5YqzTvgovGw55PwpahTAGig==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.4.tgz", + "integrity": "sha512-gs7DRKotr3l3q+jGPQBjH0ng1FjlEDm5ueQrkw5JtQvtLyEIcLASqAEaor56BhkKRzk+IcQzrcanBdb/bBQn8g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.4.tgz", + "integrity": "sha512-791ET7W17NnScOZM7h4dX5hYspxE28htPFsb1awY/NRR8+PRNkS53e475rDdxXXDrP+kwnCcNWg9CX5ztn/Aqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.4.tgz", + "integrity": "sha512-iwZQRcmj7g88g3tzefIrQY7qvmuA/cfYwhrDtTBhsmukO4U2huVO5W+86XacUMRvdSFVAc6kZUZy21JaRwiB9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.4.tgz", + "integrity": "sha512-dVHFp9gRWrdTpnqQuGfCwd7hOQDatK1VCP2iWhLY/cGrOQs/ucFzJ6A5SRqbXX12ZDI8EUuejSM5kwg+ja7Png==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.4.tgz", + "integrity": "sha512-t3NlauOW6gxZVVFcBEnO62Cb4wbyDFL416gTg1uFI/2tgqYQlf69FbSE115Ajre9I+c26Lk4mcmdFUsS/DGifQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.4.tgz", + "integrity": "sha512-xWuIaSye5FWZF8+UYtVEcHtRJDN5kN9Kfgxx3Kq8XIov9KSKbc1fiqQCm90SKrgQbUXZelbnUhnlUJmfSE7P9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.4.tgz", + "integrity": "sha512-9ALJJUOg/ZflMJepVo2PlgsGxSaxN7SQ4Z8GoZfVlarWr6r3rkHUNsd/zAio7p4YMtChSMXPionxej4Hkf6CXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.4.tgz", + "integrity": "sha512-blj9z5qx/Pv4WU0W1NMFDB97e0JH5ed+aZGywW8WCvp/NhWX/4PFAq5uu6Q0AebNn+Vo6KzUYDT++JzTT5ojlQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.4.tgz", + "integrity": "sha512-Erx822VRBwLa124shbj+wNXe//BOgMEctDV0m1aqTQdNO1S69DgNUCFKC1RCeZfixs1J31l6igk1ziyXErbigQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.6.tgz", + "integrity": "sha512-SGrw/h3KPFshy3OE6ZL53LMBG5vGQQ8/gIpiqz/kRZhPJ7HgwCEs8LBuNtWLa8dvGZVpSF7+Bf+c11HUrCb/yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.63.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.4.tgz", + "integrity": "sha512-4U0liVayNIoLp3GFl1FcI8561WepLnZ1rqfraGh7S9B3Ur5F9S283y8Futii7RUU2C/97tOBmBy7nYvhoiOpbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.4", + "@rollup/rollup-android-arm64": "4.63.4", + "@rollup/rollup-darwin-arm64": "4.63.4", + "@rollup/rollup-darwin-x64": "4.63.4", + "@rollup/rollup-freebsd-arm64": "4.63.4", + "@rollup/rollup-freebsd-x64": "4.63.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.4", + "@rollup/rollup-linux-arm-musleabihf": "4.63.4", + "@rollup/rollup-linux-arm64-gnu": "4.63.4", + "@rollup/rollup-linux-arm64-musl": "4.63.4", + "@rollup/rollup-linux-loong64-gnu": "4.63.4", + "@rollup/rollup-linux-loong64-musl": "4.63.4", + "@rollup/rollup-linux-ppc64-gnu": "4.63.4", + "@rollup/rollup-linux-ppc64-musl": "4.63.4", + "@rollup/rollup-linux-riscv64-gnu": "4.63.4", + "@rollup/rollup-linux-riscv64-musl": "4.63.4", + "@rollup/rollup-linux-s390x-gnu": "4.63.4", + "@rollup/rollup-linux-x64-gnu": "4.63.4", + "@rollup/rollup-linux-x64-musl": "4.63.4", + "@rollup/rollup-openbsd-x64": "4.63.4", + "@rollup/rollup-openharmony-arm64": "4.63.4", + "@rollup/rollup-win32-arm64-msvc": "4.63.4", + "@rollup/rollup-win32-ia32-msvc": "4.63.4", + "@rollup/rollup-win32-x64-gnu": "4.63.4", + "@rollup/rollup-win32-x64-msvc": "4.63.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsx": { + "version": "4.23.14", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.14.tgz", + "integrity": "sha512-yFwMnbAsUFz/T3kR8P2ENc/DDUaYd9tKg4oVYo0lhkX0LlK4UuqYAO6mpQ2y6lmcyip1uPJ34C2kPACopDwG4w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json index 13dc18f..5ba0c73 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,28 @@ "version": "0.1.0", "description": "Schema-driven, layer-based poster and image editor core for Web and mini apps", "type": "module", - "main": "./src/index.js", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { - ".": "./src/index.js", - "./renderer": "./src/renderer.js", + ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" }, + "./renderer": { "types": "./dist/renderer.d.ts", "import": "./dist/renderer.js", "require": "./dist/renderer.cjs" }, "./schema": "./schema/poster-document.schema.json", - "./xhs": "./xhs/index.cjs" + "./xhs": { "types": "./dist/xhs.d.ts", "import": "./dist/xhs.js", "require": "./dist/xhs.cjs" }, + "./xhs-legacy": "./xhs/index.cjs" }, - "files": ["src", "schema", "xhs"], + "files": ["dist", "schema", "xhs"], "scripts": { - "test": "node --test", - "check": "node --check src/index.js && node --check src/renderer.js && node --check xhs/index.cjs" + "build": "tsup src/index.ts src/renderer.ts src/xhs.ts --format esm,cjs --dts --sourcemap --clean", + "test": "npm run build && node --test test/*.test.mjs", + "check": "tsc --noEmit && node --check xhs/index.cjs", + "prepack": "npm run check && npm test && npm run build" + }, + "engines": { "node": ">=20.9" }, + "devDependencies": { + "@types/node": "^24.7.0", + "tsup": "^8.5.0", + "typescript": "^5.9.3" }, - "engines": { "node": ">=20" }, "license": "UNLICENSED" } diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 87f2a50..0000000 --- a/src/index.js +++ /dev/null @@ -1,136 +0,0 @@ -export const DOCUMENT_VERSION = 1; - -export const LAYER_TYPES = Object.freeze([ - 'background', 'group', 'image', 'vector', 'text', 'sticker', 'brush' -]); - -export const ADJUSTMENTS = Object.freeze([ - 'brightness', 'contrast', 'shadows', 'highlights', 'temperature', - 'hue', 'saturation', 'vignette', 'sharpen', 'grain' -]); - -export const LAYOUT_PRESETS = Object.freeze([ - { id: 'two-equal-columns', count: 2, tags: ['2', 'equal'], slots: [[0,0,0.5,1],[0.5,0,0.5,1]] }, - { id: 'two-equal-rows', count: 2, tags: ['2', 'equal'], slots: [[0,0,1,0.5],[0,0.5,1,0.5]] }, - { id: 'two-top-large', count: 2, tags: ['2', 'top-large'], slots: [[0,0,1,0.66],[0,0.66,1,0.34]] }, - { id: 'two-bottom-large', count: 2, tags: ['2', 'bottom-large'], slots: [[0,0,1,0.34],[0,0.34,1,0.66]] }, - { id: 'two-left-large', count: 2, tags: ['2', 'left-large'], slots: [[0,0,0.66,1],[0.66,0,0.34,1]] }, - { id: 'two-right-large', count: 2, tags: ['2', 'right-large'], slots: [[0,0,0.34,1],[0.34,0,0.66,1]] }, - { id: 'three-hero-left', count: 3, tags: ['3', 'hero'], slots: [[0,0,0.62,1],[0.62,0,0.38,0.5],[0.62,0.5,0.38,0.5]] }, - { id: 'four-grid', count: 4, tags: ['4', 'grid'], slots: [[0,0,0.5,0.5],[0.5,0,0.5,0.5],[0,0.5,0.5,0.5],[0.5,0.5,0.5,0.5]] }, - { id: 'five-hero', count: 5, tags: ['5', 'hero'], slots: [[0,0,1,0.56],[0,0.56,0.25,0.44],[0.25,0.56,0.25,0.44],[0.5,0.56,0.25,0.44],[0.75,0.56,0.25,0.44]] } -]); - -export const BRUSH_PRESETS = Object.freeze([ - { id: 'round', engine: 'stroke', spacing: 0.08, jitter: 0 }, - { id: 'marker', engine: 'stroke', spacing: 0.05, opacity: 0.55 }, - { id: 'pencil', engine: 'texture', spacing: 0.06, texture: 'pencil-grain' }, - { id: 'chalk', engine: 'texture', spacing: 0.08, texture: 'chalk-grain', jitter: 0.08 }, - { id: 'spray', engine: 'particles', density: 18, jitter: 0.8 }, - { id: 'neon', engine: 'stroke', glow: 0.8 }, - { id: 'flowers', engine: 'stamp', stamp: 'flower', spacing: 0.7, angleJitter: 18, sizeJitter: 0.18 } -]); - -export const TOOL_DEFINITIONS = Object.freeze({ - filter: { accepts: ['image', 'background'], writes: ['effects'] }, - text: { accepts: ['text'], writes: ['text', 'style'] }, - sticker: { accepts: ['group'], creates: 'sticker' }, - crop: { accepts: ['image', 'background'], writes: ['crop', 'mask'] }, - adjust: { accepts: ['image', 'background'], writes: ['adjustments'] }, - layout: { accepts: ['group'], writes: ['layout'] }, - brush: { accepts: ['brush'], writes: ['strokes'] } -}); - -export function createDocument(options = {}) { - return { - schema: 'entroia.poster-document', version: DOCUMENT_VERSION, - id: options.id || cryptoId(), seed: options.seed || cryptoId(), - canvas: { width: options.width || 1080, height: options.height || 1440, background: options.background || '#ffffff' }, - layers: options.layers ? structuredCloneSafe(options.layers) : [], - bindings: options.bindings ? structuredCloneSafe(options.bindings) : {}, - meta: { title: options.title || '', createdAt: new Date().toISOString(), ...(options.meta || {}) } - }; -} - -export function createLayer(type, props = {}) { - if (!LAYER_TYPES.includes(type)) throw new Error(`Unsupported layer type: ${type}`); - return { - id: props.id || cryptoId(), type, name: props.name || type, - visible: props.visible !== false, locked: Boolean(props.locked), opacity: props.opacity ?? 1, - blendMode: props.blendMode || 'source-over', transform: props.transform || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }, - effects: props.effects || [], tags: props.tags || [], ...props - }; -} - -export function stableHash(value) { - let hash = 2166136261; - for (const char of String(value)) { hash ^= char.codePointAt(0); hash = Math.imul(hash, 16777619); } - return hash >>> 0; -} - -export function deterministicGlyphAngle({ documentSeed, layerId, index, glyph, maxAngle = 7, mode = 'position' }) { - const key = mode === 'glyph' ? `${documentSeed}:${layerId}:${glyph}` : `${documentSeed}:${layerId}:${index}`; - return ((stableHash(key) / 0xffffffff) * 2 - 1) * Math.abs(maxAngle); -} - -export function layoutDecoratedText(text, measure, style = {}) { - const glyphs = Array.from(text); - let cursor = 0; - const runs = glyphs.map((glyph, index) => { - const width = measure(glyph, style); - const run = { glyph, index, x: cursor + width / 2, width, - rotation: deterministicGlyphAngle({ documentSeed: style.documentSeed || '', layerId: style.layerId || '', index, glyph, maxAngle: style.maxAngle || 0, mode: style.rotationMode }) }; - cursor += width + (style.letterSpacing || 0); - return run; - }); - const paddingX = style.background?.paddingX ?? 20; - const paddingY = style.background?.paddingY ?? 12; - return { glyphs: runs, width: Math.max(0, cursor - (style.letterSpacing || 0)), - background: style.background ? { ...style.background, width: Math.max(0, cursor) + paddingX * 2, height: (style.lineHeight || style.fontSize || 48) + paddingY * 2, resizeMode: style.background.resizeMode || 'nine-slice' } : null }; -} - -export function resolveBinding(template, context) { - if (typeof template !== 'string') return template; - return template.replace(/\{([^}]+)\}/g, (_, path) => path.split('.').reduce((value, key) => value?.[key], context) ?? ''); -} - -export function resolveToolTargets(document, toolId, context = {}) { - const binding = document.bindings?.tools?.[toolId]; - if (!binding) return []; - const ids = new Set((binding.layerIds || []).map(id => resolveBinding(id, context))); - return flattenLayers(document.layers).filter(layer => - ids.has(layer.id) || (binding.tags || []).some(tag => layer.tags?.includes(resolveBinding(tag, context))) - ); -} - -export function selectCatalogItems(items, query = {}) { - const all = new Set(query.all || []), any = new Set(query.any || []), none = new Set(query.none || []); - return items.filter(item => { - const tags = new Set(item.tags || []); - return [...all].every(tag => tags.has(tag)) && (!any.size || [...any].some(tag => tags.has(tag))) && ![...none].some(tag => tags.has(tag)); - }); -} - -export function applyLayout(presetId, group, gap = 0) { - const preset = LAYOUT_PRESETS.find(item => item.id === presetId); - if (!preset) throw new Error(`Unknown layout: ${presetId}`); - return preset.slots.map(([x,y,width,height], index) => ({ index, x: x + gap / 2, y: y + gap / 2, width: Math.max(0, width - gap), height: Math.max(0, height - gap), mask: group?.mask || 'rect' })); -} - -export function validateDocument(document) { - const errors = []; - if (document?.schema !== 'entroia.poster-document') errors.push('schema must be entroia.poster-document'); - if (document?.version !== DOCUMENT_VERSION) errors.push(`unsupported version: ${document?.version}`); - if (!(document?.canvas?.width > 0 && document?.canvas?.height > 0)) errors.push('canvas dimensions must be positive'); - const ids = new Set(); - for (const layer of document?.layers || []) { - if (!LAYER_TYPES.includes(layer.type)) errors.push(`unsupported layer type: ${layer.type}`); - if (!layer.id || ids.has(layer.id)) errors.push(`invalid or duplicate layer id: ${layer.id}`); - ids.add(layer.id); - } - return { valid: errors.length === 0, errors }; -} - -function cryptoId() { return globalThis.crypto?.randomUUID?.() || `layer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; } -function structuredCloneSafe(value) { return globalThis.structuredClone ? structuredClone(value) : JSON.parse(JSON.stringify(value)); } -function flattenLayers(layers) { return (layers || []).flatMap(layer => [layer, ...flattenLayers(layer.children)]); } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..eab85dc --- /dev/null +++ b/src/index.ts @@ -0,0 +1,31 @@ +import type { BrushPreset, CatalogItem, CatalogQuery, CreateDocumentOptions, DecoratedTextLayout, DecoratedTextStyle, DocumentBindings, LayerProps, LayerType, LayoutBox, LayoutPreset, PosterDocument, PosterLayer, ValidationResult } from './types.js'; +export type * from './types.js'; + +export const DOCUMENT_VERSION = 1 as const; +export const LAYER_TYPES = Object.freeze(['background','group','image','vector','text','sticker','brush'] as const); +export const ADJUSTMENTS = Object.freeze(['brightness','contrast','shadows','highlights','temperature','hue','saturation','vignette','sharpen','grain'] as const); +export const LAYOUT_PRESETS: readonly LayoutPreset[] = Object.freeze([ + {id:'two-equal-columns',count:2,tags:['2','equal'],slots:[[0,0,.5,1],[.5,0,.5,1]]},{id:'two-equal-rows',count:2,tags:['2','equal'],slots:[[0,0,1,.5],[0,.5,1,.5]]}, + {id:'two-top-large',count:2,tags:['2','top-large'],slots:[[0,0,1,.66],[0,.66,1,.34]]},{id:'two-bottom-large',count:2,tags:['2','bottom-large'],slots:[[0,0,1,.34],[0,.34,1,.66]]}, + {id:'two-left-large',count:2,tags:['2','left-large'],slots:[[0,0,.66,1],[.66,0,.34,1]]},{id:'two-right-large',count:2,tags:['2','right-large'],slots:[[0,0,.34,1],[.34,0,.66,1]]}, + {id:'three-hero-left',count:3,tags:['3','hero'],slots:[[0,0,.62,1],[.62,0,.38,.5],[.62,.5,.38,.5]]},{id:'four-grid',count:4,tags:['4','grid'],slots:[[0,0,.5,.5],[.5,0,.5,.5],[0,.5,.5,.5],[.5,.5,.5,.5]]}, + {id:'five-hero',count:5,tags:['5','hero'],slots:[[0,0,1,.56],[0,.56,.25,.44],[.25,.56,.25,.44],[.5,.56,.25,.44],[.75,.56,.25,.44]]} +]); +export const BRUSH_PRESETS: readonly BrushPreset[] = Object.freeze([ + {id:'round',engine:'stroke',spacing:.08,jitter:0},{id:'marker',engine:'stroke',spacing:.05,opacity:.55},{id:'pencil',engine:'texture',spacing:.06,texture:'pencil-grain'}, + {id:'chalk',engine:'texture',spacing:.08,texture:'chalk-grain',jitter:.08},{id:'spray',engine:'particles',density:18,jitter:.8},{id:'neon',engine:'stroke',glow:.8}, + {id:'flowers',engine:'stamp',stamp:'flower',spacing:.7,angleJitter:18,sizeJitter:.18} +]); +export const TOOL_DEFINITIONS = Object.freeze({filter:{accepts:['image','background'],writes:['effects']},text:{accepts:['text'],writes:['text','style']},sticker:{accepts:['group'],creates:'sticker'},crop:{accepts:['image','background'],writes:['crop','mask']},adjust:{accepts:['image','background'],writes:['adjustments']},layout:{accepts:['group'],writes:['layout']},brush:{accepts:['brush'],writes:['strokes']}}); + +export function createDocument(options: CreateDocumentOptions = {}): PosterDocument { return {schema:'entroia.poster-document',version:DOCUMENT_VERSION,id:options.id??cryptoId(),seed:options.seed??cryptoId(),canvas:{width:options.width??1080,height:options.height??1440,background:options.background??'#ffffff'},layers:options.layers?clone(options.layers):[],bindings:options.bindings?clone(options.bindings):{},meta:{title:options.title??'',createdAt:new Date().toISOString(),...(options.meta??{})}}; } +export function createLayer(type: LayerType, props: LayerProps = {}): PosterLayer { if(!(LAYER_TYPES as readonly string[]).includes(type))throw new Error(`Unsupported layer type: ${type}`);const layer:PosterLayer={id:props.id??cryptoId(),type,name:props.name??type,visible:props.visible!==false,locked:Boolean(props.locked),opacity:props.opacity??1,blendMode:props.blendMode??'source-over',transform:props.transform??{x:0,y:0,scaleX:1,scaleY:1,rotation:0},effects:props.effects??[],tags:props.tags??[]};return Object.assign(layer,props); } +export function stableHash(value: unknown): number { let hash=2166136261;for(const char of String(value)){hash^=char.codePointAt(0)??0;hash=Math.imul(hash,16777619);}return hash>>>0; } +export function deterministicGlyphAngle({documentSeed,layerId,index,glyph,maxAngle=7,mode='position'}:{documentSeed:string;layerId:string;index:number;glyph:string;maxAngle?:number;mode?:'position'|'glyph'}):number { const key=mode==='glyph'?`${documentSeed}:${layerId}:${glyph}`:`${documentSeed}:${layerId}:${index}`;return((stableHash(key)/0xffffffff)*2-1)*Math.abs(maxAngle); } +export function layoutDecoratedText(text:string,measure:(glyph:string,style:DecoratedTextStyle)=>number,style:DecoratedTextStyle={}):DecoratedTextLayout { let cursor=0;const glyphs=Array.from(text).map((glyph,index)=>{const width=measure(glyph,style),run={glyph,index,x:cursor+width/2,width,rotation:deterministicGlyphAngle({documentSeed:style.documentSeed??'',layerId:style.layerId??'',index,glyph,maxAngle:style.maxAngle??0,mode:style.rotationMode??'position'})};cursor+=width+(style.letterSpacing??0);return run;});const paddingX=style.background?.paddingX??20,paddingY=style.background?.paddingY??12;return{glyphs,width:Math.max(0,cursor-(style.letterSpacing??0)),background:style.background?{...style.background,paddingX,paddingY,width:Math.max(0,cursor)+paddingX*2,height:(style.lineHeight??style.fontSize??48)+paddingY*2,resizeMode:style.background.resizeMode??'nine-slice'}:null}; } +export function resolveBinding(template:unknown,context:Record):unknown { if(typeof template!=='string')return template;return template.replace(/\{([^}]+)\}/g,(_,path:string)=>String(path.split('.').reduce((value,key)=>isRecord(value)?value[key]:undefined,context)??'')); } +export function resolveToolTargets(document:PosterDocument,toolId:string,context:Record={}):PosterLayer[]{const binding=document.bindings.tools?.[toolId];if(!binding)return[];const ids=new Set((binding.layerIds??[]).map(id=>String(resolveBinding(id,context))));return flattenLayers(document.layers).filter(layer=>ids.has(layer.id)||(binding.tags??[]).some(tag=>layer.tags.includes(String(resolveBinding(tag,context)))));} +export function selectCatalogItems(items:readonly T[],query:CatalogQuery={}):T[]{const all=new Set(query.all??[]),any=new Set(query.any??[]),none=new Set(query.none??[]);return items.filter(item=>{const tags=new Set(item.tags??[]);return[...all].every(tag=>tags.has(tag))&&(!any.size||[...any].some(tag=>tags.has(tag)))&&![...none].some(tag=>tags.has(tag));});} +export function applyLayout(presetId:string,group?:{mask?:unknown},gap=0):LayoutBox[]{const preset=LAYOUT_PRESETS.find(item=>item.id===presetId);if(!preset)throw new Error(`Unknown layout: ${presetId}`);return preset.slots.map(([x,y,width,height],index)=>({index,x:x+gap/2,y:y+gap/2,width:Math.max(0,width-gap),height:Math.max(0,height-gap),mask:group?.mask??'rect'}));} +export function validateDocument(document:unknown):ValidationResult {const errors:string[]=[];if(!isRecord(document)||document.schema!=='entroia.poster-document')errors.push('schema must be entroia.poster-document');if(!isRecord(document)||document.version!==DOCUMENT_VERSION)errors.push(`unsupported version: ${isRecord(document)?String(document.version):'undefined'}`);const canvas=isRecord(document)&&isRecord(document.canvas)?document.canvas:null;if(!(canvas&&typeof canvas.width==='number'&&canvas.width>0&&typeof canvas.height==='number'&&canvas.height>0))errors.push('canvas dimensions must be positive');const layers=isRecord(document)&&Array.isArray(document.layers)?document.layers:[],ids=new Set();for(const value of layers){if(!isRecord(value))continue;const type=String(value.type),id=String(value.id??'');if(!(LAYER_TYPES as readonly string[]).includes(type))errors.push(`unsupported layer type: ${type}`);if(!id||ids.has(id))errors.push(`invalid or duplicate layer id: ${id}`);ids.add(id);}return{valid:errors.length===0,errors};} +function cryptoId():string{const cryptoApi=globalThis.crypto as {randomUUID?:()=>string}|undefined;return cryptoApi?.randomUUID?.()??`layer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;}function clone(value:T):T{const cloneFn=(globalThis as {structuredClone?:(input:U)=>U}).structuredClone;return cloneFn?cloneFn(value):JSON.parse(JSON.stringify(value)) as T;}function flattenLayers(layers:PosterLayer[]=[]):PosterLayer[]{return layers.flatMap(layer=>[layer,...flattenLayers(layer.children)]);}function isRecord(value:unknown):value is Record{return typeof value==='object'&&value!==null;} diff --git a/src/renderer.js b/src/renderer.js deleted file mode 100644 index b8269b3..0000000 --- a/src/renderer.js +++ /dev/null @@ -1,65 +0,0 @@ -import { layoutDecoratedText } from './index.js'; - -export class CanvasRenderer { - constructor(context, assets = {}) { this.ctx = context; this.assets = assets; } - render(document) { - const ctx = this.ctx; - ctx.save(); ctx.fillStyle = document.canvas.background || '#fff'; ctx.fillRect(0, 0, document.canvas.width, document.canvas.height); - for (const layer of document.layers) this.renderLayer(layer, document); - ctx.restore(); - } - renderLayer(layer, document) { - if (!layer.visible) return; - const ctx = this.ctx, t = layer.transform || {}; - ctx.save(); ctx.globalAlpha *= layer.opacity ?? 1; ctx.globalCompositeOperation = layer.blendMode || 'source-over'; - ctx.translate(t.x || 0, t.y || 0); ctx.rotate((t.rotation || 0) * Math.PI / 180); ctx.scale(t.scaleX ?? 1, t.scaleY ?? 1); - if (layer.type === 'background' || layer.type === 'vector') this.drawVector(layer); - if (layer.type === 'text') this.drawText(layer, document); - if (layer.type === 'image' || layer.type === 'sticker') this.drawImage(layer); - if (layer.type === 'brush') this.drawBrush(layer); - for (const child of layer.children || []) this.renderLayer(child, document); - ctx.restore(); - } - drawVector(layer) { - const ctx = this.ctx; - for (const command of layer.commands || []) { - ctx.beginPath(); ctx.strokeStyle = command.stroke || 'transparent'; ctx.fillStyle = command.fill || 'transparent'; ctx.lineWidth = command.strokeWidth || 1; - if (command.kind === 'line') { ctx.moveTo(command.x1, command.y1); ctx.lineTo(command.x2, command.y2); ctx.stroke(); } - if (command.kind === 'rect') { if (command.radius && ctx.roundRect) ctx.roundRect(command.x, command.y, command.width, command.height, command.radius); else ctx.rect(command.x, command.y, command.width, command.height); if (command.fill) ctx.fill(); if (command.stroke) ctx.stroke(); } - if (command.kind === 'text') { ctx.font = command.font || '24px sans-serif'; ctx.textAlign = command.align || 'left'; ctx.fillText(command.text, command.x, command.y); } - } - } - drawText(layer, document) { - const ctx = this.ctx, style = layer.style || {}; - ctx.font = `${style.fontWeight || 700} ${style.fontSize || 48}px ${style.fontFamily || 'sans-serif'}`; - const measured = layoutDecoratedText(layer.text || '', glyph => ctx.measureText(glyph).width, { ...style, documentSeed: document.seed, layerId: layer.id }); - if (measured.background) this.drawStretchBackground(measured.background, style.background); - ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = style.fill || '#111'; ctx.strokeStyle = style.stroke || 'transparent'; ctx.lineWidth = style.strokeWidth || 0; - for (const glyph of measured.glyphs) { ctx.save(); ctx.translate(glyph.x + (measured.background?.paddingX || 0), (style.lineHeight || style.fontSize || 48) / 2 + (measured.background?.paddingY || 0)); ctx.rotate(glyph.rotation * Math.PI / 180); if (style.strokeWidth) ctx.strokeText(glyph.glyph, 0, 0); ctx.fillText(glyph.glyph, 0, 0); ctx.restore(); } - } - drawStretchBackground(box, style = {}) { - const ctx = this.ctx; ctx.save(); ctx.fillStyle = style.fill || '#ffe36e'; - const image = style.assetId && this.assets[style.assetId]; - if (image && style.capInsets) this.drawNineSlice(image, box.width, box.height, style.capInsets); - else if (style.shape === 'pill') { ctx.beginPath(); if (ctx.roundRect) ctx.roundRect(0, 0, box.width, box.height, box.height / 2); else ctx.rect(0, 0, box.width, box.height); ctx.fill(); } - else ctx.fillRect(0, 0, box.width, box.height); - ctx.restore(); - } - drawNineSlice(image, width, height, cap) { - const ctx = this.ctx, left = cap.left, right = cap.right, top = cap.top, bottom = cap.bottom; - const sx = [0, left, image.width - right], sy = [0, top, image.height - bottom]; - const sw = [left, image.width - left - right, right], sh = [top, image.height - top - bottom, bottom]; - const dx = [0, left, width - right], dy = [0, top, height - bottom]; - const dw = [left, Math.max(0, width - left - right), right], dh = [top, Math.max(0, height - top - bottom), bottom]; - for (let y = 0; y < 3; y += 1) for (let x = 0; x < 3; x += 1) ctx.drawImage(image, sx[x], sy[y], sw[x], sh[y], dx[x], dy[y], dw[x], dh[y]); - } - drawImage(layer) { const image = this.assets[layer.assetId]; if (image) this.ctx.drawImage(image, 0, 0, layer.width || image.width, layer.height || image.height); } - drawBrush(layer) { - const ctx = this.ctx; - for (const stroke of layer.strokes || []) { - const points = stroke.points || []; if (!points.length) continue; - if (stroke.engine === 'stamp') { for (let i = 0; i < points.length; i += Math.max(1, stroke.step || 3)) { const p = points[i], stamp = stroke.assetId && this.assets[stroke.assetId]; ctx.save(); ctx.translate(p.x,p.y); ctx.rotate((p.angle || 0)*Math.PI/180); if (stamp) { const size = p.size || stroke.size || 28; ctx.drawImage(stamp,-size/2,-size/2,size,size); } else { ctx.font = `${stroke.size || 28}px sans-serif`; ctx.fillText(stroke.stampGlyph || '✿',0,0); } ctx.restore(); } } - else { ctx.beginPath(); ctx.strokeStyle = stroke.color || '#111'; ctx.globalAlpha *= stroke.opacity ?? 1; ctx.lineWidth = stroke.size || 8; ctx.lineCap = 'round'; ctx.moveTo(points[0].x,points[0].y); for (const p of points.slice(1)) ctx.lineTo(p.x,p.y); ctx.stroke(); } - } - } -} diff --git a/src/renderer.ts b/src/renderer.ts new file mode 100644 index 0000000..6b2e7c9 --- /dev/null +++ b/src/renderer.ts @@ -0,0 +1,17 @@ +import { layoutDecoratedText } from './index.js'; +import type { PosterDocument, PosterLayer, TextBackgroundStyle } from './types.js'; + +export type AssetMap = Record; +type AnyRecord = Record; + +export class CanvasRenderer { + constructor(public readonly ctx: CanvasRenderingContext2D, public readonly assets: AssetMap = {}) {} + render(document: PosterDocument): void { const{ctx}=this;ctx.save();ctx.fillStyle=document.canvas.background||'#fff';ctx.fillRect(0,0,document.canvas.width,document.canvas.height);for(const layer of document.layers)this.renderLayer(layer,document);ctx.restore(); } + renderLayer(layer:PosterLayer,document:PosterDocument):void {if(!layer.visible)return;const ctx=this.ctx,t=layer.transform;ctx.save();ctx.globalAlpha*=layer.opacity;ctx.globalCompositeOperation=layer.blendMode;ctx.translate(t.x,t.y);ctx.rotate(t.rotation*Math.PI/180);ctx.scale(t.scaleX,t.scaleY);if(layer.type==='background'||layer.type==='vector')this.drawVector(layer as PosterLayer&AnyRecord);if(layer.type==='text')this.drawText(layer as PosterLayer&AnyRecord,document);if(layer.type==='image'||layer.type==='sticker')this.drawImage(layer as PosterLayer&AnyRecord);if(layer.type==='brush')this.drawBrush(layer as PosterLayer&AnyRecord);for(const child of layer.children??[])this.renderLayer(child,document);ctx.restore();} + drawVector(layer:PosterLayer&AnyRecord):void {for(const command of layer.commands??[]){const ctx=this.ctx;ctx.beginPath();ctx.strokeStyle=command.stroke??'transparent';ctx.fillStyle=command.fill??'transparent';ctx.lineWidth=command.strokeWidth??1;if(command.kind==='line'){ctx.moveTo(command.x1,command.y1);ctx.lineTo(command.x2,command.y2);ctx.stroke();}if(command.kind==='rect'){command.radius?ctx.roundRect(command.x,command.y,command.width,command.height,command.radius):ctx.rect(command.x,command.y,command.width,command.height);if(command.fill)ctx.fill();if(command.stroke)ctx.stroke();}if(command.kind==='text'){ctx.font=command.font??'24px sans-serif';ctx.textAlign=command.align??'left';ctx.fillText(command.text,command.x,command.y);}}} + drawText(layer:PosterLayer&AnyRecord,document:PosterDocument):void {const ctx=this.ctx,style=(layer.style??{}) as AnyRecord;ctx.font=`${style.fontWeight??700} ${style.fontSize??48}px ${style.fontFamily??'sans-serif'}`;const measured=layoutDecoratedText(layer.text??'',glyph=>ctx.measureText(glyph).width,{...style,documentSeed:document.seed,layerId:layer.id});if(measured.background)this.drawStretchBackground(measured.background,style.background);ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=style.fill??'#111';ctx.strokeStyle=style.stroke??'transparent';ctx.lineWidth=style.strokeWidth??0;for(const glyph of measured.glyphs){ctx.save();ctx.translate(glyph.x+(measured.background?.paddingX??0),(style.lineHeight??style.fontSize??48)/2+(measured.background?.paddingY??0));ctx.rotate(glyph.rotation*Math.PI/180);if(style.strokeWidth)ctx.strokeText(glyph.glyph,0,0);ctx.fillText(glyph.glyph,0,0);ctx.restore();}} + drawStretchBackground(box:{width:number;height:number},style:TextBackgroundStyle={}):void {const ctx=this.ctx;ctx.save();ctx.fillStyle=style.fill??'#ffe36e';const image=style.assetId?this.assets[style.assetId]:undefined;if(image&&style.capInsets)this.drawNineSlice(image,box.width,box.height,style.capInsets);else if(style.shape==='pill'){ctx.beginPath();ctx.roundRect(0,0,box.width,box.height,box.height/2);ctx.fill();}else ctx.fillRect(0,0,box.width,box.height);ctx.restore();} + drawNineSlice(image:AssetMap[string],width:number,height:number,cap:{left:number;right:number;top:number;bottom:number}):void {const{ctx}=this,left=cap.left,right=cap.right,top=cap.top,bottom=cap.bottom,sx=[0,left,image.width-right],sy=[0,top,image.height-bottom],sw=[left,image.width-left-right,right],sh=[top,image.height-top-bottom,bottom],dx=[0,left,width-right],dy=[0,top,height-bottom],dw=[left,Math.max(0,width-left-right),right],dh=[top,Math.max(0,height-top-bottom),bottom];for(let y=0;y<3;y+=1)for(let x=0;x<3;x+=1)ctx.drawImage(image,sx[x]!,sy[y]!,sw[x]!,sh[y]!,dx[x]!,dy[y]!,dw[x]!,dh[y]!);} + drawImage(layer:PosterLayer&AnyRecord):void {const image=this.assets[layer.assetId];if(image)this.ctx.drawImage(image,0,0,layer.width??image.width,layer.height??image.height);} + drawBrush(layer:PosterLayer&AnyRecord):void {const ctx=this.ctx;for(const stroke of layer.strokes??[]){const points=stroke.points??[];if(!points.length)continue;if(stroke.engine==='stamp'){for(let index=0;index; [key: string]: unknown } +export interface PosterLayer { + id: string; type: LayerType; name: string; visible: boolean; locked: boolean; opacity: number; + blendMode: GlobalCompositeOperation; transform: Transform; effects: unknown[]; tags: string[]; + children?: PosterLayer[]; [key: string]: unknown; +} +export interface PosterDocument { + schema: 'entroia.poster-document'; version: 1; id: string; seed: string; canvas: CanvasSpec; + layers: PosterLayer[]; bindings: DocumentBindings; meta: Record & { title: string; createdAt: string }; +} +export interface CreateDocumentOptions { + id?: string; seed?: string; width?: number; height?: number; background?: string; + layers?: PosterLayer[]; bindings?: DocumentBindings; title?: string; meta?: Record; +} +export interface LayerProps { + id?: string; name?: string; visible?: boolean; locked?: boolean; opacity?: number; + blendMode?: GlobalCompositeOperation; transform?: Transform; effects?: unknown[]; tags?: string[]; + children?: PosterLayer[]; [key: string]: unknown; +} +export interface CatalogItem { id: string | number; tags?: string[]; [key: string]: unknown } +export interface CatalogQuery { all?: string[]; any?: string[]; none?: string[] } +export type LayoutSlot = readonly [number, number, number, number]; +export interface LayoutPreset { id: string; count: number; tags: string[]; slots: readonly LayoutSlot[] } +export interface BrushPreset { id: string; engine: 'stroke' | 'texture' | 'particles' | 'stamp'; [key: string]: string | number | undefined } +export interface TextBackgroundStyle { + paddingX?: number; paddingY?: number; resizeMode?: string; fill?: string; shape?: string; + assetId?: string; capInsets?: { left: number; right: number; top: number; bottom: number }; +} +export interface DecoratedTextStyle { + documentSeed?: string; layerId?: string; maxAngle?: number; rotationMode?: 'position' | 'glyph'; + letterSpacing?: number; lineHeight?: number; fontSize?: number; background?: TextBackgroundStyle; + [key: string]: unknown; +} +export interface GlyphRun { glyph: string; index: number; x: number; width: number; rotation: number } +export interface DecoratedTextLayout { + glyphs: GlyphRun[]; width: number; + background: (TextBackgroundStyle & { width: number; height: number; resizeMode: string; paddingX: number; paddingY: number }) | null; +} +export interface LayoutBox { index: number; x: number; y: number; width: number; height: number; mask: unknown } +export interface ValidationResult { valid: boolean; errors: string[] } diff --git a/src/xhs.ts b/src/xhs.ts new file mode 100644 index 0000000..52febf8 --- /dev/null +++ b/src/xhs.ts @@ -0,0 +1,2 @@ +export { createDocument, deterministicGlyphAngle, stableHash } from './index.js'; +export type { CreateDocumentOptions, PosterDocument } from './types.js'; diff --git a/test/editor.test.js b/test/editor.test.js deleted file mode 100644 index b2f8e36..0000000 --- a/test/editor.test.js +++ /dev/null @@ -1,26 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { applyLayout, createDocument, createLayer, deterministicGlyphAngle, layoutDecoratedText, resolveToolTargets, selectCatalogItems, validateDocument } from '../src/index.js'; - -test('document and layers validate', () => { - const doc=createDocument({layers:[createLayer('text',{text:'hello'})]}); - assert.equal(validateDocument(doc).valid,true); -}); -test('glyph rotations are deterministic and bounded', () => { - const input={documentSeed:'a',layerId:'b',index:3,glyph:'花',maxAngle:8}; - assert.equal(deterministicGlyphAngle(input),deterministicGlyphAngle(input)); - assert.ok(Math.abs(deterministicGlyphAngle(input))<=8); -}); -test('decorated background grows with text', () => { - const short=layoutDecoratedText('花',()=>20,{fontSize:40,background:{paddingX:10}}); - const long=layoutDecoratedText('花花花',()=>20,{fontSize:40,background:{paddingX:10}}); - assert.ok(long.background.width>short.background.width); -}); -test('tag rules and layouts are deterministic', () => { - assert.deepEqual(selectCatalogItems([{id:1,tags:['a','b']},{id:2,tags:['a','c']}],{all:['a'],none:['c']}).map(x=>x.id),[1]); - assert.equal(applyLayout('two-top-large').length,2); -}); -test('tool bindings target layers by id and dynamic tag', () => { - const doc=createDocument({layers:[createLayer('background',{id:'base',tags:['base-user-42']}),createLayer('image',{id:'photo',tags:['photo']})],bindings:{tools:{adjust:{layerIds:['base'],tags:['base-{user.id}']}}}}); - assert.deepEqual(resolveToolTargets(doc,'adjust',{user:{id:42}}).map(item=>item.id),['base']); -}); diff --git a/test/editor.test.mjs b/test/editor.test.mjs new file mode 100644 index 0000000..7e7e172 --- /dev/null +++ b/test/editor.test.mjs @@ -0,0 +1,9 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { applyLayout, createDocument, createLayer, deterministicGlyphAngle, layoutDecoratedText, resolveToolTargets, selectCatalogItems, validateDocument } from '../dist/index.js'; + +test('document and layers validate',()=>{const doc=createDocument({layers:[createLayer('text',{text:'hello'})]});assert.equal(validateDocument(doc).valid,true);}); +test('glyph rotations are deterministic and bounded',()=>{const input={documentSeed:'a',layerId:'b',index:3,glyph:'花',maxAngle:8};assert.equal(deterministicGlyphAngle(input),deterministicGlyphAngle(input));assert.ok(Math.abs(deterministicGlyphAngle(input))<=8);}); +test('decorated background grows with text',()=>{const short=layoutDecoratedText('花',()=>20,{fontSize:40,background:{paddingX:10}}),long=layoutDecoratedText('花花花',()=>20,{fontSize:40,background:{paddingX:10}});assert.ok(long.background&&short.background&&long.background.width>short.background.width);}); +test('tag rules and layouts are deterministic',()=>{assert.deepEqual(selectCatalogItems([{id:1,tags:['a','b']},{id:2,tags:['a','c']}],{all:['a'],none:['c']}).map(item=>item.id),[1]);assert.equal(applyLayout('two-top-large').length,2);}); +test('tool bindings target layers by id and dynamic tag',()=>{const doc=createDocument({layers:[createLayer('background',{id:'base',tags:['base-user-42']}),createLayer('image',{id:'photo',tags:['photo']})],bindings:{tools:{adjust:{layerIds:['base'],tags:['base-{user.id}']}}}});assert.deepEqual(resolveToolTargets(doc,'adjust',{user:{id:42}}).map(item=>item.id),['base']);}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..67c8f12 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022", "DOM"], + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +}