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
package/dist/index.cjs CHANGED
@@ -5632,12 +5632,14 @@ function resolvePgArrayCast(sql2, values) {
5632
5632
  function encodeJsonArrayParam(dialect, values) {
5633
5633
  return encodeJsonParam(dialect, values);
5634
5634
  }
5635
+ var BIGINT_SENTINEL = "\0int:";
5635
5636
  function encodeJsonParam(dialect, payload) {
5636
- return JSON.stringify(payload, (_key, v) => {
5637
- if (typeof v === "bigint") return Number(v);
5637
+ const tagged = JSON.stringify(payload, (_key, v) => {
5638
+ if (typeof v === "bigint") return `${BIGINT_SENTINEL}${v.toString()}`;
5638
5639
  if (dialect === "mysql" && typeof v === "boolean") return v ? 1 : 0;
5639
5640
  return v;
5640
5641
  });
5642
+ return tagged.replace(/"\\u0000int:(-?\d+)"/g, "$1");
5641
5643
  }
5642
5644
 
5643
5645
  // src/scp/makesql/json-batch.ts
@@ -5799,8 +5801,8 @@ function sqlTypeToMaterializeClass(sqlType) {
5799
5801
  return "passthrough";
5800
5802
  }
5801
5803
  switch (t) {
5802
- // 32-bit int family: JS number holds the full range exactly. The PostgreSQL SERIAL family maps to
5803
- // its underlying int width (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2).
5804
+ // Integer family. The PostgreSQL SERIAL types are the same integers under an auto-increment
5805
+ // default (SERIAL/SERIAL4=int4, SMALLSERIAL/SERIAL2=int2, BIGSERIAL/SERIAL8=int8).
5804
5806
  case "INTEGER":
5805
5807
  case "INT":
5806
5808
  case "SMALLINT":
@@ -5812,13 +5814,11 @@ function sqlTypeToMaterializeClass(sqlType) {
5812
5814
  case "SERIAL4":
5813
5815
  case "SMALLSERIAL":
5814
5816
  case "SERIAL2":
5815
- return "int32";
5816
- // 64-bit int: needs JS bigint for exactness.
5817
5817
  case "BIGINT":
5818
5818
  case "INT8":
5819
5819
  case "BIGSERIAL":
5820
5820
  case "SERIAL8":
5821
- return "int64";
5821
+ return "int";
5822
5822
  case "BOOLEAN":
5823
5823
  case "BOOL":
5824
5824
  return "bool";
@@ -5853,31 +5853,33 @@ function sqlTypeToMaterializeClass(sqlType) {
5853
5853
  );
5854
5854
  }
5855
5855
  }
5856
+ var I64_MIN2 = -(2n ** 63n);
5857
+ var I64_MAX2 = 2n ** 63n - 1n;
5858
+ function checkI642(v) {
5859
+ if (v < I64_MIN2 || v > I64_MAX2) {
5860
+ throw new Error(`materialize int: ${v} is outside the i64 range behavior-contracts' \`int\` admits`);
5861
+ }
5862
+ return v;
5863
+ }
5856
5864
  function materializeCell(value, klass) {
5857
5865
  if (value === null || value === void 0) return value;
5858
5866
  switch (klass) {
5859
- case "int64": {
5860
- if (typeof value === "bigint") return value.toString();
5867
+ case "int": {
5868
+ if (typeof value === "bigint") return checkI642(value);
5861
5869
  if (typeof value === "string") {
5862
- if (!/^-?\d+$/.test(value)) throw new Error(`materialize int64: driver returned a non-integer string '${value}'`);
5863
- return value;
5870
+ if (!/^-?\d+$/.test(value)) throw new Error(`materialize int: driver returned a non-integer string '${value}'`);
5871
+ return checkI642(BigInt(value));
5864
5872
  }
5865
5873
  if (typeof value === "number") {
5866
- if (!Number.isInteger(value)) throw new Error(`materialize int64: driver returned a non-integer number ${value} for a 64-bit int column`);
5874
+ if (!Number.isInteger(value)) throw new Error(`materialize int: driver returned a non-integer number ${value} for an integer column`);
5867
5875
  if (!Number.isSafeInteger(value)) {
5868
5876
  throw new Error(
5869
- `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).`
5877
+ `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).`
5870
5878
  );
5871
5879
  }
5872
- return value.toString();
5880
+ return BigInt(value);
5873
5881
  }
5874
- throw new Error(`materialize int64: unexpected driver JS type ${typeof value}`);
5875
- }
5876
- case "int32": {
5877
- if (typeof value === "number") return value;
5878
- if (typeof value === "bigint") return Number(value);
5879
- if (typeof value === "string" && /^-?\d+$/.test(value)) return Number(value);
5880
- throw new Error(`materialize int32: unexpected driver JS type ${typeof value} (${String(value)})`);
5882
+ throw new Error(`materialize int: unexpected driver JS type ${typeof value}`);
5881
5883
  }
5882
5884
  case "date": {
5883
5885
  if (typeof value === "string") return value;
@@ -6376,133 +6378,123 @@ function compileCreateManyBundle(name, options, dialectName = "sqlite", resolveC
6376
6378
  var import_reflect_metadata = __toESM(require_Reflect());
6377
6379
  var COLUMNS_KEY = /* @__PURE__ */ Symbol("litedbmodel:columns");
6378
6380
  var RELATIONS_KEY = /* @__PURE__ */ Symbol("litedbmodel:relations");
6379
- function inferTypeCastFromDesignType(target, propertyKey) {
6380
- const designType = Reflect.getMetadata("design:type", target, propertyKey);
6381
- if (!designType) return void 0;
6382
- switch (designType) {
6383
- case Boolean:
6384
- return {
6385
- // v2 read contract: BOOLEAN → boolean, via the shared materializer.
6386
- typeCast: (v) => {
6387
- if (v === void 0) return void 0;
6388
- return materializeCell(v, "bool");
6389
- },
6390
- sqlCast: "boolean"
6391
- };
6392
- case Date:
6393
- return {
6394
- typeCast: (v) => {
6395
- if (v === void 0) return void 0;
6396
- return materializeCell(v, "date");
6397
- },
6398
- // Delegate to driver-specific serializeDatetime
6399
- serialize: (val, typeCast) => {
6400
- if (val === null || val === void 0) return null;
6401
- if (val instanceof Date) {
6402
- return typeCast?.serializeDatetime(val) ?? val;
6403
- }
6404
- return val;
6405
- },
6406
- sqlCast: "timestamp"
6407
- };
6408
- case Number:
6409
- return {
6410
- typeCast: (v) => {
6411
- if (v === void 0) return void 0;
6412
- if (v === null) return null;
6413
- const n = Number(v);
6414
- return isNaN(n) ? null : n;
6415
- }
6416
- };
6417
- case BigInt:
6418
- return {
6419
- // v2 read contract: BIGINT/INT8 → EXACT decimal STRING (no i64 rounding, JSON-safe), via the
6420
- // shared materializer. NOTE: the TS field type stays `bigint` in existing models for source
6421
- // compat, but the VALUE is now the exact string — the whole point of the #9 realignment.
6422
- typeCast: (v) => {
6423
- if (v === void 0) return void 0;
6424
- return materializeCell(v, "int64");
6425
- },
6426
- sqlCast: "bigint"
6427
- };
6428
- // Array, Object, String, and other types require explicit specification
6429
- default:
6430
- return void 0;
6381
+ if (typeof Symbol === "function" && !Symbol.metadata) {
6382
+ Object.defineProperty(Symbol, "metadata", {
6383
+ value: /* @__PURE__ */ Symbol.for("Symbol.metadata"),
6384
+ writable: false,
6385
+ enumerable: false,
6386
+ configurable: true
6387
+ });
6388
+ }
6389
+ function isStandardContext(second) {
6390
+ return typeof second === "object" && second !== null && typeof second.kind === "string";
6391
+ }
6392
+ function classMetadataBag(context, propertyKey) {
6393
+ const bag = context.metadata;
6394
+ if (bag === void 0) {
6395
+ throw new Error(
6396
+ `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.`
6397
+ );
6431
6398
  }
6399
+ return bag;
6432
6400
  }
6433
- function inferBaseSqlTypeFromDesignType(target, propertyKey) {
6434
- const designType = Reflect.getMetadata("design:type", target, propertyKey);
6435
- if (!designType) return void 0;
6436
- switch (designType) {
6437
- case String:
6438
- return "TEXT";
6439
- case Number:
6440
- return "INTEGER";
6441
- case Boolean:
6442
- return "BOOLEAN";
6443
- case Date:
6444
- return "TIMESTAMP";
6445
- case BigInt:
6446
- return "BIGINT";
6447
- default:
6448
- return void 0;
6401
+ function legacyColumnMap(constructor) {
6402
+ const own = Reflect.getOwnMetadata(COLUMNS_KEY, constructor);
6403
+ if (own !== void 0) return own;
6404
+ const inherited = Reflect.getMetadata(COLUMNS_KEY, constructor);
6405
+ const map = new Map(inherited);
6406
+ Reflect.defineMetadata(COLUMNS_KEY, map, constructor);
6407
+ return map;
6408
+ }
6409
+ function standardColumnMap(bag) {
6410
+ if (!Object.prototype.hasOwnProperty.call(bag, COLUMNS_KEY)) {
6411
+ bag[COLUMNS_KEY] = new Map(bag[COLUMNS_KEY]);
6449
6412
  }
6413
+ return bag[COLUMNS_KEY];
6450
6414
  }
6451
- function registerColumn(target, propertyKey, options) {
6452
- const constructor = target.constructor;
6453
- const columns = Reflect.getMetadata(COLUMNS_KEY, constructor) || /* @__PURE__ */ new Map();
6454
- let finalTypeCast = options.typeCast;
6455
- let finalSerialize = options.serialize;
6456
- let finalSqlCast = options.sqlCast;
6457
- if (!options.skipAutoInfer) {
6458
- const inferred = inferTypeCastFromDesignType(target, propertyKey);
6459
- if (inferred) {
6460
- if (!finalTypeCast) finalTypeCast = inferred.typeCast;
6461
- if (!finalSerialize) finalSerialize = inferred.serialize;
6462
- if (!finalSqlCast) finalSqlCast = inferred.sqlCast;
6463
- }
6415
+ function legacyRelationList(constructor) {
6416
+ const own = Reflect.getOwnMetadata(RELATIONS_KEY, constructor);
6417
+ if (own !== void 0) return own;
6418
+ const inherited = Reflect.getMetadata(RELATIONS_KEY, constructor);
6419
+ const list = inherited !== void 0 ? [...inherited] : [];
6420
+ Reflect.defineMetadata(RELATIONS_KEY, list, constructor);
6421
+ return list;
6422
+ }
6423
+ function standardRelationList(bag) {
6424
+ if (!Object.prototype.hasOwnProperty.call(bag, RELATIONS_KEY)) {
6425
+ const inherited = bag[RELATIONS_KEY];
6426
+ bag[RELATIONS_KEY] = inherited !== void 0 ? [...inherited] : [];
6464
6427
  }
6465
- const baseSqlType = inferBaseSqlTypeFromDesignType(target, propertyKey);
6428
+ return bag[RELATIONS_KEY];
6429
+ }
6430
+ function registerColumn(columns, propertyKey, options) {
6466
6431
  columns.set(propertyKey, {
6467
6432
  columnName: options.columnName,
6468
- typeCast: finalTypeCast,
6469
- serialize: finalSerialize,
6433
+ typeCast: options.typeCast,
6434
+ serialize: options.serialize,
6470
6435
  primaryKey: options.primaryKey,
6471
6436
  autoIncrement: options.autoIncrement,
6472
- sqlCast: finalSqlCast,
6473
- ...baseSqlType !== void 0 ? { baseSqlType } : {}
6437
+ sqlCast: options.sqlCast,
6438
+ ...options.baseSqlType !== void 0 ? { baseSqlType: options.baseSqlType } : {}
6474
6439
  });
6475
- Reflect.defineMetadata(COLUMNS_KEY, columns, constructor);
6476
6440
  }
6477
- function createColumnDecorator(typeCast, serialize, skipAutoInfer = false, sqlCast) {
6441
+ function parseColumnOptions(columnNameOrOptions, propKey) {
6442
+ if (typeof columnNameOrOptions === "string") return { columnName: columnNameOrOptions };
6443
+ if (columnNameOrOptions) {
6444
+ return {
6445
+ columnName: columnNameOrOptions.columnName || propKey,
6446
+ primaryKey: columnNameOrOptions.primaryKey,
6447
+ autoIncrement: columnNameOrOptions.autoIncrement
6448
+ };
6449
+ }
6450
+ return { columnName: propKey };
6451
+ }
6452
+ function rejectNonText(family, val) {
6453
+ if (val === null || val === void 0) return null;
6454
+ if (val instanceof Date) {
6455
+ throw new Error(
6456
+ `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.`
6457
+ );
6458
+ }
6459
+ if (typeof val === "object") {
6460
+ throw new Error(
6461
+ `litedbmodel: @column.${family}() received an object. Use @column.json<T>() for a JSON column, or @column.custom() with a serializer.`
6462
+ );
6463
+ }
6464
+ return val;
6465
+ }
6466
+ function createColumnDecorator(typeCast, serialize, sqlCast, baseSqlType) {
6478
6467
  return function(columnNameOrOptions) {
6479
- return function(target, propertyKey) {
6480
- const propKey = String(propertyKey);
6481
- const shouldSkipAutoInfer = skipAutoInfer || typeCast !== void 0;
6482
- let columnName;
6483
- let primaryKey;
6484
- let autoIncrement;
6485
- if (typeof columnNameOrOptions === "string") {
6486
- columnName = columnNameOrOptions;
6487
- } else if (columnNameOrOptions) {
6488
- columnName = columnNameOrOptions.columnName || propKey;
6489
- primaryKey = columnNameOrOptions.primaryKey;
6490
- autoIncrement = columnNameOrOptions.autoIncrement;
6491
- } else {
6492
- columnName = propKey;
6468
+ return function(targetOrValue, keyOrContext) {
6469
+ if (isStandardContext(keyOrContext)) {
6470
+ const propKey2 = String(keyOrContext.name);
6471
+ const parsed2 = parseColumnOptions(columnNameOrOptions, propKey2);
6472
+ registerColumn(standardColumnMap(classMetadataBag(keyOrContext, propKey2)), propKey2, {
6473
+ ...parsed2,
6474
+ typeCast,
6475
+ serialize,
6476
+ sqlCast,
6477
+ baseSqlType
6478
+ });
6479
+ return;
6493
6480
  }
6494
- registerColumn(target, propKey, {
6495
- columnName,
6481
+ const propKey = String(keyOrContext);
6482
+ const parsed = parseColumnOptions(columnNameOrOptions, propKey);
6483
+ registerColumn(legacyColumnMap(targetOrValue.constructor), propKey, {
6484
+ ...parsed,
6496
6485
  typeCast,
6497
6486
  serialize,
6498
- skipAutoInfer: shouldSkipAutoInfer,
6499
- primaryKey,
6500
- autoIncrement,
6501
- sqlCast
6487
+ sqlCast,
6488
+ baseSqlType
6502
6489
  });
6503
6490
  };
6504
6491
  };
6505
6492
  }
6493
+ function requireColumnFamily() {
6494
+ throw new Error(
6495
+ "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."
6496
+ );
6497
+ }
6506
6498
  function serializeArray(val, typeCast) {
6507
6499
  if (val === null || val === void 0) return null;
6508
6500
  if (!Array.isArray(val)) return val;
@@ -6540,19 +6532,44 @@ function serializeJson(val, typeCast) {
6540
6532
  return JSON.stringify(val);
6541
6533
  }
6542
6534
  var column = Object.assign(
6543
- // Basic @column() - no type conversion
6544
- createColumnDecorator(),
6535
+ // The bare `@column()` declared no type — it is rejected, with the migration in the message.
6536
+ (_columnNameOrOptions) => requireColumnFamily(),
6545
6537
  {
6546
6538
  // ============================================
6547
6539
  // Primitive Types
6548
6540
  // ============================================
6541
+ /**
6542
+ * Text type (TEXT / VARCHAR / CHAR / ENUM / citext) — the driver value is already a string, so no
6543
+ * cast is applied. The family still DECLARES the column so the typed read types it as `TEXT`.
6544
+ * @example @column.text() name?: string;
6545
+ * @example @column.text('user_name') name?: string;
6546
+ */
6547
+ text: (columnNameOrOptions) => createColumnDecorator(
6548
+ // The family DECLARES `string`, so it must hand back one. Most drivers already do, but SQLite
6549
+ // is dynamically typed: it returns a JS number for a `NUMERIC`/`DECIMAL` column whatever the
6550
+ // declaration says, and the declared type would be a lie on that dialect alone.
6551
+ (v) => {
6552
+ if (v === void 0 || v === null) return v;
6553
+ if (typeof v === "string") return v;
6554
+ if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);
6555
+ throw new Error(
6556
+ `materialize text: driver returned ${typeof v} for a text column. Use @column.passthrough() for a BYTEA/BLOB column, or @column.json<T>() for JSON.`
6557
+ );
6558
+ },
6559
+ (val) => rejectNonText("text", val),
6560
+ // …and it cannot SERIALIZE a Date or an object
6561
+ void 0,
6562
+ // no sqlCast family (a text literal needs no SQL cast)
6563
+ "TEXT"
6564
+ // §4.1 token: types the SCP typed read
6565
+ )(columnNameOrOptions),
6549
6566
  /**
6550
6567
  * Boolean type conversion
6551
6568
  * Converts 't'/'f', 'true'/'false', 1/0 to boolean
6552
6569
  * Preserves null for nullable columns, undefined stays undefined
6553
6570
  * @example @column.boolean() is_active?: boolean;
6554
6571
  */
6555
- boolean: (columnName) => createColumnDecorator(
6572
+ boolean: (columnNameOrOptions) => createColumnDecorator(
6556
6573
  // v2 read contract: BOOLEAN → boolean, via the shared materializer (issue #9).
6557
6574
  (v) => {
6558
6575
  if (v === void 0) return void 0;
@@ -6560,42 +6577,38 @@ var column = Object.assign(
6560
6577
  },
6561
6578
  void 0,
6562
6579
  // no custom serialize
6563
- false,
6564
- // allow auto-inference
6565
6580
  "boolean"
6566
6581
  // sqlCast for updateMany type inference
6567
- )(columnName),
6582
+ )(columnNameOrOptions),
6568
6583
  /**
6569
6584
  * Number type conversion (from string)
6570
6585
  * Preserves null for nullable columns, undefined stays undefined
6571
6586
  * @example @column.number() amount?: number;
6572
6587
  */
6573
- number: (columnName) => createColumnDecorator((v) => {
6588
+ number: (columnNameOrOptions) => createColumnDecorator((v) => {
6574
6589
  if (v === void 0) return void 0;
6575
6590
  if (v === null) return null;
6576
6591
  const n = Number(v);
6577
6592
  return isNaN(n) ? null : n;
6578
- })(columnName),
6593
+ })(columnNameOrOptions),
6579
6594
  /**
6580
6595
  * BigInt type conversion
6581
6596
  * Preserves null for nullable columns, undefined stays undefined
6582
- * @example @column.bigint() large_id?: bigint;
6597
+ * @example @column.bigint() large_id?: bigint; // bc `int` — a JS bigint, checked i64
6583
6598
  */
6584
- bigint: (columnName) => createColumnDecorator(
6585
- // v2 read contract: BIGINT/INT8 → EXACT decimal STRING (no i64 rounding, JSON-safe), via the
6586
- // shared materializer (issue #9). The declared TS field type may still be `bigint` in existing
6587
- // models, but the runtime value is now the exact string — that IS the realignment.
6599
+ bigint: (columnNameOrOptions) => createColumnDecorator(
6600
+ // An integer column reads back as a JS `bigint` — behavior-contracts' `int` value model
6601
+ // (`int` = bigint, checked i64), which is what the drivers already hand over. `JSON.stringify`
6602
+ // does not serialize a bigint: give the field a `toJSON`, or convert at the boundary.
6588
6603
  (v) => {
6589
6604
  if (v === void 0) return void 0;
6590
- return materializeCell(v, "int64");
6605
+ return materializeCell(v, "int");
6591
6606
  },
6592
6607
  void 0,
6593
6608
  // no custom serialize
6594
- false,
6595
- // allow auto-inference
6596
6609
  "bigint"
6597
6610
  // sqlCast for WHERE/INSERT type casting
6598
- )(columnName),
6611
+ )(columnNameOrOptions),
6599
6612
  // ============================================
6600
6613
  // Date/Time Types
6601
6614
  // ============================================
@@ -6607,9 +6620,9 @@ var column = Object.assign(
6607
6620
  * - PostgreSQL: Serializes to ISO 8601 UTC string with 'Z' suffix for explicit timezone
6608
6621
  * - MySQL/SQLite: Passes Date object to driver (driver-dependent timezone handling)
6609
6622
  *
6610
- * @example @column.datetime() created_at?: Date;
6623
+ * @example @column.datetime() created_at?: string; // TZ-attached string, NOT a JS Date
6611
6624
  */
6612
- datetime: (columnName) => createColumnDecorator(
6625
+ datetime: (columnNameOrOptions) => createColumnDecorator(
6613
6626
  // v2 read contract: DATE/TIMESTAMP/TIMESTAMPTZ/DATETIME/TIME → TZ-attached STRING (NOT a
6614
6627
  // TZ-shifted JS Date), via the shared materializer (issue #9). A driver already returning the
6615
6628
  // native textual form passes through; a JS Date is rendered to its lossless ISO instant.
@@ -6625,11 +6638,9 @@ var column = Object.assign(
6625
6638
  }
6626
6639
  return val;
6627
6640
  },
6628
- false,
6629
- // allow auto-inference
6630
6641
  "timestamp"
6631
6642
  // sqlCast for updateMany type inference
6632
- )(columnName),
6643
+ )(columnNameOrOptions),
6633
6644
  /**
6634
6645
  * Date type conversion — returns YYYY-MM-DD string.
6635
6646
  * Preserves null for nullable columns, undefined stays undefined.
@@ -6637,9 +6648,9 @@ var column = Object.assign(
6637
6648
  * DB values (Date object or string) are normalized to 'YYYY-MM-DD' string.
6638
6649
  * On write, string values are passed through; Date objects are formatted as 'YYYY-MM-DD'.
6639
6650
  *
6640
- * @example @column.date() birth_date?: string;
6651
+ * @example @column.date() birth_date?: string; // 'YYYY-MM-DD'
6641
6652
  */
6642
- date: (columnName) => createColumnDecorator(
6653
+ date: (columnNameOrOptions) => createColumnDecorator(
6643
6654
  (v) => {
6644
6655
  if (v === void 0) return void 0;
6645
6656
  if (v === null) return null;
@@ -6664,11 +6675,9 @@ var column = Object.assign(
6664
6675
  }
6665
6676
  return val;
6666
6677
  },
6667
- false,
6668
- // allow auto-inference
6669
6678
  "date"
6670
6679
  // sqlCast for updateMany type inference
6671
- )(columnName),
6680
+ )(columnNameOrOptions),
6672
6681
  // ============================================
6673
6682
  // Array Types
6674
6683
  // ============================================
@@ -6677,71 +6686,71 @@ var column = Object.assign(
6677
6686
  * Preserves null for nullable columns, undefined stays undefined
6678
6687
  * @example @column.stringArray() tags?: string[];
6679
6688
  */
6680
- stringArray: (columnName) => createColumnDecorator(
6689
+ stringArray: (columnNameOrOptions) => createColumnDecorator(
6681
6690
  (v) => {
6682
6691
  if (v === void 0) return void 0;
6683
6692
  if (v === null) return null;
6684
6693
  return castToStringArray(v);
6685
6694
  },
6686
6695
  serializeArray,
6687
- false,
6688
6696
  "text[]"
6689
- )(columnName),
6697
+ )(columnNameOrOptions),
6690
6698
  /**
6691
6699
  * Integer array type conversion (integer[])
6692
6700
  * Preserves null for nullable columns, undefined stays undefined
6693
6701
  * @example @column.intArray() scores?: number[];
6694
6702
  */
6695
- intArray: (columnName) => createColumnDecorator(
6703
+ intArray: (columnNameOrOptions) => createColumnDecorator(
6696
6704
  (v) => {
6697
6705
  if (v === void 0) return void 0;
6698
6706
  if (v === null) return null;
6699
6707
  return castToIntegerArray(v);
6700
6708
  },
6701
6709
  serializeArray,
6702
- false,
6703
6710
  "int[]"
6704
- )(columnName),
6711
+ )(columnNameOrOptions),
6705
6712
  /**
6706
6713
  * Numeric array type conversion (numeric[], allows null elements)
6707
6714
  * Preserves null for nullable columns, undefined stays undefined
6708
6715
  * @example @column.numericArray() values?: (number | null)[];
6709
6716
  */
6710
- numericArray: (columnName) => createColumnDecorator(
6717
+ numericArray: (columnNameOrOptions) => createColumnDecorator(
6711
6718
  (v) => {
6712
6719
  if (v === void 0) return void 0;
6713
6720
  if (v === null) return null;
6714
6721
  return castToNumericArray(v);
6715
6722
  },
6716
6723
  serializeArray,
6717
- false,
6718
6724
  "numeric[]"
6719
- )(columnName),
6725
+ )(columnNameOrOptions),
6720
6726
  /**
6721
6727
  * Boolean array type conversion (boolean[])
6722
6728
  * Preserves null for nullable columns, undefined stays undefined
6723
6729
  * @example @column.booleanArray() flags?: (boolean | null)[];
6724
6730
  */
6725
- booleanArray: (columnName) => createColumnDecorator(
6731
+ booleanArray: (columnNameOrOptions) => createColumnDecorator(
6726
6732
  (v) => {
6727
6733
  if (v === void 0) return void 0;
6728
6734
  if (v === null) return null;
6729
6735
  return castToBooleanArray(v);
6730
6736
  },
6731
6737
  serializeBooleanArray,
6732
- false,
6733
6738
  "boolean[]"
6734
- )(columnName),
6739
+ )(columnNameOrOptions),
6735
6740
  /**
6736
6741
  * DateTime array type conversion (timestamp[])
6737
6742
  * Preserves null for nullable columns, undefined stays undefined
6738
- * @example @column.datetimeArray() event_dates?: (Date | null)[];
6743
+ * @example @column.datetimeArray() event_dates?: (string | null)[];
6739
6744
  */
6740
- datetimeArray: (columnName) => createColumnDecorator(
6745
+ datetimeArray: (columnNameOrOptions) => createColumnDecorator(
6746
+ // Element-wise the SAME contract as `@column.datetime()`: a TZ-attached STRING, never a
6747
+ // TZ-shifted JS Date. The array family used to be the one place a `Date` still came back, so
6748
+ // `JSON.stringify` of a row silently re-rendered those elements in UTC — the exact hazard #9
6749
+ // closed for the scalar.
6741
6750
  (v) => {
6742
6751
  if (v === void 0) return void 0;
6743
6752
  if (v === null) return null;
6744
- return castToDatetimeArray(v);
6753
+ return castToDatetimeArray(v).map((d) => d === null ? null : materializeCell(d, "date"));
6745
6754
  },
6746
6755
  // DateTime arrays serialize each Date to ISO string
6747
6756
  (val) => {
@@ -6750,7 +6759,7 @@ var column = Object.assign(
6750
6759
  const mapped = val.map((v) => v === null ? "NULL" : v instanceof Date ? v.toISOString() : String(v));
6751
6760
  return `{${mapped.join(",")}}`;
6752
6761
  }
6753
- )(columnName),
6762
+ )(columnNameOrOptions),
6754
6763
  // ============================================
6755
6764
  // JSON Types
6756
6765
  // ============================================
@@ -6760,16 +6769,15 @@ var column = Object.assign(
6760
6769
  * @example @column.json() metadata?: Record<string, unknown>;
6761
6770
  * @example @column.json<UserSettings>() settings?: UserSettings;
6762
6771
  */
6763
- json: (columnName) => createColumnDecorator(
6772
+ json: (columnNameOrOptions) => createColumnDecorator(
6764
6773
  (v) => {
6765
6774
  if (v === void 0) return void 0;
6766
6775
  if (v === null) return null;
6767
6776
  return castToJson(v);
6768
6777
  },
6769
6778
  serializeJson,
6770
- false,
6771
6779
  "jsonb"
6772
- )(columnName),
6780
+ )(columnNameOrOptions),
6773
6781
  // ============================================
6774
6782
  // UUID Type (PostgreSQL)
6775
6783
  // ============================================
@@ -6798,61 +6806,101 @@ var column = Object.assign(
6798
6806
  if (v === null) return null;
6799
6807
  return String(v);
6800
6808
  },
6801
- void 0,
6802
- // No serialization needed - handled by sqlCast
6803
- true,
6804
- // Skip auto-inference
6809
+ (val) => rejectNonText("uuid", val),
6810
+ // the ::uuid cast handles the rest
6805
6811
  "uuid"
6806
6812
  // SQL type for casting
6807
6813
  )(columnNameOrOptions),
6808
6814
  // ============================================
6809
6815
  // Custom Type Conversion
6810
6816
  // ============================================
6817
+ /**
6818
+ * The driver's value, UNCAST — for a column whose type the library has no cast for (`BYTEA` /
6819
+ * `BLOB` arrives as a driver Buffer, a vendor type as whatever the driver decodes). It states
6820
+ * "this column is passed through", which is a DECLARATION; the removed bare `@column()` was the
6821
+ * absence of one, and produced this same passthrough by accident for every unresolvable type.
6822
+ * @example @column.passthrough() payload?: unknown;
6823
+ */
6824
+ passthrough: (columnNameOrOptions) => createColumnDecorator()(columnNameOrOptions),
6811
6825
  /**
6812
6826
  * Custom type conversion with user-provided function
6813
6827
  * @example @column.custom((v) => String(v).toUpperCase()) status?: string;
6814
6828
  * @example @column.custom((v) => v, (v) => JSON.stringify(v)) data?: MyType; // with serializer
6815
6829
  */
6816
- custom: (castFn, serializeFn, columnName) => createColumnDecorator(castFn, serializeFn)(columnName)
6830
+ custom: (castFn, serializeFn, columnNameOrOptions) => createColumnDecorator(castFn, serializeFn)(columnNameOrOptions)
6817
6831
  }
6818
6832
  );
6819
- function registerRelation(target, propertyKey, type, keysFactory, options) {
6820
- const constructor = target.constructor;
6821
- const relations = Reflect.getMetadata(RELATIONS_KEY, constructor) || [];
6822
- relations.push({
6823
- propertyKey,
6824
- type,
6825
- keysFactory,
6826
- options
6827
- });
6828
- Reflect.defineMetadata(RELATIONS_KEY, relations, constructor);
6833
+ function registerRelation(relations, propertyKey, type, keysFactory, options) {
6834
+ relations.push({ propertyKey, type, keysFactory, options });
6835
+ }
6836
+ function createRelationDecorator(type, keys, options) {
6837
+ return function(targetOrValue, keyOrContext) {
6838
+ if (isStandardContext(keyOrContext)) {
6839
+ const propKey2 = String(keyOrContext.name);
6840
+ registerRelation(standardRelationList(classMetadataBag(keyOrContext, propKey2)), propKey2, type, keys, options);
6841
+ if (typeof keyOrContext.addInitializer !== "function") {
6842
+ throw new Error(
6843
+ `litedbmodel: the standard-decorator context for relation '${propKey2}' has no \`addInitializer\`, so the class field cannot be removed and would shadow the relation getter.`
6844
+ );
6845
+ }
6846
+ keyOrContext.addInitializer(function() {
6847
+ if (Object.prototype.hasOwnProperty.call(this, propKey2)) {
6848
+ delete this[propKey2];
6849
+ }
6850
+ });
6851
+ return;
6852
+ }
6853
+ const propKey = String(keyOrContext);
6854
+ registerRelation(legacyRelationList(targetOrValue.constructor), propKey, type, keys, options);
6855
+ };
6829
6856
  }
6830
6857
  function hasMany(keys, options) {
6831
- return function(target, propertyKey) {
6832
- registerRelation(target, String(propertyKey), "hasMany", keys, options);
6833
- };
6858
+ return createRelationDecorator("hasMany", keys, options);
6834
6859
  }
6835
6860
  function belongsTo(keys, options) {
6836
- return function(target, propertyKey) {
6837
- registerRelation(target, String(propertyKey), "belongsTo", keys, options);
6838
- };
6861
+ return createRelationDecorator("belongsTo", keys, options);
6839
6862
  }
6840
6863
  function hasOne(keys, options) {
6841
- return function(target, propertyKey) {
6842
- registerRelation(target, String(propertyKey), "hasOne", keys, options);
6843
- };
6864
+ return createRelationDecorator("hasOne", keys, options);
6844
6865
  }
6845
6866
  function getRelationMeta(modelClass) {
6846
6867
  return modelClass._relationMeta || Reflect.getMetadata(RELATIONS_KEY, modelClass) || [];
6847
6868
  }
6848
- function model(tableNameOrConstructor, options) {
6869
+ function model(tableNameOrConstructor, optionsOrContext) {
6849
6870
  if (typeof tableNameOrConstructor === "string") {
6850
6871
  const tableName = tableNameOrConstructor;
6851
- return function(constructor) {
6852
- return applyModelDecorator(constructor, tableName, options);
6872
+ const options = optionsOrContext;
6873
+ return function(constructor, context) {
6874
+ return applyToClass(constructor, context, tableName, options);
6875
+ };
6876
+ }
6877
+ return applyToClass(tableNameOrConstructor, optionsOrContext);
6878
+ }
6879
+ function applyToClass(constructor, context, tableName, options) {
6880
+ const members = modelMembers(constructor, context);
6881
+ if (!isStandardContext(context)) return applyModelDecorator(constructor, members, tableName, options);
6882
+ if (typeof context.addInitializer !== "function") {
6883
+ throw new Error(
6884
+ `litedbmodel: the standard-decorator context for '${constructor.name}' has no \`addInitializer\`, so \`@model\` cannot assemble the model after the class is defined.`
6885
+ );
6886
+ }
6887
+ context.addInitializer(function() {
6888
+ applyModelDecorator(this, members, tableName, options);
6889
+ });
6890
+ return constructor;
6891
+ }
6892
+ function modelMembers(constructor, context) {
6893
+ if (isStandardContext(context)) {
6894
+ const bag = classMetadataBag(context, String(constructor.name ?? "model"));
6895
+ return {
6896
+ columns: bag[COLUMNS_KEY] ?? /* @__PURE__ */ new Map(),
6897
+ relations: bag[RELATIONS_KEY] ?? []
6853
6898
  };
6854
6899
  }
6855
- return applyModelDecorator(tableNameOrConstructor);
6900
+ return {
6901
+ columns: Reflect.getMetadata(COLUMNS_KEY, constructor) ?? /* @__PURE__ */ new Map(),
6902
+ relations: Reflect.getMetadata(RELATIONS_KEY, constructor) ?? []
6903
+ };
6856
6904
  }
6857
6905
  function isCompositeKeys(keys) {
6858
6906
  return Array.isArray(keys[0]);
@@ -6872,9 +6920,8 @@ function parseKeys(keys) {
6872
6920
  };
6873
6921
  }
6874
6922
  }
6875
- function applyModelDecorator(constructor, tableName, options) {
6876
- const columns = Reflect.getMetadata(COLUMNS_KEY, constructor) || /* @__PURE__ */ new Map();
6877
- const relations = Reflect.getMetadata(RELATIONS_KEY, constructor) || [];
6923
+ function applyModelDecorator(constructor, members, tableName, options) {
6924
+ const { columns, relations } = members;
6878
6925
  const modelName = constructor.name;
6879
6926
  if (tableName) {
6880
6927
  Object.defineProperty(constructor, "TABLE_NAME", {
@@ -6992,6 +7039,26 @@ function applyModelDecorator(constructor, tableName, options) {
6992
7039
  relationName: propertyKey
6993
7040
  });
6994
7041
  },
7042
+ /**
7043
+ * A relation is a getter, but a class FIELD declaring it still writes to the property. Which
7044
+ * write it is depends on `useDefineForClassFields`: with define semantics (the default from
7045
+ * target ES2022) the field is `defineProperty`-d onto the instance, bypassing this setter — the
7046
+ * standard protocol's initializer removes that shadowing property. With SET semantics (the
7047
+ * default below ES2022) the field ASSIGNS, and an accessor with no setter throws
7048
+ * `Cannot set property … which has only a getter` on EVERY instantiation.
7049
+ *
7050
+ * So: the field's own `undefined` is ignored, and any other value is honoured as an own
7051
+ * property — priming a relation (a loaded graph, a test double) reads back what was set.
7052
+ */
7053
+ set: function(value) {
7054
+ if (value === void 0) return;
7055
+ Object.defineProperty(this, propertyKey, {
7056
+ value,
7057
+ writable: true,
7058
+ enumerable: true,
7059
+ configurable: true
7060
+ });
7061
+ },
6995
7062
  enumerable: true,
6996
7063
  configurable: false
6997
7064
  });
@@ -8054,8 +8121,8 @@ var DBModel = class _DBModel {
8054
8121
  * ```typescript
8055
8122
  * @model('users')
8056
8123
  * class UserModel extends DBModel {
8057
- * @column() id?: number;
8058
- * @column() name?: string;
8124
+ * @column.number() id?: number;
8125
+ * @column.text() name?: string;
8059
8126
  * }
8060
8127
  * export const User = UserModel.asModel();
8061
8128
  * export type User = UserModel;
@@ -8187,15 +8254,14 @@ var DBModel = class _DBModel {
8187
8254
  static _scpColumnTypesCache = /* @__PURE__ */ new WeakMap();
8188
8255
  /**
8189
8256
  * The §4.1 column-type OVERRIDES for the SCP read/write bundle (the F1 `columnTypes` escape hatch).
8190
- * Phase F-2 (#105): a column whose SQL type cannot be derived from the decorator — no explicit
8191
- * `sqlCast` family AND no `baseSqlType` (its TS `design:type` is `Object`, e.g. a NULLABLE
8192
- * `string | null` / `number | null` field: TS emits `design:type = Object` for a union) — would
8193
- * otherwise take the blanket-INTEGER default and THROW `materialize int32` on a live string. Because
8257
+ * A column whose SQL type cannot be derived from its family — `@column.number()`, whose JS `number`
8258
+ * backs an INTEGER just as well as a REAL/DECIMAL (pg returns DECIMAL as a string that an `int32`
8259
+ * materialize REJECTS) — would otherwise take the blanket-INTEGER default and THROW. Because
8194
8260
  * the DBModel read path does its OWN v1 de-box (`_createInstance` → `typeCastFromDB`), the SCP typed
8195
8261
  * read only needs a NON-THROWING, passthrough-safe SQL type for such columns: we pin them to `TEXT`
8196
8262
  * (passthrough — the driver value flows through unchanged, then v1 casts it). Columns WITH a derivable
8197
- * type (explicit family / a non-union `design:type`) keep it. This preserves the v1 read contract
8198
- * exactly (int→number, string→string) while never throwing on an undeterminable nullable column.
8263
+ * type (every family but `@column.number()`) keep it. This preserves the v1 read contract exactly
8264
+ * (int→number, string→string) while never throwing on a column whose width the model did not state.
8199
8265
  * @internal
8200
8266
  */
8201
8267
  static _scpColumnTypes() {
@@ -8207,7 +8273,7 @@ var DBModel = class _DBModel {
8207
8273
  for (const [propKey, m] of meta) {
8208
8274
  const baseSqlType = m.baseSqlType;
8209
8275
  if (m.sqlCast !== void 0) continue;
8210
- if (baseSqlType !== void 0 && baseSqlType !== "INTEGER") continue;
8276
+ if (baseSqlType !== void 0) continue;
8211
8277
  columnTypes[propKey] = "TEXT";
8212
8278
  }
8213
8279
  }
@@ -10426,12 +10492,12 @@ var DBModel = class _DBModel {
10426
10492
  * // Define models using appropriate base class
10427
10493
  * @model('users')
10428
10494
  * class UserModel extends BaseDB {
10429
- * @column() id?: number;
10495
+ * @column.number() id?: number;
10430
10496
  * }
10431
10497
  *
10432
10498
  * @model('articles')
10433
10499
  * class ArticleModel extends CmsDB {
10434
- * @column() id?: number;
10500
+ * @column.number() id?: number;
10435
10501
  * }
10436
10502
  *
10437
10503
  * // Each base class has independent transactions