From 5c202ba21e2273bec789849a7a34cd690b6120e4 Mon Sep 17 00:00:00 2001 From: implementer Date: Sat, 29 Aug 2026 11:14:14 +0000 Subject: [PATCH] feat: add database-postgres driver boundary package (E00-S03-T02) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - packages/database-postgres (@personal-blog/database-postgres): the single workspace package allowed to import the PostgreSQL driver — declares pg and kysely as exact dependencies (@types/pg for types) and its src/index.ts imports and re-exports the driver pieces (Pool, Kysely, PostgresDialect) so the isolation is real, not a placeholder - pnpm-lock.yaml: importer for packages/database-postgres plus the resolved pg/kysely dependency tree (frozen-lockfile install keeps working) - .gitea/workflows/ci.yml: new database-postgres-imports job runs tests/database-postgres-imports.test.mjs on every PR so the isolation criterion gates merges --- .gitea/workflows/ci.yml | 16 +++ packages/database-postgres/package.json | 26 +++++ packages/database-postgres/src/index.ts | 20 ++++ packages/database-postgres/tsconfig.json | 8 ++ pnpm-lock.yaml | 135 +++++++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 packages/database-postgres/package.json create mode 100644 packages/database-postgres/src/index.ts create mode 100644 packages/database-postgres/tsconfig.json diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 249222b..abc625a 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -37,6 +37,22 @@ jobs: - name: Run secrets-not-embedded test suite run: node --test tests/secrets-not-embedded.test.mjs + # E00-S03-T02: the static assertions of tests/database-postgres-imports.test.mjs + # gate every PR — the scan proves pg/Kysely imports live only in + # packages/database-postgres and the mutation probes prove the scan catches + # a driver import injected into any other package. + database-postgres-imports: + name: Database-postgres import isolation (E00-S03-T02) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Node.js 24 + uses: actions/setup-node@v4 + with: + node-version: '24' + - name: Run database-postgres import isolation suite + run: node --test tests/database-postgres-imports.test.mjs + # E00-S03-T01: the static assertions of tests/compose-config.test.mjs (db # image pinned to postgres:18.6-bookworm, health gate, volume persistence, # build platforms) gate every PR (the docker-gated real-stack probes inside diff --git a/packages/database-postgres/package.json b/packages/database-postgres/package.json new file mode 100644 index 0000000..95e9e54 --- /dev/null +++ b/packages/database-postgres/package.json @@ -0,0 +1,26 @@ +{ + "name": "@personal-blog/database-postgres", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "EPPP PostgreSQL database adapter package. The single workspace package allowed to import the pg driver and Kysely (E00-S03-T02); the concrete adapter (migration ledger, advisory lock) lands in later stories.", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "kysely": "0.29.5", + "pg": "8.23.0" + }, + "devDependencies": { + "@types/pg": "8.23.1" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + } +} diff --git a/packages/database-postgres/src/index.ts b/packages/database-postgres/src/index.ts new file mode 100644 index 0000000..c7a8780 --- /dev/null +++ b/packages/database-postgres/src/index.ts @@ -0,0 +1,20 @@ +/** + * @personal-blog/database-postgres — EPPP PostgreSQL adapter package. + * + * [E00-S03-T02] this package is the single owner of the PostgreSQL driver + * stack: it is the only workspace package allowed to import `pg` and Kysely. + * Every piece of the workspace that talks to the database goes through this + * boundary — the pool, the query builder dialect and the adapter API this + * package will expose — so no other package ever imports the driver directly. + * + * This module is the driver boundary: it imports the PostgreSQL driver (`pg`) + * and Kysely and re-exports the pieces later stories build the adapter on + * (migration ledger E00-S03-T03, advisory lock E00-S03-T04). Until then the + * re-exports keep the driver reachable only from here — the isolation is real, + * not a placeholder. + */ + +import { Pool } from 'pg'; +import { Kysely, PostgresDialect } from 'kysely'; + +export { Pool, Kysely, PostgresDialect }; diff --git a/packages/database-postgres/tsconfig.json b/packages/database-postgres/tsconfig.json new file mode 100644 index 0000000..5285d28 --- /dev/null +++ b/packages/database-postgres/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 007743e..6630a77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,11 +22,85 @@ importers: packages/core: {} + packages/database-postgres: + dependencies: + kysely: + specifier: 0.29.5 + version: 0.29.5 + pg: + specifier: 8.23.0 + version: 8.23.0 + devDependencies: + '@types/pg': + specifier: 8.23.1 + version: 8.23.1 + packages: '@types/node@24.13.3': resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} + '@types/pg@8.23.1': + resolution: {integrity: sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==} + + kysely@0.29.5: + resolution: {integrity: sha512-ooa+eSbBNPTo3MycPEuW5jdrxQdQwdtB3LC3h43FiXQbIry5tR0C5lDG7eealK0E4D7XjrnOP5DIUg/LyjRMYQ==} + engines: {node: '>=22.0.0'} + + pg-cloudflare@1.4.0: + resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} + + pg-connection-string@2.14.0: + resolution: {integrity: sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.14.0: + resolution: {integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.16.0: + resolution: {integrity: sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.23.0: + resolution: {integrity: sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==} + 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==} + + 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'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} @@ -35,12 +109,73 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + snapshots: '@types/node@24.13.3': dependencies: undici-types: 7.18.2 + '@types/pg@8.23.1': + dependencies: + '@types/node': 24.13.3 + pg-protocol: 1.16.0 + pg-types: 2.2.0 + + kysely@0.29.5: {} + + pg-cloudflare@1.4.0: + optional: true + + pg-connection-string@2.14.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.14.0(pg@8.23.0): + dependencies: + pg: 8.23.0 + + pg-protocol@1.16.0: {} + + 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 + + pg@8.23.0: + dependencies: + pg-connection-string: 2.14.0 + pg-pool: 3.14.0(pg@8.23.0) + pg-protocol: 1.16.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.4.0 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.1: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + split2@4.2.0: {} + typescript@6.0.3: {} undici-types@7.18.2: {} + + xtend@4.0.2: {}