@axiom-lattice/pg-stores 1.0.4 → 1.0.6

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/dist/index.js CHANGED
@@ -23,9 +23,11 @@ __export(index_exports, {
23
23
  MigrationManager: () => MigrationManager,
24
24
  PostgreSQLAssistantStore: () => PostgreSQLAssistantStore,
25
25
  PostgreSQLScheduleStorage: () => PostgreSQLScheduleStorage,
26
+ PostgreSQLSkillStore: () => PostgreSQLSkillStore,
26
27
  PostgreSQLThreadStore: () => PostgreSQLThreadStore,
27
28
  createAssistantsTable: () => createAssistantsTable,
28
29
  createScheduledTasksTable: () => createScheduledTasksTable,
30
+ createSkillsTable: () => createSkillsTable,
29
31
  createThreadsTable: () => createThreadsTable
30
32
  });
31
33
  module.exports = __toCommonJS(index_exports);
@@ -76,36 +78,45 @@ var MigrationManager = class {
76
78
  }
77
79
  /**
78
80
  * Apply pending migrations
81
+ * Uses PostgreSQL advisory locks to prevent concurrent migrations
79
82
  */
80
83
  async migrate() {
81
84
  const client = await this.pool.connect();
82
85
  try {
83
- await client.query("BEGIN");
84
- await this.ensureMigrationsTable(client);
85
- const appliedMigrations = await this.getAppliedMigrations(client);
86
- const appliedVersions = new Set(appliedMigrations.map((m) => m.version));
87
- const pendingMigrations = this.migrations.filter(
88
- (m) => !appliedVersions.has(m.version)
89
- );
90
- if (pendingMigrations.length === 0) {
91
- console.log("No pending migrations");
92
- await client.query("COMMIT");
93
- return;
94
- }
95
- for (const migration of pendingMigrations) {
96
- console.log(
97
- `Applying migration ${migration.version}: ${migration.name}`
98
- );
99
- await migration.up(client);
100
- await client.query(
101
- "INSERT INTO lattice_schema_migrations (version, name) VALUES ($1, $2)",
102
- [migration.version, migration.name]
86
+ const lockId = 123456789;
87
+ await client.query("SELECT pg_advisory_lock($1)", [lockId]);
88
+ try {
89
+ await client.query("BEGIN");
90
+ await this.ensureMigrationsTable(client);
91
+ const appliedMigrations = await this.getAppliedMigrations(client);
92
+ const appliedVersions = new Set(appliedMigrations.map((m) => m.version));
93
+ const pendingMigrations = this.migrations.filter(
94
+ (m) => !appliedVersions.has(m.version)
103
95
  );
96
+ if (pendingMigrations.length === 0) {
97
+ console.log("No pending migrations");
98
+ await client.query("COMMIT");
99
+ return;
100
+ }
101
+ for (const migration of pendingMigrations) {
102
+ console.log(
103
+ `Applying migration ${migration.version}: ${migration.name}`
104
+ );
105
+ await migration.up(client);
106
+ await client.query(
107
+ "INSERT INTO lattice_schema_migrations (version, name) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING",
108
+ [migration.version, migration.name]
109
+ );
110
+ }
111
+ await client.query("COMMIT");
112
+ console.log(`Applied ${pendingMigrations.length} migration(s)`);
113
+ } catch (error) {
114
+ await client.query("ROLLBACK");
115
+ throw error;
116
+ } finally {
117
+ await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
104
118
  }
105
- await client.query("COMMIT");
106
- console.log(`Applied ${pendingMigrations.length} migration(s)`);
107
119
  } catch (error) {
108
- await client.query("ROLLBACK");
109
120
  throw error;
110
121
  } finally {
111
122
  client.release();
@@ -200,9 +211,11 @@ var createThreadsTable = {
200
211
 
201
212
  // src/stores/PostgreSQLThreadStore.ts
202
213
  var PostgreSQLThreadStore = class {
214
+ // Promise-based lock to prevent concurrent initialization
203
215
  constructor(options) {
204
216
  this.initialized = false;
205
217
  this.ownsPool = true;
218
+ this.initPromise = null;
206
219
  if (typeof options.poolConfig === "string") {
207
220
  this.pool = new import_pg.Pool({ connectionString: options.poolConfig });
208
221
  } else {
@@ -228,13 +241,24 @@ var PostgreSQLThreadStore = class {
228
241
  }
229
242
  /**
230
243
  * Initialize the store and run migrations
244
+ * Uses a promise-based lock to prevent concurrent initialization
231
245
  */
232
246
  async initialize() {
233
247
  if (this.initialized) {
234
248
  return;
235
249
  }
236
- await this.migrationManager.migrate();
237
- this.initialized = true;
250
+ if (this.initPromise) {
251
+ return this.initPromise;
252
+ }
253
+ this.initPromise = (async () => {
254
+ try {
255
+ await this.migrationManager.migrate();
256
+ this.initialized = true;
257
+ } finally {
258
+ this.initPromise = null;
259
+ }
260
+ })();
261
+ return this.initPromise;
238
262
  }
239
263
  /**
240
264
  * Get all threads for a specific assistant
@@ -1146,14 +1170,352 @@ var PostgreSQLScheduleStorage = class {
1146
1170
  };
1147
1171
  }
1148
1172
  };
1173
+
1174
+ // src/stores/PostgreSQLSkillStore.ts
1175
+ var import_pg4 = require("pg");
1176
+
1177
+ // src/migrations/skill_migrations.ts
1178
+ var createSkillsTable = {
1179
+ version: 1,
1180
+ name: "create_skills_table",
1181
+ up: async (client) => {
1182
+ await client.query(`
1183
+ CREATE TABLE IF NOT EXISTS lattice_skills (
1184
+ id VARCHAR(255) PRIMARY KEY,
1185
+ name VARCHAR(255) NOT NULL,
1186
+ description TEXT NOT NULL,
1187
+ license VARCHAR(255),
1188
+ compatibility VARCHAR(255),
1189
+ metadata JSONB DEFAULT '{}',
1190
+ content TEXT,
1191
+ sub_skills JSONB DEFAULT '[]',
1192
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
1193
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
1194
+ )
1195
+ `);
1196
+ await client.query(`
1197
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_name
1198
+ ON lattice_skills(name)
1199
+ `);
1200
+ await client.query(`
1201
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_license
1202
+ ON lattice_skills(license)
1203
+ `);
1204
+ await client.query(`
1205
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_compatibility
1206
+ ON lattice_skills(compatibility)
1207
+ `);
1208
+ await client.query(`
1209
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_created_at
1210
+ ON lattice_skills(created_at DESC)
1211
+ `);
1212
+ await client.query(`
1213
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_metadata
1214
+ ON lattice_skills USING GIN (metadata)
1215
+ `);
1216
+ },
1217
+ down: async (client) => {
1218
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_metadata");
1219
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_created_at");
1220
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_compatibility");
1221
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_license");
1222
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_name");
1223
+ await client.query("DROP TABLE IF EXISTS lattice_skills");
1224
+ }
1225
+ };
1226
+
1227
+ // src/stores/PostgreSQLSkillStore.ts
1228
+ var PostgreSQLSkillStore = class {
1229
+ constructor(options) {
1230
+ this.initialized = false;
1231
+ this.ownsPool = true;
1232
+ if (typeof options.poolConfig === "string") {
1233
+ this.pool = new import_pg4.Pool({ connectionString: options.poolConfig });
1234
+ } else {
1235
+ this.pool = new import_pg4.Pool(options.poolConfig);
1236
+ }
1237
+ this.migrationManager = new MigrationManager(this.pool);
1238
+ this.migrationManager.register(createSkillsTable);
1239
+ if (options.autoMigrate !== false) {
1240
+ this.initialize().catch((error) => {
1241
+ console.error("Failed to initialize PostgreSQLSkillStore:", error);
1242
+ throw error;
1243
+ });
1244
+ }
1245
+ }
1246
+ /**
1247
+ * Dispose resources and close the connection pool
1248
+ * Should be called when the store is no longer needed
1249
+ */
1250
+ async dispose() {
1251
+ if (this.ownsPool && this.pool) {
1252
+ await this.pool.end();
1253
+ }
1254
+ }
1255
+ /**
1256
+ * Initialize the store and run migrations
1257
+ */
1258
+ async initialize() {
1259
+ if (this.initialized) {
1260
+ return;
1261
+ }
1262
+ await this.migrationManager.migrate();
1263
+ this.initialized = true;
1264
+ }
1265
+ /**
1266
+ * Ensure store is initialized
1267
+ */
1268
+ async ensureInitialized() {
1269
+ if (!this.initialized) {
1270
+ await this.initialize();
1271
+ }
1272
+ }
1273
+ /**
1274
+ * Map database row to Skill object
1275
+ */
1276
+ mapRowToSkill(row) {
1277
+ return {
1278
+ id: row.id,
1279
+ name: row.name,
1280
+ description: row.description,
1281
+ license: row.license || void 0,
1282
+ compatibility: row.compatibility || void 0,
1283
+ metadata: row.metadata || {},
1284
+ content: row.content || void 0,
1285
+ subSkills: Array.isArray(row.sub_skills) && row.sub_skills.length > 0 ? row.sub_skills : void 0,
1286
+ createdAt: row.created_at,
1287
+ updatedAt: row.updated_at
1288
+ };
1289
+ }
1290
+ /**
1291
+ * Get all skills
1292
+ */
1293
+ async getAllSkills() {
1294
+ await this.ensureInitialized();
1295
+ const result = await this.pool.query(
1296
+ `
1297
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1298
+ FROM lattice_skills
1299
+ ORDER BY created_at DESC
1300
+ `
1301
+ );
1302
+ return result.rows.map(this.mapRowToSkill);
1303
+ }
1304
+ /**
1305
+ * Get skill by ID
1306
+ */
1307
+ async getSkillById(id) {
1308
+ await this.ensureInitialized();
1309
+ const result = await this.pool.query(
1310
+ `
1311
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1312
+ FROM lattice_skills
1313
+ WHERE id = $1
1314
+ `,
1315
+ [id]
1316
+ );
1317
+ if (result.rows.length === 0) {
1318
+ return null;
1319
+ }
1320
+ return this.mapRowToSkill(result.rows[0]);
1321
+ }
1322
+ /**
1323
+ * Create a new skill
1324
+ */
1325
+ async createSkill(id, data) {
1326
+ await this.ensureInitialized();
1327
+ const now = /* @__PURE__ */ new Date();
1328
+ const metadata = data.metadata || {};
1329
+ await this.pool.query(
1330
+ `
1331
+ INSERT INTO lattice_skills (id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at)
1332
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
1333
+ ON CONFLICT (id) DO UPDATE SET
1334
+ name = EXCLUDED.name,
1335
+ description = EXCLUDED.description,
1336
+ license = EXCLUDED.license,
1337
+ compatibility = EXCLUDED.compatibility,
1338
+ metadata = EXCLUDED.metadata,
1339
+ content = EXCLUDED.content,
1340
+ sub_skills = EXCLUDED.sub_skills,
1341
+ updated_at = EXCLUDED.updated_at
1342
+ `,
1343
+ [
1344
+ id,
1345
+ data.name,
1346
+ data.description,
1347
+ data.license || null,
1348
+ data.compatibility || null,
1349
+ JSON.stringify(metadata),
1350
+ data.content || null,
1351
+ JSON.stringify(data.subSkills || []),
1352
+ now,
1353
+ now
1354
+ ]
1355
+ );
1356
+ return this.getSkillById(id);
1357
+ }
1358
+ /**
1359
+ * Update an existing skill
1360
+ */
1361
+ async updateSkill(id, updates) {
1362
+ await this.ensureInitialized();
1363
+ const updateFields = [];
1364
+ const values = [];
1365
+ let paramIndex = 1;
1366
+ if (updates.name !== void 0) {
1367
+ updateFields.push(`name = $${paramIndex++}`);
1368
+ values.push(updates.name);
1369
+ }
1370
+ if (updates.description !== void 0) {
1371
+ updateFields.push(`description = $${paramIndex++}`);
1372
+ values.push(updates.description);
1373
+ }
1374
+ if (updates.license !== void 0) {
1375
+ updateFields.push(`license = $${paramIndex++}`);
1376
+ values.push(updates.license || null);
1377
+ }
1378
+ if (updates.compatibility !== void 0) {
1379
+ updateFields.push(`compatibility = $${paramIndex++}`);
1380
+ values.push(updates.compatibility || null);
1381
+ }
1382
+ if (updates.metadata !== void 0) {
1383
+ updateFields.push(`metadata = $${paramIndex++}`);
1384
+ values.push(JSON.stringify(updates.metadata || {}));
1385
+ }
1386
+ if (updates.content !== void 0) {
1387
+ updateFields.push(`content = $${paramIndex++}`);
1388
+ values.push(updates.content || null);
1389
+ }
1390
+ if (updates.subSkills !== void 0) {
1391
+ updateFields.push(`sub_skills = $${paramIndex++}`);
1392
+ values.push(JSON.stringify(updates.subSkills || []));
1393
+ }
1394
+ if (updateFields.length === 0) {
1395
+ return this.getSkillById(id);
1396
+ }
1397
+ updateFields.push(`updated_at = $${paramIndex++}`);
1398
+ values.push(/* @__PURE__ */ new Date());
1399
+ values.push(id);
1400
+ await this.pool.query(
1401
+ `
1402
+ UPDATE lattice_skills
1403
+ SET ${updateFields.join(", ")}
1404
+ WHERE id = $${paramIndex}
1405
+ `,
1406
+ values
1407
+ );
1408
+ return this.getSkillById(id);
1409
+ }
1410
+ /**
1411
+ * Delete a skill by ID
1412
+ */
1413
+ async deleteSkill(id) {
1414
+ await this.ensureInitialized();
1415
+ const result = await this.pool.query(
1416
+ `
1417
+ DELETE FROM lattice_skills
1418
+ WHERE id = $1
1419
+ `,
1420
+ [id]
1421
+ );
1422
+ return result.rowCount !== null && result.rowCount > 0;
1423
+ }
1424
+ /**
1425
+ * Check if skill exists
1426
+ */
1427
+ async hasSkill(id) {
1428
+ await this.ensureInitialized();
1429
+ const result = await this.pool.query(
1430
+ `
1431
+ SELECT COUNT(*) as count
1432
+ FROM lattice_skills
1433
+ WHERE id = $1
1434
+ `,
1435
+ [id]
1436
+ );
1437
+ return parseInt(result.rows[0].count, 10) > 0;
1438
+ }
1439
+ /**
1440
+ * Search skills by metadata
1441
+ */
1442
+ async searchByMetadata(metadataKey, metadataValue) {
1443
+ await this.ensureInitialized();
1444
+ const result = await this.pool.query(
1445
+ `
1446
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1447
+ FROM lattice_skills
1448
+ WHERE metadata->>$1 = $2
1449
+ ORDER BY created_at DESC
1450
+ `,
1451
+ [metadataKey, metadataValue]
1452
+ );
1453
+ return result.rows.map(this.mapRowToSkill);
1454
+ }
1455
+ /**
1456
+ * Filter skills by compatibility
1457
+ */
1458
+ async filterByCompatibility(compatibility) {
1459
+ await this.ensureInitialized();
1460
+ const result = await this.pool.query(
1461
+ `
1462
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1463
+ FROM lattice_skills
1464
+ WHERE compatibility = $1
1465
+ ORDER BY created_at DESC
1466
+ `,
1467
+ [compatibility]
1468
+ );
1469
+ return result.rows.map(this.mapRowToSkill);
1470
+ }
1471
+ /**
1472
+ * Filter skills by license
1473
+ */
1474
+ async filterByLicense(license) {
1475
+ await this.ensureInitialized();
1476
+ const result = await this.pool.query(
1477
+ `
1478
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1479
+ FROM lattice_skills
1480
+ WHERE license = $1
1481
+ ORDER BY created_at DESC
1482
+ `,
1483
+ [license]
1484
+ );
1485
+ return result.rows.map(this.mapRowToSkill);
1486
+ }
1487
+ /**
1488
+ * Get sub-skills of a parent skill
1489
+ */
1490
+ async getSubSkills(parentSkillName) {
1491
+ await this.ensureInitialized();
1492
+ const parentSkill = await this.getSkillById(parentSkillName);
1493
+ if (!parentSkill || !parentSkill.subSkills || parentSkill.subSkills.length === 0) {
1494
+ return [];
1495
+ }
1496
+ const placeholders = parentSkill.subSkills.map((_, index) => `$${index + 1}`).join(", ");
1497
+ const result = await this.pool.query(
1498
+ `
1499
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1500
+ FROM lattice_skills
1501
+ WHERE name IN (${placeholders})
1502
+ ORDER BY created_at DESC
1503
+ `,
1504
+ parentSkill.subSkills
1505
+ );
1506
+ return result.rows.map(this.mapRowToSkill);
1507
+ }
1508
+ };
1149
1509
  // Annotate the CommonJS export names for ESM import in node:
1150
1510
  0 && (module.exports = {
1151
1511
  MigrationManager,
1152
1512
  PostgreSQLAssistantStore,
1153
1513
  PostgreSQLScheduleStorage,
1514
+ PostgreSQLSkillStore,
1154
1515
  PostgreSQLThreadStore,
1155
1516
  createAssistantsTable,
1156
1517
  createScheduledTasksTable,
1518
+ createSkillsTable,
1157
1519
  createThreadsTable
1158
1520
  });
1159
1521
  //# sourceMappingURL=index.js.map