@noego/proper 0.1.0 → 0.2.1

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": "@noego/proper",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "module": "bin/index.mjs",
@@ -64,6 +64,7 @@
64
64
  "source-map-support": "^0.5.21",
65
65
  "sqlite": "^5.1.1",
66
66
  "sqlite3": "^5.1.7",
67
- "tsx": "^4.19.4"
67
+ "tsx": "^4.19.4",
68
+ "yaml": "^2.9.0"
68
69
  }
69
70
  }
package/readme.md CHANGED
@@ -266,6 +266,103 @@ describe('Database Tests', () => {
266
266
 
267
267
  ---
268
268
 
269
+ ## Ledger Patches (`proper patch`)
270
+
271
+ Ledger patches are one-shot, versioned YAML files that repair `proper_migrations`
272
+ history **without executing schema SQL**. Use them when applied migration files
273
+ were renamed after installation, when a ledger row is missing for schema that
274
+ already exists, or when a ledger row wrongly claims success.
275
+
276
+ Patches are discovered and applied automatically before Proper reads migration
277
+ completion state (CLI and `MigrationRunnerFactory.create()` alike), are
278
+ transactional, are applied at most once per configured migration ledger, and
279
+ work identically for MySQL/MariaDB, SQLite, and PostgreSQL.
280
+
281
+ ### Creating a patch
282
+
283
+ ```bash
284
+ proper patch fix_renamed_keys
285
+ proper patch --name fix_renamed_keys
286
+ ```
287
+
288
+ This scaffolds `<patch_folder>/<timestamp>_fix_renamed_keys.yaml` without
289
+ opening a database. The scaffold's `operations: []` is intentionally not
290
+ runnable — complete the file before running another database-backed command.
291
+
292
+ ### Configuration
293
+
294
+ ```jsonc
295
+ {
296
+ "patch_folder": "patches", // default: a `patches` sibling of migration_folder
297
+ "patch_table": "proper_patches" // default
298
+ }
299
+ ```
300
+
301
+ Patches may also be co-located with migrations by setting `patch_folder` to
302
+ the same path as `migration_folder` — migration discovery only considers
303
+ `*.up/down.sql|js` files and patch discovery only considers top-level
304
+ `*.yaml` files, so the two never interfere.
305
+
306
+ ### Patch file format (version 1)
307
+
308
+ ```yaml
309
+ version: 1
310
+ description: fix hand-stamped migration names
311
+ operations:
312
+ - rename_migration:
313
+ from: 1787905000000_create_conversation_run_lifecycle
314
+ to: 1787941600000_create_conversation_run_lifecycle
315
+ - mark_applied:
316
+ key: 1787941700000_existing_schema_baseline
317
+ - unmark_applied:
318
+ key: 1787941800000_incorrect_ledger_row
319
+ ```
320
+
321
+ Operations run in file order, then operation order. Each is a conditional
322
+ bookkeeping repair:
323
+
324
+ - `rename_migration` — moves a ledger row from `from` to `to`; a no-op when the
325
+ database already converged; a hard conflict when both keys exist.
326
+ - `mark_applied` — inserts a ledger row without running `up` SQL (you are
327
+ responsible for verifying the schema already matches).
328
+ - `unmark_applied` — deletes a ledger row without running `down` SQL, making
329
+ the migration pending again.
330
+
331
+ Parsing is strict: unknown fields/verbs, duplicate keys, YAML anchors/aliases,
332
+ merge keys, and custom tags are rejected. There is no raw SQL escape hatch.
333
+
334
+ ### Immutability
335
+
336
+ **Patch files are permanent, append-only migration history.** Once any database
337
+ records a patch, the file must never be edited, renamed, or deleted — Proper
338
+ stores a SHA-256 checksum at application time and refuses to run migrations if
339
+ an applied patch's file is missing or changed. A correction is a new, later
340
+ patch (`A -> B -> C` and `A -> B -> A` chains are supported and validated
341
+ against the current migration files).
342
+
343
+ ### Deployment and rollback warning
344
+
345
+ A history rename must ship the renamed migration files, the patch file(s), and
346
+ a patch-aware Proper version **in the same application artifact**. Do not run
347
+ old and new application versions' migration startup concurrently during the
348
+ first deployment of a rename. After a rename patch commits, rolling back to an
349
+ artifact that only contains the old migration filenames is unsafe — that old
350
+ runner can attempt the old migration again. Roll back only to artifacts
351
+ consistent with the repaired ledger, or restore the database separately.
352
+
353
+ `proper down` and `proper reset` never touch patch history; destructive
354
+ `terminate()` drops both the migration table and the patch table.
355
+
356
+ ### Programmatic API
357
+
358
+ ```ts
359
+ const runner = await MigrationRunnerFactory.create("proper.json"); // patches already applied
360
+ const results: PatchApplyResult[] = await runner.applyPendingPatches(); // idempotent
361
+ const path: string = runner.createPatch("fix_renamed_keys"); // scaffold, no DB
362
+ ```
363
+
364
+ ---
365
+
269
366
  ## Seeding
270
367
 
271
368
  SQL Proper supports a powerful seeding feature that allows you to populate your database with initial data (reference data, test users, etc.). Seeds can be SQL files or JavaScript/TypeScript modules.