@axiom-lattice/pg-stores 1.0.86 → 1.0.87

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/pg-stores",
3
- "version": "1.0.86",
3
+ "version": "1.0.87",
4
4
  "description": "PG stores implementation for Axiom Lattice framework",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -25,8 +25,8 @@
25
25
  "@langchain/core": "1.1.30",
26
26
  "pg": "^8.16.3",
27
27
  "uuid": "^9.0.1",
28
- "@axiom-lattice/core": "2.1.95",
29
- "@axiom-lattice/protocols": "2.1.49"
28
+ "@axiom-lattice/core": "2.1.96",
29
+ "@axiom-lattice/protocols": "2.1.50"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^20.11.24",
@@ -3,6 +3,7 @@ import { MigrationManager } from "./migrations/migration";
3
3
  import { PostgreSQLThreadStore } from "./stores/PostgreSQLThreadStore";
4
4
  import { PostgreSQLAssistantStore } from "./stores/PostgreSQLAssistantStore";
5
5
  import { PostgreSQLDatabaseConfigStore } from "./stores/PostgreSQLDatabaseConfigStore";
6
+ import { PostgreSQLConnectionStore } from "./stores/PostgreSQLConnectionStore";
6
7
  import { PostgreSQLMetricsServerConfigStore } from "./stores/PostgreSQLMetricsServerConfigStore";
7
8
  import { PostgreSQLMcpServerConfigStore } from "./stores/PostgreSQLMcpServerConfigStore";
8
9
  import { PostgreSQLWorkspaceStore } from "./stores/PostgreSQLWorkspaceStore";
@@ -50,6 +51,7 @@ import { createChannelInstallationsTable } from "./migrations/channel_installati
50
51
  import { alterChannelInstallationsTable } from "./migrations/channel_installations_alter_migration";
51
52
  import { createChannelBindingsTable } from "./migrations/channel_bindings_migration";
52
53
  import { createDatabaseConfigsTable } from "./migrations/database_config_migrations";
54
+ import { createConnectionConfigsTable } from "./migrations/connection_config_migrations";
53
55
  import { createMetricsConfigsTable } from "./migrations/metrics_config_migrations";
54
56
  import { createMcpServerConfigsTable } from "./migrations/mcp_server_config_migrations";
55
57
  import { createThreadMessageQueueTable } from "./migrations/thread_message_queue_migrations";
@@ -116,6 +118,7 @@ export async function createPgStoreConfig(connectionString: string) {
116
118
  mm.register(createSharedResourcesTable); // v135
117
119
  mm.register(addFileContentType); // v136
118
120
  mm.register(createCollectionsTable); // v137
121
+ mm.register(createConnectionConfigsTable); // v160
119
122
 
120
123
  await mm.migrate();
121
124
 
@@ -137,6 +140,7 @@ export async function createPgStoreConfig(connectionString: string) {
137
140
  channelInstallation: new PostgreSQLChannelInstallationStore(opts),
138
141
  thread: new PostgreSQLThreadStore(opts),
139
142
  database: new PostgreSQLDatabaseConfigStore(opts),
143
+ connection: new PostgreSQLConnectionStore(pool),
140
144
  metrics: new PostgreSQLMetricsServerConfigStore(opts),
141
145
  mcp: new PostgreSQLMcpServerConfigStore(opts),
142
146
  assistant: new PostgreSQLAssistantStore(opts),
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export * from "./stores/PostgreSQLAssistantStore";
15
15
  export * from "./stores/PostgreSQLScheduleStorage";
16
16
  export * from "./stores/PostgreSQLSkillStore";
17
17
  export * from "./stores/PostgreSQLDatabaseConfigStore";
18
+ export * from "./stores/PostgreSQLConnectionStore";
18
19
  export * from "./stores/PostgreSQLMetricsServerConfigStore";
19
20
  export * from "./stores/PostgreSQLMcpServerConfigStore";
20
21
  export * from "./stores/PostgreSQLWorkspaceStore";
@@ -74,6 +75,7 @@ export { PostgreSQLAssistantStore } from "./stores/PostgreSQLAssistantStore";
74
75
  export { PostgreSQLScheduleStorage } from "./stores/PostgreSQLScheduleStorage";
75
76
  export { PostgreSQLSkillStore } from "./stores/PostgreSQLSkillStore";
76
77
  export { PostgreSQLDatabaseConfigStore } from "./stores/PostgreSQLDatabaseConfigStore";
78
+ export { PostgreSQLConnectionStore } from "./stores/PostgreSQLConnectionStore";
77
79
  export { PostgreSQLMetricsServerConfigStore } from "./stores/PostgreSQLMetricsServerConfigStore";
78
80
  export { PostgreSQLMcpServerConfigStore } from "./stores/PostgreSQLMcpServerConfigStore";
79
81
  export { PostgreSQLWorkspaceStore } from "./stores/PostgreSQLWorkspaceStore";
@@ -111,6 +113,8 @@ export type {
111
113
  UpdateDatabaseConfigRequest,
112
114
  DatabaseConfig,
113
115
  DatabaseType,
116
+ ConnectionStore,
117
+ ConnectionEntry,
114
118
  Binding,
115
119
  BindingRegistry,
116
120
  CreateBindingInput,
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Connection configs table migration
3
+ *
4
+ * Generic connection storage for plugin connections (ERP, CRM, etc.).
5
+ * Uses JSONB for config/metadata to accommodate any plugin type's shape
6
+ * without schema changes.
7
+ */
8
+ import { PoolClient } from "pg";
9
+ import { Migration } from "./migration";
10
+
11
+ export const createConnectionConfigsTable: Migration = {
12
+ version: 160,
13
+ name: "create_connection_configs_table",
14
+ up: async (client: PoolClient) => {
15
+ await client.query(`
16
+ CREATE TABLE IF NOT EXISTS lattice_connection_configs (
17
+ id VARCHAR(255) NOT NULL,
18
+ tenant_id VARCHAR(255) NOT NULL,
19
+ type VARCHAR(255) NOT NULL,
20
+ key VARCHAR(255) NOT NULL,
21
+ name VARCHAR(255) NOT NULL,
22
+ description TEXT,
23
+ config JSONB NOT NULL DEFAULT '{}',
24
+ status VARCHAR(32) NOT NULL DEFAULT 'active',
25
+ last_test_result JSONB,
26
+ metadata JSONB NOT NULL DEFAULT '{}',
27
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
28
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
29
+
30
+ PRIMARY KEY (tenant_id, id),
31
+ CONSTRAINT uk_lattice_connection_configs_tenant_type_key UNIQUE (tenant_id, type, key)
32
+ )
33
+ `);
34
+
35
+ await client.query(`
36
+ CREATE INDEX IF NOT EXISTS idx_connection_configs_tenant_id
37
+ ON lattice_connection_configs(tenant_id)
38
+ `);
39
+
40
+ await client.query(`
41
+ CREATE INDEX IF NOT EXISTS idx_connection_configs_tenant_type
42
+ ON lattice_connection_configs(tenant_id, type)
43
+ `);
44
+ },
45
+ down: async (client: PoolClient) => {
46
+ await client.query("DROP INDEX IF EXISTS idx_connection_configs_tenant_type");
47
+ await client.query("DROP INDEX IF EXISTS idx_connection_configs_tenant_id");
48
+ await client.query("DROP TABLE IF EXISTS lattice_connection_configs");
49
+ },
50
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * PostgreSQL implementation of ConnectionStore
3
+ */
4
+
5
+ import { Pool } from "pg";
6
+ import type { ConnectionEntry, ConnectionStore } from "@axiom-lattice/protocols";
7
+ import { v4 as uuid } from "uuid";
8
+
9
+ interface ConnectionRow {
10
+ id: string;
11
+ tenant_id: string;
12
+ type: string;
13
+ key: string;
14
+ name: string;
15
+ description: string | null;
16
+ config: unknown;
17
+ status: string;
18
+ last_test_result: unknown;
19
+ metadata: unknown;
20
+ created_at: Date;
21
+ updated_at: Date;
22
+ }
23
+
24
+ const TABLE = "lattice_connection_configs";
25
+
26
+ export class PostgreSQLConnectionStore implements ConnectionStore {
27
+ private db: Pool;
28
+
29
+ constructor(db: Pool) {
30
+ this.db = db;
31
+ }
32
+
33
+ async listByType(tenantId: string, type: string): Promise<ConnectionEntry[]> {
34
+ const result = await this.db.query<ConnectionRow>(
35
+ `SELECT * FROM ${TABLE} WHERE tenant_id = $1 AND type = $2 ORDER BY created_at`,
36
+ [tenantId, type],
37
+ );
38
+ return result.rows.map((row) => this.mapRow(row));
39
+ }
40
+
41
+ async getByKey(tenantId: string, type: string, key: string): Promise<ConnectionEntry | null> {
42
+ const result = await this.db.query<ConnectionRow>(
43
+ `SELECT * FROM ${TABLE} WHERE tenant_id = $1 AND type = $2 AND key = $3`,
44
+ [tenantId, type, key],
45
+ );
46
+ if (result.rows.length === 0) return null;
47
+ return this.mapRow(result.rows[0]);
48
+ }
49
+
50
+ async create(entry: Omit<ConnectionEntry, "id" | "createdAt" | "updatedAt">): Promise<ConnectionEntry> {
51
+ const id = uuid();
52
+ const now = new Date().toISOString();
53
+
54
+ await this.db.query(
55
+ `INSERT INTO ${TABLE} (id, tenant_id, type, key, name, description, config, status, created_at, updated_at)
56
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8::timestamp, $9::timestamp)`,
57
+ [
58
+ id, entry.tenantId, entry.type, entry.key, entry.name,
59
+ entry.description || null, JSON.stringify(entry.config), now, now,
60
+ ],
61
+ );
62
+
63
+ return { id, ...entry, createdAt: now, updatedAt: now };
64
+ }
65
+
66
+ async update(
67
+ tenantId: string, type: string, key: string, updates: Partial<ConnectionEntry>,
68
+ ): Promise<ConnectionEntry | null> {
69
+ const existing = await this.getByKey(tenantId, type, key);
70
+ if (!existing) return null;
71
+
72
+ const setClauses: string[] = [];
73
+ const values: unknown[] = [];
74
+ let paramIndex = 1;
75
+
76
+ if (updates.name !== undefined) {
77
+ setClauses.push(`name = $${paramIndex++}`);
78
+ values.push(updates.name);
79
+ }
80
+ if (updates.description !== undefined) {
81
+ setClauses.push(`description = $${paramIndex++}`);
82
+ values.push(updates.description || null);
83
+ }
84
+ if (updates.config !== undefined) {
85
+ setClauses.push(`config = $${paramIndex++}`);
86
+ values.push(JSON.stringify(updates.config));
87
+ }
88
+
89
+ if (setClauses.length === 0) return existing;
90
+
91
+ setClauses.push(`updated_at = $${paramIndex++}::timestamp`);
92
+ values.push(new Date().toISOString());
93
+
94
+ values.push(tenantId, type, key);
95
+ const whereTenant = paramIndex++;
96
+ const whereType = paramIndex++;
97
+ const whereKey = paramIndex;
98
+
99
+ await this.db.query(
100
+ `UPDATE ${TABLE} SET ${setClauses.join(", ")} WHERE tenant_id = $${whereTenant} AND type = $${whereType} AND key = $${whereKey}`,
101
+ values,
102
+ );
103
+
104
+ return this.getByKey(tenantId, type, key);
105
+ }
106
+
107
+ async delete(tenantId: string, type: string, key: string): Promise<boolean> {
108
+ const result = await this.db.query(
109
+ `DELETE FROM ${TABLE} WHERE tenant_id = $1 AND type = $2 AND key = $3`,
110
+ [tenantId, type, key],
111
+ );
112
+ return (result.rowCount ?? 0) > 0;
113
+ }
114
+
115
+ private mapRow(row: ConnectionRow): ConnectionEntry {
116
+ return {
117
+ id: row.id,
118
+ tenantId: row.tenant_id,
119
+ type: row.type,
120
+ key: row.key,
121
+ name: row.name,
122
+ description: row.description || undefined,
123
+ config: (typeof row.config === "string" ? JSON.parse(row.config) : row.config) as Record<string, unknown>,
124
+ createdAt: row.created_at instanceof Date ? row.created_at.toISOString() : String(row.created_at),
125
+ updatedAt: row.updated_at instanceof Date ? row.updated_at.toISOString() : String(row.updated_at),
126
+ };
127
+ }
128
+ }