litedbmodel 2.2.6 → 2.2.7

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 (116) hide show
  1. package/README.md +145 -86
  2. package/dist/Column.d.ts +17 -4
  3. package/dist/Column.d.ts.map +1 -1
  4. package/dist/Column.js.map +1 -1
  5. package/dist/DBModel.d.ts +13 -14
  6. package/dist/DBModel.d.ts.map +1 -1
  7. package/dist/DBModel.js +20 -21
  8. package/dist/DBModel.js.map +1 -1
  9. package/dist/LazyRelation.d.ts +2 -2
  10. package/dist/LazyRelation.js +2 -2
  11. package/dist/decorators.d.ts +120 -60
  12. package/dist/decorators.d.ts.map +1 -1
  13. package/dist/decorators.js +379 -248
  14. package/dist/decorators.js.map +1 -1
  15. package/dist/index.cjs +283 -217
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/scp/coltype.d.ts +14 -5
  20. package/dist/scp/coltype.d.ts.map +1 -1
  21. package/dist/scp/coltype.js +35 -36
  22. package/dist/scp/coltype.js.map +1 -1
  23. package/dist/scp/decorator-adapter.d.ts +11 -15
  24. package/dist/scp/decorator-adapter.d.ts.map +1 -1
  25. package/dist/scp/decorator-adapter.js +11 -15
  26. package/dist/scp/decorator-adapter.js.map +1 -1
  27. package/dist/scp/index.cjs +186 -193
  28. package/dist/scp/index.mjs +186 -193
  29. package/dist/scp/makesql/json-array.d.ts +6 -0
  30. package/dist/scp/makesql/json-array.d.ts.map +1 -1
  31. package/dist/scp/makesql/json-array.js +17 -2
  32. package/dist/scp/makesql/json-array.js.map +1 -1
  33. package/dist/scp/makesql/tx.d.ts.map +1 -1
  34. package/dist/scp/makesql/tx.js +1 -3
  35. package/dist/scp/makesql/tx.js.map +1 -1
  36. package/dist/scp/relation.d.ts.map +1 -1
  37. package/dist/scp/relation.js +8 -10
  38. package/dist/scp/relation.js.map +1 -1
  39. package/docs/BENCHMARK.md +3 -3
  40. package/docs/COMPARISON.md +7 -7
  41. package/docs/api/README.md +18 -18
  42. package/docs/api/classes/Conditions.md +1 -1
  43. package/docs/api/classes/DBHandler.md +1 -1
  44. package/docs/api/classes/DBModel.md +82 -82
  45. package/docs/api/classes/LimitExceededError.md +1 -1
  46. package/docs/api/classes/Middleware.md +1 -1
  47. package/docs/api/classes/SqlRaw.md +1 -1
  48. package/docs/api/classes/SqlRef.md +1 -1
  49. package/docs/api/classes/Values.md +1 -1
  50. package/docs/api/classes/WriteInReadOnlyContextError.md +1 -1
  51. package/docs/api/classes/WriteOutsideTransactionError.md +1 -1
  52. package/docs/api/functions/belongsTo.md +10 -4
  53. package/docs/api/functions/closeAllPools.md +1 -1
  54. package/docs/api/functions/condsToRecord.md +2 -2
  55. package/docs/api/functions/createMiddleware.md +1 -1
  56. package/docs/api/functions/createPostgresDriver.md +1 -1
  57. package/docs/api/functions/createSqliteDriver.md +1 -1
  58. package/docs/api/functions/dbCast.md +1 -1
  59. package/docs/api/functions/dbCastIn.md +1 -1
  60. package/docs/api/functions/dbDynamic.md +1 -1
  61. package/docs/api/functions/dbFalse.md +1 -1
  62. package/docs/api/functions/dbImmediate.md +1 -1
  63. package/docs/api/functions/dbIn.md +1 -1
  64. package/docs/api/functions/dbNotNull.md +1 -1
  65. package/docs/api/functions/dbNow.md +1 -1
  66. package/docs/api/functions/dbNull.md +1 -1
  67. package/docs/api/functions/dbRaw.md +1 -1
  68. package/docs/api/functions/dbTrue.md +1 -1
  69. package/docs/api/functions/dbUuid.md +1 -1
  70. package/docs/api/functions/dbUuidIn.md +1 -1
  71. package/docs/api/functions/formatLocalDate.md +1 -1
  72. package/docs/api/functions/formatUTCDate.md +1 -1
  73. package/docs/api/functions/getDBConfig.md +1 -1
  74. package/docs/api/functions/getDBHandler.md +1 -1
  75. package/docs/api/functions/getTransactionConnection.md +1 -1
  76. package/docs/api/functions/getTransactionContext.md +1 -1
  77. package/docs/api/functions/hasMany.md +11 -5
  78. package/docs/api/functions/hasOne.md +10 -4
  79. package/docs/api/functions/initDBHandler.md +1 -1
  80. package/docs/api/functions/isAnySqlFragment.md +1 -1
  81. package/docs/api/functions/isConnectionError.md +1 -1
  82. package/docs/api/functions/isSqlCondition.md +1 -1
  83. package/docs/api/functions/isSqlFragment.md +1 -1
  84. package/docs/api/functions/isSqlRaw.md +1 -1
  85. package/docs/api/functions/isSqlRef.md +1 -1
  86. package/docs/api/functions/isSqlTypedFragment.md +1 -1
  87. package/docs/api/functions/model.md +8 -155
  88. package/docs/api/functions/parentRef.md +1 -1
  89. package/docs/api/functions/sql.md +1 -1
  90. package/docs/api/globals.md +96 -58
  91. package/docs/api/interfaces/Column.md +1 -1
  92. package/docs/api/interfaces/ColumnDecorator.md +81 -0
  93. package/docs/api/interfaces/ColumnOptions.md +6 -10
  94. package/docs/api/interfaces/CreatedMiddlewareClass.md +1 -1
  95. package/docs/api/interfaces/DBConfig.md +1 -1
  96. package/docs/api/interfaces/DBConfigOptions.md +1 -1
  97. package/docs/api/interfaces/DeleteOptions.md +1 -1
  98. package/docs/api/interfaces/InsertOptions.md +1 -1
  99. package/docs/api/interfaces/LimitConfig.md +1 -1
  100. package/docs/api/interfaces/MiddlewareConfig.md +1 -1
  101. package/docs/api/interfaces/ModelClassDecorator.md +64 -0
  102. package/docs/api/interfaces/ModelOptions.md +1 -1
  103. package/docs/api/interfaces/PkeyResult.md +1 -1
  104. package/docs/api/interfaces/RelationDecorator.md +72 -0
  105. package/docs/api/interfaces/SelectOptions.md +1 -1
  106. package/docs/api/interfaces/SqlCondition.md +1 -1
  107. package/docs/api/interfaces/SqlFragment.md +1 -1
  108. package/docs/api/interfaces/SqlTypedFragment.md +1 -1
  109. package/docs/api/interfaces/TransactionOptions.md +1 -1
  110. package/docs/api/interfaces/UpdateManyOptions.md +1 -1
  111. package/docs/api/interfaces/UpdateOptions.md +1 -1
  112. package/docs/api-guide.md +17 -17
  113. package/docs/architecture.md +3 -3
  114. package/docs/sql-tagged-template.md +5 -5
  115. package/eslint-plugin/rules/require-declare-for-relations.js +59 -4
  116. 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;
@@ -3426,8 +3428,8 @@ function sqlTypeToMaterializeClass(sqlType) {
3426
3428
  return "passthrough";
3427
3429
  }
3428
3430
  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).
3431
+ // Integer family. The PostgreSQL SERIAL types are the same integers under an auto-increment
3432
+ // default (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2, BIGSERIAL/SERIAL8=int8).
3431
3433
  case "INTEGER":
3432
3434
  case "INT":
3433
3435
  case "SMALLINT":
@@ -3439,13 +3441,11 @@ function sqlTypeToMaterializeClass(sqlType) {
3439
3441
  case "SERIAL4":
3440
3442
  case "SMALLSERIAL":
3441
3443
  case "SERIAL2":
3442
- return "int32";
3443
- // 64-bit int: needs JS bigint for exactness.
3444
3444
  case "BIGINT":
3445
3445
  case "INT8":
3446
3446
  case "BIGSERIAL":
3447
3447
  case "SERIAL8":
3448
- return "int64";
3448
+ return "int";
3449
3449
  case "BOOLEAN":
3450
3450
  case "BOOL":
3451
3451
  return "bool";
@@ -3482,44 +3482,45 @@ function sqlTypeToMaterializeClass(sqlType) {
3482
3482
  }
3483
3483
  function keyArrayElemScalar(sqlType) {
3484
3484
  switch (sqlTypeToMaterializeClass(sqlType)) {
3485
- case "int32":
3486
- return "float";
3487
- // materializes to a JS number → bc float
3488
- case "int64":
3485
+ case "int":
3486
+ return "int";
3487
+ // materializes to a JS bigint → bc int
3489
3488
  case "date":
3490
3489
  return "string";
3491
- // materializes to a decimal / TZ string → bc string
3490
+ // materializes to a TZ string → bc string
3492
3491
  case "bool":
3493
3492
  return "bool";
3494
3493
  case "passthrough":
3495
3494
  return sqlTypeToBcScalar(sqlType) === "float" ? "float" : "string";
3496
3495
  }
3497
3496
  }
3497
+ var I64_MIN = -(2n ** 63n);
3498
+ var I64_MAX = 2n ** 63n - 1n;
3499
+ function checkI64(v) {
3500
+ if (v < I64_MIN || v > I64_MAX) {
3501
+ throw new Error(`materialize int: ${v} is outside the i64 range behavior-contracts' \`int\` admits`);
3502
+ }
3503
+ return v;
3504
+ }
3498
3505
  function materializeCell(value, klass) {
3499
3506
  if (value === null || value === void 0) return value;
3500
3507
  switch (klass) {
3501
- case "int64": {
3502
- if (typeof value === "bigint") return value.toString();
3508
+ case "int": {
3509
+ if (typeof value === "bigint") return checkI64(value);
3503
3510
  if (typeof value === "string") {
3504
- if (!/^-?\d+$/.test(value)) throw new Error(`materialize int64: driver returned a non-integer string '${value}'`);
3505
- return value;
3511
+ if (!/^-?\d+$/.test(value)) throw new Error(`materialize int: driver returned a non-integer string '${value}'`);
3512
+ return checkI64(BigInt(value));
3506
3513
  }
3507
3514
  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`);
3515
+ if (!Number.isInteger(value)) throw new Error(`materialize int: driver returned a non-integer number ${value} for an integer column`);
3509
3516
  if (!Number.isSafeInteger(value)) {
3510
3517
  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).`
3518
+ `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
3519
  );
3513
3520
  }
3514
- return value.toString();
3521
+ return BigInt(value);
3515
3522
  }
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)})`);
3523
+ throw new Error(`materialize int: unexpected driver JS type ${typeof value}`);
3523
3524
  }
3524
3525
  case "date": {
3525
3526
  if (typeof value === "string") return value;
@@ -5008,8 +5009,7 @@ function onConflictTail(dialect, ports, cols) {
5008
5009
  }
5009
5010
  function pgTypeSpecimen(sqlType) {
5010
5011
  const klass = sqlTypeToMaterializeClass(sqlType);
5011
- if (klass === "int32") return 0;
5012
- if (klass === "int64") return 0n;
5012
+ if (klass === "int") return 0n;
5013
5013
  if (klass === "bool") return false;
5014
5014
  if (klass === "date") return /* @__PURE__ */ new Date(0);
5015
5015
  const scalar = sqlTypeToBcScalar(sqlType);
@@ -5601,10 +5601,6 @@ function materializeChildRows(rows, cols) {
5601
5601
  row[key] = materializeCell(row[key], klass);
5602
5602
  continue;
5603
5603
  }
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
5604
  }
5609
5605
  }
5610
5606
  return rows;
@@ -5744,133 +5740,108 @@ function compileDeleteManyBundle(name, options, dialectName = "sqlite") {
5744
5740
  var import_reflect_metadata = __toESM(require_Reflect());
5745
5741
  var COLUMNS_KEY = /* @__PURE__ */ Symbol("litedbmodel:columns");
5746
5742
  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;
5743
+ if (typeof Symbol === "function" && !Symbol.metadata) {
5744
+ Object.defineProperty(Symbol, "metadata", {
5745
+ value: /* @__PURE__ */ Symbol.for("Symbol.metadata"),
5746
+ writable: false,
5747
+ enumerable: false,
5748
+ configurable: true
5749
+ });
5750
+ }
5751
+ function isStandardContext(second) {
5752
+ return typeof second === "object" && second !== null && typeof second.kind === "string";
5753
+ }
5754
+ function classMetadataBag(context, propertyKey) {
5755
+ const bag = context.metadata;
5756
+ if (bag === void 0) {
5757
+ throw new Error(
5758
+ `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.`
5759
+ );
5817
5760
  }
5761
+ return bag;
5818
5762
  }
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
- }
5763
+ function legacyColumnMap(constructor) {
5764
+ const own = Reflect.getOwnMetadata(COLUMNS_KEY, constructor);
5765
+ if (own !== void 0) return own;
5766
+ const inherited = Reflect.getMetadata(COLUMNS_KEY, constructor);
5767
+ const map = new Map(inherited);
5768
+ Reflect.defineMetadata(COLUMNS_KEY, map, constructor);
5769
+ return map;
5770
+ }
5771
+ function standardColumnMap(bag) {
5772
+ if (!Object.prototype.hasOwnProperty.call(bag, COLUMNS_KEY)) {
5773
+ bag[COLUMNS_KEY] = new Map(bag[COLUMNS_KEY]);
5832
5774
  }
5833
- const baseSqlType = inferBaseSqlTypeFromDesignType(target, propertyKey);
5775
+ return bag[COLUMNS_KEY];
5776
+ }
5777
+ function registerColumn(columns, propertyKey, options) {
5834
5778
  columns.set(propertyKey, {
5835
5779
  columnName: options.columnName,
5836
- typeCast: finalTypeCast,
5837
- serialize: finalSerialize,
5780
+ typeCast: options.typeCast,
5781
+ serialize: options.serialize,
5838
5782
  primaryKey: options.primaryKey,
5839
5783
  autoIncrement: options.autoIncrement,
5840
- sqlCast: finalSqlCast,
5841
- ...baseSqlType !== void 0 ? { baseSqlType } : {}
5784
+ sqlCast: options.sqlCast,
5785
+ ...options.baseSqlType !== void 0 ? { baseSqlType: options.baseSqlType } : {}
5842
5786
  });
5843
- Reflect.defineMetadata(COLUMNS_KEY, columns, constructor);
5844
5787
  }
5845
- function createColumnDecorator(typeCast, serialize, skipAutoInfer = false, sqlCast) {
5788
+ function parseColumnOptions(columnNameOrOptions, propKey) {
5789
+ if (typeof columnNameOrOptions === "string") return { columnName: columnNameOrOptions };
5790
+ if (columnNameOrOptions) {
5791
+ return {
5792
+ columnName: columnNameOrOptions.columnName || propKey,
5793
+ primaryKey: columnNameOrOptions.primaryKey,
5794
+ autoIncrement: columnNameOrOptions.autoIncrement
5795
+ };
5796
+ }
5797
+ return { columnName: propKey };
5798
+ }
5799
+ function rejectNonText(family, val) {
5800
+ if (val === null || val === void 0) return null;
5801
+ if (val instanceof Date) {
5802
+ throw new Error(
5803
+ `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.`
5804
+ );
5805
+ }
5806
+ if (typeof val === "object") {
5807
+ throw new Error(
5808
+ `litedbmodel: @column.${family}() received an object. Use @column.json<T>() for a JSON column, or @column.custom() with a serializer.`
5809
+ );
5810
+ }
5811
+ return val;
5812
+ }
5813
+ function createColumnDecorator(typeCast, serialize, sqlCast, baseSqlType) {
5846
5814
  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;
5815
+ return function(targetOrValue, keyOrContext) {
5816
+ if (isStandardContext(keyOrContext)) {
5817
+ const propKey2 = String(keyOrContext.name);
5818
+ const parsed2 = parseColumnOptions(columnNameOrOptions, propKey2);
5819
+ registerColumn(standardColumnMap(classMetadataBag(keyOrContext, propKey2)), propKey2, {
5820
+ ...parsed2,
5821
+ typeCast,
5822
+ serialize,
5823
+ sqlCast,
5824
+ baseSqlType
5825
+ });
5826
+ return;
5861
5827
  }
5862
- registerColumn(target, propKey, {
5863
- columnName,
5828
+ const propKey = String(keyOrContext);
5829
+ const parsed = parseColumnOptions(columnNameOrOptions, propKey);
5830
+ registerColumn(legacyColumnMap(targetOrValue.constructor), propKey, {
5831
+ ...parsed,
5864
5832
  typeCast,
5865
5833
  serialize,
5866
- skipAutoInfer: shouldSkipAutoInfer,
5867
- primaryKey,
5868
- autoIncrement,
5869
- sqlCast
5834
+ sqlCast,
5835
+ baseSqlType
5870
5836
  });
5871
5837
  };
5872
5838
  };
5873
5839
  }
5840
+ function requireColumnFamily() {
5841
+ throw new Error(
5842
+ "litedbmodel: `@column()` no longer declares a column \u2014 a column must state its type with a family: @column.text() / .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."
5843
+ );
5844
+ }
5874
5845
  function serializeArray(val, typeCast) {
5875
5846
  if (val === null || val === void 0) return null;
5876
5847
  if (!Array.isArray(val)) return val;
@@ -5908,19 +5879,44 @@ function serializeJson(val, typeCast) {
5908
5879
  return JSON.stringify(val);
5909
5880
  }
5910
5881
  var column = Object.assign(
5911
- // Basic @column() - no type conversion
5912
- createColumnDecorator(),
5882
+ // The bare `@column()` declared no type — it is rejected, with the migration in the message.
5883
+ (_columnNameOrOptions) => requireColumnFamily(),
5913
5884
  {
5914
5885
  // ============================================
5915
5886
  // Primitive Types
5916
5887
  // ============================================
5888
+ /**
5889
+ * Text type (TEXT / VARCHAR / CHAR / ENUM / citext) — the driver value is already a string, so no
5890
+ * cast is applied. The family still DECLARES the column so the typed read types it as `TEXT`.
5891
+ * @example @column.text() name?: string;
5892
+ * @example @column.text('user_name') name?: string;
5893
+ */
5894
+ text: (columnNameOrOptions) => createColumnDecorator(
5895
+ // The family DECLARES `string`, so it must hand back one. Most drivers already do, but SQLite
5896
+ // is dynamically typed: it returns a JS number for a `NUMERIC`/`DECIMAL` column whatever the
5897
+ // declaration says, and the declared type would be a lie on that dialect alone.
5898
+ (v) => {
5899
+ if (v === void 0 || v === null) return v;
5900
+ if (typeof v === "string") return v;
5901
+ if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);
5902
+ throw new Error(
5903
+ `materialize text: driver returned ${typeof v} for a text column. Use @column.passthrough() for a BYTEA/BLOB column, or @column.json<T>() for JSON.`
5904
+ );
5905
+ },
5906
+ (val) => rejectNonText("text", val),
5907
+ // …and it cannot SERIALIZE a Date or an object
5908
+ void 0,
5909
+ // no sqlCast family (a text literal needs no SQL cast)
5910
+ "TEXT"
5911
+ // §4.1 token: types the SCP typed read
5912
+ )(columnNameOrOptions),
5917
5913
  /**
5918
5914
  * Boolean type conversion
5919
5915
  * Converts 't'/'f', 'true'/'false', 1/0 to boolean
5920
5916
  * Preserves null for nullable columns, undefined stays undefined
5921
5917
  * @example @column.boolean() is_active?: boolean;
5922
5918
  */
5923
- boolean: (columnName) => createColumnDecorator(
5919
+ boolean: (columnNameOrOptions) => createColumnDecorator(
5924
5920
  // v2 read contract: BOOLEAN → boolean, via the shared materializer (issue #9).
5925
5921
  (v) => {
5926
5922
  if (v === void 0) return void 0;
@@ -5928,42 +5924,38 @@ var column = Object.assign(
5928
5924
  },
5929
5925
  void 0,
5930
5926
  // no custom serialize
5931
- false,
5932
- // allow auto-inference
5933
5927
  "boolean"
5934
5928
  // sqlCast for updateMany type inference
5935
- )(columnName),
5929
+ )(columnNameOrOptions),
5936
5930
  /**
5937
5931
  * Number type conversion (from string)
5938
5932
  * Preserves null for nullable columns, undefined stays undefined
5939
5933
  * @example @column.number() amount?: number;
5940
5934
  */
5941
- number: (columnName) => createColumnDecorator((v) => {
5935
+ number: (columnNameOrOptions) => createColumnDecorator((v) => {
5942
5936
  if (v === void 0) return void 0;
5943
5937
  if (v === null) return null;
5944
5938
  const n = Number(v);
5945
5939
  return isNaN(n) ? null : n;
5946
- })(columnName),
5940
+ })(columnNameOrOptions),
5947
5941
  /**
5948
5942
  * BigInt type conversion
5949
5943
  * Preserves null for nullable columns, undefined stays undefined
5950
- * @example @column.bigint() large_id?: bigint;
5944
+ * @example @column.bigint() large_id?: bigint; // bc `int` — a JS bigint, checked i64
5951
5945
  */
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.
5946
+ bigint: (columnNameOrOptions) => createColumnDecorator(
5947
+ // An integer column reads back as a JS `bigint` — behavior-contracts' `int` value model
5948
+ // (`int` = bigint, checked i64), which is what the drivers already hand over. `JSON.stringify`
5949
+ // does not serialize a bigint: give the field a `toJSON`, or convert at the boundary.
5956
5950
  (v) => {
5957
5951
  if (v === void 0) return void 0;
5958
- return materializeCell(v, "int64");
5952
+ return materializeCell(v, "int");
5959
5953
  },
5960
5954
  void 0,
5961
5955
  // no custom serialize
5962
- false,
5963
- // allow auto-inference
5964
5956
  "bigint"
5965
5957
  // sqlCast for WHERE/INSERT type casting
5966
- )(columnName),
5958
+ )(columnNameOrOptions),
5967
5959
  // ============================================
5968
5960
  // Date/Time Types
5969
5961
  // ============================================
@@ -5975,9 +5967,9 @@ var column = Object.assign(
5975
5967
  * - PostgreSQL: Serializes to ISO 8601 UTC string with 'Z' suffix for explicit timezone
5976
5968
  * - MySQL/SQLite: Passes Date object to driver (driver-dependent timezone handling)
5977
5969
  *
5978
- * @example @column.datetime() created_at?: Date;
5970
+ * @example @column.datetime() created_at?: string; // TZ-attached string, NOT a JS Date
5979
5971
  */
5980
- datetime: (columnName) => createColumnDecorator(
5972
+ datetime: (columnNameOrOptions) => createColumnDecorator(
5981
5973
  // v2 read contract: DATE/TIMESTAMP/TIMESTAMPTZ/DATETIME/TIME → TZ-attached STRING (NOT a
5982
5974
  // TZ-shifted JS Date), via the shared materializer (issue #9). A driver already returning the
5983
5975
  // native textual form passes through; a JS Date is rendered to its lossless ISO instant.
@@ -5993,11 +5985,9 @@ var column = Object.assign(
5993
5985
  }
5994
5986
  return val;
5995
5987
  },
5996
- false,
5997
- // allow auto-inference
5998
5988
  "timestamp"
5999
5989
  // sqlCast for updateMany type inference
6000
- )(columnName),
5990
+ )(columnNameOrOptions),
6001
5991
  /**
6002
5992
  * Date type conversion — returns YYYY-MM-DD string.
6003
5993
  * Preserves null for nullable columns, undefined stays undefined.
@@ -6005,9 +5995,9 @@ var column = Object.assign(
6005
5995
  * DB values (Date object or string) are normalized to 'YYYY-MM-DD' string.
6006
5996
  * On write, string values are passed through; Date objects are formatted as 'YYYY-MM-DD'.
6007
5997
  *
6008
- * @example @column.date() birth_date?: string;
5998
+ * @example @column.date() birth_date?: string; // 'YYYY-MM-DD'
6009
5999
  */
6010
- date: (columnName) => createColumnDecorator(
6000
+ date: (columnNameOrOptions) => createColumnDecorator(
6011
6001
  (v) => {
6012
6002
  if (v === void 0) return void 0;
6013
6003
  if (v === null) return null;
@@ -6032,11 +6022,9 @@ var column = Object.assign(
6032
6022
  }
6033
6023
  return val;
6034
6024
  },
6035
- false,
6036
- // allow auto-inference
6037
6025
  "date"
6038
6026
  // sqlCast for updateMany type inference
6039
- )(columnName),
6027
+ )(columnNameOrOptions),
6040
6028
  // ============================================
6041
6029
  // Array Types
6042
6030
  // ============================================
@@ -6045,71 +6033,71 @@ var column = Object.assign(
6045
6033
  * Preserves null for nullable columns, undefined stays undefined
6046
6034
  * @example @column.stringArray() tags?: string[];
6047
6035
  */
6048
- stringArray: (columnName) => createColumnDecorator(
6036
+ stringArray: (columnNameOrOptions) => createColumnDecorator(
6049
6037
  (v) => {
6050
6038
  if (v === void 0) return void 0;
6051
6039
  if (v === null) return null;
6052
6040
  return castToStringArray(v);
6053
6041
  },
6054
6042
  serializeArray,
6055
- false,
6056
6043
  "text[]"
6057
- )(columnName),
6044
+ )(columnNameOrOptions),
6058
6045
  /**
6059
6046
  * Integer array type conversion (integer[])
6060
6047
  * Preserves null for nullable columns, undefined stays undefined
6061
6048
  * @example @column.intArray() scores?: number[];
6062
6049
  */
6063
- intArray: (columnName) => createColumnDecorator(
6050
+ intArray: (columnNameOrOptions) => createColumnDecorator(
6064
6051
  (v) => {
6065
6052
  if (v === void 0) return void 0;
6066
6053
  if (v === null) return null;
6067
6054
  return castToIntegerArray(v);
6068
6055
  },
6069
6056
  serializeArray,
6070
- false,
6071
6057
  "int[]"
6072
- )(columnName),
6058
+ )(columnNameOrOptions),
6073
6059
  /**
6074
6060
  * Numeric array type conversion (numeric[], allows null elements)
6075
6061
  * Preserves null for nullable columns, undefined stays undefined
6076
6062
  * @example @column.numericArray() values?: (number | null)[];
6077
6063
  */
6078
- numericArray: (columnName) => createColumnDecorator(
6064
+ numericArray: (columnNameOrOptions) => createColumnDecorator(
6079
6065
  (v) => {
6080
6066
  if (v === void 0) return void 0;
6081
6067
  if (v === null) return null;
6082
6068
  return castToNumericArray(v);
6083
6069
  },
6084
6070
  serializeArray,
6085
- false,
6086
6071
  "numeric[]"
6087
- )(columnName),
6072
+ )(columnNameOrOptions),
6088
6073
  /**
6089
6074
  * Boolean array type conversion (boolean[])
6090
6075
  * Preserves null for nullable columns, undefined stays undefined
6091
6076
  * @example @column.booleanArray() flags?: (boolean | null)[];
6092
6077
  */
6093
- booleanArray: (columnName) => createColumnDecorator(
6078
+ booleanArray: (columnNameOrOptions) => createColumnDecorator(
6094
6079
  (v) => {
6095
6080
  if (v === void 0) return void 0;
6096
6081
  if (v === null) return null;
6097
6082
  return castToBooleanArray(v);
6098
6083
  },
6099
6084
  serializeBooleanArray,
6100
- false,
6101
6085
  "boolean[]"
6102
- )(columnName),
6086
+ )(columnNameOrOptions),
6103
6087
  /**
6104
6088
  * DateTime array type conversion (timestamp[])
6105
6089
  * Preserves null for nullable columns, undefined stays undefined
6106
- * @example @column.datetimeArray() event_dates?: (Date | null)[];
6090
+ * @example @column.datetimeArray() event_dates?: (string | null)[];
6107
6091
  */
6108
- datetimeArray: (columnName) => createColumnDecorator(
6092
+ datetimeArray: (columnNameOrOptions) => createColumnDecorator(
6093
+ // Element-wise the SAME contract as `@column.datetime()`: a TZ-attached STRING, never a
6094
+ // TZ-shifted JS Date. The array family used to be the one place a `Date` still came back, so
6095
+ // `JSON.stringify` of a row silently re-rendered those elements in UTC — the exact hazard #9
6096
+ // closed for the scalar.
6109
6097
  (v) => {
6110
6098
  if (v === void 0) return void 0;
6111
6099
  if (v === null) return null;
6112
- return castToDatetimeArray(v);
6100
+ return castToDatetimeArray(v).map((d) => d === null ? null : materializeCell(d, "date"));
6113
6101
  },
6114
6102
  // DateTime arrays serialize each Date to ISO string
6115
6103
  (val) => {
@@ -6118,7 +6106,7 @@ var column = Object.assign(
6118
6106
  const mapped = val.map((v) => v === null ? "NULL" : v instanceof Date ? v.toISOString() : String(v));
6119
6107
  return `{${mapped.join(",")}}`;
6120
6108
  }
6121
- )(columnName),
6109
+ )(columnNameOrOptions),
6122
6110
  // ============================================
6123
6111
  // JSON Types
6124
6112
  // ============================================
@@ -6128,16 +6116,15 @@ var column = Object.assign(
6128
6116
  * @example @column.json() metadata?: Record<string, unknown>;
6129
6117
  * @example @column.json<UserSettings>() settings?: UserSettings;
6130
6118
  */
6131
- json: (columnName) => createColumnDecorator(
6119
+ json: (columnNameOrOptions) => createColumnDecorator(
6132
6120
  (v) => {
6133
6121
  if (v === void 0) return void 0;
6134
6122
  if (v === null) return null;
6135
6123
  return castToJson(v);
6136
6124
  },
6137
6125
  serializeJson,
6138
- false,
6139
6126
  "jsonb"
6140
- )(columnName),
6127
+ )(columnNameOrOptions),
6141
6128
  // ============================================
6142
6129
  // UUID Type (PostgreSQL)
6143
6130
  // ============================================
@@ -6166,22 +6153,28 @@ var column = Object.assign(
6166
6153
  if (v === null) return null;
6167
6154
  return String(v);
6168
6155
  },
6169
- void 0,
6170
- // No serialization needed - handled by sqlCast
6171
- true,
6172
- // Skip auto-inference
6156
+ (val) => rejectNonText("uuid", val),
6157
+ // the ::uuid cast handles the rest
6173
6158
  "uuid"
6174
6159
  // SQL type for casting
6175
6160
  )(columnNameOrOptions),
6176
6161
  // ============================================
6177
6162
  // Custom Type Conversion
6178
6163
  // ============================================
6164
+ /**
6165
+ * The driver's value, UNCAST — for a column whose type the library has no cast for (`BYTEA` /
6166
+ * `BLOB` arrives as a driver Buffer, a vendor type as whatever the driver decodes). It states
6167
+ * "this column is passed through", which is a DECLARATION; the removed bare `@column()` was the
6168
+ * absence of one, and produced this same passthrough by accident for every unresolvable type.
6169
+ * @example @column.passthrough() payload?: unknown;
6170
+ */
6171
+ passthrough: (columnNameOrOptions) => createColumnDecorator()(columnNameOrOptions),
6179
6172
  /**
6180
6173
  * Custom type conversion with user-provided function
6181
6174
  * @example @column.custom((v) => String(v).toUpperCase()) status?: string;
6182
6175
  * @example @column.custom((v) => v, (v) => JSON.stringify(v)) data?: MyType; // with serializer
6183
6176
  */
6184
- custom: (castFn, serializeFn, columnName) => createColumnDecorator(castFn, serializeFn)(columnName)
6177
+ custom: (castFn, serializeFn, columnNameOrOptions) => createColumnDecorator(castFn, serializeFn)(columnNameOrOptions)
6185
6178
  }
6186
6179
  );
6187
6180
  function getRelationMeta(modelClass) {
@@ -122,6 +122,12 @@ export declare function encodeJsonArrayParam(dialect: Dialect, values: readonly
122
122
  * the batch record sets in {@link import('./json-batch')} alike. Both bind values that came OFF a read, so
123
123
  * both meet the same two cases (a bc `int` is a `BigInt`, which `JSON.stringify` refuses; a MySQL boolean
124
124
  * must ride as 1/0), and a second bare `JSON.stringify` would silently diverge on either.
125
+ *
126
+ * A bc `int` is written as a JSON NUMBER TOKEN carrying every digit — `JSON.stringify` cannot emit one
127
+ * (it throws on a BigInt), and routing it through `Number` LOSES the value: an id past 2^53 was rounded
128
+ * inside the JSON text and the rounded id is what MySQL/SQLite stored, silently. JSON numbers are
129
+ * arbitrary-precision by grammar, and both `JSON_TABLE` (MySQL) and `json_each` (SQLite) read an
130
+ * integer literal within i64 exactly — which is the whole range bc's `int` admits.
125
131
  */
126
132
  export declare function encodeJsonParam(dialect: Dialect, payload: unknown): string;
127
133
  /**