@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-engine.d.ts","sourceRoot":"","sources":["../src/query-engine.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAUlC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,GAAG,WAAW,CAoDpF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createQueryEngine()` — the Kysely dialect factory for the pooled data
|
|
3
|
+
* connection (05-introspection-engine.md §3 `QueryEngine`, 08-server-api.md
|
|
4
|
+
* §3.7 "query port").
|
|
5
|
+
*
|
|
6
|
+
* `@adminium/engine` does not depend on `kysely`, so `QueryEngine.dialect`
|
|
7
|
+
* is typed opaquely there; `@adminium/server` casts it to `kysely.Dialect`
|
|
8
|
+
* at the composition boundary. This package owns the concrete dependency.
|
|
9
|
+
*/
|
|
10
|
+
import { PostgresDialect } from 'kysely';
|
|
11
|
+
import pg from 'pg';
|
|
12
|
+
import { AdapterError, } from '@adminium/engine/adapter';
|
|
13
|
+
import { PG_MAX_IDENTIFIER_LENGTH, postgresSerializers, quoteIdentifier, } from './serialization.js';
|
|
14
|
+
const DEFAULT_DATA_POOL_MAX = 10;
|
|
15
|
+
/**
|
|
16
|
+
* Build the CRUD query port for a data-role connection. Accepts a DSN string
|
|
17
|
+
* or the role-branded config (which must carry a `dsn`). The pool is lazy —
|
|
18
|
+
* no connection is opened until the first query — and `destroy()` tears it
|
|
19
|
+
* down idempotently.
|
|
20
|
+
*/
|
|
21
|
+
export function createQueryEngine(config) {
|
|
22
|
+
const dsn = typeof config === 'string' ? config : config.dsn;
|
|
23
|
+
if (dsn === undefined || dsn.length === 0) {
|
|
24
|
+
throw new AdapterError('UNKNOWN', 'postgres query engine requires a DSN', {
|
|
25
|
+
hint: 'pass a postgres:// connection string (TLS options honored from sslmode)',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const poolMax = typeof config === 'string' ? undefined : config.poolMax;
|
|
29
|
+
const pool = new pg.Pool({
|
|
30
|
+
connectionString: dsn,
|
|
31
|
+
max: poolMax ?? DEFAULT_DATA_POOL_MAX,
|
|
32
|
+
});
|
|
33
|
+
// Surface idle-client failures as pool-level noise, not process crashes —
|
|
34
|
+
// the same guard `createAdapter`'s pool carries in ./index.ts, which this
|
|
35
|
+
// pool was missing.
|
|
36
|
+
//
|
|
37
|
+
// `pg` emits 'error' on the POOL when a client dies while idle in the pool
|
|
38
|
+
// (nobody is awaiting a query, so there is no promise to reject). An EventEmitter
|
|
39
|
+
// 'error' event with no listener is rethrown as an uncaught exception, so a
|
|
40
|
+
// Postgres-side termination of an idle connection — failover, a restart, an
|
|
41
|
+
// admin `pg_terminate_backend`, `DROP DATABASE ... WITH (FORCE)`, or an idle
|
|
42
|
+
// timeout on the server — would take the whole Node process down rather than
|
|
43
|
+
// being retried on the next checkout. Every one of those is routine operational
|
|
44
|
+
// life for a long-lived data pool, and the pool recovers on its own: the dead
|
|
45
|
+
// client is discarded and the next `connect()` opens a fresh one.
|
|
46
|
+
//
|
|
47
|
+
// Found by the M7 Wave 4 verification pass: apps/server's crud.test.ts drops its
|
|
48
|
+
// test database WITH (FORCE) in `afterAll`, which terminates any client still
|
|
49
|
+
// closing (57P01). That surfaced as a *rare* unhandled error failing the whole
|
|
50
|
+
// `pnpm test` run — the harness merely reproduced, under load, what a production
|
|
51
|
+
// failover does on purpose.
|
|
52
|
+
pool.on('error', () => {
|
|
53
|
+
/* mapped when the next query fails */
|
|
54
|
+
});
|
|
55
|
+
// Structural cast: kysely's `PostgresPool` narrows `QueryResult.command`
|
|
56
|
+
// to a command-name union while `pg` types it as `string`. The runtime
|
|
57
|
+
// shapes are identical (kysely's own docs pass a `pg.Pool` here), and the
|
|
58
|
+
// dialect crosses the `QueryEngine` boundary as `unknown` anyway.
|
|
59
|
+
const kyselyPool = pool;
|
|
60
|
+
let destroyed = false;
|
|
61
|
+
return {
|
|
62
|
+
dialect: new PostgresDialect({ pool: kyselyPool }),
|
|
63
|
+
identifiers: { quote: quoteIdentifier, maxLength: PG_MAX_IDENTIFIER_LENGTH },
|
|
64
|
+
serializers: postgresSerializers,
|
|
65
|
+
async destroy() {
|
|
66
|
+
if (destroyed)
|
|
67
|
+
return;
|
|
68
|
+
destroyed = true;
|
|
69
|
+
await pool.end();
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=query-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-engine.js","sourceRoot":"","sources":["../src/query-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,eAAe,EAA8B,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,OAAO,EACL,YAAY,GAGb,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAqC;IACrE,MAAM,GAAG,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAC7D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,sCAAsC,EAAE;YACxE,IAAI,EAAE,yEAAyE;SAChF,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IACxE,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC;QACvB,gBAAgB,EAAE,GAAG;QACrB,GAAG,EAAE,OAAO,IAAI,qBAAqB;KACtC,CAAC,CAAC;IACH,0EAA0E;IAC1E,0EAA0E;IAC1E,oBAAoB;IACpB,EAAE;IACF,2EAA2E;IAC3E,kFAAkF;IAClF,4EAA4E;IAC5E,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,gFAAgF;IAChF,8EAA8E;IAC9E,kEAAkE;IAClE,EAAE;IACF,iFAAiF;IACjF,8EAA8E;IAC9E,+EAA+E;IAC/E,iFAAiF;IACjF,4BAA4B;IAC5B,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACpB,sCAAsC;IACxC,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,uEAAuE;IACvE,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,UAAU,GAAG,IAAgD,CAAC;IAEpE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,OAAO;QACL,OAAO,EAAE,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAClD,WAAW,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,wBAAwB,EAAE;QAC5E,WAAW,EAAE,mBAAmB;QAChC,KAAK,CAAC,OAAO;YACX,IAAI,SAAS;gBAAE,OAAO;YACtB,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifier quoting + per-LogicalType value serialization for the Postgres
|
|
3
|
+
* data connection — 05-introspection-engine.md §3 (`QueryEngine`) and
|
|
4
|
+
* 08-server-api.md §3.7. Pure module (no `pg`/`kysely` import) so the policy
|
|
5
|
+
* is unit-testable offline.
|
|
6
|
+
*
|
|
7
|
+
* Serialization policy (05 §3 `TypeSerializer` contract):
|
|
8
|
+
* - `bigint` (int8) and `decimal` (numeric/money) travel as STRINGS end to
|
|
9
|
+
* end — the pg driver already returns them as strings and JS numbers would
|
|
10
|
+
* silently lose precision.
|
|
11
|
+
* - timestamps serialize to ISO-8601 UTC; `date`/`time` keep their SQL text
|
|
12
|
+
* forms (`YYYY-MM-DD`, `HH:MM:SS`).
|
|
13
|
+
* - `json` values pass through as parsed objects; `toDb` stringifies so
|
|
14
|
+
* Kysely binds a single jsonb parameter.
|
|
15
|
+
* - BYTEA POLICY: `binary` columns are EXCLUDED from CRUD v1 — no serializer
|
|
16
|
+
* is registered for `'binary'`. Grids/forms never select bytea columns
|
|
17
|
+
* (they render as an attachment placeholder); file/blob transfer arrives
|
|
18
|
+
* with the dedicated attachment endpoints post-v1.
|
|
19
|
+
*/
|
|
20
|
+
import type { LogicalType, TypeSerializer } from '@adminium/engine/adapter';
|
|
21
|
+
/** Postgres truncates identifiers at 63 bytes (NAMEDATALEN - 1). */
|
|
22
|
+
export declare const PG_MAX_IDENTIFIER_LENGTH = 63;
|
|
23
|
+
/**
|
|
24
|
+
* Double-quote an identifier, escaping embedded quotes. Identifiers are
|
|
25
|
+
* snapshot-validated before they ever reach this point (no raw SQL escape
|
|
26
|
+
* hatch — 05 §3); quoting is defense in depth, not sanitization.
|
|
27
|
+
*/
|
|
28
|
+
export declare function quoteIdentifier(identifier: string): string;
|
|
29
|
+
/** Per-LogicalType converters handed to the CRUD layer via `QueryEngine`. */
|
|
30
|
+
export declare const postgresSerializers: Partial<Record<LogicalType, TypeSerializer>>;
|
|
31
|
+
//# sourceMappingURL=serialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../src/serialization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE5E,oEAAoE;AACpE,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AA2BD,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAW5E,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Postgres truncates identifiers at 63 bytes (NAMEDATALEN - 1). */
|
|
2
|
+
export const PG_MAX_IDENTIFIER_LENGTH = 63;
|
|
3
|
+
/**
|
|
4
|
+
* Double-quote an identifier, escaping embedded quotes. Identifiers are
|
|
5
|
+
* snapshot-validated before they ever reach this point (no raw SQL escape
|
|
6
|
+
* hatch — 05 §3); quoting is defense in depth, not sanitization.
|
|
7
|
+
*/
|
|
8
|
+
export function quoteIdentifier(identifier) {
|
|
9
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
10
|
+
}
|
|
11
|
+
function toIsoUtc(value) {
|
|
12
|
+
if (value instanceof Date)
|
|
13
|
+
return value.toISOString();
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
/** `YYYY-MM-DD` from a driver-parsed Date (local calendar date, not UTC). */
|
|
17
|
+
function toIsoDate(value) {
|
|
18
|
+
if (value instanceof Date) {
|
|
19
|
+
const month = String(value.getMonth() + 1).padStart(2, '0');
|
|
20
|
+
const day = String(value.getDate()).padStart(2, '0');
|
|
21
|
+
return `${value.getFullYear()}-${month}-${day}`;
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function numericToDb(value) {
|
|
26
|
+
if (typeof value === 'bigint' || typeof value === 'number')
|
|
27
|
+
return value.toString();
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
const lossless = {
|
|
31
|
+
toDb: numericToDb,
|
|
32
|
+
fromDb: (value) => (typeof value === 'number' ? value.toString() : value),
|
|
33
|
+
};
|
|
34
|
+
/** Per-LogicalType converters handed to the CRUD layer via `QueryEngine`. */
|
|
35
|
+
export const postgresSerializers = {
|
|
36
|
+
bigint: lossless,
|
|
37
|
+
decimal: lossless,
|
|
38
|
+
timestamp: { toDb: (value) => value, fromDb: toIsoUtc },
|
|
39
|
+
timestamptz: { toDb: (value) => value, fromDb: toIsoUtc },
|
|
40
|
+
date: { toDb: (value) => value, fromDb: toIsoDate },
|
|
41
|
+
json: {
|
|
42
|
+
toDb: (value) => (typeof value === 'string' ? value : JSON.stringify(value)),
|
|
43
|
+
fromDb: (value) => value,
|
|
44
|
+
},
|
|
45
|
+
// NOTE: no 'binary' entry on purpose — bytea is excluded from CRUD v1.
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=serialization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.js","sourceRoot":"","sources":["../src/serialization.ts"],"names":[],"mappings":"AAqBA,oEAAoE;AACpE,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;IACtD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6EAA6E;AAC7E,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,OAAO,GAAG,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IACpF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,QAAQ,GAAmB;IAC/B,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;CAC1E,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAiD;IAC/E,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE;IACvD,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE;IACzD,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE;IACnD,IAAI,EAAE;QACJ,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC5E,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;KACzB;IACD,uEAAuE;CACxE,CAAC"}
|
package/dist/stats.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres table-statistics collector — 06-llm-assist.md §4.2.
|
|
3
|
+
*
|
|
4
|
+
* Cheap-first strategy (sample-free by default): row-count from
|
|
5
|
+
* `pg_class.reltuples` with an exact `COUNT(*)` fallback for small/stale
|
|
6
|
+
* tables; per-column null-fraction and distinct-count from `pg_stats` when the
|
|
7
|
+
* table has been analyzed, else a single bounded full-scan of aggregates
|
|
8
|
+
* (`SUM(col IS NULL)`, `COUNT(DISTINCT col)`) — aggregates only, never a cell
|
|
9
|
+
* value. `pg_stats.most_common_vals` is row data and is deliberately never
|
|
10
|
+
* read: concrete values appear ONLY under the sampling opt-in, and NEVER for
|
|
11
|
+
* PII-suspected or secret columns.
|
|
12
|
+
*
|
|
13
|
+
* Executor-agnostic (like introspect.ts): takes a `sql → rows` runner so the
|
|
14
|
+
* logic is unit-testable without the `pg` driver.
|
|
15
|
+
*/
|
|
16
|
+
import type { CollectStatsOptions, StatsResult, TableRef } from '@adminium/engine/adapter';
|
|
17
|
+
/** Any `sql → rows` runner (the pg data pool in production). */
|
|
18
|
+
export type StatsExecutor = (sql: string) => Promise<Record<string, unknown>[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Normalize `pg_stats.n_distinct`: `>0` is an absolute estimate, `<0` is the
|
|
21
|
+
* negative fraction of the row count, `0`/absent means unknown.
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizePgDistinct(nDistinct: number | null, rowCount: number | null): number | null;
|
|
24
|
+
export declare function collectPostgresStats(exec: StatsExecutor, table: TableRef, opts?: CollectStatsOptions): Promise<StatsResult>;
|
|
25
|
+
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EACV,mBAAmB,EAGnB,WAAW,EAEX,QAAQ,EACT,MAAM,0BAA0B,CAAC;AAUlC,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAmChF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAKpG;AA8BD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,QAAQ,EACf,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,WAAW,CAAC,CAmItB"}
|
package/dist/stats.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { STATS_DEFAULT_SAMPLE_VALUES, STATS_EXACT_COUNT_THRESHOLD, STATS_MAX_SCAN_ROWS, STATS_SAMPLE_VALUE_MAX_CHARS, } from '@adminium/engine/adapter';
|
|
2
|
+
import { quoteIdentifier } from './serialization.js';
|
|
3
|
+
/** Ordered logical types eligible for min/max under sampling opt-in. */
|
|
4
|
+
const ORDERED_TYPES = new Set([
|
|
5
|
+
'integer',
|
|
6
|
+
'bigint',
|
|
7
|
+
'decimal',
|
|
8
|
+
'float',
|
|
9
|
+
'date',
|
|
10
|
+
'time',
|
|
11
|
+
'timestamp',
|
|
12
|
+
'timestamptz',
|
|
13
|
+
]);
|
|
14
|
+
function num(value) {
|
|
15
|
+
if (value === null || value === undefined)
|
|
16
|
+
return null;
|
|
17
|
+
const n = Number(value);
|
|
18
|
+
return Number.isFinite(n) ? n : null;
|
|
19
|
+
}
|
|
20
|
+
function str(value) {
|
|
21
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
22
|
+
}
|
|
23
|
+
function literal(value) {
|
|
24
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
25
|
+
}
|
|
26
|
+
function clampFraction(value) {
|
|
27
|
+
if (value === null)
|
|
28
|
+
return null;
|
|
29
|
+
if (value < 0)
|
|
30
|
+
return 0;
|
|
31
|
+
if (value > 1)
|
|
32
|
+
return 1;
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Normalize `pg_stats.n_distinct`: `>0` is an absolute estimate, `<0` is the
|
|
37
|
+
* negative fraction of the row count, `0`/absent means unknown.
|
|
38
|
+
*/
|
|
39
|
+
export function normalizePgDistinct(nDistinct, rowCount) {
|
|
40
|
+
if (nDistinct === null || nDistinct === 0)
|
|
41
|
+
return null;
|
|
42
|
+
if (nDistinct > 0)
|
|
43
|
+
return Math.round(nDistinct);
|
|
44
|
+
if (rowCount === null)
|
|
45
|
+
return null;
|
|
46
|
+
return Math.round(Math.abs(nDistinct) * rowCount);
|
|
47
|
+
}
|
|
48
|
+
function toScalar(value) {
|
|
49
|
+
if (value === null || value === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === 'bigint')
|
|
55
|
+
return value.toString();
|
|
56
|
+
if (value instanceof Date)
|
|
57
|
+
return value.toISOString();
|
|
58
|
+
if (value instanceof Uint8Array)
|
|
59
|
+
return '[binary]';
|
|
60
|
+
return String(value);
|
|
61
|
+
}
|
|
62
|
+
function truncate(value) {
|
|
63
|
+
if (typeof value === 'string' && value.length > STATS_SAMPLE_VALUE_MAX_CHARS) {
|
|
64
|
+
return value.slice(0, STATS_SAMPLE_VALUE_MAX_CHARS);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
/** One batched full-scan: `COUNT(*)` + per-column null count + distinct count. */
|
|
69
|
+
function buildNullDistinctScan(qualified, columns) {
|
|
70
|
+
const parts = columns.map((name, k) => {
|
|
71
|
+
const q = quoteIdentifier(name);
|
|
72
|
+
return `sum(CASE WHEN ${q} IS NULL THEN 1 ELSE 0 END)::int8 AS nf_${k}, count(DISTINCT ${q})::int8 AS dc_${k}`;
|
|
73
|
+
});
|
|
74
|
+
const cols = parts.length > 0 ? `, ${parts.join(', ')}` : '';
|
|
75
|
+
return `SELECT count(*)::int8 AS row_total${cols} FROM ${qualified}`;
|
|
76
|
+
}
|
|
77
|
+
export async function collectPostgresStats(exec, table, opts = {}) {
|
|
78
|
+
const schema = table.schema ?? 'public';
|
|
79
|
+
const name = table.name;
|
|
80
|
+
const qualified = `${quoteIdentifier(schema)}.${quoteIdentifier(name)}`;
|
|
81
|
+
const columns = opts.columns ?? [];
|
|
82
|
+
const sampling = opts.sampling ?? null;
|
|
83
|
+
const maxScanRows = opts.maxScanRows ?? STATS_MAX_SCAN_ROWS;
|
|
84
|
+
const warnings = [];
|
|
85
|
+
// 1. Row-count estimate from the catalog, exact COUNT for small/stale tables.
|
|
86
|
+
const relRows = await exec(`SELECT c.reltuples::float8 AS reltuples
|
|
87
|
+
FROM pg_catalog.pg_class c
|
|
88
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
89
|
+
WHERE n.nspname = ${literal(schema)} AND c.relname = ${literal(name)}
|
|
90
|
+
LIMIT 1`);
|
|
91
|
+
const reltuples = num(relRows[0]?.['reltuples']);
|
|
92
|
+
let rowCountEstimate;
|
|
93
|
+
let rowCountExact;
|
|
94
|
+
if (reltuples !== null && reltuples >= STATS_EXACT_COUNT_THRESHOLD) {
|
|
95
|
+
rowCountEstimate = Math.round(reltuples);
|
|
96
|
+
rowCountExact = false;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// reltuples null / negative (never analyzed) / below threshold → exact count.
|
|
100
|
+
const countRows = await exec(`SELECT count(*)::int8 AS n FROM ${qualified}`);
|
|
101
|
+
rowCountEstimate = num(countRows[0]?.['n']);
|
|
102
|
+
rowCountExact = rowCountEstimate !== null;
|
|
103
|
+
}
|
|
104
|
+
// 2. Cheap per-column stats from pg_stats (never most_common_vals — row data).
|
|
105
|
+
const statByCol = new Map();
|
|
106
|
+
if (columns.length > 0) {
|
|
107
|
+
const statRows = await exec(`SELECT attname, null_frac::float8 AS null_frac, n_distinct::float8 AS n_distinct
|
|
108
|
+
FROM pg_catalog.pg_stats
|
|
109
|
+
WHERE schemaname = ${literal(schema)} AND tablename = ${literal(name)}`);
|
|
110
|
+
for (const row of statRows) {
|
|
111
|
+
const attname = str(row['attname']);
|
|
112
|
+
if (attname === null)
|
|
113
|
+
continue;
|
|
114
|
+
statByCol.set(attname, {
|
|
115
|
+
nullFrac: num(row['null_frac']),
|
|
116
|
+
nDistinct: num(row['n_distinct']),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const colStats = columns.map((c) => ({
|
|
121
|
+
column: c.name,
|
|
122
|
+
nullFraction: null,
|
|
123
|
+
distinctCount: null,
|
|
124
|
+
}));
|
|
125
|
+
const uncovered = [];
|
|
126
|
+
columns.forEach((c, i) => {
|
|
127
|
+
const covered = statByCol.get(c.name);
|
|
128
|
+
if (covered !== undefined) {
|
|
129
|
+
colStats[i].nullFraction = clampFraction(covered.nullFrac);
|
|
130
|
+
colStats[i].distinctCount = normalizePgDistinct(covered.nDistinct, rowCountEstimate);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
uncovered.push(i);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// 3. Full-scan fallback for un-analyzed columns on tables under the cap.
|
|
137
|
+
if (uncovered.length > 0) {
|
|
138
|
+
const scannable = rowCountEstimate !== null && rowCountEstimate <= maxScanRows;
|
|
139
|
+
if (scannable) {
|
|
140
|
+
const scanRows = await exec(buildNullDistinctScan(qualified, uncovered.map((i) => columns[i].name)));
|
|
141
|
+
const row = scanRows[0] ?? {};
|
|
142
|
+
const total = num(row['row_total']);
|
|
143
|
+
uncovered.forEach((ci, k) => {
|
|
144
|
+
const nulls = num(row[`nf_${k}`]);
|
|
145
|
+
colStats[ci].nullFraction =
|
|
146
|
+
total === null || total === 0 ? (total === 0 ? 0 : null) : nulls === null ? null : nulls / total;
|
|
147
|
+
colStats[ci].distinctCount = num(row[`dc_${k}`]);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
warnings.push(`null-fraction/distinct-count skipped for ${uncovered.length} column(s): table exceeds maxScanRows and lacks analyzed statistics`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// 4. Sampling opt-in — most-common values + ordered min/max; never PII/secret.
|
|
155
|
+
let sampled = false;
|
|
156
|
+
if (sampling !== null) {
|
|
157
|
+
const maxValues = sampling.maxValuesPerColumn > 0
|
|
158
|
+
? Math.floor(sampling.maxValuesPerColumn)
|
|
159
|
+
: STATS_DEFAULT_SAMPLE_VALUES;
|
|
160
|
+
for (let i = 0; i < columns.length; i += 1) {
|
|
161
|
+
const c = columns[i];
|
|
162
|
+
if (c.piiSuspected === true || c.secret === true)
|
|
163
|
+
continue; // NEVER sample PII/secret.
|
|
164
|
+
const q = quoteIdentifier(c.name);
|
|
165
|
+
if (ORDERED_TYPES.has(c.logicalType)) {
|
|
166
|
+
// No ::text cast — ordered types are numeric/temporal; the driver
|
|
167
|
+
// returns numbers/dates that toScalar keeps JSON-native (bigint/decimal
|
|
168
|
+
// arrive as strings for lossless transport).
|
|
169
|
+
const mm = await exec(`SELECT min(${q}) AS lo, max(${q}) AS hi FROM ${qualified}`);
|
|
170
|
+
colStats[i].min = toScalar(mm[0]?.['lo']);
|
|
171
|
+
colStats[i].max = toScalar(mm[0]?.['hi']);
|
|
172
|
+
sampled = true;
|
|
173
|
+
}
|
|
174
|
+
const mcv = await exec(`SELECT ${q}::text AS v, count(*)::int8 AS c
|
|
175
|
+
FROM ${qualified}
|
|
176
|
+
WHERE ${q} IS NOT NULL
|
|
177
|
+
GROUP BY ${q}
|
|
178
|
+
ORDER BY count(*) DESC, ${q}
|
|
179
|
+
LIMIT ${maxValues}`);
|
|
180
|
+
colStats[i].sampleValues = mcv.map((r) => truncate(toScalar(r['v'])));
|
|
181
|
+
sampled = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const result = {
|
|
185
|
+
table: { schema: table.schema, name },
|
|
186
|
+
rowCountEstimate,
|
|
187
|
+
rowCountExact,
|
|
188
|
+
columns: colStats,
|
|
189
|
+
sampled,
|
|
190
|
+
};
|
|
191
|
+
if (warnings.length > 0)
|
|
192
|
+
result.warnings = warnings;
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres dbType → portable LogicalType mapping — 05-introspection-engine.md
|
|
3
|
+
* §2.2 ("canonical table — extend, never fork"), Postgres column.
|
|
4
|
+
*
|
|
5
|
+
* Input is the verbatim `pg_catalog.format_type(atttypid, atttypmod)` string
|
|
6
|
+
* (e.g. `character varying(120)`, `timestamp with time zone`, `numeric(10,2)`)
|
|
7
|
+
* so the mapping is pure and unit-testable offline. Enum/domain/array
|
|
8
|
+
* resolution happens in the introspection assembler (it needs `pg_type`
|
|
9
|
+
* rows); this module only handles the base-name mapping plus the
|
|
10
|
+
* length/precision/scale extraction rules.
|
|
11
|
+
*/
|
|
12
|
+
import type { ColumnDefault, LogicalType } from '@adminium/engine/adapter';
|
|
13
|
+
export interface MappedType {
|
|
14
|
+
logicalType: LogicalType;
|
|
15
|
+
maxLength: number | null;
|
|
16
|
+
numericPrecision: number | null;
|
|
17
|
+
numericScale: number | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Map a verbatim Postgres type string to its portable shape. Unmappable
|
|
21
|
+
* types become `'unknown'` with the verbatim `dbType` preserved by the
|
|
22
|
+
* caller (rendered read-only as text — 05 §2.2).
|
|
23
|
+
*/
|
|
24
|
+
export declare function mapPostgresType(dbType: string): MappedType;
|
|
25
|
+
/**
|
|
26
|
+
* Classify a column default — 05 §4.1: `serial`/`nextval` and identity
|
|
27
|
+
* columns → `autoincrement`; `now()`/`CURRENT_TIMESTAMP` → `now`;
|
|
28
|
+
* `gen_random_uuid()`/`uuid_generate_v4()` → `uuid`.
|
|
29
|
+
*
|
|
30
|
+
* @param defaultExpr `pg_get_expr(adbin, adrelid)` output, or null.
|
|
31
|
+
* @param identity `pg_attribute.attidentity` ('' | 'a' | 'd').
|
|
32
|
+
*/
|
|
33
|
+
export declare function classifyDefault(defaultExpr: string | null, identity: string): ColumnDefault;
|
|
34
|
+
//# sourceMappingURL=type-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-map.d.ts","sourceRoot":"","sources":["../src/type-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AA+D3E,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAsBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAoB1D;AAUD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAS3F"}
|
package/dist/type-map.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** `format_type` base name (parenthesized modifiers stripped) → LogicalType. */
|
|
2
|
+
const BASE_TYPE_MAP = {
|
|
3
|
+
// text family
|
|
4
|
+
text: 'text',
|
|
5
|
+
citext: 'text',
|
|
6
|
+
name: 'text',
|
|
7
|
+
'character varying': 'varchar',
|
|
8
|
+
varchar: 'varchar',
|
|
9
|
+
character: 'varchar',
|
|
10
|
+
char: 'varchar',
|
|
11
|
+
bpchar: 'varchar',
|
|
12
|
+
// integers (serial pseudo-types never appear in format_type output but are
|
|
13
|
+
// kept for defensive parsing of imported DDL strings)
|
|
14
|
+
smallint: 'integer',
|
|
15
|
+
integer: 'integer',
|
|
16
|
+
int2: 'integer',
|
|
17
|
+
int4: 'integer',
|
|
18
|
+
serial: 'integer',
|
|
19
|
+
smallserial: 'integer',
|
|
20
|
+
bigint: 'bigint',
|
|
21
|
+
int8: 'bigint',
|
|
22
|
+
bigserial: 'bigint',
|
|
23
|
+
oid: 'integer',
|
|
24
|
+
// exact + approximate numerics
|
|
25
|
+
numeric: 'decimal',
|
|
26
|
+
decimal: 'decimal',
|
|
27
|
+
money: 'decimal',
|
|
28
|
+
real: 'float',
|
|
29
|
+
float4: 'float',
|
|
30
|
+
'double precision': 'float',
|
|
31
|
+
float8: 'float',
|
|
32
|
+
// booleans
|
|
33
|
+
boolean: 'boolean',
|
|
34
|
+
bool: 'boolean',
|
|
35
|
+
// date/time
|
|
36
|
+
date: 'date',
|
|
37
|
+
'time without time zone': 'time',
|
|
38
|
+
'time with time zone': 'time',
|
|
39
|
+
time: 'time',
|
|
40
|
+
timetz: 'time',
|
|
41
|
+
'timestamp without time zone': 'timestamp',
|
|
42
|
+
timestamp: 'timestamp',
|
|
43
|
+
'timestamp with time zone': 'timestamptz',
|
|
44
|
+
timestamptz: 'timestamptz',
|
|
45
|
+
interval: 'interval',
|
|
46
|
+
// structured / misc
|
|
47
|
+
uuid: 'uuid',
|
|
48
|
+
json: 'json',
|
|
49
|
+
jsonb: 'json',
|
|
50
|
+
bytea: 'binary',
|
|
51
|
+
inet: 'inet',
|
|
52
|
+
cidr: 'inet',
|
|
53
|
+
// PostGIS (flag; rendered read-only as text in v1 — 05 §2.2)
|
|
54
|
+
geometry: 'geometry',
|
|
55
|
+
geography: 'geometry',
|
|
56
|
+
};
|
|
57
|
+
/** Binary-float precision constants surfaced by the catalogs. */
|
|
58
|
+
const FLOAT4_PRECISION = 24;
|
|
59
|
+
const FLOAT8_PRECISION = 53;
|
|
60
|
+
/** Strip `(…)` modifiers, `[]` suffixes, and interval field qualifiers. */
|
|
61
|
+
function baseTypeName(dbType) {
|
|
62
|
+
let base = dbType.trim().toLowerCase();
|
|
63
|
+
base = base.replace(/\[\s*\]\s*$/g, ''); // array suffix (possibly multi-dim already collapsed)
|
|
64
|
+
while (/\[\s*\]$/.test(base))
|
|
65
|
+
base = base.replace(/\[\s*\]$/, '');
|
|
66
|
+
base = base.replace(/\(([^)]*)\)/, '').replace(/\s+/g, ' ').trim();
|
|
67
|
+
if (base.startsWith('interval'))
|
|
68
|
+
return 'interval';
|
|
69
|
+
return base;
|
|
70
|
+
}
|
|
71
|
+
/** Extract `(n)` / `(p,s)` modifier numbers, when present. */
|
|
72
|
+
function modifiers(dbType) {
|
|
73
|
+
const match = /\(([\d\s,]+)\)/.exec(dbType);
|
|
74
|
+
if (match?.[1] === undefined)
|
|
75
|
+
return [];
|
|
76
|
+
return match[1]
|
|
77
|
+
.split(',')
|
|
78
|
+
.map((part) => Number.parseInt(part.trim(), 10))
|
|
79
|
+
.filter((n) => Number.isFinite(n));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Map a verbatim Postgres type string to its portable shape. Unmappable
|
|
83
|
+
* types become `'unknown'` with the verbatim `dbType` preserved by the
|
|
84
|
+
* caller (rendered read-only as text — 05 §2.2).
|
|
85
|
+
*/
|
|
86
|
+
export function mapPostgresType(dbType) {
|
|
87
|
+
const base = baseTypeName(dbType);
|
|
88
|
+
const logicalType = BASE_TYPE_MAP[base] ?? 'unknown';
|
|
89
|
+
const mods = modifiers(dbType);
|
|
90
|
+
let maxLength = null;
|
|
91
|
+
let numericPrecision = null;
|
|
92
|
+
let numericScale = null;
|
|
93
|
+
if (logicalType === 'varchar' || logicalType === 'text') {
|
|
94
|
+
maxLength = mods[0] ?? null;
|
|
95
|
+
}
|
|
96
|
+
else if (logicalType === 'decimal') {
|
|
97
|
+
numericPrecision = mods[0] ?? null;
|
|
98
|
+
numericScale = mods[1] ?? null;
|
|
99
|
+
}
|
|
100
|
+
else if (logicalType === 'float') {
|
|
101
|
+
numericPrecision =
|
|
102
|
+
base === 'real' || base === 'float4' ? FLOAT4_PRECISION : FLOAT8_PRECISION;
|
|
103
|
+
}
|
|
104
|
+
return { logicalType, maxLength, numericPrecision, numericScale };
|
|
105
|
+
}
|
|
106
|
+
const NOW_DEFAULT = /^(now\(\)|current_timestamp(\(\d*\))?|transaction_timestamp\(\)|statement_timestamp\(\)|current_date|current_time(\(\d*\))?)/i;
|
|
107
|
+
const UUID_DEFAULT = /^(gen_random_uuid\(\)|uuid_generate_v4\(\))/i;
|
|
108
|
+
const AUTOINCREMENT_DEFAULT = /^nextval\(/i;
|
|
109
|
+
/** `'…'::type`, bare numbers, booleans, or a typed NULL are literals. */
|
|
110
|
+
const LITERAL_DEFAULT = /^('(?:[^']|'')*'(?:::[a-z_"][\w ."[\]]*(?:\(\d+(?:,\s*\d+)?\))?)?|-?\d+(\.\d+)?|true|false|NULL(?:::[a-z_"][\w ."[\]]*)?)$/i;
|
|
111
|
+
/**
|
|
112
|
+
* Classify a column default — 05 §4.1: `serial`/`nextval` and identity
|
|
113
|
+
* columns → `autoincrement`; `now()`/`CURRENT_TIMESTAMP` → `now`;
|
|
114
|
+
* `gen_random_uuid()`/`uuid_generate_v4()` → `uuid`.
|
|
115
|
+
*
|
|
116
|
+
* @param defaultExpr `pg_get_expr(adbin, adrelid)` output, or null.
|
|
117
|
+
* @param identity `pg_attribute.attidentity` ('' | 'a' | 'd').
|
|
118
|
+
*/
|
|
119
|
+
export function classifyDefault(defaultExpr, identity) {
|
|
120
|
+
if (identity === 'a' || identity === 'd')
|
|
121
|
+
return { kind: 'autoincrement' };
|
|
122
|
+
if (defaultExpr === null || defaultExpr === '')
|
|
123
|
+
return null;
|
|
124
|
+
const text = defaultExpr.trim();
|
|
125
|
+
if (AUTOINCREMENT_DEFAULT.test(text))
|
|
126
|
+
return { kind: 'autoincrement' };
|
|
127
|
+
if (NOW_DEFAULT.test(text))
|
|
128
|
+
return { kind: 'now' };
|
|
129
|
+
if (UUID_DEFAULT.test(text))
|
|
130
|
+
return { kind: 'uuid' };
|
|
131
|
+
if (LITERAL_DEFAULT.test(text))
|
|
132
|
+
return { kind: 'literal', text };
|
|
133
|
+
return { kind: 'expression', text };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=type-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-map.js","sourceRoot":"","sources":["../src/type-map.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adminiumjs/adapter-postgres",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/MoSofi/Adminium.git",
|
|
8
|
+
"directory": "packages/adapter-postgres"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://adminium.dev",
|
|
11
|
+
"bugs": "https://github.com/MoSofi/Adminium/issues",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"description": "PostgreSQL adapter",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@adminium/engine": "npm:@adminiumjs/engine@0.1.0-rc.1",
|
|
24
|
+
"kysely": "^0.28.2",
|
|
25
|
+
"pg": "^8.16.3"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
]
|
|
30
|
+
}
|