tina4-nodejs 3.13.60 → 3.13.61

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/CLAUDE.md CHANGED
@@ -777,7 +777,7 @@ bindDatabase(await createAdapterFromUrl("postgres://localhost:5432/analytics"),
777
777
  // silently falling back to the default. (initDatabase / the internal setAdapter are unchanged.)
778
778
  ```
779
779
 
780
- **Soft delete:** set `static softDelete = true`. Adds an `is_deleted` INTEGER column (0/1). `delete()` flips the flag, `forceDelete()` removes the row, `restore()` clears it.
780
+ **Soft delete:** set `static softDelete = true`. Server boot (`syncModels()`) adds the `is_deleted` INTEGER column (0/1) — but **`Model.createTable()` does not**, so declare it there yourself. `delete()` flips the flag, `forceDelete()` removes the row, `restore()` clears it.
781
781
 
782
782
  ## Module: QueryBuilder (`packages/orm/src/queryBuilder.ts`)
783
783
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tina4-nodejs",
3
- "version": "3.13.60",
3
+ "version": "3.13.61",
4
4
  "type": "module",
5
5
  "description": "Tina4 for Node.js/TypeScript - 54 built-in features, zero dependencies",
6
6
  "keywords": [
@@ -753,7 +753,35 @@ export class BaseModel {
753
753
  typeof (db as any).getError === "function" ? (db as any).getError() :
754
754
  typeof (db as any).getLastError === "function" ? (db as any).getLastError() :
755
755
  null;
756
- this.lastError = adapterErr || e?.message || String(e);
756
+ let cause = adapterErr || e?.message || String(e);
757
+ // ── DX hint (v3.13.60 parity with the Python master): turn a bare driver
758
+ // error into an actionable fix for the two commonest ORM write footguns.
759
+ // Matched case-insensitively (SQLite via node:sqlite: "no such table" /
760
+ // "no such column: is_deleted" / "has no column named is_deleted";
761
+ // Postgres/MySQL: "does not exist" / "doesn't exist" / "unknown column").
762
+ // Any OTHER error keeps its raw cause untouched so an unrelated failure
763
+ // (NOT NULL, duplicate PK) is never masked. ──
764
+ const low = cause.toLowerCase();
765
+ if (
766
+ ModelClass.softDelete && low.includes("is_deleted") &&
767
+ (low.includes("no such column") || low.includes("has no column") ||
768
+ low.includes("does not exist") || low.includes("doesn't exist") ||
769
+ low.includes("unknown column"))
770
+ ) {
771
+ cause +=
772
+ " — softDelete=true needs an is_deleted column; declare it " +
773
+ "(is_deleted: { type: 'integer', default: 0 }), boot the server so " +
774
+ "syncModels() adds it, or run a migration";
775
+ } else if (
776
+ low.includes("no such table") ||
777
+ ((low.includes("does not exist") || low.includes("doesn't exist")) &&
778
+ !low.includes("column"))
779
+ ) {
780
+ cause +=
781
+ ` — table '${ModelClass.tableName}' does not exist; call ` +
782
+ `${ModelClass.name}.createTable() or run a migration`;
783
+ }
784
+ this.lastError = cause;
757
785
  Log.error(
758
786
  `${ModelClass.name}.save() failed for table ` +
759
787
  `'${ModelClass.tableName}': ${this.lastError}`,