pecunia-cli 0.2.7 → 0.2.8

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/api.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as generateKyselySchema, n as generateSchema, o as generateDrizzleSchema, r as generatePrismaSchema, t as adapters } from "./generators-Cb42XRwg.mjs";
1
+ import { a as generateKyselySchema, n as generateSchema, o as generateDrizzleSchema, r as generatePrismaSchema, t as adapters } from "./generators-BXA4y5O8.mjs";
2
2
 
3
3
  export { adapters, generateDrizzleSchema, generateKyselySchema, generatePrismaSchema, generateSchema };
@@ -325,29 +325,115 @@ async function getDrizzleMigrationsDir(projectRoot) {
325
325
  return drizzleDir;
326
326
  }
327
327
  /**
328
- * Find existing invariants migration file if it exists.
328
+ * Get the path to the Drizzle meta directory.
329
+ */
330
+ function getDrizzleMetaDir(projectRoot) {
331
+ return path.resolve(projectRoot, "drizzle", "meta");
332
+ }
333
+ /**
334
+ * Get the path to the Drizzle journal file.
335
+ */
336
+ function getJournalPath(projectRoot) {
337
+ return path.resolve(projectRoot, "drizzle", "meta", "_journal.json");
338
+ }
339
+ /**
340
+ * Read the Drizzle journal file, or return null if it doesn't exist.
341
+ */
342
+ async function readJournal(projectRoot) {
343
+ const journalPath = getJournalPath(projectRoot);
344
+ if (!existsSync(journalPath)) return null;
345
+ const content = await fs$1.readFile(journalPath, "utf-8");
346
+ return JSON.parse(content);
347
+ }
348
+ /**
349
+ * Write the Drizzle journal file with stable JSON formatting (2-space indent).
350
+ */
351
+ async function writeJournal(projectRoot, journal) {
352
+ const journalPath = getJournalPath(projectRoot);
353
+ await fs$1.writeFile(journalPath, JSON.stringify(journal, null, 2) + "\n", "utf-8");
354
+ }
355
+ /**
356
+ * Ensure the Drizzle journal exists. If it doesn't exist, create it with a baseline init migration.
329
357
  *
330
- * @param migrationsDir - Path to the drizzle migrations directory
331
- * @returns The migration number if found, or null
358
+ * @param projectRoot - The project root directory
359
+ * @returns true if journal was created, false if it already existed
332
360
  */
333
- async function findExistingInvariantsMigration(migrationsDir) {
334
- if (!existsSync(migrationsDir)) return null;
335
- const invariantsFile = (await fs$1.readdir(migrationsDir)).find((file) => file.endsWith("_pecunia_invariants.sql") && /^\d{4}_/.test(file));
336
- if (!invariantsFile) return null;
337
- const match = invariantsFile.match(/^(\d{4})_/);
338
- return match ? match[1] : null;
361
+ async function ensureDrizzleJournal(projectRoot) {
362
+ if (existsSync(getJournalPath(projectRoot))) return false;
363
+ const metaDir = getDrizzleMetaDir(projectRoot);
364
+ await fs$1.mkdir(metaDir, { recursive: true });
365
+ await writeJournal(projectRoot, {
366
+ version: "7",
367
+ dialect: "postgresql",
368
+ entries: []
369
+ });
370
+ const migrationsDir = await getDrizzleMigrationsDir(projectRoot);
371
+ const initMigrationPath = path.join(migrationsDir, "0000_pecunia_init.sql");
372
+ await fs$1.writeFile(initMigrationPath, "-- Initial no-op migration created by pecunia-cli\n", "utf-8");
373
+ await upsertJournalEntry(projectRoot, "0000_pecunia_init");
374
+ return true;
339
375
  }
340
376
  /**
341
- * Determine the next migration number by scanning existing migration files.
342
- * Migration files are expected to follow the pattern: `NNNN_description.sql`
343
- * where NNNN is a 4-digit number.
377
+ * Upsert a journal entry. If an entry with the same tag exists, do nothing.
378
+ * Otherwise, append a new entry with the next available idx.
344
379
  *
345
- * @param migrationsDir - Path to the drizzle migrations directory
380
+ * @param projectRoot - The project root directory
381
+ * @param tag - The migration tag (filename base without .sql)
382
+ */
383
+ async function upsertJournalEntry(projectRoot, tag) {
384
+ const journal = await readJournal(projectRoot);
385
+ if (!journal) throw new Error("Journal does not exist. Call ensureDrizzleJournal() first.");
386
+ if (journal.entries.find((entry) => entry.tag === tag)) return;
387
+ const nextIdx = (journal.entries.length > 0 ? Math.max(...journal.entries.map((e) => e.idx)) : -1) + 1;
388
+ const hasBreakpoints = journal.entries.some((e) => e.breakpoints === true);
389
+ const newEntry = {
390
+ tag,
391
+ idx: nextIdx,
392
+ version: journal.version,
393
+ when: Date.now(),
394
+ ...hasBreakpoints && { breakpoints: true }
395
+ };
396
+ journal.entries.push(newEntry);
397
+ await writeJournal(projectRoot, journal);
398
+ }
399
+ /**
400
+ * Find existing invariants migration tag in the journal.
401
+ *
402
+ * @param projectRoot - The project root directory
403
+ * @returns The tag if found, or null
404
+ */
405
+ async function findExistingInvariantsTag(projectRoot) {
406
+ const journal = await readJournal(projectRoot);
407
+ if (!journal) return null;
408
+ const invariantsEntry = journal.entries.find((entry) => entry.tag.endsWith("_pecunia_invariants"));
409
+ return invariantsEntry ? invariantsEntry.tag : null;
410
+ }
411
+ /**
412
+ * Determine the next migration number by checking journal entries and existing files.
413
+ * Prefers checking journal entries first, then falls back to scanning files.
414
+ *
415
+ * @param projectRoot - The project root directory
346
416
  * @returns The next migration number (4-digit string, e.g., "0001")
347
417
  */
348
- async function getNextMigrationNumber(migrationsDir) {
349
- const existingInvariantsNumber = await findExistingInvariantsMigration(migrationsDir);
350
- if (existingInvariantsNumber) return existingInvariantsNumber;
418
+ async function nextMigrationNumber(projectRoot) {
419
+ const existingInvariantsTag = await findExistingInvariantsTag(projectRoot);
420
+ if (existingInvariantsTag) {
421
+ const match = existingInvariantsTag.match(/^(\d{4})_/);
422
+ if (match) return match[1];
423
+ }
424
+ const journal = await readJournal(projectRoot);
425
+ if (journal && journal.entries.length > 0) {
426
+ const numbers$1 = journal.entries.map((entry) => {
427
+ const match = entry.tag.match(/^(\d{4})_/);
428
+ return match ? parseInt(match[1], 10) : 0;
429
+ }).filter((n) => n >= 0);
430
+ if (numbers$1.length > 0) {
431
+ const nextNumber$1 = Math.max(...numbers$1) + 1;
432
+ if (nextNumber$1 >= 9999) return "9999";
433
+ return String(nextNumber$1).padStart(4, "0");
434
+ }
435
+ }
436
+ const migrationsDir = await getDrizzleMigrationsDir(projectRoot);
351
437
  if (!existsSync(migrationsDir)) return "0001";
352
438
  const migrationFiles = (await fs$1.readdir(migrationsDir)).filter((file) => file.endsWith(".sql") && /^\d{4}_/.test(file));
353
439
  if (migrationFiles.length === 0) return "0001";
@@ -373,31 +459,57 @@ function getInvariantsMigrationPath(migrationsDir, migrationNumber) {
373
459
  return path.join(migrationsDir, `${migrationNumber}_pecunia_invariants.sql`);
374
460
  }
375
461
  /**
462
+ * Upsert a Drizzle migration file and register it in the journal.
463
+ *
464
+ * @param projectRoot - The project root directory
465
+ * @param tag - The migration tag (filename base without .sql)
466
+ * @param sql - The SQL content to write
467
+ */
468
+ async function upsertDrizzleMigration(projectRoot, tag, sql) {
469
+ const migrationsDir = await getDrizzleMigrationsDir(projectRoot);
470
+ const migrationPath = path.join(migrationsDir, `${tag}.sql`);
471
+ if (existsSync(migrationPath)) {
472
+ if (await fs$1.readFile(migrationPath, "utf-8") === sql) {
473
+ await upsertJournalEntry(projectRoot, tag);
474
+ return;
475
+ }
476
+ }
477
+ await fs$1.writeFile(migrationPath, sql, "utf-8");
478
+ await upsertJournalEntry(projectRoot, tag);
479
+ }
480
+ /**
376
481
  * Write the invariants SQL file to the drizzle migrations directory.
377
- * Handles idempotency by checking if the file exists and has the same content.
482
+ * Ensures journal exists, determines migration number, and registers in journal.
378
483
  *
379
484
  * @param projectRoot - The project root directory
380
485
  * @param sqlContent - The SQL content to write
381
- * @returns The path to the written file, or null if no changes were needed
486
+ * @returns The path to the written file and status flags
382
487
  */
383
488
  async function writeInvariantsMigration(projectRoot, sqlContent) {
384
- const migrationsDir = await getDrizzleMigrationsDir(projectRoot);
385
- const migrationPath = getInvariantsMigrationPath(migrationsDir, await getNextMigrationNumber(migrationsDir));
489
+ const journalCreated = await ensureDrizzleJournal(projectRoot);
490
+ const migrationNumber = await nextMigrationNumber(projectRoot);
491
+ const tag = `${migrationNumber}_pecunia_invariants`;
492
+ const migrationPath = getInvariantsMigrationPath(await getDrizzleMigrationsDir(projectRoot), migrationNumber);
386
493
  let created = false;
387
494
  let updated = false;
388
495
  if (existsSync(migrationPath)) {
389
- if (await fs$1.readFile(migrationPath, "utf-8") === sqlContent) return {
390
- path: migrationPath,
391
- created: false,
392
- updated: false
393
- };
496
+ if (await fs$1.readFile(migrationPath, "utf-8") === sqlContent) {
497
+ await upsertJournalEntry(projectRoot, tag);
498
+ return {
499
+ path: migrationPath,
500
+ created: false,
501
+ updated: false,
502
+ journalCreated
503
+ };
504
+ }
394
505
  updated = true;
395
506
  } else created = true;
396
- await fs$1.writeFile(migrationPath, sqlContent, "utf-8");
507
+ await upsertDrizzleMigration(projectRoot, tag, sqlContent);
397
508
  return {
398
509
  path: migrationPath,
399
510
  created,
400
- updated
511
+ updated,
512
+ journalCreated
401
513
  };
402
514
  }
403
515
 
@@ -796,7 +908,8 @@ const generateDrizzleSchema = async ({ options, file, adapter }) => {
796
908
  getModelName,
797
909
  getFieldName
798
910
  }), "public"));
799
- if (result.created) console.log(`Generated invariants migration: ${path.relative(projectRoot, result.path)}\nNote: Created ./drizzle directory. Invariants SQL is placed in Drizzle migrations.`);
911
+ if (result.journalCreated) console.log(`Created Drizzle migration journal and invariants migration; now run \`npx drizzle-kit migrate\`.`);
912
+ else if (result.created) console.log(`Generated invariants migration: ${path.relative(projectRoot, result.path)}`);
800
913
  else if (result.updated) console.log(`Updated invariants migration: ${path.relative(projectRoot, result.path)}`);
801
914
  else console.log(`Invariants migration up to date: ${path.relative(projectRoot, result.path)}`);
802
915
  }
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as getPackageInfo, n as generateSchema } from "./generators-Cb42XRwg.mjs";
2
+ import { i as getPackageInfo, n as generateSchema } from "./generators-BXA4y5O8.mjs";
3
3
  import { Command } from "commander";
4
4
  import fs, { existsSync, readFileSync } from "node:fs";
5
5
  import fs$1 from "node:fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pecunia-cli",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "type": "module",
5
5
  "module": "dist/index.mjs",
6
6
  "main": "./dist/index.mjs",