@streetjs/schema-inspector 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.
@@ -0,0 +1,79 @@
1
+ # Architecture — @streetjs/schema-inspector
2
+
3
+ ## Purpose
4
+
5
+ `@streetjs/schema-inspector` turns a live database connection into a normalized,
6
+ engine-agnostic `DatabaseSchema`. It is the introspection layer the migration
7
+ differ and dev tooling build on. It is deliberately decoupled from any concrete
8
+ connection pool: it speaks only the structural `QueryablePool` interface and
9
+ routes by the pool's constructor name.
10
+
11
+ ## Dependencies
12
+
13
+ ```
14
+ @streetjs/postgres (the shared DbResult result type only)
15
+ ```
16
+
17
+ That is the sole dependency, and it is used purely for the `DbResult` type that
18
+ `QueryablePool.query` resolves to. No cyclic dependencies; no coupling to
19
+ `@streetjs/pool`, the wasm SQLite module, or any MySQL driver.
20
+
21
+ ## Design
22
+
23
+ ### Structural pool interface
24
+
25
+ ```ts
26
+ interface QueryablePool { query(sql: string, params?: unknown[]): Promise<DbResult> }
27
+ ```
28
+
29
+ `PgPool`, `MysqlPool`, and `SqlitePool` all satisfy this structurally, so the
30
+ inspector never imports a concrete class. Dialect selection is by
31
+ `pool.constructor.name`:
32
+
33
+ - `SqlitePool` → PRAGMA-based introspection;
34
+ - `PgPool` → `information_schema` + `pg_indexes`;
35
+ - anything else → MySQL/MariaDB `information_schema`.
36
+
37
+ This name-based routing is what lets the package stay dependency-light: the
38
+ concrete pools live in other packages/core, but the inspector only needs their
39
+ runtime `query` method.
40
+
41
+ ### Normalization
42
+
43
+ Each engine path issues a small, fixed set of catalogue queries and folds the
44
+ rows into a `Map<tableName, TableSchema>`:
45
+
46
+ - **Postgres** parses index column lists out of the `indexdef` DDL string and
47
+ treats `is_pk` values of `'t'`/`'true'` as primary-key membership.
48
+ - **MySQL** groups the one-row-per-column `STATISTICS` output into composite
49
+ `IndexMeta` and reads `NON_UNIQUE = '0'` as unique.
50
+ - **SQLite** reads `PRAGMA table_info` for columns/PK (sorted by the `pk`
51
+ position for composite keys), `foreign_key_list` for FKs, and
52
+ `index_list` + `index_info` for indexes.
53
+
54
+ All values arrive as strings (matching the text-protocol/affinity semantics of
55
+ `DbResult`), so parsing is uniform and every field access is defended with a
56
+ fallback (`?? ''` / `?? null`).
57
+
58
+ ### Caching
59
+
60
+ `inspect` caches the resulting `DatabaseSchema` per pool object (a
61
+ `Map<object, { schema, expiresAt }>`) for a configurable TTL (default 60s).
62
+ `invalidateCache(pool)` drops the entry to force a fresh read. The cache is keyed
63
+ by object identity, so distinct pools never collide.
64
+
65
+ ## Testing
66
+
67
+ The suite runs with **no live database** by supplying fake `QueryablePool`
68
+ objects whose `constructor.name` is `PgPool`/`SqlitePool`/`MysqlPool`, driving
69
+ each dialect path with canned catalogue rows. It covers primary keys (including
70
+ composite and the `'t'`/`'true'` encodings), foreign keys, unique/non-unique and
71
+ multi-column index parsing, quoted-identifier stripping, empty databases,
72
+ per-field fallbacks for sparse rows, and the full cache/TTL/invalidation
73
+ lifecycle. Coverage is 100% lines/functions and ≥98% branches.
74
+
75
+ ## Non-goals
76
+
77
+ - No DDL generation or migration diffing (that is the migration layer).
78
+ - No connection management (that is `@streetjs/pool` / the SQLite pool).
79
+ - No data reads beyond catalogue introspection.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@streetjs/schema-inspector` are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0]
9
+
10
+ ### Added
11
+
12
+ - Initial standalone release of the StreetJS database schema introspector,
13
+ extracted from the `streetjs` core (`src/database/schema-inspector.ts`).
14
+ - `SchemaInspector.inspect(pool, opts?)` returning a unified `DatabaseSchema`
15
+ (tables, columns, primary keys, foreign keys, indexes) for PostgreSQL,
16
+ MySQL/MariaDB, and SQLite, with per-pool TTL caching (default 60s).
17
+ - `SchemaInspector.invalidateCache(pool)`.
18
+ - Public types: `DatabaseSchema`, `TableSchema`, `ColumnMeta`, `FkMeta`,
19
+ `IndexMeta`, and the structural `QueryablePool` interface.
20
+ - Decoupled from concrete pools: routes by `constructor.name` and depends only on
21
+ `@streetjs/postgres` for the shared `DbResult` type. ESM.
22
+ - 14 tests (no live database required), 100% line coverage, and a runnable
23
+ example. During extraction the public signature was generalized from
24
+ `PgPool | SqlitePool | QueryablePool` to `QueryablePool`; both `PgPool` and
25
+ `SqlitePool` satisfy it structurally, so existing callers are unaffected.
26
+
27
+ [1.0.0]: https://github.com/hassanmubiru/StreetJS/releases/tag/schema-inspector-v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 street contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @streetjs/schema-inspector
2
+
3
+ The StreetJS database schema introspector: produces a unified
4
+ `DatabaseSchema` — tables, columns, primary keys, foreign keys, and indexes —
5
+ from a live **PostgreSQL**, **MySQL/MariaDB**, or **SQLite** database, with
6
+ per-pool TTL caching. ESM, strict-TypeScript.
7
+
8
+ It talks only through a structural `QueryablePool` interface and routes by the
9
+ pool's constructor name, so it has **no dependency on any concrete pool
10
+ implementation** — the only dependency is `@streetjs/postgres` for the shared
11
+ `DbResult` type.
12
+
13
+ This is the standalone home of the inspector that also backs the `streetjs`
14
+ framework. The framework re-exports this package, so there is a single source of
15
+ truth.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install @streetjs/schema-inspector
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import { SchemaInspector } from '@streetjs/schema-inspector';
27
+
28
+ // `pool` can be any object with `query(sql, params?): Promise<DbResult>`
29
+ // — PgPool, MysqlPool, and SqlitePool all qualify.
30
+ const schema = await SchemaInspector.inspect(pool);
31
+
32
+ for (const table of schema.tables) {
33
+ console.log(table.name, table.primaryKey, table.columns.length);
34
+ }
35
+ ```
36
+
37
+ ## API
38
+
39
+ ### `SchemaInspector.inspect(pool, opts?): Promise<DatabaseSchema>`
40
+
41
+ Introspects the database and returns its schema. Results are cached per pool for
42
+ `opts.ttlMs` milliseconds (default `60000`). The dialect is detected from the
43
+ pool's constructor name (`PgPool` → PostgreSQL, `SqlitePool` → SQLite, anything
44
+ else → MySQL/MariaDB).
45
+
46
+ ### `SchemaInspector.invalidateCache(pool): void`
47
+
48
+ Drops the cached schema for `pool` so the next `inspect` re-fetches.
49
+
50
+ ### Types
51
+
52
+ ```ts
53
+ interface DatabaseSchema { tables: TableSchema[]; inspectedAt: Date }
54
+ interface TableSchema {
55
+ name: string;
56
+ columns: ColumnMeta[];
57
+ primaryKey: string[];
58
+ foreignKeys: FkMeta[];
59
+ indexes: IndexMeta[];
60
+ }
61
+ interface ColumnMeta { name: string; type: string; nullable: boolean; default: string | null }
62
+ interface FkMeta { column: string; refTable: string; refColumn: string }
63
+ interface IndexMeta { name: string; columns: string[]; unique: boolean }
64
+ interface QueryablePool { query(sql: string, params?: unknown[]): Promise<DbResult> }
65
+ ```
66
+
67
+ ## How it works
68
+
69
+ - **PostgreSQL** — three round-trips against `information_schema` (columns +
70
+ primary keys), `referential_constraints` (foreign keys), and `pg_indexes`
71
+ (indexes, with the column list parsed from `indexdef`).
72
+ - **MySQL/MariaDB** — `information_schema.COLUMNS`/`KEY_COLUMN_USAGE`/`STATISTICS`,
73
+ grouping the per-column `STATISTICS` rows into composite indexes.
74
+ - **SQLite** — `sqlite_master` for the table list, then `PRAGMA table_info`,
75
+ `index_list`/`index_info`, and `foreign_key_list` per table.
76
+
77
+ All engines normalize into the same `DatabaseSchema` shape.
78
+
79
+ ## Example
80
+
81
+ A complete runnable example lives in
82
+ [`src/examples/integration.ts`](./src/examples/integration.ts):
83
+
84
+ ```bash
85
+ npm run example -w packages/schema-inspector
86
+ ```
87
+
88
+ ## License
89
+
90
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @streetjs/schema-inspector — the StreetJS database schema introspector.
3
+ *
4
+ * Produces a unified {@link DatabaseSchema} — tables, columns, primary keys,
5
+ * foreign keys, and indexes — from a live PostgreSQL, MySQL/MariaDB, or SQLite
6
+ * database, with per-pool TTL caching. It routes by the pool's constructor name
7
+ * and talks only through the structural {@link QueryablePool} interface, so it
8
+ * has no dependency on any concrete pool implementation. Public API only.
9
+ *
10
+ * ```ts
11
+ * import { SchemaInspector } from '@streetjs/schema-inspector';
12
+ *
13
+ * const schema = await SchemaInspector.inspect(pool); // any queryable pool
14
+ * for (const table of schema.tables) console.log(table.name, table.columns.length);
15
+ * ```
16
+ *
17
+ * > This is the standalone home of the inspector that also backs the `streetjs`
18
+ * > framework; the framework re-exports it, so there is a single implementation.
19
+ */
20
+ export { SchemaInspector } from './schema-inspector.js';
21
+ export type { ColumnMeta, IndexMeta, FkMeta, TableSchema, DatabaseSchema, QueryablePool, } from './schema-inspector.js';
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EACV,UAAU,EACV,SAAS,EACT,MAAM,EACN,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @streetjs/schema-inspector — the StreetJS database schema introspector.
3
+ *
4
+ * Produces a unified {@link DatabaseSchema} — tables, columns, primary keys,
5
+ * foreign keys, and indexes — from a live PostgreSQL, MySQL/MariaDB, or SQLite
6
+ * database, with per-pool TTL caching. It routes by the pool's constructor name
7
+ * and talks only through the structural {@link QueryablePool} interface, so it
8
+ * has no dependency on any concrete pool implementation. Public API only.
9
+ *
10
+ * ```ts
11
+ * import { SchemaInspector } from '@streetjs/schema-inspector';
12
+ *
13
+ * const schema = await SchemaInspector.inspect(pool); // any queryable pool
14
+ * for (const table of schema.tables) console.log(table.name, table.columns.length);
15
+ * ```
16
+ *
17
+ * > This is the standalone home of the inspector that also backs the `streetjs`
18
+ * > framework; the framework re-exports it, so there is a single implementation.
19
+ */
20
+ export { SchemaInspector } from './schema-inspector.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,80 @@
1
+ import type { DbResult } from '@streetjs/postgres';
2
+ export interface ColumnMeta {
3
+ name: string;
4
+ type: string;
5
+ nullable: boolean;
6
+ default: string | null;
7
+ }
8
+ export interface IndexMeta {
9
+ name: string;
10
+ columns: string[];
11
+ unique: boolean;
12
+ }
13
+ export interface FkMeta {
14
+ column: string;
15
+ refTable: string;
16
+ refColumn: string;
17
+ }
18
+ export interface TableSchema {
19
+ name: string;
20
+ columns: ColumnMeta[];
21
+ primaryKey: string[];
22
+ foreignKeys: FkMeta[];
23
+ indexes: IndexMeta[];
24
+ }
25
+ export interface DatabaseSchema {
26
+ tables: TableSchema[];
27
+ inspectedAt: Date;
28
+ }
29
+ /**
30
+ * Minimal interface satisfied by any Street database pool — `PgPool`,
31
+ * `MysqlPool`, and `SqlitePool` all match it structurally. The inspector routes
32
+ * by the pool's constructor name, so it never needs to import a concrete pool
33
+ * class (keeping this package free of engine-specific dependencies).
34
+ */
35
+ export interface QueryablePool {
36
+ query(sql: string, params?: unknown[]): Promise<DbResult>;
37
+ }
38
+ interface CacheEntry {
39
+ schema: DatabaseSchema;
40
+ expiresAt: number;
41
+ }
42
+ export declare class SchemaInspector {
43
+ /** Schema cache keyed by pool object reference. */
44
+ static readonly _cache: Map<object, CacheEntry>;
45
+ /**
46
+ * Inspect the connected database and return its schema.
47
+ * Results are cached for `opts.ttlMs` milliseconds (default 60 000).
48
+ */
49
+ static inspect(pool: QueryablePool, opts?: {
50
+ ttlMs?: number;
51
+ }): Promise<DatabaseSchema>;
52
+ /**
53
+ * Remove the cached schema for `pool`, forcing the next `inspect()` call
54
+ * to fetch fresh data from the database.
55
+ */
56
+ static invalidateCache(pool: QueryablePool): void;
57
+ private static _isSqlitePool;
58
+ private static _isPgPool;
59
+ /**
60
+ * Inspect a PostgreSQL database.
61
+ * Uses 3 round-trips:
62
+ * 1. columns + primary keys (information_schema.columns + key_column_usage)
63
+ * 2. foreign keys (information_schema.referential_constraints + key_column_usage)
64
+ * 3. indexes (pg_indexes)
65
+ */
66
+ private static _inspectPostgres;
67
+ /**
68
+ * Inspect a MySQL/MariaDB database using information_schema catalog tables.
69
+ * Uses 3 parallel round-trips: columns+pk, foreign keys, indexes.
70
+ */
71
+ private static _inspectMysql;
72
+ /**
73
+ * Inspect a SQLite database using PRAGMA statements.
74
+ * Fetches the table list first, then issues PRAGMA queries for each table.
75
+ */
76
+ private static _inspectSqlite;
77
+ private static _inspectSqliteTable;
78
+ }
79
+ export {};
80
+ //# sourceMappingURL=schema-inspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-inspector.d.ts","sourceRoot":"","sources":["../src/schema-inspector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAInD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,IAAI,CAAC;CACnB;AAID;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC3D;AAID,UAAU,UAAU;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,eAAe;IAC1B,mDAAmD;IACnD,MAAM,CAAC,QAAQ,CAAC,MAAM,0BAAiC;IAEvD;;;OAGG;WACU,OAAO,CAClB,IAAI,EAAE,aAAa,EACnB,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,OAAO,CAAC,cAAc,CAAC;IA4B1B;;;OAGG;IACH,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAMjD,OAAO,CAAC,MAAM,CAAC,aAAa;IAW5B,OAAO,CAAC,MAAM,CAAC,SAAS;IAUxB;;;;;;OAMG;mBACkB,gBAAgB;IAiIrC;;;OAGG;mBACkB,aAAa;IAwGlC;;;OAGG;mBACkB,cAAc;mBAed,mBAAmB;CAgEzC"}
@@ -0,0 +1,351 @@
1
+ // src/schema-inspector.ts
2
+ // Database schema introspection for PostgreSQL, MySQL, and SQLite.
3
+ // Provides a unified DatabaseSchema regardless of the underlying engine.
4
+ // ─── SchemaInspector ─────────────────────────────────────────────────────────
5
+ export class SchemaInspector {
6
+ /** Schema cache keyed by pool object reference. */
7
+ static _cache = new Map();
8
+ /**
9
+ * Inspect the connected database and return its schema.
10
+ * Results are cached for `opts.ttlMs` milliseconds (default 60 000).
11
+ */
12
+ static async inspect(pool, opts) {
13
+ const ttlMs = opts?.ttlMs ?? 60_000;
14
+ const now = Date.now();
15
+ const cached = SchemaInspector._cache.get(pool);
16
+ if (cached && cached.expiresAt > now) {
17
+ return cached.schema;
18
+ }
19
+ // Detect dialect by duck-typing the pool
20
+ let schema;
21
+ if (SchemaInspector._isSqlitePool(pool)) {
22
+ schema = await SchemaInspector._inspectSqlite(pool);
23
+ }
24
+ else if (SchemaInspector._isPgPool(pool)) {
25
+ schema = await SchemaInspector._inspectPostgres(pool);
26
+ }
27
+ else {
28
+ // MySQL or any other pool that satisfies QueryablePool
29
+ schema = await SchemaInspector._inspectMysql(pool);
30
+ }
31
+ SchemaInspector._cache.set(pool, {
32
+ schema,
33
+ expiresAt: now + ttlMs,
34
+ });
35
+ return schema;
36
+ }
37
+ /**
38
+ * Remove the cached schema for `pool`, forcing the next `inspect()` call
39
+ * to fetch fresh data from the database.
40
+ */
41
+ static invalidateCache(pool) {
42
+ SchemaInspector._cache.delete(pool);
43
+ }
44
+ // ── Dialect detection ───────────────────────────────────────────────────────
45
+ static _isSqlitePool(pool) {
46
+ // SqlitePool uses worker_threads and exposes a `filePath`-based constructor.
47
+ // We detect it by relying on the pool constructor name.
48
+ return (pool !== null &&
49
+ typeof pool === 'object' &&
50
+ pool['constructor'] !== undefined &&
51
+ pool.constructor.name === 'SqlitePool');
52
+ }
53
+ static _isPgPool(pool) {
54
+ return (pool !== null &&
55
+ typeof pool === 'object' &&
56
+ pool.constructor.name === 'PgPool');
57
+ }
58
+ // ── PostgreSQL introspection ────────────────────────────────────────────────
59
+ /**
60
+ * Inspect a PostgreSQL database.
61
+ * Uses 3 round-trips:
62
+ * 1. columns + primary keys (information_schema.columns + key_column_usage)
63
+ * 2. foreign keys (information_schema.referential_constraints + key_column_usage)
64
+ * 3. indexes (pg_indexes)
65
+ */
66
+ static async _inspectPostgres(pool) {
67
+ // Round-trip 1: columns and primary key membership
68
+ const colSql = `
69
+ SELECT
70
+ c.table_name,
71
+ c.column_name,
72
+ c.data_type,
73
+ c.is_nullable,
74
+ c.column_default,
75
+ CASE WHEN kcu.column_name IS NOT NULL THEN true ELSE false END AS is_pk
76
+ FROM information_schema.columns c
77
+ LEFT JOIN (
78
+ SELECT kcu.table_schema, kcu.table_name, kcu.column_name
79
+ FROM information_schema.key_column_usage kcu
80
+ JOIN information_schema.table_constraints tc
81
+ ON tc.constraint_name = kcu.constraint_name
82
+ AND tc.table_schema = kcu.table_schema
83
+ AND tc.table_name = kcu.table_name
84
+ WHERE tc.constraint_type = 'PRIMARY KEY'
85
+ AND tc.table_schema = current_schema()
86
+ ) kcu
87
+ ON kcu.table_schema = c.table_schema
88
+ AND kcu.table_name = c.table_name
89
+ AND kcu.column_name = c.column_name
90
+ WHERE c.table_schema = current_schema()
91
+ AND c.table_name IN (
92
+ SELECT table_name FROM information_schema.tables
93
+ WHERE table_schema = current_schema()
94
+ AND table_type = 'BASE TABLE'
95
+ )
96
+ ORDER BY c.table_name, c.ordinal_position
97
+ `;
98
+ // Round-trip 2: foreign keys
99
+ const fkSql = `
100
+ SELECT
101
+ kcu.table_name,
102
+ kcu.column_name,
103
+ ccu.table_name AS ref_table,
104
+ ccu.column_name AS ref_column
105
+ FROM information_schema.referential_constraints rc
106
+ JOIN information_schema.key_column_usage kcu
107
+ ON kcu.constraint_name = rc.constraint_name
108
+ AND kcu.table_schema = rc.constraint_schema
109
+ JOIN information_schema.constraint_column_usage ccu
110
+ ON ccu.constraint_name = rc.unique_constraint_name
111
+ AND ccu.table_schema = rc.unique_constraint_schema
112
+ WHERE rc.constraint_schema = current_schema()
113
+ ORDER BY kcu.table_name, kcu.column_name
114
+ `;
115
+ // Round-trip 3: indexes
116
+ const idxSql = `
117
+ SELECT
118
+ tablename,
119
+ indexname,
120
+ indexdef
121
+ FROM pg_indexes
122
+ WHERE schemaname = current_schema()
123
+ ORDER BY tablename, indexname
124
+ `;
125
+ const [colResult, fkResult, idxResult] = await Promise.all([
126
+ pool.query(colSql),
127
+ pool.query(fkSql),
128
+ pool.query(idxSql),
129
+ ]);
130
+ // Build table map from columns
131
+ const tableMap = new Map();
132
+ for (const row of colResult.rows) {
133
+ const tbl = row['table_name'] ?? '';
134
+ if (!tableMap.has(tbl)) {
135
+ tableMap.set(tbl, { name: tbl, columns: [], primaryKey: [], foreignKeys: [], indexes: [] });
136
+ }
137
+ const ts = tableMap.get(tbl);
138
+ const col = {
139
+ name: row['column_name'] ?? '',
140
+ type: row['data_type'] ?? '',
141
+ nullable: row['is_nullable'] === 'YES',
142
+ default: row['column_default'] ?? null,
143
+ };
144
+ ts.columns.push(col);
145
+ if (row['is_pk'] === 'true' || row['is_pk'] === 't') {
146
+ ts.primaryKey.push(col.name);
147
+ }
148
+ }
149
+ // Attach foreign keys
150
+ for (const row of fkResult.rows) {
151
+ const tbl = row['table_name'] ?? '';
152
+ const ts = tableMap.get(tbl);
153
+ if (!ts)
154
+ continue;
155
+ ts.foreignKeys.push({
156
+ column: row['column_name'] ?? '',
157
+ refTable: row['ref_table'] ?? '',
158
+ refColumn: row['ref_column'] ?? '',
159
+ });
160
+ }
161
+ // Parse and attach indexes
162
+ for (const row of idxResult.rows) {
163
+ const tbl = row['tablename'] ?? '';
164
+ const ts = tableMap.get(tbl);
165
+ if (!ts)
166
+ continue;
167
+ const indexDef = row['indexdef'] ?? '';
168
+ const indexName = row['indexname'] ?? '';
169
+ const unique = /CREATE UNIQUE INDEX/i.test(indexDef);
170
+ // Parse column list from index definition:
171
+ // "CREATE [UNIQUE] INDEX name ON table USING method (col1, col2)"
172
+ const colMatch = indexDef.match(/\(([^)]+)\)/);
173
+ const columns = colMatch
174
+ ? colMatch[1].split(',').map((s) => s.trim().replace(/^"(.+)"$/, '$1'))
175
+ : [];
176
+ ts.indexes.push({ name: indexName, columns, unique });
177
+ }
178
+ return {
179
+ tables: Array.from(tableMap.values()),
180
+ inspectedAt: new Date(),
181
+ };
182
+ }
183
+ // ── MySQL introspection ─────────────────────────────────────────────────────
184
+ /**
185
+ * Inspect a MySQL/MariaDB database using information_schema catalog tables.
186
+ * Uses 3 parallel round-trips: columns+pk, foreign keys, indexes.
187
+ */
188
+ static async _inspectMysql(pool) {
189
+ const colSql = `
190
+ SELECT
191
+ c.TABLE_NAME AS table_name,
192
+ c.COLUMN_NAME AS column_name,
193
+ c.DATA_TYPE AS data_type,
194
+ c.IS_NULLABLE AS is_nullable,
195
+ c.COLUMN_DEFAULT AS column_default,
196
+ c.COLUMN_KEY AS column_key
197
+ FROM information_schema.COLUMNS c
198
+ WHERE c.TABLE_SCHEMA = DATABASE()
199
+ ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION
200
+ `;
201
+ const fkSql = `
202
+ SELECT
203
+ kcu.TABLE_NAME AS table_name,
204
+ kcu.COLUMN_NAME AS column_name,
205
+ kcu.REFERENCED_TABLE_NAME AS ref_table,
206
+ kcu.REFERENCED_COLUMN_NAME AS ref_column
207
+ FROM information_schema.KEY_COLUMN_USAGE kcu
208
+ WHERE kcu.TABLE_SCHEMA = DATABASE()
209
+ AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
210
+ ORDER BY kcu.TABLE_NAME, kcu.COLUMN_NAME
211
+ `;
212
+ const idxSql = `
213
+ SELECT
214
+ TABLE_NAME AS table_name,
215
+ INDEX_NAME AS index_name,
216
+ COLUMN_NAME AS column_name,
217
+ NON_UNIQUE AS non_unique
218
+ FROM information_schema.STATISTICS
219
+ WHERE TABLE_SCHEMA = DATABASE()
220
+ ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX
221
+ `;
222
+ const [colResult, fkResult, idxResult] = await Promise.all([
223
+ pool.query(colSql),
224
+ pool.query(fkSql),
225
+ pool.query(idxSql),
226
+ ]);
227
+ const tableMap = new Map();
228
+ for (const row of colResult.rows) {
229
+ const tbl = row['table_name'] ?? '';
230
+ if (!tableMap.has(tbl)) {
231
+ tableMap.set(tbl, { name: tbl, columns: [], primaryKey: [], foreignKeys: [], indexes: [] });
232
+ }
233
+ const ts = tableMap.get(tbl);
234
+ const col = {
235
+ name: row['column_name'] ?? '',
236
+ type: row['data_type'] ?? '',
237
+ nullable: row['is_nullable'] === 'YES',
238
+ default: row['column_default'] ?? null,
239
+ };
240
+ ts.columns.push(col);
241
+ if (row['column_key'] === 'PRI') {
242
+ ts.primaryKey.push(col.name);
243
+ }
244
+ }
245
+ for (const row of fkResult.rows) {
246
+ const tbl = row['table_name'] ?? '';
247
+ const ts = tableMap.get(tbl);
248
+ if (!ts)
249
+ continue;
250
+ ts.foreignKeys.push({
251
+ column: row['column_name'] ?? '',
252
+ refTable: row['ref_table'] ?? '',
253
+ refColumn: row['ref_column'] ?? '',
254
+ });
255
+ }
256
+ // Group index rows (one row per column in the index) into IndexMeta
257
+ const indexAccum = new Map();
258
+ for (const row of idxResult.rows) {
259
+ const tbl = row['table_name'] ?? '';
260
+ const idxName = row['index_name'] ?? '';
261
+ const key = `${tbl}::${idxName}`;
262
+ if (!indexAccum.has(key)) {
263
+ indexAccum.set(key, {
264
+ tableName: tbl,
265
+ name: idxName,
266
+ columns: [],
267
+ unique: row['non_unique'] === '0',
268
+ });
269
+ }
270
+ indexAccum.get(key).columns.push(row['column_name'] ?? '');
271
+ }
272
+ for (const idx of indexAccum.values()) {
273
+ const ts = tableMap.get(idx.tableName);
274
+ if (!ts)
275
+ continue;
276
+ ts.indexes.push({ name: idx.name, columns: idx.columns, unique: idx.unique });
277
+ }
278
+ return {
279
+ tables: Array.from(tableMap.values()),
280
+ inspectedAt: new Date(),
281
+ };
282
+ }
283
+ // ── SQLite introspection ────────────────────────────────────────────────────
284
+ /**
285
+ * Inspect a SQLite database using PRAGMA statements.
286
+ * Fetches the table list first, then issues PRAGMA queries for each table.
287
+ */
288
+ static async _inspectSqlite(pool) {
289
+ // Get all user tables (exclude internal sqlite_ tables)
290
+ const tablesResult = await pool.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name");
291
+ const tableNames = tablesResult.rows.map((r) => r['name'] ?? '');
292
+ const tables = await Promise.all(tableNames.map((name) => SchemaInspector._inspectSqliteTable(pool, name)));
293
+ return { tables, inspectedAt: new Date() };
294
+ }
295
+ static async _inspectSqliteTable(pool, tableName) {
296
+ // Run all three PRAGMAs for this table
297
+ const [colResult, idxListResult, fkResult] = await Promise.all([
298
+ pool.query(`PRAGMA table_info(${JSON.stringify(tableName)})`),
299
+ pool.query(`PRAGMA index_list(${JSON.stringify(tableName)})`),
300
+ pool.query(`PRAGMA foreign_key_list(${JSON.stringify(tableName)})`),
301
+ ]);
302
+ // Columns and primary key from table_info
303
+ // Columns: cid, name, type, notnull, dflt_value, pk
304
+ const columns = [];
305
+ for (const row of colResult.rows) {
306
+ columns.push({
307
+ name: row['name'] ?? '',
308
+ type: row['type'] ?? '',
309
+ nullable: row['notnull'] === '0',
310
+ default: row['dflt_value'] ?? null,
311
+ });
312
+ }
313
+ // Sort primary key columns by their pk position
314
+ const pkEntries = colResult.rows
315
+ .filter((r) => r['pk'] !== '0' && r['pk'] !== null && r['pk'] !== undefined)
316
+ .sort((a, b) => Number(a['pk']) - Number(b['pk']))
317
+ .map((r) => r['name'] ?? '');
318
+ // Foreign keys from foreign_key_list
319
+ // Columns: id, seq, table, from, to, on_update, on_delete, match
320
+ const foreignKeys = fkResult.rows.map((row) => ({
321
+ column: row['from'] ?? '',
322
+ refTable: row['table'] ?? '',
323
+ refColumn: row['to'] ?? '',
324
+ }));
325
+ // Indexes from index_list, then fetch columns for each non-partial index
326
+ // Columns: seq, name, unique, origin, partial
327
+ const indexes = [];
328
+ for (const idxRow of idxListResult.rows) {
329
+ const idxName = idxRow['name'] ?? '';
330
+ // Fetch columns for this index via PRAGMA index_info
331
+ const infoResult = await pool.query(`PRAGMA index_info(${JSON.stringify(idxName)})`);
332
+ // Columns: seqno, cid, name
333
+ const cols = infoResult.rows
334
+ .sort((a, b) => Number(a['seqno']) - Number(b['seqno']))
335
+ .map((r) => r['name'] ?? '');
336
+ indexes.push({
337
+ name: idxName,
338
+ columns: cols,
339
+ unique: idxRow['unique'] === '1',
340
+ });
341
+ }
342
+ return {
343
+ name: tableName,
344
+ columns,
345
+ primaryKey: pkEntries,
346
+ foreignKeys,
347
+ indexes,
348
+ };
349
+ }
350
+ }
351
+ //# sourceMappingURL=schema-inspector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-inspector.js","sourceRoot":"","sources":["../src/schema-inspector.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@streetjs/schema-inspector",
3
+ "version": "1.0.0",
4
+ "description": "StreetJS database schema introspection: a unified DatabaseSchema (tables, columns, primary keys, foreign keys, indexes) across PostgreSQL, MySQL/MariaDB, and SQLite, with TTL caching. Works with any queryable pool.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist/**/*.js",
16
+ "dist/**/*.js.map",
17
+ "dist/**/*.d.ts",
18
+ "dist/**/*.d.ts.map",
19
+ "!dist/**/*.test.js",
20
+ "!dist/**/*.test.js.map",
21
+ "!dist/**/*.test.d.ts",
22
+ "!dist/**/*.test.d.ts.map",
23
+ "!dist/tests/**",
24
+ "!dist/examples/**",
25
+ "README.md",
26
+ "ARCHITECTURE.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
30
+ "c8": {
31
+ "include": ["dist/**/*.js"],
32
+ "exclude": ["dist/tests/**", "dist/examples/**"],
33
+ "reporter": ["text", "lcov"],
34
+ "all": true,
35
+ "check-coverage": true,
36
+ "lines": 90,
37
+ "functions": 90,
38
+ "branches": 90,
39
+ "statements": 90
40
+ },
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "test": "node --test dist/tests/*.test.js",
44
+ "coverage": "c8 node --test dist/tests/*.test.js",
45
+ "example": "node dist/examples/integration.js",
46
+ "lint": "tsc --noEmit",
47
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
48
+ },
49
+ "keywords": [
50
+ "streetjs",
51
+ "street-framework",
52
+ "schema",
53
+ "introspection",
54
+ "database",
55
+ "postgresql",
56
+ "mysql",
57
+ "sqlite",
58
+ "information-schema",
59
+ "typescript",
60
+ "esm"
61
+ ],
62
+ "author": "street contributors",
63
+ "license": "MIT",
64
+ "homepage": "https://hassanmubiru.github.io/StreetJS/",
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/hassanmubiru/StreetJS.git"
68
+ },
69
+ "bugs": {
70
+ "url": "https://github.com/hassanmubiru/StreetJS/issues"
71
+ },
72
+ "dependencies": {
73
+ "@streetjs/postgres": "^1.0.0"
74
+ },
75
+ "devDependencies": {
76
+ "@types/node": "^26.1.0",
77
+ "c8": "^11.0.0",
78
+ "typescript": "^6.0.3"
79
+ },
80
+ "peerDependencies": {
81
+ "typescript": ">=5.0.0"
82
+ },
83
+ "engines": {
84
+ "node": ">=22.0.0"
85
+ },
86
+ "publishConfig": {
87
+ "access": "public"
88
+ }
89
+ }