perf: slim database migrator and application image layers
This commit is contained in:
parent
e5a20753b6
commit
9270bbb457
@ -226,3 +226,10 @@ temp_*.sql
|
|||||||
!workers/detail-renderer/package.json
|
!workers/detail-renderer/package.json
|
||||||
!workers/detail-renderer/package-lock.json
|
!workers/detail-renderer/package-lock.json
|
||||||
!public/detail-generator/fonts/OFL.txt
|
!public/detail-generator/fonts/OFL.txt
|
||||||
|
|
||||||
|
# Isolated migration tooling, locked to the application's dependency versions.
|
||||||
|
/workers/database-migrator/node_modules
|
||||||
|
/workers/database-migrator/node_modules/**
|
||||||
|
!workers/database-migrator/package.json
|
||||||
|
!workers/database-migrator/package-lock.json
|
||||||
|
!scripts/database-migrator-lock.mjs
|
||||||
|
|||||||
93
Dockerfile
93
Dockerfile
@ -156,6 +156,22 @@ RUN echo "清理 standalone 目录..." && \
|
|||||||
rm -rf .next/standalone/scripts && \
|
rm -rf .next/standalone/scripts && \
|
||||||
echo "清理完成"
|
echo "清理完成"
|
||||||
|
|
||||||
|
# Install only the migration/backup/seed dependency closure. Its lock is derived
|
||||||
|
# from the application lock and checked before installation to prevent drift.
|
||||||
|
FROM base AS migrator-deps
|
||||||
|
ARG ALPINE_MIRROR
|
||||||
|
WORKDIR /app
|
||||||
|
RUN sed -i "s|https://dl-cdn.alpinelinux.org/alpine|${ALPINE_MIRROR}|g" /etc/apk/repositories && \
|
||||||
|
apk add --no-cache openssl
|
||||||
|
COPY package-lock.json ./package-lock.json
|
||||||
|
COPY workers/database-migrator/package.json workers/database-migrator/package-lock.json ./workers/database-migrator/
|
||||||
|
COPY scripts/database-migrator-lock.mjs ./scripts/database-migrator-lock.mjs
|
||||||
|
RUN node scripts/database-migrator-lock.mjs --check
|
||||||
|
# The matching generated client is copied from builder; do not generate a second
|
||||||
|
# client during installation. The CLI engines still run their normal postinstall.
|
||||||
|
RUN --mount=type=cache,target=/root/.npm \
|
||||||
|
cd workers/database-migrator && PRISMA_SKIP_POSTINSTALL_GENERATE=1 npm ci --no-audit --no-fund
|
||||||
|
|
||||||
# Explicit, one-shot database migrator. It is never used by the application
|
# Explicit, one-shot database migrator. It is never used by the application
|
||||||
# container or the normal deploy entry. The operator must supply production DB
|
# container or the normal deploy entry. The operator must supply production DB
|
||||||
# identity, exact source SHA and a hashed backup receipt before --apply works.
|
# identity, exact source SHA and a hashed backup receipt before --apply works.
|
||||||
@ -171,20 +187,23 @@ RUN sed -i "s|https://dl-cdn.alpinelinux.org/alpine|${ALPINE_MIRROR}|g" /etc/apk
|
|||||||
command -v psql >/dev/null
|
command -v psql >/dev/null
|
||||||
ARG VCS_REF
|
ARG VCS_REF
|
||||||
LABEL org.opencontainers.image.revision=$VCS_REF
|
LABEL org.opencontainers.image.revision=$VCS_REF
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
COPY --from=migrator-deps --chown=root:root /app/workers/database-migrator/node_modules ./node_modules
|
||||||
COPY --from=builder /app/prisma ./prisma
|
COPY --from=builder --chown=root:root /app/node_modules/.prisma ./node_modules/.prisma
|
||||||
COPY --from=builder /app/scripts/safe-production-migrate.mjs ./scripts/safe-production-migrate.mjs
|
COPY --from=builder --chown=root:root /app/prisma ./prisma
|
||||||
COPY --from=builder /app/scripts/create-verified-production-backup.mjs ./scripts/create-verified-production-backup.mjs
|
COPY --from=builder --chown=root:root /app/scripts/safe-production-migrate.mjs ./scripts/safe-production-migrate.mjs
|
||||||
COPY --from=builder /app/scripts/seed-team-function-config.ts ./scripts/seed-team-function-config.ts
|
COPY --from=builder --chown=root:root /app/scripts/create-verified-production-backup.mjs ./scripts/create-verified-production-backup.mjs
|
||||||
COPY --from=builder /app/lib ./lib
|
COPY --from=builder --chown=root:root /app/scripts/seed-team-function-config.ts ./scripts/seed-team-function-config.ts
|
||||||
COPY --from=builder /app/tsconfig.json ./tsconfig.json
|
# Source closure for the team-function seed; no application routes or services.
|
||||||
COPY --from=builder /app/data/team-function-initial-content.json ./data/team-function-initial-content.json
|
COPY --from=builder --chown=root:root /app/lib/auto-personnel-rules.ts /app/lib/organization-material-permissions.ts /app/lib/prisma.ts /app/lib/team-function-contract.ts /app/lib/department-permission.ts /app/lib/employee-directory.ts /app/lib/env-validation.ts /app/lib/material-type-mapper.ts /app/lib/material-type-options.ts /app/lib/permission-groups.ts /app/lib/team-functions.ts /app/lib/user-permission-inherit.ts /app/lib/video-material-types.ts ./lib/
|
||||||
COPY --from=builder /app/DEPLOYMENT_COMMIT /app/DEPLOYMENT_COMMIT
|
COPY --from=builder --chown=root:root /app/tsconfig.json ./tsconfig.json
|
||||||
|
COPY --from=builder --chown=root:root /app/data/team-function-initial-content.json ./data/team-function-initial-content.json
|
||||||
|
COPY --from=builder --chown=root:root /app/DEPLOYMENT_COMMIT /app/DEPLOYMENT_COMMIT
|
||||||
RUN test "$(cat /app/DEPLOYMENT_COMMIT)" = "$VCS_REF" && \
|
RUN test "$(cat /app/DEPLOYMENT_COMMIT)" = "$VCS_REF" && \
|
||||||
test -x /app/node_modules/prisma/node_modules/@prisma/engines/schema-engine-linux-musl && \
|
test -x /app/node_modules/prisma/node_modules/@prisma/engines/schema-engine-linux-musl-openssl-3.0.x && \
|
||||||
chown -R node:node /app/node_modules/prisma/node_modules/@prisma/engines && \
|
test "$(stat -c '%a' /app/DEPLOYMENT_COMMIT)" = 444
|
||||||
chmod -R a+rX,a-w /app/prisma /app/scripts /app/DEPLOYMENT_COMMIT
|
|
||||||
USER node
|
USER node
|
||||||
|
# Prove the CLI can use the bundled native engine without downloading or writing.
|
||||||
|
RUN --network=none ./node_modules/.bin/prisma -v
|
||||||
ENTRYPOINT ["node", "scripts/safe-production-migrate.mjs"]
|
ENTRYPOINT ["node", "scripts/safe-production-migrate.mjs"]
|
||||||
|
|
||||||
# 生产运行阶段
|
# 生产运行阶段
|
||||||
@ -210,9 +229,9 @@ RUN addgroup --system --gid 1001 nodejs && \
|
|||||||
RUN mkdir -p /app/scripts
|
RUN mkdir -p /app/scripts
|
||||||
|
|
||||||
# 复制必要文件
|
# 复制必要文件
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=builder --chown=root:root /app/.next/standalone ./
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder --chown=root:root /app/.next/static ./.next/static
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/app ./app
|
COPY --from=builder --chown=root:root /app/app ./app
|
||||||
COPY --from=builder --chown=root:root /app/public ./public
|
COPY --from=builder --chown=root:root /app/public ./public
|
||||||
COPY --from=builder --chown=root:root /app/DEPLOYMENT_COMMIT /app/DEPLOYMENT_COMMIT
|
COPY --from=builder --chown=root:root /app/DEPLOYMENT_COMMIT /app/DEPLOYMENT_COMMIT
|
||||||
COPY --from=detail-renderer-deps --chown=root:root /opt/detail-renderer /opt/detail-renderer
|
COPY --from=detail-renderer-deps --chown=root:root /opt/detail-renderer /opt/detail-renderer
|
||||||
@ -221,15 +240,15 @@ ENV DETAIL_RENDERER_NODE=/opt/detail-renderer/bin/node
|
|||||||
ENV DETAIL_RENDERER_ENTRY=/opt/detail-renderer/cli.mjs
|
ENV DETAIL_RENDERER_ENTRY=/opt/detail-renderer/cli.mjs
|
||||||
|
|
||||||
# 只复制运行时必需的 node_modules
|
# 只复制运行时必需的 node_modules
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.prisma ./node_modules/.prisma
|
COPY --from=builder --chown=root:root /app/node_modules/.prisma ./node_modules/.prisma
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ag-psd ./node_modules/ag-psd
|
COPY --from=builder --chown=root:root /app/node_modules/ag-psd ./node_modules/ag-psd
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ioredis ./node_modules/ioredis
|
COPY --from=builder --chown=root:root /app/node_modules/ioredis ./node_modules/ioredis
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@ioredis ./node_modules/@ioredis
|
COPY --from=builder --chown=root:root /app/node_modules/@ioredis ./node_modules/@ioredis
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@aws-sdk ./node_modules/@aws-sdk
|
COPY --from=builder --chown=root:root /app/node_modules/@aws-sdk ./node_modules/@aws-sdk
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@smithy ./node_modules/@smithy
|
COPY --from=builder --chown=root:root /app/node_modules/@smithy ./node_modules/@smithy
|
||||||
|
|
||||||
# 复制运行时 schema,以及切流前只读校验所需的精确 migration;仍不复制 engine-cache。
|
# 复制运行时 schema,以及切流前只读校验所需的精确 migration;仍不复制 engine-cache。
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/prisma/schema.prisma ./prisma/schema.prisma
|
COPY --from=builder --chown=root:root /app/prisma/schema.prisma ./prisma/schema.prisma
|
||||||
COPY --from=builder --chown=root:root /app/prisma/migrations/20251118000000_add_upload_id_to_materials ./prisma/migrations/20251118000000_add_upload_id_to_materials
|
COPY --from=builder --chown=root:root /app/prisma/migrations/20251118000000_add_upload_id_to_materials ./prisma/migrations/20251118000000_add_upload_id_to_materials
|
||||||
COPY --from=builder --chown=root:root /app/prisma/migrations/20260811_add_video_transcode_lease ./prisma/migrations/20260811_add_video_transcode_lease
|
COPY --from=builder --chown=root:root /app/prisma/migrations/20260811_add_video_transcode_lease ./prisma/migrations/20260811_add_video_transcode_lease
|
||||||
COPY --from=builder --chown=root:root /app/prisma/migrations/20260811_update_brand_logo_defaults ./prisma/migrations/20260811_update_brand_logo_defaults
|
COPY --from=builder --chown=root:root /app/prisma/migrations/20260811_update_brand_logo_defaults ./prisma/migrations/20260811_update_brand_logo_defaults
|
||||||
@ -265,29 +284,17 @@ COPY --from=builder --chown=root:root /app/prisma/migrations/20260924_add_certif
|
|||||||
COPY --from=builder --chown=root:root /app/prisma/migrations/20261012_detail_generator ./prisma/migrations/20261012_detail_generator
|
COPY --from=builder --chown=root:root /app/prisma/migrations/20261012_detail_generator ./prisma/migrations/20261012_detail_generator
|
||||||
COPY --from=builder --chown=root:root /app/prisma/migrations/20261012_ai_model_registry ./prisma/migrations/20261012_ai_model_registry
|
COPY --from=builder --chown=root:root /app/prisma/migrations/20261012_ai_model_registry ./prisma/migrations/20261012_ai_model_registry
|
||||||
COPY --from=builder --chown=root:root /app/prisma/migrations/20261013_ai_model_capabilities ./prisma/migrations/20261013_ai_model_capabilities
|
COPY --from=builder --chown=root:root /app/prisma/migrations/20261013_ai_model_capabilities ./prisma/migrations/20261013_ai_model_capabilities
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib
|
COPY --from=builder --chown=root:root /app/lib ./lib
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/scripts/public-traffic-rollup-from-nginx.py ./scripts/public-traffic-rollup-from-nginx.py
|
COPY --from=builder --chown=root:root /app/scripts/public-traffic-rollup-from-nginx.py ./scripts/public-traffic-rollup-from-nginx.py
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/scripts/collapse-performance-survey-template-versions.mjs ./scripts/collapse-performance-survey-template-versions.mjs
|
COPY --from=builder --chown=root:root /app/scripts/collapse-performance-survey-template-versions.mjs ./scripts/collapse-performance-survey-template-versions.mjs
|
||||||
|
|
||||||
# 创建必要目录并收紧权限。
|
# 代码在 COPY 时直接归 root,避免再次改动整棵目录产生重复镜像层。
|
||||||
# 应用代码归 root 所有,运行账号只能写上传目录和 Next.js 运行时缓存;
|
# 仅运行时缓存和存储目录允许 nextjs 写入,代码权限保持只读。
|
||||||
# 即使上层文件路径校验再次出现缺陷,也不能覆盖 server.js 或业务源码。
|
RUN install -d -o nextjs -g nodejs -m 0755 /app/.next/cache && \
|
||||||
RUN mkdir -p /app/public /app/uploads && \
|
install -d -o nextjs -g nodejs -m 0750 /app/uploads /app/uploads/temp /app/uploads/assembled /app/uploads/materials /app/uploads/cleanup && \
|
||||||
mkdir -p /app/detail-generator-private && \
|
install -d -o nextjs -g nodejs -m 0700 /app/detail-generator-private && \
|
||||||
mkdir -p /app/scripts && \
|
|
||||||
mkdir -p /app/.next/cache && \
|
|
||||||
mkdir -p /app/uploads/temp && \
|
|
||||||
mkdir -p /app/uploads/assembled && \
|
|
||||||
mkdir -p /app/uploads/materials && \
|
|
||||||
mkdir -p /app/uploads/cleanup && \
|
|
||||||
chown -R root:root /app && \
|
|
||||||
chmod -R a+rX /app/public /app/prisma/migrations /app/scripts && \
|
|
||||||
chown -R nextjs:nodejs /app/.next/cache /app/uploads && \
|
|
||||||
chown nextjs:nodejs /app/detail-generator-private && \
|
|
||||||
chmod 0700 /app/detail-generator-private && \
|
|
||||||
chmod 0750 /app/uploads /app/uploads/temp /app/uploads/assembled /app/uploads/materials /app/uploads/cleanup && \
|
|
||||||
test "$(cat /app/DEPLOYMENT_COMMIT)" = "$VCS_REF" && \
|
test "$(cat /app/DEPLOYMENT_COMMIT)" = "$VCS_REF" && \
|
||||||
chmod 0444 /app/DEPLOYMENT_COMMIT && \
|
test "$(stat -c '%a' /app/DEPLOYMENT_COMMIT)" = 444 && \
|
||||||
test -s /app/public/brand/header-logo.png && \
|
test -s /app/public/brand/header-logo.png && \
|
||||||
test -s /app/public/brand/login-logo.png && \
|
test -s /app/public/brand/login-logo.png && \
|
||||||
test -s /app/public/brand/favicon.png && \
|
test -s /app/public/brand/favicon.png && \
|
||||||
|
|||||||
101
scripts/database-migrator-lock.mjs
Normal file
101
scripts/database-migrator-lock.mjs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { readFile, writeFile } from 'node:fs/promises'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
const MANIFEST = 'workers/database-migrator/package.json'
|
||||||
|
const LOCK = 'workers/database-migrator/package-lock.json'
|
||||||
|
const DIRECT_DEPENDENCIES = ['@prisma/client', 'prisma', 'tsx', 'zod']
|
||||||
|
|
||||||
|
// Preserve the main lock's exact locations, versions and integrity hashes. In
|
||||||
|
// particular Prisma 5's nested engines must not resolve to the app's Prisma 6
|
||||||
|
// engines. npm ci consumes this subset without resolving a new dependency tree.
|
||||||
|
export function createMigratorLock(rootLock, manifest) {
|
||||||
|
assert.equal(rootLock.lockfileVersion, 3, 'Main lock must use lockfileVersion 3')
|
||||||
|
assert.deepEqual(Object.keys(manifest.dependencies).sort(), DIRECT_DEPENDENCIES,
|
||||||
|
'Migrator direct dependencies must be explicitly reviewed')
|
||||||
|
assert.ok(!manifest.devDependencies && !manifest.optionalDependencies,
|
||||||
|
'Migrator manifest cannot introduce unreviewed dependency groups')
|
||||||
|
|
||||||
|
const packages = {}
|
||||||
|
const visit = packagePath => {
|
||||||
|
if (packages[packagePath]) return
|
||||||
|
const record = rootLock.packages[packagePath]
|
||||||
|
assert.ok(record && !record.link, `Missing or linked dependency: ${packagePath}`)
|
||||||
|
assert.ok(record.version && record.resolved && record.integrity,
|
||||||
|
`Dependency must have a pinned version, source and integrity: ${packagePath}`)
|
||||||
|
const copy = structuredClone(record)
|
||||||
|
// These packages are all runtime dependencies of the one-shot image.
|
||||||
|
delete copy.dev
|
||||||
|
delete copy.devOptional
|
||||||
|
packages[packagePath] = copy
|
||||||
|
const resolve = name => {
|
||||||
|
let parent = packagePath
|
||||||
|
while (parent) {
|
||||||
|
const candidate = `${parent}/node_modules/${name}`
|
||||||
|
if (rootLock.packages[candidate]) return candidate
|
||||||
|
const index = parent.lastIndexOf('/node_modules/')
|
||||||
|
parent = index < 0 ? '' : parent.slice(0, index)
|
||||||
|
}
|
||||||
|
const candidate = `node_modules/${name}`
|
||||||
|
assert.ok(rootLock.packages[candidate], `Unresolved dependency ${name} from ${packagePath}`)
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
for (const name of Object.keys({ ...record.dependencies, ...record.optionalDependencies })) {
|
||||||
|
visit(resolve(name))
|
||||||
|
}
|
||||||
|
for (const name of Object.keys(record.peerDependencies ?? {})) {
|
||||||
|
// A newly introduced optional peer could change runtime behavior even
|
||||||
|
// when npm accepts its absence. Review it instead of silently omitting it.
|
||||||
|
if (record.peerDependenciesMeta?.[name]?.optional) {
|
||||||
|
assert.ok(packagePath === 'node_modules/@prisma/client' && name === 'prisma',
|
||||||
|
`Unreviewed optional peer ${name} from ${packagePath}`)
|
||||||
|
}
|
||||||
|
visit(resolve(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, version] of Object.entries(manifest.dependencies)) {
|
||||||
|
const packagePath = `node_modules/${name}`
|
||||||
|
assert.equal(rootLock.packages[packagePath]?.version, version,
|
||||||
|
`${name} differs from the main lock; review and update the migrator manifest`)
|
||||||
|
visit(packagePath)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: manifest.name,
|
||||||
|
version: manifest.version,
|
||||||
|
lockfileVersion: 3,
|
||||||
|
requires: true,
|
||||||
|
packages: {
|
||||||
|
'': {
|
||||||
|
name: manifest.name,
|
||||||
|
version: manifest.version,
|
||||||
|
dependencies: manifest.dependencies,
|
||||||
|
engines: manifest.engines,
|
||||||
|
},
|
||||||
|
...Object.fromEntries(Object.entries(packages).sort(([a], [b]) => a.localeCompare(b, 'en'))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const [mode = '--check', ...extra] = process.argv.slice(2)
|
||||||
|
assert.ok(['--check', '--write'].includes(mode) && extra.length === 0,
|
||||||
|
'Usage: node scripts/database-migrator-lock.mjs [--check|--write]')
|
||||||
|
const rootLock = JSON.parse(await readFile(path.join(ROOT, 'package-lock.json'), 'utf8'))
|
||||||
|
const manifest = JSON.parse(await readFile(path.join(ROOT, MANIFEST), 'utf8'))
|
||||||
|
const expected = createMigratorLock(rootLock, manifest)
|
||||||
|
if (mode === '--write') {
|
||||||
|
await writeFile(path.join(ROOT, LOCK), `${JSON.stringify(expected, null, 2)}\n`)
|
||||||
|
} else {
|
||||||
|
const actual = JSON.parse(await readFile(path.join(ROOT, LOCK), 'utf8'))
|
||||||
|
assert.deepEqual(actual, expected,
|
||||||
|
'Migrator lock differs from the main lock. Run node scripts/database-migrator-lock.mjs --write and review it.')
|
||||||
|
}
|
||||||
|
console.log(`Migrator lock ${mode === '--write' ? 'written' : 'verified'}: ${Object.keys(expected.packages).length - 1} pinned packages`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||||
|
main().catch(error => { console.error(error.message); process.exitCode = 1 })
|
||||||
|
}
|
||||||
112
scripts/database-migrator-lock.test.mjs
Normal file
112
scripts/database-migrator-lock.test.mjs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { existsSync, readFileSync } from 'node:fs'
|
||||||
|
import { createRequire } from 'node:module'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import test from 'node:test'
|
||||||
|
import { createMigratorLock } from './database-migrator-lock.mjs'
|
||||||
|
|
||||||
|
const read = relative => JSON.parse(readFileSync(fileURLToPath(new URL(relative, import.meta.url)), 'utf8'))
|
||||||
|
const root = read('../package-lock.json')
|
||||||
|
const manifest = read('../workers/database-migrator/package.json')
|
||||||
|
const lock = read('../workers/database-migrator/package-lock.json')
|
||||||
|
|
||||||
|
test('committed migrator lock is the exact reproducible main-lock subset', () => {
|
||||||
|
assert.deepEqual(lock, createMigratorLock(root, manifest))
|
||||||
|
assert.ok(Object.keys(lock.packages).length < 50)
|
||||||
|
for (const [name, record] of Object.entries(lock.packages)) {
|
||||||
|
if (!name) continue
|
||||||
|
for (const key of ['version', 'resolved', 'integrity']) assert.equal(record[key], root.packages[name][key])
|
||||||
|
assert.equal(record.dev, undefined)
|
||||||
|
assert.equal(record.devOptional, undefined)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Prisma CLI uses its matching nested engines without app Prisma 6 or UI/media/test packages', () => {
|
||||||
|
assert.equal(lock.packages['node_modules/prisma/node_modules/@prisma/engines'].version, '5.22.0')
|
||||||
|
for (const name of ['@prisma/engines', 'next', 'react', 'canvas', 'sharp', 'jest', 'typescript', '@aws-sdk/client-s3']) {
|
||||||
|
assert.equal(lock.packages[`node_modules/${name}`], undefined, `Unexpected app package ${name}`)
|
||||||
|
}
|
||||||
|
assert.ok(lock.packages['node_modules/@esbuild/linux-x64'])
|
||||||
|
assert.ok(lock.packages['node_modules/get-tsconfig'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('direct dependency version drift and new dependency groups fail closed', () => {
|
||||||
|
const changedRoot = structuredClone(root)
|
||||||
|
changedRoot.packages['node_modules/prisma'].version = '6.18.0'
|
||||||
|
assert.throws(() => createMigratorLock(changedRoot, manifest), /differs from the main lock/)
|
||||||
|
assert.throws(() => createMigratorLock(root, { ...manifest, devDependencies: { next: '14.2.33' } }), /unreviewed dependency groups/)
|
||||||
|
assert.throws(() => createMigratorLock(root, { ...manifest, dependencies: { ...manifest.dependencies, next: '14.2.33' } }), /explicitly reviewed/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('missing transitive package and missing integrity fail closed', () => {
|
||||||
|
const missing = structuredClone(root)
|
||||||
|
delete missing.packages['node_modules/get-tsconfig']
|
||||||
|
assert.throws(() => createMigratorLock(missing, manifest), /Unresolved dependency/)
|
||||||
|
const unsigned = structuredClone(root)
|
||||||
|
delete unsigned.packages['node_modules/prisma/node_modules/@prisma/engines'].integrity
|
||||||
|
assert.throws(() => createMigratorLock(unsigned, manifest), /integrity/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('optional peer additions require explicit review instead of silently disappearing', () => {
|
||||||
|
const changed = structuredClone(root)
|
||||||
|
changed.packages['node_modules/zod'].peerDependencies = { next: '*' }
|
||||||
|
changed.packages['node_modules/zod'].peerDependenciesMeta = { next: { optional: true } }
|
||||||
|
assert.throws(() => createMigratorLock(changed, manifest), /Unreviewed optional peer/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('slim migrator copies the full seed source closure and no unreviewed runtime dependency', () => {
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
const ts = require('typescript')
|
||||||
|
const rootDir = fileURLToPath(new URL('../', import.meta.url))
|
||||||
|
const files = new Set()
|
||||||
|
const packages = new Set()
|
||||||
|
const visitFile = relative => {
|
||||||
|
relative = relative.replaceAll('\\', '/')
|
||||||
|
if (files.has(relative)) return
|
||||||
|
files.add(relative)
|
||||||
|
const absolute = path.join(rootDir, relative)
|
||||||
|
const ast = ts.createSourceFile(absolute, readFileSync(absolute, 'utf8'), ts.ScriptTarget.Latest, true)
|
||||||
|
const resolve = specifier => {
|
||||||
|
if (specifier.startsWith('node:')) return
|
||||||
|
if (!specifier.startsWith('.') && !specifier.startsWith('@/')) {
|
||||||
|
packages.add(specifier)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const base = specifier.startsWith('@/') ? specifier.slice(2) : path.join(path.dirname(relative), specifier)
|
||||||
|
const next = [base, `${base}.ts`, `${base}.tsx`, `${base}/index.ts`].find(candidate => existsSync(path.join(rootDir, candidate)))
|
||||||
|
assert.ok(next, `Unresolved seed import ${specifier} from ${relative}`)
|
||||||
|
visitFile(next)
|
||||||
|
}
|
||||||
|
const visitNode = node => {
|
||||||
|
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier) resolve(node.moduleSpecifier.text)
|
||||||
|
if (ts.isCallExpression(node) && node.expression.getText(ast) === 'require' && ts.isStringLiteral(node.arguments[0])) resolve(node.arguments[0].text)
|
||||||
|
ts.forEachChild(node, visitNode)
|
||||||
|
}
|
||||||
|
visitNode(ast)
|
||||||
|
}
|
||||||
|
visitFile('scripts/seed-team-function-config.ts')
|
||||||
|
assert.deepEqual([...packages].sort(), ['@prisma/client', 'zod'])
|
||||||
|
const dockerfile = readFileSync(path.join(rootDir, 'Dockerfile'), 'utf8')
|
||||||
|
const migrator = dockerfile.slice(dockerfile.search(/^FROM base AS migrator\r?$/m), dockerfile.search(/^FROM base AS runner\r?$/m))
|
||||||
|
for (const file of files) assert.ok(migrator.includes(`/app/${file}`), `Seed dependency missing from slim image: ${file}`)
|
||||||
|
assert.equal(files.size, 14, 'Review seed dependency closure before changing the image copy list')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Docker locks the isolated install and runs Prisma as nonroot with no network', () => {
|
||||||
|
const dockerfile = readFileSync(fileURLToPath(new URL('../Dockerfile', import.meta.url)), 'utf8')
|
||||||
|
assert.match(dockerfile, /node scripts\/database-migrator-lock\.mjs --check/)
|
||||||
|
const depsStart = dockerfile.search(/^FROM base AS migrator-deps\r?$/m)
|
||||||
|
assert.ok(depsStart >= 0)
|
||||||
|
const deps = dockerfile.slice(depsStart, dockerfile.indexOf('\nFROM ', depsStart + 1))
|
||||||
|
assert.match(deps, /npm ci --no-audit --no-fund/)
|
||||||
|
assert.match(deps, /PRISMA_SKIP_POSTINSTALL_GENERATE=1 npm ci/)
|
||||||
|
assert.doesNotMatch(deps, /--ignore-scripts/)
|
||||||
|
assert.doesNotMatch(deps, /npm (?:install|update)/)
|
||||||
|
assert.match(deps, /apk add --no-cache openssl/)
|
||||||
|
const start = dockerfile.search(/^FROM base AS migrator\r?$/m)
|
||||||
|
const migrator = dockerfile.slice(start, dockerfile.indexOf('\nFROM ', start + 1))
|
||||||
|
assert.match(migrator, /USER node[\s\S]*RUN --network=none[^\n]*prisma (?:--version|-v)/)
|
||||||
|
assert.match(migrator, /COPY --from=builder --chown=root:root \/app\/node_modules\/\.prisma/)
|
||||||
|
assert.match(migrator, /ENTRYPOINT \["node", "scripts\/safe-production-migrate\.mjs"\]/)
|
||||||
|
})
|
||||||
@ -891,7 +891,7 @@ test('container supply chain and migrator image are immutable while SOP keeps co
|
|||||||
readFile(new URL('../.dockerignore', import.meta.url), 'utf8'),
|
readFile(new URL('../.dockerignore', import.meta.url), 'utf8'),
|
||||||
readFile(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8'),
|
readFile(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8'),
|
||||||
])
|
])
|
||||||
const migratorStart = dockerfile.indexOf('FROM base AS migrator')
|
const migratorStart = dockerfile.search(/^FROM base AS migrator\r?$/m)
|
||||||
const runnerStart = dockerfile.indexOf('FROM base AS runner', migratorStart)
|
const runnerStart = dockerfile.indexOf('FROM base AS runner', migratorStart)
|
||||||
const migrator = dockerfile.slice(migratorStart, runnerStart)
|
const migrator = dockerfile.slice(migratorStart, runnerStart)
|
||||||
const depsStart = dockerfile.indexOf('FROM base AS deps')
|
const depsStart = dockerfile.indexOf('FROM base AS deps')
|
||||||
@ -916,10 +916,12 @@ test('container supply chain and migrator image are immutable while SOP keeps co
|
|||||||
assert.match(migrator, /command -v pg_dump >\/dev\/null/)
|
assert.match(migrator, /command -v pg_dump >\/dev\/null/)
|
||||||
assert.match(migrator, /command -v pg_restore >\/dev\/null/)
|
assert.match(migrator, /command -v pg_restore >\/dev\/null/)
|
||||||
assert.match(migrator, /command -v psql >\/dev\/null/)
|
assert.match(migrator, /command -v psql >\/dev\/null/)
|
||||||
assert.match(migrator, /COPY --from=builder \/app\/scripts\/create-verified-production-backup\.mjs/)
|
assert.match(migrator, /COPY --from=builder --chown=root:root \/app\/scripts\/create-verified-production-backup\.mjs/)
|
||||||
assert.match(migrator, /test -x \/app\/node_modules\/prisma\/node_modules\/@prisma\/engines\/schema-engine-linux-musl/)
|
assert.match(migrator, /test -x \/app\/node_modules\/prisma\/node_modules\/@prisma\/engines\/schema-engine-linux-musl-openssl-3\.0\.x(?:\s|$)/)
|
||||||
assert.match(migrator, /chown -R node:node \/app\/node_modules\/prisma\/node_modules\/@prisma\/engines/)
|
assert.doesNotMatch(migrator, /chown\s+(?:-R\s+)?node:node/)
|
||||||
assert.match(migrator, /chmod -R a\+rX,a-w \/app\/prisma \/app\/scripts \/app\/DEPLOYMENT_COMMIT/)
|
assert.match(migrator, /COPY --from=migrator-deps --chown=root:root \/app\/workers\/database-migrator\/node_modules/)
|
||||||
|
assert.doesNotMatch(migrator, /COPY[^\n]*\/app\/node_modules\s+\.\/node_modules/)
|
||||||
|
assert.doesNotMatch(migrator, /COPY[^\n]*\/app\/lib\s+\.\/lib/)
|
||||||
assert.match(migrator, /USER node/)
|
assert.match(migrator, /USER node/)
|
||||||
assert.match(sop, /tar -xOf "\$MIGRATOR_ARCHIVE" manifest\.json/)
|
assert.match(sop, /tar -xOf "\$MIGRATOR_ARCHIVE" manifest\.json/)
|
||||||
assert.match(sop, /entry\.RepoTags\.includes\(image\)/)
|
assert.match(sop, /entry\.RepoTags\.includes\(image\)/)
|
||||||
|
|||||||
26
workers/database-migrator/README.md
Normal file
26
workers/database-migrator/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Database migrator dependencies
|
||||||
|
|
||||||
|
此目录只为一次性数据库工具提供依赖,不安装完整应用依赖,也不改变生产迁移、备份和恢复的授权门禁。
|
||||||
|
|
||||||
|
| 依赖 | 用途 |
|
||||||
|
| --- | --- |
|
||||||
|
| `@prisma/client` | 迁移前后的数据库结构、身份核验;团队职能 seed 查询与写入 |
|
||||||
|
| `prisma` | 执行 `prisma migrate deploy` 及所需的原生 schema engine |
|
||||||
|
| `tsx` | 执行现有 TypeScript 团队职能 seed,不复制 TypeScript 编译器 |
|
||||||
|
| `zod` | 复用团队职能输入校验 |
|
||||||
|
|
||||||
|
独立 lock 从根 `package-lock.json` 提取完整依赖闭包,保留精确版本、下载来源、integrity 和嵌套路径。Prisma CLI 使用自身锁定的 5.22 引擎,不使用应用根目录的另一版本引擎。生成器会检查直接依赖版本;新增可选 peer 也必须先人工核对,不能静默省略。
|
||||||
|
|
||||||
|
根 lock 更新后,先检查这四个依赖的实际锁定版本是否变化;有变化时审核兼容性并同步本目录 `package.json`,然后执行:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node scripts/database-migrator-lock.mjs --write
|
||||||
|
node scripts/database-migrator-lock.mjs --check
|
||||||
|
node --test scripts/database-migrator-lock.test.mjs scripts/safe-production-migrate.test.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
审核独立 lock 的差异后重新构建 `migrator`。构建过程先执行 `--check`,根 lock 与独立 lock 不一致时直接失败,不重新解析版本。
|
||||||
|
|
||||||
|
Docker 的独立依赖阶段先安装 OpenSSL 3,再执行 `PRISMA_SKIP_POSTINSTALL_GENERATE=1 npm ci`,将匹配 Alpine 的 `schema-engine-linux-musl-openssl-3.0.x` 打包。该变量仅跳过此无 schema 目录中多余的 Client 自动生成;`@prisma/engines` 的安装脚本照常执行。生成的 Prisma Client 从同次应用 builder 的 `.prisma` 目录复制,保证与相同版本的 schema 一致。最终镜像以 `node` 用户运行,代码和引擎归 root 所有,并在断网条件下执行 Prisma CLI 验证,避免运行时下载引擎。
|
||||||
|
|
||||||
|
团队职能 seed 仍复用项目原有权限、花名册及数据写入逻辑。镜像只复制其 13 个 `lib` 源文件;测试解析脚本的完整导入关系,检查复制列表及外部依赖。修改 seed 的导入关系后,必须重新审核这一依赖闭包。备份工具继续依赖系统提供的 `pg_dump`、`pg_restore` 和 `psql`。
|
||||||
623
workers/database-migrator/package-lock.json
generated
Normal file
623
workers/database-migrator/package-lock.json
generated
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
{
|
||||||
|
"name": "gongxiangp2-database-migrator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "gongxiangp2-database-migrator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "5.22.0",
|
||||||
|
"prisma": "5.22.0",
|
||||||
|
"tsx": "4.21.0",
|
||||||
|
"zod": "3.25.76"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/client": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.13"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prisma": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"prisma": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.27.2",
|
||||||
|
"@esbuild/android-arm": "0.27.2",
|
||||||
|
"@esbuild/android-arm64": "0.27.2",
|
||||||
|
"@esbuild/android-x64": "0.27.2",
|
||||||
|
"@esbuild/darwin-arm64": "0.27.2",
|
||||||
|
"@esbuild/darwin-x64": "0.27.2",
|
||||||
|
"@esbuild/freebsd-arm64": "0.27.2",
|
||||||
|
"@esbuild/freebsd-x64": "0.27.2",
|
||||||
|
"@esbuild/linux-arm": "0.27.2",
|
||||||
|
"@esbuild/linux-arm64": "0.27.2",
|
||||||
|
"@esbuild/linux-ia32": "0.27.2",
|
||||||
|
"@esbuild/linux-loong64": "0.27.2",
|
||||||
|
"@esbuild/linux-mips64el": "0.27.2",
|
||||||
|
"@esbuild/linux-ppc64": "0.27.2",
|
||||||
|
"@esbuild/linux-riscv64": "0.27.2",
|
||||||
|
"@esbuild/linux-s390x": "0.27.2",
|
||||||
|
"@esbuild/linux-x64": "0.27.2",
|
||||||
|
"@esbuild/netbsd-arm64": "0.27.2",
|
||||||
|
"@esbuild/netbsd-x64": "0.27.2",
|
||||||
|
"@esbuild/openbsd-arm64": "0.27.2",
|
||||||
|
"@esbuild/openbsd-x64": "0.27.2",
|
||||||
|
"@esbuild/openharmony-arm64": "0.27.2",
|
||||||
|
"@esbuild/sunos-x64": "0.27.2",
|
||||||
|
"@esbuild/win32-arm64": "0.27.2",
|
||||||
|
"@esbuild/win32-ia32": "0.27.2",
|
||||||
|
"@esbuild/win32-x64": "0.27.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-tsconfig": {
|
||||||
|
"version": "4.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.12.0.tgz",
|
||||||
|
"integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"resolve-pkg-maps": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/engines": "5.22.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.13"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prisma/node_modules/@prisma/debug": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/prisma/node_modules/@prisma/engines": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.22.0",
|
||||||
|
"@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
|
||||||
|
"@prisma/fetch-engine": "5.22.0",
|
||||||
|
"@prisma/get-platform": "5.22.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prisma/node_modules/@prisma/engines-version": {
|
||||||
|
"version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz",
|
||||||
|
"integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/prisma/node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.22.0",
|
||||||
|
"@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
|
||||||
|
"@prisma/get-platform": "5.22.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prisma/node_modules/@prisma/get-platform": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.22.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/resolve-pkg-maps": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx": {
|
||||||
|
"version": "4.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
|
||||||
|
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "~0.27.0",
|
||||||
|
"get-tsconfig": "^4.7.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tsx": "dist/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.25.76",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
workers/database-migrator/package.json
Normal file
15
workers/database-migrator/package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "gongxiangp2-database-migrator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Isolated dependencies for migration, verified backup and team-function seed tools",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "5.22.0",
|
||||||
|
"prisma": "5.22.0",
|
||||||
|
"tsx": "4.21.0",
|
||||||
|
"zod": "3.25.76"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user