@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.mjs CHANGED
@@ -44,36 +44,45 @@ var MigrationManager = class {
44
44
  }
45
45
  /**
46
46
  * Apply pending migrations
47
+ * Uses PostgreSQL advisory locks to prevent concurrent migrations
47
48
  */
48
49
  async migrate() {
49
50
  const client = await this.pool.connect();
50
51
  try {
51
- await client.query("BEGIN");
52
- await this.ensureMigrationsTable(client);
53
- const appliedMigrations = await this.getAppliedMigrations(client);
54
- const appliedVersions = new Set(appliedMigrations.map((m) => m.version));
55
- const pendingMigrations = this.migrations.filter(
56
- (m) => !appliedVersions.has(m.version)
57
- );
58
- if (pendingMigrations.length === 0) {
59
- console.log("No pending migrations");
60
- await client.query("COMMIT");
61
- return;
62
- }
63
- for (const migration of pendingMigrations) {
64
- console.log(
65
- `Applying migration ${migration.version}: ${migration.name}`
66
- );
67
- await migration.up(client);
68
- await client.query(
69
- "INSERT INTO lattice_schema_migrations (version, name) VALUES ($1, $2)",
70
- [migration.version, migration.name]
52
+ const lockId = 123456789;
53
+ await client.query("SELECT pg_advisory_lock($1)", [lockId]);
54
+ try {
55
+ await client.query("BEGIN");
56
+ await this.ensureMigrationsTable(client);
57
+ const appliedMigrations = await this.getAppliedMigrations(client);
58
+ const appliedVersions = new Set(appliedMigrations.map((m) => m.version));
59
+ const pendingMigrations = this.migrations.filter(
60
+ (m) => !appliedVersions.has(m.version)
71
61
  );
62
+ if (pendingMigrations.length === 0) {
63
+ console.log("No pending migrations");
64
+ await client.query("COMMIT");
65
+ return;
66
+ }
67
+ for (const migration of pendingMigrations) {
68
+ console.log(
69
+ `Applying migration ${migration.version}: ${migration.name}`
70
+ );
71
+ await migration.up(client);
72
+ await client.query(
73
+ "INSERT INTO lattice_schema_migrations (version, name) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING",
74
+ [migration.version, migration.name]
75
+ );
76
+ }
77
+ await client.query("COMMIT");
78
+ console.log(`Applied ${pendingMigrations.length} migration(s)`);
79
+ } catch (error) {
80
+ await client.query("ROLLBACK");
81
+ throw error;
82
+ } finally {
83
+ await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
72
84
  }
73
- await client.query("COMMIT");
74
- console.log(`Applied ${pendingMigrations.length} migration(s)`);
75
85
  } catch (error) {
76
- await client.query("ROLLBACK");
77
86
  throw error;
78
87
  } finally {
79
88
  client.release();
@@ -168,9 +177,11 @@ var createThreadsTable = {
168
177
 
169
178
  // src/stores/PostgreSQLThreadStore.ts
170
179
  var PostgreSQLThreadStore = class {
180
+ // Promise-based lock to prevent concurrent initialization
171
181
  constructor(options) {
172
182
  this.initialized = false;
173
183
  this.ownsPool = true;
184
+ this.initPromise = null;
174
185
  if (typeof options.poolConfig === "string") {
175
186
  this.pool = new Pool({ connectionString: options.poolConfig });
176
187
  } else {
@@ -196,13 +207,24 @@ var PostgreSQLThreadStore = class {
196
207
  }
197
208
  /**
198
209
  * Initialize the store and run migrations
210
+ * Uses a promise-based lock to prevent concurrent initialization
199
211
  */
200
212
  async initialize() {
201
213
  if (this.initialized) {
202
214
  return;
203
215
  }
204
- await this.migrationManager.migrate();
205
- this.initialized = true;
216
+ if (this.initPromise) {
217
+ return this.initPromise;
218
+ }
219
+ this.initPromise = (async () => {
220
+ try {
221
+ await this.migrationManager.migrate();
222
+ this.initialized = true;
223
+ } finally {
224
+ this.initPromise = null;
225
+ }
226
+ })();
227
+ return this.initPromise;
206
228
  }
207
229
  /**
208
230
  * Get all threads for a specific assistant
@@ -1116,13 +1138,351 @@ var PostgreSQLScheduleStorage = class {
1116
1138
  };
1117
1139
  }
1118
1140
  };
1141
+
1142
+ // src/stores/PostgreSQLSkillStore.ts
1143
+ import { Pool as Pool4 } from "pg";
1144
+
1145
+ // src/migrations/skill_migrations.ts
1146
+ var createSkillsTable = {
1147
+ version: 1,
1148
+ name: "create_skills_table",
1149
+ up: async (client) => {
1150
+ await client.query(`
1151
+ CREATE TABLE IF NOT EXISTS lattice_skills (
1152
+ id VARCHAR(255) PRIMARY KEY,
1153
+ name VARCHAR(255) NOT NULL,
1154
+ description TEXT NOT NULL,
1155
+ license VARCHAR(255),
1156
+ compatibility VARCHAR(255),
1157
+ metadata JSONB DEFAULT '{}',
1158
+ content TEXT,
1159
+ sub_skills JSONB DEFAULT '[]',
1160
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
1161
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
1162
+ )
1163
+ `);
1164
+ await client.query(`
1165
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_name
1166
+ ON lattice_skills(name)
1167
+ `);
1168
+ await client.query(`
1169
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_license
1170
+ ON lattice_skills(license)
1171
+ `);
1172
+ await client.query(`
1173
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_compatibility
1174
+ ON lattice_skills(compatibility)
1175
+ `);
1176
+ await client.query(`
1177
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_created_at
1178
+ ON lattice_skills(created_at DESC)
1179
+ `);
1180
+ await client.query(`
1181
+ CREATE INDEX IF NOT EXISTS idx_lattice_skills_metadata
1182
+ ON lattice_skills USING GIN (metadata)
1183
+ `);
1184
+ },
1185
+ down: async (client) => {
1186
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_metadata");
1187
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_created_at");
1188
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_compatibility");
1189
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_license");
1190
+ await client.query("DROP INDEX IF EXISTS idx_lattice_skills_name");
1191
+ await client.query("DROP TABLE IF EXISTS lattice_skills");
1192
+ }
1193
+ };
1194
+
1195
+ // src/stores/PostgreSQLSkillStore.ts
1196
+ var PostgreSQLSkillStore = class {
1197
+ constructor(options) {
1198
+ this.initialized = false;
1199
+ this.ownsPool = true;
1200
+ if (typeof options.poolConfig === "string") {
1201
+ this.pool = new Pool4({ connectionString: options.poolConfig });
1202
+ } else {
1203
+ this.pool = new Pool4(options.poolConfig);
1204
+ }
1205
+ this.migrationManager = new MigrationManager(this.pool);
1206
+ this.migrationManager.register(createSkillsTable);
1207
+ if (options.autoMigrate !== false) {
1208
+ this.initialize().catch((error) => {
1209
+ console.error("Failed to initialize PostgreSQLSkillStore:", error);
1210
+ throw error;
1211
+ });
1212
+ }
1213
+ }
1214
+ /**
1215
+ * Dispose resources and close the connection pool
1216
+ * Should be called when the store is no longer needed
1217
+ */
1218
+ async dispose() {
1219
+ if (this.ownsPool && this.pool) {
1220
+ await this.pool.end();
1221
+ }
1222
+ }
1223
+ /**
1224
+ * Initialize the store and run migrations
1225
+ */
1226
+ async initialize() {
1227
+ if (this.initialized) {
1228
+ return;
1229
+ }
1230
+ await this.migrationManager.migrate();
1231
+ this.initialized = true;
1232
+ }
1233
+ /**
1234
+ * Ensure store is initialized
1235
+ */
1236
+ async ensureInitialized() {
1237
+ if (!this.initialized) {
1238
+ await this.initialize();
1239
+ }
1240
+ }
1241
+ /**
1242
+ * Map database row to Skill object
1243
+ */
1244
+ mapRowToSkill(row) {
1245
+ return {
1246
+ id: row.id,
1247
+ name: row.name,
1248
+ description: row.description,
1249
+ license: row.license || void 0,
1250
+ compatibility: row.compatibility || void 0,
1251
+ metadata: row.metadata || {},
1252
+ content: row.content || void 0,
1253
+ subSkills: Array.isArray(row.sub_skills) && row.sub_skills.length > 0 ? row.sub_skills : void 0,
1254
+ createdAt: row.created_at,
1255
+ updatedAt: row.updated_at
1256
+ };
1257
+ }
1258
+ /**
1259
+ * Get all skills
1260
+ */
1261
+ async getAllSkills() {
1262
+ await this.ensureInitialized();
1263
+ const result = await this.pool.query(
1264
+ `
1265
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1266
+ FROM lattice_skills
1267
+ ORDER BY created_at DESC
1268
+ `
1269
+ );
1270
+ return result.rows.map(this.mapRowToSkill);
1271
+ }
1272
+ /**
1273
+ * Get skill by ID
1274
+ */
1275
+ async getSkillById(id) {
1276
+ await this.ensureInitialized();
1277
+ const result = await this.pool.query(
1278
+ `
1279
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1280
+ FROM lattice_skills
1281
+ WHERE id = $1
1282
+ `,
1283
+ [id]
1284
+ );
1285
+ if (result.rows.length === 0) {
1286
+ return null;
1287
+ }
1288
+ return this.mapRowToSkill(result.rows[0]);
1289
+ }
1290
+ /**
1291
+ * Create a new skill
1292
+ */
1293
+ async createSkill(id, data) {
1294
+ await this.ensureInitialized();
1295
+ const now = /* @__PURE__ */ new Date();
1296
+ const metadata = data.metadata || {};
1297
+ await this.pool.query(
1298
+ `
1299
+ INSERT INTO lattice_skills (id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at)
1300
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
1301
+ ON CONFLICT (id) DO UPDATE SET
1302
+ name = EXCLUDED.name,
1303
+ description = EXCLUDED.description,
1304
+ license = EXCLUDED.license,
1305
+ compatibility = EXCLUDED.compatibility,
1306
+ metadata = EXCLUDED.metadata,
1307
+ content = EXCLUDED.content,
1308
+ sub_skills = EXCLUDED.sub_skills,
1309
+ updated_at = EXCLUDED.updated_at
1310
+ `,
1311
+ [
1312
+ id,
1313
+ data.name,
1314
+ data.description,
1315
+ data.license || null,
1316
+ data.compatibility || null,
1317
+ JSON.stringify(metadata),
1318
+ data.content || null,
1319
+ JSON.stringify(data.subSkills || []),
1320
+ now,
1321
+ now
1322
+ ]
1323
+ );
1324
+ return this.getSkillById(id);
1325
+ }
1326
+ /**
1327
+ * Update an existing skill
1328
+ */
1329
+ async updateSkill(id, updates) {
1330
+ await this.ensureInitialized();
1331
+ const updateFields = [];
1332
+ const values = [];
1333
+ let paramIndex = 1;
1334
+ if (updates.name !== void 0) {
1335
+ updateFields.push(`name = $${paramIndex++}`);
1336
+ values.push(updates.name);
1337
+ }
1338
+ if (updates.description !== void 0) {
1339
+ updateFields.push(`description = $${paramIndex++}`);
1340
+ values.push(updates.description);
1341
+ }
1342
+ if (updates.license !== void 0) {
1343
+ updateFields.push(`license = $${paramIndex++}`);
1344
+ values.push(updates.license || null);
1345
+ }
1346
+ if (updates.compatibility !== void 0) {
1347
+ updateFields.push(`compatibility = $${paramIndex++}`);
1348
+ values.push(updates.compatibility || null);
1349
+ }
1350
+ if (updates.metadata !== void 0) {
1351
+ updateFields.push(`metadata = $${paramIndex++}`);
1352
+ values.push(JSON.stringify(updates.metadata || {}));
1353
+ }
1354
+ if (updates.content !== void 0) {
1355
+ updateFields.push(`content = $${paramIndex++}`);
1356
+ values.push(updates.content || null);
1357
+ }
1358
+ if (updates.subSkills !== void 0) {
1359
+ updateFields.push(`sub_skills = $${paramIndex++}`);
1360
+ values.push(JSON.stringify(updates.subSkills || []));
1361
+ }
1362
+ if (updateFields.length === 0) {
1363
+ return this.getSkillById(id);
1364
+ }
1365
+ updateFields.push(`updated_at = $${paramIndex++}`);
1366
+ values.push(/* @__PURE__ */ new Date());
1367
+ values.push(id);
1368
+ await this.pool.query(
1369
+ `
1370
+ UPDATE lattice_skills
1371
+ SET ${updateFields.join(", ")}
1372
+ WHERE id = $${paramIndex}
1373
+ `,
1374
+ values
1375
+ );
1376
+ return this.getSkillById(id);
1377
+ }
1378
+ /**
1379
+ * Delete a skill by ID
1380
+ */
1381
+ async deleteSkill(id) {
1382
+ await this.ensureInitialized();
1383
+ const result = await this.pool.query(
1384
+ `
1385
+ DELETE FROM lattice_skills
1386
+ WHERE id = $1
1387
+ `,
1388
+ [id]
1389
+ );
1390
+ return result.rowCount !== null && result.rowCount > 0;
1391
+ }
1392
+ /**
1393
+ * Check if skill exists
1394
+ */
1395
+ async hasSkill(id) {
1396
+ await this.ensureInitialized();
1397
+ const result = await this.pool.query(
1398
+ `
1399
+ SELECT COUNT(*) as count
1400
+ FROM lattice_skills
1401
+ WHERE id = $1
1402
+ `,
1403
+ [id]
1404
+ );
1405
+ return parseInt(result.rows[0].count, 10) > 0;
1406
+ }
1407
+ /**
1408
+ * Search skills by metadata
1409
+ */
1410
+ async searchByMetadata(metadataKey, metadataValue) {
1411
+ await this.ensureInitialized();
1412
+ const result = await this.pool.query(
1413
+ `
1414
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1415
+ FROM lattice_skills
1416
+ WHERE metadata->>$1 = $2
1417
+ ORDER BY created_at DESC
1418
+ `,
1419
+ [metadataKey, metadataValue]
1420
+ );
1421
+ return result.rows.map(this.mapRowToSkill);
1422
+ }
1423
+ /**
1424
+ * Filter skills by compatibility
1425
+ */
1426
+ async filterByCompatibility(compatibility) {
1427
+ await this.ensureInitialized();
1428
+ const result = await this.pool.query(
1429
+ `
1430
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1431
+ FROM lattice_skills
1432
+ WHERE compatibility = $1
1433
+ ORDER BY created_at DESC
1434
+ `,
1435
+ [compatibility]
1436
+ );
1437
+ return result.rows.map(this.mapRowToSkill);
1438
+ }
1439
+ /**
1440
+ * Filter skills by license
1441
+ */
1442
+ async filterByLicense(license) {
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 license = $1
1449
+ ORDER BY created_at DESC
1450
+ `,
1451
+ [license]
1452
+ );
1453
+ return result.rows.map(this.mapRowToSkill);
1454
+ }
1455
+ /**
1456
+ * Get sub-skills of a parent skill
1457
+ */
1458
+ async getSubSkills(parentSkillName) {
1459
+ await this.ensureInitialized();
1460
+ const parentSkill = await this.getSkillById(parentSkillName);
1461
+ if (!parentSkill || !parentSkill.subSkills || parentSkill.subSkills.length === 0) {
1462
+ return [];
1463
+ }
1464
+ const placeholders = parentSkill.subSkills.map((_, index) => `$${index + 1}`).join(", ");
1465
+ const result = await this.pool.query(
1466
+ `
1467
+ SELECT id, name, description, license, compatibility, metadata, content, sub_skills, created_at, updated_at
1468
+ FROM lattice_skills
1469
+ WHERE name IN (${placeholders})
1470
+ ORDER BY created_at DESC
1471
+ `,
1472
+ parentSkill.subSkills
1473
+ );
1474
+ return result.rows.map(this.mapRowToSkill);
1475
+ }
1476
+ };
1119
1477
  export {
1120
1478
  MigrationManager,
1121
1479
  PostgreSQLAssistantStore,
1122
1480
  PostgreSQLScheduleStorage,
1481
+ PostgreSQLSkillStore,
1123
1482
  PostgreSQLThreadStore,
1124
1483
  createAssistantsTable,
1125
1484
  createScheduledTasksTable,
1485
+ createSkillsTable,
1126
1486
  createThreadsTable
1127
1487
  };
1128
1488
  //# sourceMappingURL=index.mjs.map