@kky42/pievo-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +17 -0
- package/.output/public/assets/CodeField-dOzyFQNH.js +34 -0
- package/.output/public/assets/LoopView-B4H9QibC.js +55 -0
- package/.output/public/assets/Modal-D33gNSgx.js +1 -0
- package/.output/public/assets/SignIn-SHuRRyOv.js +1 -0
- package/.output/public/assets/actionUi-ChGGALzg.js +1 -0
- package/.output/public/assets/app-DElSo_jy.css +2 -0
- package/.output/public/assets/artifactView-DFuubXLJ.js +66 -0
- package/.output/public/assets/index-DfpTWRSz.js +12 -0
- package/.output/public/assets/invite._token-DJtd-XE2.js +1 -0
- package/.output/public/assets/invite._token-T8oo4Qtw.js +1 -0
- package/.output/public/assets/jsx-runtime-BseJUIpC.js +1 -0
- package/.output/public/assets/lifecycleUi-DKkChdnU.js +1 -0
- package/.output/public/assets/loopApi-BM2nj71x.js +3 -0
- package/.output/public/assets/loops._loopId-B19Jw7jC.js +5 -0
- package/.output/public/assets/loops._loopId_.runs._runId-_CflHV38.js +3 -0
- package/.output/public/assets/notifyFns-DCubjpRy.js +1 -0
- package/.output/public/assets/preload-helper-CKUiEq3b.js +3 -0
- package/.output/public/assets/routes-B9tgli5f.js +1 -0
- package/.output/public/assets/routes-CTtlM_qt.js +1 -0
- package/.output/public/assets/t._teamId-Bm21mngW.js +1 -0
- package/.output/public/assets/t._teamId-gR7BVKkR.js +1 -0
- package/.output/public/assets/ui-Dp88qUWh.js +1 -0
- package/.output/public/assets/useRouter-DsjH8G0p.js +1 -0
- package/.output/public/favicon.svg +12 -0
- package/.output/public/fonts/GeistPixel-Square.woff2 +0 -0
- package/.output/server/_chunks/ssr-renderer.mjs +26 -0
- package/.output/server/_libs/@aws-crypto/crc32+[...].mjs +419 -0
- package/.output/server/_libs/@aws-sdk/checksums+[...].mjs +10732 -0
- package/.output/server/_libs/@aws-sdk/client-s3+[...].mjs +9475 -0
- package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +137 -0
- package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +440 -0
- package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +62 -0
- package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +818 -0
- package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +59 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +11125 -0
- package/.output/server/_libs/@codemirror/autocomplete+[...].mjs +19713 -0
- package/.output/server/_libs/@codemirror/lang-css+[...].mjs +2636 -0
- package/.output/server/_libs/@codemirror/lang-html+[...].mjs +2247 -0
- package/.output/server/_libs/@codemirror/lang-json+[...].mjs +79 -0
- package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1736 -0
- package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +342 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +14300 -0
- package/.output/server/_libs/@uiw/codemirror-extensions-basic-setup+[...].mjs +76 -0
- package/.output/server/_libs/atomic-sleep.mjs +27 -0
- package/.output/server/_libs/aws-crypto__crc32c.mjs +314 -0
- package/.output/server/_libs/aws-sdk__nested-clients.mjs +2671 -0
- package/.output/server/_libs/babel__runtime.mjs +30 -0
- package/.output/server/_libs/clsx.mjs +16 -0
- package/.output/server/_libs/codemirror__commands.mjs +1725 -0
- package/.output/server/_libs/codemirror__lint.mjs +565 -0
- package/.output/server/_libs/codemirror__search.mjs +1138 -0
- package/.output/server/_libs/codemirror__theme-one-dark.mjs +161 -0
- package/.output/server/_libs/colorette.mjs +129 -0
- package/.output/server/_libs/croner.mjs +735 -0
- package/.output/server/_libs/d3-array.mjs +231 -0
- package/.output/server/_libs/d3-color.mjs +330 -0
- package/.output/server/_libs/d3-format.mjs +252 -0
- package/.output/server/_libs/d3-interpolate.mjs +163 -0
- package/.output/server/_libs/d3-path.mjs +77 -0
- package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
- package/.output/server/_libs/d3-shape.mjs +689 -0
- package/.output/server/_libs/dateformat.mjs +341 -0
- package/.output/server/_libs/decimal.js-light.mjs +884 -0
- package/.output/server/_libs/diff.mjs +732 -0
- package/.output/server/_libs/dom-serializer+[...].mjs +708 -0
- package/.output/server/_libs/domhandler.mjs +461 -0
- package/.output/server/_libs/dompurify.mjs +2111 -0
- package/.output/server/_libs/domutils.mjs +868 -0
- package/.output/server/_libs/drizzle-orm+postgres.mjs +7689 -0
- package/.output/server/_libs/electric-sql__pglite.mjs +14674 -0
- package/.output/server/_libs/end-of-stream+once+wrappy.mjs +145 -0
- package/.output/server/_libs/es-toolkit.mjs +1737 -0
- package/.output/server/_libs/eventemitter3.mjs +236 -0
- package/.output/server/_libs/fast-copy.mjs +285 -0
- package/.output/server/_libs/fast-safe-stringify.mjs +172 -0
- package/.output/server/_libs/h3+rou3+srvx.mjs +1201 -0
- package/.output/server/_libs/html-dom-parser+htmlparser2.mjs +1560 -0
- package/.output/server/_libs/html-react-parser+[...].mjs +1360 -0
- package/.output/server/_libs/initdb.wasm +0 -0
- package/.output/server/_libs/marked.mjs +1548 -0
- package/.output/server/_libs/on-exit-leak-free.mjs +80 -0
- package/.output/server/_libs/pglite.data +37667 -8
- package/.output/server/_libs/pglite.wasm +0 -0
- package/.output/server/_libs/pino+[...].mjs +3196 -0
- package/.output/server/_libs/pino-abstract-transport+split2.mjs +194 -0
- package/.output/server/_libs/pino-pretty+[...].mjs +1623 -0
- package/.output/server/_libs/pinojs__redact.mjs +280 -0
- package/.output/server/_libs/react-redux+[...].mjs +205 -0
- package/.output/server/_libs/recharts+victory-vendor.mjs +15875 -0
- package/.output/server/_libs/tanstack__history.mjs +342 -0
- package/.output/server/_libs/ufo.mjs +64 -0
- package/.output/server/_libs/uiw__codemirror-themes.mjs +32 -0
- package/.output/server/_libs/uiw__react-codemirror.mjs +354 -0
- package/.output/server/_runtime.mjs +37 -0
- package/.output/server/_ssr/CodeField-tm4rr6G3.mjs +143 -0
- package/.output/server/_ssr/DashboardView-CC7htv28.mjs +2795 -0
- package/.output/server/_ssr/LoopPlaybook-BXH8mhiX.mjs +2105 -0
- package/.output/server/_ssr/LoopView-DCugEbHx.mjs +1106 -0
- package/.output/server/_ssr/Modal-DpKZHLZK.mjs +49 -0
- package/.output/server/_ssr/SignIn-dTndU6Hw.mjs +67 -0
- package/.output/server/_ssr/actionUi-CZbEixbf.mjs +119 -0
- package/.output/server/_ssr/adapters-BS8XosjF.mjs +130 -0
- package/.output/server/_ssr/artifactFiles-Cagtj7J0.mjs +61 -0
- package/.output/server/_ssr/artifactKind-BW0isV8L.mjs +37 -0
- package/.output/server/_ssr/artifactView-DS4O22wl.mjs +304 -0
- package/.output/server/_ssr/artifacts-DngKyW2u.mjs +2 -0
- package/.output/server/_ssr/auth-DY1DuYZd.mjs +38429 -0
- package/.output/server/_ssr/boot-BbtG0ImW.mjs +9010 -0
- package/.output/server/_ssr/boot-CMHTxQpJ.mjs +2 -0
- package/.output/server/_ssr/bun-sqlite-dialect-BW9W1_Ps-CU1Spd6z.mjs +162 -0
- package/.output/server/_ssr/core-RrmWoKne.mjs +5 -0
- package/.output/server/_ssr/createServerRpc-BD8Xn_0_.mjs +12 -0
- package/.output/server/_ssr/d1-sqlite-dialect-BLC8LXE6-DfG7tB8s.mjs +112 -0
- package/.output/server/_ssr/db-DWAuZq1o.mjs +2 -0
- package/.output/server/_ssr/dist-BIxFFVZg.mjs +342 -0
- package/.output/server/_ssr/empty-plugin-adapters-DKUlnwwl.mjs +5 -0
- package/.output/server/_ssr/factory-Blq0DYfT.mjs +6903 -0
- package/.output/server/_ssr/invite._token-BRCo9LGB.mjs +36 -0
- package/.output/server/_ssr/invite._token-DJxGLILJ.mjs +28 -0
- package/.output/server/_ssr/invite._token-NewFt27g.mjs +92 -0
- package/.output/server/_ssr/kysely-adapter-D3XTe64J.mjs +2 -0
- package/.output/server/_ssr/kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs +19 -0
- package/.output/server/_ssr/lifecycleUi-C9lg1r3r.mjs +38 -0
- package/.output/server/_ssr/logger-Dw82jNjH.mjs +2 -0
- package/.output/server/_ssr/loopApi-BtfkZQYK.mjs +532 -0
- package/.output/server/_ssr/loopApi-Cxe8WyOB.mjs +105 -0
- package/.output/server/_ssr/loops._loopId-7NQJPB9W.mjs +30 -0
- package/.output/server/_ssr/loops._loopId-DhnArGdL.mjs +1635 -0
- package/.output/server/_ssr/loops._loopId_.runs._runId-Bvuo2nki.mjs +29 -0
- package/.output/server/_ssr/loops._loopId_.runs._runId-Dip5Xk-q.mjs +674 -0
- package/.output/server/_ssr/machineFns-Dvr5ICUy.mjs +266 -0
- package/.output/server/_ssr/node-sqlite-dialect-DhyOpJTX.mjs +162 -0
- package/.output/server/_ssr/notifyFns-BcMRY5kP.mjs +118 -0
- package/.output/server/_ssr/notifyFns-Q458pZmE.mjs +580 -0
- package/.output/server/_ssr/router-J8KZ30xw.mjs +678 -0
- package/.output/server/_ssr/routes-Bcs4T1ox.mjs +28 -0
- package/.output/server/_ssr/routes-BoGrcf-b.mjs +43 -0
- package/.output/server/_ssr/routes-D4am3Zff.mjs +28 -0
- package/.output/server/_ssr/runDiff-CpEFqo7G.mjs +113 -0
- package/.output/server/_ssr/ssr.mjs +5837 -0
- package/.output/server/_ssr/start-4ifArtKx.mjs +4 -0
- package/.output/server/_ssr/store-b3JcQ08F.mjs +2 -0
- package/.output/server/_ssr/t._teamId-CeUhzgzT.mjs +32 -0
- package/.output/server/_ssr/t._teamId-DR-hQK7o.mjs +47 -0
- package/.output/server/_ssr/t._teamId-Dy_McbRt.mjs +31 -0
- package/.output/server/_ssr/teamCookie-B7hLOIie.mjs +10 -0
- package/.output/server/_ssr/teamFns-BIDnsg2A.mjs +473 -0
- package/.output/server/_ssr/teamFns-DA4ovI5g.mjs +33 -0
- package/.output/server/_ssr/tokens-ytd60FUt.mjs +2 -0
- package/.output/server/_ssr/ui-Dej2qjuS.mjs +267 -0
- package/.output/server/_tanstack-start-manifest_v-CVS6c7re.mjs +95 -0
- package/.output/server/drizzle/0000_round_marauders.sql +193 -0
- package/.output/server/drizzle/0001_naive_silver_fox.sql +25 -0
- package/.output/server/drizzle/0002_funny_sage.sql +12 -0
- package/.output/server/drizzle/0003_optimal_slapstick.sql +102 -0
- package/.output/server/drizzle/0004_remove_grok_agent.sql +5 -0
- package/.output/server/drizzle/0005_heavy_gauntlet.sql +8 -0
- package/.output/server/drizzle/0006_tranquil_sharon_carter.sql +1 -0
- package/.output/server/drizzle/0007_faulty_starbolt.sql +30 -0
- package/.output/server/drizzle/0008_durable_lifecycle_evidence.sql +15 -0
- package/.output/server/drizzle/meta/0000_snapshot.json +1333 -0
- package/.output/server/drizzle/meta/0001_snapshot.json +1498 -0
- package/.output/server/drizzle/meta/0002_snapshot.json +1576 -0
- package/.output/server/drizzle/meta/0003_snapshot.json +1666 -0
- package/.output/server/drizzle/meta/0005_snapshot.json +1666 -0
- package/.output/server/drizzle/meta/0006_snapshot.json +1672 -0
- package/.output/server/drizzle/meta/0007_snapshot.json +1787 -0
- package/.output/server/drizzle/meta/_journal.json +69 -0
- package/.output/server/index.mjs +464 -0
- package/.output/server/node_modules/tslib/modules/index.js +70 -0
- package/.output/server/node_modules/tslib/modules/package.json +3 -0
- package/.output/server/node_modules/tslib/package.json +47 -0
- package/.output/server/node_modules/tslib/tslib.js +484 -0
- package/.output/server/package.json +9 -0
- package/LICENSE +21 -0
- package/drizzle/0000_round_marauders.sql +193 -0
- package/drizzle/0001_naive_silver_fox.sql +25 -0
- package/drizzle/0002_funny_sage.sql +12 -0
- package/drizzle/0003_optimal_slapstick.sql +102 -0
- package/drizzle/0004_remove_grok_agent.sql +5 -0
- package/drizzle/0005_heavy_gauntlet.sql +8 -0
- package/drizzle/0006_tranquil_sharon_carter.sql +1 -0
- package/drizzle/0007_faulty_starbolt.sql +30 -0
- package/drizzle/0008_durable_lifecycle_evidence.sql +15 -0
- package/drizzle/meta/0000_snapshot.json +1333 -0
- package/drizzle/meta/0001_snapshot.json +1498 -0
- package/drizzle/meta/0002_snapshot.json +1576 -0
- package/drizzle/meta/0003_snapshot.json +1666 -0
- package/drizzle/meta/0005_snapshot.json +1666 -0
- package/drizzle/meta/0006_snapshot.json +1672 -0
- package/drizzle/meta/0007_snapshot.json +1787 -0
- package/drizzle/meta/_journal.json +69 -0
- package/package.json +84 -0
- package/scripts/pievo-server.mjs +390 -0
- package/scripts/prestart.mjs +66 -0
- package/scripts/server-cli-lib.mjs +451 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "7",
|
|
3
|
+
"dialect": "postgresql",
|
|
4
|
+
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"idx": 0,
|
|
7
|
+
"version": "7",
|
|
8
|
+
"when": 1783393995571,
|
|
9
|
+
"tag": "0000_round_marauders",
|
|
10
|
+
"breakpoints": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idx": 1,
|
|
14
|
+
"version": "7",
|
|
15
|
+
"when": 1783417162251,
|
|
16
|
+
"tag": "0001_naive_silver_fox",
|
|
17
|
+
"breakpoints": true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"idx": 2,
|
|
21
|
+
"version": "7",
|
|
22
|
+
"when": 1783502268448,
|
|
23
|
+
"tag": "0002_funny_sage",
|
|
24
|
+
"breakpoints": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"idx": 3,
|
|
28
|
+
"version": "7",
|
|
29
|
+
"when": 1784459031460,
|
|
30
|
+
"tag": "0003_optimal_slapstick",
|
|
31
|
+
"breakpoints": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"idx": 4,
|
|
35
|
+
"version": "7",
|
|
36
|
+
"when": 1784466600000,
|
|
37
|
+
"tag": "0004_remove_grok_agent",
|
|
38
|
+
"breakpoints": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"idx": 5,
|
|
42
|
+
"version": "7",
|
|
43
|
+
"when": 1784480337337,
|
|
44
|
+
"tag": "0005_heavy_gauntlet",
|
|
45
|
+
"breakpoints": true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"idx": 6,
|
|
49
|
+
"version": "7",
|
|
50
|
+
"when": 1784482476327,
|
|
51
|
+
"tag": "0006_tranquil_sharon_carter",
|
|
52
|
+
"breakpoints": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"idx": 7,
|
|
56
|
+
"version": "7",
|
|
57
|
+
"when": 1784529980417,
|
|
58
|
+
"tag": "0007_faulty_starbolt",
|
|
59
|
+
"breakpoints": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"idx": 8,
|
|
63
|
+
"version": "7",
|
|
64
|
+
"when": 1784530000000,
|
|
65
|
+
"tag": "0008_durable_lifecycle_evidence",
|
|
66
|
+
"breakpoints": true
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kky42/pievo-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/kky42/pievo.git",
|
|
8
|
+
"directory": "packages/server"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"description": "Pievo server — a self-hostable zero-exec control plane for scheduled agent loops.",
|
|
12
|
+
"bin": {
|
|
13
|
+
"pievo-server": "scripts/pievo-server.mjs"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
".output",
|
|
17
|
+
"drizzle",
|
|
18
|
+
"scripts/pievo-server.mjs",
|
|
19
|
+
"scripts/server-cli-lib.mjs",
|
|
20
|
+
"scripts/prestart.mjs",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.13.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"dev": "vite dev",
|
|
31
|
+
"build": "vite build && node scripts/copy-pglite-assets.mjs",
|
|
32
|
+
"build:publish": "vite build && node scripts/copy-pglite-assets.mjs --strict",
|
|
33
|
+
"start": "node scripts/prestart.mjs && node .output/server/index.mjs",
|
|
34
|
+
"routes:generate": "tsr generate",
|
|
35
|
+
"typecheck": "tsr generate && tsc --noEmit",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"db:generate": "drizzle-kit generate",
|
|
38
|
+
"db:migrate": "drizzle-kit migrate",
|
|
39
|
+
"verify:package": "node scripts/verify-package.mjs",
|
|
40
|
+
"prepublishOnly": "pnpm build:publish"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@aws-sdk/client-s3": "^3.1075.0",
|
|
44
|
+
"@base-ui/react": "^1.6.0",
|
|
45
|
+
"@electric-sql/pglite": "^0.5.4",
|
|
46
|
+
"@codemirror/lang-html": "^6.4.11",
|
|
47
|
+
"@codemirror/lang-javascript": "^6.2.5",
|
|
48
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
49
|
+
"@lezer/highlight": "^1.2.3",
|
|
50
|
+
"@tanstack/react-router": "^1.170.16",
|
|
51
|
+
"@tanstack/react-start": "^1.168.26",
|
|
52
|
+
"@uiw/codemirror-themes": "^4.25.10",
|
|
53
|
+
"@uiw/react-codemirror": "^4.25.10",
|
|
54
|
+
"better-auth": "^1.6.19",
|
|
55
|
+
"croner": "^10.0.1",
|
|
56
|
+
"diff": "^9.0.0",
|
|
57
|
+
"dompurify": "^3.4.11",
|
|
58
|
+
"drizzle-orm": "^0.45.2",
|
|
59
|
+
"html-react-parser": "^6.1.3",
|
|
60
|
+
"marked": "^18.0.5",
|
|
61
|
+
"postgres": "^3.4.9",
|
|
62
|
+
"pino": "^10.3.1",
|
|
63
|
+
"pino-pretty": "^13.1.3",
|
|
64
|
+
"react": "^19.2.7",
|
|
65
|
+
"react-dom": "^19.2.7",
|
|
66
|
+
"recharts": "^3.9.1"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@tailwindcss/vite": "^4.3.1",
|
|
70
|
+
"@tanstack/react-router-devtools": "^1.167.0",
|
|
71
|
+
"@tanstack/router-cli": "1.167.17",
|
|
72
|
+
"@types/react": "^19.2.17",
|
|
73
|
+
"@types/react-dom": "^19.2.3",
|
|
74
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
75
|
+
"drizzle-kit": "^0.31.10",
|
|
76
|
+
"jsdom": "^29.1.1",
|
|
77
|
+
"nitro": "3.0.260610-beta",
|
|
78
|
+
"tailwindcss": "^4.3.1",
|
|
79
|
+
"tsx": "^4.22.4",
|
|
80
|
+
"typescript": "^6.0.3",
|
|
81
|
+
"vite": "^8.0.16",
|
|
82
|
+
"vitest": "^4.1.9"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
acquireStartLock,
|
|
9
|
+
acquireStartLockEventually,
|
|
10
|
+
buildEnvPlan,
|
|
11
|
+
buildRestartPlan,
|
|
12
|
+
clearPidRecord,
|
|
13
|
+
parseArgs,
|
|
14
|
+
pidStatus,
|
|
15
|
+
processStartTime,
|
|
16
|
+
readinessReady,
|
|
17
|
+
readPidRecord,
|
|
18
|
+
recordedProcessState,
|
|
19
|
+
terminateRecordedProcess,
|
|
20
|
+
updatePidRecordState,
|
|
21
|
+
usage,
|
|
22
|
+
waitFor,
|
|
23
|
+
withLaunchNonce,
|
|
24
|
+
writePidRecord,
|
|
25
|
+
} from "./server-cli-lib.mjs";
|
|
26
|
+
|
|
27
|
+
const scriptFile = fileURLToPath(import.meta.url);
|
|
28
|
+
const packageRoot = path.resolve(path.dirname(scriptFile), "..");
|
|
29
|
+
const READY_TIMEOUT_MS = 60_000;
|
|
30
|
+
const DETACHED_LAUNCH_TIMEOUT_MS = 120_000;
|
|
31
|
+
const STOP_TIMEOUT_MS = 10_000;
|
|
32
|
+
const FORCE_STOP_TIMEOUT_MS = 2_000;
|
|
33
|
+
const LOCK_HANDOFF_TIMEOUT_MS = 5_000;
|
|
34
|
+
|
|
35
|
+
function installShutdown(plan, expected) {
|
|
36
|
+
let stopping = false;
|
|
37
|
+
let stopSignal;
|
|
38
|
+
let prestartChild;
|
|
39
|
+
let nitroLoaded = false;
|
|
40
|
+
let replayedForNitro = false;
|
|
41
|
+
|
|
42
|
+
const shutdownApplication = () => {
|
|
43
|
+
const booted = globalThis.__pievoBooted;
|
|
44
|
+
if (!booted) return;
|
|
45
|
+
void Promise.resolve(booted)
|
|
46
|
+
.then((value) => value.shutdown())
|
|
47
|
+
.catch((error) => console.error(`[pievo-server] application shutdown failed: ${error instanceof Error ? error.message : error}`));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const onSignal = (signal) => {
|
|
51
|
+
if (!stopping) {
|
|
52
|
+
stopping = true;
|
|
53
|
+
stopSignal = signal;
|
|
54
|
+
console.log(`[pievo-server] received ${signal}; shutting down`);
|
|
55
|
+
shutdownApplication();
|
|
56
|
+
if (prestartChild?.exitCode === null && prestartChild?.signalCode === null) {
|
|
57
|
+
try { prestartChild.kill(signal); } catch {}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
process.on("SIGINT", onSignal);
|
|
63
|
+
process.on("SIGTERM", onSignal);
|
|
64
|
+
// This process is authoritative for its own clean exit. SIGKILL cannot run this
|
|
65
|
+
// hook, so an external stopper still has to prove death before clearing.
|
|
66
|
+
process.on("exit", () => clearPidRecord(plan.pidFile, expected));
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
get stopping() { return stopping; },
|
|
70
|
+
trackPrestart(child) { prestartChild = child; },
|
|
71
|
+
clearPrestart(child) { if (prestartChild === child) prestartChild = undefined; },
|
|
72
|
+
nitroLoaded() {
|
|
73
|
+
nitroLoaded = true;
|
|
74
|
+
// If the first signal arrived while Nitro was importing, its graceful HTTP
|
|
75
|
+
// handler did not see it. Replay once after srvx has installed that handler.
|
|
76
|
+
if (stopping && !replayedForNitro) {
|
|
77
|
+
replayedForNitro = true;
|
|
78
|
+
setImmediate(() => {
|
|
79
|
+
try { process.kill(process.pid, stopSignal ?? "SIGTERM"); } catch {}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
requestShutdown(signal = "SIGTERM") {
|
|
84
|
+
onSignal(signal);
|
|
85
|
+
if (nitroLoaded && !replayedForNitro) {
|
|
86
|
+
replayedForNitro = true;
|
|
87
|
+
setImmediate(() => {
|
|
88
|
+
try { process.kill(process.pid, signal); } catch {}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function runPrestart(plan, shutdown) {
|
|
96
|
+
const child = spawn(process.execPath, [path.join(packageRoot, "scripts", "prestart.mjs")], {
|
|
97
|
+
cwd: packageRoot,
|
|
98
|
+
env: plan.env,
|
|
99
|
+
stdio: "inherit",
|
|
100
|
+
});
|
|
101
|
+
shutdown.trackPrestart(child);
|
|
102
|
+
try {
|
|
103
|
+
const result = await new Promise((resolve, reject) => {
|
|
104
|
+
child.once("error", reject);
|
|
105
|
+
child.once("exit", (code, signal) => resolve({ code, signal }));
|
|
106
|
+
});
|
|
107
|
+
if (shutdown.stopping) throw new Error("startup interrupted by shutdown signal");
|
|
108
|
+
if (result.code !== 0) {
|
|
109
|
+
throw new Error(`migration prestart failed (${result.signal ? `signal ${result.signal}` : `exit ${result.code ?? "unknown"}`})`);
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
shutdown.clearPrestart(child);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function waitForPidAuthority(plan, expected, shutdown) {
|
|
117
|
+
return waitFor(() => {
|
|
118
|
+
if (shutdown.stopping) return true;
|
|
119
|
+
const current = readPidRecord(plan.pidFile);
|
|
120
|
+
return current?.pid === expected.pid
|
|
121
|
+
&& current.startTime === expected.startTime
|
|
122
|
+
&& current.launchNonce === expected.launchNonce;
|
|
123
|
+
}, 5_000);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function serve(plan, expected) {
|
|
127
|
+
// Signal handling comes first: startup remains controllable while waiting for
|
|
128
|
+
// the parent record and while the async migration child is running.
|
|
129
|
+
const shutdown = installShutdown(plan, expected);
|
|
130
|
+
if (!(await waitForPidAuthority(plan, expected, shutdown))) {
|
|
131
|
+
throw new Error("launcher did not establish pid authority before startup");
|
|
132
|
+
}
|
|
133
|
+
if (shutdown.stopping) return;
|
|
134
|
+
|
|
135
|
+
const current = pidStatus(plan.pidFile);
|
|
136
|
+
if (current.state !== "running" || current.record.pid !== process.pid || current.record.startTime !== expected.startTime) {
|
|
137
|
+
throw new Error("pid authority changed before startup");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await runPrestart(plan, shutdown);
|
|
141
|
+
if (shutdown.stopping) return;
|
|
142
|
+
const afterPrestart = readPidRecord(plan.pidFile);
|
|
143
|
+
if (afterPrestart?.pid !== expected.pid
|
|
144
|
+
|| afterPrestart.startTime !== expected.startTime
|
|
145
|
+
|| afterPrestart.launchNonce !== expected.launchNonce) {
|
|
146
|
+
throw new Error("pid authority changed during migration prestart");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
await import(pathToFileURL(path.join(packageRoot, ".output", "server", "index.mjs")).href);
|
|
151
|
+
shutdown.nitroLoaded();
|
|
152
|
+
if (shutdown.stopping) return;
|
|
153
|
+
|
|
154
|
+
const ready = await waitFor(
|
|
155
|
+
() => shutdown.stopping || readinessReady(plan.readyUrl, expected.launchNonce),
|
|
156
|
+
READY_TIMEOUT_MS,
|
|
157
|
+
);
|
|
158
|
+
if (shutdown.stopping) return;
|
|
159
|
+
if (!ready) throw new Error(`server did not become ready at ${plan.readyUrl}`);
|
|
160
|
+
updatePidRecordState(plan, expected, "running");
|
|
161
|
+
console.log(`[pievo-server] ready at http://${plan.host}:${plan.port} (pid ${process.pid})`);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
// Once Nitro may own a listening socket, ask its srvx signal handler to drain
|
|
164
|
+
// HTTP rather than forcing process.exit from our application handler.
|
|
165
|
+
shutdown.requestShutdown("SIGTERM");
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function waitForProcessStart(pid, timeoutMs = 2_000) {
|
|
171
|
+
let value;
|
|
172
|
+
await waitFor(() => {
|
|
173
|
+
value = processStartTime(pid);
|
|
174
|
+
return Boolean(value);
|
|
175
|
+
}, timeoutMs, 25);
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function waitForDetachedReady(plan, expected) {
|
|
180
|
+
const deadline = Date.now() + DETACHED_LAUNCH_TIMEOUT_MS;
|
|
181
|
+
while (Date.now() < deadline) {
|
|
182
|
+
const identity = recordedProcessState(expected);
|
|
183
|
+
if (identity.state === "gone") throw new Error(`server process ${expected.pid} exited during startup`);
|
|
184
|
+
if (identity.state === "unsafe") throw new Error(identity.error);
|
|
185
|
+
const current = readPidRecord(plan.pidFile);
|
|
186
|
+
if (current?.state === "running"
|
|
187
|
+
&& current.pid === expected.pid
|
|
188
|
+
&& current.startTime === expected.startTime
|
|
189
|
+
&& current.launchNonce === expected.launchNonce
|
|
190
|
+
&& await readinessReady(plan.readyUrl, expected.launchNonce)) return;
|
|
191
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
192
|
+
}
|
|
193
|
+
throw new Error(`server failed to become ready; see ${plan.logFile}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function cleanFailedLaunch(plan, expected) {
|
|
197
|
+
const state = recordedProcessState(expected);
|
|
198
|
+
if (state.state === "gone") {
|
|
199
|
+
clearPidRecord(plan.pidFile, expected);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (state.state === "unsafe") throw new Error(`${state.error}; retaining ${plan.pidFile}`);
|
|
203
|
+
await terminateRecordedProcess(expected, {
|
|
204
|
+
gracefulTimeoutMs: STOP_TIMEOUT_MS,
|
|
205
|
+
forceTimeoutMs: FORCE_STOP_TIMEOUT_MS,
|
|
206
|
+
});
|
|
207
|
+
clearPidRecord(plan.pidFile, expected);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function start(basePlan, foreground) {
|
|
211
|
+
const existing = pidStatus(basePlan.pidFile);
|
|
212
|
+
if (existing.state === "running") {
|
|
213
|
+
console.log(`pievo-server is already ${existing.record.state === "starting" ? "starting" : "running"} (pid ${existing.record.pid}, http://${existing.record.host}:${existing.record.port})`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (existing.state === "unsafe") throw new Error(`${existing.error}; refusing to start over the existing pid record`);
|
|
217
|
+
|
|
218
|
+
const release = acquireStartLock(basePlan);
|
|
219
|
+
try {
|
|
220
|
+
const raced = pidStatus(basePlan.pidFile);
|
|
221
|
+
if (raced.state === "running") {
|
|
222
|
+
console.log(`pievo-server is already ${raced.record.state === "starting" ? "starting" : "running"} (pid ${raced.record.pid})`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (raced.state === "unsafe") throw new Error(`${raced.error}; refusing to start over the existing pid record`);
|
|
226
|
+
|
|
227
|
+
const plan = withLaunchNonce(basePlan);
|
|
228
|
+
if (foreground) {
|
|
229
|
+
const record = writePidRecord(plan, {
|
|
230
|
+
launchNonce: plan.launchNonce,
|
|
231
|
+
managedGroup: false,
|
|
232
|
+
state: "starting",
|
|
233
|
+
});
|
|
234
|
+
await serve(plan, record);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fs.mkdirSync(plan.dataDir, { recursive: true });
|
|
239
|
+
const logFd = fs.openSync(plan.logFile, "a", 0o600);
|
|
240
|
+
let child;
|
|
241
|
+
try {
|
|
242
|
+
child = spawn(process.execPath, [scriptFile, "_serve"], {
|
|
243
|
+
cwd: packageRoot,
|
|
244
|
+
env: plan.env,
|
|
245
|
+
detached: true,
|
|
246
|
+
stdio: ["ignore", logFd, logFd],
|
|
247
|
+
});
|
|
248
|
+
} finally {
|
|
249
|
+
fs.closeSync(logFd);
|
|
250
|
+
}
|
|
251
|
+
if (!child.pid) throw new Error("failed to spawn server process");
|
|
252
|
+
child.unref();
|
|
253
|
+
|
|
254
|
+
const childStart = await waitForProcessStart(child.pid);
|
|
255
|
+
if (!childStart) throw new Error(`cannot verify spawned server process ${child.pid}`);
|
|
256
|
+
const record = writePidRecord(plan, {
|
|
257
|
+
pid: child.pid,
|
|
258
|
+
startTime: childStart,
|
|
259
|
+
launchNonce: plan.launchNonce,
|
|
260
|
+
managedGroup: true,
|
|
261
|
+
state: "starting",
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
await waitForDetachedReady(plan, record);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
try {
|
|
268
|
+
await cleanFailedLaunch(plan, record);
|
|
269
|
+
} catch (cleanupError) {
|
|
270
|
+
throw new Error(`${error instanceof Error ? error.message : error}; cleanup failed: ${cleanupError instanceof Error ? cleanupError.message : cleanupError}`);
|
|
271
|
+
}
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
console.log(`pievo-server started (pid ${child.pid}, http://${plan.host}:${plan.port})`);
|
|
275
|
+
console.log(`log: ${plan.logFile}`);
|
|
276
|
+
} finally {
|
|
277
|
+
release();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function stop(plan) {
|
|
282
|
+
let release;
|
|
283
|
+
let stoppedRecord;
|
|
284
|
+
try {
|
|
285
|
+
try {
|
|
286
|
+
release = acquireStartLock(plan);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error?.code !== "START_LOCKED") throw error;
|
|
289
|
+
// A detached launcher owns the lock until readiness/failure. Its child pid
|
|
290
|
+
// record is written before migrations, so stop can still terminate exactly
|
|
291
|
+
// that starting process, then wait for the launcher to relinquish the lock.
|
|
292
|
+
const launching = pidStatus(plan.pidFile);
|
|
293
|
+
if (launching.state !== "running") {
|
|
294
|
+
if (launching.state === "unsafe") throw new Error(`${launching.error}; refusing to stop during launch`);
|
|
295
|
+
throw new Error("a launch is in progress but no server process can be safely identified");
|
|
296
|
+
}
|
|
297
|
+
stoppedRecord = launching.record;
|
|
298
|
+
const result = await terminateRecordedProcess(stoppedRecord, {
|
|
299
|
+
gracefulTimeoutMs: STOP_TIMEOUT_MS,
|
|
300
|
+
forceTimeoutMs: FORCE_STOP_TIMEOUT_MS,
|
|
301
|
+
});
|
|
302
|
+
if (result.forced) console.warn(`[pievo-server] graceful stop timed out; killed pid ${stoppedRecord.pid}`);
|
|
303
|
+
clearPidRecord(plan.pidFile, stoppedRecord);
|
|
304
|
+
release = await acquireStartLockEventually(plan, LOCK_HANDOFF_TIMEOUT_MS);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const status = pidStatus(plan.pidFile);
|
|
308
|
+
if (status.state === "unsafe") throw new Error(`${status.error}; refusing to signal the existing pid record`);
|
|
309
|
+
if (status.state === "running") {
|
|
310
|
+
stoppedRecord = status.record;
|
|
311
|
+
const result = await terminateRecordedProcess(stoppedRecord, {
|
|
312
|
+
gracefulTimeoutMs: STOP_TIMEOUT_MS,
|
|
313
|
+
forceTimeoutMs: FORCE_STOP_TIMEOUT_MS,
|
|
314
|
+
});
|
|
315
|
+
if (result.forced) console.warn(`[pievo-server] graceful stop timed out; killed pid ${stoppedRecord.pid}`);
|
|
316
|
+
clearPidRecord(plan.pidFile, stoppedRecord);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (stoppedRecord) console.log(`pievo-server stopped (pid ${stoppedRecord.pid})`);
|
|
320
|
+
else console.log("pievo-server is not running");
|
|
321
|
+
} finally {
|
|
322
|
+
release?.();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function status(plan) {
|
|
327
|
+
const current = pidStatus(plan.pidFile);
|
|
328
|
+
if (current.state === "running") {
|
|
329
|
+
console.log(`pievo-server is ${current.record.state === "starting" ? "starting" : "running"} (pid ${current.record.pid})`);
|
|
330
|
+
console.log(`url: http://${current.record.host}:${current.record.port}`);
|
|
331
|
+
console.log(`data: ${plan.dataDir}`);
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
334
|
+
if (current.state === "unsafe") {
|
|
335
|
+
const pid = current.record?.pid ? ` (pid ${current.record.pid})` : "";
|
|
336
|
+
console.error(`pievo-server status is unsafe: ${current.error}${pid}`);
|
|
337
|
+
return 1;
|
|
338
|
+
}
|
|
339
|
+
console.log(`pievo-server is stopped${current.stale ? " (cleared stale pid file)" : ""}`);
|
|
340
|
+
return 1;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function main() {
|
|
344
|
+
let options;
|
|
345
|
+
try {
|
|
346
|
+
options = parseArgs(process.argv.slice(2));
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error(`pievo-server: ${error.message}\n\n${usage()}`);
|
|
349
|
+
process.exitCode = 2;
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (options.command === "help") {
|
|
353
|
+
console.log(usage());
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const inheritedNonce = process.env.PIEVO_SERVER_LAUNCH_NONCE;
|
|
358
|
+
const instancePlan = buildEnvPlan(options);
|
|
359
|
+
if (options.command === "_serve") {
|
|
360
|
+
if (!inheritedNonce) throw new Error("internal serve command requires a launch nonce");
|
|
361
|
+
const plan = { ...instancePlan, launchNonce: inheritedNonce, env: { ...instancePlan.env, PIEVO_SERVER_LAUNCH_NONCE: inheritedNonce } };
|
|
362
|
+
const startTime = processStartTime(process.pid);
|
|
363
|
+
if (!startTime) throw new Error("cannot verify server process identity");
|
|
364
|
+
const expected = {
|
|
365
|
+
pid: process.pid,
|
|
366
|
+
startTime,
|
|
367
|
+
launchNonce: inheritedNonce,
|
|
368
|
+
managedGroup: true,
|
|
369
|
+
};
|
|
370
|
+
return serve(plan, expected);
|
|
371
|
+
}
|
|
372
|
+
if (options.command === "start") return start(instancePlan, options.foreground);
|
|
373
|
+
if (options.command === "stop") return stop(instancePlan);
|
|
374
|
+
if (options.command === "status") {
|
|
375
|
+
process.exitCode = status(instancePlan);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (options.command === "restart") {
|
|
379
|
+
const before = pidStatus(instancePlan.pidFile);
|
|
380
|
+
if (before.state === "unsafe") throw new Error(`${before.error}; refusing to restart the existing pid record`);
|
|
381
|
+
const restartPlan = buildRestartPlan(options, process.env, before.record);
|
|
382
|
+
await stop(instancePlan);
|
|
383
|
+
await start(restartPlan, options.foreground);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
main().catch((error) => {
|
|
388
|
+
console.error(`pievo-server: ${error instanceof Error ? error.message : error}`);
|
|
389
|
+
process.exitCode = 1;
|
|
390
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Pre-start migration gate + the BUILT server's boot-time config guard.
|
|
3
|
+
//
|
|
4
|
+
// HOSTED Postgres tier (DATABASE_URL set): apply pending migrations over the
|
|
5
|
+
// DIRECT/session URL using the SAME postgres-js migrator the app uses at boot
|
|
6
|
+
// (db/index.ts `runMigrations`). We deliberately do NOT shell out to
|
|
7
|
+
// `drizzle-kit migrate` — its bundled driver hangs/fails against the Supabase
|
|
8
|
+
// Supavisor pooler, whereas postgres-js connects cleanly. Running it here (a
|
|
9
|
+
// separate pre-serve step) makes a bad migration fail the deploy LOUDLY.
|
|
10
|
+
//
|
|
11
|
+
// Embedded pglite tier (no DATABASE_URL - light self-host): the database
|
|
12
|
+
// migrates IN-PROCESS at boot, so no migration here - but the tier must be
|
|
13
|
+
// OPTED INTO with PIEVO_DB=pglite. The built artifact carries production
|
|
14
|
+
// semantics (vite bakes NODE_ENV=production into the bundle), so a missing
|
|
15
|
+
// DATABASE_URL secret must fail the container HERE, at startup, loudly -
|
|
16
|
+
// db/index.ts has the same refusal, but it sits in a lazily-imported chunk,
|
|
17
|
+
// so on its own the container would boot green and only 500 on the first DB
|
|
18
|
+
// request. `pnpm start` and the Docker CMD both run this script first, which
|
|
19
|
+
// turns that late 500 into a nonzero exit before the server ever serves.
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
|
|
23
|
+
if (!process.env.DATABASE_URL) {
|
|
24
|
+
if (process.env.PIEVO_DB !== "pglite") {
|
|
25
|
+
console.error(
|
|
26
|
+
"[prestart] refusing to start on the ephemeral embedded database - " +
|
|
27
|
+
"set DATABASE_URL (hosted Postgres), or PIEVO_DB=pglite to opt into " +
|
|
28
|
+
"the embedded pglite tier (and point PIEVO_DATA_DIR at persistent storage)",
|
|
29
|
+
);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
console.log("[prestart] no DATABASE_URL - embedded pglite (PIEVO_DB=pglite) migrates in-process at boot; skipping");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { default: postgres } = await import("postgres");
|
|
37
|
+
const { drizzle } = await import("drizzle-orm/postgres-js");
|
|
38
|
+
const { migrate } = await import("drizzle-orm/postgres-js/migrator");
|
|
39
|
+
|
|
40
|
+
// DDL + the migrator advisory lock run over the DIRECT (session-mode) URL —
|
|
41
|
+
// never the transaction pooler. Falls back to DATABASE_URL for a plain Postgres,
|
|
42
|
+
// but FAILS LOUD when that fallback would route DDL over the Supabase transaction
|
|
43
|
+
// pooler (it multiplexes connections and cannot hold the migrator's session-scoped
|
|
44
|
+
// advisory lock) - mirrors env.ts `directDatabaseUrl()`, which this script can't
|
|
45
|
+
// import (it runs unbundled, without the TS build).
|
|
46
|
+
const url = process.env.DIRECT_DATABASE_URL || process.env.DATABASE_URL;
|
|
47
|
+
if (!process.env.DIRECT_DATABASE_URL && (url.includes(":6543") || url.includes("pooler.supabase.com"))) {
|
|
48
|
+
console.error(
|
|
49
|
+
"[prestart] DIRECT_DATABASE_URL is unset but DATABASE_URL points at the Supabase transaction pooler " +
|
|
50
|
+
"(:6543 / pooler.supabase.com) - set DIRECT_DATABASE_URL to the direct (:5432, session-mode) " +
|
|
51
|
+
"connection so migrations run off the pooler",
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const migrationsFolder = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "drizzle");
|
|
56
|
+
|
|
57
|
+
const client = postgres(url, { max: 1 });
|
|
58
|
+
try {
|
|
59
|
+
await migrate(drizzle(client), { migrationsFolder });
|
|
60
|
+
console.log("[prestart] migrations applied");
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.error("[prestart] migration failed:", e?.message ?? e);
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
} finally {
|
|
65
|
+
await client.end();
|
|
66
|
+
}
|