@zerotal/orm 1.1.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -8,11 +8,58 @@ follows the Zerotal monorepo's unified versioning.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [1.3.0] — 2026-08-09
12
+
13
+ ### Changed — BREAKING
14
+
15
+ - **`BaseModelWith(...)` is replaced by the `Model.using(...)` static.** Mixin composition is now
16
+ a property of the base class rather than a helper shipped alongside it, so there is one idiom to
17
+ learn and nothing extra to import.
18
+
19
+ ```ts
20
+ // before
21
+ import { BaseModelWith } from "@zerotal/orm";
22
+ class User extends BaseModelWith(Authenticatable, Permissions, Roles) {}
23
+
24
+ // after
25
+ import { Model } from "@zerotal/orm";
26
+ class User extends Model.using(Authenticatable, Permissions, Roles) {}
27
+ ```
28
+
29
+ Run `bun run scripts/codemod-mixin-composition.ts` to rewrite call sites and imports.
30
+
31
+ How mixins are **authored** is unchanged — `<T extends Constructor>(Base: T) => class extends Base`
32
+ still works exactly as before, and every shipped mixin (`SoftDeletes`, `State`, `Authenticatable`,
33
+ `Roles`, `Permissions`, `Notifiable`, `Tenantable`, `Auditable`, …) keeps its signature. The
34
+ `Constructor` and `Mixin` types are still exported; `Compose` (the type of `Model.using`) joins
35
+ them. Mixin authors declaring columns still call `registerColumn` imperatively.
36
+
37
+ `with` was deliberately **not** used for this. It is reserved for the eager-load static
38
+ (`User.with("posts")`), the one conspicuous gap in the model's existing query-forwarder family
39
+ (`where`, `whereIn`, `orderBy`, `latest`, `first`, `paginate`, `find`, `all`, `count`, …).
40
+
41
+ ### Changed
42
+
43
+ - **`Model` is now the canonical name for the base class; `BaseModel` is the alias.** They are the
44
+ same class object and both remain exported, so no code breaks — but `class User extends Model {}`
45
+ is the documented form from here, mirroring Flow's `class PostsPage extends Component {}`.
46
+ `BaseModel` was previously the canonical name and `Model` an unused compat alias added in 1.0.2.
47
+
48
+ ### Added
49
+
50
+ - **`using` composes onto any class in the chain, not just the root.** An app-level base model can
51
+ now carry mixins without being flattened out of the prototype chain — `AppModel.using(SoftDeletes)`
52
+ keeps `AppModel` and its statics in the lineage. `BaseModelWith` hardcoded `BaseModel`, so this
53
+ previously required hand-nesting.
54
+ - **Composition chains.** The composed class carries `using` itself, so
55
+ `Model.using(a, b).using(c, d)` works past the 8-mixin overload set — which is why the overload
56
+ set shrank from 20 hand-written arities to 8 without losing any capability.
57
+
11
58
  ## [1.1.0] — 2026-08-08
12
59
 
13
60
  ### Fixed
14
61
 
15
- - **A `json` column returns the type it was given.** Writing skipped `JSON.stringify` for values that were already strings, so a string went into the column as bare characters — `62812345678`, not `"62812345678"` — and the read side's `JSON.parse` turned it back into a number. A `json`-cast setting holding an account number came back as a number, and only for *some* values, since a string that fails to parse fell through unchanged. Encoding is now symmetric in both directions, and `where()` against a `json` column encodes the same way, so a query finds what a write stores. **Upgrade note:** rows written by an older version hold bare scalars, so a string column may still read back as a number, and a `where()` on a string will not match those older rows — they are stored unquoted. Only affects bare scalars in `json`/`array` columns; objects and arrays were always encoded and are untouched.
62
+ - **A `json` column returns the type it was given.** Writing skipped `JSON.stringify` for values that were already strings, so a string went into the column as bare characters — `62812345678`, not `"62812345678"` — and the read side's `JSON.parse` turned it back into a number. A `json`-cast setting holding an account number came back as a number, and only for _some_ values, since a string that fails to parse fell through unchanged. Encoding is now symmetric in both directions, and `where()` against a `json` column encodes the same way, so a query finds what a write stores. **Upgrade note:** rows written by an older version hold bare scalars, so a string column may still read back as a number, and a `where()` on a string will not match those older rows — they are stored unquoted. Only affects bare scalars in `json`/`array` columns; objects and arrays were always encoded and are untouched.
16
63
  - **`bun zt make:model` generates a file that parses.** The stub emitted `@table('posts').withTimestamps()`, which is not valid decorator syntax — the grammar allows a call at the end of the chain, not in the middle — so every generated model failed with `Expected "class" but found "."`. The stub now emits plain `@table('posts')`; timestamps are on by default and the chained form needs outer parentheses, `@(table("x").withoutTimestamps())`. The same broken form is corrected in the `BaseModel` docblocks, and every generated stub is now parsed by a test rather than checked for substrings.
17
64
  - **A column `default` is applied on insert.** A declared field that was never assigned was written as an explicit `NULL`, so the INSERT named the column, the database never applied its own default, and a `NOT NULL` column failed outright — on a model and migration that both declared `default: 0`. `undefined` now means "I didn't say": the declared default is used, or the column is omitted so the database decides. An explicit `null` still stores `NULL`.
18
65
  - **A `Date` compared against a timestamp column matches again.** Bound values are serialised through the column's cast metadata, but the framework-managed `created_at` / `updated_at` / `deleted_at` carry no `@column` registration — so a `Date` was bound raw and matched nothing. `where("created_at", ">=", monthStart)` is the commonest reporting query there is, and it silently returned zero rows: a dashboard reading "0 this month" looks like a quiet month, not a broken query.
package/README.md CHANGED
@@ -43,11 +43,11 @@ export default DatabaseConfig({
43
43
  ### Define a model
44
44
 
45
45
  ```ts
46
- import { BaseModel, column, table, belongsTo, hasMany } from "@zerotal/orm";
46
+ import { Model, column, table, belongsTo, hasMany } from "@zerotal/orm";
47
47
  import type { Columns } from "@zerotal/orm";
48
48
 
49
49
  @(table("posts").withTimestamps().withSoftDeletes())
50
- export class Post extends BaseModel {
50
+ export class Post extends Model {
51
51
  static fillable: Columns<Post>[] = ["title", "body", "status", "userId"];
52
52
 
53
53
  @column("string") title!: string;
@@ -144,7 +144,7 @@ This package exposes two subpath entry points:
144
144
 
145
145
  Main exports from the default entry point:
146
146
 
147
- - **Models** — `BaseModel` / `Model`, `ModelQueryBuilder`, `DB`, `QueryBuilder`
147
+ - **Models** — `Model` (aka `BaseModel`), `ModelQueryBuilder`, `DB`, `QueryBuilder`
148
148
  - **Decorators** — `column`, `table`, `belongsTo`, `hasMany`, `hasOne`, `manyToMany`, `morphTo`, `morphMany`, `morphOne`, `hasManyThrough`, `hasOneThrough`, `morphToMany`, `morphedByMany`
149
149
  - **Schema / migrations** — `Schema`, `Blueprint`, `Migration`, `MigrationRunner`, `SchemaInspector`, `ModelInspector`, `SchemaDiffer`, `synchronizeSchema`
150
150
  - **Seeding** — `Seeder`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerotal/orm",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "license": "MIT",
5
5
  "maturity": "stable",
6
6
  "private": false,
@@ -30,8 +30,8 @@
30
30
  "typecheck": "tsc --noEmit"
31
31
  },
32
32
  "dependencies": {
33
- "@zerotal/core": "1.1.0",
34
- "@zerotal/validator": "1.1.0"
33
+ "@zerotal/core": "1.3.0",
34
+ "@zerotal/validator": "1.3.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "typescript": "^5.8.0"
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A Bun-native Active Record ORM built on `Bun.sql`.
3
3
  *
4
- * Models extend {@link BaseModel}: you declare columns with {@link column | `@column`},
4
+ * Models extend {@link Model}: you declare columns with {@link column | `@column`},
5
5
  * relationships with decorators like {@link hasMany | `@hasMany`} and
6
6
  * {@link belongsTo | `@belongsTo`}, and then query and persist through the model's
7
7
  * static and instance methods. Under the hood a {@link QueryBuilder} routes every
@@ -11,17 +11,17 @@
11
11
  * using the {@link Schema} facade and the {@link Blueprint} table builder.
12
12
  *
13
13
  * Mass assignment is **guarded by default** — a model with neither `fillable` nor
14
- * `guarded` declared rejects all attributes in {@link BaseModel.fill | `fill()`}.
14
+ * `guarded` declared rejects all attributes in {@link Model.fill | `fill()`}.
15
15
  * Soft deletes and state machines are opt-in mixins composed via
16
- * {@link BaseModelWith}. The ORM's CLI commands (`migrate`, `make:model`, …) live
16
+ * `Model.using(...)`. The ORM's CLI commands (`migrate`, `make:model`, …) live
17
17
  * under the `@zerotal/orm/commands` subpath.
18
18
  *
19
19
  * @example Define a model
20
20
  * ```ts
21
- * import { BaseModel, column, hasMany, type HasMany } from "@zerotal/orm";
21
+ * import { Model, column, hasMany, type HasMany } from "@zerotal/orm";
22
22
  * import { Post } from "./Post.ts";
23
23
  *
24
- * export class User extends BaseModel {
24
+ * export class User extends Model {
25
25
  * @column({ primary: true }) id!: number;
26
26
  * @column() email!: string;
27
27
  * @column() name!: string;
@@ -70,12 +70,14 @@
70
70
 
71
71
  // @zerotal/orm — public API barrel
72
72
 
73
- export { BaseModel, Model } from "./model/BaseModel.ts";
74
- export { BaseModelWith } from "./model/mixins.ts";
75
- export type { Constructor, Mixin } from "./model/mixins.ts";
76
- // State-machine behaviour is an opt-in mixin — compose with `BaseModelWith(State)`.
73
+ // `Model` is the canonical base class; `BaseModel` is the same class under its original name.
74
+ export { Model, BaseModel } from "./model/BaseModel.ts";
75
+ // Mixin authoring types. Compose them onto a model with the `Model.using(...)` static —
76
+ // `class User extends Model.using(Authenticatable, Roles)`.
77
+ export type { Constructor, Mixin, Compose } from "./model/mixins.ts";
78
+ // State-machine behaviour is an opt-in mixin — compose with `Model.using(State)`.
77
79
  export { State } from "./model/State.ts";
78
- // Soft deletes are opt-in — compose with `BaseModelWith(SoftDeletes)`.
80
+ // Soft deletes are opt-in — compose with `Model.using(SoftDeletes)`.
79
81
  export { SoftDeletes } from "./model/SoftDeletes.ts";
80
82
  export type {
81
83
  StateDefinition,
@@ -143,7 +145,7 @@ export { column, columnRegistry } from "./model/decorators/column.ts";
143
145
  export type { ColumnOptions, ColumnShorthand } from "./model/decorators/column.ts";
144
146
  export { registerModel, modelByName, modelsByName } from "./model/decorators/_metadata.ts";
145
147
  // Imperative column registration — for mixin authors composing model behaviour with
146
- // BaseModelWith (the @column decorator can't run inside a returned class expression).
148
+ // Model.using (the @column decorator can't run inside a returned class expression).
147
149
  export { registerColumn, columnsFor } from "./model/decorators/_metadata.ts";
148
150
  export { table } from "./model/decorators/table.ts";
149
151
  export type { TableDecoratorBuilder, TableOptions } from "./model/decorators/table.ts";
@@ -33,6 +33,8 @@ import {
33
33
  } from "../errors/index.ts";
34
34
  import { type ManyToMany } from "./relations/RelationRegistry.ts";
35
35
  import { installReactiveAccessors, type ColumnOptions } from "./decorators/column.ts";
36
+ import { _compose } from "./mixins.ts";
37
+ import type { Compose } from "./mixins.ts";
36
38
  import { columnsFor, relationsFor } from "./decorators/_metadata.ts";
37
39
  import { TransactionContext } from "../db/TransactionContext.ts";
38
40
  import type { InsertPayload, UpdatePayload, FillablePayload } from "./payload.ts";
@@ -567,6 +569,37 @@ export class BaseModel {
567
569
  */
568
570
  declare readonly __isZerotalModel: true;
569
571
 
572
+ /**
573
+ * Compose one or more model mixins onto this class, folding them left-to-right, so reusable
574
+ * model behaviour (auth contract, roles, permissions, soft deletes, tenancy, …) stacks flat
575
+ * instead of nesting.
576
+ *
577
+ * @remarks
578
+ * Each mixin receives the accumulated base and returns an extended class, so this class's full
579
+ * static surface (`query()`, `find()`, `create()`, scopes, …) and every mixin's instance and
580
+ * static members flow through to the composed class — fully type-checked. Prefer this over
581
+ * hand-nesting mixins (`Roles(Permissions(AuthUser))`), which reads inside-out and repeats the
582
+ * base.
583
+ *
584
+ * `using` composes onto whatever class it is called on, so it also works on an intermediate
585
+ * model base, and the composed class carries `using` itself, so `Model.using(a, b).using(c)`
586
+ * chains past the 8-mixin overload set.
587
+ *
588
+ * Mixin authors declaring columns must call {@link registerColumn} imperatively — the `@column`
589
+ * decorator cannot run inside a returned class expression.
590
+ *
591
+ * @param mixins - Mixin factories applied in order; each receives the class the previous one produced.
592
+ * @returns A model class extending this one with every mixin applied.
593
+ *
594
+ * @example
595
+ * ```ts
596
+ * class User extends Model.using(Authenticatable, Permissions, Roles) {}
597
+ * ```
598
+ *
599
+ * @category Composition
600
+ */
601
+ static using: Compose = _compose;
602
+
570
603
  /**
571
604
  * Database table this model maps to. Usually set for you by the `@table("…")`
572
605
  * decorator; assign directly to override.
@@ -2530,5 +2563,7 @@ function _applyRow(inst: BaseModel, row: Record<string, unknown>): void {
2530
2563
  (inst as unknown as { _original: Record<string, unknown> })._original = orig;
2531
2564
  }
2532
2565
 
2533
- // Backward-compat alias (index.ts exports `Model`)
2566
+ // `Model` is the canonical name at the declaration site — `class User extends Model.using(…)`
2567
+ // mirrors Flow's `class PostsPage extends Component.using(…)`. `BaseModel` remains exported as
2568
+ // an alias (same class object) for code that references the base class by that name.
2534
2569
  export { BaseModel as Model };
@@ -4,10 +4,10 @@
4
4
  // `deletedAt`, `forceDelete()`, `restore()`, `trashed()`, and the `withTrashed()` /
5
5
  // `onlyTrashed()` query scopes. A hard-delete model has none of these.
6
6
  //
7
- // import { BaseModelWith, SoftDeletes } from "@zerotal/orm";
7
+ // import { Model, SoftDeletes } from "@zerotal/orm";
8
8
  //
9
9
  // @table("posts")
10
- // class Post extends BaseModelWith(SoftDeletes) {
10
+ // class Post extends Model.using(SoftDeletes) {
11
11
  // @column() title!: string;
12
12
  // }
13
13
  //
@@ -47,7 +47,7 @@ interface SoftDeleteModelClass<T extends BaseModel> {
47
47
  * @example
48
48
  * ```ts
49
49
  * @table("posts")
50
- * class Post extends BaseModelWith(SoftDeletes) {
50
+ * class Post extends Model.using(SoftDeletes) {
51
51
  * @column() title!: string;
52
52
  * }
53
53
  *
@@ -4,7 +4,7 @@
4
4
  // workflow carry the API — `transitionTo` / `forceState` / `onTransition` / the
5
5
  // `states` + `stateField` statics never appear on models that don't use them.
6
6
  //
7
- // import { BaseModelWith } from "@zerotal/orm";
7
+ // import { Model, State } from "@zerotal/orm";
8
8
  //
9
9
  // const States = {
10
10
  // pending: { canTransitionTo: ["active", "cancelled"] as const },
@@ -14,7 +14,7 @@
14
14
  // cancelled: { canTransitionTo: [] as const },
15
15
  // } as const;
16
16
  //
17
- // class Subscription extends BaseModelWith(State) {
17
+ // class Subscription extends Model.using(State) {
18
18
  // static states = States;
19
19
  // @column() status!: keyof typeof States;
20
20
  // }
@@ -132,7 +132,7 @@ interface StateModelClass {
132
132
  * cancelled: { canTransitionTo: [] as const },
133
133
  * } as const;
134
134
  *
135
- * class Subscription extends BaseModelWith(State) {
135
+ * class Subscription extends Model.using(State) {
136
136
  * static states = States;
137
137
  * @column() status!: keyof typeof States;
138
138
  * }
@@ -46,7 +46,7 @@ interface TableConfig {
46
46
  * export class Post extends BaseModel { ... }
47
47
  *
48
48
  * Soft deletes are opt-in via the `SoftDeletes` mixin, not `@table`:
49
- * `class Post extends BaseModelWith(SoftDeletes) {}` — see /docs/orm/lifecycle.
49
+ * `class Post extends Model.using(SoftDeletes) {}` — see /docs/orm/lifecycle.
50
50
  */
51
51
  export interface TableDecoratorBuilder {
52
52
  /** Apply the decorator to a class constructor (called automatically by TS). */
@@ -77,7 +77,7 @@ export interface TableDecoratorBuilder {
77
77
  * ```
78
78
  *
79
79
  * Timestamps are **on by default** — use `.withoutTimestamps()` to opt out. Soft
80
- * deletes are **opt-in via the `SoftDeletes` mixin**: `extends BaseModelWith(SoftDeletes)`.
80
+ * deletes are **opt-in via the `SoftDeletes` mixin**: `extends Model.using(SoftDeletes)`.
81
81
  *
82
82
  * `@table` is the single, required way to configure a model: besides setting the
83
83
  * table name and options, it anchors the class's `@column`/relation registrations at
@@ -1,22 +1,25 @@
1
1
  // ── Model mixins ──────────────────────────────────────────────────────────────
2
2
  //
3
- // `BaseModelWith(...)` composes any number of model mixins on top of `BaseModel`,
4
- // so packages can ship reusable model behaviour (auth contract, roles, permissions,
5
- // soft deletes, …) that apps stack without "wrapper hell":
3
+ // Mixin composition machinery for the ORM. The entry point is `Model.using(...)`
4
+ // (see BaseModel.ts) this module owns the types it is built from, and the fold
5
+ // itself. Nothing here imports `BaseModel`: the fold seeds from its receiver, so
6
+ // this is a leaf module.
7
+ //
8
+ // `Model.using(...)` composes any number of model mixins, so packages can ship
9
+ // reusable model behaviour (auth contract, roles, permissions, soft deletes, …)
10
+ // that apps stack without "wrapper hell":
6
11
  //
7
12
  // // before — nested, base repeated, order reads inside-out
8
13
  // class User extends Roles(Permissions(AuthUser)) {}
9
14
  //
10
15
  // // after — flat, left-to-right, base baked in
11
- // class User extends BaseModelWith(Authenticatable, Permissions, Roles) {}
16
+ // class User extends Model.using(Authenticatable, Permissions, Roles) {}
12
17
  //
13
18
  // A mixin is the canonical generic form `(<T>(Base: T) => class extends Base { … })`.
14
- // Because each one returns `class extends Base`, BaseModel's full static surface
19
+ // Because each one returns `class extends Base`, the base's full static surface
15
20
  // (`User.query()`, `find()`, `create()`, scopes, …) and every mixin's instance
16
21
  // members flow through to the final class — fully type-checked.
17
22
 
18
- import { BaseModel } from "./BaseModel.ts";
19
-
20
23
  /** A concrete class constructor. */
21
24
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic mixin base bound
22
25
  export type Constructor<T = object> = new (...args: any[]) => T;
@@ -32,505 +35,127 @@ export type Mixin<TIn extends Constructor = Constructor, TOut extends Constructo
32
35
  Base: TIn,
33
36
  ) => TOut;
34
37
 
35
- type Base = typeof BaseModel;
36
-
37
- // Typed overloads (1–8 mixins) thread the accumulated type through each step so the
38
- // result is `BaseModel`'s statics + every mixin's members. Need more? Nest a
39
- // `BaseModelWith(...)` call or add another overload.
40
-
41
38
  /**
42
- * Compose any number of model {@link Mixin | mixins} on top of {@link BaseModel},
43
- * folding them left-to-right so the resulting class carries `BaseModel`'s full
44
- * static surface (`query()`, `find()`, `create()`, scopes, …) plus every mixin's
45
- * instance and static members — all fully type-checked.
46
- *
47
- * Prefer this over hand-nesting mixins (`Roles(Permissions(AuthUser))`), which
48
- * reads inside-out and repeats the base. Each overload (1–20 mixins) threads the
49
- * accumulated type through every step; for more, nest a second `BaseModelWith(...)`.
39
+ * The call signatures behind `Model.using(...)`.
50
40
  *
51
- * @param mixins - Mixin factories applied in order; each receives the class the
52
- * previous one produced.
53
- * @returns A model class extending `BaseModel` with every mixin applied.
41
+ * Each overload threads the accumulated type through every step, so the composed class carries
42
+ * the base's full static surface plus every mixin's instance and static members — fully
43
+ * type-checked. The `this: TBase` parameter is what makes the base polymorphic: `using` composes
44
+ * onto whatever class it is called on, not onto a hardcoded `BaseModel`.
54
45
  *
55
- * @example
56
- * ```ts
57
- * class User extends BaseModelWith(Authenticatable, Permissions, Roles) {}
58
- * ```
46
+ * Overloads cover 1–8 mixins. There is no ceiling: the composed class carries `using` too, so
47
+ * `Model.using(a, b).using(c, d)` chains.
59
48
  */
60
- export function BaseModelWith<A extends Constructor>(a: (base: Base) => A): A;
61
- export function BaseModelWith<A extends Constructor, B extends Constructor>(
62
- a: (base: Base) => A,
63
- b: (base: A) => B,
64
- ): B;
65
- export function BaseModelWith<A extends Constructor, B extends Constructor, C extends Constructor>(
66
- a: (base: Base) => A,
67
- b: (base: A) => B,
68
- c: (base: B) => C,
69
- ): C;
70
- export function BaseModelWith<
71
- A extends Constructor,
72
- B extends Constructor,
73
- C extends Constructor,
74
- D extends Constructor,
75
- >(a: (base: Base) => A, b: (base: A) => B, c: (base: B) => C, d: (base: C) => D): D;
76
- export function BaseModelWith<
77
- A extends Constructor,
78
- B extends Constructor,
79
- C extends Constructor,
80
- D extends Constructor,
81
- E extends Constructor,
82
- >(
83
- a: (base: Base) => A,
84
- b: (base: A) => B,
85
- c: (base: B) => C,
86
- d: (base: C) => D,
87
- e: (base: D) => E,
88
- ): E;
89
- export function BaseModelWith<
90
- A extends Constructor,
91
- B extends Constructor,
92
- C extends Constructor,
93
- D extends Constructor,
94
- E extends Constructor,
95
- F extends Constructor,
96
- >(
97
- a: (base: Base) => A,
98
- b: (base: A) => B,
99
- c: (base: B) => C,
100
- d: (base: C) => D,
101
- e: (base: D) => E,
102
- f: (base: E) => F,
103
- ): F;
104
- export function BaseModelWith<
105
- A extends Constructor,
106
- B extends Constructor,
107
- C extends Constructor,
108
- D extends Constructor,
109
- E extends Constructor,
110
- F extends Constructor,
111
- G extends Constructor,
112
- >(
113
- a: (base: Base) => A,
114
- b: (base: A) => B,
115
- c: (base: B) => C,
116
- d: (base: C) => D,
117
- e: (base: D) => E,
118
- f: (base: E) => F,
119
- g: (base: F) => G,
120
- ): G;
121
- export function BaseModelWith<
122
- A extends Constructor,
123
- B extends Constructor,
124
- C extends Constructor,
125
- D extends Constructor,
126
- E extends Constructor,
127
- F extends Constructor,
128
- G extends Constructor,
129
- H extends Constructor,
130
- >(
131
- a: (base: Base) => A,
132
- b: (base: A) => B,
133
- c: (base: B) => C,
134
- d: (base: C) => D,
135
- e: (base: D) => E,
136
- f: (base: E) => F,
137
- g: (base: F) => G,
138
- h: (base: G) => H,
139
- ): H;
140
- export function BaseModelWith<
141
- A extends Constructor,
142
- B extends Constructor,
143
- C extends Constructor,
144
- D extends Constructor,
145
- E extends Constructor,
146
- F extends Constructor,
147
- G extends Constructor,
148
- H extends Constructor,
149
- I extends Constructor,
150
- >(
151
- a: (base: Base) => A,
152
- b: (base: A) => B,
153
- c: (base: B) => C,
154
- d: (base: C) => D,
155
- e: (base: D) => E,
156
- f: (base: E) => F,
157
- g: (base: F) => G,
158
- h: (base: G) => H,
159
- i: (base: H) => I,
160
- ): I;
161
- export function BaseModelWith<
162
- A extends Constructor,
163
- B extends Constructor,
164
- C extends Constructor,
165
- D extends Constructor,
166
- E extends Constructor,
167
- F extends Constructor,
168
- G extends Constructor,
169
- H extends Constructor,
170
- I extends Constructor,
171
- J extends Constructor,
172
- >(
173
- a: (base: Base) => A,
174
- b: (base: A) => B,
175
- c: (base: B) => C,
176
- d: (base: C) => D,
177
- e: (base: D) => E,
178
- f: (base: E) => F,
179
- g: (base: F) => G,
180
- h: (base: G) => H,
181
- i: (base: H) => I,
182
- j: (base: I) => J,
183
- ): J;
184
- export function BaseModelWith<
185
- A extends Constructor,
186
- B extends Constructor,
187
- C extends Constructor,
188
- D extends Constructor,
189
- E extends Constructor,
190
- F extends Constructor,
191
- G extends Constructor,
192
- H extends Constructor,
193
- I extends Constructor,
194
- J extends Constructor,
195
- K extends Constructor,
196
- >(
197
- a: (base: Base) => A,
198
- b: (base: A) => B,
199
- c: (base: B) => C,
200
- d: (base: C) => D,
201
- e: (base: D) => E,
202
- f: (base: E) => F,
203
- g: (base: F) => G,
204
- h: (base: G) => H,
205
- i: (base: H) => I,
206
- j: (base: I) => J,
207
- k: (base: J) => K,
208
- ): K;
209
- export function BaseModelWith<
210
- A extends Constructor,
211
- B extends Constructor,
212
- C extends Constructor,
213
- D extends Constructor,
214
- E extends Constructor,
215
- F extends Constructor,
216
- G extends Constructor,
217
- H extends Constructor,
218
- I extends Constructor,
219
- J extends Constructor,
220
- K extends Constructor,
221
- L extends Constructor,
222
- >(
223
- a: (base: Base) => A,
224
- b: (base: A) => B,
225
- c: (base: B) => C,
226
- d: (base: C) => D,
227
- e: (base: D) => E,
228
- f: (base: E) => F,
229
- g: (base: F) => G,
230
- h: (base: G) => H,
231
- i: (base: H) => I,
232
- j: (base: I) => J,
233
- k: (base: J) => K,
234
- l: (base: K) => L,
235
- ): L;
236
- export function BaseModelWith<
237
- A extends Constructor,
238
- B extends Constructor,
239
- C extends Constructor,
240
- D extends Constructor,
241
- E extends Constructor,
242
- F extends Constructor,
243
- G extends Constructor,
244
- H extends Constructor,
245
- I extends Constructor,
246
- J extends Constructor,
247
- K extends Constructor,
248
- L extends Constructor,
249
- M extends Constructor,
250
- >(
251
- a: (base: Base) => A,
252
- b: (base: A) => B,
253
- c: (base: B) => C,
254
- d: (base: C) => D,
255
- e: (base: D) => E,
256
- f: (base: E) => F,
257
- g: (base: F) => G,
258
- h: (base: G) => H,
259
- i: (base: H) => I,
260
- j: (base: I) => J,
261
- k: (base: J) => K,
262
- l: (base: K) => L,
263
- m: (base: L) => M,
264
- ): M;
265
- export function BaseModelWith<
266
- A extends Constructor,
267
- B extends Constructor,
268
- C extends Constructor,
269
- D extends Constructor,
270
- E extends Constructor,
271
- F extends Constructor,
272
- G extends Constructor,
273
- H extends Constructor,
274
- I extends Constructor,
275
- J extends Constructor,
276
- K extends Constructor,
277
- L extends Constructor,
278
- M extends Constructor,
279
- N extends Constructor,
280
- >(
281
- a: (base: Base) => A,
282
- b: (base: A) => B,
283
- c: (base: B) => C,
284
- d: (base: C) => D,
285
- e: (base: D) => E,
286
- f: (base: E) => F,
287
- g: (base: F) => G,
288
- h: (base: G) => H,
289
- i: (base: H) => I,
290
- j: (base: I) => J,
291
- k: (base: J) => K,
292
- l: (base: K) => L,
293
- m: (base: L) => M,
294
- n: (base: M) => N,
295
- ): N;
296
- export function BaseModelWith<
297
- A extends Constructor,
298
- B extends Constructor,
299
- C extends Constructor,
300
- D extends Constructor,
301
- E extends Constructor,
302
- F extends Constructor,
303
- G extends Constructor,
304
- H extends Constructor,
305
- I extends Constructor,
306
- J extends Constructor,
307
- K extends Constructor,
308
- L extends Constructor,
309
- M extends Constructor,
310
- N extends Constructor,
311
- O extends Constructor,
312
- >(
313
- a: (base: Base) => A,
314
- b: (base: A) => B,
315
- c: (base: B) => C,
316
- d: (base: C) => D,
317
- e: (base: D) => E,
318
- f: (base: E) => F,
319
- g: (base: F) => G,
320
- h: (base: G) => H,
321
- i: (base: H) => I,
322
- j: (base: I) => J,
323
- k: (base: J) => K,
324
- l: (base: K) => L,
325
- m: (base: L) => M,
326
- n: (base: M) => N,
327
- o: (base: N) => O,
328
- ): O;
329
- export function BaseModelWith<
330
- A extends Constructor,
331
- B extends Constructor,
332
- C extends Constructor,
333
- D extends Constructor,
334
- E extends Constructor,
335
- F extends Constructor,
336
- G extends Constructor,
337
- H extends Constructor,
338
- I extends Constructor,
339
- J extends Constructor,
340
- K extends Constructor,
341
- L extends Constructor,
342
- M extends Constructor,
343
- N extends Constructor,
344
- O extends Constructor,
345
- P extends Constructor,
346
- >(
347
- a: (base: Base) => A,
348
- b: (base: A) => B,
349
- c: (base: B) => C,
350
- d: (base: C) => D,
351
- e: (base: D) => E,
352
- f: (base: E) => F,
353
- g: (base: F) => G,
354
- h: (base: G) => H,
355
- i: (base: H) => I,
356
- j: (base: I) => J,
357
- k: (base: J) => K,
358
- l: (base: K) => L,
359
- m: (base: L) => M,
360
- n: (base: M) => N,
361
- o: (base: N) => O,
362
- p: (base: O) => P,
363
- ): P;
364
- export function BaseModelWith<
365
- A extends Constructor,
366
- B extends Constructor,
367
- C extends Constructor,
368
- D extends Constructor,
369
- E extends Constructor,
370
- F extends Constructor,
371
- G extends Constructor,
372
- H extends Constructor,
373
- I extends Constructor,
374
- J extends Constructor,
375
- K extends Constructor,
376
- L extends Constructor,
377
- M extends Constructor,
378
- N extends Constructor,
379
- O extends Constructor,
380
- P extends Constructor,
381
- Q extends Constructor,
382
- >(
383
- a: (base: Base) => A,
384
- b: (base: A) => B,
385
- c: (base: B) => C,
386
- d: (base: C) => D,
387
- e: (base: D) => E,
388
- f: (base: E) => F,
389
- g: (base: F) => G,
390
- h: (base: G) => H,
391
- i: (base: H) => I,
392
- j: (base: I) => J,
393
- k: (base: J) => K,
394
- l: (base: K) => L,
395
- m: (base: L) => M,
396
- n: (base: M) => N,
397
- o: (base: N) => O,
398
- p: (base: O) => P,
399
- q: (base: P) => Q,
400
- ): Q;
401
- export function BaseModelWith<
402
- A extends Constructor,
403
- B extends Constructor,
404
- C extends Constructor,
405
- D extends Constructor,
406
- E extends Constructor,
407
- F extends Constructor,
408
- G extends Constructor,
409
- H extends Constructor,
410
- I extends Constructor,
411
- J extends Constructor,
412
- K extends Constructor,
413
- L extends Constructor,
414
- M extends Constructor,
415
- N extends Constructor,
416
- O extends Constructor,
417
- P extends Constructor,
418
- Q extends Constructor,
419
- R extends Constructor,
420
- >(
421
- a: (base: Base) => A,
422
- b: (base: A) => B,
423
- c: (base: B) => C,
424
- d: (base: C) => D,
425
- e: (base: D) => E,
426
- f: (base: E) => F,
427
- g: (base: F) => G,
428
- h: (base: G) => H,
429
- i: (base: H) => I,
430
- j: (base: I) => J,
431
- k: (base: J) => K,
432
- l: (base: K) => L,
433
- m: (base: L) => M,
434
- n: (base: M) => N,
435
- o: (base: N) => O,
436
- p: (base: O) => P,
437
- q: (base: P) => Q,
438
- r: (base: Q) => R,
439
- ): R;
440
- export function BaseModelWith<
441
- A extends Constructor,
442
- B extends Constructor,
443
- C extends Constructor,
444
- D extends Constructor,
445
- E extends Constructor,
446
- F extends Constructor,
447
- G extends Constructor,
448
- H extends Constructor,
449
- I extends Constructor,
450
- J extends Constructor,
451
- K extends Constructor,
452
- L extends Constructor,
453
- M extends Constructor,
454
- N extends Constructor,
455
- O extends Constructor,
456
- P extends Constructor,
457
- Q extends Constructor,
458
- R extends Constructor,
459
- S extends Constructor,
460
- >(
461
- a: (base: Base) => A,
462
- b: (base: A) => B,
463
- c: (base: B) => C,
464
- d: (base: C) => D,
465
- e: (base: D) => E,
466
- f: (base: E) => F,
467
- g: (base: F) => G,
468
- h: (base: G) => H,
469
- i: (base: H) => I,
470
- j: (base: I) => J,
471
- k: (base: J) => K,
472
- l: (base: K) => L,
473
- m: (base: L) => M,
474
- n: (base: M) => N,
475
- o: (base: N) => O,
476
- p: (base: O) => P,
477
- q: (base: P) => Q,
478
- r: (base: Q) => R,
479
- s: (base: R) => S,
480
- ): S;
481
- export function BaseModelWith<
482
- A extends Constructor,
483
- B extends Constructor,
484
- C extends Constructor,
485
- D extends Constructor,
486
- E extends Constructor,
487
- F extends Constructor,
488
- G extends Constructor,
489
- H extends Constructor,
490
- I extends Constructor,
491
- J extends Constructor,
492
- K extends Constructor,
493
- L extends Constructor,
494
- M extends Constructor,
495
- N extends Constructor,
496
- O extends Constructor,
497
- P extends Constructor,
498
- Q extends Constructor,
499
- R extends Constructor,
500
- S extends Constructor,
501
- T extends Constructor,
502
- >(
503
- a: (base: Base) => A,
504
- b: (base: A) => B,
505
- c: (base: B) => C,
506
- d: (base: C) => D,
507
- e: (base: D) => E,
508
- f: (base: E) => F,
509
- g: (base: F) => G,
510
- h: (base: G) => H,
511
- i: (base: H) => I,
512
- j: (base: I) => J,
513
- k: (base: J) => K,
514
- l: (base: K) => L,
515
- m: (base: L) => M,
516
- n: (base: M) => N,
517
- o: (base: N) => O,
518
- p: (base: O) => P,
519
- q: (base: P) => Q,
520
- r: (base: Q) => R,
521
- s: (base: R) => S,
522
- t: (base: S) => T,
523
- ): T;
49
+ export interface Compose {
50
+ <TBase extends Constructor, A extends Constructor>(this: TBase, a: (base: TBase) => A): A;
51
+ <TBase extends Constructor, A extends Constructor, B extends Constructor>(
52
+ this: TBase,
53
+ a: (base: TBase) => A,
54
+ b: (base: A) => B,
55
+ ): B;
56
+ <TBase extends Constructor, A extends Constructor, B extends Constructor, C extends Constructor>(
57
+ this: TBase,
58
+ a: (base: TBase) => A,
59
+ b: (base: A) => B,
60
+ c: (base: B) => C,
61
+ ): C;
62
+ <
63
+ TBase extends Constructor,
64
+ A extends Constructor,
65
+ B extends Constructor,
66
+ C extends Constructor,
67
+ D extends Constructor,
68
+ >(
69
+ this: TBase,
70
+ a: (base: TBase) => A,
71
+ b: (base: A) => B,
72
+ c: (base: B) => C,
73
+ d: (base: C) => D,
74
+ ): D;
75
+ <
76
+ TBase extends Constructor,
77
+ A extends Constructor,
78
+ B extends Constructor,
79
+ C extends Constructor,
80
+ D extends Constructor,
81
+ E extends Constructor,
82
+ >(
83
+ this: TBase,
84
+ a: (base: TBase) => A,
85
+ b: (base: A) => B,
86
+ c: (base: B) => C,
87
+ d: (base: C) => D,
88
+ e: (base: D) => E,
89
+ ): E;
90
+ <
91
+ TBase extends Constructor,
92
+ A extends Constructor,
93
+ B extends Constructor,
94
+ C extends Constructor,
95
+ D extends Constructor,
96
+ E extends Constructor,
97
+ F extends Constructor,
98
+ >(
99
+ this: TBase,
100
+ a: (base: TBase) => A,
101
+ b: (base: A) => B,
102
+ c: (base: B) => C,
103
+ d: (base: C) => D,
104
+ e: (base: D) => E,
105
+ f: (base: E) => F,
106
+ ): F;
107
+ <
108
+ TBase extends Constructor,
109
+ A extends Constructor,
110
+ B extends Constructor,
111
+ C extends Constructor,
112
+ D extends Constructor,
113
+ E extends Constructor,
114
+ F extends Constructor,
115
+ G extends Constructor,
116
+ >(
117
+ this: TBase,
118
+ a: (base: TBase) => A,
119
+ b: (base: A) => B,
120
+ c: (base: B) => C,
121
+ d: (base: C) => D,
122
+ e: (base: D) => E,
123
+ f: (base: E) => F,
124
+ g: (base: F) => G,
125
+ ): G;
126
+ <
127
+ TBase extends Constructor,
128
+ A extends Constructor,
129
+ B extends Constructor,
130
+ C extends Constructor,
131
+ D extends Constructor,
132
+ E extends Constructor,
133
+ F extends Constructor,
134
+ G extends Constructor,
135
+ H extends Constructor,
136
+ >(
137
+ this: TBase,
138
+ a: (base: TBase) => A,
139
+ b: (base: A) => B,
140
+ c: (base: B) => C,
141
+ d: (base: C) => D,
142
+ e: (base: D) => E,
143
+ f: (base: E) => F,
144
+ g: (base: F) => G,
145
+ h: (base: G) => H,
146
+ ): H;
147
+ }
524
148
 
525
- // Implementation: fold the mixins over BaseModel, left to right. The base param is
526
- // `any` so each typed overload above (whose base is the narrower `typeof BaseModel`
527
- // or a prior mixin's result) stays assignable to this implementation signature.
528
- export function BaseModelWith(
149
+ /**
150
+ * The fold behind {@link Compose}: apply each mixin left-to-right, seeding from `this` (the
151
+ * class `using` was called on). Assigned to `BaseModel.using` not exported publicly.
152
+ *
153
+ * @internal
154
+ */
155
+ export const _compose = function (
156
+ this: Constructor,
529
157
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- variadic mixin folder
530
158
  ...mixins: Array<(base: any) => Constructor>
531
159
  ): Constructor {
532
- return mixins.reduce<Constructor>(
533
- (acc, mixin) => mixin(acc),
534
- BaseModel as unknown as Constructor,
535
- );
536
- }
160
+ return mixins.reduce<Constructor>((acc, mixin) => mixin(acc), this);
161
+ } as Compose;