imodel 0.19.1 → 0.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.19.1
2
+ * imodel v0.19.3
3
3
  * (c) 2019-2026 undefined
4
4
  * @license undefined
5
5
  */
@@ -508,10 +508,126 @@ type FieldDecorator<T> = (val: {
508
508
  }, ctx: ClassAccessorDecoratorContext) => any;
509
509
  type MethodDecorator<T extends Function> = (val: T, ctx: ClassMethodDecoratorContext<any, T>) => any;
510
510
 
511
+ declare class ConnectionError extends Error {
512
+ /**
513
+ * @param {string} code - 自定义通用错误码 (例如 constraint:unique)
514
+ * @param {string} message - 原始错误信息或自定义信息
515
+ */
516
+ constructor(code: string, message: string);
517
+ /** @type {string} */
518
+ code: string;
519
+ }
520
+ /**
521
+ * 非空约束违规
522
+ * 对应 PG 23502 / MySQL 1048
523
+ * 场景:插入数据时,必填字段为空
524
+ */
525
+ declare class NotNullViolationError extends ConnectionError {
526
+ /** @type {string} */
527
+ table: string;
528
+ /** @type {string} */
529
+ column: string;
530
+ }
531
+ /**
532
+ * 唯一性约束违规
533
+ * 对应 PG 23505 / MySQL 1062
534
+ * 支持单字段和多字段组合冲突
535
+ */
536
+ declare class UniqueViolationError extends ConnectionError {
537
+ /**
538
+ * @param {string} table - 表名
539
+ * @param {string | string[]} columns - 冲突的列名(可以是字符串或字符串数组)
540
+ * @param {any} [values] - 导致冲突的值(可选,多字段时为对象或数组)
541
+ */
542
+ constructor(table: string, columns: string | string[], values?: any);
543
+ /** @type {string} */
544
+ table: string;
545
+ /** @type {string[]} */
546
+ columns: string[];
547
+ /** @type {any} */
548
+ values: any;
549
+ }
550
+ /**
551
+ * 外键约束违规
552
+ * 对应 PG 23503 / MySQL 1452
553
+ * 场景:插入了一个不存在的 user_id
554
+ */
555
+ declare class ForeignKeyViolationError extends ConnectionError {
556
+ /**
557
+ * @param {string} table - 当前操作的表
558
+ * @param {string | string[]} columns - 当前操作的外键列
559
+ * @param {string} [referencedTable] - 被引用的父表
560
+ */
561
+ constructor(table: string, columns: string | string[], referencedTable?: string);
562
+ /** @type {string} */
563
+ table: string;
564
+ /** @type {string[]} */
565
+ columns: string[];
566
+ /** @type {string} */
567
+ referencedTable: string;
568
+ }
569
+ /**
570
+ * 检查约束违规
571
+ * 对应 PG 23514
572
+ * 场景:年龄字段要求 > 0,但插入了 -5
573
+ */
574
+ declare class CheckViolationError extends ConnectionError {
575
+ /**
576
+ * @param {string} table - 当前操作的表
577
+ * @param {string} [constraint] - 当前操作的外键列
578
+ */
579
+ constructor(table: string, constraint?: string);
580
+ /** @type {string} */
581
+ table: string;
582
+ /** @type {string} */
583
+ constraint: string;
584
+ }
585
+ /**
586
+ * 无效的类型输入语法错误
587
+ * 对应 PG 22P02
588
+ * 场景:无效的 UUID、日期格式错误等
589
+ */
590
+ declare class InvalidInputSyntaxError extends ConnectionError {
591
+ /**
592
+ * @param {string} message - 目标数据类型
593
+ */
594
+ constructor(message: string);
595
+ }
596
+ /**
597
+ * 操作符不匹配错误
598
+ * 对应 PG 42883
599
+ * 场景:uuid = integer 等类型不兼容的操作
600
+ */
601
+ declare class OperatorMismatchError extends ConnectionError {
602
+ /**
603
+ * @param {string} message
604
+ */
605
+ constructor(message: string);
606
+ }
607
+
608
+ type errors_CheckViolationError = CheckViolationError;
609
+ declare const errors_CheckViolationError: typeof CheckViolationError;
610
+ type errors_ConnectionError = ConnectionError;
611
+ declare const errors_ConnectionError: typeof ConnectionError;
612
+ type errors_ForeignKeyViolationError = ForeignKeyViolationError;
613
+ declare const errors_ForeignKeyViolationError: typeof ForeignKeyViolationError;
614
+ type errors_InvalidInputSyntaxError = InvalidInputSyntaxError;
615
+ declare const errors_InvalidInputSyntaxError: typeof InvalidInputSyntaxError;
616
+ type errors_NotNullViolationError = NotNullViolationError;
617
+ declare const errors_NotNullViolationError: typeof NotNullViolationError;
618
+ type errors_OperatorMismatchError = OperatorMismatchError;
619
+ declare const errors_OperatorMismatchError: typeof OperatorMismatchError;
620
+ type errors_UniqueViolationError = UniqueViolationError;
621
+ declare const errors_UniqueViolationError: typeof UniqueViolationError;
622
+ declare namespace errors {
623
+ export { errors_CheckViolationError as CheckViolationError, errors_ConnectionError as ConnectionError, errors_ForeignKeyViolationError as ForeignKeyViolationError, errors_InvalidInputSyntaxError as InvalidInputSyntaxError, errors_NotNullViolationError as NotNullViolationError, errors_OperatorMismatchError as OperatorMismatchError, errors_UniqueViolationError as UniqueViolationError };
624
+ }
625
+
511
626
  interface Environment<T extends object> {
512
627
  transaction: T | null;
513
628
  isDevelopment: boolean;
514
629
  values: typeof values;
630
+ errors: typeof errors;
515
631
  }
516
632
 
517
633
  /** 特性支持情况 */
@@ -1373,6 +1489,7 @@ interface Options {
1373
1489
  update?: Record<string, SetValue>;
1374
1490
  alias?: string;
1375
1491
  group?: string[];
1492
+ lock?: 'S' | 'X' | null;
1376
1493
  }
1377
1494
  interface Queryable<T extends Fields = Fields> extends TableDefine<T> {
1378
1495
  readonly options: Options;
@@ -1397,6 +1514,7 @@ interface JOIN {
1397
1514
  alias?: string;
1398
1515
  condition?: WhereValue[];
1399
1516
  }
1517
+ type LockType = 'S' | 'X';
1400
1518
  interface SelectItem {
1401
1519
  distinct?: boolean;
1402
1520
  select: [string, Select][];
@@ -1413,6 +1531,7 @@ interface SelectItem {
1413
1531
  offset?: number;
1414
1532
  /** 最大返回数 */
1415
1533
  limit?: number;
1534
+ lock?: LockType | null;
1416
1535
  }
1417
1536
  type GetName = (table?: string) => string;
1418
1537
  interface DBColumn {
@@ -1486,6 +1605,17 @@ interface Skip {
1486
1605
  interface TransactionFn {
1487
1606
  <T>(fn: (t: Connection) => PromiseLike<T> | T): Promise<T>;
1488
1607
  }
1608
+ type TransactionHandlerArray = [
1609
+ promise: TransactionHandler['promise'],
1610
+ commit: TransactionHandler['commit'],
1611
+ rollback: TransactionHandler['rollback']
1612
+ ];
1613
+ interface TransactionHandler extends TransactionHandlerArray {
1614
+ promise: Promise<void>;
1615
+ commit(): Promise<void>;
1616
+ rollback(e?: any): Promise<void>;
1617
+ [Symbol.dispose](): void;
1618
+ }
1489
1619
 
1490
1620
  declare const Destroy: unique symbol;
1491
1621
  interface Destroy {
@@ -1991,9 +2121,10 @@ declare class Connection<E extends {} = {}> {
1991
2121
  transaction<T>(fn: (t: Connection) => PromiseLike<T> | T): Promise<T>;
1992
2122
  /**
1993
2123
  * @overload
1994
- * @returns {[Promise<void>, commit: () => Promise<void>, rollback: (e?: any) => Promise<void>]}
2124
+ * @returns {TransactionHandler}
1995
2125
  */
1996
- transaction(): [Promise<void>, commit: () => Promise<void>, rollback: (e?: any) => Promise<void>];
2126
+ transaction(): TransactionHandler;
2127
+ [Symbol.dispose](): void;
1997
2128
  #private;
1998
2129
  }
1999
2130
 
@@ -2014,8 +2145,12 @@ declare function isPseudo(model: Queryable): boolean;
2014
2145
 
2015
2146
  /** @import { SelectItem } from './types' */
2016
2147
  declare class Subquery {
2017
- /** @returns {SelectItem[]} */
2018
- toSubquery(): SelectItem[];
2148
+ /**
2149
+ *
2150
+ * @param {boolean?} [lockable]
2151
+ * @returns {SelectItem[]}
2152
+ */
2153
+ toSubquery(lockable?: boolean | null): SelectItem[];
2019
2154
  }
2020
2155
 
2021
2156
  type QueryOptions<T extends Fields, TB extends unknown = any> = {
@@ -2082,10 +2217,10 @@ declare class Query<T extends Fields, TB extends unknown = any> extends Subquery
2082
2217
  withDeleted(): this;
2083
2218
  /**
2084
2219
  * 增加排序项
2085
- * @param {...string | boolean | [string, boolean?]} sort
2220
+ * @param {...string | null | boolean | [string, boolean?]} sort
2086
2221
  * @returns {this}
2087
2222
  */
2088
- sort(...sort: (string | boolean | [string, boolean?])[]): this;
2223
+ sort(...sort: (string | null | boolean | [string, boolean?])[]): this;
2089
2224
  /**
2090
2225
  * 设置获取到的最大数量
2091
2226
  * @param {number} [limit]
@@ -2219,6 +2354,11 @@ declare class Query<T extends Fields, TB extends unknown = any> extends Subquery
2219
2354
  * @param {boolean} [distinct]
2220
2355
  */
2221
2356
  distinct(distinct?: boolean): this;
2357
+ /**
2358
+ *
2359
+ * @param {LockType?} [lock]
2360
+ */
2361
+ lock(lock?: LockType | null): this;
2222
2362
  /**
2223
2363
  *
2224
2364
  * @param {object} a
@@ -2674,4 +2814,4 @@ declare function toBool(v: any): boolean;
2674
2814
  declare function setDevelopment(d?: boolean): void;
2675
2815
  declare let isDevelopment: boolean;
2676
2816
 
2677
- export { Build, type ClassDecorator, type Column, type ColumnOptions, Connection, type Constraint, Create, type DBColumn, type DBIndex, type DBTable, Destroy, type Environment, type FieldDecorator, type FieldDefine, type FieldDefineOption, type FieldDefineType, type FieldSpecific, type FieldSpecificValue, type FieldType, type FieldTypeDefine, type FieldValue, type Fields, type FindArg, type FindRange, type GetName, type Hook, type Hooks, type IConnection, type Index, type IndexInfo, type IndexOptions, type JOIN, type Join, type JoinType, type MainFieldType, type MaybePromise, type MethodDecorator, Model, type Options, PseudoDestroy, Query, type QueryOptions, type Queryable, Save, Scene, Select, type SelectItem, SetValue, type Skip, Submodel, type Support, type TableConnect, type TableConnection, type TableDefine, type TableType, type TableValue, type ToFieldType, type ToType, type TransactionFn, type VirtualTable, Where, type WhereExists, type WhereItem, type WhereLike, type WhereOr, type WhereRaw, type WhereValue, type Wheres, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, bindHooks, coefficient, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, hooks, immutable, increment, index, isDevelopment, isPseudo, mergeHooks, model, multiply, now, primary, prop, pseudo, setDevelopment, sort, model as submodel, toBool, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, where, withDeleted };
2817
+ export { Build, CheckViolationError, type ClassDecorator, type Column, type ColumnOptions, Connection, ConnectionError, type Constraint, Create, type DBColumn, type DBIndex, type DBTable, Destroy, type Environment, type FieldDecorator, type FieldDefine, type FieldDefineOption, type FieldDefineType, type FieldSpecific, type FieldSpecificValue, type FieldType, type FieldTypeDefine, type FieldValue, type Fields, type FindArg, type FindRange, ForeignKeyViolationError, type GetName, type Hook, type Hooks, type IConnection, type Index, type IndexInfo, type IndexOptions, InvalidInputSyntaxError, type JOIN, type Join, type JoinType, type LockType, type MainFieldType, type MaybePromise, type MethodDecorator, Model, NotNullViolationError, OperatorMismatchError, type Options, PseudoDestroy, Query, type QueryOptions, type Queryable, Save, Scene, Select, type SelectItem, SetValue, type Skip, Submodel, type Support, type TableConnect, type TableConnection, type TableDefine, type TableType, type TableValue, type ToFieldType, type ToType, type TransactionFn, type TransactionHandler, type TransactionHandlerArray, UniqueViolationError, type VirtualTable, Where, type WhereExists, type WhereItem, type WhereLike, type WhereOr, type WhereRaw, type WhereValue, type Wheres, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, bindHooks, coefficient, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, hooks, immutable, increment, index, isDevelopment, isPseudo, mergeHooks, model, multiply, now, primary, prop, pseudo, setDevelopment, sort, model as submodel, toBool, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, where, withDeleted };
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.19.1
2
+ * imodel v0.19.3
3
3
  * (c) 2019-2026 undefined
4
4
  * @license undefined
5
5
  */
@@ -15,8 +15,12 @@ function setDevelopment(d) {
15
15
 
16
16
  /** @import { SelectItem } from './types' */
17
17
  class Subquery {
18
- /** @returns {SelectItem[]} */
19
- toSubquery() {
18
+ /**
19
+ *
20
+ * @param {boolean?} [lockable]
21
+ * @returns {SelectItem[]}
22
+ */
23
+ toSubquery(lockable) {
20
24
  throw new Error('toSubquery 方法未在子类中实现');
21
25
  }
22
26
  }
@@ -800,31 +804,192 @@ var values = /*#__PURE__*/Object.freeze({
800
804
  uuid: uuid
801
805
  });
802
806
 
807
+ class ConnectionError extends Error {
808
+ /** @type {string} */
809
+
810
+ /**
811
+ * @param {string} code - 自定义通用错误码 (例如 constraint:unique)
812
+ * @param {string} message - 原始错误信息或自定义信息
813
+ */
814
+ constructor(code, message) {
815
+ super(message);
816
+ this.code = code;
817
+ }
818
+ }
819
+ /**
820
+ * 非空约束违规
821
+ * 对应 PG 23502 / MySQL 1048
822
+ * 场景:插入数据时,必填字段为空
823
+ */
824
+ class NotNullViolationError extends ConnectionError {
825
+ /** @type {string} */
826
+
827
+ /** @type {string} */
828
+
829
+ /**
830
+ * @param {string} table - 表名
831
+ * @param {string} column - 列名
832
+ */
833
+ constructor(table, column) {
834
+ const msg = `Column '${column}' in table '${table}' cannot be null`;
835
+ super('constraint:notNull', msg);
836
+ this.table = table;
837
+ this.column = column;
838
+ }
839
+ }
840
+ /**
841
+ * 唯一性约束违规
842
+ * 对应 PG 23505 / MySQL 1062
843
+ * 支持单字段和多字段组合冲突
844
+ */
845
+ class UniqueViolationError extends ConnectionError {
846
+ /** @type {string} */
847
+
848
+ /** @type {string[]} */
849
+
850
+ /** @type {any} */
851
+
852
+ /**
853
+ * @param {string} table - 表名
854
+ * @param {string | string[]} columns - 冲突的列名(可以是字符串或字符串数组)
855
+ * @param {any} [values] - 导致冲突的值(可选,多字段时为对象或数组)
856
+ */
857
+ constructor(table, columns, values = undefined) {
858
+ // 确保 columns 始终是数组,方便后续处理
859
+ const cols = Array.isArray(columns) ? columns : [columns];
860
+ let msg = `Duplicate value for ${cols.join(', ')} in table '${table}'`;
861
+ if (values !== undefined) {
862
+ msg += ` (values: ${JSON.stringify(values)})`;
863
+ }
864
+ super('constraint:unique', msg);
865
+ this.table = table;
866
+ this.columns = cols;
867
+ this.values = values;
868
+ }
869
+ }
870
+ /**
871
+ * 外键约束违规
872
+ * 对应 PG 23503 / MySQL 1452
873
+ * 场景:插入了一个不存在的 user_id
874
+ */
875
+ class ForeignKeyViolationError extends ConnectionError {
876
+ /** @type {string} */
877
+
878
+ /** @type {string[]} */
879
+
880
+ /** @type {string} */
881
+
882
+ /**
883
+ * @param {string} table - 当前操作的表
884
+ * @param {string | string[]} columns - 当前操作的外键列
885
+ * @param {string} [referencedTable] - 被引用的父表
886
+ */
887
+ constructor(table, columns, referencedTable) {
888
+ // 确保 columns 始终是数组,方便后续处理
889
+ const cols = Array.isArray(columns) ? columns : [columns];
890
+ let msg = `Foreign key constraint failed for column ${cols.join(', ')}`;
891
+ msg += ` in table '${table}'`;
892
+ if (referencedTable) {
893
+ msg += ` (references '${referencedTable}')`;
894
+ }
895
+ super('constraint:foreignKey', msg);
896
+ this.table = table;
897
+ this.columns = cols;
898
+ this.referencedTable = referencedTable || '';
899
+ }
900
+ }
901
+ /**
902
+ * 检查约束违规
903
+ * 对应 PG 23514
904
+ * 场景:年龄字段要求 > 0,但插入了 -5
905
+ */
906
+ class CheckViolationError extends ConnectionError {
907
+ /** @type {string} */
908
+
909
+ /** @type {string} */
910
+
911
+ /**
912
+ * @param {string} table - 当前操作的表
913
+ * @param {string} [constraint] - 当前操作的外键列
914
+ */
915
+ constructor(table, constraint) {
916
+ let msg = `Check constraint failed in table '${table}'`;
917
+ if (constraint) {
918
+ msg += ` (${constraint})`;
919
+ }
920
+ super('constraint:check', msg);
921
+ this.table = table;
922
+ this.constraint = constraint || '';
923
+ }
924
+ }
925
+ /**
926
+ * 无效的类型输入语法错误
927
+ * 对应 PG 22P02
928
+ * 场景:无效的 UUID、日期格式错误等
929
+ */
930
+ class InvalidInputSyntaxError extends ConnectionError {
931
+ /**
932
+ * @param {string} message - 目标数据类型
933
+ */
934
+ constructor(message) {
935
+ super('data:type:invalidInputSyntax', message);
936
+ }
937
+ }
938
+ /**
939
+ * 操作符不匹配错误
940
+ * 对应 PG 42883
941
+ * 场景:uuid = integer 等类型不兼容的操作
942
+ */
943
+ class OperatorMismatchError extends ConnectionError {
944
+ /**
945
+ * @param {string} message
946
+ */
947
+ constructor(message) {
948
+ super('syntax:operator:mismatch', message);
949
+ }
950
+ }
951
+
952
+ var errors = /*#__PURE__*/Object.freeze({
953
+ __proto__: null,
954
+ CheckViolationError: CheckViolationError,
955
+ ConnectionError: ConnectionError,
956
+ ForeignKeyViolationError: ForeignKeyViolationError,
957
+ InvalidInputSyntaxError: InvalidInputSyntaxError,
958
+ NotNullViolationError: NotNullViolationError,
959
+ OperatorMismatchError: OperatorMismatchError,
960
+ UniqueViolationError: UniqueViolationError
961
+ });
962
+
803
963
  /** @import { Options, Queryable } from '../types' */
804
964
  /**
805
965
  *
806
- * @param {Queryable} model
966
+ * @param {Partial<Queryable>} queryable
807
967
  * @returns {Options}
808
968
  */
809
- function getOptions(model) {
969
+ function getOptions({
970
+ fields,
971
+ pseudo,
972
+ options: modelOptions
973
+ }) {
810
974
  const options = {
811
- ...model.options
975
+ where: [],
976
+ ...modelOptions
812
977
  };
813
- if (!model.pseudo) {
978
+ if (!pseudo || !fields) {
814
979
  return options;
815
980
  }
816
- if (!(model.pseudo in model.fields)) {
981
+ if (!(pseudo in fields)) {
817
982
  return options;
818
983
  }
819
984
  if (options.range === undeleted) {
820
985
  options.where = [...options.where, {
821
- field: model.pseudo,
986
+ field: pseudo,
822
987
  operator: '<>',
823
988
  value: 0
824
989
  }];
825
990
  } else if (options.range !== withDeleted) {
826
991
  options.where = [...options.where, {
827
- field: model.pseudo,
992
+ field: pseudo,
828
993
  operator: '=',
829
994
  value: 0
830
995
  }];
@@ -2136,6 +2301,7 @@ function toSelectItem(select, fieldColumns) {
2136
2301
  */
2137
2302
  function select2column(columns, fieldColumns, select) {
2138
2303
  /** @type {[string, Select][]} */
2304
+ // eslint-disable-next-line no-nested-ternary
2139
2305
  const list = Array.isArray(select) ? select.map(v => typeof v === 'string' ? [v, {
2140
2306
  field: v
2141
2307
  }] : v) : select ? Object.entries(select) : [];
@@ -2178,11 +2344,14 @@ function getDefaultSort(columns, noColumn) {
2178
2344
 
2179
2345
  /**
2180
2346
  *
2181
- * @param {[field: string, desc: boolean][]} sort
2182
- * @param {Record<string, string>?} fieldColumns
2347
+ * @param {[field: string, desc: boolean][]?} [sort]
2348
+ * @param {Record<string, string>?} [fieldColumns]
2183
2349
  * @returns {[field: string, desc: boolean][]}
2184
2350
  */
2185
2351
  function sort2column(sort, fieldColumns) {
2352
+ if (!sort) {
2353
+ return [];
2354
+ }
2186
2355
  if (!fieldColumns) {
2187
2356
  return sort;
2188
2357
  }
@@ -2190,7 +2359,7 @@ function sort2column(sort, fieldColumns) {
2190
2359
  }
2191
2360
 
2192
2361
  /** @import Connection from './index.mjs' */
2193
- /** @import { Environment, FieldDefine, Fields, Skip, TableDefine, FieldType, TableConnection, SelectItem } from '../types' */
2362
+ /** @import { Environment, FieldDefine, Fields, Skip, TableDefine, FieldType, TableConnection, SelectItem, LockType } from '../types' */
2194
2363
  /**
2195
2364
  * @param {string[] | boolean | null} [items]
2196
2365
  */
@@ -2248,10 +2417,11 @@ function getSelectFields(select, columns, tableFields, baseFieldSet) {
2248
2417
  * @param {[string, FieldDefine<TableDefine>][]} fields
2249
2418
  * @param {Set<string>} baseParentFieldSet
2250
2419
  * @param {Environment} env
2420
+ * @param {LockType?} [lock]
2251
2421
  * @param {Record<string, string[] | undefined>?} [returnFields]
2252
2422
  * @returns {Promise<any[]>}
2253
2423
  */
2254
- async function findSub(conn, list, fields, baseParentFieldSet, env, returnFields) {
2424
+ async function findSub(conn, list, fields, baseParentFieldSet, env, lock, returnFields) {
2255
2425
  if (!list.length) {
2256
2426
  return list;
2257
2427
  }
@@ -2291,13 +2461,14 @@ async function findSub(conn, list, fields, baseParentFieldSet, env, returnFields
2291
2461
  columns,
2292
2462
  select,
2293
2463
  where,
2294
- sort: getDefaultSort(columns, noColumn) || [],
2464
+ sort: getDefaultSort(columns, noColumn),
2295
2465
  group: [],
2296
- having: []
2466
+ having: [],
2467
+ lock
2297
2468
  }];
2298
2469
  /** @type {PromiseLike<object[]>} */
2299
2470
  const result = table && typeof table === 'object' ? table.select(env, conn, selects) : conn.select(env, selects);
2300
- let promise = result.then(list => findSub(conn, list, tableFields, baseFieldSet, env, fieldMap));
2471
+ let promise = result.then(list => findSub(conn, list, tableFields, baseFieldSet, env, lock, fieldMap));
2301
2472
  promises.push(array ? promise.then(subList => {
2302
2473
  for (const item of list) {
2303
2474
  item[key] = subList.filter(createFilter(item, mapList));
@@ -2332,7 +2503,8 @@ async function search(conn, that, p, env, returnFields, skip) {
2332
2503
  const {
2333
2504
  sort,
2334
2505
  offset,
2335
- limit
2506
+ limit,
2507
+ lock
2336
2508
  } = options;
2337
2509
  const [columns, fieldColumns, tableFields] = toColumns(fields);
2338
2510
  const where = where2column(options.where, fieldColumns);
@@ -2344,7 +2516,7 @@ async function search(conn, that, p, env, returnFields, skip) {
2344
2516
  const baseFieldSet = new Set(Object.keys(fieldColumns || columns));
2345
2517
  const selected = fieldMap && getSelectFields(fieldMap[''] || [], columns, tableFields, baseFieldSet) || null;
2346
2518
  const select = select2column(columns, fieldColumns, selected);
2347
- const sorted = sort?.length ? sort2column(sort, fieldColumns) : getDefaultSort(columns);
2519
+ const sorted = sort2column(sort, fieldColumns);
2348
2520
  /** @type {SelectItem[]} */
2349
2521
  const selects = [{
2350
2522
  table: typeof table === 'string' ? table : '',
@@ -2353,16 +2525,17 @@ async function search(conn, that, p, env, returnFields, skip) {
2353
2525
  where,
2354
2526
  offset,
2355
2527
  limit,
2356
- sort: sorted || [],
2528
+ sort: sorted,
2357
2529
  group: [],
2358
- having: []
2530
+ having: [],
2531
+ lock
2359
2532
  }];
2360
2533
  const list = table && typeof table === 'object' ? await table.select(env, conn, selects) : await conn.select(env, selects);
2361
2534
  if (!list.length) {
2362
2535
  return [];
2363
2536
  }
2364
2537
  if (returnFields) {
2365
- await findSub(conn, list, tableFields, baseFieldSet, env, fieldMap);
2538
+ await findSub(conn, list, tableFields, baseFieldSet, env, lock, fieldMap);
2366
2539
  }
2367
2540
  return list;
2368
2541
  }
@@ -2384,10 +2557,10 @@ async function first(conn, that, p, env, skip) {
2384
2557
  table,
2385
2558
  fields
2386
2559
  } = p;
2387
- // @ts-ignore
2388
2560
  const options = getOptions(p);
2389
2561
  const {
2390
- sort
2562
+ sort,
2563
+ lock
2391
2564
  } = options;
2392
2565
  const [columns, fieldColumns, tableFields] = toColumns(fields);
2393
2566
  const where = where2column(options.where, fieldColumns);
@@ -2402,14 +2575,15 @@ async function first(conn, that, p, env, skip) {
2402
2575
  columns,
2403
2576
  where,
2404
2577
  limit: 1,
2405
- sort: sort?.length ? sort2column(sort, fieldColumns) || [] : getDefaultSort(columns) || [],
2578
+ sort: sort2column(sort, fieldColumns),
2406
2579
  select: select2column(columns, fieldColumns),
2407
2580
  group: [],
2408
- having: []
2581
+ having: [],
2582
+ lock
2409
2583
  }];
2410
2584
  const list = table && typeof table === 'object' ? await table.select(env, conn, selects) : await conn.select(env, selects);
2411
2585
  const baseFieldSet = new Set(Object.keys(fieldColumns || columns));
2412
- const [item] = await findSub(conn, list, tableFields, baseFieldSet, env);
2586
+ const [item] = await findSub(conn, list, tableFields, baseFieldSet, env, lock);
2413
2587
  if (!item) {
2414
2588
  return null;
2415
2589
  }
@@ -2581,9 +2755,10 @@ function table2db(tables) {
2581
2755
  /**
2582
2756
  * @template {Fields} T
2583
2757
  * @param {Queryable<T>} queryable
2758
+ * @param {boolean?} [lockable]
2584
2759
  * @returns {SelectItem[]}
2585
2760
  */
2586
- function toSelect(queryable) {
2761
+ function toSelect(queryable, lockable) {
2587
2762
  const [columns, fieldColumns, tableFields] = toColumns(queryable.fields);
2588
2763
  const {
2589
2764
  table
@@ -2591,7 +2766,8 @@ function toSelect(queryable) {
2591
2766
  const options = getOptions(queryable);
2592
2767
  const {
2593
2768
  offset,
2594
- limit
2769
+ limit,
2770
+ lock
2595
2771
  } = options;
2596
2772
  const where = where2column(options.where, fieldColumns);
2597
2773
  where.push(...getFixedValueWhere(columns));
@@ -2607,16 +2783,24 @@ function toSelect(queryable) {
2607
2783
  limit,
2608
2784
  where,
2609
2785
  select,
2610
- sort: sort?.length ? sort2column(sort, fieldColumns) : getDefaultSort(columns),
2786
+ sort: sort2column(sort, fieldColumns),
2611
2787
  group: options.group || [],
2612
2788
  distinct: Boolean(options.distinct),
2613
- having: []
2789
+ having: [],
2790
+ lock: lockable ? lock : null
2614
2791
  }];
2615
2792
  return result;
2616
2793
  }
2617
2794
 
2618
2795
  /* eslint-disable max-lines */
2619
2796
  /* eslint-disable max-len */
2797
+ /** @import { Wheres } from '../Where.mjs' */
2798
+ /** @import { SetValue } from '../set.mjs' */
2799
+ /** @import { Environment, TableConnection, TableValue } from '../types' */
2800
+ /** @import { Fields, FieldType, FieldValue, IndexOptions, MainFieldType, TableDefine } from '../types/table' */
2801
+ /** @import { Queryable } from '../types/options' */
2802
+ /** @import { Column, ColumnOptions } from '../types/column' */
2803
+ /** @import { DBTable, IConnection, Skip, TableConnect, TableType, TransactionHandler } from '../types/connection' */
2620
2804
  /**
2621
2805
  *
2622
2806
  * @param {Fields<keyof FieldType>} fields
@@ -2675,7 +2859,8 @@ class Connection {
2675
2859
  this.#env = {
2676
2860
  isDevelopment,
2677
2861
  values,
2678
- transaction
2862
+ transaction,
2863
+ errors
2679
2864
  };
2680
2865
  }
2681
2866
  /**
@@ -3302,11 +3487,11 @@ class Connection {
3302
3487
  }, data, keys, conflictKeys, conflictSet) {
3303
3488
  const [columns, fieldColumns, baseFields] = toColumns(fields);
3304
3489
  const isArray = Array.isArray(data);
3305
- const insertValues = (isArray ? data : [data]
3490
+ const insertValues = await Promise.all((isArray ? data : [data]
3306
3491
  // @ts-ignore
3307
3492
  ).map(d => getInsertValue(d, columns, fieldColumns, {
3308
3493
  uncreatable: true
3309
- }));
3494
+ })));
3310
3495
  if (!insertValues.length) {
3311
3496
  throw new Error();
3312
3497
  }
@@ -3467,7 +3652,7 @@ class Connection {
3467
3652
  table
3468
3653
  } = queryable;
3469
3654
  const conn = await this.#getTableConnection(queryable.connect);
3470
- const selects = toSelect(queryable);
3655
+ const selects = toSelect(queryable, true);
3471
3656
  const result = table && typeof table === 'object' ? await table.select(this.#env, conn, selects) : await conn.select(this.#env, selects);
3472
3657
  return result;
3473
3658
  }
@@ -3636,6 +3821,9 @@ class Connection {
3636
3821
  }
3637
3822
  return false;
3638
3823
  }
3824
+ [Symbol.dispose]() {
3825
+ this.abort();
3826
+ }
3639
3827
  /**
3640
3828
  * @param {string} table
3641
3829
  * @returns {Promise<DBTable?>}
@@ -3676,12 +3864,19 @@ class Connection {
3676
3864
  */
3677
3865
  /**
3678
3866
  * @overload
3679
- * @returns {[Promise<void>, commit: () => Promise<void>, rollback: (e?: any) => Promise<void>]}
3867
+ * @returns {TransactionHandler}
3680
3868
  */
3681
3869
  /**
3682
3870
  *
3683
- * @param {(t: Connection, signal: AbortSignal) => any} [fn]
3684
- * @returns {any}
3871
+ * @template T
3872
+ * @param {(t: Connection, signal: AbortSignal) => PromiseLike<T> | T} [fn]
3873
+ * @returns {Promise<T> | TransactionHandler}
3874
+ */
3875
+ /**
3876
+ *
3877
+ * @template T
3878
+ * @param {(t: Connection, signal: AbortSignal) => PromiseLike<T> | T} [fn]
3879
+ * @returns {Promise<T> | TransactionHandler}
3685
3880
  */
3686
3881
  transaction(fn) {
3687
3882
  if (typeof fn === 'function') {
@@ -3711,13 +3906,24 @@ class Connection {
3711
3906
  }
3712
3907
  return donePromise;
3713
3908
  }).then(() => {}, () => {});
3714
- return [abortedPromise, async () => {
3909
+ const handler = /** @type {TransactionHandler} */[abortedPromise, () => {
3715
3910
  commit();
3716
3911
  return dbiPromise;
3717
- }, async () => {
3718
- rollback();
3912
+ }, e => {
3913
+ rollback(e);
3719
3914
  return dbiPromise;
3720
3915
  }];
3916
+ handler.promise = abortedPromise;
3917
+ handler.commit = () => {
3918
+ commit();
3919
+ return dbiPromise;
3920
+ };
3921
+ handler.rollback = e => {
3922
+ rollback(e);
3923
+ return dbiPromise;
3924
+ };
3925
+ handler[Symbol.dispose] = commit;
3926
+ return handler;
3721
3927
  }
3722
3928
  }
3723
3929
 
@@ -4978,7 +5184,7 @@ function toBool(v) {
4978
5184
  return !falseValues.has(v.toLowerCase());
4979
5185
  }
4980
5186
 
4981
- /** @import { FieldDefine, Fields, Options, Queryable, Select, TableConnect, VirtualTable } from './types' */
5187
+ /** @import { FieldDefine, Fields, LockType, Options, Queryable, Select, TableConnect, VirtualTable } from './types' */
4982
5188
  /** @import { WhereValue, Wheres } from './Where.mjs' */
4983
5189
  /**
4984
5190
  *
@@ -5081,8 +5287,9 @@ class Query extends Subquery {
5081
5287
  get options() {
5082
5288
  return this.#options;
5083
5289
  }
5084
- toSubquery() {
5085
- return toSelect(this);
5290
+ /** @param {boolean?} [lockable] */
5291
+ toSubquery(lockable) {
5292
+ return toSelect(this, lockable);
5086
5293
  }
5087
5294
  /**
5088
5295
  * @param {QueryOptions<T, TB> | Query<T, TB>} options
@@ -5158,7 +5365,7 @@ class Query extends Subquery {
5158
5365
  }
5159
5366
  /**
5160
5367
  * 增加排序项
5161
- * @param {...string | boolean | [string, boolean?]} sort
5368
+ * @param {...string | null | boolean | [string, boolean?]} sort
5162
5369
  * @returns {this}
5163
5370
  */
5164
5371
  sort(...sort) {
@@ -5175,10 +5382,19 @@ class Query extends Subquery {
5175
5382
  desc = value[1] === true;
5176
5383
  [value] = value;
5177
5384
  }
5178
- if (typeof value !== 'string') {
5385
+ if (typeof value === 'string') {
5386
+ list.push([value, desc]);
5387
+ continue;
5388
+ }
5389
+ if (value !== null) {
5179
5390
  continue;
5180
5391
  }
5181
- list.push([value, desc]);
5392
+ /** @type {[string, boolean, number][]} */
5393
+ const sorted = Object.entries(this.fields).map(([f, d]) => [f, (d.sort || 0) < 0, Math.abs(d.sort || 0)]);
5394
+ const sortFields = sorted.filter(v => v[2]).sort(([,, a], [,, b]) => a - b);
5395
+ for (const [field, desc] of sortFields) {
5396
+ list.push([field, desc]);
5397
+ }
5182
5398
  }
5183
5399
  if (list.length) {
5184
5400
  this.#options.sort = [...(this.#options.sort || []), ...list];
@@ -5499,6 +5715,15 @@ class Query extends Subquery {
5499
5715
  this.#options.distinct = Boolean(distinct);
5500
5716
  return this;
5501
5717
  }
5718
+ /**
5719
+ *
5720
+ * @param {LockType?} [lock]
5721
+ */
5722
+ lock(lock) {
5723
+ const type = typeof lock === 'string' && lock.toUpperCase() || null;
5724
+ this.#options.lock = type === 'S' || type === 'X' ? type : null;
5725
+ return this;
5726
+ }
5502
5727
  }
5503
5728
 
5504
5729
  /** @import { Fields, IndexInfo, FieldDefine } from './types' */
@@ -6833,4 +7058,4 @@ function field(type, {
6833
7058
  };
6834
7059
  }
6835
7060
 
6836
- export { Build, Connection, Create, Destroy, Model, PseudoDestroy, Query, Save, Scene, Submodel, Where, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, bindHooks, coefficient, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, hooks, immutable, increment, index, isDevelopment, isPseudo, mergeHooks, model, multiply, now, primary, prop, pseudo, setDevelopment, sort, model as submodel, toBool, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, where, withDeleted };
7061
+ export { Build, CheckViolationError, Connection, ConnectionError, Create, Destroy, ForeignKeyViolationError, InvalidInputSyntaxError, Model, NotNullViolationError, OperatorMismatchError, PseudoDestroy, Query, Save, Scene, Submodel, UniqueViolationError, Where, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, bindHooks, coefficient, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, hooks, immutable, increment, index, isDevelopment, isPseudo, mergeHooks, model, multiply, now, primary, prop, pseudo, setDevelopment, sort, model as submodel, toBool, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, where, withDeleted };
package/migrate.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.19.1
2
+ * imodel v0.19.3
3
3
  * (c) 2019-2026 undefined
4
4
  * @license undefined
5
5
  */
package/migrate.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.19.1
2
+ * imodel v0.19.3
3
3
  * (c) 2019-2026 undefined
4
4
  * @license undefined
5
5
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imodel",
3
- "version": "0.19.1",
3
+ "version": "0.19.3",
4
4
  "main": "index.mjs",
5
5
  "type": "module",
6
6
  "repository": {