@shaferllc/keel 0.80.0 → 0.81.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/dist/accounts/accounts.config.stub +50 -0
- package/dist/accounts/config.d.ts +46 -0
- package/dist/accounts/config.js +39 -0
- package/dist/accounts/flows.d.ts +50 -0
- package/dist/accounts/flows.js +133 -0
- package/dist/accounts/index.d.ts +28 -0
- package/dist/accounts/index.js +23 -0
- package/dist/accounts/migration.d.ts +14 -0
- package/dist/accounts/migration.js +39 -0
- package/dist/accounts/provider.d.ts +18 -0
- package/dist/accounts/provider.js +37 -0
- package/dist/accounts/routes.d.ts +15 -0
- package/dist/accounts/routes.js +116 -0
- package/dist/accounts/store.d.ts +33 -0
- package/dist/accounts/store.js +37 -0
- package/dist/accounts/tokens.d.ts +60 -0
- package/dist/accounts/tokens.js +116 -0
- package/dist/accounts/totp.d.ts +58 -0
- package/dist/accounts/totp.js +134 -0
- package/dist/accounts/two-factor.d.ts +56 -0
- package/dist/accounts/two-factor.js +146 -0
- package/dist/core/database.d.ts +36 -0
- package/dist/core/database.js +141 -4
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.js +3 -2
- package/dist/core/migrations.d.ts +52 -2
- package/dist/core/migrations.js +134 -3
- package/dist/core/model-events.d.ts +34 -0
- package/dist/core/model-events.js +71 -0
- package/dist/core/model-query.d.ts +68 -0
- package/dist/core/model-query.js +234 -0
- package/dist/core/model.d.ts +91 -4
- package/dist/core/model.js +217 -32
- package/dist/core/relations.d.ts +53 -0
- package/dist/core/relations.js +242 -0
- package/docs/accounts.md +214 -0
- package/docs/ai-manifest.json +63 -1
- package/docs/database.md +33 -0
- package/docs/examples/accounts.ts +150 -0
- package/docs/migrations.md +32 -3
- package/docs/models.md +133 -3
- package/llms-full.txt +419 -6
- package/llms.txt +2 -0
- package/package.json +6 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A model-aware query builder — what `Model.query()` sugar and eager loading are
|
|
3
|
+
* built on. It wraps the plain `QueryBuilder`, hydrates rows into models (firing
|
|
4
|
+
* `retrieved`), and adds the relationship-aware operations Eloquent has and a raw
|
|
5
|
+
* builder can't: `with()` (nested eager loading), `withCount()`, and existence
|
|
6
|
+
* filters `has()` / `whereHas()` / `doesntHave()`.
|
|
7
|
+
*
|
|
8
|
+
* const users = await User.query()
|
|
9
|
+
* .where("active", true)
|
|
10
|
+
* .with("posts.comments")
|
|
11
|
+
* .withCount("posts")
|
|
12
|
+
* .whereHas("posts", (q) => q.where("published", true))
|
|
13
|
+
* .get();
|
|
14
|
+
*
|
|
15
|
+
* Existence filters use the same two-query strategy as the relations themselves
|
|
16
|
+
* (a `whereIn` over keys gathered from the related table), so everything stays on
|
|
17
|
+
* the driver-agnostic builder — no JOIN or correlated subquery required. They're
|
|
18
|
+
* recorded and resolved at `get()`/`first()`/`count()` time so the chain stays
|
|
19
|
+
* synchronous.
|
|
20
|
+
*/
|
|
21
|
+
import { fireModelEvent } from "./model-events.js";
|
|
22
|
+
/** Group dotted eager-load paths by their first segment. */
|
|
23
|
+
function groupPaths(paths) {
|
|
24
|
+
const groups = new Map();
|
|
25
|
+
for (const path of paths) {
|
|
26
|
+
const [top, ...rest] = path.split(".");
|
|
27
|
+
const nested = groups.get(top) ?? [];
|
|
28
|
+
if (rest.length)
|
|
29
|
+
nested.push(rest.join("."));
|
|
30
|
+
groups.set(top, nested);
|
|
31
|
+
}
|
|
32
|
+
return groups;
|
|
33
|
+
}
|
|
34
|
+
/** Recursively eager-load dotted relation paths onto a set of models. */
|
|
35
|
+
async function eagerLoad(models, paths) {
|
|
36
|
+
if (!models.length)
|
|
37
|
+
return;
|
|
38
|
+
for (const [top, nested] of groupPaths(paths)) {
|
|
39
|
+
const relation = models[0][top]?.();
|
|
40
|
+
if (!relation)
|
|
41
|
+
throw new Error(`${models[0].constructor.name} has no relation "${top}"`);
|
|
42
|
+
await relation.eager(models, top);
|
|
43
|
+
if (nested.length) {
|
|
44
|
+
const children = [];
|
|
45
|
+
for (const model of models) {
|
|
46
|
+
const loaded = model.getRelation(top);
|
|
47
|
+
if (Array.isArray(loaded))
|
|
48
|
+
children.push(...loaded);
|
|
49
|
+
else if (loaded)
|
|
50
|
+
children.push(loaded);
|
|
51
|
+
}
|
|
52
|
+
await eagerLoad(children, nested);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export class ModelQuery {
|
|
57
|
+
cls;
|
|
58
|
+
builder;
|
|
59
|
+
eagers = [];
|
|
60
|
+
counts = [];
|
|
61
|
+
hasConstraints = [];
|
|
62
|
+
constructor(cls, builder) {
|
|
63
|
+
this.cls = cls;
|
|
64
|
+
this.builder = builder;
|
|
65
|
+
}
|
|
66
|
+
/** The wrapped builder, for anything `ModelQuery` doesn't proxy. */
|
|
67
|
+
toBase() {
|
|
68
|
+
return this.builder;
|
|
69
|
+
}
|
|
70
|
+
/* --------------------------- builder passthrough --------------------------- */
|
|
71
|
+
where(column, opOrValue, value) {
|
|
72
|
+
if (value === undefined)
|
|
73
|
+
this.builder.where(column, opOrValue);
|
|
74
|
+
else
|
|
75
|
+
this.builder.where(column, opOrValue, value);
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
orWhere(column, opOrValue, value) {
|
|
79
|
+
if (value === undefined)
|
|
80
|
+
this.builder.orWhere(column, opOrValue);
|
|
81
|
+
else
|
|
82
|
+
this.builder.orWhere(column, opOrValue, value);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
whereIn(column, values) {
|
|
86
|
+
this.builder.whereIn(column, values);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
whereNotIn(column, values) {
|
|
90
|
+
this.builder.whereNotIn(column, values);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
whereNull(column) {
|
|
94
|
+
this.builder.whereNull(column);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
whereNotNull(column) {
|
|
98
|
+
this.builder.whereNotNull(column);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
whereBetween(column, range) {
|
|
102
|
+
this.builder.whereBetween(column, range);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
whereLike(column, pattern) {
|
|
106
|
+
this.builder.whereLike(column, pattern);
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
orderBy(column, direction = "asc") {
|
|
110
|
+
this.builder.orderBy(column, direction);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
latest(column = "created_at") {
|
|
114
|
+
this.builder.latest(column);
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
oldest(column = "created_at") {
|
|
118
|
+
this.builder.oldest(column);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
limit(n) {
|
|
122
|
+
this.builder.limit(n);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
offset(n) {
|
|
126
|
+
this.builder.offset(n);
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
/* ---------------------------- relationship ops ----------------------------- */
|
|
130
|
+
/** Eager-load relations (dotted paths for nesting: `"posts.comments"`). */
|
|
131
|
+
with(...names) {
|
|
132
|
+
this.eagers.push(...names);
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
/** Add a `<relation>_count` to each result. */
|
|
136
|
+
withCount(...names) {
|
|
137
|
+
this.counts.push(...names);
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/** Constrain to models that have at least one related row. */
|
|
141
|
+
has(name) {
|
|
142
|
+
this.hasConstraints.push({ name, negate: false });
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
/** Constrain to models whose related rows match `constrain`. */
|
|
146
|
+
whereHas(name, constrain) {
|
|
147
|
+
this.hasConstraints.push({ name, ...(constrain ? { constrain } : {}), negate: false });
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
/** Constrain to models with no matching related row. */
|
|
151
|
+
doesntHave(name, constrain) {
|
|
152
|
+
this.hasConstraints.push({ name, ...(constrain ? { constrain } : {}), negate: true });
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
/* -------------------------------- terminals -------------------------------- */
|
|
156
|
+
async get() {
|
|
157
|
+
await this.resolveHasConstraints();
|
|
158
|
+
const rows = await this.builder.get();
|
|
159
|
+
const models = await Promise.all(rows.map((row) => this.hydrate(row)));
|
|
160
|
+
if (this.eagers.length)
|
|
161
|
+
await eagerLoad(models, this.eagers);
|
|
162
|
+
if (this.counts.length)
|
|
163
|
+
await this.loadCounts(models);
|
|
164
|
+
return models;
|
|
165
|
+
}
|
|
166
|
+
async first() {
|
|
167
|
+
this.builder.limit(1);
|
|
168
|
+
const [model] = await this.get();
|
|
169
|
+
return model ?? null;
|
|
170
|
+
}
|
|
171
|
+
async count() {
|
|
172
|
+
await this.resolveHasConstraints();
|
|
173
|
+
return this.builder.count();
|
|
174
|
+
}
|
|
175
|
+
async exists() {
|
|
176
|
+
return (await this.count()) > 0;
|
|
177
|
+
}
|
|
178
|
+
async paginate(page = 1, perPage = 15) {
|
|
179
|
+
await this.resolveHasConstraints();
|
|
180
|
+
const result = await this.builder.paginate(page, perPage);
|
|
181
|
+
const data = await Promise.all(result.data.map((row) => this.hydrate(row)));
|
|
182
|
+
if (this.eagers.length)
|
|
183
|
+
await eagerLoad(data, this.eagers);
|
|
184
|
+
if (this.counts.length)
|
|
185
|
+
await this.loadCounts(data);
|
|
186
|
+
return { ...result, data };
|
|
187
|
+
}
|
|
188
|
+
/* -------------------------------- internals -------------------------------- */
|
|
189
|
+
async hydrate(row) {
|
|
190
|
+
const model = new this.cls(row);
|
|
191
|
+
await fireModelEvent(this.cls, "retrieved", model);
|
|
192
|
+
return model;
|
|
193
|
+
}
|
|
194
|
+
relationFor(name) {
|
|
195
|
+
const probe = new this.cls();
|
|
196
|
+
const method = probe[name];
|
|
197
|
+
if (typeof method !== "function") {
|
|
198
|
+
throw new Error(`${this.cls.name} has no relation "${name}"`);
|
|
199
|
+
}
|
|
200
|
+
return method.call(probe);
|
|
201
|
+
}
|
|
202
|
+
/** Turn recorded has/doesntHave constraints into `whereIn`/`whereNotIn` on the builder. */
|
|
203
|
+
async resolveHasConstraints() {
|
|
204
|
+
for (const { name, constrain, negate } of this.hasConstraints) {
|
|
205
|
+
const relation = this.relationFor(name);
|
|
206
|
+
const keys = await relation.matchingParentKeys(constrain);
|
|
207
|
+
const column = relation.parentColumn();
|
|
208
|
+
if (negate) {
|
|
209
|
+
if (keys.length)
|
|
210
|
+
this.builder.whereNotIn(column, keys);
|
|
211
|
+
}
|
|
212
|
+
else if (keys.length) {
|
|
213
|
+
this.builder.whereIn(column, keys);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// No related rows exist → nothing can match. A contradiction yields zero
|
|
217
|
+
// rows without an invalid empty `IN ()`.
|
|
218
|
+
this.builder.whereNull(column).whereNotNull(column);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.hasConstraints = [];
|
|
222
|
+
}
|
|
223
|
+
async loadCounts(models) {
|
|
224
|
+
for (const name of this.counts) {
|
|
225
|
+
const relation = this.relationFor(name);
|
|
226
|
+
const column = relation.parentColumn();
|
|
227
|
+
const keys = [...new Set(models.map((m) => m[column]).filter((v) => v != null))];
|
|
228
|
+
const counts = await relation.countsByParent(keys);
|
|
229
|
+
for (const model of models) {
|
|
230
|
+
model[`${name}_count`] = counts.get(model[column]) ?? 0;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
package/dist/core/model.d.ts
CHANGED
|
@@ -16,8 +16,14 @@
|
|
|
16
16
|
* await user.save();
|
|
17
17
|
*/
|
|
18
18
|
import { type QueryBuilder, type Row, type Paginated } from "./database.js";
|
|
19
|
-
import { BelongsTo, BelongsToMany, HasMany, HasOne } from "./relations.js";
|
|
19
|
+
import { BelongsTo, BelongsToMany, HasMany, HasOne, MorphMany, MorphOne, MorphTo } from "./relations.js";
|
|
20
20
|
import { type Casts } from "./casts.js";
|
|
21
|
+
import { type ModelHook, type ModelObserver } from "./model-events.js";
|
|
22
|
+
import { ModelQuery } from "./model-query.js";
|
|
23
|
+
/** A global scope: a constraint applied to every query a model builds. */
|
|
24
|
+
export type GlobalScope = (query: QueryBuilder) => void;
|
|
25
|
+
/** How a model query treats soft-deleted rows. */
|
|
26
|
+
type TrashedMode = "exclude" | "with" | "only";
|
|
21
27
|
type ModelClass<T extends Model> = (new (attributes?: Row) => T) & typeof Model;
|
|
22
28
|
export declare class Model {
|
|
23
29
|
static table: string;
|
|
@@ -34,10 +40,78 @@ export declare class Model {
|
|
|
34
40
|
static timestamps: boolean;
|
|
35
41
|
static createdAtColumn: string;
|
|
36
42
|
static updatedAtColumn: string;
|
|
43
|
+
/** Columns stripped from `toJSON()` output (e.g. `["password"]`). */
|
|
44
|
+
static hidden: string[];
|
|
45
|
+
/** If set, ONLY these keys survive `toJSON()` — an allowlist that wins over `hidden`. */
|
|
46
|
+
static visible: string[];
|
|
47
|
+
/** Computed accessor names (getters or zero-arg methods) added to `toJSON()`. */
|
|
48
|
+
static appends: string[];
|
|
49
|
+
/** Soft deletes: `delete()` sets `deleted_at` instead of removing the row. */
|
|
50
|
+
static softDeletes: boolean;
|
|
51
|
+
static deletedAtColumn: string;
|
|
37
52
|
[key: string]: unknown;
|
|
38
53
|
constructor(attributes?: Row);
|
|
39
|
-
/**
|
|
54
|
+
/** Register a global scope — a constraint applied to every query this model builds. */
|
|
55
|
+
static addGlobalScope(name: string, scope: GlobalScope): void;
|
|
56
|
+
/** Build this model's base query, applying global scopes and the soft-delete filter. */
|
|
57
|
+
protected static baseQuery(trashed?: TrashedMode): QueryBuilder;
|
|
58
|
+
/** A query builder scoped to this model's table (global scopes applied). */
|
|
40
59
|
static query(): QueryBuilder;
|
|
60
|
+
/** Include soft-deleted rows (bypasses the soft-delete scope). */
|
|
61
|
+
static withTrashed(): QueryBuilder;
|
|
62
|
+
/** Only soft-deleted rows. */
|
|
63
|
+
static onlyTrashed(): QueryBuilder;
|
|
64
|
+
/** A model-aware query — hydrates to models and supports `with`/`withCount`/`whereHas`. */
|
|
65
|
+
static newQuery<T extends Model>(this: ModelClass<T>): ModelQuery<T>;
|
|
66
|
+
/** Start a query eager-loading the given relations (dotted paths nest). */
|
|
67
|
+
static with<T extends Model>(this: ModelClass<T>, ...names: string[]): ModelQuery<T>;
|
|
68
|
+
/** Start a query counting the given relations into `<relation>_count`. */
|
|
69
|
+
static withCount<T extends Model>(this: ModelClass<T>, ...names: string[]): ModelQuery<T>;
|
|
70
|
+
/** Start a query constrained to models that have at least one related row. */
|
|
71
|
+
static has<T extends Model>(this: ModelClass<T>, name: string): ModelQuery<T>;
|
|
72
|
+
/** Start a query constrained to models whose related rows match `constrain`. */
|
|
73
|
+
static whereHas<T extends Model>(this: ModelClass<T>, name: string, constrain?: (q: QueryBuilder) => void): ModelQuery<T>;
|
|
74
|
+
/** Start a query constrained to models with no matching related row. */
|
|
75
|
+
static doesntHave<T extends Model>(this: ModelClass<T>, name: string, constrain?: (q: QueryBuilder) => void): ModelQuery<T>;
|
|
76
|
+
/** Construct a model from a row and fire its `retrieved` event. */
|
|
77
|
+
protected static hydrate<T extends Model>(this: ModelClass<T>, row: Row): Promise<T>;
|
|
78
|
+
static creating<T extends Model>(this: {
|
|
79
|
+
new (...args: never[]): T;
|
|
80
|
+
}, hook: ModelHook<T>): void;
|
|
81
|
+
static created<T extends Model>(this: {
|
|
82
|
+
new (...args: never[]): T;
|
|
83
|
+
}, hook: ModelHook<T>): void;
|
|
84
|
+
static updating<T extends Model>(this: {
|
|
85
|
+
new (...args: never[]): T;
|
|
86
|
+
}, hook: ModelHook<T>): void;
|
|
87
|
+
static updated<T extends Model>(this: {
|
|
88
|
+
new (...args: never[]): T;
|
|
89
|
+
}, hook: ModelHook<T>): void;
|
|
90
|
+
static saving<T extends Model>(this: {
|
|
91
|
+
new (...args: never[]): T;
|
|
92
|
+
}, hook: ModelHook<T>): void;
|
|
93
|
+
static saved<T extends Model>(this: {
|
|
94
|
+
new (...args: never[]): T;
|
|
95
|
+
}, hook: ModelHook<T>): void;
|
|
96
|
+
static deleting<T extends Model>(this: {
|
|
97
|
+
new (...args: never[]): T;
|
|
98
|
+
}, hook: ModelHook<T>): void;
|
|
99
|
+
static deleted<T extends Model>(this: {
|
|
100
|
+
new (...args: never[]): T;
|
|
101
|
+
}, hook: ModelHook<T>): void;
|
|
102
|
+
static restoring<T extends Model>(this: {
|
|
103
|
+
new (...args: never[]): T;
|
|
104
|
+
}, hook: ModelHook<T>): void;
|
|
105
|
+
static restored<T extends Model>(this: {
|
|
106
|
+
new (...args: never[]): T;
|
|
107
|
+
}, hook: ModelHook<T>): void;
|
|
108
|
+
static retrieved<T extends Model>(this: {
|
|
109
|
+
new (...args: never[]): T;
|
|
110
|
+
}, hook: ModelHook<T>): void;
|
|
111
|
+
/** Register an observer whose methods are named after the events they handle. */
|
|
112
|
+
static observe<T extends Model>(this: {
|
|
113
|
+
new (...args: never[]): T;
|
|
114
|
+
}, observer: ModelObserver<T>): void;
|
|
41
115
|
/** Keep only the attributes mass-assignment allows (fillable / guarded). */
|
|
42
116
|
static filterFillable(attributes: Row): Row;
|
|
43
117
|
/** Cast attributes to their storable primitives for a write. */
|
|
@@ -57,7 +131,7 @@ export declare class Model {
|
|
|
57
131
|
static firstOrCreate<T extends Model>(this: ModelClass<T>, match: Row, values?: Row): Promise<T>;
|
|
58
132
|
/** Update the first row matching `match` with `values`, or create it. */
|
|
59
133
|
static updateOrCreate<T extends Model>(this: ModelClass<T>, match: Row, values?: Row): Promise<T>;
|
|
60
|
-
/** A query scoped to every column/value in `match
|
|
134
|
+
/** A query scoped to every column/value in `match` (global scopes applied). */
|
|
61
135
|
private static matching;
|
|
62
136
|
/**
|
|
63
137
|
* Eager-load relations onto an array of models with one extra query each —
|
|
@@ -75,17 +149,30 @@ export declare class Model {
|
|
|
75
149
|
belongsTo<T extends Model>(related: ModelClass<T>, foreignKey?: string, ownerKey?: string): BelongsTo<T>;
|
|
76
150
|
/** Many-to-many through a pivot table (default name: the two tables, sorted). */
|
|
77
151
|
belongsToMany<T extends Model>(related: ModelClass<T>, pivotTable?: string, foreignPivotKey?: string, relatedPivotKey?: string, parentKey?: string, relatedKey?: string): BelongsToMany<T>;
|
|
152
|
+
/** Polymorphic one-to-many: children carry `<name>_id` + `<name>_type`. */
|
|
153
|
+
morphMany<T extends Model>(related: ModelClass<T>, name: string, localKey?: string): MorphMany<T>;
|
|
154
|
+
/** Polymorphic one-to-one. */
|
|
155
|
+
morphOne<T extends Model>(related: ModelClass<T>, name: string, localKey?: string): MorphOne<T>;
|
|
156
|
+
/** The owning side of a polymorphic relation — resolves via `<name>_type` (see `registerMorphType`). */
|
|
157
|
+
morphTo(name: string, idColumn?: string, typeColumn?: string): MorphTo;
|
|
78
158
|
/** Store an eager-loaded relation result under `name`. */
|
|
79
159
|
setRelation(name: string, value: unknown): this;
|
|
80
160
|
/** Read a previously loaded relation (returns undefined if not loaded). */
|
|
81
161
|
getRelation<T = unknown>(name: string): T | undefined;
|
|
82
|
-
/** Insert (no primary key) or update (has one). */
|
|
162
|
+
/** Insert (no primary key) or update (has one). Fires save/create/update events. */
|
|
83
163
|
save(): Promise<this>;
|
|
84
164
|
/** Mass-assign then save — `fill` + `save` in one call. */
|
|
85
165
|
update(attributes: Row): Promise<this>;
|
|
86
166
|
/** Reload this model's columns from the database. */
|
|
87
167
|
refresh(): Promise<this>;
|
|
168
|
+
/** Delete the row — or, with soft deletes on, set `deleted_at`. Fires delete events. */
|
|
88
169
|
delete(): Promise<void>;
|
|
170
|
+
/** Permanently delete a soft-deletable row (bypasses soft deletes). */
|
|
171
|
+
forceDelete(): Promise<void>;
|
|
172
|
+
/** Restore a soft-deleted row (clears `deleted_at`). Fires restore events. */
|
|
173
|
+
restore(): Promise<this>;
|
|
174
|
+
/** Whether this soft-deletable model is currently trashed. */
|
|
175
|
+
trashed(): boolean;
|
|
89
176
|
/** Merge mass-assignable attributes into the model (cast, not saved). */
|
|
90
177
|
fill(attributes: Row): this;
|
|
91
178
|
/** Force-assign attributes, bypassing mass-assignment guarding (still cast). */
|