chore: pin npm deps, quiet install, validate exact versions
This commit is contained in:
3
client/.npmrc
Normal file
3
client/.npmrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
save-exact=true
|
||||||
|
fund=false
|
||||||
|
audit=false
|
||||||
36
client/package-lock.json
generated
36
client/package-lock.json
generated
@@ -8,26 +8,26 @@
|
|||||||
"name": "client",
|
"name": "client",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "11.14.1",
|
||||||
"@mui/material": "^7.3.9",
|
"@mui/material": "7.3.9",
|
||||||
"keycloak-js": "^26.2.3",
|
"keycloak-js": "26.2.3",
|
||||||
"ra-data-simple-rest": "^5.14.4",
|
"ra-data-simple-rest": "5.14.4",
|
||||||
"react": "^18.2.0",
|
"react": "18.3.1",
|
||||||
"react-admin": "^5.14.4",
|
"react-admin": "5.14.4",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.55",
|
"@types/react": "18.3.28",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "18.3.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "4.7.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "8.57.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "4.6.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-react-refresh": "0.4.26",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "5.9.3",
|
||||||
"vite": "^5.1.0"
|
"vite": "5.4.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
|
|||||||
@@ -10,25 +10,25 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "11.14.1",
|
||||||
"@mui/material": "^7.3.9",
|
"@mui/material": "7.3.9",
|
||||||
"keycloak-js": "^26.2.3",
|
"keycloak-js": "26.2.3",
|
||||||
"ra-data-simple-rest": "^5.14.4",
|
"ra-data-simple-rest": "5.14.4",
|
||||||
"react": "^18.2.0",
|
"react": "18.3.1",
|
||||||
"react-admin": "^5.14.4",
|
"react-admin": "5.14.4",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.55",
|
"@types/react": "18.3.28",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "18.3.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "4.7.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "8.57.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "4.6.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-react-refresh": "0.4.26",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "5.9.3",
|
||||||
"vite": "^5.1.0"
|
"vite": "5.4.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
generation/context/dependency-pins.md
Normal file
5
generation/context/dependency-pins.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Зависимости workspace (зафиксированные версии)
|
||||||
|
|
||||||
|
- Рабочие манифесты: **`server/package.json`**, **`client/package.json`** — прямые зависимости **без** префиксов `^` / `~`; точные версии синхронизированы с **`package-lock.json`** (скрипт `tools/pin-package-versions.mjs`).
|
||||||
|
- После добавления пакета: `npm install <pkg>` в `server/` или `client/` (с `save-exact` в `.npmrc`), затем при необходимости снова **`node tools/pin-package-versions.mjs server|client`** и коммит обоих файлов.
|
||||||
|
- Генератор **`generation/generate.mjs`** не перезаписывает `package.json`; агенты не должны ослаблять диапазоны версий при правках кода.
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
"name": "toir-generation-context",
|
"name": "toir-generation-context",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"pin:deps": "node tools/pin-package-versions.mjs server && node tools/pin-package-versions.mjs client",
|
||||||
"generate:domain-summary": "node tools/generate-domain-summary.mjs",
|
"generate:domain-summary": "node tools/generate-domain-summary.mjs",
|
||||||
"validate:generation": "node tools/validate-generation.mjs",
|
"validate:generation": "node tools/validate-generation.mjs",
|
||||||
"validate:generation:runtime": "node tools/validate-generation.mjs --run-runtime",
|
"validate:generation:runtime": "node tools/validate-generation.mjs --run-runtime",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Validation is now a lightweight automated gate instead of a prose-only checklist
|
|||||||
- generation must not pass validation if framework scaffolding files were deleted and replaced by a hand-written minimal skeleton
|
- generation must not pass validation if framework scaffolding files were deleted and replaced by a hand-written minimal skeleton
|
||||||
- if dependencies are installed, build verification runs for `server/` and `client/`
|
- if dependencies are installed, build verification runs for `server/` and `client/`
|
||||||
- if dependencies are missing, build verification is reported as skipped with reason instead of green
|
- if dependencies are missing, build verification is reported as skipped with reason instead of green
|
||||||
|
- `server/package.json` and `client/package.json`: every entry in `dependencies`, `devDependencies`, `optionalDependencies`, and `peerDependencies` must use an exact version string (no `^` or `~` prefix)
|
||||||
|
|
||||||
### Auth checks
|
### Auth checks
|
||||||
|
|
||||||
|
|||||||
3
server/.npmrc
Normal file
3
server/.npmrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
save-exact=true
|
||||||
|
fund=false
|
||||||
|
audit=false
|
||||||
64
server/package-lock.json
generated
64
server/package-lock.json
generated
@@ -10,40 +10,40 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "10.4.22",
|
||||||
"@nestjs/config": "^4.0.3",
|
"@nestjs/config": "4.0.3",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "10.4.22",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "10.4.22",
|
||||||
"@prisma/client": "^5.22.0",
|
"@prisma/client": "5.22.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "0.14.4",
|
||||||
"jose": "^6.2.2",
|
"jose": "6.2.2",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "7.8.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "10.4.9",
|
||||||
"@nestjs/schematics": "^10.0.0",
|
"@nestjs/schematics": "10.2.3",
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "10.4.22",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "5.0.6",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "29.5.14",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "20.19.37",
|
||||||
"@types/supertest": "^6.0.0",
|
"@types/supertest": "6.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "8.57.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "8.57.0",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "8.57.1",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "9.1.2",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "5.5.5",
|
||||||
"jest": "^29.5.0",
|
"jest": "29.7.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "3.8.1",
|
||||||
"prisma": "^5.22.0",
|
"prisma": "5.22.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "7.2.2",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "29.4.6",
|
||||||
"ts-loader": "^9.4.3",
|
"ts-loader": "9.5.4",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "10.9.2",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "5.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-devkit/core": {
|
"node_modules/@angular-devkit/core": {
|
||||||
|
|||||||
@@ -26,40 +26,40 @@
|
|||||||
"seed": "ts-node prisma/seed.ts"
|
"seed": "ts-node prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "10.4.22",
|
||||||
"@nestjs/config": "^4.0.3",
|
"@nestjs/config": "4.0.3",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "10.4.22",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "10.4.22",
|
||||||
"@prisma/client": "^5.22.0",
|
"@prisma/client": "5.22.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "0.14.4",
|
||||||
"jose": "^6.2.2",
|
"jose": "6.2.2",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "7.8.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "10.4.9",
|
||||||
"@nestjs/schematics": "^10.0.0",
|
"@nestjs/schematics": "10.2.3",
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "10.4.22",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "5.0.6",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "29.5.14",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "20.19.37",
|
||||||
"@types/supertest": "^6.0.0",
|
"@types/supertest": "6.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "8.57.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "8.57.0",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "8.57.1",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "9.1.2",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "5.5.5",
|
||||||
"jest": "^29.5.0",
|
"jest": "29.7.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "3.8.1",
|
||||||
"prisma": "^5.22.0",
|
"prisma": "5.22.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "7.2.2",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "29.4.6",
|
||||||
"ts-loader": "^9.4.3",
|
"ts-loader": "9.5.4",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "10.9.2",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "5.9.3"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
|
|||||||
44
tools/pin-package-versions.mjs
Normal file
44
tools/pin-package-versions.mjs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* ╨Ч╨░╨┐╨╕╤Б╤Л╨▓╨░╨╡╤В ╨▓ package.json ╤В╨╛╤З╨╜╤Л╨╡ ╨▓╨╡╤А╤Б╨╕╨╕ ╨┐╤А╤П╨╝╤Л╤Е ╨╖╨░╨▓╨╕╤Б╨╕╨╝╨╛╤Б╤В╨╡╨╣ ╨╕╨╖ package-lock.json (lockfile v3).
|
||||||
|
* ╨Ш╤Б╨┐╨╛╨╗╤М╨╖╨╛╨▓╨░╨╜╨╕╨╡: node tools/pin-package-versions.mjs server
|
||||||
|
* node tools/pin-package-versions.mjs client
|
||||||
|
*/
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const dir = process.argv[2];
|
||||||
|
if (!dir || !['server', 'client'].includes(dir)) {
|
||||||
|
console.error('Usage: node tools/pin-package-versions.mjs <server|client>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = path.resolve(process.cwd(), dir);
|
||||||
|
const lockPath = path.join(root, 'package-lock.json');
|
||||||
|
const pkgPath = path.join(root, 'package.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(lockPath)) {
|
||||||
|
console.error(`Missing ${lockPath}. Run npm install in ${dir}/ first.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||||
|
|
||||||
|
function pinSection(section) {
|
||||||
|
if (!section) return;
|
||||||
|
for (const name of Object.keys(section)) {
|
||||||
|
const lockKey = `node_modules/${name}`;
|
||||||
|
const entry = lock.packages?.[lockKey];
|
||||||
|
if (!entry?.version) {
|
||||||
|
console.error(`Missing lock entry for ${lockKey}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
section[name] = entry.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pinSection(pkg.dependencies);
|
||||||
|
pinSection(pkg.devDependencies);
|
||||||
|
|
||||||
|
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
||||||
|
console.log(`Pinned ${dir}/package.json from package-lock.json`);
|
||||||
@@ -181,6 +181,26 @@ function validateBuildChecks() {
|
|||||||
assertCondition(Boolean(clientPackage.devDependencies?.vite), 'client/package.json must keep Vite as a dev dependency');
|
assertCondition(Boolean(clientPackage.devDependencies?.vite), 'client/package.json must keep Vite as a dev dependency');
|
||||||
assertCondition(Boolean(clientPackage.devDependencies?.['@vitejs/plugin-react']), 'client/package.json must keep @vitejs/plugin-react as a dev dependency');
|
assertCondition(Boolean(clientPackage.devDependencies?.['@vitejs/plugin-react']), 'client/package.json must keep @vitejs/plugin-react as a dev dependency');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validatePinnedPackageJsonVersions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function validatePinnedPackageJsonVersions() {
|
||||||
|
for (const rel of ['server/package.json', 'client/package.json']) {
|
||||||
|
const pkg = parseJson(rel);
|
||||||
|
if (!pkg) continue;
|
||||||
|
for (const section of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
|
||||||
|
const deps = pkg[section];
|
||||||
|
if (!deps || typeof deps !== 'object') continue;
|
||||||
|
for (const [name, ver] of Object.entries(deps)) {
|
||||||
|
if (typeof ver !== 'string') continue;
|
||||||
|
assertCondition(
|
||||||
|
!/^[\^~]/.test(ver),
|
||||||
|
`${rel} ${section}.${name} must be pinned (no ^ or ~): got ${ver}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateAuthChecks() {
|
function validateAuthChecks() {
|
||||||
|
|||||||
Reference in New Issue
Block a user