fix: build image libraries for baseline VPS CPUs
Some checks failed
Test and deploy / Verify production build (push) Failing after 15m48s
Some checks failed
Test and deploy / Verify production build (push) Failing after 15m48s
Replace the SIMD-dependent WebAssembly fallback with source-built libvips and Sharp. Pin the libvips archive checksum, use baseline x86-64 compiler flags, retain the native binding and runtime libraries, and check JPEG/PNG/WebP processing before compilation and in the runtime image. Lint, type checking, and content/security tests passed. Full Docker verification was stopped at the user request; production build and baseline CPU runtime verification remain pending.
This commit is contained in:
45
Dockerfile
45
Dockerfile
@@ -1,14 +1,33 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
FROM node:22-alpine AS dependencies
|
FROM node:22-alpine AS image-runtime
|
||||||
|
|
||||||
|
# Distribution libraries support baseline CPUs and dispatch optional SIMD at runtime.
|
||||||
|
RUN apk add --no-cache glib expat libjpeg-turbo libpng libwebp tiff lcms2 librsvg libstdc++
|
||||||
|
|
||||||
|
FROM image-runtime AS image-build
|
||||||
|
RUN apk add --no-cache python3 make g++ meson ninja pkgconf xz \
|
||||||
|
glib-dev expat-dev libjpeg-turbo-dev libpng-dev libwebp-dev tiff-dev lcms2-dev librsvg-dev
|
||||||
|
|
||||||
|
ADD --checksum=sha256:3c41e1d5458081bfa4a5bc54e116c46259c75c6760a18027764555632b9dda3e https://github.com/libvips/libvips/releases/download/v8.18.6/vips-8.18.6.tar.xz /tmp/vips.tar.xz
|
||||||
|
RUN if [ "$(uname -m)" = x86_64 ]; then export CFLAGS='-O2 -march=x86-64 -mtune=generic' CXXFLAGS='-O2 -march=x86-64 -mtune=generic'; fi \
|
||||||
|
&& mkdir /tmp/vips && tar -xJf /tmp/vips.tar.xz -C /tmp/vips --strip-components=1 \
|
||||||
|
&& meson setup /tmp/vips/build /tmp/vips --prefix=/opt/vips --libdir=lib \
|
||||||
|
--buildtype=release --auto-features=disabled \
|
||||||
|
-Dhighway=disabled -Dorc=disabled -Dcplusplus=true -Dexamples=false \
|
||||||
|
-Djpeg=enabled -Dpng=enabled -Dwebp=enabled -Dtiff=enabled -Dlcms=enabled -Drsvg=enabled -Dzlib=enabled \
|
||||||
|
&& meson compile -C /tmp/vips/build -j 2 \
|
||||||
|
&& meson install -C /tmp/vips/build
|
||||||
|
|
||||||
|
FROM image-build AS dependencies
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# npm may build the native SQLite binding when no matching prebuild is available.
|
|
||||||
RUN apk add --no-cache python3 make g++
|
|
||||||
|
|
||||||
ARG NPM_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
ARG NPM_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1 \
|
||||||
|
PKG_CONFIG_PATH=/opt/vips/lib/pkgconfig \
|
||||||
|
LD_LIBRARY_PATH=/opt/vips/lib \
|
||||||
|
npm_config_nodedir=/usr/local
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN --mount=type=cache,id=portfolio-npm-node22,target=/root/.npm,sharing=locked \
|
RUN --mount=type=cache,id=portfolio-npm-node22,target=/root/.npm,sharing=locked \
|
||||||
@@ -17,8 +36,12 @@ RUN --mount=type=cache,id=portfolio-npm-node22,target=/root/.npm,sharing=locked
|
|||||||
--replace-registry-host=registry.npmjs.org \
|
--replace-registry-host=registry.npmjs.org \
|
||||||
--include=optional
|
--include=optional
|
||||||
|
|
||||||
|
# Compile Sharp against our baseline libvips, not its x86-64-v2 prebuilt binary.
|
||||||
|
RUN if [ "$(uname -m)" = x86_64 ]; then export CFLAGS='-O2 -march=x86-64 -mtune=generic' CXXFLAGS='-O2 -march=x86-64 -mtune=generic'; fi \
|
||||||
|
&& npm explore sharp -- npm run build
|
||||||
|
|
||||||
COPY scripts/check-image-runtime.mjs ./scripts/check-image-runtime.mjs
|
COPY scripts/check-image-runtime.mjs ./scripts/check-image-runtime.mjs
|
||||||
RUN node scripts/check-image-runtime.mjs
|
RUN node scripts/check-image-runtime.mjs --require-source
|
||||||
|
|
||||||
FROM dependencies AS verification
|
FROM dependencies AS verification
|
||||||
|
|
||||||
@@ -29,14 +52,15 @@ RUN --mount=type=cache,id=portfolio-next-node22,target=/app/.next/cache,sharing=
|
|||||||
npm run build
|
npm run build
|
||||||
RUN npm test
|
RUN npm test
|
||||||
|
|
||||||
FROM node:22-alpine AS runtime
|
FROM image-runtime AS runtime
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
NEXT_TELEMETRY_DISABLED=1 \
|
NEXT_TELEMETRY_DISABLED=1 \
|
||||||
HOSTNAME=0.0.0.0 \
|
HOSTNAME=0.0.0.0 \
|
||||||
PORT=3000 \
|
PORT=3000 \
|
||||||
DATA_DIR=/data
|
DATA_DIR=/data \
|
||||||
|
LD_LIBRARY_PATH=/opt/vips/lib
|
||||||
|
|
||||||
RUN addgroup --system --gid 1001 nodejs \
|
RUN addgroup --system --gid 1001 nodejs \
|
||||||
&& adduser --system --uid 1001 nextjs
|
&& adduser --system --uid 1001 nextjs
|
||||||
@@ -44,6 +68,11 @@ RUN addgroup --system --gid 1001 nodejs \
|
|||||||
COPY --from=verification --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=verification --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
COPY --from=verification --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=verification --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
COPY --from=verification --chown=nextjs:nodejs /app/public ./public
|
COPY --from=verification --chown=nextjs:nodejs /app/public ./public
|
||||||
|
COPY --from=image-build /opt/vips/lib /opt/vips/lib
|
||||||
|
# Explicitly retain the custom binding even if Next's file tracer misses it.
|
||||||
|
COPY --from=dependencies --chown=nextjs:nodejs /app/node_modules/sharp/src/build/Release/*.node ./node_modules/sharp/src/build/Release/
|
||||||
|
COPY scripts/check-image-runtime.mjs ./scripts/check-image-runtime.mjs
|
||||||
|
RUN node scripts/check-image-runtime.mjs --require-source
|
||||||
RUN mkdir -p .next/cache /data && chown -R nextjs:nodejs .next/cache /data
|
RUN mkdir -p .next/cache /data && chown -R nextjs:nodejs .next/cache /data
|
||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|||||||
@@ -57,10 +57,12 @@ Drizzle Kit, npm installs, or a compiler at runtime. Startup applies the committ
|
|||||||
SQL migrations and seeds samples only when the database is empty. `/api/health`
|
SQL migrations and seeds samples only when the database is empty. `/api/health`
|
||||||
checks database readiness without rendering the portfolio.
|
checks database readiness without rendering the portfolio.
|
||||||
|
|
||||||
Sharp uses native image processing where supported and includes a WebAssembly
|
The Docker build compiles libvips and Sharp from source for baseline CPUs. This
|
||||||
fallback for servers without the required x86-64 CPU features. The fallback
|
supports VPS hosts that lack the features required by Sharp's prebuilt native
|
||||||
preserves uploads and image optimization, but can use more CPU time. Keep
|
and WebAssembly binaries. The first build takes longer; Docker caches the image
|
||||||
`@img/sharp-wasm32` at the same version as `sharp` when upgrading.
|
libraries for later application changes. Image processing is checked before
|
||||||
|
Next.js compilation and again in the final runtime image. When upgrading Sharp,
|
||||||
|
keep the libvips source version in the Dockerfile at or above Sharp's minimum.
|
||||||
|
|
||||||
Use one application instance with a local filesystem. Do not mount the SQLite
|
Use one application instance with a local filesystem. Do not mount the SQLite
|
||||||
database over NFS/SMB, use an ephemeral serverless disk, or run multiple replicas
|
database over NFS/SMB, use an ephemeral serverless disk, or run multiple replicas
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ const nextConfig = {
|
|||||||
outputFileTracingIncludes: {
|
outputFileTracingIncludes: {
|
||||||
'/*': [
|
'/*': [
|
||||||
'./migrations/**/*',
|
'./migrations/**/*',
|
||||||
'./node_modules/@img/sharp-wasm32/**/*',
|
'./node_modules/sharp/src/build/Release/*.node',
|
||||||
'./node_modules/@emnapi/runtime/**/*',
|
|
||||||
'./node_modules/tslib/**/*',
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
outputFileTracingExcludes: {
|
outputFileTracingExcludes: {
|
||||||
|
|||||||
199
package-lock.json
generated
199
package-lock.json
generated
@@ -8,7 +8,6 @@
|
|||||||
"name": "portfolio-v2-antigravity",
|
"name": "portfolio-v2-antigravity",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@img/sharp-wasm32": "0.35.4",
|
|
||||||
"better-auth": "^1.7.5",
|
"better-auth": "^1.7.5",
|
||||||
"better-sqlite3": "^13.0.3",
|
"better-sqlite3": "^13.0.3",
|
||||||
"drizzle-orm": "^0.45.2",
|
"drizzle-orm": "^0.45.2",
|
||||||
@@ -33,6 +32,8 @@
|
|||||||
"esbuild": "^0.28.2",
|
"esbuild": "^0.28.2",
|
||||||
"eslint": "^9.39.5",
|
"eslint": "^9.39.5",
|
||||||
"eslint-config-next": "^16.3.4",
|
"eslint-config-next": "^16.3.4",
|
||||||
|
"node-addon-api": "8.9.2",
|
||||||
|
"node-gyp": "12.4.0",
|
||||||
"tsx": "^4.23.13",
|
"tsx": "^4.23.13",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.7.3"
|
||||||
}
|
}
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@emnapi/runtime/-/runtime-1.11.3.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@emnapi/runtime/-/runtime-1.11.3.tgz",
|
||||||
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
|
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
@@ -1610,6 +1612,7 @@
|
|||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
|
||||||
"integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
|
"integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
|
||||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||||
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emnapi/runtime": "^1.11.3"
|
"@emnapi/runtime": "^1.11.3"
|
||||||
},
|
},
|
||||||
@@ -1696,6 +1699,19 @@
|
|||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@isaacs/fs-minipass": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^7.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.13",
|
"version": "0.3.13",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
@@ -2734,6 +2750,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/abbrev": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/abbrev/-/abbrev-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.18.0",
|
"version": "8.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
|
||||||
@@ -3348,6 +3374,16 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chownr": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/chownr/-/chownr-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/client-only": {
|
"node_modules/client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/client-only/-/client-only-0.0.1.tgz",
|
||||||
@@ -3730,6 +3766,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/env-paths": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/env-paths/-/env-paths-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.24.2",
|
"version": "1.24.2",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/es-abstract/-/es-abstract-1.24.2.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/es-abstract/-/es-abstract-1.24.2.tgz",
|
||||||
@@ -4486,6 +4532,13 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/exponential-backoff": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@@ -4838,6 +4891,13 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/has-bigints": {
|
"node_modules/has-bigints": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/has-bigints/-/has-bigints-1.1.0.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/has-bigints/-/has-bigints-1.1.0.tgz",
|
||||||
@@ -5728,6 +5788,29 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minipass": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/minipass/-/minipass-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/minizlib/-/minizlib-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -5882,6 +5965,57 @@
|
|||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gyp": {
|
||||||
|
"version": "12.4.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/node-gyp/-/node-gyp-12.4.0.tgz",
|
||||||
|
"integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"env-paths": "^2.2.0",
|
||||||
|
"exponential-backoff": "^3.1.1",
|
||||||
|
"graceful-fs": "^4.2.6",
|
||||||
|
"nopt": "^9.0.0",
|
||||||
|
"proc-log": "^6.0.0",
|
||||||
|
"semver": "^7.3.5",
|
||||||
|
"tar": "^7.5.4",
|
||||||
|
"tinyglobby": "^0.2.12",
|
||||||
|
"undici": "^6.25.0",
|
||||||
|
"which": "^6.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-gyp": "bin/node-gyp.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-gyp/node_modules/isexe": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/isexe/-/isexe-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-gyp/node_modules/which": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/which/-/which-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-which": "bin/which.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.54",
|
"version": "2.0.54",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/node-releases/-/node-releases-2.0.54.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/node-releases/-/node-releases-2.0.54.tgz",
|
||||||
@@ -5892,6 +6026,22 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/nopt/-/nopt-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -6220,6 +6370,16 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proc-log": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/proc-log/-/proc-log-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@@ -6948,6 +7108,33 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar": {
|
||||||
|
"version": "7.5.22",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/tar/-/tar-7.5.22.tgz",
|
||||||
|
"integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@isaacs/fs-minipass": "^4.0.0",
|
||||||
|
"chownr": "^3.0.0",
|
||||||
|
"minipass": "^7.1.2",
|
||||||
|
"minizlib": "^3.1.0",
|
||||||
|
"yallist": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tar/node_modules/yallist": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/yallist/-/yallist-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/three": {
|
"node_modules/three": {
|
||||||
"version": "0.180.0",
|
"version": "0.180.0",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/three/-/three-0.180.0.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/three/-/three-0.180.0.tgz",
|
||||||
@@ -7227,6 +7414,16 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici": {
|
||||||
|
"version": "6.28.1",
|
||||||
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/undici/-/undici-6.28.1.tgz",
|
||||||
|
"integrity": "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
"maintenance": "tsx --env-file-if-exists=.env scripts/maintenance.ts"
|
"maintenance": "tsx --env-file-if-exists=.env scripts/maintenance.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@img/sharp-wasm32": "0.35.4",
|
|
||||||
"better-auth": "^1.7.5",
|
"better-auth": "^1.7.5",
|
||||||
"better-sqlite3": "^13.0.3",
|
"better-sqlite3": "^13.0.3",
|
||||||
"drizzle-orm": "^0.45.2",
|
"drizzle-orm": "^0.45.2",
|
||||||
@@ -41,6 +40,8 @@
|
|||||||
"esbuild": "^0.28.2",
|
"esbuild": "^0.28.2",
|
||||||
"eslint": "^9.39.5",
|
"eslint": "^9.39.5",
|
||||||
"eslint-config-next": "^16.3.4",
|
"eslint-config-next": "^16.3.4",
|
||||||
|
"node-addon-api": "8.9.2",
|
||||||
|
"node-gyp": "12.4.0",
|
||||||
"tsx": "^4.23.13",
|
"tsx": "^4.23.13",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.7.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,24 +2,19 @@ import { createRequire } from 'node:module';
|
|||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
// Check the fallback directly: Sharp 0.35.4 can hide its load error behind
|
|
||||||
// an unrelated `err.code.endsWith` exception on unsupported native CPUs.
|
|
||||||
try {
|
|
||||||
require('@img/sharp-wasm32/sharp.node');
|
|
||||||
console.log('Sharp WebAssembly binding loaded.');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Sharp WebAssembly binding failed to load:', error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { default: sharp } = await import('sharp');
|
const { default: sharp } = await import('sharp');
|
||||||
const input = await sharp({ create: { width: 8, height: 8, channels: 3, background: '#336699' } }).webp().toBuffer();
|
if (process.argv.includes('--require-source') && !Object.keys(require.cache).some(file => /sharp[/\\]src[/\\]build[/\\]Release[/\\].+\.node$/.test(file))) {
|
||||||
const metadata = await sharp(input).metadata();
|
throw new Error('Expected the source-built Sharp binding; refusing to use a prebuilt fallback');
|
||||||
if (metadata.format !== 'webp' || metadata.width !== 8 || metadata.height !== 8) {
|
|
||||||
throw new Error('Image encode/decode verification failed');
|
|
||||||
}
|
}
|
||||||
console.log('Sharp ESM import and image processing passed.');
|
for (const format of ['jpeg', 'png', 'webp']) {
|
||||||
|
const input = await sharp({ create: { width: 8, height: 8, channels: 3, background: '#336699' } }).toFormat(format).toBuffer();
|
||||||
|
const metadata = await sharp(input).metadata();
|
||||||
|
if (metadata.format !== format || metadata.width !== 8 || metadata.height !== 8) {
|
||||||
|
throw new Error(`${format} encode/decode verification failed`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`Sharp ${sharp.versions.sharp}, libvips ${sharp.versions.vips}: JPEG, PNG and WebP passed.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Sharp image runtime failed:', error);
|
console.error('Sharp image runtime failed:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { spawnSync } from 'node:child_process';
|
|
||||||
import test from 'node:test';
|
|
||||||
|
|
||||||
test('image processing falls back to WebAssembly without native CPU support', () => {
|
|
||||||
// Isolate the native-binding simulation from the rest of the test process.
|
|
||||||
const result = spawnSync(process.execPath, ['-e', `
|
|
||||||
const Module = require('node:module');
|
|
||||||
const original = Module._load;
|
|
||||||
Module._load = function (id, ...args) {
|
|
||||||
if (id.endsWith('.node') && !id.includes('wasm32')) {
|
|
||||||
const error = new Error('Simulated unavailable native binding');
|
|
||||||
error.code = 'MODULE_NOT_FOUND';
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return original.call(this, id, ...args);
|
|
||||||
};
|
|
||||||
const sharp = require('sharp');
|
|
||||||
(async () => {
|
|
||||||
const image = await sharp({ create: { width: 8, height: 8, channels: 3, background: '#336699' } }).webp().toBuffer();
|
|
||||||
const metadata = await sharp(image).metadata();
|
|
||||||
if (metadata.format !== 'webp' || metadata.width !== 8 || metadata.height !== 8) throw new Error('Image round trip failed');
|
|
||||||
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
||||||
`], { encoding: 'utf8', timeout: 30000 });
|
|
||||||
assert.equal(result.status, 0, result.stderr || result.error?.message);
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user