Replace PGlite with embedded-postgres and add startup banner

Switch from PGlite (WebAssembly) to embedded-postgres for zero-config
local development — provides a real PostgreSQL server with full
compatibility. Add startup banner with config summary on server boot.
Improve server bootstrap with auto port detection, database creation,
and migration on startup. Update DATABASE.md, DEVELOPING.md, and
SPEC-implementation.md to reflect the change. Update CLI database
check and prompts. Simplify OnboardingWizard database options.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-18 11:45:43 -06:00
parent 0d436911cd
commit cc24722090
18 changed files with 738 additions and 101 deletions

251
pnpm-lock.yaml generated
View File

@@ -45,15 +45,12 @@ importers:
packages/db:
dependencies:
'@electric-sql/pglite':
specifier: ^0.3.15
version: 0.3.15
'@paperclip/shared':
specifier: workspace:*
version: link:../shared
drizzle-orm:
specifier: ^0.38.4
version: 0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(postgres@3.4.8)(react@19.2.4)
version: 0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4)
postgres:
specifier: ^3.4.5
version: 3.4.8
@@ -89,9 +86,12 @@ importers:
'@paperclip/shared':
specifier: workspace:*
version: link:../packages/shared
detect-port:
specifier: ^2.1.0
version: 2.1.0
drizzle-orm:
specifier: ^0.38.4
version: 0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(postgres@3.4.8)(react@19.2.4)
version: 0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4)
express:
specifier: ^5.1.0
version: 5.2.1
@@ -107,6 +107,10 @@ importers:
zod:
specifier: ^3.24.2
version: 3.25.76
optionalDependencies:
embedded-postgres:
specifier: ^18.1.0-beta.16
version: 18.1.0-beta.16
devDependencies:
'@types/express':
specifier: ^5.0.0
@@ -126,6 +130,9 @@ importers:
typescript:
specifier: ^5.7.3
version: 5.9.3
vite:
specifier: ^6.1.0
version: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
vitest:
specifier: ^3.0.5
version: 3.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
@@ -294,6 +301,54 @@ packages:
'@electric-sql/pglite@0.3.15':
resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==}
'@embedded-postgres/darwin-arm64@18.1.0-beta.16':
resolution: {integrity: sha512-tU/syLOamFZdXMC+p7AYczmFKIiolFlZ8y3Qb7KonX37O07ezc/OSDiQ641sheV3X0WPf9V10qyK8c81rleDdw==}
engines: {node: '>=16'}
cpu: [arm64]
os: [darwin]
'@embedded-postgres/darwin-x64@18.1.0-beta.16':
resolution: {integrity: sha512-4zHNCscGJt/3pmkpLCuU/IpMJzwENM6OqSHE+WWkOoNqYid49ZnmgB1ltOelgZgRoPRIy/HDEnrMeuVxQHBhEw==}
engines: {node: '>=16'}
cpu: [x64]
os: [darwin]
'@embedded-postgres/linux-arm64@18.1.0-beta.16':
resolution: {integrity: sha512-G0f/reVFc7svqncDQL7blwKulzYIYsz+o/3TEtAJOaGMXYkD8Swzv9RFKfEJOr9+IVRwCmoFFppMeBnUwMoGZg==}
engines: {node: '>=16'}
cpu: [arm64]
os: [linux]
'@embedded-postgres/linux-arm@18.1.0-beta.16':
resolution: {integrity: sha512-aB1t95YGnqay8swh70s7zo587Nog90UL9yUYrzMMNMq40Qfrq9aWiBn0+6vCxp1rSFaPyJMPvW62/rIKLFBkKg==}
engines: {node: '>=16'}
cpu: [arm]
os: [linux]
'@embedded-postgres/linux-ia32@18.1.0-beta.16':
resolution: {integrity: sha512-gMTIryUMnwyLancs34gXqaiuXIFMgn8RGYZ2wJZEuXpW0SQ/cE07kFINmoQO1sN+5T/IR0KvMXPzxo867wO3ew==}
engines: {node: '>=16'}
cpu: [ia32]
os: [linux]
'@embedded-postgres/linux-ppc64@18.1.0-beta.16':
resolution: {integrity: sha512-wTglX0bZVBretiUJrZUO/EmEP8w7jC+i8ZAEKesHHIqIDXEV2F9l+6aTjWt2wRu5SAJ6gpe2RbcKvJ6x+6m1Qw==}
engines: {node: '>=16'}
cpu: [ppc64]
os: [linux]
'@embedded-postgres/linux-x64@18.1.0-beta.16':
resolution: {integrity: sha512-+GIabpHh7QV2AcYBuzyQC41AYczSFphxfHy4ccTPIPrp6OSthZXH+A9fymjQzOiDHg9+1UeYET00Aj7sScjXrg==}
engines: {node: '>=16'}
cpu: [x64]
os: [linux]
'@embedded-postgres/windows-x64@18.1.0-beta.16':
resolution: {integrity: sha512-v6AXH1zi6YyoqPM6U7mX08prJ33yD9gqsbo3YdtPi8FDx0C/y9sYa3aQVf/3blPJvHyERYbY8fZnYXbb79Lo0Q==}
engines: {node: '>=16'}
cpu: [x64]
os: [win32]
'@esbuild-kit/core-utils@3.3.2':
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
deprecated: 'Merged into tsx: https://tsx.is'
@@ -1825,6 +1880,10 @@ packages:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
address@2.0.3:
resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==}
engines: {node: '>= 16.0.0'}
aria-hidden@1.2.6:
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'}
@@ -1836,6 +1895,10 @@ packages:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
async-exit-hook@2.0.1:
resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==}
engines: {node: '>=0.12.0'}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
@@ -1967,6 +2030,11 @@ packages:
detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
detect-port@2.1.0:
resolution: {integrity: sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==}
engines: {node: '>= 16.0.0'}
hasBin: true
dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
@@ -2076,6 +2144,10 @@ packages:
electron-to-chromium@1.5.286:
resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==}
embedded-postgres@18.1.0-beta.16:
resolution: {integrity: sha512-TDp7Ld0h84x5fzIIZFyreYWqZrxUNjuXB6OxqJCmV6PodB2vzQ+1hlL6n4uK1de7bIAxYt5OkDykwcuIONQdQg==}
engines: {node: '>=16'}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
@@ -2430,6 +2502,40 @@ packages:
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
engines: {node: '>= 14.16'}
pg-cloudflare@1.3.0:
resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==}
pg-connection-string@2.11.0:
resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==}
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
pg-pool@3.11.0:
resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==}
peerDependencies:
pg: '>=8.0'
pg-protocol@1.11.0:
resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==}
pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
pg@8.18.0:
resolution: {integrity: sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==}
engines: {node: '>= 16.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -2454,6 +2560,22 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
postgres-bytea@1.0.1:
resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==}
engines: {node: '>=0.10.0'}
postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
postgres-interval@1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
postgres@3.4.8:
resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==}
engines: {node: '>=12'}
@@ -2892,6 +3014,10 @@ packages:
utf-8-validate:
optional: true
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -3025,7 +3151,32 @@ snapshots:
'@drizzle-team/brocli@0.10.2': {}
'@electric-sql/pglite@0.3.15': {}
'@electric-sql/pglite@0.3.15':
optional: true
'@embedded-postgres/darwin-arm64@18.1.0-beta.16':
optional: true
'@embedded-postgres/darwin-x64@18.1.0-beta.16':
optional: true
'@embedded-postgres/linux-arm64@18.1.0-beta.16':
optional: true
'@embedded-postgres/linux-arm@18.1.0-beta.16':
optional: true
'@embedded-postgres/linux-ia32@18.1.0-beta.16':
optional: true
'@embedded-postgres/linux-ppc64@18.1.0-beta.16':
optional: true
'@embedded-postgres/linux-x64@18.1.0-beta.16':
optional: true
'@embedded-postgres/windows-x64@18.1.0-beta.16':
optional: true
'@esbuild-kit/core-utils@3.3.2':
dependencies:
@@ -4367,6 +4518,8 @@ snapshots:
mime-types: 3.0.2
negotiator: 1.0.0
address@2.0.3: {}
aria-hidden@1.2.6:
dependencies:
tslib: 2.8.1
@@ -4375,6 +4528,9 @@ snapshots:
assertion-error@2.0.1: {}
async-exit-hook@2.0.1:
optional: true
asynckit@0.4.0: {}
atomic-sleep@1.0.0: {}
@@ -4487,6 +4643,10 @@ snapshots:
detect-node-es@1.1.0: {}
detect-port@2.1.0:
dependencies:
address: 2.0.3
dezalgo@1.0.4:
dependencies:
asap: 2.0.6
@@ -4501,10 +4661,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
drizzle-orm@0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(postgres@3.4.8)(react@19.2.4):
drizzle-orm@0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4):
optionalDependencies:
'@electric-sql/pglite': 0.3.15
'@types/react': 19.2.14
pg: 8.18.0
postgres: 3.4.8
react: 19.2.4
@@ -4518,6 +4679,23 @@ snapshots:
electron-to-chromium@1.5.286: {}
embedded-postgres@18.1.0-beta.16:
dependencies:
async-exit-hook: 2.0.1
pg: 8.18.0
optionalDependencies:
'@embedded-postgres/darwin-arm64': 18.1.0-beta.16
'@embedded-postgres/darwin-x64': 18.1.0-beta.16
'@embedded-postgres/linux-arm': 18.1.0-beta.16
'@embedded-postgres/linux-arm64': 18.1.0-beta.16
'@embedded-postgres/linux-ia32': 18.1.0-beta.16
'@embedded-postgres/linux-ppc64': 18.1.0-beta.16
'@embedded-postgres/linux-x64': 18.1.0-beta.16
'@embedded-postgres/windows-x64': 18.1.0-beta.16
transitivePeerDependencies:
- pg-native
optional: true
encodeurl@2.0.0: {}
enhanced-resolve@5.19.0:
@@ -4900,6 +5078,48 @@ snapshots:
pathval@2.0.1: {}
pg-cloudflare@1.3.0:
optional: true
pg-connection-string@2.11.0:
optional: true
pg-int8@1.0.1:
optional: true
pg-pool@3.11.0(pg@8.18.0):
dependencies:
pg: 8.18.0
optional: true
pg-protocol@1.11.0:
optional: true
pg-types@2.2.0:
dependencies:
pg-int8: 1.0.1
postgres-array: 2.0.0
postgres-bytea: 1.0.1
postgres-date: 1.0.7
postgres-interval: 1.2.0
optional: true
pg@8.18.0:
dependencies:
pg-connection-string: 2.11.0
pg-pool: 3.11.0(pg@8.18.0)
pg-protocol: 1.11.0
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.3.0
optional: true
pgpass@1.0.5:
dependencies:
split2: 4.2.0
optional: true
picocolors@1.1.1: {}
picomatch@4.0.3: {}
@@ -4937,6 +5157,20 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
postgres-array@2.0.0:
optional: true
postgres-bytea@1.0.1:
optional: true
postgres-date@1.0.7:
optional: true
postgres-interval@1.2.0:
dependencies:
xtend: 4.0.2
optional: true
postgres@3.4.8: {}
process-warning@5.0.0: {}
@@ -5413,6 +5647,9 @@ snapshots:
ws@8.19.0: {}
xtend@4.0.2:
optional: true
yallist@3.1.1: {}
zod@3.25.76: {}