feat: add backend container delivery #20

Merged
ksv741 merged 2 commits from codex/containerized-deployment into develop 2026-09-16 11:19:21 +03:00
9 changed files with 216 additions and 138 deletions
+13
View File
@@ -0,0 +1,13 @@
**
!package.json
!package-lock.json
!.nvmrc
!nest-cli.json
!tsconfig*.json
!prisma.config.ts
!prisma/
!prisma/schema.prisma
!prisma/migrations/
!prisma/migrations/**
!src/
!src/**
+59
View File
@@ -0,0 +1,59 @@
name: Backend image
on:
push:
tags: ['v*']
concurrency:
group: backend-image-publication
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
env:
IMAGE: git.personal.ksv741.ru/wealthledger/backend
RELEASE_TAG: ${{ gitea.ref_name }}
REVISION: ${{ gitea.sha }}
DOCKER_CONFIG: ${{ runner.temp }}/wealthledger-backend-docker
steps:
- uses: https://github.com/actions/checkout@v4
- uses: https://github.com/actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: Validate release version
run: |
node --input-type=commonjs -e 'const p = require("./package.json"); const l = require("./package-lock.json"); if (!/^v\d+\.\d+\.\d+$/.test(process.env.RELEASE_TAG) || process.env.RELEASE_TAG !== `v${p.version}` || l.version !== p.version || l.packages[""].version !== p.version) process.exit(1)'
test "$(git rev-parse HEAD)" = "$REVISION"
docker version
docker buildx version
- run: npm ci
- run: npx --no-install prisma generate
- run: npm run verify
- run: npm run test:e2e
- name: Login with dedicated package writer
env:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
test -n "$REGISTRY_USERNAME" && test -n "$REGISTRY_TOKEN"
install -d -m 700 "$DOCKER_CONFIG"
printf '%s' "$REGISTRY_TOKEN" | docker login git.personal.ksv741.ru --username "$REGISTRY_USERNAME" --password-stdin
- name: Publish immutable release tag
run: |
VERSION="${RELEASE_TAG#v}"
if docker manifest inspect "$IMAGE:$VERSION" >/dev/null 2>manifest-error.log; then
echo 'Refusing to overwrite an existing release tag'; exit 1
fi
if ! grep -Eq 'manifest unknown|no such manifest' manifest-error.log; then
echo 'Cannot prove tag absence; refusing publication'; exit 1
fi
docker buildx build --target production --build-arg VERSION="$VERSION" --build-arg REVISION="$REVISION" --tag "$IMAGE:$VERSION" --metadata-file image-metadata.json --push .
DIGEST="$(node --input-type=commonjs -p 'require("./image-metadata.json")["containerimage.digest"]')"
docker pull "$IMAGE@$DIGEST"
test "$(docker image inspect "$IMAGE@$DIGEST" --format '{{index .Config.Labels "org.opencontainers.image.revision"}}')" = "$REVISION"
npm run test:container -- "$IMAGE@$DIGEST"
printf 'Backend image: %s@%s\n' "$IMAGE" "$DIGEST"
- name: Remove registry session
if: always()
run: rm -rf "$DOCKER_CONFIG"
+2
View File
@@ -15,3 +15,5 @@ dfca11c81f7e6dadce3ca6f961d265c5e3f8ff8e:src/tbank/portfolio.service.spec.ts:gen
# Historical CircleCI badge copied verbatim from nestjs/typescript-starter README.
# Verified against https://github.com/nestjs/typescript-starter/blob/master/README.md
f8dcfb71c1767696a9aa6741ddaf6575416c901e:README.md:generic-api-key:5
# Container smoke fixture: same deterministic 32-byte test key, never a runtime secret.
1a6be432f24f757728a03415051392069112af1f:test/container-smoke.cjs:generic-api-key:14
+41
View File
@@ -0,0 +1,41 @@
FROM node:24.21.0-bookworm-slim AS base
WORKDIR /app
FROM base AS dependencies
# Native SQLite/bcrypt builds happen only here; compilers stay out of runtime.
RUN apt-get update && apt-get install -y --no-install-recommends python3=3.11.2-1+b1 make=4.3-4.1 g++=4:12.2.0-3 ca-certificates=20250419~deb12u1 openssl=3.0.20-1~deb12u2 && rm -rf /var/lib/apt/lists/*
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npx --no-install prisma generate
FROM dependencies AS development
# Nest watch uses ps to stop the previous process tree before restarting.
RUN apt-get update && apt-get install -y --no-install-recommends procps=2:4.0.2-3 && rm -rf /var/lib/apt/lists/*
RUN mkdir /data && chown node:node /data && chown -R node:node /app
USER node
ENV NODE_ENV=development DATABASE_URL=file:/data/wealthledger.db
EXPOSE 3000
CMD ["npm", "run", "start:dev"]
FROM dependencies AS build
RUN npm run build && npm prune --omit=dev --omit=optional
FROM base AS production
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates=20250419~deb12u1 openssl=3.0.20-1~deb12u2 && rm -rf /var/lib/apt/lists/* \
&& mkdir /data && chown node:node /data
ARG VERSION
ARG REVISION
LABEL org.opencontainers.image.version=$VERSION \
org.opencontainers.image.revision=$REVISION
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/prisma ./prisma
COPY --from=build /app/prisma.config.ts ./prisma.config.ts
COPY --from=build /app/package.json /app/package-lock.json ./
ENV NODE_ENV=production DATABASE_URL=file:/data/wealthledger.db
USER node
EXPOSE 3000
CMD ["node", "dist/src/main.js"]
HEALTHCHECK --interval=10s --timeout=3s --start-period=20s --retries=6 \
CMD node -e 'fetch("http://127.0.0.1:3000/api/v1/auth/me").then(async r=>{await r.arrayBuffer();process.exit(r.status===401?0:1)}).catch(()=>process.exit(1))'
+27
View File
@@ -22,6 +22,33 @@ npm run start:dev
T-Bank token в `.env`: он вводится пользователем и хранится в зашифрованном
виде.
## Контейнерный image
`Dockerfile` имеет targets `development` (Nest watch, generated Prisma client и Linux native modules)
и `production` (compiled Node process от UID 1000, Prisma CLI/migrations и T-Bank proto assets). Node соответствует `.nvmrc`.
Build context ограничен `.dockerignore`; `.env`, local DB и host node_modules
не попадают в image.
```bash
docker build --target development -t wealthledger-backend:development .
docker build --target production -t wealthledger-backend:production .
nvm use --silent
npm run test:container -- wealthledger-backend:production
```
Gitea `image.yml` запускается на защищённом release tag `vX.Y.Z`, проверяет
package/lock version и canonical gates. Runner `ubuntu-latest` должен иметь
Docker/buildx. Dedicated package writer задаётся Actions secrets
`REGISTRY_USERNAME`/`REGISTRY_TOKEN`; server использует отдельную read-only
identity. Публикуются semver tags, без `latest`; существующие
release tags не перезаписываются. Production выбирает опубликованный version tag.
Compose topology принадлежит workspace, эксплуатационная последовательность —
[документация](https://git.personal.ksv741.ru/WealthLedger/docs),
страница `architecture/deployment.md`.
Проверка публикации требует настроенного registry/runner; локальный build
не доказывает package permissions.
## Проверка
Единый completion gate не изменяет versioned-файлы:
+1 -129
View File
@@ -31,6 +31,7 @@
"passport": "0.7.0",
"passport-jwt": "4.0.1",
"pino-http": "11.0.0",
"prisma": "7.10.0",
"reflect-metadata": "0.2.2",
"rxjs": "7.8.2"
},
@@ -57,7 +58,6 @@
"globals": "17.12.0",
"jest": "30.5.1",
"prettier": "3.9.6",
"prisma": "7.10.0",
"release-it": "21.0.2",
"source-map-support": "0.5.21",
"supertest": "7.2.2",
@@ -1280,14 +1280,12 @@
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.4.3.tgz",
"integrity": "sha512-ichuWTgtd4mOM1G4SpyGJa5trT03lWbMypDV0fUXUCXg5hiHqVAz/bZyV68NqmkLB7WcYmj1RMJVSp8HV/v/ZQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@electric-sql/pglite-socket": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.1.3.tgz",
"integrity": "sha512-LAciWM0M1dCL8hlsxu2venbVZcdxema0BtDfpWYVqr+Y468UADw0pFWidhKw1M8sfJ8rdLT71tjMmnirf/IZRQ==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"pglite-server": "dist/scripts/server.js"
@@ -1300,7 +1298,6 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.3.3.tgz",
"integrity": "sha512-AlzLJTRJ8+UFgK8CmxIpyIpJ0+YaFw02IiOSdYrqxwPXdSyeIShz8aa9Tq+tYFXdPwcaMp/Fc80mQZ1dkOQ/wg==",
"devOptional": true,
"license": "Apache-2.0",
"peerDependencies": {
"@electric-sql/pglite": "0.4.3"
@@ -3872,7 +3869,6 @@
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.10.0.tgz",
"integrity": "sha512-Rcg828gIRE3HOQ3pOATFjV5d/P0U9OIobxhd/IMxlfWjA4vru0eGwb0AIwFw0rmcLMVShohZYWPixVxkBHsxUA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"c12": "3.3.4",
@@ -3891,7 +3887,6 @@
"version": "0.24.17",
"resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.24.17.tgz",
"integrity": "sha512-UvdZzmpFwknnfreh6Jije84ekkYGPYEJhXG1tFzCsCfQyzJifrOo38eZc0qajzvaC6OLUOrN9ML5XfCnEZL9DA==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"@electric-sql/pglite": "0.4.3",
@@ -3924,7 +3919,6 @@
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.10.0.tgz",
"integrity": "sha512-KNumN6NHFwybvfdYzTee9pqwx5PvknpWAaHn6L5NsbrKdl+SQrsVZs9opKs6U6SAsvB26HDt3WybRjOhgoWOYQ==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3938,14 +3932,12 @@
"version": "7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3.tgz",
"integrity": "sha512-8OJ6RuZTZ06eFUOtBwxVmv8XMmOW6HWN5F+uxUbZkGxR0Bfab1dfAdXaHPmR5mb59E+fmUo8IOzXlbLY1SClbw==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/engines/node_modules/@prisma/get-platform": {
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.10.0.tgz",
"integrity": "sha512-0bra1LFYi8xNw0yqV62bHJNQk4BKleOngZiqPWIQc7a3+9q6rqsnqJ15BuepP8943PFMvtmgnP52juZsyYkA6w==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "7.10.0"
@@ -3955,7 +3947,6 @@
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.10.0.tgz",
"integrity": "sha512-Zqyu8DY14t6W/xwmAxUYWCXtHrvQnSvT644EAZSsdM8NSmCS74vJJbBKdVsK3ucFpnUWkEpbO1a0CxJXrg130g==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "7.10.0",
@@ -3967,7 +3958,6 @@
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.10.0.tgz",
"integrity": "sha512-0bra1LFYi8xNw0yqV62bHJNQk4BKleOngZiqPWIQc7a3+9q6rqsnqJ15BuepP8943PFMvtmgnP52juZsyYkA6w==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "7.10.0"
@@ -3977,7 +3967,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz",
"integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "7.2.0"
@@ -3987,21 +3976,18 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz",
"integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/query-plan-executor": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz",
"integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/streams-local": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@prisma/streams-local/-/streams-local-0.1.11.tgz",
"integrity": "sha512-0TcebL559MByKqTJ+SsrFIEg228iw8UCVRFckzgfRSiJqczhs+MuAgWOF9lnOIV/IVqvu+KMnFTH0eDeTQMpUg==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"ajv": "^8.12.0",
@@ -4018,7 +4004,6 @@
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -4035,14 +4020,12 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@prisma/studio-core": {
"version": "0.33.0",
"resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.33.0.tgz",
"integrity": "sha512-V2fX/nKEymNTrHXwfP26PGjoLStO35Ogu+ex7CFJbLrMYEcZxxZpiSNOs7px23Hk5mzLWvM5RsqG6Ka+rha+wg==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@radix-ui/react-toggle": "1.1.10",
@@ -4128,14 +4111,12 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -4151,7 +4132,6 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
@@ -4175,7 +4155,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
@@ -4194,7 +4173,6 @@
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz",
"integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
@@ -4220,7 +4198,6 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-effect-event": "0.0.2",
@@ -4240,7 +4217,6 @@
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
@@ -4259,7 +4235,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -4564,35 +4539,30 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz",
"integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/d3-delaunay": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz",
"integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/d3-format": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz",
"integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/d3-geo": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
@@ -4602,7 +4572,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
@@ -4612,14 +4581,12 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
@@ -4629,7 +4596,6 @@
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
@@ -4639,14 +4605,12 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz",
"integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/d3-time-format": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz",
"integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/esrecurse": {
@@ -4692,7 +4656,6 @@
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/http-errors": {
@@ -4761,7 +4724,6 @@
"version": "4.17.25",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz",
"integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/methods": {
@@ -4843,7 +4805,6 @@
"version": "19.2.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
@@ -5560,7 +5521,6 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/curve/-/curve-4.0.1-alpha.0.tgz",
"integrity": "sha512-jRu61Uz274pV1zyioXmboyrLutYbnKsgjj4njSGCnhdXj5GkZvZbg+ThDb6oOzoAnJOBRLz4rzPlWvNJOzuVMg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@visx/vendor": "4.0.0-alpha.0"
@@ -5570,7 +5530,6 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/event/-/event-4.0.1-alpha.0.tgz",
"integrity": "sha512-EQqCMSv/s8NbFjo+hz3FKsvvYfP+2QslsFJ/24/O5l/W+7UC6J6aAvO0ujVwrTwdYbuQ+vhxKi1xdPdKR/qj1g==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/react": "*",
@@ -5581,7 +5540,6 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/grid/-/grid-4.0.1-alpha.0.tgz",
"integrity": "sha512-rycutGmTHO+znNdPumheWMglm7YfpffvRwUkVy5zy4WoORIuKTMkDxwnOzHG2xMxU3EE/YCd37xFV5AxA30yeg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/react": "*",
@@ -5600,7 +5558,6 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/group/-/group-4.0.1-alpha.0.tgz",
"integrity": "sha512-V19l7iQ7jccBv8kao/EByuI6o4xtxzzLV9nqVI1hRvmdzTVsuLpqlwzYCZUXJaTVvUWf8s4D2SQFjGkj/Nw+0w==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/react": "*",
@@ -5614,14 +5571,12 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/point/-/point-4.0.1-alpha.0.tgz",
"integrity": "sha512-ijTfr/Nx09f03vIj9nyTr3z4Xth4Y75427UaogJh6dnIRLMEFHQOwNu791sbfiNj0a+ZXuaE32h0vKrFe4/8Qg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/@visx/responsive": {
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-4.0.1-alpha.0.tgz",
"integrity": "sha512-o+1zGywQZY0+yOx3Iw87wc4bbPJRr/HnIukTwfOz4UVyj9pB1OQNVHB7OORO1+LBHJceWpB31co/ZV9KHncKrA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/lodash": "^4.17.13",
@@ -5636,7 +5591,6 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/scale/-/scale-4.0.1-alpha.0.tgz",
"integrity": "sha512-nzjeE87vFSAXGWFiiNfBpNLAf0Q8Qmf6syvKLjqNi4kGZkdhbUll3E/59YsgWXmjM8+llPLWzGsP+JPvo5eq1A==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@visx/vendor": "4.0.0-alpha.0"
@@ -5646,7 +5600,6 @@
"version": "4.0.1-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/shape/-/shape-4.0.1-alpha.0.tgz",
"integrity": "sha512-62QeiVNmPlterQGwhkEDcbq7M0MqY0lBsK5QKXtM9ZoPZWkuGV3aykA3+Xu20B2FAvyJq4LqJzBc7Sxr+EAdbA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/lodash": "^4.17.13",
@@ -5666,7 +5619,6 @@
"version": "4.0.0-alpha.0",
"resolved": "https://registry.npmjs.org/@visx/vendor/-/vendor-4.0.0-alpha.0.tgz",
"integrity": "sha512-6I+MuqXBcv9jnlcVowHoHKSdk9gXTWkHLKyqBwRWg7LY6A3Ei8SHfubpqGV5rBUSppxMq2RszPJUS6w+H0YgmQ==",
"devOptional": true,
"license": "MIT and ISC",
"dependencies": {
"@types/d3-array": "3.0.3",
@@ -5698,7 +5650,6 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz",
"integrity": "sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
@@ -6193,7 +6144,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 6.0.0"
@@ -6373,7 +6323,6 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/better-result/-/better-result-2.10.0.tgz",
"integrity": "sha512-oQhh0y1qo2/ZKdAAEvHZAqKKiHOFU5k/bW96fE2ScgQOVkJRiHwB+nOS1SgFsYqRlxMDWvefXi9Q3px7QvgNDw==",
"devOptional": true,
"license": "MIT"
},
"node_modules/better-sqlite3": {
@@ -6595,7 +6544,6 @@
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz",
"integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"chokidar": "^5.0.0",
@@ -6624,7 +6572,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"readdirp": "^5.0.0"
@@ -6640,7 +6587,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz",
"integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 20.19.0"
@@ -6871,7 +6817,6 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"devOptional": true,
"license": "MIT"
},
"node_modules/cli-cursor": {
@@ -7069,7 +7014,6 @@
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
"integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/content-disposition": {
@@ -7365,7 +7309,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -7408,14 +7351,12 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
@@ -7428,7 +7369,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"devOptional": true,
"license": "ISC",
"engines": {
"node": ">=12"
@@ -7438,7 +7378,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
"integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"delaunator": "5"
@@ -7451,7 +7390,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"devOptional": true,
"license": "ISC",
"engines": {
"node": ">=12"
@@ -7461,7 +7399,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
"integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"d3-array": "2.5.0 - 3"
@@ -7474,7 +7411,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
@@ -7487,7 +7423,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"devOptional": true,
"license": "ISC",
"engines": {
"node": ">=12"
@@ -7497,7 +7432,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"d3-array": "2.10.0 - 3",
@@ -7514,7 +7448,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"d3-path": "^3.1.0"
@@ -7527,7 +7460,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"d3-array": "2 - 3"
@@ -7540,7 +7472,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"d3-time": "1 - 3"
@@ -7636,7 +7567,6 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-8.0.2.tgz",
"integrity": "sha512-uqbvqLUMrc6p0MO+WBRtTxY55hmyh94WRwI5a++PZe54X+bfVh59FSN7uWCBCW1CCVjzjnrwzfI8zidE2obMMw==",
"devOptional": true,
"funding": [
{
"type": "ko-fi",
@@ -7699,7 +7629,6 @@
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
"integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/degenerator": {
@@ -7724,7 +7653,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",
"integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"robust-predicates": "^3.0.2"
@@ -7753,7 +7681,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/detect-libc": {
@@ -7918,7 +7845,6 @@
"version": "3.20.0",
"resolved": "https://registry.npmjs.org/effect/-/effect-3.20.0.tgz",
"integrity": "sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
@@ -7936,7 +7862,6 @@
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz",
"integrity": "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==",
"devOptional": true,
"license": "EPL-2.0"
},
"node_modules/emittery": {
@@ -7969,7 +7894,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
"integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=14"
@@ -8049,7 +7973,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
"integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
@@ -8628,14 +8551,12 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz",
"integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==",
"devOptional": true,
"license": "MIT"
},
"node_modules/fast-check": {
"version": "3.23.2",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz",
"integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==",
"devOptional": true,
"funding": [
{
"type": "individual",
@@ -8658,7 +8579,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
"devOptional": true,
"funding": [
{
"type": "individual",
@@ -8675,14 +8595,12 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
"integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"devOptional": true,
"license": "MIT"
},
"node_modules/fast-diff": {
@@ -8710,7 +8628,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
"integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"fast-decode-uri-component": "^1.0.1"
@@ -8743,7 +8660,6 @@
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
"integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
"devOptional": true,
"funding": [
{
"type": "github",
@@ -8863,7 +8779,6 @@
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.7.0.tgz",
"integrity": "sha512-f2JHn75x2JlwUwLenZypgczR7YWMb/uO9BvUXtus+JMgkbIkLADd38cI4EiV+OQqrGo1Zlq6V8wnqMJ8e62wUQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -8914,7 +8829,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.6",
@@ -9022,7 +8936,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"is-property": "^1.0.2"
@@ -9098,7 +9011,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz",
"integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==",
"devOptional": true,
"license": "MIT"
},
"node_modules/get-proto": {
@@ -9146,7 +9058,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz",
"integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==",
"devOptional": true,
"license": "MIT",
"bin": {
"giget": "dist/cli.mjs"
@@ -9304,21 +9215,18 @@
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"devOptional": true,
"license": "ISC"
},
"node_modules/grammex": {
"version": "3.1.13",
"resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.13.tgz",
"integrity": "sha512-LnPnhOBLEJEVKS8WFDVaA397L9Kq55Q9oSITJiVLHVdhAclfUkWzQv74KhvZHKL2Q09Pb1XdsrOsZ4LfTFFTEg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/graphmatch": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz",
"integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/handlebars": {
@@ -9630,7 +9538,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"devOptional": true,
"license": "ISC",
"engines": {
"node": ">=12"
@@ -9788,7 +9695,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
"devOptional": true,
"license": "MIT"
},
"node_modules/is-ssh": {
@@ -9847,7 +9753,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"devOptional": true,
"license": "ISC"
},
"node_modules/issue-parser": {
@@ -10528,7 +10433,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
"integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
"devOptional": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -10754,7 +10658,6 @@
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"devOptional": true,
"license": "MIT"
},
"node_modules/lodash.camelcase": {
@@ -10876,7 +10779,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz",
"integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==",
"devOptional": true,
"license": "MIT",
"engines": {
"bun": ">=1.0.0",
@@ -11353,7 +11255,6 @@
"version": "3.24.4",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.24.4.tgz",
"integrity": "sha512-A2olluVlj0mvgyIRRISMEzXc51m+21mRtcMVjJyIpt2GG98+XrC9m9HzsqcMsX2LcnfccJvY5NB22g8fENBnOA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"aws-ssl-profiles": "^1.1.2",
@@ -11375,7 +11276,6 @@
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz",
"integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"lru.min": "^1.1.0"
@@ -11609,7 +11509,6 @@
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz",
"integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==",
"devOptional": true,
"license": "MIT"
},
"node_modules/on-exit-leak-free": {
@@ -12046,7 +11945,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -12083,7 +11981,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"devOptional": true,
"license": "MIT"
},
"node_modules/pause": {
@@ -12095,7 +11992,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
"integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
"devOptional": true,
"license": "MIT"
},
"node_modules/picocolors": {
@@ -12250,7 +12146,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.3.tgz",
"integrity": "sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"confbox": "^0.3.1",
@@ -12262,7 +12157,6 @@
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz",
"integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==",
"devOptional": true,
"license": "MIT"
},
"node_modules/pluralize": {
@@ -12279,7 +12173,6 @@
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz",
"integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==",
"devOptional": true,
"license": "Unlicense",
"engines": {
"node": ">=12"
@@ -12401,7 +12294,6 @@
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-7.10.0.tgz",
"integrity": "sha512-o0ornyJOWgygVAzGCpr8PdXV8EJLHyVGDDUr/voBQt8Azzw8cYTByzzPGcA/m4tCkPcnJA8raEOv2CslsKhPEw==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -12451,7 +12343,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
"integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -12463,7 +12354,6 @@
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"devOptional": true,
"license": "ISC"
},
"node_modules/protobufjs": {
@@ -12709,7 +12599,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/rc9/-/rc9-3.1.0.tgz",
"integrity": "sha512-ufjkNVzbRHKcCOmTahZkmVsyc3W+MSk3jY03m+a7tGHkIsdVMG9l10/3HvFbWkkKzY5VFp3pkRsIo/UYgmFL7Q==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"defu": "^6.1.7",
@@ -12720,7 +12609,6 @@
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"engines": {
@@ -12731,7 +12619,6 @@
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
"integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
@@ -12861,7 +12748,6 @@
"version": "2.33.4",
"resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz",
"integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==",
"devOptional": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/remeda"
@@ -12880,7 +12766,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -12956,7 +12841,6 @@
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz",
"integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -12966,7 +12850,6 @@
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 4"
@@ -12976,7 +12859,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
"integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
"devOptional": true,
"license": "Unlicense"
},
"node_modules/router": {
@@ -13041,7 +12923,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz",
"integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==",
"devOptional": true,
"funding": [
{
"type": "github",
@@ -13079,7 +12960,6 @@
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"devOptional": true,
"license": "MIT",
"peer": true
},
@@ -13150,7 +13030,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -13163,7 +13042,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -13245,7 +13123,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"devOptional": true,
"license": "ISC",
"engines": {
"node": ">=14"
@@ -13423,7 +13300,6 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.5.1.tgz",
"integrity": "sha512-4toX5E1fQbBrpfXidaHnF0669nkAdETeIPTs2SUjxxD7RRIs9ICG4gtpmfc68JCEKehsdwLFqBu9VlQqZ1P1gg==",
"devOptional": true,
"license": "MIT",
"engines": {
"bun": ">=1.0.0",
@@ -13471,7 +13347,6 @@
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/stdin-discarder": {
@@ -14484,7 +14359,6 @@
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz",
"integrity": "sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
"typescript": ">=5"
@@ -14737,7 +14611,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"devOptional": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -15026,7 +14899,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz",
"integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"grammex": "^3.1.11",
+2 -1
View File
@@ -27,6 +27,7 @@
"test:debug": "node --experimental-vm-modules --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "npm run build && node test/sqlite.cjs --all",
"test:sqlite": "node test/sqlite.cjs",
"test:container": "node test/container-smoke.cjs",
"test:durability": "node test/bank-cards-durability.cjs",
"test:openapi-contract": "npm run build && node test/sqlite.cjs openapi-contract.e2e-spec.ts",
"verify": "npm run lint && npm run typecheck:app && npm run typecheck:test && npm test -- --runInBand && npm run build",
@@ -57,6 +58,7 @@
"passport": "0.7.0",
"passport-jwt": "4.0.1",
"pino-http": "11.0.0",
"prisma": "7.10.0",
"reflect-metadata": "0.2.2",
"rxjs": "7.8.2"
},
@@ -83,7 +85,6 @@
"globals": "17.12.0",
"jest": "30.5.1",
"prettier": "3.9.6",
"prisma": "7.10.0",
"release-it": "21.0.2",
"source-map-support": "0.5.21",
"supertest": "7.2.2",
+2 -8
View File
@@ -15,7 +15,7 @@ import {
ServiceUnavailableException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { existsSync, readFileSync } from 'node:fs';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { StructuredLogger } from '../../common/logging/structured-logger.service';
import { Environment } from '../../config/env.validation';
@@ -172,13 +172,7 @@ export class TbankClient {
config: ConfigService<Environment, true>,
private readonly logger: StructuredLogger,
) {
const sourceProtoRoot = join(
process.cwd(),
'src/integrations/tbank/proto/contracts',
);
const protoRoot = existsSync(sourceProtoRoot)
? sourceProtoRoot
: join(process.cwd(), 'dist/integrations/tbank/proto/contracts');
const protoRoot = join(__dirname, 'proto/contracts');
const definition = loadSync(
[
'users.proto',
+69
View File
@@ -0,0 +1,69 @@
// Real image boundary: catches missing native modules, proto assets and migrations.
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const { randomUUID } = require('node:crypto');
const image = process.argv[2];
assert.ok(image, 'Pass a built backend image reference');
const name = `wealthledger-smoke-${randomUUID()}`;
const docker = (...args) => execFileSync('docker', args, { encoding: 'utf8' }).trim();
const environment = [
'-e', 'DATABASE_URL=file:/data/wealthledger.db',
'-e', 'FRONTEND_ORIGIN=http://localhost',
'-e', 'JWT_SECRET=disposable-image-smoke-secret-at-least-32-characters',
'-e', 'TOKEN_ENCRYPTION_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=',
'-e', 'LOG_LEVEL=error',
];
async function ready() {
for (let attempt = 0; attempt < 90; attempt++) {
const state = JSON.parse(docker('inspect', name, '--format', '{{json .State}}'));
assert.equal(state.Running, true, 'Image exited before readiness');
if (state.Health.Status === 'healthy') return;
await new Promise((resolve) => setTimeout(resolve, 1000));
}
throw new Error('Image readiness timed out');
}
(async () => {
docker('volume', 'create', name);
try {
assert.throws(() => docker('run', '--rm', '--read-only', image),
'Missing runtime configuration must prevent startup');
const migrate = () => docker('run', '--rm', '--read-only', '-v', `${name}:/data`,
...environment, image, 'node', 'node_modules/prisma/build/index.js', 'migrate', 'deploy');
migrate();
docker('run', '-d', '--name', name, '--read-only', '-v', `${name}:/data`, ...environment, image);
await ready();
assert.deepEqual(JSON.parse(docker('inspect', name, '--format', '{{json .HostConfig.PortBindings}}')), {});
docker('exec', name, 'node', '-e', `
const assert = require('node:assert/strict');
const fs = require('node:fs');
assert.notEqual(process.getuid(), 0);
assert.equal(fs.existsSync('src'), false);
assert.equal(fs.existsSync('.env'), false);
assert.equal(fs.existsSync('node_modules/typescript'), false);
assert.equal(fs.existsSync('node_modules/@nestjs/cli'), false);
fs.accessSync('dist/src/integrations/tbank/proto/contracts/users.proto');
fetch('http://localhost:3000/api/v1/auth/register', {
method: 'POST', headers: {'content-type':'application/json'},
body: JSON.stringify({email:'image-smoke@example.test',password:'fictional-image-smoke-password'})
}).then(async r => {assert.equal(r.status, 200); await r.json()}).catch(e => {console.error(e); process.exitCode=1});
`);
docker('stop', name);
migrate();
docker('start', name);
await ready();
docker('exec', name, 'node', '-e', `
const assert = require('node:assert/strict');
const db = require('better-sqlite3')('/data/wealthledger.db');
assert.equal(db.prepare('SELECT count(*) AS n FROM User').get().n, 1);
assert.equal(db.prepare('SELECT count(*) AS n FROM Session').get().n, 1);
assert.equal(db.pragma('integrity_check', {simple:true}), 'ok');
db.close();
`);
console.log('PASS image: fresh/existing migration, non-root read-only runtime, auth, persistence, proto assets');
} finally {
try { docker('rm', '-f', name); } finally { docker('volume', 'rm', name); }
}
})().catch((error) => { console.error(error); process.exitCode = 1; });