schemalens 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A local-first developer tool that generates living architecture diagrams from your database schema. Point it at a database, and SchemaLens visualizes tables, columns, and foreign-key relationships on an interactive canvas.
4
4
 
5
- Supports **PostgreSQL** and **SQLite**.
5
+ Supports **PostgreSQL**, **MySQL**, **SQL Server**, **Oracle**, and **SQLite**.
6
6
 
7
7
  ## Features
8
8
 
package/dist/index.js CHANGED
@@ -40378,9 +40378,37 @@ var postgresConfigSchema = external_exports.object({
40378
40378
  password: external_exports.string(),
40379
40379
  ssl: external_exports.boolean().optional()
40380
40380
  });
40381
+ var mysqlConfigSchema = external_exports.object({
40382
+ dialect: external_exports.literal("mysql"),
40383
+ host: external_exports.string(),
40384
+ port: external_exports.number(),
40385
+ database: external_exports.string(),
40386
+ user: external_exports.string(),
40387
+ password: external_exports.string()
40388
+ });
40389
+ var mssqlConfigSchema = external_exports.object({
40390
+ dialect: external_exports.literal("mssql"),
40391
+ host: external_exports.string(),
40392
+ port: external_exports.number(),
40393
+ database: external_exports.string(),
40394
+ user: external_exports.string(),
40395
+ password: external_exports.string(),
40396
+ trustServerCertificate: external_exports.boolean().optional()
40397
+ });
40398
+ var oracleConfigSchema = external_exports.object({
40399
+ dialect: external_exports.literal("oracle"),
40400
+ host: external_exports.string(),
40401
+ port: external_exports.number(),
40402
+ serviceName: external_exports.string(),
40403
+ user: external_exports.string(),
40404
+ password: external_exports.string()
40405
+ });
40381
40406
  var connectionConfigSchema = external_exports.discriminatedUnion("dialect", [
40382
40407
  sqliteConfigSchema,
40383
- postgresConfigSchema
40408
+ postgresConfigSchema,
40409
+ mysqlConfigSchema,
40410
+ mssqlConfigSchema,
40411
+ oracleConfigSchema
40384
40412
  ]);
40385
40413
  var connectionIdSchema = external_exports.object({
40386
40414
  connectionId: external_exports.string().uuid()
@@ -40393,7 +40421,7 @@ var saveWorkspaceSchema = external_exports.object({
40393
40421
  id: external_exports.string().uuid().optional(),
40394
40422
  name: external_exports.string().min(1),
40395
40423
  databaseLabel: external_exports.string(),
40396
- dialect: external_exports.enum(["sqlite", "postgres"]),
40424
+ dialect: external_exports.enum(["sqlite", "postgres", "mysql", "mssql", "oracle"]),
40397
40425
  startTable: external_exports.string(),
40398
40426
  startSchema: external_exports.string().nullable(),
40399
40427
  depth: external_exports.number().int().positive(),
@@ -40539,37 +40567,287 @@ var PostgresDialect = class {
40539
40567
  }
40540
40568
  };
40541
40569
 
40570
+ // ../core/dist/introspection/dialects/mysql.js
40571
+ var MysqlDialect = class {
40572
+ async listTables(knex2) {
40573
+ const [rows] = await knex2.raw(`SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.tables
40574
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'
40575
+ ORDER BY TABLE_NAME`);
40576
+ return rows.map((r) => ({ name: r.TABLE_NAME, schema: r.TABLE_SCHEMA }));
40577
+ }
40578
+ async getTable(knex2, name, schema = null) {
40579
+ const s = schema ?? await this.currentDatabase(knex2);
40580
+ const columns = await this.getColumns(knex2, name, s);
40581
+ const foreignKeys = await this.getForeignKeys(knex2, name, s);
40582
+ return { name, schema: s, columns, foreignKeys };
40583
+ }
40584
+ async currentDatabase(knex2) {
40585
+ const [[row]] = await knex2.raw("SELECT DATABASE() AS db");
40586
+ return row.db;
40587
+ }
40588
+ async getColumns(knex2, table, schema) {
40589
+ const [rows] = await knex2.raw(`SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, ORDINAL_POSITION, COLUMN_KEY
40590
+ FROM information_schema.columns
40591
+ WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
40592
+ ORDER BY ORDINAL_POSITION`, [schema, table]);
40593
+ return rows.map((r) => ({
40594
+ name: r.COLUMN_NAME,
40595
+ dataType: r.DATA_TYPE,
40596
+ nullable: r.IS_NULLABLE === "YES",
40597
+ isPrimaryKey: r.COLUMN_KEY === "PRI",
40598
+ defaultValue: r.COLUMN_DEFAULT,
40599
+ ordinalPosition: r.ORDINAL_POSITION - 1
40600
+ }));
40601
+ }
40602
+ async getForeignKeys(knex2, table, schema) {
40603
+ const [rows] = await knex2.raw(`SELECT kcu.CONSTRAINT_NAME, kcu.COLUMN_NAME,
40604
+ kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_TABLE_SCHEMA, kcu.REFERENCED_COLUMN_NAME
40605
+ FROM information_schema.key_column_usage kcu
40606
+ JOIN information_schema.referential_constraints rc
40607
+ ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
40608
+ AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
40609
+ WHERE kcu.TABLE_SCHEMA = ? AND kcu.TABLE_NAME = ?
40610
+ AND kcu.REFERENCED_TABLE_NAME IS NOT NULL`, [schema, table]);
40611
+ return rows.map((r) => ({
40612
+ constraintName: r.CONSTRAINT_NAME,
40613
+ columnName: r.COLUMN_NAME,
40614
+ referencedTable: r.REFERENCED_TABLE_NAME,
40615
+ referencedSchema: r.REFERENCED_TABLE_SCHEMA,
40616
+ referencedColumn: r.REFERENCED_COLUMN_NAME
40617
+ }));
40618
+ }
40619
+ };
40620
+
40621
+ // ../core/dist/introspection/dialects/mssql.js
40622
+ var MssqlDialect = class {
40623
+ async listTables(knex2) {
40624
+ const result = await knex2.raw(`SELECT TABLE_SCHEMA AS table_schema, TABLE_NAME AS table_name
40625
+ FROM INFORMATION_SCHEMA.TABLES
40626
+ WHERE TABLE_TYPE = 'BASE TABLE'
40627
+ AND TABLE_SCHEMA NOT IN ('sys', 'INFORMATION_SCHEMA')
40628
+ ORDER BY TABLE_SCHEMA, TABLE_NAME`);
40629
+ return result.map((r) => ({
40630
+ name: r.table_name,
40631
+ schema: r.table_schema
40632
+ }));
40633
+ }
40634
+ async getTable(knex2, name, schema = "dbo") {
40635
+ const s = schema ?? "dbo";
40636
+ const columns = await this.getColumns(knex2, name, s);
40637
+ const foreignKeys = await this.getForeignKeys(knex2, name, s);
40638
+ return { name, schema: s, columns, foreignKeys };
40639
+ }
40640
+ async getColumns(knex2, table, schema) {
40641
+ const rows = await knex2.raw(`SELECT c.COLUMN_NAME AS column_name,
40642
+ c.DATA_TYPE AS data_type,
40643
+ c.IS_NULLABLE AS is_nullable,
40644
+ c.COLUMN_DEFAULT AS column_default,
40645
+ c.ORDINAL_POSITION AS ordinal_position,
40646
+ CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS is_pk
40647
+ FROM INFORMATION_SCHEMA.COLUMNS c
40648
+ LEFT JOIN (
40649
+ SELECT kcu.COLUMN_NAME, kcu.TABLE_NAME, kcu.TABLE_SCHEMA
40650
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
40651
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
40652
+ ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
40653
+ AND tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
40654
+ WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
40655
+ ) pk ON pk.COLUMN_NAME = c.COLUMN_NAME
40656
+ AND pk.TABLE_NAME = c.TABLE_NAME
40657
+ AND pk.TABLE_SCHEMA = c.TABLE_SCHEMA
40658
+ WHERE c.TABLE_SCHEMA = @p0 AND c.TABLE_NAME = @p1
40659
+ ORDER BY c.ORDINAL_POSITION`, [schema, table]);
40660
+ return rows.map((r) => ({
40661
+ name: r.column_name,
40662
+ dataType: r.data_type,
40663
+ nullable: r.is_nullable === "YES",
40664
+ isPrimaryKey: r.is_pk === 1,
40665
+ defaultValue: r.column_default,
40666
+ ordinalPosition: r.ordinal_position - 1
40667
+ }));
40668
+ }
40669
+ async getForeignKeys(knex2, table, schema) {
40670
+ const rows = await knex2.raw(`SELECT
40671
+ fk.CONSTRAINT_NAME AS constraint_name,
40672
+ cu.COLUMN_NAME AS column_name,
40673
+ pk.TABLE_NAME AS referenced_table,
40674
+ pk.TABLE_SCHEMA AS referenced_schema,
40675
+ pt.COLUMN_NAME AS referenced_column
40676
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS fk
40677
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu
40678
+ ON fk.CONSTRAINT_NAME = cu.CONSTRAINT_NAME
40679
+ AND fk.CONSTRAINT_SCHEMA = cu.TABLE_SCHEMA
40680
+ JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS pkc
40681
+ ON fk.UNIQUE_CONSTRAINT_NAME = pkc.CONSTRAINT_NAME
40682
+ AND fk.UNIQUE_CONSTRAINT_SCHEMA = pkc.TABLE_SCHEMA
40683
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE pt
40684
+ ON pkc.CONSTRAINT_NAME = pt.CONSTRAINT_NAME
40685
+ AND pkc.TABLE_SCHEMA = pt.TABLE_SCHEMA
40686
+ AND cu.ORDINAL_POSITION = pt.ORDINAL_POSITION
40687
+ CROSS APPLY (
40688
+ SELECT pkc.TABLE_NAME, pkc.TABLE_SCHEMA
40689
+ ) pk
40690
+ WHERE cu.TABLE_SCHEMA = @p0 AND cu.TABLE_NAME = @p1`, [schema, table]);
40691
+ return rows.map((r) => ({
40692
+ constraintName: r.constraint_name,
40693
+ columnName: r.column_name,
40694
+ referencedTable: r.referenced_table,
40695
+ referencedSchema: r.referenced_schema,
40696
+ referencedColumn: r.referenced_column
40697
+ }));
40698
+ }
40699
+ };
40700
+
40701
+ // ../core/dist/introspection/dialects/oracle.js
40702
+ var OracleDialect = class {
40703
+ async listTables(knex2) {
40704
+ const rows = await knex2.raw(`SELECT OWNER, TABLE_NAME FROM ALL_TABLES
40705
+ WHERE OWNER NOT IN ('SYS','SYSTEM','OUTLN','DIP','ORACLE_OCM','DBSNMP','APPQOSSYS','WMSYS','XDB','CTXSYS','MDSYS','OLAPSYS','ORDDATA','ORDSYS','LBACSYS','DVSYS','AUDSYS','GSMADMIN_INTERNAL')
40706
+ ORDER BY OWNER, TABLE_NAME`);
40707
+ return rows.map((r) => ({
40708
+ name: r.table_name,
40709
+ schema: r.owner
40710
+ }));
40711
+ }
40712
+ async getTable(knex2, name, schema = null) {
40713
+ const s = schema ?? await this.currentSchema(knex2);
40714
+ const columns = await this.getColumns(knex2, name, s);
40715
+ const foreignKeys = await this.getForeignKeys(knex2, name, s);
40716
+ return { name, schema: s, columns, foreignKeys };
40717
+ }
40718
+ async currentSchema(knex2) {
40719
+ const rows = await knex2.raw("SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') AS current_schema FROM DUAL");
40720
+ return rows[0].current_schema;
40721
+ }
40722
+ async getColumns(knex2, table, schema) {
40723
+ const rows = await knex2.raw(`SELECT c.COLUMN_NAME AS column_name,
40724
+ c.DATA_TYPE AS data_type,
40725
+ c.NULLABLE AS nullable,
40726
+ c.DATA_DEFAULT AS data_default,
40727
+ c.COLUMN_ID AS column_id,
40728
+ CASE WHEN pc.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS is_pk
40729
+ FROM ALL_TAB_COLUMNS c
40730
+ LEFT JOIN (
40731
+ SELECT cc.COLUMN_NAME, cc.TABLE_NAME, cc.OWNER
40732
+ FROM ALL_CONSTRAINTS con
40733
+ JOIN ALL_CONS_COLUMNS cc
40734
+ ON con.CONSTRAINT_NAME = cc.CONSTRAINT_NAME AND con.OWNER = cc.OWNER
40735
+ WHERE con.CONSTRAINT_TYPE = 'P'
40736
+ ) pc ON pc.COLUMN_NAME = c.COLUMN_NAME
40737
+ AND pc.TABLE_NAME = c.TABLE_NAME
40738
+ AND pc.OWNER = c.OWNER
40739
+ WHERE c.OWNER = :schema AND c.TABLE_NAME = :table
40740
+ ORDER BY c.COLUMN_ID`, { schema, table });
40741
+ return rows.map((r) => ({
40742
+ name: r.column_name,
40743
+ dataType: r.data_type,
40744
+ nullable: r.nullable === "Y",
40745
+ isPrimaryKey: r.is_pk === 1,
40746
+ defaultValue: r.data_default?.trim() ?? null,
40747
+ ordinalPosition: r.column_id - 1
40748
+ }));
40749
+ }
40750
+ async getForeignKeys(knex2, table, schema) {
40751
+ const rows = await knex2.raw(`SELECT
40752
+ c.CONSTRAINT_NAME AS constraint_name,
40753
+ cc.COLUMN_NAME AS column_name,
40754
+ rc.TABLE_NAME AS referenced_table,
40755
+ rc.OWNER AS referenced_schema,
40756
+ rc.COLUMN_NAME AS referenced_column
40757
+ FROM ALL_CONSTRAINTS c
40758
+ JOIN ALL_CONS_COLUMNS cc
40759
+ ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME AND c.OWNER = cc.OWNER
40760
+ JOIN ALL_CONS_COLUMNS rc
40761
+ ON c.R_CONSTRAINT_NAME = rc.CONSTRAINT_NAME AND c.R_OWNER = rc.OWNER
40762
+ AND cc.POSITION = rc.POSITION
40763
+ WHERE c.CONSTRAINT_TYPE = 'R'
40764
+ AND c.OWNER = :schema AND c.TABLE_NAME = :table`, { schema, table });
40765
+ return rows.map((r) => ({
40766
+ constraintName: r.constraint_name,
40767
+ columnName: r.column_name,
40768
+ referencedTable: r.referenced_table,
40769
+ referencedSchema: r.referenced_schema,
40770
+ referencedColumn: r.referenced_column
40771
+ }));
40772
+ }
40773
+ };
40774
+
40542
40775
  // ../core/dist/introspection/introspector.js
40543
40776
  var SchemaIntrospector = class {
40544
40777
  knex;
40545
40778
  dialect;
40546
40779
  constructor(config2) {
40547
- if (config2.dialect === "sqlite") {
40548
- this.knex = knex({
40549
- client: "better-sqlite3",
40550
- connection: { filename: config2.filePath },
40551
- useNullAsDefault: true,
40552
- pool: {
40553
- afterCreate(conn, done) {
40554
- conn.pragma("foreign_keys = ON");
40555
- done(null, conn);
40780
+ switch (config2.dialect) {
40781
+ case "sqlite":
40782
+ this.knex = knex({
40783
+ client: "better-sqlite3",
40784
+ connection: { filename: config2.filePath },
40785
+ useNullAsDefault: true,
40786
+ pool: {
40787
+ afterCreate(conn, done) {
40788
+ conn.pragma("foreign_keys = ON");
40789
+ done(null, conn);
40790
+ }
40556
40791
  }
40557
- }
40558
- });
40559
- this.dialect = new SqliteDialect();
40560
- } else {
40561
- this.knex = knex({
40562
- client: "pg",
40563
- connection: {
40564
- host: config2.host,
40565
- port: config2.port,
40566
- database: config2.database,
40567
- user: config2.user,
40568
- password: config2.password,
40569
- ssl: config2.ssl ? { rejectUnauthorized: false } : false
40570
- }
40571
- });
40572
- this.dialect = new PostgresDialect();
40792
+ });
40793
+ this.dialect = new SqliteDialect();
40794
+ break;
40795
+ case "postgres":
40796
+ this.knex = knex({
40797
+ client: "pg",
40798
+ connection: {
40799
+ host: config2.host,
40800
+ port: config2.port,
40801
+ database: config2.database,
40802
+ user: config2.user,
40803
+ password: config2.password,
40804
+ ssl: config2.ssl ? { rejectUnauthorized: false } : false
40805
+ }
40806
+ });
40807
+ this.dialect = new PostgresDialect();
40808
+ break;
40809
+ case "mysql":
40810
+ this.knex = knex({
40811
+ client: "mysql2",
40812
+ connection: {
40813
+ host: config2.host,
40814
+ port: config2.port,
40815
+ database: config2.database,
40816
+ user: config2.user,
40817
+ password: config2.password
40818
+ }
40819
+ });
40820
+ this.dialect = new MysqlDialect();
40821
+ break;
40822
+ case "mssql":
40823
+ this.knex = knex({
40824
+ client: "mssql",
40825
+ connection: {
40826
+ server: config2.host,
40827
+ port: config2.port,
40828
+ database: config2.database,
40829
+ user: config2.user,
40830
+ password: config2.password,
40831
+ options: {
40832
+ trustServerCertificate: config2.trustServerCertificate ?? false
40833
+ }
40834
+ }
40835
+ });
40836
+ this.dialect = new MssqlDialect();
40837
+ break;
40838
+ case "oracle":
40839
+ this.knex = knex({
40840
+ client: "oracledb",
40841
+ connection: {
40842
+ host: config2.host,
40843
+ port: config2.port,
40844
+ user: config2.user,
40845
+ password: config2.password,
40846
+ connectString: `${config2.host}:${config2.port}/${config2.serviceName}`
40847
+ }
40848
+ });
40849
+ this.dialect = new OracleDialect();
40850
+ break;
40573
40851
  }
40574
40852
  }
40575
40853
  async listTables() {
@@ -40680,7 +40958,7 @@ var connectionStore = {
40680
40958
  list() {
40681
40959
  return Array.from(connections.entries()).map(([connectionId, stored]) => {
40682
40960
  const { config: config2, createdAt } = stored;
40683
- const displayConfig = config2.dialect === "sqlite" ? { dialect: config2.dialect, filePath: config2.filePath } : { dialect: config2.dialect, host: config2.host, port: config2.port, database: config2.database, user: config2.user, ssl: config2.ssl };
40961
+ const displayConfig = config2.dialect === "sqlite" ? { dialect: config2.dialect, filePath: config2.filePath } : config2.dialect === "oracle" ? { dialect: config2.dialect, host: config2.host, port: config2.port, serviceName: config2.serviceName, user: config2.user } : { dialect: config2.dialect, host: config2.host, port: config2.port, database: config2.database, user: config2.user };
40684
40962
  return { connectionId, dialect: config2.dialect, createdAt, displayConfig };
40685
40963
  });
40686
40964
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schemalens",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "schemalens": "dist/index.js"
@@ -18,11 +18,14 @@
18
18
  "dependencies": {
19
19
  "better-sqlite3": "^11.0.0",
20
20
  "knex": "^3.1.0",
21
- "pg": "^8.13.0"
21
+ "mysql2": "^3.16.2",
22
+ "oracledb": "^6.10.0",
23
+ "pg": "^8.13.0",
24
+ "tedious": "^19.2.0"
22
25
  },
23
26
  "devDependencies": {
24
27
  "@schemalens/server": "*",
25
- "open": "^10.1.0",
26
- "esbuild": "^0.24.0"
28
+ "esbuild": "^0.24.0",
29
+ "open": "^10.1.0"
27
30
  }
28
31
  }