imodel 0.5.1 → 0.6.0

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.5.1
2
+ * imodel v0.6.0
3
3
  * (c) 2019-2025 undefined
4
4
  * @license undefined
5
5
  */
@@ -686,7 +686,17 @@ interface IndexInfo extends IndexOptions {
686
686
  interface Index extends IndexInfo {
687
687
  name: string;
688
688
  }
689
- interface TableDefine<T extends Fields = Fields, TT extends string | VirtualTable | void = string | VirtualTable | void> {
689
+ interface Hooks<T extends Record<string, any> = Record<string, any>> {
690
+ beforeCreateMany(conn: Connection, records: Record<string, any>[]): PromiseLike<void | Record<string, any>[]> | void | Record<string, any>[];
691
+ afterCreateMany(conn: Connection, records: Record<string, any>[]): PromiseLike<void> | void;
692
+ beforeCreate(conn: Connection, record: Record<string, any>): PromiseLike<void | Record<string, any>> | void | Record<string, any>;
693
+ afterCreate(conn: Connection, record: Record<string, any>): PromiseLike<void> | void;
694
+ beforeUpdate(conn: Connection, record: Record<string, any>, set: Record<string, any>): PromiseLike<void | Record<string, any>> | void | Record<string, any>;
695
+ afterUpdate(conn: Connection, record: Record<string, any>, oldRecord: Record<string, any>): PromiseLike<void> | void;
696
+ beforeDelete(conn: Connection, record: T, update: Record<string, any> | null): PromiseLike<void> | void;
697
+ afterDelete(conn: Connection, record: T, update: Record<string, any> | null): PromiseLike<void> | void;
698
+ }
699
+ interface TableDefine<T extends Fields = Fields, TT extends string | VirtualTable | undefined = string | VirtualTable | undefined> {
690
700
  /** 字段信息 */
691
701
  readonly fields: T;
692
702
  /** 表名 */
@@ -695,6 +705,8 @@ interface TableDefine<T extends Fields = Fields, TT extends string | VirtualTabl
695
705
  readonly pseudo?: string;
696
706
  /** 索引信息 */
697
707
  readonly indexes?: readonly IndexInfo[];
708
+ /** 钩子 */
709
+ readonly hooks?: Partial<Hooks>;
698
710
  }
699
711
 
700
712
  type deleted = typeof deleted;
@@ -863,7 +875,7 @@ interface Save {
863
875
  }
864
876
  declare const Create: unique symbol;
865
877
  interface Create<T = unknown> {
866
- [Create](connection: Connection, data: object, run: (data: any) => Promise<object>, table: TableDefine): PromiseLike<T>;
878
+ [Create](connection: Connection, data: object, run: (data: any, that: any) => Promise<object>, table: TableDefine): PromiseLike<T>;
867
879
  }
868
880
  declare const Build: unique symbol;
869
881
  interface Build<T = unknown> {
@@ -1549,7 +1561,7 @@ declare function defaultValue<T>(value: T): FieldDecorator<T>;
1549
1561
  * @param {string} table
1550
1562
  * @returns {ClassDecorator}
1551
1563
  */
1552
- declare function submodel(table: string): ClassDecorator;
1564
+ declare function model(table: string): ClassDecorator;
1553
1565
  /**
1554
1566
  * @template {Record<string, any>} T
1555
1567
  * @overload
@@ -1559,7 +1571,7 @@ declare function submodel(table: string): ClassDecorator;
1559
1571
  * @param {FieldDefineOption} [options]
1560
1572
  * @returns {FieldDecorator<T[]>}
1561
1573
  */
1562
- declare function submodel<T extends Record<string, any>>(type: TableDefine & {
1574
+ declare function model<T extends Record<string, any>>(type: TableDefine & {
1563
1575
  new (...a: any): T;
1564
1576
  }, constraints: Record<string, Constraint>, array: true, options?: FieldDefineOption | undefined): FieldDecorator<T[]>;
1565
1577
  /**
@@ -1571,7 +1583,7 @@ declare function submodel<T extends Record<string, any>>(type: TableDefine & {
1571
1583
  * @param {FieldDefineOption} [options]
1572
1584
  * @returns {FieldDecorator<T>}
1573
1585
  */
1574
- declare function submodel<T extends Record<string, any>>(type: TableDefine & {
1586
+ declare function model<T extends Record<string, any>>(type: TableDefine & {
1575
1587
  new (...a: any): T;
1576
1588
  }, constraints: Record<string, Constraint>, array?: false | undefined, options?: FieldDefineOption | undefined): FieldDecorator<T>;
1577
1589
  /**
@@ -1582,7 +1594,7 @@ declare function submodel<T extends Record<string, any>>(type: TableDefine & {
1582
1594
  * @param {FieldDefineOption} [options]
1583
1595
  * @returns {FieldDecorator<T>}
1584
1596
  */
1585
- declare function submodel<T extends Record<string, any>>(type: TableDefine & {
1597
+ declare function model<T extends Record<string, any>>(type: TableDefine & {
1586
1598
  new (...a: any): T;
1587
1599
  }, constraints: Record<string, Constraint>, options?: FieldDefineOption | undefined): FieldDecorator<T>;
1588
1600
  /**
@@ -1595,7 +1607,7 @@ declare function submodel<T extends Record<string, any>>(type: TableDefine & {
1595
1607
  * @param {FieldDefineOption} [options]
1596
1608
  * @returns {FieldDecorator<A extends false ? T : A extends true ? T[] : T | T[]>}
1597
1609
  */
1598
- declare function submodel<T extends Record<string, any>, A extends boolean = boolean>(type: TableDefine & {
1610
+ declare function model<T extends Record<string, any>, A extends boolean = boolean>(type: TableDefine & {
1599
1611
  new (...a: any): T;
1600
1612
  }, constraints: Record<string, Constraint>, array: A, options?: FieldDefineOption | undefined): FieldDecorator<A extends false ? T : A extends true ? T[] : T | T[]>;
1601
1613
  /**
@@ -1607,7 +1619,7 @@ declare function submodel<T extends Record<string, any>, A extends boolean = boo
1607
1619
  * @param {FieldDefineOption} [options]
1608
1620
  * @returns {FieldDecorator<T[]>}
1609
1621
  */
1610
- declare function submodel<T extends Record<string, any>>(type: TableDefine, constraints: Record<string, Constraint>, array: true, options?: FieldDefineOption | undefined): FieldDecorator<T[]>;
1622
+ declare function model<T extends Record<string, any>>(type: TableDefine, constraints: Record<string, Constraint>, array: true, options?: FieldDefineOption | undefined): FieldDecorator<T[]>;
1611
1623
  /**
1612
1624
  * @template {object} T
1613
1625
  * @overload
@@ -1617,7 +1629,7 @@ declare function submodel<T extends Record<string, any>>(type: TableDefine, cons
1617
1629
  * @param {FieldDefineOption} [options]
1618
1630
  * @returns {FieldDecorator<T>}
1619
1631
  */
1620
- declare function submodel<T extends Record<string, any>>(type: TableDefine, constraints: Record<string, Constraint>, array?: false | undefined, options?: FieldDefineOption | undefined): FieldDecorator<T>;
1632
+ declare function model<T extends Record<string, any>>(type: TableDefine, constraints: Record<string, Constraint>, array?: false | undefined, options?: FieldDefineOption | undefined): FieldDecorator<T>;
1621
1633
  /**
1622
1634
  * @template {object} T
1623
1635
  * @overload
@@ -1626,7 +1638,7 @@ declare function submodel<T extends Record<string, any>>(type: TableDefine, cons
1626
1638
  * @param {FieldDefineOption} [options]
1627
1639
  * @returns {FieldDecorator<T>}
1628
1640
  */
1629
- declare function submodel<T extends Record<string, any>>(type: TableDefine, constraints: Record<string, Constraint>, options?: FieldDefineOption | undefined): FieldDecorator<T>;
1641
+ declare function model<T extends Record<string, any>>(type: TableDefine, constraints: Record<string, Constraint>, options?: FieldDefineOption | undefined): FieldDecorator<T>;
1630
1642
  /**
1631
1643
  * @template {object} T
1632
1644
  * @template {boolean} [A=boolean]
@@ -1637,7 +1649,7 @@ declare function submodel<T extends Record<string, any>>(type: TableDefine, cons
1637
1649
  * @param {FieldDefineOption} [options]
1638
1650
  * @returns {FieldDecorator<A extends false ? T :A extends true ? T[] : T | T[]>}
1639
1651
  */
1640
- declare function submodel<T extends Record<string, any>, A extends boolean = boolean>(type: TableDefine, constraints: Record<string, Constraint>, array: A, options?: FieldDefineOption | undefined): FieldDecorator<A extends false ? T : A extends true ? T[] : T | T[]>;
1652
+ declare function model<T extends Record<string, any>, A extends boolean = boolean>(type: TableDefine, constraints: Record<string, Constraint>, array: A, options?: FieldDefineOption | undefined): FieldDecorator<A extends false ? T : A extends true ? T[] : T | T[]>;
1641
1653
 
1642
1654
  /**
1643
1655
  * @param {number} [size]
@@ -1655,11 +1667,29 @@ declare function updating(size?: number): FieldDecorator<Date | null>;
1655
1667
  */
1656
1668
  declare function deleting(size?: number): FieldDecorator<Date | null>;
1657
1669
 
1670
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeCreateMany']>} */
1671
+ declare const beforeCreateMany: (insert?: boolean) => MethodDecorator<Hooks["beforeCreateMany"]>;
1672
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterCreateMany']>} */
1673
+ declare const afterCreateMany: (insert?: boolean) => MethodDecorator<Hooks["afterCreateMany"]>;
1674
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeCreate']>} */
1675
+ declare const beforeCreate: (insert?: boolean) => MethodDecorator<Hooks["beforeCreate"]>;
1676
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterCreate']>} */
1677
+ declare const afterCreate: (insert?: boolean) => MethodDecorator<Hooks["afterCreate"]>;
1678
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeUpdate']>} */
1679
+ declare const beforeUpdate: (insert?: boolean) => MethodDecorator<Hooks["beforeUpdate"]>;
1680
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterUpdate']>} */
1681
+ declare const afterUpdate: (insert?: boolean) => MethodDecorator<Hooks["afterUpdate"]>;
1682
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeDelete']>} */
1683
+ declare const beforeDelete: (insert?: boolean) => MethodDecorator<Hooks["beforeDelete"]>;
1684
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterDelete']>} */
1685
+ declare const afterDelete: (insert?: boolean) => MethodDecorator<Hooks["afterDelete"]>;
1686
+
1658
1687
  type ClassDecorator = (val: Function, ctx: ClassDecoratorContext) => any;
1659
1688
  type FieldDecorator<T> = (val: {
1660
1689
  get(): T;
1661
1690
  set(v: T): void;
1662
1691
  }, ctx: ClassAccessorDecoratorContext) => any;
1692
+ type MethodDecorator<T extends Function> = (val: T, ctx: ClassMethodDecoratorContext<any, T>) => any;
1663
1693
 
1664
1694
  type QueryOptions<T extends Fields, TB extends unknown = any> = {
1665
1695
  table?: string | VirtualTable<object> | undefined;
@@ -1694,8 +1724,9 @@ declare class Query<T extends Fields, TB extends unknown = any> implements Query
1694
1724
  static table<F extends Fields, T_1 extends new (o: QueryOptions<F>) => Query<F>>(this: T_1, table: string, fields: F, pseudo?: string): InstanceType<T_1>;
1695
1725
  /**
1696
1726
  * @param {QueryOptions<T, TB> | Query<T, TB>} options
1727
+ * @param {boolean} [noBuild]
1697
1728
  */
1698
- constructor(options: QueryOptions<T, TB> | Query<T, TB>);
1729
+ constructor(options: QueryOptions<T, TB> | Query<T, TB>, noBuild?: boolean);
1699
1730
  /** @readonly @type {string | VirtualTable | undefined} */
1700
1731
  readonly table: string | VirtualTable | undefined;
1701
1732
  /** @readonly @type {T} */
@@ -1920,6 +1951,7 @@ declare class Model implements Save, Destroy, PseudoDestroy {
1920
1951
  static readonly fields: Fields;
1921
1952
  /** @readonly @type {readonly IndexInfo[]} */
1922
1953
  static readonly indexes: readonly IndexInfo[];
1954
+ static get hooks(): Readonly<Partial<Hooks<Record<string, any>>>>;
1923
1955
  /**
1924
1956
  * @template {typeof Model} T
1925
1957
  * @this {T}
@@ -1940,10 +1972,10 @@ declare class Model implements Save, Destroy, PseudoDestroy {
1940
1972
  * @this {T}
1941
1973
  * @param {Connection} t
1942
1974
  * @param {object} p
1943
- * @param {(data: object) => Promise<object>} run
1975
+ * @param {(data: object, that: any) => Promise<object>} run
1944
1976
  * @returns {Promise<InstanceType<T>>}
1945
1977
  */
1946
- static [Create]<T extends typeof Model>(this: T, t: Connection, p: object, run: (data: object) => Promise<object>): Promise<InstanceType<T>>;
1978
+ static [Create]<T extends typeof Model>(this: T, t: Connection, p: object, run: (data: object, that: any) => Promise<object>): Promise<InstanceType<T>>;
1947
1979
  /**
1948
1980
  * @template {typeof Model} T
1949
1981
  * @this {T}
@@ -2271,4 +2303,4 @@ declare function field<T extends MainFieldType>(type: T, options: {
2271
2303
  declare function setDevelopment(d?: boolean): void;
2272
2304
  declare let isDevelopment: boolean;
2273
2305
 
2274
- 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 IConnection, type Index, type IndexInfo, type IndexOptions, type Join, type JoinType, type MainFieldType, type MaybePromise, Model, type Options, PseudoDestroy, Query, type QueryOptions, type Queryable, Save, Scene, Select, SetValue, type Skip, Submodel, type Support, type TableDefine, type TableType, type ToFieldType, type ToType, type TransactionFn, type VirtualTable, Where, type WhereItem, type WhereLike, type WhereOr, type WhereRaw, type WhereValue, type Wheres, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, immutable, increment, index, isDevelopment, isPseudo, submodel as model, multiply, now, primary, prop, pseudo, setDevelopment, sort, submodel, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, withDeleted };
2306
+ 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 Hooks, type IConnection, type Index, type IndexInfo, type IndexOptions, type Join, type JoinType, type MainFieldType, type MaybePromise, type MethodDecorator, Model, type Options, PseudoDestroy, Query, type QueryOptions, type Queryable, Save, Scene, Select, SetValue, type Skip, Submodel, type Support, type TableDefine, type TableType, type ToFieldType, type ToType, type TransactionFn, type VirtualTable, Where, type WhereItem, type WhereLike, type WhereOr, type WhereRaw, type WhereValue, type Wheres, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, immutable, increment, index, isDevelopment, isPseudo, model, multiply, now, primary, prop, pseudo, setDevelopment, sort, model as submodel, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, withDeleted };
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.5.1
2
+ * imodel v0.6.0
3
3
  * (c) 2019-2025 undefined
4
4
  * @license undefined
5
5
  */
@@ -1822,18 +1822,63 @@ class Connection {
1822
1822
  * @returns {Promise<object[] | (T extends Build<infer R> ? R : object)>}
1823
1823
  */
1824
1824
  async create(model, p, skip) {
1825
- if (Array.isArray(p) || typeof model[Create] !== 'function') {
1825
+ const {
1826
+ beforeCreateMany,
1827
+ afterCreateMany,
1828
+ beforeCreate,
1829
+ afterCreate
1830
+ } = model.hooks || {};
1831
+ const isArray = Array.isArray(p);
1832
+ const list = [p].flat(Infinity).filter(Boolean);
1833
+ if (!list.length) {
1834
+ // @ts-ignore
1835
+ return isArray ? [] : null;
1836
+ }
1837
+ if (list.length !== 1) {
1826
1838
  const modelBuild = model[Build];
1827
1839
  const md = typeof modelBuild === 'function' ? modelBuild.bind(model) : undefined;
1828
1840
  const conn = await this.#getConnection();
1829
- return create(conn, this.#env, model, p, skip, md);
1841
+ let setData = list;
1842
+ if (typeof beforeCreateMany === 'function') {
1843
+ // @ts-ignore
1844
+ setData = (await beforeCreateMany.call(setData, this, setData)) || setData;
1845
+ }
1846
+ const r = await create(conn, this.#env, model, setData, skip, md);
1847
+ if (typeof afterCreateMany === 'function') {
1848
+ await afterCreateMany.call(r, this, r);
1849
+ }
1850
+ return r;
1851
+ }
1852
+ const [data] = list;
1853
+ if (typeof model[Create] !== 'function') {
1854
+ const modelBuild = model[Build];
1855
+ const md = typeof modelBuild === 'function' ? modelBuild.bind(model) : undefined;
1856
+ const conn = await this.#getConnection();
1857
+ let setData = data;
1858
+ if (typeof beforeCreate === 'function') {
1859
+ setData = (await beforeCreate.call(setData, this, setData)) || setData;
1860
+ }
1861
+ const result = await create(conn, this.#env, model, setData, skip, md);
1862
+ if (typeof afterCreate === 'function') {
1863
+ await afterCreate.call(result, this, result);
1864
+ }
1865
+ return isArray ? [result] : result;
1830
1866
  }
1831
1867
  // @ts-ignore
1832
1868
  const m = /** @type {Create} */model;
1833
- return m[Create](this, p, async data => {
1869
+ const result = m[Create](this, data, async (data, that) => {
1870
+ let setData = data;
1834
1871
  const conn = await this.#getConnection();
1835
- return create(conn, this.#env, model, data, skip);
1872
+ if (typeof beforeCreate === 'function') {
1873
+ setData = (await beforeCreate.call(that ?? setData, this, setData)) || setData;
1874
+ }
1875
+ const r = await create(conn, this.#env, model, setData, skip);
1876
+ if (typeof afterCreate === 'function') {
1877
+ await afterCreate.call(that ?? r, this, r);
1878
+ }
1879
+ return r;
1836
1880
  }, model);
1881
+ return isArray ? [result] : result;
1837
1882
  }
1838
1883
  /**
1839
1884
  *
@@ -1864,24 +1909,69 @@ class Connection {
1864
1909
  * @returns {Promise<(T extends Save ? T : object)?>}
1865
1910
  */
1866
1911
  async save(model, data, newData, skip) {
1912
+ const {
1913
+ beforeCreate,
1914
+ afterCreate,
1915
+ beforeUpdate,
1916
+ afterUpdate
1917
+ } = model.hooks || {};
1867
1918
  if (typeof data[Save] !== 'function') {
1868
1919
  const conn = await this.#getConnection();
1869
1920
  if (newData) {
1870
- /** @type {any} */
1871
- const doc = data;
1921
+ let setData = newData;
1922
+ if (typeof beforeUpdate === 'function') {
1923
+ // @ts-ignore
1924
+ setData = (await beforeUpdate.call(data, this, data, setData)) || setData;
1925
+ }
1926
+ // @ts-ignore
1927
+ const r = await updateData(conn, model, data, setData, this.#env, skip);
1928
+ if (r) {
1929
+ if (typeof afterUpdate === 'function') {
1930
+ // @ts-ignore
1931
+ await afterUpdate.call(r, this, r, data);
1932
+ }
1933
+ }
1872
1934
  // @ts-ignore
1873
- return updateData(conn, model, doc, newData, this.#env, skip);
1935
+ return r;
1936
+ }
1937
+ /** @type {Record<string, any>} */
1938
+ // @ts-ignore
1939
+ let setData = data;
1940
+ if (typeof beforeCreate === 'function') {
1941
+ setData = (await beforeCreate.call(data, this, setData)) || setData;
1942
+ }
1943
+ const r = await create(conn, this.#env, model, setData, skip);
1944
+ if (typeof afterCreate === 'function') {
1945
+ await afterCreate.call(r, this, r);
1874
1946
  }
1875
- return create(conn, this.#env, model, data, skip);
1947
+ return r;
1876
1948
  }
1877
- const r = /** @type {Save}*/data;
1949
+ const that = /** @type {Save}*/data;
1878
1950
  // @ts-ignore
1879
- return r[Save](this, async (doc, newData) => {
1951
+ return that[Save](this, async (data, newData) => {
1880
1952
  const conn = await this.#getConnection();
1881
1953
  if (newData) {
1882
- return updateData(conn, model, doc, newData, this.#env, skip);
1954
+ let setData = newData;
1955
+ if (typeof beforeUpdate === 'function') {
1956
+ setData = (await beforeUpdate.call(that, this, data, setData)) || setData;
1957
+ }
1958
+ const r = await updateData(conn, model, data, setData, this.#env, skip);
1959
+ if (r) {
1960
+ if (typeof afterUpdate === 'function') {
1961
+ await afterUpdate.call(that, this, r, data);
1962
+ }
1963
+ }
1964
+ return r;
1965
+ }
1966
+ let setData = data;
1967
+ if (typeof beforeCreate === 'function') {
1968
+ setData = (await beforeCreate.call(that, this, setData)) || setData;
1969
+ }
1970
+ const r = await create(conn, this.#env, model, setData, skip);
1971
+ if (typeof afterCreate === 'function') {
1972
+ await afterCreate.call(that, this, r);
1883
1973
  }
1884
- return create(conn, this.#env, model, doc, skip);
1974
+ return r;
1885
1975
  }, newData || null, model);
1886
1976
  }
1887
1977
  /**
@@ -1892,15 +1982,35 @@ class Connection {
1892
1982
  * @returns {Promise<(T extends Destroy ? T : object)?>}
1893
1983
  */
1894
1984
  async completelyDestroy(tableDef, data) {
1985
+ const {
1986
+ beforeDelete,
1987
+ afterDelete
1988
+ } = tableDef.hooks || {};
1895
1989
  if (typeof data[Destroy] !== 'function') {
1896
1990
  const conn = await this.#getConnection();
1897
- return completelyDelete(conn, tableDef, data, this.#env);
1991
+ if (typeof beforeDelete === 'function') {
1992
+ // @ts-ignore
1993
+ await beforeDelete.call(data, this, data, null);
1994
+ }
1995
+ const r = await completelyDelete(conn, tableDef, data, this.#env);
1996
+ if (r && typeof afterDelete === 'function') {
1997
+ // @ts-ignore
1998
+ await afterDelete.call(r, this, data, null);
1999
+ }
2000
+ return r;
1898
2001
  }
1899
- const r = /** @type {Destroy} */data;
2002
+ const that = /** @type {Destroy} */data;
1900
2003
  // @ts-ignore
1901
- return r[Destroy](this, async data => {
2004
+ return that[Destroy](this, async data => {
1902
2005
  const conn = await this.#getConnection();
1903
- return completelyDelete(conn, tableDef, data, this.#env);
2006
+ if (typeof beforeDelete === 'function') {
2007
+ await beforeDelete.call(that, this, data, null);
2008
+ }
2009
+ const r = await completelyDelete(conn, tableDef, data, this.#env);
2010
+ if (r && typeof afterDelete === 'function') {
2011
+ await afterDelete.call(that, this, data, null);
2012
+ }
2013
+ return r;
1904
2014
  }, tableDef);
1905
2015
  }
1906
2016
  /**
@@ -1915,19 +2025,40 @@ class Connection {
1915
2025
  if (!canPseudo(tableDef)) {
1916
2026
  return null;
1917
2027
  }
2028
+ const {
2029
+ beforeDelete,
2030
+ afterDelete
2031
+ } = tableDef.hooks || {};
1918
2032
  const update = {
1919
2033
  ...value
1920
2034
  };
1921
2035
  if (typeof data[PseudoDestroy] !== 'function') {
1922
2036
  const conn = await this.#getConnection();
2037
+ if (typeof beforeDelete === 'function') {
2038
+ // @ts-ignore
2039
+ await beforeDelete.call(data, this, data, update);
2040
+ }
2041
+ const r = await pseudoDestroy(conn, tableDef, data, update, this.#env);
2042
+ if (r && typeof afterDelete === 'function') {
2043
+ // @ts-ignore
2044
+ await afterDelete.call(r, this, data, update);
2045
+ }
1923
2046
  // @ts-ignore
1924
- return pseudoDestroy(conn, tableDef, data, update, this.#env);
2047
+ return r;
1925
2048
  }
1926
- const r = /** @type {PseudoDestroy} */data;
2049
+ const that = /** @type {PseudoDestroy} */data;
1927
2050
  // @ts-ignore
1928
- return r[PseudoDestroy](this, update, async data => {
2051
+ return that[PseudoDestroy](this, update, async data => {
1929
2052
  const conn = await this.#getConnection();
1930
- return pseudoDestroy(conn, tableDef, data, update, this.#env);
2053
+ if (typeof beforeDelete === 'function') {
2054
+ await beforeDelete.call(that, this, data, update);
2055
+ }
2056
+ const r = await pseudoDestroy(conn, tableDef, data, update, this.#env);
2057
+ if (r && typeof afterDelete === 'function') {
2058
+ await afterDelete.call(that, this, data, update);
2059
+ }
2060
+ // @ts-ignore
2061
+ return r;
1931
2062
  }, tableDef);
1932
2063
  }
1933
2064
  /**
@@ -2446,6 +2577,219 @@ function check(ctx) {
2446
2577
  return name;
2447
2578
  }
2448
2579
 
2580
+ /** @import { Hooks } from '../types' */
2581
+ /** @import { MethodDecorator } from './index.mjs' */
2582
+ const appendHookMetaKey = Symbol();
2583
+ const insertHookMetaKey = Symbol();
2584
+ const beforeCreateManyMetaKey = Symbol();
2585
+ const afterCreateManyMetaKey = Symbol();
2586
+ const beforeCreateMetaKey = Symbol();
2587
+ const afterCreateMetaKey = Symbol();
2588
+ const beforeUpdateMetaKey = Symbol();
2589
+ const afterUpdateMetaKey = Symbol();
2590
+ const beforeDeleteMetaKey = Symbol();
2591
+ const afterDeleteMetaKey = Symbol();
2592
+ const hookMetaKeys = {
2593
+ beforeCreateMany: beforeCreateManyMetaKey,
2594
+ afterCreateMany: afterCreateManyMetaKey,
2595
+ beforeCreate: beforeCreateMetaKey,
2596
+ afterCreate: afterCreateMetaKey,
2597
+ beforeUpdate: beforeUpdateMetaKey,
2598
+ afterUpdate: afterUpdateMetaKey,
2599
+ beforeDelete: beforeDeleteMetaKey,
2600
+ afterDelete: afterDeleteMetaKey
2601
+ };
2602
+ /** @typedef {{static: boolean; hook: Function}} Hook */
2603
+ /**
2604
+ *
2605
+ * @template {Function} T
2606
+ * @param {string} key
2607
+ * @param {boolean} onlyStatic
2608
+ * @param {boolean} [insert]
2609
+ * @returns {MethodDecorator<T>}
2610
+ */
2611
+ function hook(key, onlyStatic, insert = false) {
2612
+ return (value, ctx) => {
2613
+ if (!ctx || ctx.kind !== 'method') {
2614
+ throw new Error('钩子属性只能是方法(method)');
2615
+ }
2616
+ if (onlyStatic && !ctx.static) {
2617
+ throw new Error(`${key} 钩子只能是静态属性`);
2618
+ }
2619
+ const metaKey = insert ? insertHookMetaKey : appendHookMetaKey;
2620
+ let hooks = /** @type {Record<string, Hook[]>?} */ctx.metadata[metaKey];
2621
+ if (!hooks) {
2622
+ hooks = {};
2623
+ ctx.metadata[metaKey] = hooks;
2624
+ }
2625
+ let list = hooks[key];
2626
+ /** @type {Hook} */
2627
+ const item = {
2628
+ static: ctx.static,
2629
+ hook: value
2630
+ };
2631
+ if (Array.isArray(list)) {
2632
+ list.push(item);
2633
+ } else {
2634
+ hooks[key] = [item];
2635
+ }
2636
+ };
2637
+ }
2638
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeCreateMany']>} */
2639
+ const beforeCreateMany = hook.bind(null, 'beforeCreateMany', true);
2640
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterCreateMany']>} */
2641
+ const afterCreateMany = hook.bind(null, 'afterCreateMany', true);
2642
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeCreate']>} */
2643
+ const beforeCreate = hook.bind(null, 'beforeCreate', false);
2644
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterCreate']>} */
2645
+ const afterCreate = hook.bind(null, 'afterCreate', false);
2646
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeUpdate']>} */
2647
+ const beforeUpdate = hook.bind(null, 'beforeUpdate', false);
2648
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterUpdate']>} */
2649
+ const afterUpdate = hook.bind(null, 'afterUpdate', false);
2650
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['beforeDelete']>} */
2651
+ const beforeDelete = hook.bind(null, 'beforeDelete', false);
2652
+ /** @type {(insert?: boolean) => MethodDecorator<Hooks['afterDelete']>} */
2653
+ const afterDelete = hook.bind(null, 'afterDelete', false);
2654
+ /**
2655
+ *
2656
+ * @param {Hook[]?} hooks
2657
+ */
2658
+ function flatHook(hooks) {
2659
+ const list = [hooks].flat(Infinity);
2660
+ return /** @type {Hook[]} */list.filter(Boolean);
2661
+ }
2662
+ /**
2663
+ *
2664
+ * @param {any} Model
2665
+ * @returns {Readonly<Partial<Hooks>>}
2666
+ */
2667
+ function buildHooks(Model) {
2668
+ const metadata = Model[Symbol.metadata];
2669
+ if (!metadata) {
2670
+ return {};
2671
+ }
2672
+ /** @type {Record<symbol, Hook[]?>} */
2673
+ const {
2674
+ [beforeCreateManyMetaKey]: beforeCreateManyMeta,
2675
+ [afterCreateManyMetaKey]: afterCreateManyMeta,
2676
+ [beforeCreateMetaKey]: beforeCreateMeta,
2677
+ [afterCreateMetaKey]: afterCreateMeta,
2678
+ [beforeUpdateMetaKey]: beforeUpdateMeta,
2679
+ [afterUpdateMetaKey]: afterUpdateMeta,
2680
+ [beforeDeleteMetaKey]: beforeDeleteMeta,
2681
+ [afterDeleteMetaKey]: afterDeleteMeta
2682
+ } = metadata;
2683
+ return {
2684
+ async beforeCreateMany(conn, record) {
2685
+ let data = record;
2686
+ for (const {
2687
+ static: isStatic,
2688
+ hook
2689
+ } of flatHook(beforeCreateManyMeta)) {
2690
+ if (typeof hook !== 'function') {
2691
+ continue;
2692
+ }
2693
+ data = (await hook.call(Model, conn, data)) || data;
2694
+ }
2695
+ return data;
2696
+ },
2697
+ async afterCreateMany(conn, record) {
2698
+ for (const {
2699
+ static: isStatic,
2700
+ hook
2701
+ } of flatHook(afterCreateManyMeta)) {
2702
+ if (typeof hook !== 'function') {
2703
+ continue;
2704
+ }
2705
+ await hook.call(Model, conn, record);
2706
+ }
2707
+ },
2708
+ async beforeCreate(conn, record) {
2709
+ let that;
2710
+ let data = record;
2711
+ for (const {
2712
+ static: isStatic,
2713
+ hook
2714
+ } of flatHook(beforeCreateMeta)) {
2715
+ if (typeof hook !== 'function') {
2716
+ continue;
2717
+ }
2718
+ const thisArg = isStatic ? Model : that ||= this instanceof Model ? this : new Model(record);
2719
+ data = (await hook.call(thisArg, conn, data)) || data;
2720
+ }
2721
+ return data;
2722
+ },
2723
+ async afterCreate(conn, record) {
2724
+ let that;
2725
+ for (const {
2726
+ static: isStatic,
2727
+ hook
2728
+ } of flatHook(afterCreateMeta)) {
2729
+ if (typeof hook !== 'function') {
2730
+ continue;
2731
+ }
2732
+ const thisArg = isStatic ? Model : that ||= this instanceof Model ? this : new Model(record, true);
2733
+ await hook.call(thisArg, conn, record);
2734
+ }
2735
+ },
2736
+ async beforeUpdate(conn, record, set) {
2737
+ let that;
2738
+ let data = set;
2739
+ for (const {
2740
+ static: isStatic,
2741
+ hook
2742
+ } of flatHook(beforeUpdateMeta)) {
2743
+ if (typeof hook !== 'function') {
2744
+ continue;
2745
+ }
2746
+ const thisArg = isStatic ? Model : that ||= this instanceof Model ? this : new Model(record, set);
2747
+ data = (await hook.call(thisArg, conn, record, data)) || data;
2748
+ }
2749
+ return data;
2750
+ },
2751
+ async afterUpdate(conn, record, oldRecord) {
2752
+ let that;
2753
+ for (const {
2754
+ static: isStatic,
2755
+ hook
2756
+ } of flatHook(afterUpdateMeta)) {
2757
+ if (typeof hook !== 'function') {
2758
+ continue;
2759
+ }
2760
+ const thisArg = isStatic ? Model : that ||= this instanceof Model ? this : new Model(record, true);
2761
+ await hook.call(thisArg, conn, record, oldRecord);
2762
+ }
2763
+ },
2764
+ async beforeDelete(conn, record, update) {
2765
+ let that;
2766
+ for (const {
2767
+ static: isStatic,
2768
+ hook
2769
+ } of flatHook(beforeDeleteMeta)) {
2770
+ if (typeof hook !== 'function') {
2771
+ continue;
2772
+ }
2773
+ const thisArg = isStatic ? Model : that ||= this instanceof Model ? this : new Model(record, true);
2774
+ await hook.call(thisArg, conn, record, update);
2775
+ }
2776
+ },
2777
+ async afterDelete(conn, record, update) {
2778
+ let that;
2779
+ for (const {
2780
+ static: isStatic,
2781
+ hook
2782
+ } of flatHook(afterDeleteMeta)) {
2783
+ if (typeof hook !== 'function') {
2784
+ continue;
2785
+ }
2786
+ const thisArg = isStatic ? Model : that ||= this instanceof Model ? this : new Model(record, true);
2787
+ await hook.call(thisArg, conn, record, update);
2788
+ }
2789
+ }
2790
+ };
2791
+ }
2792
+
2449
2793
  /** @import { ClassDecorator } from './index.mjs' */
2450
2794
  const fieldKey = Symbol();
2451
2795
  const propsKey = Symbol();
@@ -2537,7 +2881,7 @@ const typeKey = Symbol();
2537
2881
  * @param {*} [p1]
2538
2882
  * @param {*} [p2]
2539
2883
  */
2540
- function submodel(type, constraints, p1, p2) {
2884
+ function model(type, constraints, p1, p2) {
2541
2885
  if (typeof type === 'string') {
2542
2886
  const table = type;
2543
2887
  /**
@@ -2561,6 +2905,12 @@ function submodel(type, constraints, p1, p2) {
2561
2905
  if (!fields && !classField) {
2562
2906
  throw new Error('字段未定义');
2563
2907
  }
2908
+ const insertHooks = metadata[insertHookMetaKey] || {};
2909
+ const appendHooks = metadata[appendHookMetaKey] || {};
2910
+ const parentMetadata = Object.getPrototypeOf(ModelClass)?.[Symbol.metadata] || {};
2911
+ for (const [name, key] of Object.entries(hookMetaKeys)) {
2912
+ metadata[key] = [...(insertHooks[name] || []), ...(parentMetadata[key] || []), ...(appendHooks[name] || [])];
2913
+ }
2564
2914
  Object.defineProperty(ModelClass, 'table', {
2565
2915
  value: table,
2566
2916
  configurable: true,
@@ -3930,13 +4280,14 @@ class Query {
3930
4280
  }
3931
4281
  /**
3932
4282
  * @param {QueryOptions<T, TB> | Query<T, TB>} options
4283
+ * @param {boolean} [noBuild]
3933
4284
  */
3934
- constructor(options) {
4285
+ constructor(options, noBuild) {
3935
4286
  if (options instanceof Query) {
3936
4287
  this.table = options.table;
3937
4288
  this.fields = options.fields;
3938
4289
  this.pseudo = options.pseudo;
3939
- this.#build = options.#build;
4290
+ this.#build = noBuild ? null : options.#build;
3940
4291
  this.#options = {
3941
4292
  ...options.#options
3942
4293
  };
@@ -3954,7 +4305,9 @@ class Query {
3954
4305
  this.pseudo = typeof pseudo === 'string' ? pseudo : '';
3955
4306
  this.#build = typeof build === 'function' ? build : null;
3956
4307
  this.#primaryFields = Object.entries(fields).filter(([, v]) => v.primary).sort(([, a], [, b]) => Number(a.primary) - Number(b.primary)).map(([v]) => v);
3957
- if (typeof options[Build] === 'function') {
4308
+ if (noBuild) {
4309
+ this.#build = null;
4310
+ } else if (typeof options[Build] === 'function') {
3958
4311
  this.#build = options[Build].bind(options);
3959
4312
  } else if (typeof build === 'function') {
3960
4313
  this.#build = build.bind(options);
@@ -4684,6 +5037,9 @@ class Model {
4684
5037
  static fields = {};
4685
5038
  /** @readonly @type {readonly IndexInfo[]} */
4686
5039
  static indexes = [];
5040
+ static get hooks() {
5041
+ return buildHooks(this);
5042
+ }
4687
5043
  /**
4688
5044
  * @template {typeof Model} T
4689
5045
  * @this {T}
@@ -4730,7 +5086,7 @@ class Model {
4730
5086
  * @this {T}
4731
5087
  * @param {Connection} t
4732
5088
  * @param {object} p
4733
- * @param {(data: object) => Promise<object>} run
5089
+ * @param {(data: object, that: any) => Promise<object>} run
4734
5090
  * @returns {Promise<InstanceType<T>>}
4735
5091
  */
4736
5092
  static async [Create](t, p, run) {
@@ -4741,7 +5097,7 @@ class Model {
4741
5097
  return doc.#do(() => doc.$_onBeforeSave(t, false), () => run({
4742
5098
  ...doc.#data,
4743
5099
  ...doc.#newData
4744
- }), () => doc.$_onAfterSave(t, false)).then(() => doc);
5100
+ }, doc), () => doc.$_onAfterSave(t, false)).then(() => doc);
4745
5101
  }
4746
5102
  /**
4747
5103
  * @template {typeof Model} T
@@ -5508,4 +5864,4 @@ function field(type, {
5508
5864
  };
5509
5865
  }
5510
5866
 
5511
- export { Build, Connection, Create, Destroy, Model, PseudoDestroy, Query, Save, Scene, Submodel, Where, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, immutable, increment, index, isDevelopment, isPseudo, submodel as model, multiply, now, primary, prop, pseudo, setDevelopment, sort, submodel, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, withDeleted };
5867
+ export { Build, Connection, Create, Destroy, Model, PseudoDestroy, Query, Save, Scene, Submodel, Where, afterCreate, afterCreateMany, afterDelete, afterUpdate, beforeCreate, beforeCreateMany, beforeDelete, beforeUpdate, column, creating, decrement, defaultValue, field as define, deleted, deleting, divide, field$1 as field, getPrimaryFields, getPrimaryKeys, immutable, increment, index, isDevelopment, isPseudo, model, multiply, now, primary, prop, pseudo, setDevelopment, sort, model as submodel, toNot, toPrimaries, toPrimary, uncreatable, undeleted, updating, uuid, values, withDeleted };
package/migrate.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.5.1
2
+ * imodel v0.6.0
3
3
  * (c) 2019-2025 undefined
4
4
  * @license undefined
5
5
  */
package/migrate.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * imodel v0.5.1
2
+ * imodel v0.6.0
3
3
  * (c) 2019-2025 undefined
4
4
  * @license undefined
5
5
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imodel",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "main": "index.mjs",
5
5
  "type": "module",
6
6
  "repository": {