@adminiumjs/adapter-postgres 0.1.0-rc.1
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/LICENSE +661 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -0
- package/dist/introspect.d.ts +56 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +623 -0
- package/dist/introspect.js.map +1 -0
- package/dist/query-engine.d.ts +9 -0
- package/dist/query-engine.d.ts.map +1 -0
- package/dist/query-engine.js +73 -0
- package/dist/query-engine.js.map +1 -0
- package/dist/serialization.d.ts +31 -0
- package/dist/serialization.d.ts.map +1 -0
- package/dist/serialization.js +47 -0
- package/dist/serialization.js.map +1 -0
- package/dist/stats.d.ts +25 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +195 -0
- package/dist/stats.js.map +1 -0
- package/dist/type-map.d.ts +34 -0
- package/dist/type-map.d.ts.map +1 -0
- package/dist/type-map.js +135 -0
- package/dist/type-map.js.map +1 -0
- package/package.json +30 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver error → typed `AdapterError` mapping — 05-introspection-engine.md §3.
|
|
3
|
+
* The Studio wizard and the `diagnostics-readout` widget map `code` to
|
|
4
|
+
* remediation copy, so the mapping here is the whole UX for failure states.
|
|
5
|
+
*/
|
|
6
|
+
import { AdapterError } from '@adminium/engine/adapter';
|
|
7
|
+
const AUTH_SQLSTATES = new Set(['28P01', '28000']);
|
|
8
|
+
const NETWORK_ERRNOS = new Set([
|
|
9
|
+
'ECONNREFUSED',
|
|
10
|
+
'ECONNRESET',
|
|
11
|
+
'ENOTFOUND',
|
|
12
|
+
'EHOSTUNREACH',
|
|
13
|
+
'ENETUNREACH',
|
|
14
|
+
'ETIMEDOUT',
|
|
15
|
+
'EPIPE',
|
|
16
|
+
'EAI_AGAIN',
|
|
17
|
+
]);
|
|
18
|
+
const TLS_CODES = new Set([
|
|
19
|
+
'SELF_SIGNED_CERT_IN_CHAIN',
|
|
20
|
+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
|
|
21
|
+
'CERT_HAS_EXPIRED',
|
|
22
|
+
'DEPTH_ZERO_SELF_SIGNED_CERT',
|
|
23
|
+
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
|
|
24
|
+
'HOSTNAME_MISMATCH',
|
|
25
|
+
]);
|
|
26
|
+
const HINTS = {
|
|
27
|
+
AUTH: 'check the username, password, and database name in the connection string',
|
|
28
|
+
HOST_UNREACHABLE: 'check the host/port and any firewall or IP allowlist — the database must accept connections from this server',
|
|
29
|
+
TLS: "check the sslmode in the DSN and the server's certificate chain",
|
|
30
|
+
PERMISSION: 'the connected role lacks the required privilege — grant it or use a different role',
|
|
31
|
+
TIMEOUT: 'the statement exceeded its time budget; retry or narrow the operation',
|
|
32
|
+
};
|
|
33
|
+
function messageOf(error) {
|
|
34
|
+
return error instanceof Error ? error.message : String(error);
|
|
35
|
+
}
|
|
36
|
+
/** Coerce any driver/socket failure into the one allowed error type. */
|
|
37
|
+
export function toAdapterError(error, context) {
|
|
38
|
+
if (error instanceof AdapterError)
|
|
39
|
+
return error;
|
|
40
|
+
const message = messageOf(error);
|
|
41
|
+
const code = typeof error === 'object' && error !== null && 'code' in error
|
|
42
|
+
? String(error.code)
|
|
43
|
+
: '';
|
|
44
|
+
let mapped = 'UNKNOWN';
|
|
45
|
+
if (AUTH_SQLSTATES.has(code) || code === '3D000')
|
|
46
|
+
mapped = 'AUTH';
|
|
47
|
+
else if (code === '42501')
|
|
48
|
+
mapped = 'PERMISSION';
|
|
49
|
+
else if (code === '57014')
|
|
50
|
+
mapped = 'TIMEOUT';
|
|
51
|
+
else if (code === '42P01' || code === '42703')
|
|
52
|
+
mapped = 'SCHEMA_DRIFT';
|
|
53
|
+
else if (NETWORK_ERRNOS.has(code))
|
|
54
|
+
mapped = 'HOST_UNREACHABLE';
|
|
55
|
+
else if (TLS_CODES.has(code) ||
|
|
56
|
+
code.startsWith('ERR_TLS') ||
|
|
57
|
+
/\b(ssl|tls|certificate)\b/i.test(message)) {
|
|
58
|
+
mapped = 'TLS';
|
|
59
|
+
}
|
|
60
|
+
const options = {
|
|
61
|
+
detail: code.length > 0 ? `${code}: ${message}` : message,
|
|
62
|
+
cause: error,
|
|
63
|
+
};
|
|
64
|
+
const hint = HINTS[mapped];
|
|
65
|
+
if (hint !== undefined)
|
|
66
|
+
options.hint = hint;
|
|
67
|
+
return new AdapterError(mapped, `${context}: ${message}`, options);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAyB,MAAM,0BAA0B,CAAC;AAE/E,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACnD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,cAAc;IACd,YAAY;IACZ,WAAW;IACX,cAAc;IACd,aAAa;IACb,WAAW;IACX,OAAO;IACP,WAAW;CACZ,CAAC,CAAC;AACH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,2BAA2B;IAC3B,iCAAiC;IACjC,kBAAkB;IAClB,6BAA6B;IAC7B,mCAAmC;IACnC,mBAAmB;CACpB,CAAC,CAAC;AAEH,MAAM,KAAK,GAA8C;IACvD,IAAI,EAAE,0EAA0E;IAChF,gBAAgB,EACd,8GAA8G;IAChH,GAAG,EAAE,iEAAiE;IACtE,UAAU,EAAE,oFAAoF;IAChG,OAAO,EAAE,uEAAuE;CACjF,CAAC;AAEF,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,OAAe;IAC5D,IAAI,KAAK,YAAY,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,IAAI,GACR,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK;QAC5D,CAAC,CAAC,MAAM,CAAE,KAA2B,CAAC,IAAI,CAAC;QAC3C,CAAC,CAAC,EAAE,CAAC;IAET,IAAI,MAAM,GAAqB,SAAS,CAAC;IACzC,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,OAAO;QAAE,MAAM,GAAG,MAAM,CAAC;SAC7D,IAAI,IAAI,KAAK,OAAO;QAAE,MAAM,GAAG,YAAY,CAAC;SAC5C,IAAI,IAAI,KAAK,OAAO;QAAE,MAAM,GAAG,SAAS,CAAC;SACzC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO;QAAE,MAAM,GAAG,cAAc,CAAC;SAClE,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,MAAM,GAAG,kBAAkB,CAAC;SAC1D,IACH,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAC1B,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC,EAC1C,CAAC;QACD,MAAM,GAAG,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,GAAsD;QACjE,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO;QACzD,KAAK,EAAE,KAAK;KACb,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAC5C,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,GAAG,OAAO,KAAK,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type AdapterProvider, type AdapterRegistry, type CapabilityProbeResult, type CollectStatsOptions, type ColumnSampleOptions, type ConnectionConfig, type ConnectionRole, type DatabaseAdapter, type DatabaseModel, type Dialect, type FilterSpec, type IntrospectOptions, type MutationResult, type MutationSpec, type QueryResult, type QuerySpec, type Row, type SampleOptions, type StatsResult, type TableRef, type TestResult } from '@adminium/engine/adapter';
|
|
2
|
+
export declare class PostgresAdapter<Role extends ConnectionRole = ConnectionRole> implements DatabaseAdapter<Role> {
|
|
3
|
+
#private;
|
|
4
|
+
readonly dialect: Dialect;
|
|
5
|
+
readonly capabilities: {
|
|
6
|
+
hasEnums: boolean;
|
|
7
|
+
hasFKs: boolean;
|
|
8
|
+
hasSchemas: boolean;
|
|
9
|
+
hasComments: boolean;
|
|
10
|
+
hasChecks: boolean;
|
|
11
|
+
hasRLS: boolean;
|
|
12
|
+
hasMaterializedViews: boolean;
|
|
13
|
+
hasRowEstimates: boolean;
|
|
14
|
+
supportsStatementTimeout: boolean;
|
|
15
|
+
supportsReturning: boolean;
|
|
16
|
+
maxIdentifierLength: number;
|
|
17
|
+
};
|
|
18
|
+
readonly role: Role;
|
|
19
|
+
constructor(role: Role);
|
|
20
|
+
/** Lazy pool — no socket is opened until the first query. */
|
|
21
|
+
connect(config: ConnectionConfig<Role>): Promise<void>;
|
|
22
|
+
test(): Promise<TestResult>;
|
|
23
|
+
/** Runs on every connect/test; results persist on the connection row. */
|
|
24
|
+
probeCapabilities(): Promise<CapabilityProbeResult>;
|
|
25
|
+
/** SCHEMA ONLY — reads `pg_catalog` exclusively (05 §10). */
|
|
26
|
+
introspect(this: DatabaseAdapter<'introspect'>, opts?: IntrospectOptions): Promise<DatabaseModel>;
|
|
27
|
+
count(this: DatabaseAdapter<'data'>, _table: TableRef, _filter?: FilterSpec, _opts?: {
|
|
28
|
+
cap?: number;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
value: number;
|
|
31
|
+
capped: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
sample(this: DatabaseAdapter<'data'>, _table: TableRef, _opts: SampleOptions): Promise<Row[]>;
|
|
34
|
+
sampleColumn(this: DatabaseAdapter<'data'>, _table: TableRef, _column: string, _opts: ColumnSampleOptions): Promise<unknown[]>;
|
|
35
|
+
query(this: DatabaseAdapter<'data'>, _spec: QuerySpec): Promise<QueryResult>;
|
|
36
|
+
mutate(this: DatabaseAdapter<'data'>, _spec: MutationSpec): Promise<MutationResult>;
|
|
37
|
+
/** Aggregate statistics for LLM enrichment (06 §4.2); sample-free by default. */
|
|
38
|
+
collectTableStats(this: DatabaseAdapter<'data'>, table: TableRef, opts?: CollectStatsOptions): Promise<StatsResult>;
|
|
39
|
+
/** Release the pool; idempotent. */
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/** What the server registers at boot (01-architecture.md §2.3.1). */
|
|
43
|
+
export declare const postgresAdapter: AdapterProvider;
|
|
44
|
+
/**
|
|
45
|
+
* Boot registration helper — `@adminium/server` calls `register()` once at
|
|
46
|
+
* startup; tests may pass their own registry instance.
|
|
47
|
+
*/
|
|
48
|
+
export declare function register(registry?: AdapterRegistry<AdapterProvider>): void;
|
|
49
|
+
export { createQueryEngine } from './query-engine.js';
|
|
50
|
+
export { interpretProbe, introspectPostgres, parseCheckEnum, POSTGRES_CAPABILITIES, PROBE_SQL, type CatalogExecutor, type CatalogRow, type IntrospectContext, type ProbeResult, } from './introspect.js';
|
|
51
|
+
export { classifyDefault, mapPostgresType, type MappedType } from './type-map.js';
|
|
52
|
+
export { collectPostgresStats, normalizePgDistinct, type StatsExecutor } from './stats.js';
|
|
53
|
+
export { PG_MAX_IDENTIFIER_LENGTH, postgresSerializers, quoteIdentifier, } from './serialization.js';
|
|
54
|
+
export { toAdapterError } from './errors.js';
|
|
55
|
+
/** @deprecated M0 scaffold export; kept so early imports keep compiling. */
|
|
56
|
+
export declare const PACKAGE_NAME = "@adminium/adapter-postgres";
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,GAAG,EACR,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAkBlC,qBAAa,eAAe,CAAC,IAAI,SAAS,cAAc,GAAG,cAAc,CACvE,YAAW,eAAe,CAAC,IAAI,CAAC;;IAEhC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAc;IACvC,QAAQ,CAAC,YAAY;;;;;;;;;;;;MAAgC;IACrD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;gBAKR,IAAI,EAAE,IAAI;IAItB,6DAA6D;IACvD,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkEtD,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAyBjC,yEAAyE;IACnE,iBAAiB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAezD,6DAA6D;IACvD,UAAU,CACd,IAAI,EAAE,eAAe,CAAC,YAAY,CAAC,EACnC,IAAI,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC,aAAa,CAAC;IAgCnB,KAAK,CACT,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,QAAQ,EAChB,OAAO,CAAC,EAAE,UAAU,EACpB,KAAK,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACvB,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAIxC,MAAM,CACV,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,GAAG,EAAE,CAAC;IAIX,YAAY,CAChB,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,OAAO,EAAE,CAAC;IAIf,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAI5E,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAKzF,iFAAiF;IAC3E,iBAAiB,CACrB,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,KAAK,EAAE,QAAQ,EACf,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,WAAW,CAAC;IAYvB,oCAAoC;IAC9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B;AAED,qEAAqE;AACrE,eAAO,MAAM,eAAe,EAAE,eAU7B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,GAAE,eAAe,CAAC,eAAe,CAAmB,GAAG,IAAI,CAM3F;AAED,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3F,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,4EAA4E;AAC5E,eAAO,MAAM,YAAY,+BAA+B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @adminium/adapter-postgres — the `pg`-driver implementation of the
|
|
3
|
+
* `DatabaseAdapter` contract (05-introspection-engine.md §3/§4.1, M3-T01).
|
|
4
|
+
*
|
|
5
|
+
* Scope in this milestone: connect/test/probeCapabilities, `introspect()`
|
|
6
|
+
* (schema only — never rows), the Kysely query-engine factory, and the boot
|
|
7
|
+
* registration helper. The data-role methods (`query`/`mutate`/`sample`/
|
|
8
|
+
* `count`) land with 05-T05 and currently reject with a typed `UNSUPPORTED`
|
|
9
|
+
* error after their role guard runs.
|
|
10
|
+
*/
|
|
11
|
+
import pg from 'pg';
|
|
12
|
+
import { AdapterError, adapterRegistry, registerAdapter, } from '@adminium/engine/adapter';
|
|
13
|
+
import { toAdapterError } from './errors.js';
|
|
14
|
+
import { interpretProbe, introspectPostgres, POSTGRES_CAPABILITIES, PROBE_SQL, } from './introspect.js';
|
|
15
|
+
import { createQueryEngine } from './query-engine.js';
|
|
16
|
+
import { collectPostgresStats } from './stats.js';
|
|
17
|
+
const INTROSPECT_POOL_MAX = 5;
|
|
18
|
+
const DATA_POOL_MAX = 10;
|
|
19
|
+
const DEFAULT_STATEMENT_TIMEOUT_MS = 15_000;
|
|
20
|
+
export class PostgresAdapter {
|
|
21
|
+
dialect = 'postgres';
|
|
22
|
+
capabilities = { ...POSTGRES_CAPABILITIES };
|
|
23
|
+
role;
|
|
24
|
+
#pool = null;
|
|
25
|
+
#closed = false;
|
|
26
|
+
constructor(role) {
|
|
27
|
+
this.role = role;
|
|
28
|
+
}
|
|
29
|
+
/** Lazy pool — no socket is opened until the first query. */
|
|
30
|
+
async connect(config) {
|
|
31
|
+
if (config.role !== this.role) {
|
|
32
|
+
throw new AdapterError('PERMISSION', `connection config is branded "${config.role}" but this adapter instance is "${this.role}"`, { hint: 'the three logical connections are never interchangeable (01-architecture.md §3)' });
|
|
33
|
+
}
|
|
34
|
+
if (config.dsn === undefined || config.dsn.length === 0) {
|
|
35
|
+
throw new AdapterError('UNKNOWN', 'postgres connections require a DSN', {
|
|
36
|
+
hint: 'pass a postgres:// connection string; TLS is honored from sslmode',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const statementTimeoutMs = Math.floor(config.statementTimeoutMs ?? DEFAULT_STATEMENT_TIMEOUT_MS);
|
|
40
|
+
// Session setup on every new connection — 05 §4.1. Sent in the startup
|
|
41
|
+
// packet (`options`) rather than an on-connect `client.query()`: the pool
|
|
42
|
+
// hands a freshly-connected client to its pending waiter in the same
|
|
43
|
+
// ready-for-query tick as the 'connect' event, so a fire-and-forget setup
|
|
44
|
+
// query would still be in the client's queue when the waiter's query is
|
|
45
|
+
// pushed — pg 8.22 deprecates that (removed in pg@9). Startup options cost
|
|
46
|
+
// zero extra round trips and cannot race. Trade-offs: an explicit
|
|
47
|
+
// `options` overrides any `options=` in the user DSN (rare), and
|
|
48
|
+
// transaction-pooling pgbouncer rejects startup options (the previous
|
|
49
|
+
// per-connection SET was equally broken there).
|
|
50
|
+
const sessionOptions = `-c statement_timeout=${statementTimeoutMs}` +
|
|
51
|
+
(this.role === 'introspect'
|
|
52
|
+
? ' -c lock_timeout=2s -c idle_in_transaction_session_timeout=10s'
|
|
53
|
+
: '');
|
|
54
|
+
const pool = new pg.Pool({
|
|
55
|
+
connectionString: config.dsn,
|
|
56
|
+
max: config.poolMax ?? (this.role === 'introspect' ? INTROSPECT_POOL_MAX : DATA_POOL_MAX),
|
|
57
|
+
options: sessionOptions,
|
|
58
|
+
});
|
|
59
|
+
// Surface idle-client failures as pool-level noise, not process crashes.
|
|
60
|
+
pool.on('error', () => {
|
|
61
|
+
/* mapped when the next query fails */
|
|
62
|
+
});
|
|
63
|
+
this.#pool = pool;
|
|
64
|
+
this.#closed = false;
|
|
65
|
+
}
|
|
66
|
+
#requirePool() {
|
|
67
|
+
if (this.#pool === null || this.#closed) {
|
|
68
|
+
throw new AdapterError('UNKNOWN', 'adapter is not connected — call connect() first');
|
|
69
|
+
}
|
|
70
|
+
return this.#pool;
|
|
71
|
+
}
|
|
72
|
+
async #query(sql) {
|
|
73
|
+
const pool = this.#requirePool();
|
|
74
|
+
try {
|
|
75
|
+
const result = await pool.query(sql);
|
|
76
|
+
return result.rows;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw toAdapterError(error, 'postgres query failed');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async #probe() {
|
|
83
|
+
const rows = await this.#query(PROBE_SQL);
|
|
84
|
+
return interpretProbe(rows[0] ?? {});
|
|
85
|
+
}
|
|
86
|
+
async test() {
|
|
87
|
+
const startedAt = Date.now();
|
|
88
|
+
try {
|
|
89
|
+
const probe = await this.#probe();
|
|
90
|
+
return {
|
|
91
|
+
ok: true,
|
|
92
|
+
latencyMs: Date.now() - startedAt,
|
|
93
|
+
serverVersion: probe.serverVersion,
|
|
94
|
+
currentUser: probe.roleName,
|
|
95
|
+
canWrite: !probe.readOnly,
|
|
96
|
+
ssl: probe.ssl,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
latencyMs: Date.now() - startedAt,
|
|
103
|
+
serverVersion: null,
|
|
104
|
+
currentUser: null,
|
|
105
|
+
canWrite: false,
|
|
106
|
+
ssl: false,
|
|
107
|
+
error: toAdapterError(error, 'connection test failed'),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Runs on every connect/test; results persist on the connection row. */
|
|
112
|
+
async probeCapabilities() {
|
|
113
|
+
const probe = await this.#probe();
|
|
114
|
+
return {
|
|
115
|
+
capabilities: { ...POSTGRES_CAPABILITIES },
|
|
116
|
+
privileges: {
|
|
117
|
+
canReadSchema: true,
|
|
118
|
+
canRead: true,
|
|
119
|
+
canWrite: !probe.readOnly,
|
|
120
|
+
canDDL: probe.canCreate,
|
|
121
|
+
},
|
|
122
|
+
serverVersion: probe.serverVersion,
|
|
123
|
+
currentRole: { name: probe.roleName, readOnly: probe.readOnly },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** SCHEMA ONLY — reads `pg_catalog` exclusively (05 §10). */
|
|
127
|
+
async introspect(opts) {
|
|
128
|
+
const self = this;
|
|
129
|
+
// Runtime guard behind the compile-time role brand (05 §3).
|
|
130
|
+
if (self.role !== 'introspect') {
|
|
131
|
+
throw new AdapterError('PERMISSION', 'introspect() is only available on the introspect-role instance');
|
|
132
|
+
}
|
|
133
|
+
const probe = await self.#probe();
|
|
134
|
+
return introspectPostgres(async (sql) => self.#query(sql), { connectionId: probe.databaseName, databaseName: probe.databaseName }, opts);
|
|
135
|
+
}
|
|
136
|
+
#guardDataRole(method) {
|
|
137
|
+
if (this.role !== 'data') {
|
|
138
|
+
throw new AdapterError('PERMISSION', `${method}() is only available on the data-role instance`, { hint: 'row-touching methods never run on the introspect connection (05 §10)' });
|
|
139
|
+
}
|
|
140
|
+
throw new AdapterError('UNSUPPORTED', `${method}() lands with 05-T05 (dynamic Kysely CRUD)`, {
|
|
141
|
+
hint: 'use createQueryEngine() for the CRUD query port in the meantime',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/* eslint-disable @typescript-eslint/no-unused-vars -- 05-T05 stubs: the
|
|
145
|
+
parameter lists must match the DatabaseAdapter contract exactly. */
|
|
146
|
+
async count(_table, _filter, _opts) {
|
|
147
|
+
return this.#guardDataRole('count');
|
|
148
|
+
}
|
|
149
|
+
async sample(_table, _opts) {
|
|
150
|
+
return this.#guardDataRole('sample');
|
|
151
|
+
}
|
|
152
|
+
async sampleColumn(_table, _column, _opts) {
|
|
153
|
+
return this.#guardDataRole('sampleColumn');
|
|
154
|
+
}
|
|
155
|
+
async query(_spec) {
|
|
156
|
+
return this.#guardDataRole('query');
|
|
157
|
+
}
|
|
158
|
+
async mutate(_spec) {
|
|
159
|
+
return this.#guardDataRole('mutate');
|
|
160
|
+
}
|
|
161
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
162
|
+
/** Aggregate statistics for LLM enrichment (06 §4.2); sample-free by default. */
|
|
163
|
+
async collectTableStats(table, opts) {
|
|
164
|
+
const self = this;
|
|
165
|
+
if (self.role !== 'data') {
|
|
166
|
+
throw new AdapterError('PERMISSION', 'collectTableStats() is only available on the data-role instance', { hint: 'statistics touch user rows and never run on the introspect connection (05 §10)' });
|
|
167
|
+
}
|
|
168
|
+
return collectPostgresStats((sql) => self.#query(sql), table, opts);
|
|
169
|
+
}
|
|
170
|
+
/** Release the pool; idempotent. */
|
|
171
|
+
async close() {
|
|
172
|
+
if (this.#pool === null || this.#closed)
|
|
173
|
+
return;
|
|
174
|
+
this.#closed = true;
|
|
175
|
+
const pool = this.#pool;
|
|
176
|
+
this.#pool = null;
|
|
177
|
+
await pool.end();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** What the server registers at boot (01-architecture.md §2.3.1). */
|
|
181
|
+
export const postgresAdapter = {
|
|
182
|
+
dialect: 'postgres',
|
|
183
|
+
async create(config) {
|
|
184
|
+
const adapter = new PostgresAdapter(config.role);
|
|
185
|
+
await adapter.connect(config);
|
|
186
|
+
return adapter;
|
|
187
|
+
},
|
|
188
|
+
createQueryEngine,
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Boot registration helper — `@adminium/server` calls `register()` once at
|
|
192
|
+
* startup; tests may pass their own registry instance.
|
|
193
|
+
*/
|
|
194
|
+
export function register(registry = adapterRegistry) {
|
|
195
|
+
if (registry === adapterRegistry) {
|
|
196
|
+
registerAdapter(postgresAdapter);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
registry.register(postgresAdapter);
|
|
200
|
+
}
|
|
201
|
+
export { createQueryEngine } from './query-engine.js';
|
|
202
|
+
export { interpretProbe, introspectPostgres, parseCheckEnum, POSTGRES_CAPABILITIES, PROBE_SQL, } from './introspect.js';
|
|
203
|
+
export { classifyDefault, mapPostgresType } from './type-map.js';
|
|
204
|
+
export { collectPostgresStats, normalizePgDistinct } from './stats.js';
|
|
205
|
+
export { PG_MAX_IDENTIFIER_LENGTH, postgresSerializers, quoteIdentifier, } from './serialization.js';
|
|
206
|
+
export { toAdapterError } from './errors.js';
|
|
207
|
+
/** @deprecated M0 scaffold export; kept so early imports keep compiling. */
|
|
208
|
+
export const PACKAGE_NAME = '@adminium/adapter-postgres';
|
|
209
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres introspection — 05-introspection-engine.md §4.1.
|
|
3
|
+
*
|
|
4
|
+
* Reads `pg_catalog`, not `information_schema` (too slow at 500 tables): a
|
|
5
|
+
* FIXED set of 7 set-based catalog queries regardless of table count, joined
|
|
6
|
+
* in memory. This module is executor-agnostic — `introspectPostgres` takes a
|
|
7
|
+
* `CatalogExecutor` (any `sql → rows` function) so the assembly logic is
|
|
8
|
+
* testable without the `pg` driver; `src/index.ts` wires it to the pool.
|
|
9
|
+
*
|
|
10
|
+
* THE "SCHEMA ONLY" INVARIANT (05 §10): every statement here references
|
|
11
|
+
* `pg_catalog.*` exclusively — no user table is ever touched during setup.
|
|
12
|
+
*/
|
|
13
|
+
import { type AdapterCapabilities, type DatabaseModel, type IntrospectOptions } from '@adminium/engine/adapter';
|
|
14
|
+
export type CatalogRow = Record<string, unknown>;
|
|
15
|
+
/** Any `sql → rows` runner (the pg pool in production, psql in the harness). */
|
|
16
|
+
export type CatalogExecutor = (sql: string) => Promise<CatalogRow[]>;
|
|
17
|
+
export interface IntrospectContext {
|
|
18
|
+
/** `adminium_connections` row id; falls back to the database name. */
|
|
19
|
+
connectionId: string;
|
|
20
|
+
/** `current_database()` — becomes `model.name`. */
|
|
21
|
+
databaseName: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Static dialect capabilities — 05 §2.1 (probe refines per-connection).
|
|
25
|
+
* Canonical values live in the engine's capability matrix (M9-T04) so the
|
|
26
|
+
* wizard's degradation copy and the adapter never disagree.
|
|
27
|
+
*/
|
|
28
|
+
export declare const POSTGRES_CAPABILITIES: AdapterCapabilities;
|
|
29
|
+
/**
|
|
30
|
+
* Session probe. Read-only detection per the M3 workplan: recovery mode
|
|
31
|
+
* (`pg_is_in_recovery()`), the session default
|
|
32
|
+
* (`default_transaction_read_only`), or the role lacking CREATE on the
|
|
33
|
+
* database (`has_database_privilege`).
|
|
34
|
+
*/
|
|
35
|
+
export declare const PROBE_SQL = "\nSELECT current_setting('server_version') AS server_version,\n current_user AS role_name,\n pg_catalog.current_database() AS database_name,\n pg_catalog.pg_is_in_recovery() AS in_recovery,\n current_setting('default_transaction_read_only') AS default_read_only,\n pg_catalog.has_database_privilege(pg_catalog.current_database(), 'CREATE') AS can_create,\n COALESCE(\n (SELECT s.ssl FROM pg_catalog.pg_stat_ssl s WHERE s.pid = pg_catalog.pg_backend_pid()),\n false\n ) AS ssl";
|
|
36
|
+
export interface ProbeResult {
|
|
37
|
+
serverVersion: string;
|
|
38
|
+
roleName: string;
|
|
39
|
+
databaseName: string;
|
|
40
|
+
readOnly: boolean;
|
|
41
|
+
canCreate: boolean;
|
|
42
|
+
ssl: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Interpret one {@link PROBE_SQL} row (executor-agnostic, pure). */
|
|
45
|
+
export declare function interpretProbe(row: CatalogRow): ProbeResult;
|
|
46
|
+
/** Parse `col = ANY (ARRAY['a', 'b'])` / `col IN ('a','b')` check shapes. */
|
|
47
|
+
export declare function parseCheckEnum(definition: string): {
|
|
48
|
+
column: string;
|
|
49
|
+
values: string[];
|
|
50
|
+
} | null;
|
|
51
|
+
/**
|
|
52
|
+
* Run the fixed catalog query set through `exec` and assemble the
|
|
53
|
+
* `DatabaseModel`. Schema only — never touches user rows.
|
|
54
|
+
*/
|
|
55
|
+
export declare function introspectPostgres(exec: CatalogExecutor, ctx: IntrospectContext, opts?: IntrospectOptions): Promise<DatabaseModel>;
|
|
56
|
+
//# sourceMappingURL=introspect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAGL,KAAK,mBAAmB,EAExB,KAAK,aAAa,EAIlB,KAAK,iBAAiB,EAIvB,MAAM,0BAA0B,CAAC;AAQlC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;AAErE,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,mBAEnC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,0hBAUN,CAAC;AAEjB,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;CACd;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,WAAW,CAY3D;AA8MD,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,GACjB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAQ7C;AA8CD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,iBAAiB,EACtB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAmVxB"}
|