litedbmodel 2.2.6 → 2.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.
Files changed (142) hide show
  1. package/README.md +146 -86
  2. package/dist/Column.d.ts +30 -6
  3. package/dist/Column.d.ts.map +1 -1
  4. package/dist/Column.js +30 -10
  5. package/dist/Column.js.map +1 -1
  6. package/dist/DBModel.d.ts +28 -16
  7. package/dist/DBModel.d.ts.map +1 -1
  8. package/dist/DBModel.js +60 -46
  9. package/dist/DBModel.js.map +1 -1
  10. package/dist/LazyRelation.d.ts +2 -2
  11. package/dist/LazyRelation.js +2 -2
  12. package/dist/TypeCast.d.ts +2 -2
  13. package/dist/TypeCast.d.ts.map +1 -1
  14. package/dist/TypeCast.js +7 -9
  15. package/dist/TypeCast.js.map +1 -1
  16. package/dist/decorators.d.ts +128 -60
  17. package/dist/decorators.d.ts.map +1 -1
  18. package/dist/decorators.js +393 -251
  19. package/dist/decorators.js.map +1 -1
  20. package/dist/drivers/MysqlHelper.d.ts +3 -3
  21. package/dist/drivers/MysqlHelper.d.ts.map +1 -1
  22. package/dist/drivers/MysqlHelper.js +3 -17
  23. package/dist/drivers/MysqlHelper.js.map +1 -1
  24. package/dist/drivers/MysqlSqlBuilder.d.ts.map +1 -1
  25. package/dist/drivers/MysqlSqlBuilder.js +2 -13
  26. package/dist/drivers/MysqlSqlBuilder.js.map +1 -1
  27. package/dist/drivers/PostgresHelper.d.ts +3 -2
  28. package/dist/drivers/PostgresHelper.d.ts.map +1 -1
  29. package/dist/drivers/PostgresHelper.js +3 -16
  30. package/dist/drivers/PostgresHelper.js.map +1 -1
  31. package/dist/drivers/PostgresSqlBuilder.d.ts.map +1 -1
  32. package/dist/drivers/PostgresSqlBuilder.js +2 -14
  33. package/dist/drivers/PostgresSqlBuilder.js.map +1 -1
  34. package/dist/drivers/SqliteHelper.d.ts +4 -2
  35. package/dist/drivers/SqliteHelper.d.ts.map +1 -1
  36. package/dist/drivers/SqliteHelper.js +4 -2
  37. package/dist/drivers/SqliteHelper.js.map +1 -1
  38. package/dist/drivers/SqliteSqlBuilder.d.ts.map +1 -1
  39. package/dist/drivers/SqliteSqlBuilder.js +2 -13
  40. package/dist/drivers/SqliteSqlBuilder.js.map +1 -1
  41. package/dist/index.cjs +861 -816
  42. package/dist/index.d.ts +2 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/scp/coltype.d.ts +14 -5
  46. package/dist/scp/coltype.d.ts.map +1 -1
  47. package/dist/scp/coltype.js +35 -36
  48. package/dist/scp/coltype.js.map +1 -1
  49. package/dist/scp/decorator-adapter.d.ts +12 -15
  50. package/dist/scp/decorator-adapter.d.ts.map +1 -1
  51. package/dist/scp/decorator-adapter.js +13 -15
  52. package/dist/scp/decorator-adapter.js.map +1 -1
  53. package/dist/scp/index.cjs +237 -233
  54. package/dist/scp/index.mjs +237 -233
  55. package/dist/scp/makesql/json-array.d.ts +6 -0
  56. package/dist/scp/makesql/json-array.d.ts.map +1 -1
  57. package/dist/scp/makesql/json-array.js +17 -2
  58. package/dist/scp/makesql/json-array.js.map +1 -1
  59. package/dist/scp/makesql/tx.d.ts.map +1 -1
  60. package/dist/scp/makesql/tx.js +1 -3
  61. package/dist/scp/makesql/tx.js.map +1 -1
  62. package/dist/scp/relation.d.ts.map +1 -1
  63. package/dist/scp/relation.js +8 -10
  64. package/dist/scp/relation.js.map +1 -1
  65. package/docs/BENCHMARK.md +3 -3
  66. package/docs/COMPARISON.md +7 -7
  67. package/docs/api/README.md +18 -18
  68. package/docs/api/classes/Conditions.md +10 -10
  69. package/docs/api/classes/DBHandler.md +1 -1
  70. package/docs/api/classes/DBModel.md +84 -84
  71. package/docs/api/classes/LimitExceededError.md +1 -1
  72. package/docs/api/classes/Middleware.md +1 -1
  73. package/docs/api/classes/SqlRaw.md +1 -1
  74. package/docs/api/classes/SqlRef.md +1 -1
  75. package/docs/api/classes/Values.md +6 -6
  76. package/docs/api/classes/WriteInReadOnlyContextError.md +1 -1
  77. package/docs/api/classes/WriteOutsideTransactionError.md +1 -1
  78. package/docs/api/functions/belongsTo.md +10 -4
  79. package/docs/api/functions/closeAllPools.md +1 -1
  80. package/docs/api/functions/condsToRecord.md +4 -3
  81. package/docs/api/functions/createMiddleware.md +1 -1
  82. package/docs/api/functions/createPostgresDriver.md +1 -1
  83. package/docs/api/functions/createSqliteDriver.md +1 -1
  84. package/docs/api/functions/dbCast.md +1 -1
  85. package/docs/api/functions/dbCastIn.md +1 -1
  86. package/docs/api/functions/dbDynamic.md +1 -1
  87. package/docs/api/functions/dbFalse.md +1 -1
  88. package/docs/api/functions/dbImmediate.md +1 -1
  89. package/docs/api/functions/dbIn.md +1 -1
  90. package/docs/api/functions/dbNotNull.md +1 -1
  91. package/docs/api/functions/dbNow.md +1 -1
  92. package/docs/api/functions/dbNull.md +1 -1
  93. package/docs/api/functions/dbRaw.md +1 -1
  94. package/docs/api/functions/dbTrue.md +1 -1
  95. package/docs/api/functions/dbUuid.md +1 -1
  96. package/docs/api/functions/dbUuidIn.md +1 -1
  97. package/docs/api/functions/formatLocalDate.md +2 -2
  98. package/docs/api/functions/formatUTCDate.md +2 -2
  99. package/docs/api/functions/getDBConfig.md +1 -1
  100. package/docs/api/functions/getDBHandler.md +1 -1
  101. package/docs/api/functions/getTransactionConnection.md +2 -2
  102. package/docs/api/functions/getTransactionContext.md +2 -2
  103. package/docs/api/functions/hasMany.md +11 -5
  104. package/docs/api/functions/hasOne.md +10 -4
  105. package/docs/api/functions/initDBHandler.md +1 -1
  106. package/docs/api/functions/isAnySqlFragment.md +1 -1
  107. package/docs/api/functions/isConnectionError.md +1 -1
  108. package/docs/api/functions/isSqlCondition.md +1 -1
  109. package/docs/api/functions/isSqlFragment.md +1 -1
  110. package/docs/api/functions/isSqlRaw.md +1 -1
  111. package/docs/api/functions/isSqlRef.md +1 -1
  112. package/docs/api/functions/isSqlTypedFragment.md +1 -1
  113. package/docs/api/functions/model.md +8 -155
  114. package/docs/api/functions/parentRef.md +1 -1
  115. package/docs/api/functions/sql.md +1 -1
  116. package/docs/api/globals.md +99 -58
  117. package/docs/api/interfaces/Column.md +32 -31
  118. package/docs/api/interfaces/ColumnDecorator.md +81 -0
  119. package/docs/api/interfaces/ColumnOptions.md +6 -10
  120. package/docs/api/interfaces/CreatedMiddlewareClass.md +1 -1
  121. package/docs/api/interfaces/DBConfig.md +1 -1
  122. package/docs/api/interfaces/DBConfigOptions.md +1 -1
  123. package/docs/api/interfaces/DeleteOptions.md +1 -1
  124. package/docs/api/interfaces/InsertOptions.md +1 -1
  125. package/docs/api/interfaces/LimitConfig.md +1 -1
  126. package/docs/api/interfaces/MiddlewareConfig.md +1 -1
  127. package/docs/api/interfaces/ModelClassDecorator.md +64 -0
  128. package/docs/api/interfaces/ModelOptions.md +1 -1
  129. package/docs/api/interfaces/PkeyResult.md +1 -1
  130. package/docs/api/interfaces/RelationDecorator.md +72 -0
  131. package/docs/api/interfaces/SelectOptions.md +1 -1
  132. package/docs/api/interfaces/SqlCondition.md +1 -1
  133. package/docs/api/interfaces/SqlFragment.md +1 -1
  134. package/docs/api/interfaces/SqlTypedFragment.md +1 -1
  135. package/docs/api/interfaces/TransactionOptions.md +1 -1
  136. package/docs/api/interfaces/UpdateManyOptions.md +1 -1
  137. package/docs/api/interfaces/UpdateOptions.md +1 -1
  138. package/docs/api-guide.md +17 -17
  139. package/docs/architecture.md +14 -10
  140. package/docs/sql-tagged-template.md +5 -5
  141. package/eslint-plugin/rules/require-declare-for-relations.js +59 -4
  142. package/package.json +8 -7
@@ -2054,12 +2054,14 @@ function tupleInPredicate(dialect, table, columns, pgElementTypes) {
2054
2054
  function encodeJsonArrayParam(dialect, values) {
2055
2055
  return encodeJsonParam(dialect, values);
2056
2056
  }
2057
+ var BIGINT_SENTINEL = "\0int:";
2057
2058
  function encodeJsonParam(dialect, payload) {
2058
- return JSON.stringify(payload, (_key, v) => {
2059
- if (typeof v === "bigint") return Number(v);
2059
+ const tagged = JSON.stringify(payload, (_key, v) => {
2060
+ if (typeof v === "bigint") return `${BIGINT_SENTINEL}${v.toString()}`;
2060
2061
  if (dialect === "mysql" && typeof v === "boolean") return v ? 1 : 0;
2061
2062
  return v;
2062
2063
  });
2064
+ return tagged.replace(/"\\u0000int:(-?\d+)"/g, "$1");
2063
2065
  }
2064
2066
  var JsonArrayConditions = class extends DBConditions {
2065
2067
  _dialect;
@@ -2106,6 +2108,11 @@ function andTrailing(head, tails) {
2106
2108
  return [head, ...guardedTails];
2107
2109
  }
2108
2110
 
2111
+ // src/drivers/PostgresHelper.ts
2112
+ function castToJson(val) {
2113
+ return val ?? null;
2114
+ }
2115
+
2109
2116
  // src/TypeCast.ts
2110
2117
  var defaultTypeCast = {
2111
2118
  castToDatetime(val) {
@@ -2230,17 +2237,15 @@ var defaultTypeCast = {
2230
2237
  }
2231
2238
  return [];
2232
2239
  },
2240
+ // A string is JSON text to decode; any other value is already a JSON value
2233
2241
  castToJson(val) {
2234
2242
  if (val === null || val === void 0) return null;
2235
- if (typeof val === "object") return val;
2236
- if (typeof val === "string") {
2237
- try {
2238
- return JSON.parse(val);
2239
- } catch {
2240
- return null;
2241
- }
2243
+ if (typeof val !== "string") return val;
2244
+ try {
2245
+ return JSON.parse(val);
2246
+ } catch {
2247
+ return null;
2242
2248
  }
2243
- return null;
2244
2249
  }
2245
2250
  };
2246
2251
  function parsePostgresArray(literal) {
@@ -2297,7 +2302,7 @@ function castToBooleanArray(val) {
2297
2302
  function castToDatetimeArray(val) {
2298
2303
  return currentTypeCast.castToDatetimeArray(val);
2299
2304
  }
2300
- function castToJson(val) {
2305
+ function castToJson2(val) {
2301
2306
  return currentTypeCast.castToJson(val);
2302
2307
  }
2303
2308
  function formatLocalDate(d) {
@@ -2341,16 +2346,7 @@ var postgresTypeCast = {
2341
2346
  return JSON.stringify(val);
2342
2347
  },
2343
2348
  deserializeJson(val) {
2344
- if (val === null || val === void 0) return null;
2345
- if (typeof val === "object") return val;
2346
- if (typeof val === "string") {
2347
- try {
2348
- return JSON.parse(val);
2349
- } catch {
2350
- return null;
2351
- }
2352
- }
2353
- return null;
2349
+ return castToJson(val);
2354
2350
  },
2355
2351
  serializeBooleanArray(val) {
2356
2352
  if (val === null || val === void 0) return null;
@@ -2654,6 +2650,11 @@ var postgresSqlBuilder = {
2654
2650
  inferPgType
2655
2651
  };
2656
2652
 
2653
+ // src/drivers/MysqlHelper.ts
2654
+ function castToJson3(val) {
2655
+ return val ?? null;
2656
+ }
2657
+
2657
2658
  // src/drivers/MysqlSqlBuilder.ts
2658
2659
  var mysqlTypeCast = {
2659
2660
  driverName: "mysql",
@@ -2679,16 +2680,7 @@ var mysqlTypeCast = {
2679
2680
  return JSON.stringify(val);
2680
2681
  },
2681
2682
  deserializeJson(val) {
2682
- if (val === null || val === void 0) return null;
2683
- if (typeof val === "object") return val;
2684
- if (typeof val === "string") {
2685
- try {
2686
- return JSON.parse(val);
2687
- } catch {
2688
- return null;
2689
- }
2690
- }
2691
- return null;
2683
+ return castToJson3(val);
2692
2684
  },
2693
2685
  serializeBooleanArray(val) {
2694
2686
  if (val === null || val === void 0) return null;
@@ -2856,6 +2848,24 @@ var mysqlSqlBuilder = {
2856
2848
  }
2857
2849
  };
2858
2850
 
2851
+ // src/drivers/SqliteHelper.ts
2852
+ function castToJson4(val) {
2853
+ if (val === null || val === void 0) {
2854
+ return null;
2855
+ }
2856
+ if (typeof val === "object" || typeof val === "number") {
2857
+ return val;
2858
+ }
2859
+ if (typeof val === "string") {
2860
+ try {
2861
+ return JSON.parse(val);
2862
+ } catch {
2863
+ return null;
2864
+ }
2865
+ }
2866
+ return null;
2867
+ }
2868
+
2859
2869
  // src/drivers/SqliteSqlBuilder.ts
2860
2870
  var sqliteTypeCast = {
2861
2871
  driverName: "sqlite",
@@ -2881,16 +2891,7 @@ var sqliteTypeCast = {
2881
2891
  return JSON.stringify(val);
2882
2892
  },
2883
2893
  deserializeJson(val) {
2884
- if (val === null || val === void 0) return null;
2885
- if (typeof val === "object") return val;
2886
- if (typeof val === "string") {
2887
- try {
2888
- return JSON.parse(val);
2889
- } catch {
2890
- return null;
2891
- }
2892
- }
2893
- return null;
2894
+ return castToJson4(val);
2894
2895
  },
2895
2896
  serializeBooleanArray(val) {
2896
2897
  if (val === null || val === void 0) return null;
@@ -3426,8 +3427,8 @@ function sqlTypeToMaterializeClass(sqlType) {
3426
3427
  return "passthrough";
3427
3428
  }
3428
3429
  switch (t) {
3429
- // 32-bit int family: JS number holds the full range exactly. The PostgreSQL SERIAL family maps to
3430
- // its underlying int width (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2).
3430
+ // Integer family. The PostgreSQL SERIAL types are the same integers under an auto-increment
3431
+ // default (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2, BIGSERIAL/SERIAL8=int8).
3431
3432
  case "INTEGER":
3432
3433
  case "INT":
3433
3434
  case "SMALLINT":
@@ -3439,13 +3440,11 @@ function sqlTypeToMaterializeClass(sqlType) {
3439
3440
  case "SERIAL4":
3440
3441
  case "SMALLSERIAL":
3441
3442
  case "SERIAL2":
3442
- return "int32";
3443
- // 64-bit int: needs JS bigint for exactness.
3444
3443
  case "BIGINT":
3445
3444
  case "INT8":
3446
3445
  case "BIGSERIAL":
3447
3446
  case "SERIAL8":
3448
- return "int64";
3447
+ return "int";
3449
3448
  case "BOOLEAN":
3450
3449
  case "BOOL":
3451
3450
  return "bool";
@@ -3482,44 +3481,45 @@ function sqlTypeToMaterializeClass(sqlType) {
3482
3481
  }
3483
3482
  function keyArrayElemScalar(sqlType) {
3484
3483
  switch (sqlTypeToMaterializeClass(sqlType)) {
3485
- case "int32":
3486
- return "float";
3487
- // materializes to a JS number → bc float
3488
- case "int64":
3484
+ case "int":
3485
+ return "int";
3486
+ // materializes to a JS bigint → bc int
3489
3487
  case "date":
3490
3488
  return "string";
3491
- // materializes to a decimal / TZ string → bc string
3489
+ // materializes to a TZ string → bc string
3492
3490
  case "bool":
3493
3491
  return "bool";
3494
3492
  case "passthrough":
3495
3493
  return sqlTypeToBcScalar(sqlType) === "float" ? "float" : "string";
3496
3494
  }
3497
3495
  }
3496
+ var I64_MIN = -(2n ** 63n);
3497
+ var I64_MAX = 2n ** 63n - 1n;
3498
+ function checkI64(v) {
3499
+ if (v < I64_MIN || v > I64_MAX) {
3500
+ throw new Error(`materialize int: ${v} is outside the i64 range behavior-contracts' \`int\` admits`);
3501
+ }
3502
+ return v;
3503
+ }
3498
3504
  function materializeCell(value, klass) {
3499
3505
  if (value === null || value === void 0) return value;
3500
3506
  switch (klass) {
3501
- case "int64": {
3502
- if (typeof value === "bigint") return value.toString();
3507
+ case "int": {
3508
+ if (typeof value === "bigint") return checkI64(value);
3503
3509
  if (typeof value === "string") {
3504
- if (!/^-?\d+$/.test(value)) throw new Error(`materialize int64: driver returned a non-integer string '${value}'`);
3505
- return value;
3510
+ if (!/^-?\d+$/.test(value)) throw new Error(`materialize int: driver returned a non-integer string '${value}'`);
3511
+ return checkI64(BigInt(value));
3506
3512
  }
3507
3513
  if (typeof value === "number") {
3508
- if (!Number.isInteger(value)) throw new Error(`materialize int64: driver returned a non-integer number ${value} for a 64-bit int column`);
3514
+ if (!Number.isInteger(value)) throw new Error(`materialize int: driver returned a non-integer number ${value} for an integer column`);
3509
3515
  if (!Number.isSafeInteger(value)) {
3510
3516
  throw new Error(
3511
- `materialize int64: driver returned an UNSAFE JS number ${value} for a 64-bit int column \u2014 precision was already lost before materialization (the driver must return int8 as string/bigint, not a rounded double). Configure the driver (better-sqlite3 safeIntegers / mysql2 supportBigNumbers+bigNumberStrings / pg int8-as-string).`
3517
+ `materialize int: driver returned an UNSAFE JS number ${value} for an integer column \u2014 precision was already lost before materialization (the driver must return integers as bigint/string, not a rounded double). Configure the driver (better-sqlite3 safeIntegers / mysql2 supportBigNumbers+bigNumberStrings / pg int-as-bigint).`
3512
3518
  );
3513
3519
  }
3514
- return value.toString();
3520
+ return BigInt(value);
3515
3521
  }
3516
- throw new Error(`materialize int64: unexpected driver JS type ${typeof value}`);
3517
- }
3518
- case "int32": {
3519
- if (typeof value === "number") return value;
3520
- if (typeof value === "bigint") return Number(value);
3521
- if (typeof value === "string" && /^-?\d+$/.test(value)) return Number(value);
3522
- throw new Error(`materialize int32: unexpected driver JS type ${typeof value} (${String(value)})`);
3522
+ throw new Error(`materialize int: unexpected driver JS type ${typeof value}`);
3523
3523
  }
3524
3524
  case "date": {
3525
3525
  if (typeof value === "string") return value;
@@ -5008,8 +5008,7 @@ function onConflictTail(dialect, ports, cols) {
5008
5008
  }
5009
5009
  function pgTypeSpecimen(sqlType) {
5010
5010
  const klass = sqlTypeToMaterializeClass(sqlType);
5011
- if (klass === "int32") return 0;
5012
- if (klass === "int64") return 0n;
5011
+ if (klass === "int") return 0n;
5013
5012
  if (klass === "bool") return false;
5014
5013
  if (klass === "date") return /* @__PURE__ */ new Date(0);
5015
5014
  const scalar = sqlTypeToBcScalar(sqlType);
@@ -5601,10 +5600,6 @@ function materializeChildRows(rows, cols) {
5601
5600
  row[key] = materializeCell(row[key], klass);
5602
5601
  continue;
5603
5602
  }
5604
- if (typeof row[key] === "bigint") {
5605
- const v = row[key];
5606
- row[key] = v >= BigInt(Number.MIN_SAFE_INTEGER) && v <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(v) : v.toString();
5607
- }
5608
5603
  }
5609
5604
  }
5610
5605
  return rows;
@@ -5744,133 +5739,116 @@ function compileDeleteManyBundle(name, options, dialectName = "sqlite") {
5744
5739
  var import_reflect_metadata = __toESM(require_Reflect());
5745
5740
  var COLUMNS_KEY = /* @__PURE__ */ Symbol("litedbmodel:columns");
5746
5741
  var RELATIONS_KEY = /* @__PURE__ */ Symbol("litedbmodel:relations");
5747
- function inferTypeCastFromDesignType(target, propertyKey) {
5748
- const designType = Reflect.getMetadata("design:type", target, propertyKey);
5749
- if (!designType) return void 0;
5750
- switch (designType) {
5751
- case Boolean:
5752
- return {
5753
- // v2 read contract: BOOLEAN → boolean, via the shared materializer.
5754
- typeCast: (v) => {
5755
- if (v === void 0) return void 0;
5756
- return materializeCell(v, "bool");
5757
- },
5758
- sqlCast: "boolean"
5759
- };
5760
- case Date:
5761
- return {
5762
- typeCast: (v) => {
5763
- if (v === void 0) return void 0;
5764
- return materializeCell(v, "date");
5765
- },
5766
- // Delegate to driver-specific serializeDatetime
5767
- serialize: (val, typeCast) => {
5768
- if (val === null || val === void 0) return null;
5769
- if (val instanceof Date) {
5770
- return typeCast?.serializeDatetime(val) ?? val;
5771
- }
5772
- return val;
5773
- },
5774
- sqlCast: "timestamp"
5775
- };
5776
- case Number:
5777
- return {
5778
- typeCast: (v) => {
5779
- if (v === void 0) return void 0;
5780
- if (v === null) return null;
5781
- const n = Number(v);
5782
- return isNaN(n) ? null : n;
5783
- }
5784
- };
5785
- case BigInt:
5786
- return {
5787
- // v2 read contract: BIGINT/INT8 → EXACT decimal STRING (no i64 rounding, JSON-safe), via the
5788
- // shared materializer. NOTE: the TS field type stays `bigint` in existing models for source
5789
- // compat, but the VALUE is now the exact string — the whole point of the #9 realignment.
5790
- typeCast: (v) => {
5791
- if (v === void 0) return void 0;
5792
- return materializeCell(v, "int64");
5793
- },
5794
- sqlCast: "bigint"
5795
- };
5796
- // Array, Object, String, and other types require explicit specification
5797
- default:
5798
- return void 0;
5799
- }
5800
- }
5801
- function inferBaseSqlTypeFromDesignType(target, propertyKey) {
5802
- const designType = Reflect.getMetadata("design:type", target, propertyKey);
5803
- if (!designType) return void 0;
5804
- switch (designType) {
5805
- case String:
5806
- return "TEXT";
5807
- case Number:
5808
- return "INTEGER";
5809
- case Boolean:
5810
- return "BOOLEAN";
5811
- case Date:
5812
- return "TIMESTAMP";
5813
- case BigInt:
5814
- return "BIGINT";
5815
- default:
5816
- return void 0;
5742
+ if (typeof Symbol === "function" && !Symbol.metadata) {
5743
+ Object.defineProperty(Symbol, "metadata", {
5744
+ value: /* @__PURE__ */ Symbol.for("Symbol.metadata"),
5745
+ writable: false,
5746
+ enumerable: false,
5747
+ configurable: true
5748
+ });
5749
+ }
5750
+ function isStandardContext(second) {
5751
+ return typeof second === "object" && second !== null && typeof second.kind === "string";
5752
+ }
5753
+ function classMetadataBag(context, propertyKey) {
5754
+ const bag = context.metadata;
5755
+ if (bag === void 0) {
5756
+ throw new Error(
5757
+ `litedbmodel: the standard-decorator context for '${propertyKey}' carries no \`context.metadata\`. litedbmodel defines \`Symbol.metadata\` on import, which every current emit needs to create the bag \u2014 import litedbmodel (or 'litedbmodel/decorators') BEFORE the module that defines the model.`
5758
+ );
5817
5759
  }
5760
+ return bag;
5818
5761
  }
5819
- function registerColumn(target, propertyKey, options) {
5820
- const constructor = target.constructor;
5821
- const columns = Reflect.getMetadata(COLUMNS_KEY, constructor) || /* @__PURE__ */ new Map();
5822
- let finalTypeCast = options.typeCast;
5823
- let finalSerialize = options.serialize;
5824
- let finalSqlCast = options.sqlCast;
5825
- if (!options.skipAutoInfer) {
5826
- const inferred = inferTypeCastFromDesignType(target, propertyKey);
5827
- if (inferred) {
5828
- if (!finalTypeCast) finalTypeCast = inferred.typeCast;
5829
- if (!finalSerialize) finalSerialize = inferred.serialize;
5830
- if (!finalSqlCast) finalSqlCast = inferred.sqlCast;
5831
- }
5762
+ function legacyColumnMap(constructor) {
5763
+ const own = Reflect.getOwnMetadata(COLUMNS_KEY, constructor);
5764
+ if (own !== void 0) return own;
5765
+ const inherited = Reflect.getMetadata(COLUMNS_KEY, constructor);
5766
+ const map = new Map(inherited);
5767
+ Reflect.defineMetadata(COLUMNS_KEY, map, constructor);
5768
+ return map;
5769
+ }
5770
+ function standardColumnMap(bag) {
5771
+ if (!Object.prototype.hasOwnProperty.call(bag, COLUMNS_KEY)) {
5772
+ bag[COLUMNS_KEY] = new Map(bag[COLUMNS_KEY]);
5832
5773
  }
5833
- const baseSqlType = inferBaseSqlTypeFromDesignType(target, propertyKey);
5774
+ return bag[COLUMNS_KEY];
5775
+ }
5776
+ function registerColumn(columns, propertyKey, options) {
5834
5777
  columns.set(propertyKey, {
5835
5778
  columnName: options.columnName,
5836
- typeCast: finalTypeCast,
5837
- serialize: finalSerialize,
5779
+ typeCast: options.typeCast,
5780
+ serialize: options.serialize,
5838
5781
  primaryKey: options.primaryKey,
5839
5782
  autoIncrement: options.autoIncrement,
5840
- sqlCast: finalSqlCast,
5841
- ...baseSqlType !== void 0 ? { baseSqlType } : {}
5783
+ sqlCast: options.sqlCast,
5784
+ ...options.baseSqlType !== void 0 ? { baseSqlType: options.baseSqlType } : {}
5842
5785
  });
5843
- Reflect.defineMetadata(COLUMNS_KEY, columns, constructor);
5844
5786
  }
5845
- function createColumnDecorator(typeCast, serialize, skipAutoInfer = false, sqlCast) {
5787
+ function parseColumnOptions(columnNameOrOptions, propKey) {
5788
+ if (typeof columnNameOrOptions === "string") return { columnName: columnNameOrOptions };
5789
+ if (columnNameOrOptions) {
5790
+ return {
5791
+ columnName: columnNameOrOptions.columnName || propKey,
5792
+ primaryKey: columnNameOrOptions.primaryKey,
5793
+ autoIncrement: columnNameOrOptions.autoIncrement
5794
+ };
5795
+ }
5796
+ return { columnName: propKey };
5797
+ }
5798
+ function rejectNonText(family, val) {
5799
+ if (val === null || val === void 0) return null;
5800
+ if (val instanceof Date) {
5801
+ throw new Error(
5802
+ `litedbmodel: @column.${family}() received a Date. A text column cannot serialize one \u2014 the driver would store an object literal. Use @column.datetime() / @column.date() for a date column, or format the value yourself.`
5803
+ );
5804
+ }
5805
+ if (typeof val === "object") {
5806
+ throw new Error(
5807
+ `litedbmodel: @column.${family}() received an object. Use @column.json<T>() for a JSON column, or @column.custom() with a serializer.`
5808
+ );
5809
+ }
5810
+ return val;
5811
+ }
5812
+ function materializeString(family, v) {
5813
+ if (v === void 0 || v === null) return v;
5814
+ if (typeof v === "string") return v;
5815
+ if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);
5816
+ throw new Error(
5817
+ `materialize ${family}: driver returned ${typeof v} for a ${family} column. Use @column.passthrough() for a BYTEA/BLOB column, or @column.json<T>() for JSON.`
5818
+ );
5819
+ }
5820
+ function createColumnDecorator(typeCast, serialize, sqlCast, baseSqlType) {
5846
5821
  return function(columnNameOrOptions) {
5847
- return function(target, propertyKey) {
5848
- const propKey = String(propertyKey);
5849
- const shouldSkipAutoInfer = skipAutoInfer || typeCast !== void 0;
5850
- let columnName;
5851
- let primaryKey;
5852
- let autoIncrement;
5853
- if (typeof columnNameOrOptions === "string") {
5854
- columnName = columnNameOrOptions;
5855
- } else if (columnNameOrOptions) {
5856
- columnName = columnNameOrOptions.columnName || propKey;
5857
- primaryKey = columnNameOrOptions.primaryKey;
5858
- autoIncrement = columnNameOrOptions.autoIncrement;
5859
- } else {
5860
- columnName = propKey;
5822
+ return function(targetOrValue, keyOrContext) {
5823
+ if (isStandardContext(keyOrContext)) {
5824
+ const propKey2 = String(keyOrContext.name);
5825
+ const parsed2 = parseColumnOptions(columnNameOrOptions, propKey2);
5826
+ registerColumn(standardColumnMap(classMetadataBag(keyOrContext, propKey2)), propKey2, {
5827
+ ...parsed2,
5828
+ typeCast,
5829
+ serialize,
5830
+ sqlCast,
5831
+ baseSqlType
5832
+ });
5833
+ return;
5861
5834
  }
5862
- registerColumn(target, propKey, {
5863
- columnName,
5835
+ const propKey = String(keyOrContext);
5836
+ const parsed = parseColumnOptions(columnNameOrOptions, propKey);
5837
+ registerColumn(legacyColumnMap(targetOrValue.constructor), propKey, {
5838
+ ...parsed,
5864
5839
  typeCast,
5865
5840
  serialize,
5866
- skipAutoInfer: shouldSkipAutoInfer,
5867
- primaryKey,
5868
- autoIncrement,
5869
- sqlCast
5841
+ sqlCast,
5842
+ baseSqlType
5870
5843
  });
5871
5844
  };
5872
5845
  };
5873
5846
  }
5847
+ function requireColumnFamily() {
5848
+ throw new Error(
5849
+ "litedbmodel: `@column()` no longer declares a column \u2014 a column must state its type with a family: @column.text() / .decimal() / .number() / .boolean() / .bigint() / .datetime() / .date() / .uuid() / .json() / .stringArray() / .intArray() / .numericArray() / .booleanArray() / .datetimeArray() / .passthrough() / .custom(). Every family takes the same argument as `@column()` did (`@column.text('db_name')`, `@column.number({ primaryKey: true })`). The bare form inferred the type from `emitDecoratorMetadata`, which esbuild (tsx/vite/vitest) never emits and standard decorators do not have, so it silently produced untyped reads."
5850
+ );
5851
+ }
5874
5852
  function serializeArray(val, typeCast) {
5875
5853
  if (val === null || val === void 0) return null;
5876
5854
  if (!Array.isArray(val)) return val;
@@ -5908,19 +5886,47 @@ function serializeJson(val, typeCast) {
5908
5886
  return JSON.stringify(val);
5909
5887
  }
5910
5888
  var column = Object.assign(
5911
- // Basic @column() - no type conversion
5912
- createColumnDecorator(),
5889
+ // The bare `@column()` declared no type — it is rejected, with the migration in the message.
5890
+ (_columnNameOrOptions) => requireColumnFamily(),
5913
5891
  {
5914
5892
  // ============================================
5915
5893
  // Primitive Types
5916
5894
  // ============================================
5895
+ /**
5896
+ * Text type (TEXT / VARCHAR / CHAR / ENUM / citext) — the driver value is already a string, so no
5897
+ * cast is applied. The family still DECLARES the column so the typed read types it as `TEXT`.
5898
+ * @example @column.text() name?: string;
5899
+ * @example @column.text('user_name') name?: string;
5900
+ */
5901
+ text: (columnNameOrOptions) => createColumnDecorator(
5902
+ (v) => materializeString("text", v),
5903
+ // the family DECLARES `string`, so it hands back one
5904
+ (val) => rejectNonText("text", val),
5905
+ // …and it cannot SERIALIZE a Date or an object
5906
+ void 0,
5907
+ // no sqlCast family (a text literal needs no SQL cast)
5908
+ "TEXT"
5909
+ // §4.1 token: types the SCP typed read
5910
+ )(columnNameOrOptions),
5911
+ /**
5912
+ * Exact decimal type (NUMERIC / DECIMAL) — read back as its exact decimal STRING (a JS number
5913
+ * rounds past 2^53). The family states the column is `numeric`, so a write binds the string as a
5914
+ * numeric — a batch write (`createMany` / `updateMany`) included.
5915
+ * @example @column.decimal() price?: string;
5916
+ */
5917
+ decimal: (columnNameOrOptions) => createColumnDecorator(
5918
+ (v) => materializeString("decimal", v),
5919
+ (val) => rejectNonText("decimal", val),
5920
+ "numeric"
5921
+ // sqlCast for WHERE / INSERT / batch-write type casting
5922
+ )(columnNameOrOptions),
5917
5923
  /**
5918
5924
  * Boolean type conversion
5919
5925
  * Converts 't'/'f', 'true'/'false', 1/0 to boolean
5920
5926
  * Preserves null for nullable columns, undefined stays undefined
5921
5927
  * @example @column.boolean() is_active?: boolean;
5922
5928
  */
5923
- boolean: (columnName) => createColumnDecorator(
5929
+ boolean: (columnNameOrOptions) => createColumnDecorator(
5924
5930
  // v2 read contract: BOOLEAN → boolean, via the shared materializer (issue #9).
5925
5931
  (v) => {
5926
5932
  if (v === void 0) return void 0;
@@ -5928,42 +5934,38 @@ var column = Object.assign(
5928
5934
  },
5929
5935
  void 0,
5930
5936
  // no custom serialize
5931
- false,
5932
- // allow auto-inference
5933
5937
  "boolean"
5934
5938
  // sqlCast for updateMany type inference
5935
- )(columnName),
5939
+ )(columnNameOrOptions),
5936
5940
  /**
5937
5941
  * Number type conversion (from string)
5938
5942
  * Preserves null for nullable columns, undefined stays undefined
5939
5943
  * @example @column.number() amount?: number;
5940
5944
  */
5941
- number: (columnName) => createColumnDecorator((v) => {
5945
+ number: (columnNameOrOptions) => createColumnDecorator((v) => {
5942
5946
  if (v === void 0) return void 0;
5943
5947
  if (v === null) return null;
5944
5948
  const n = Number(v);
5945
5949
  return isNaN(n) ? null : n;
5946
- })(columnName),
5950
+ })(columnNameOrOptions),
5947
5951
  /**
5948
5952
  * BigInt type conversion
5949
5953
  * Preserves null for nullable columns, undefined stays undefined
5950
- * @example @column.bigint() large_id?: bigint;
5954
+ * @example @column.bigint() large_id?: bigint; // bc `int` — a JS bigint, checked i64
5951
5955
  */
5952
- bigint: (columnName) => createColumnDecorator(
5953
- // v2 read contract: BIGINT/INT8 → EXACT decimal STRING (no i64 rounding, JSON-safe), via the
5954
- // shared materializer (issue #9). The declared TS field type may still be `bigint` in existing
5955
- // models, but the runtime value is now the exact string — that IS the realignment.
5956
+ bigint: (columnNameOrOptions) => createColumnDecorator(
5957
+ // An integer column reads back as a JS `bigint` — behavior-contracts' `int` value model
5958
+ // (`int` = bigint, checked i64), which is what the drivers already hand over. `JSON.stringify`
5959
+ // does not serialize a bigint: give the field a `toJSON`, or convert at the boundary.
5956
5960
  (v) => {
5957
5961
  if (v === void 0) return void 0;
5958
- return materializeCell(v, "int64");
5962
+ return materializeCell(v, "int");
5959
5963
  },
5960
5964
  void 0,
5961
5965
  // no custom serialize
5962
- false,
5963
- // allow auto-inference
5964
5966
  "bigint"
5965
5967
  // sqlCast for WHERE/INSERT type casting
5966
- )(columnName),
5968
+ )(columnNameOrOptions),
5967
5969
  // ============================================
5968
5970
  // Date/Time Types
5969
5971
  // ============================================
@@ -5975,9 +5977,9 @@ var column = Object.assign(
5975
5977
  * - PostgreSQL: Serializes to ISO 8601 UTC string with 'Z' suffix for explicit timezone
5976
5978
  * - MySQL/SQLite: Passes Date object to driver (driver-dependent timezone handling)
5977
5979
  *
5978
- * @example @column.datetime() created_at?: Date;
5980
+ * @example @column.datetime() created_at?: string; // TZ-attached string, NOT a JS Date
5979
5981
  */
5980
- datetime: (columnName) => createColumnDecorator(
5982
+ datetime: (columnNameOrOptions) => createColumnDecorator(
5981
5983
  // v2 read contract: DATE/TIMESTAMP/TIMESTAMPTZ/DATETIME/TIME → TZ-attached STRING (NOT a
5982
5984
  // TZ-shifted JS Date), via the shared materializer (issue #9). A driver already returning the
5983
5985
  // native textual form passes through; a JS Date is rendered to its lossless ISO instant.
@@ -5993,11 +5995,9 @@ var column = Object.assign(
5993
5995
  }
5994
5996
  return val;
5995
5997
  },
5996
- false,
5997
- // allow auto-inference
5998
5998
  "timestamp"
5999
5999
  // sqlCast for updateMany type inference
6000
- )(columnName),
6000
+ )(columnNameOrOptions),
6001
6001
  /**
6002
6002
  * Date type conversion — returns YYYY-MM-DD string.
6003
6003
  * Preserves null for nullable columns, undefined stays undefined.
@@ -6005,9 +6005,9 @@ var column = Object.assign(
6005
6005
  * DB values (Date object or string) are normalized to 'YYYY-MM-DD' string.
6006
6006
  * On write, string values are passed through; Date objects are formatted as 'YYYY-MM-DD'.
6007
6007
  *
6008
- * @example @column.date() birth_date?: string;
6008
+ * @example @column.date() birth_date?: string; // 'YYYY-MM-DD'
6009
6009
  */
6010
- date: (columnName) => createColumnDecorator(
6010
+ date: (columnNameOrOptions) => createColumnDecorator(
6011
6011
  (v) => {
6012
6012
  if (v === void 0) return void 0;
6013
6013
  if (v === null) return null;
@@ -6032,11 +6032,9 @@ var column = Object.assign(
6032
6032
  }
6033
6033
  return val;
6034
6034
  },
6035
- false,
6036
- // allow auto-inference
6037
6035
  "date"
6038
6036
  // sqlCast for updateMany type inference
6039
- )(columnName),
6037
+ )(columnNameOrOptions),
6040
6038
  // ============================================
6041
6039
  // Array Types
6042
6040
  // ============================================
@@ -6045,71 +6043,71 @@ var column = Object.assign(
6045
6043
  * Preserves null for nullable columns, undefined stays undefined
6046
6044
  * @example @column.stringArray() tags?: string[];
6047
6045
  */
6048
- stringArray: (columnName) => createColumnDecorator(
6046
+ stringArray: (columnNameOrOptions) => createColumnDecorator(
6049
6047
  (v) => {
6050
6048
  if (v === void 0) return void 0;
6051
6049
  if (v === null) return null;
6052
6050
  return castToStringArray(v);
6053
6051
  },
6054
6052
  serializeArray,
6055
- false,
6056
6053
  "text[]"
6057
- )(columnName),
6054
+ )(columnNameOrOptions),
6058
6055
  /**
6059
6056
  * Integer array type conversion (integer[])
6060
6057
  * Preserves null for nullable columns, undefined stays undefined
6061
6058
  * @example @column.intArray() scores?: number[];
6062
6059
  */
6063
- intArray: (columnName) => createColumnDecorator(
6060
+ intArray: (columnNameOrOptions) => createColumnDecorator(
6064
6061
  (v) => {
6065
6062
  if (v === void 0) return void 0;
6066
6063
  if (v === null) return null;
6067
6064
  return castToIntegerArray(v);
6068
6065
  },
6069
6066
  serializeArray,
6070
- false,
6071
6067
  "int[]"
6072
- )(columnName),
6068
+ )(columnNameOrOptions),
6073
6069
  /**
6074
6070
  * Numeric array type conversion (numeric[], allows null elements)
6075
6071
  * Preserves null for nullable columns, undefined stays undefined
6076
6072
  * @example @column.numericArray() values?: (number | null)[];
6077
6073
  */
6078
- numericArray: (columnName) => createColumnDecorator(
6074
+ numericArray: (columnNameOrOptions) => createColumnDecorator(
6079
6075
  (v) => {
6080
6076
  if (v === void 0) return void 0;
6081
6077
  if (v === null) return null;
6082
6078
  return castToNumericArray(v);
6083
6079
  },
6084
6080
  serializeArray,
6085
- false,
6086
6081
  "numeric[]"
6087
- )(columnName),
6082
+ )(columnNameOrOptions),
6088
6083
  /**
6089
6084
  * Boolean array type conversion (boolean[])
6090
6085
  * Preserves null for nullable columns, undefined stays undefined
6091
6086
  * @example @column.booleanArray() flags?: (boolean | null)[];
6092
6087
  */
6093
- booleanArray: (columnName) => createColumnDecorator(
6088
+ booleanArray: (columnNameOrOptions) => createColumnDecorator(
6094
6089
  (v) => {
6095
6090
  if (v === void 0) return void 0;
6096
6091
  if (v === null) return null;
6097
6092
  return castToBooleanArray(v);
6098
6093
  },
6099
6094
  serializeBooleanArray,
6100
- false,
6101
6095
  "boolean[]"
6102
- )(columnName),
6096
+ )(columnNameOrOptions),
6103
6097
  /**
6104
6098
  * DateTime array type conversion (timestamp[])
6105
6099
  * Preserves null for nullable columns, undefined stays undefined
6106
- * @example @column.datetimeArray() event_dates?: (Date | null)[];
6100
+ * @example @column.datetimeArray() event_dates?: (string | null)[];
6107
6101
  */
6108
- datetimeArray: (columnName) => createColumnDecorator(
6102
+ datetimeArray: (columnNameOrOptions) => createColumnDecorator(
6103
+ // Element-wise the SAME contract as `@column.datetime()`: a TZ-attached STRING, never a
6104
+ // TZ-shifted JS Date. The array family used to be the one place a `Date` still came back, so
6105
+ // `JSON.stringify` of a row silently re-rendered those elements in UTC — the exact hazard #9
6106
+ // closed for the scalar.
6109
6107
  (v) => {
6110
6108
  if (v === void 0) return void 0;
6111
6109
  if (v === null) return null;
6112
- return castToDatetimeArray(v);
6110
+ return castToDatetimeArray(v).map((d) => d === null ? null : materializeCell(d, "date"));
6113
6111
  },
6114
6112
  // DateTime arrays serialize each Date to ISO string
6115
6113
  (val) => {
@@ -6118,7 +6116,7 @@ var column = Object.assign(
6118
6116
  const mapped = val.map((v) => v === null ? "NULL" : v instanceof Date ? v.toISOString() : String(v));
6119
6117
  return `{${mapped.join(",")}}`;
6120
6118
  }
6121
- )(columnName),
6119
+ )(columnNameOrOptions),
6122
6120
  // ============================================
6123
6121
  // JSON Types
6124
6122
  // ============================================
@@ -6128,16 +6126,15 @@ var column = Object.assign(
6128
6126
  * @example @column.json() metadata?: Record<string, unknown>;
6129
6127
  * @example @column.json<UserSettings>() settings?: UserSettings;
6130
6128
  */
6131
- json: (columnName) => createColumnDecorator(
6129
+ json: (columnNameOrOptions) => createColumnDecorator(
6132
6130
  (v) => {
6133
6131
  if (v === void 0) return void 0;
6134
6132
  if (v === null) return null;
6135
- return castToJson(v);
6133
+ return castToJson2(v);
6136
6134
  },
6137
6135
  serializeJson,
6138
- false,
6139
6136
  "jsonb"
6140
- )(columnName),
6137
+ )(columnNameOrOptions),
6141
6138
  // ============================================
6142
6139
  // UUID Type (PostgreSQL)
6143
6140
  // ============================================
@@ -6166,22 +6163,28 @@ var column = Object.assign(
6166
6163
  if (v === null) return null;
6167
6164
  return String(v);
6168
6165
  },
6169
- void 0,
6170
- // No serialization needed - handled by sqlCast
6171
- true,
6172
- // Skip auto-inference
6166
+ (val) => rejectNonText("uuid", val),
6167
+ // the ::uuid cast handles the rest
6173
6168
  "uuid"
6174
6169
  // SQL type for casting
6175
6170
  )(columnNameOrOptions),
6176
6171
  // ============================================
6177
6172
  // Custom Type Conversion
6178
6173
  // ============================================
6174
+ /**
6175
+ * The driver's value, UNCAST — for a column whose type the library has no cast for (`BYTEA` /
6176
+ * `BLOB` arrives as a driver Buffer, a vendor type as whatever the driver decodes). It states
6177
+ * "this column is passed through", which is a DECLARATION; the removed bare `@column()` was the
6178
+ * absence of one, and produced this same passthrough by accident for every unresolvable type.
6179
+ * @example @column.passthrough() payload?: unknown;
6180
+ */
6181
+ passthrough: (columnNameOrOptions) => createColumnDecorator()(columnNameOrOptions),
6179
6182
  /**
6180
6183
  * Custom type conversion with user-provided function
6181
6184
  * @example @column.custom((v) => String(v).toUpperCase()) status?: string;
6182
6185
  * @example @column.custom((v) => v, (v) => JSON.stringify(v)) data?: MyType; // with serializer
6183
6186
  */
6184
- custom: (castFn, serializeFn, columnName) => createColumnDecorator(castFn, serializeFn)(columnName)
6187
+ custom: (castFn, serializeFn, columnNameOrOptions) => createColumnDecorator(castFn, serializeFn)(columnNameOrOptions)
6185
6188
  }
6186
6189
  );
6187
6190
  function getRelationMeta(modelClass) {
@@ -6225,6 +6228,7 @@ function getPrimaryKey(modelClass) {
6225
6228
  // src/scp/decorator-adapter.ts
6226
6229
  var COLUMN_FAMILY_SQL_TYPE = {
6227
6230
  boolean: "BOOLEAN",
6231
+ numeric: "NUMERIC",
6228
6232
  bigint: "BIGINT",
6229
6233
  timestamp: "TIMESTAMP",
6230
6234
  date: "DATE",