litedbmodel 2.2.5 → 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 +11 -10
@@ -2230,12 +2230,14 @@ function tupleInPredicate(dialect, table, columns, pgElementTypes) {
2230
2230
  function encodeJsonArrayParam(dialect, values) {
2231
2231
  return encodeJsonParam(dialect, values);
2232
2232
  }
2233
+ var BIGINT_SENTINEL = "\0int:";
2233
2234
  function encodeJsonParam(dialect, payload) {
2234
- return JSON.stringify(payload, (_key, v) => {
2235
- if (typeof v === "bigint") return Number(v);
2235
+ const tagged = JSON.stringify(payload, (_key, v) => {
2236
+ if (typeof v === "bigint") return `${BIGINT_SENTINEL}${v.toString()}`;
2236
2237
  if (dialect === "mysql" && typeof v === "boolean") return v ? 1 : 0;
2237
2238
  return v;
2238
2239
  });
2240
+ return tagged.replace(/"\\u0000int:(-?\d+)"/g, "$1");
2239
2241
  }
2240
2242
  var JsonArrayConditions = class extends DBConditions {
2241
2243
  _dialect;
@@ -3941,8 +3943,8 @@ function sqlTypeToMaterializeClass(sqlType) {
3941
3943
  return "passthrough";
3942
3944
  }
3943
3945
  switch (t) {
3944
- // 32-bit int family: JS number holds the full range exactly. The PostgreSQL SERIAL family maps to
3945
- // its underlying int width (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2).
3946
+ // Integer family. The PostgreSQL SERIAL types are the same integers under an auto-increment
3947
+ // default (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2, BIGSERIAL/SERIAL8=int8).
3946
3948
  case "INTEGER":
3947
3949
  case "INT":
3948
3950
  case "SMALLINT":
@@ -3954,13 +3956,11 @@ function sqlTypeToMaterializeClass(sqlType) {
3954
3956
  case "SERIAL4":
3955
3957
  case "SMALLSERIAL":
3956
3958
  case "SERIAL2":
3957
- return "int32";
3958
- // 64-bit int: needs JS bigint for exactness.
3959
3959
  case "BIGINT":
3960
3960
  case "INT8":
3961
3961
  case "BIGSERIAL":
3962
3962
  case "SERIAL8":
3963
- return "int64";
3963
+ return "int";
3964
3964
  case "BOOLEAN":
3965
3965
  case "BOOL":
3966
3966
  return "bool";
@@ -3997,44 +3997,45 @@ function sqlTypeToMaterializeClass(sqlType) {
3997
3997
  }
3998
3998
  function keyArrayElemScalar(sqlType) {
3999
3999
  switch (sqlTypeToMaterializeClass(sqlType)) {
4000
- case "int32":
4001
- return "float";
4002
- // materializes to a JS number → bc float
4003
- case "int64":
4000
+ case "int":
4001
+ return "int";
4002
+ // materializes to a JS bigint → bc int
4004
4003
  case "date":
4005
4004
  return "string";
4006
- // materializes to a decimal / TZ string → bc string
4005
+ // materializes to a TZ string → bc string
4007
4006
  case "bool":
4008
4007
  return "bool";
4009
4008
  case "passthrough":
4010
4009
  return sqlTypeToBcScalar(sqlType) === "float" ? "float" : "string";
4011
4010
  }
4012
4011
  }
4012
+ var I64_MIN2 = -(2n ** 63n);
4013
+ var I64_MAX2 = 2n ** 63n - 1n;
4014
+ function checkI642(v) {
4015
+ if (v < I64_MIN2 || v > I64_MAX2) {
4016
+ throw new Error(`materialize int: ${v} is outside the i64 range behavior-contracts' \`int\` admits`);
4017
+ }
4018
+ return v;
4019
+ }
4013
4020
  function materializeCell(value, klass) {
4014
4021
  if (value === null || value === void 0) return value;
4015
4022
  switch (klass) {
4016
- case "int64": {
4017
- if (typeof value === "bigint") return value.toString();
4023
+ case "int": {
4024
+ if (typeof value === "bigint") return checkI642(value);
4018
4025
  if (typeof value === "string") {
4019
- if (!/^-?\d+$/.test(value)) throw new Error(`materialize int64: driver returned a non-integer string '${value}'`);
4020
- return value;
4026
+ if (!/^-?\d+$/.test(value)) throw new Error(`materialize int: driver returned a non-integer string '${value}'`);
4027
+ return checkI642(BigInt(value));
4021
4028
  }
4022
4029
  if (typeof value === "number") {
4023
- if (!Number.isInteger(value)) throw new Error(`materialize int64: driver returned a non-integer number ${value} for a 64-bit int column`);
4030
+ if (!Number.isInteger(value)) throw new Error(`materialize int: driver returned a non-integer number ${value} for an integer column`);
4024
4031
  if (!Number.isSafeInteger(value)) {
4025
4032
  throw new Error(
4026
- `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).`
4033
+ `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).`
4027
4034
  );
4028
4035
  }
4029
- return value.toString();
4036
+ return BigInt(value);
4030
4037
  }
4031
- throw new Error(`materialize int64: unexpected driver JS type ${typeof value}`);
4032
- }
4033
- case "int32": {
4034
- if (typeof value === "number") return value;
4035
- if (typeof value === "bigint") return Number(value);
4036
- if (typeof value === "string" && /^-?\d+$/.test(value)) return Number(value);
4037
- throw new Error(`materialize int32: unexpected driver JS type ${typeof value} (${String(value)})`);
4038
+ throw new Error(`materialize int: unexpected driver JS type ${typeof value}`);
4038
4039
  }
4039
4040
  case "date": {
4040
4041
  if (typeof value === "string") return value;
@@ -5523,8 +5524,7 @@ function onConflictTail(dialect, ports, cols) {
5523
5524
  }
5524
5525
  function pgTypeSpecimen(sqlType) {
5525
5526
  const klass = sqlTypeToMaterializeClass(sqlType);
5526
- if (klass === "int32") return 0;
5527
- if (klass === "int64") return 0n;
5527
+ if (klass === "int") return 0n;
5528
5528
  if (klass === "bool") return false;
5529
5529
  if (klass === "date") return /* @__PURE__ */ new Date(0);
5530
5530
  const scalar = sqlTypeToBcScalar(sqlType);
@@ -6156,10 +6156,6 @@ function materializeChildRows(rows, cols) {
6156
6156
  row[key] = materializeCell(row[key], klass);
6157
6157
  continue;
6158
6158
  }
6159
- if (typeof row[key] === "bigint") {
6160
- const v = row[key];
6161
- row[key] = v >= BigInt(Number.MIN_SAFE_INTEGER) && v <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(v) : v.toString();
6162
- }
6163
6159
  }
6164
6160
  }
6165
6161
  return rows;
@@ -6299,133 +6295,108 @@ function compileDeleteManyBundle(name, options, dialectName = "sqlite") {
6299
6295
  var import_reflect_metadata = __toESM(require_Reflect());
6300
6296
  var COLUMNS_KEY = /* @__PURE__ */ Symbol("litedbmodel:columns");
6301
6297
  var RELATIONS_KEY = /* @__PURE__ */ Symbol("litedbmodel:relations");
6302
- function inferTypeCastFromDesignType(target, propertyKey) {
6303
- const designType = Reflect.getMetadata("design:type", target, propertyKey);
6304
- if (!designType) return void 0;
6305
- switch (designType) {
6306
- case Boolean:
6307
- return {
6308
- // v2 read contract: BOOLEAN → boolean, via the shared materializer.
6309
- typeCast: (v) => {
6310
- if (v === void 0) return void 0;
6311
- return materializeCell(v, "bool");
6312
- },
6313
- sqlCast: "boolean"
6314
- };
6315
- case Date:
6316
- return {
6317
- typeCast: (v) => {
6318
- if (v === void 0) return void 0;
6319
- return materializeCell(v, "date");
6320
- },
6321
- // Delegate to driver-specific serializeDatetime
6322
- serialize: (val, typeCast) => {
6323
- if (val === null || val === void 0) return null;
6324
- if (val instanceof Date) {
6325
- return typeCast?.serializeDatetime(val) ?? val;
6326
- }
6327
- return val;
6328
- },
6329
- sqlCast: "timestamp"
6330
- };
6331
- case Number:
6332
- return {
6333
- typeCast: (v) => {
6334
- if (v === void 0) return void 0;
6335
- if (v === null) return null;
6336
- const n = Number(v);
6337
- return isNaN(n) ? null : n;
6338
- }
6339
- };
6340
- case BigInt:
6341
- return {
6342
- // v2 read contract: BIGINT/INT8 → EXACT decimal STRING (no i64 rounding, JSON-safe), via the
6343
- // shared materializer. NOTE: the TS field type stays `bigint` in existing models for source
6344
- // compat, but the VALUE is now the exact string — the whole point of the #9 realignment.
6345
- typeCast: (v) => {
6346
- if (v === void 0) return void 0;
6347
- return materializeCell(v, "int64");
6348
- },
6349
- sqlCast: "bigint"
6350
- };
6351
- // Array, Object, String, and other types require explicit specification
6352
- default:
6353
- return void 0;
6354
- }
6355
- }
6356
- function inferBaseSqlTypeFromDesignType(target, propertyKey) {
6357
- const designType = Reflect.getMetadata("design:type", target, propertyKey);
6358
- if (!designType) return void 0;
6359
- switch (designType) {
6360
- case String:
6361
- return "TEXT";
6362
- case Number:
6363
- return "INTEGER";
6364
- case Boolean:
6365
- return "BOOLEAN";
6366
- case Date:
6367
- return "TIMESTAMP";
6368
- case BigInt:
6369
- return "BIGINT";
6370
- default:
6371
- return void 0;
6298
+ if (typeof Symbol === "function" && !Symbol.metadata) {
6299
+ Object.defineProperty(Symbol, "metadata", {
6300
+ value: /* @__PURE__ */ Symbol.for("Symbol.metadata"),
6301
+ writable: false,
6302
+ enumerable: false,
6303
+ configurable: true
6304
+ });
6305
+ }
6306
+ function isStandardContext(second) {
6307
+ return typeof second === "object" && second !== null && typeof second.kind === "string";
6308
+ }
6309
+ function classMetadataBag(context, propertyKey) {
6310
+ const bag = context.metadata;
6311
+ if (bag === void 0) {
6312
+ throw new Error(
6313
+ `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.`
6314
+ );
6372
6315
  }
6316
+ return bag;
6373
6317
  }
6374
- function registerColumn(target, propertyKey, options) {
6375
- const constructor = target.constructor;
6376
- const columns = Reflect.getMetadata(COLUMNS_KEY, constructor) || /* @__PURE__ */ new Map();
6377
- let finalTypeCast = options.typeCast;
6378
- let finalSerialize = options.serialize;
6379
- let finalSqlCast = options.sqlCast;
6380
- if (!options.skipAutoInfer) {
6381
- const inferred = inferTypeCastFromDesignType(target, propertyKey);
6382
- if (inferred) {
6383
- if (!finalTypeCast) finalTypeCast = inferred.typeCast;
6384
- if (!finalSerialize) finalSerialize = inferred.serialize;
6385
- if (!finalSqlCast) finalSqlCast = inferred.sqlCast;
6386
- }
6318
+ function legacyColumnMap(constructor) {
6319
+ const own = Reflect.getOwnMetadata(COLUMNS_KEY, constructor);
6320
+ if (own !== void 0) return own;
6321
+ const inherited = Reflect.getMetadata(COLUMNS_KEY, constructor);
6322
+ const map = new Map(inherited);
6323
+ Reflect.defineMetadata(COLUMNS_KEY, map, constructor);
6324
+ return map;
6325
+ }
6326
+ function standardColumnMap(bag) {
6327
+ if (!Object.prototype.hasOwnProperty.call(bag, COLUMNS_KEY)) {
6328
+ bag[COLUMNS_KEY] = new Map(bag[COLUMNS_KEY]);
6387
6329
  }
6388
- const baseSqlType = inferBaseSqlTypeFromDesignType(target, propertyKey);
6330
+ return bag[COLUMNS_KEY];
6331
+ }
6332
+ function registerColumn(columns, propertyKey, options) {
6389
6333
  columns.set(propertyKey, {
6390
6334
  columnName: options.columnName,
6391
- typeCast: finalTypeCast,
6392
- serialize: finalSerialize,
6335
+ typeCast: options.typeCast,
6336
+ serialize: options.serialize,
6393
6337
  primaryKey: options.primaryKey,
6394
6338
  autoIncrement: options.autoIncrement,
6395
- sqlCast: finalSqlCast,
6396
- ...baseSqlType !== void 0 ? { baseSqlType } : {}
6339
+ sqlCast: options.sqlCast,
6340
+ ...options.baseSqlType !== void 0 ? { baseSqlType: options.baseSqlType } : {}
6397
6341
  });
6398
- Reflect.defineMetadata(COLUMNS_KEY, columns, constructor);
6399
6342
  }
6400
- function createColumnDecorator(typeCast, serialize, skipAutoInfer = false, sqlCast) {
6343
+ function parseColumnOptions(columnNameOrOptions, propKey) {
6344
+ if (typeof columnNameOrOptions === "string") return { columnName: columnNameOrOptions };
6345
+ if (columnNameOrOptions) {
6346
+ return {
6347
+ columnName: columnNameOrOptions.columnName || propKey,
6348
+ primaryKey: columnNameOrOptions.primaryKey,
6349
+ autoIncrement: columnNameOrOptions.autoIncrement
6350
+ };
6351
+ }
6352
+ return { columnName: propKey };
6353
+ }
6354
+ function rejectNonText(family, val) {
6355
+ if (val === null || val === void 0) return null;
6356
+ if (val instanceof Date) {
6357
+ throw new Error(
6358
+ `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.`
6359
+ );
6360
+ }
6361
+ if (typeof val === "object") {
6362
+ throw new Error(
6363
+ `litedbmodel: @column.${family}() received an object. Use @column.json<T>() for a JSON column, or @column.custom() with a serializer.`
6364
+ );
6365
+ }
6366
+ return val;
6367
+ }
6368
+ function createColumnDecorator(typeCast, serialize, sqlCast, baseSqlType) {
6401
6369
  return function(columnNameOrOptions) {
6402
- return function(target, propertyKey) {
6403
- const propKey = String(propertyKey);
6404
- const shouldSkipAutoInfer = skipAutoInfer || typeCast !== void 0;
6405
- let columnName;
6406
- let primaryKey;
6407
- let autoIncrement;
6408
- if (typeof columnNameOrOptions === "string") {
6409
- columnName = columnNameOrOptions;
6410
- } else if (columnNameOrOptions) {
6411
- columnName = columnNameOrOptions.columnName || propKey;
6412
- primaryKey = columnNameOrOptions.primaryKey;
6413
- autoIncrement = columnNameOrOptions.autoIncrement;
6414
- } else {
6415
- columnName = propKey;
6370
+ return function(targetOrValue, keyOrContext) {
6371
+ if (isStandardContext(keyOrContext)) {
6372
+ const propKey2 = String(keyOrContext.name);
6373
+ const parsed2 = parseColumnOptions(columnNameOrOptions, propKey2);
6374
+ registerColumn(standardColumnMap(classMetadataBag(keyOrContext, propKey2)), propKey2, {
6375
+ ...parsed2,
6376
+ typeCast,
6377
+ serialize,
6378
+ sqlCast,
6379
+ baseSqlType
6380
+ });
6381
+ return;
6416
6382
  }
6417
- registerColumn(target, propKey, {
6418
- columnName,
6383
+ const propKey = String(keyOrContext);
6384
+ const parsed = parseColumnOptions(columnNameOrOptions, propKey);
6385
+ registerColumn(legacyColumnMap(targetOrValue.constructor), propKey, {
6386
+ ...parsed,
6419
6387
  typeCast,
6420
6388
  serialize,
6421
- skipAutoInfer: shouldSkipAutoInfer,
6422
- primaryKey,
6423
- autoIncrement,
6424
- sqlCast
6389
+ sqlCast,
6390
+ baseSqlType
6425
6391
  });
6426
6392
  };
6427
6393
  };
6428
6394
  }
6395
+ function requireColumnFamily() {
6396
+ throw new Error(
6397
+ "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."
6398
+ );
6399
+ }
6429
6400
  function serializeArray(val, typeCast) {
6430
6401
  if (val === null || val === void 0) return null;
6431
6402
  if (!Array.isArray(val)) return val;
@@ -6463,19 +6434,44 @@ function serializeJson(val, typeCast) {
6463
6434
  return JSON.stringify(val);
6464
6435
  }
6465
6436
  var column = Object.assign(
6466
- // Basic @column() - no type conversion
6467
- createColumnDecorator(),
6437
+ // The bare `@column()` declared no type — it is rejected, with the migration in the message.
6438
+ (_columnNameOrOptions) => requireColumnFamily(),
6468
6439
  {
6469
6440
  // ============================================
6470
6441
  // Primitive Types
6471
6442
  // ============================================
6443
+ /**
6444
+ * Text type (TEXT / VARCHAR / CHAR / ENUM / citext) — the driver value is already a string, so no
6445
+ * cast is applied. The family still DECLARES the column so the typed read types it as `TEXT`.
6446
+ * @example @column.text() name?: string;
6447
+ * @example @column.text('user_name') name?: string;
6448
+ */
6449
+ text: (columnNameOrOptions) => createColumnDecorator(
6450
+ // The family DECLARES `string`, so it must hand back one. Most drivers already do, but SQLite
6451
+ // is dynamically typed: it returns a JS number for a `NUMERIC`/`DECIMAL` column whatever the
6452
+ // declaration says, and the declared type would be a lie on that dialect alone.
6453
+ (v) => {
6454
+ if (v === void 0 || v === null) return v;
6455
+ if (typeof v === "string") return v;
6456
+ if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);
6457
+ throw new Error(
6458
+ `materialize text: driver returned ${typeof v} for a text column. Use @column.passthrough() for a BYTEA/BLOB column, or @column.json<T>() for JSON.`
6459
+ );
6460
+ },
6461
+ (val) => rejectNonText("text", val),
6462
+ // …and it cannot SERIALIZE a Date or an object
6463
+ void 0,
6464
+ // no sqlCast family (a text literal needs no SQL cast)
6465
+ "TEXT"
6466
+ // §4.1 token: types the SCP typed read
6467
+ )(columnNameOrOptions),
6472
6468
  /**
6473
6469
  * Boolean type conversion
6474
6470
  * Converts 't'/'f', 'true'/'false', 1/0 to boolean
6475
6471
  * Preserves null for nullable columns, undefined stays undefined
6476
6472
  * @example @column.boolean() is_active?: boolean;
6477
6473
  */
6478
- boolean: (columnName) => createColumnDecorator(
6474
+ boolean: (columnNameOrOptions) => createColumnDecorator(
6479
6475
  // v2 read contract: BOOLEAN → boolean, via the shared materializer (issue #9).
6480
6476
  (v) => {
6481
6477
  if (v === void 0) return void 0;
@@ -6483,42 +6479,38 @@ var column = Object.assign(
6483
6479
  },
6484
6480
  void 0,
6485
6481
  // no custom serialize
6486
- false,
6487
- // allow auto-inference
6488
6482
  "boolean"
6489
6483
  // sqlCast for updateMany type inference
6490
- )(columnName),
6484
+ )(columnNameOrOptions),
6491
6485
  /**
6492
6486
  * Number type conversion (from string)
6493
6487
  * Preserves null for nullable columns, undefined stays undefined
6494
6488
  * @example @column.number() amount?: number;
6495
6489
  */
6496
- number: (columnName) => createColumnDecorator((v) => {
6490
+ number: (columnNameOrOptions) => createColumnDecorator((v) => {
6497
6491
  if (v === void 0) return void 0;
6498
6492
  if (v === null) return null;
6499
6493
  const n = Number(v);
6500
6494
  return isNaN(n) ? null : n;
6501
- })(columnName),
6495
+ })(columnNameOrOptions),
6502
6496
  /**
6503
6497
  * BigInt type conversion
6504
6498
  * Preserves null for nullable columns, undefined stays undefined
6505
- * @example @column.bigint() large_id?: bigint;
6499
+ * @example @column.bigint() large_id?: bigint; // bc `int` — a JS bigint, checked i64
6506
6500
  */
6507
- bigint: (columnName) => createColumnDecorator(
6508
- // v2 read contract: BIGINT/INT8 → EXACT decimal STRING (no i64 rounding, JSON-safe), via the
6509
- // shared materializer (issue #9). The declared TS field type may still be `bigint` in existing
6510
- // models, but the runtime value is now the exact string — that IS the realignment.
6501
+ bigint: (columnNameOrOptions) => createColumnDecorator(
6502
+ // An integer column reads back as a JS `bigint` — behavior-contracts' `int` value model
6503
+ // (`int` = bigint, checked i64), which is what the drivers already hand over. `JSON.stringify`
6504
+ // does not serialize a bigint: give the field a `toJSON`, or convert at the boundary.
6511
6505
  (v) => {
6512
6506
  if (v === void 0) return void 0;
6513
- return materializeCell(v, "int64");
6507
+ return materializeCell(v, "int");
6514
6508
  },
6515
6509
  void 0,
6516
6510
  // no custom serialize
6517
- false,
6518
- // allow auto-inference
6519
6511
  "bigint"
6520
6512
  // sqlCast for WHERE/INSERT type casting
6521
- )(columnName),
6513
+ )(columnNameOrOptions),
6522
6514
  // ============================================
6523
6515
  // Date/Time Types
6524
6516
  // ============================================
@@ -6530,9 +6522,9 @@ var column = Object.assign(
6530
6522
  * - PostgreSQL: Serializes to ISO 8601 UTC string with 'Z' suffix for explicit timezone
6531
6523
  * - MySQL/SQLite: Passes Date object to driver (driver-dependent timezone handling)
6532
6524
  *
6533
- * @example @column.datetime() created_at?: Date;
6525
+ * @example @column.datetime() created_at?: string; // TZ-attached string, NOT a JS Date
6534
6526
  */
6535
- datetime: (columnName) => createColumnDecorator(
6527
+ datetime: (columnNameOrOptions) => createColumnDecorator(
6536
6528
  // v2 read contract: DATE/TIMESTAMP/TIMESTAMPTZ/DATETIME/TIME → TZ-attached STRING (NOT a
6537
6529
  // TZ-shifted JS Date), via the shared materializer (issue #9). A driver already returning the
6538
6530
  // native textual form passes through; a JS Date is rendered to its lossless ISO instant.
@@ -6548,11 +6540,9 @@ var column = Object.assign(
6548
6540
  }
6549
6541
  return val;
6550
6542
  },
6551
- false,
6552
- // allow auto-inference
6553
6543
  "timestamp"
6554
6544
  // sqlCast for updateMany type inference
6555
- )(columnName),
6545
+ )(columnNameOrOptions),
6556
6546
  /**
6557
6547
  * Date type conversion — returns YYYY-MM-DD string.
6558
6548
  * Preserves null for nullable columns, undefined stays undefined.
@@ -6560,9 +6550,9 @@ var column = Object.assign(
6560
6550
  * DB values (Date object or string) are normalized to 'YYYY-MM-DD' string.
6561
6551
  * On write, string values are passed through; Date objects are formatted as 'YYYY-MM-DD'.
6562
6552
  *
6563
- * @example @column.date() birth_date?: string;
6553
+ * @example @column.date() birth_date?: string; // 'YYYY-MM-DD'
6564
6554
  */
6565
- date: (columnName) => createColumnDecorator(
6555
+ date: (columnNameOrOptions) => createColumnDecorator(
6566
6556
  (v) => {
6567
6557
  if (v === void 0) return void 0;
6568
6558
  if (v === null) return null;
@@ -6587,11 +6577,9 @@ var column = Object.assign(
6587
6577
  }
6588
6578
  return val;
6589
6579
  },
6590
- false,
6591
- // allow auto-inference
6592
6580
  "date"
6593
6581
  // sqlCast for updateMany type inference
6594
- )(columnName),
6582
+ )(columnNameOrOptions),
6595
6583
  // ============================================
6596
6584
  // Array Types
6597
6585
  // ============================================
@@ -6600,71 +6588,71 @@ var column = Object.assign(
6600
6588
  * Preserves null for nullable columns, undefined stays undefined
6601
6589
  * @example @column.stringArray() tags?: string[];
6602
6590
  */
6603
- stringArray: (columnName) => createColumnDecorator(
6591
+ stringArray: (columnNameOrOptions) => createColumnDecorator(
6604
6592
  (v) => {
6605
6593
  if (v === void 0) return void 0;
6606
6594
  if (v === null) return null;
6607
6595
  return castToStringArray(v);
6608
6596
  },
6609
6597
  serializeArray,
6610
- false,
6611
6598
  "text[]"
6612
- )(columnName),
6599
+ )(columnNameOrOptions),
6613
6600
  /**
6614
6601
  * Integer array type conversion (integer[])
6615
6602
  * Preserves null for nullable columns, undefined stays undefined
6616
6603
  * @example @column.intArray() scores?: number[];
6617
6604
  */
6618
- intArray: (columnName) => createColumnDecorator(
6605
+ intArray: (columnNameOrOptions) => createColumnDecorator(
6619
6606
  (v) => {
6620
6607
  if (v === void 0) return void 0;
6621
6608
  if (v === null) return null;
6622
6609
  return castToIntegerArray(v);
6623
6610
  },
6624
6611
  serializeArray,
6625
- false,
6626
6612
  "int[]"
6627
- )(columnName),
6613
+ )(columnNameOrOptions),
6628
6614
  /**
6629
6615
  * Numeric array type conversion (numeric[], allows null elements)
6630
6616
  * Preserves null for nullable columns, undefined stays undefined
6631
6617
  * @example @column.numericArray() values?: (number | null)[];
6632
6618
  */
6633
- numericArray: (columnName) => createColumnDecorator(
6619
+ numericArray: (columnNameOrOptions) => createColumnDecorator(
6634
6620
  (v) => {
6635
6621
  if (v === void 0) return void 0;
6636
6622
  if (v === null) return null;
6637
6623
  return castToNumericArray(v);
6638
6624
  },
6639
6625
  serializeArray,
6640
- false,
6641
6626
  "numeric[]"
6642
- )(columnName),
6627
+ )(columnNameOrOptions),
6643
6628
  /**
6644
6629
  * Boolean array type conversion (boolean[])
6645
6630
  * Preserves null for nullable columns, undefined stays undefined
6646
6631
  * @example @column.booleanArray() flags?: (boolean | null)[];
6647
6632
  */
6648
- booleanArray: (columnName) => createColumnDecorator(
6633
+ booleanArray: (columnNameOrOptions) => createColumnDecorator(
6649
6634
  (v) => {
6650
6635
  if (v === void 0) return void 0;
6651
6636
  if (v === null) return null;
6652
6637
  return castToBooleanArray(v);
6653
6638
  },
6654
6639
  serializeBooleanArray,
6655
- false,
6656
6640
  "boolean[]"
6657
- )(columnName),
6641
+ )(columnNameOrOptions),
6658
6642
  /**
6659
6643
  * DateTime array type conversion (timestamp[])
6660
6644
  * Preserves null for nullable columns, undefined stays undefined
6661
- * @example @column.datetimeArray() event_dates?: (Date | null)[];
6645
+ * @example @column.datetimeArray() event_dates?: (string | null)[];
6662
6646
  */
6663
- datetimeArray: (columnName) => createColumnDecorator(
6647
+ datetimeArray: (columnNameOrOptions) => createColumnDecorator(
6648
+ // Element-wise the SAME contract as `@column.datetime()`: a TZ-attached STRING, never a
6649
+ // TZ-shifted JS Date. The array family used to be the one place a `Date` still came back, so
6650
+ // `JSON.stringify` of a row silently re-rendered those elements in UTC — the exact hazard #9
6651
+ // closed for the scalar.
6664
6652
  (v) => {
6665
6653
  if (v === void 0) return void 0;
6666
6654
  if (v === null) return null;
6667
- return castToDatetimeArray(v);
6655
+ return castToDatetimeArray(v).map((d) => d === null ? null : materializeCell(d, "date"));
6668
6656
  },
6669
6657
  // DateTime arrays serialize each Date to ISO string
6670
6658
  (val) => {
@@ -6673,7 +6661,7 @@ var column = Object.assign(
6673
6661
  const mapped = val.map((v) => v === null ? "NULL" : v instanceof Date ? v.toISOString() : String(v));
6674
6662
  return `{${mapped.join(",")}}`;
6675
6663
  }
6676
- )(columnName),
6664
+ )(columnNameOrOptions),
6677
6665
  // ============================================
6678
6666
  // JSON Types
6679
6667
  // ============================================
@@ -6683,16 +6671,15 @@ var column = Object.assign(
6683
6671
  * @example @column.json() metadata?: Record<string, unknown>;
6684
6672
  * @example @column.json<UserSettings>() settings?: UserSettings;
6685
6673
  */
6686
- json: (columnName) => createColumnDecorator(
6674
+ json: (columnNameOrOptions) => createColumnDecorator(
6687
6675
  (v) => {
6688
6676
  if (v === void 0) return void 0;
6689
6677
  if (v === null) return null;
6690
6678
  return castToJson(v);
6691
6679
  },
6692
6680
  serializeJson,
6693
- false,
6694
6681
  "jsonb"
6695
- )(columnName),
6682
+ )(columnNameOrOptions),
6696
6683
  // ============================================
6697
6684
  // UUID Type (PostgreSQL)
6698
6685
  // ============================================
@@ -6721,22 +6708,28 @@ var column = Object.assign(
6721
6708
  if (v === null) return null;
6722
6709
  return String(v);
6723
6710
  },
6724
- void 0,
6725
- // No serialization needed - handled by sqlCast
6726
- true,
6727
- // Skip auto-inference
6711
+ (val) => rejectNonText("uuid", val),
6712
+ // the ::uuid cast handles the rest
6728
6713
  "uuid"
6729
6714
  // SQL type for casting
6730
6715
  )(columnNameOrOptions),
6731
6716
  // ============================================
6732
6717
  // Custom Type Conversion
6733
6718
  // ============================================
6719
+ /**
6720
+ * The driver's value, UNCAST — for a column whose type the library has no cast for (`BYTEA` /
6721
+ * `BLOB` arrives as a driver Buffer, a vendor type as whatever the driver decodes). It states
6722
+ * "this column is passed through", which is a DECLARATION; the removed bare `@column()` was the
6723
+ * absence of one, and produced this same passthrough by accident for every unresolvable type.
6724
+ * @example @column.passthrough() payload?: unknown;
6725
+ */
6726
+ passthrough: (columnNameOrOptions) => createColumnDecorator()(columnNameOrOptions),
6734
6727
  /**
6735
6728
  * Custom type conversion with user-provided function
6736
6729
  * @example @column.custom((v) => String(v).toUpperCase()) status?: string;
6737
6730
  * @example @column.custom((v) => v, (v) => JSON.stringify(v)) data?: MyType; // with serializer
6738
6731
  */
6739
- custom: (castFn, serializeFn, columnName) => createColumnDecorator(castFn, serializeFn)(columnName)
6732
+ custom: (castFn, serializeFn, columnNameOrOptions) => createColumnDecorator(castFn, serializeFn)(columnNameOrOptions)
6740
6733
  }
6741
6734
  );
6742
6735
  function getRelationMeta(modelClass) {