Harden local setup and development startup
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.cache/
|
||||||
|
.npm-cache/
|
||||||
|
|
||||||
# Build output
|
# Build output
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
cache=.cache/npm
|
||||||
|
prefer-offline=true
|
||||||
|
fetch-retries=5
|
||||||
|
fetch-retry-factor=2
|
||||||
|
fetch-retry-mintimeout=1000
|
||||||
|
fetch-retry-maxtimeout=60000
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
# AgendaPro: local setup and failure recovery
|
||||||
|
|
||||||
|
## Recommended install
|
||||||
|
|
||||||
|
Use the checked-in lockfile and a project-local npm cache:
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm run setup
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
On PowerShell machines where `npm.ps1` is blocked by execution policy, use `npm.cmd`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm.cmd run setup
|
||||||
|
npm.cmd run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The setup script uses `npm ci`; it does not mutate dependency versions. The local cache is ignored by Git and avoids failures caused by an unwritable global npm cache.
|
||||||
|
|
||||||
|
## What was audited
|
||||||
|
|
||||||
|
- `SEC_E_NO_CREDENTIALS` during clone: Git was using the Windows Schannel TLS backend and the host could not acquire TLS credentials. This is a machine-level Git/TLS issue, not an application error. Retry the clone with `git -c http.sslBackend=openssl clone <url>`.
|
||||||
|
- `npm.ps1` execution-policy error: PowerShell blocked the npm wrapper. `npm.cmd` is the same npm installation without the blocked PowerShell wrapper.
|
||||||
|
- npm `EPERM` in `AppData\Local\npm-cache`: the global cache was not writable. `.npmrc` and `scripts/setup.mjs` route the cache to `.cache/npm` inside the project.
|
||||||
|
- `tsx` `uv_os_get_passwd`/`os.userInfo()` error: the host returned an OS error while tsx selected its temporary directory. `scripts/run-tsx.mjs` keeps the native call when it works and supplies a safe username fallback when it fails.
|
||||||
|
- Vite/esbuild config loading: the original config used `__dirname` in an ESM project. `vite.config.ts` now resolves `src` from `import.meta.url`, binds the dev server deterministically to `127.0.0.1`, supports `VITE_PORT`, and fails clearly when the port is already occupied.
|
||||||
|
- Git `dubious ownership`: some managed workspaces are created by a different Windows identity. If needed, authorize only this checkout with `git config --global --add safe.directory <absolute-project-path>`.
|
||||||
|
|
||||||
|
## Preflight and recovery
|
||||||
|
|
||||||
|
The project validates Node.js >= 22.5 and `node:sqlite` before development, build, and production start. If a port is busy, inspect it with:
|
||||||
|
|
||||||
|
```text
|
||||||
|
netstat -ano | findstr :3000
|
||||||
|
netstat -ano | findstr :5173
|
||||||
|
```
|
||||||
|
|
||||||
|
Change ports without editing source:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PORT=3001 VITE_PORT=5174 API_URL=http://127.0.0.1:3001 npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
PowerShell equivalent:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$env:PORT='3001'; $env:VITE_PORT='5174'; $env:API_URL='http://127.0.0.1:3001'; npm.cmd run dev
|
||||||
|
```
|
||||||
Generated
+14
-39
@@ -91,6 +91,7 @@
|
|||||||
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.7",
|
"@babel/code-frame": "^7.29.7",
|
||||||
"@babel/generator": "^7.29.7",
|
"@babel/generator": "^7.29.7",
|
||||||
@@ -368,6 +369,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/accessibility": "^3.1.1",
|
"@dnd-kit/accessibility": "^3.1.1",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -995,6 +997,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.21.tgz",
|
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.21.tgz",
|
||||||
"integrity": "sha512-t3u/+sqh3Iq7TWtUnVLcGDUE6OWZh0UD3c04bI/l7lSLAgAKr3kngBmhHiQD1QXpwC8ZN5iNqG7a7gOVixhSKQ==",
|
"integrity": "sha512-t3u/+sqh3Iq7TWtUnVLcGDUE6OWZh0UD3c04bI/l7lSLAgAKr3kngBmhHiQD1QXpwC8ZN5iNqG7a7gOVixhSKQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preact": "~10.12.1"
|
"preact": "~10.12.1"
|
||||||
}
|
}
|
||||||
@@ -1327,9 +1330,6 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1344,9 +1344,6 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1361,9 +1358,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1378,9 +1372,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1395,9 +1386,6 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1412,9 +1400,6 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1429,9 +1414,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1446,9 +1428,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1463,9 +1442,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1480,9 +1456,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1497,9 +1470,6 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1514,9 +1484,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1531,9 +1498,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1880,6 +1844,7 @@
|
|||||||
"integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
|
"integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@@ -1968,6 +1933,7 @@
|
|||||||
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
|
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -2222,6 +2188,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.10.44",
|
"baseline-browser-mapping": "^2.10.44",
|
||||||
"caniuse-lite": "^1.0.30001806",
|
"caniuse-lite": "^1.0.30001806",
|
||||||
@@ -2927,6 +2894,7 @@
|
|||||||
"integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
|
"integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -3681,6 +3649,7 @@
|
|||||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
@@ -4299,6 +4268,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.16",
|
"nanoid": "^3.3.16",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -4568,6 +4538,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -4580,6 +4551,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
@@ -5288,6 +5260,7 @@
|
|||||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -5346,6 +5319,7 @@
|
|||||||
"integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==",
|
"integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.28.0"
|
"esbuild": "~0.28.0"
|
||||||
},
|
},
|
||||||
@@ -5509,6 +5483,7 @@
|
|||||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
|
|||||||
+8
-3
@@ -5,14 +5,19 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Visual scheduling and business management app for owners and employees",
|
"description": "Visual scheduling and business management app for owners and employees",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"setup": "node scripts/setup.mjs",
|
||||||
|
"dev:background": "node scripts/launch-dev.mjs",
|
||||||
|
"predev": "node scripts/preflight.mjs --ports=3000,5173",
|
||||||
"dev": "concurrently -k -n SERVER,WEB -c blue,magenta \"npm:dev:server\" \"npm:dev:web\"",
|
"dev": "concurrently -k -n SERVER,WEB -c blue,magenta \"npm:dev:server\" \"npm:dev:web\"",
|
||||||
"dev:server": "tsx watch server/index.ts",
|
"dev:server": "node scripts/run-tsx.mjs watch server/index.ts",
|
||||||
"dev:web": "vite",
|
"dev:web": "vite",
|
||||||
|
"prebuild": "node scripts/preflight.mjs",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"start": "cross-env NODE_ENV=production tsx server/index.ts",
|
"prestart": "node scripts/preflight.mjs --ports=3000",
|
||||||
|
"start": "cross-env NODE_ENV=production node scripts/run-tsx.mjs server/index.ts",
|
||||||
"typecheck": "tsc -b --noEmit",
|
"typecheck": "tsc -b --noEmit",
|
||||||
"lint": "eslint . --ext ts,tsx",
|
"lint": "eslint . --ext ts,tsx",
|
||||||
"seed": "tsx server/scripts/seed.ts",
|
"seed": "node scripts/run-tsx.mjs server/scripts/seed.ts",
|
||||||
"test:e2e": "node e2e-test.mjs",
|
"test:e2e": "node e2e-test.mjs",
|
||||||
"test:admin": "node admin-test.mjs",
|
"test:admin": "node admin-test.mjs",
|
||||||
"audit:visual": "node visual-audit.mjs"
|
"audit:visual": "node visual-audit.mjs"
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { spawn, spawnSync } from "node:child_process";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const projectDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
|
const runtimeDir = path.join(projectDir, ".cache", "runtime");
|
||||||
|
fs.mkdirSync(runtimeDir, { recursive: true });
|
||||||
|
|
||||||
|
const preflight = spawnSync(process.execPath, [path.join(projectDir, "scripts", "preflight.mjs"), "--ports=3000,5173"], {
|
||||||
|
cwd: projectDir,
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: false,
|
||||||
|
});
|
||||||
|
if (preflight.status !== 0) process.exit(preflight.status ?? 1);
|
||||||
|
|
||||||
|
const npmCli = process.env.npm_execpath || path.join(path.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
||||||
|
const stdout = fs.openSync(path.join(runtimeDir, "dev.stdout.log"), "a");
|
||||||
|
const stderr = fs.openSync(path.join(runtimeDir, "dev.stderr.log"), "a");
|
||||||
|
const child = spawn(process.execPath, [npmCli, "run", "dev"], {
|
||||||
|
cwd: projectDir,
|
||||||
|
detached: true,
|
||||||
|
stdio: ["ignore", stdout, stderr],
|
||||||
|
shell: false,
|
||||||
|
windowsHide: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
child.unref();
|
||||||
|
console.log(`AgendaPro dev iniciado en segundo plano (PID ${child.pid}). Logs: ${path.join(runtimeDir, "dev.stdout.log")}`);
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import net from "node:net";
|
||||||
|
|
||||||
|
const MIN_NODE = [22, 5, 0];
|
||||||
|
const actualNode = process.versions.node.split(".").map(Number);
|
||||||
|
const isVersionAtLeast = actualNode[0] > MIN_NODE[0]
|
||||||
|
|| actualNode[0] === MIN_NODE[0] && (actualNode[1] > MIN_NODE[1]
|
||||||
|
|| actualNode[1] === MIN_NODE[1] && actualNode[2] >= MIN_NODE[2]);
|
||||||
|
|
||||||
|
if (!isVersionAtLeast) {
|
||||||
|
console.error(`AgendaPro requiere Node.js >= ${MIN_NODE.join(".")}; detectado ${process.version}.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await import("node:sqlite");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("La instalación de Node no incluye node:sqlite, requerido por AgendaPro.");
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const portsArg = process.argv.find((arg) => arg.startsWith("--ports="))?.split("=", 2)[1];
|
||||||
|
const ports = portsArg ? portsArg.split(",").map(Number).filter(Boolean) : [];
|
||||||
|
|
||||||
|
function isPortBusy(port) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const socket = net.createConnection({ host: "127.0.0.1", port });
|
||||||
|
let settled = false;
|
||||||
|
const finish = (busy) => {
|
||||||
|
if (settled) return;
|
||||||
|
settled = true;
|
||||||
|
socket.destroy();
|
||||||
|
resolve(busy);
|
||||||
|
};
|
||||||
|
socket.once("connect", () => finish(true));
|
||||||
|
socket.once("error", () => finish(false));
|
||||||
|
socket.setTimeout(500, () => finish(false));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const busyPorts = [];
|
||||||
|
for (const port of ports) {
|
||||||
|
if (await isPortBusy(port)) busyPorts.push(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (busyPorts.length) {
|
||||||
|
console.error(`No se puede iniciar AgendaPro: los puertos ${busyPorts.join(", ")} ya están ocupados.`);
|
||||||
|
console.error("Cierra el proceso anterior o cambia PORT/VITE_PORT; en Windows puedes localizarlo con: netstat -ano | findstr :<puerto>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Preflight OK: Node ${process.version}${ports.length ? `; puertos ${ports.join(", ")} disponibles` : ""}.`);
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import os from "node:os";
|
||||||
|
|
||||||
|
// tsx creates a temporary directory using os.userInfo(). On some Windows
|
||||||
|
// runners that syscall fails even though the rest of Node is healthy.
|
||||||
|
// Keep the normal implementation when it works and provide a safe fallback.
|
||||||
|
const nativeUserInfo = os.userInfo.bind(os);
|
||||||
|
const safeUserInfo = (...args) => {
|
||||||
|
try {
|
||||||
|
return nativeUserInfo(...args);
|
||||||
|
} catch {
|
||||||
|
const username = (process.env.USERNAME || process.env.USER || "agendapro").replace(/[<>:"/\\|?*]/g, "-");
|
||||||
|
return {
|
||||||
|
uid: -1,
|
||||||
|
gid: -1,
|
||||||
|
username,
|
||||||
|
homedir: os.homedir(),
|
||||||
|
shell: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
os.userInfo = safeUserInfo;
|
||||||
|
} catch {
|
||||||
|
Object.defineProperty(os, "userInfo", { configurable: true, writable: true, value: safeUserInfo });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await import("tsx/cli");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("No se pudo iniciar tsx. Ejecuta primero npm run setup.");
|
||||||
|
console.error(error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import { spawnSync } from "node:child_process";
|
||||||
|
|
||||||
|
const cacheDir = path.resolve(".cache", "npm");
|
||||||
|
const npmCli = process.env.npm_execpath || path.join(path.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
||||||
|
const result = spawnSync(process.execPath, [npmCli, "ci", "--cache", cacheDir], {
|
||||||
|
stdio: "inherit",
|
||||||
|
// Invoke npm-cli.js through node so paths with spaces need no shell quoting.
|
||||||
|
shell: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
console.error(`No se pudo ejecutar npm: ${result.error.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(result.status ?? 1);
|
||||||
+10
-4
@@ -1,19 +1,25 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import path from "node:path";
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
|
||||||
|
const srcDir = fileURLToPath(new URL("./src", import.meta.url));
|
||||||
|
const apiTarget = process.env.API_URL || "http://127.0.0.1:3000";
|
||||||
|
const webPort = Number(process.env.VITE_PORT) || 5173;
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "src"),
|
"@": srcDir,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
host: process.env.VITE_HOST || "127.0.0.1",
|
||||||
|
port: webPort,
|
||||||
|
strictPort: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/api": {
|
||||||
target: "http://localhost:3000",
|
target: apiTarget,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user