@bunyad/orm 0.2.0-beta.1 → 0.2.0-beta.2

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.
@@ -12,12 +12,34 @@ type RelationWriteKey = (typeof RELATION_QUERY_WRITE)[number];
12
12
  /** Types for the methods `installRelationQueryForwarders` adds to every relation. */
13
13
  type RelationQuery<T extends Model> = Pick<ModelQuery<T>, RelationQueryKey>;
14
14
  type RelationWrite<T extends Model> = Pick<ModelQuery<T>, RelationWriteKey>;
15
- /** Strip `__pivot_*` columns onto `model.pivot`. */
16
- export declare function applyPivotAttributes(model: Model, attrs: Record<string, unknown>, pivotKeys: string[]): Model;
17
- export declare function morphMap(map: Record<string, ModelClass>): void;
15
+ /**
16
+ * Strip `__pivot_*` columns onto `model.pivot` (or the `as()` accessor). With
17
+ * `using(PivotModel)` the pivot is an instance of that model, so its casts and
18
+ * methods apply.
19
+ */
20
+ export declare function applyPivotAttributes(model: Model, attrs: Record<string, unknown>, pivotKeys: string[], options?: {
21
+ accessor?: string;
22
+ pivotClass?: ModelClass;
23
+ }): Model;
24
+ export declare function registerMorphClass(model: ModelClass): void;
25
+ /**
26
+ * `Relation::morphMap` — register aliases, or read the current map when called
27
+ * with no arguments. Pass `merge = false` to replace the existing map.
28
+ */
29
+ export declare function morphMap(): Record<string, ModelClass>;
30
+ export declare function morphMap(map: Record<string, ModelClass>, merge?: boolean): Record<string, ModelClass>;
31
+ /** `Relation::enforceMorphMap` — register the map and refuse unmapped classes. */
32
+ export declare function enforceMorphMap(map: Record<string, ModelClass>, merge?: boolean): Record<string, ModelClass>;
33
+ /** `Relation::requireMorphMap` — throw when a model without an alias is used polymorphically. */
34
+ export declare function requireMorphMap(required?: boolean): void;
35
+ export declare function requiresMorphMap(): boolean;
18
36
  export declare function clearMorphMap(): void;
19
37
  export declare function morphTypeFor(model: ModelClass): string;
38
+ /** `Relation::getMorphedModel` — class for an alias, or undefined. */
39
+ export declare function getMorphedModel(type: string): ModelClass | undefined;
20
40
  export declare function resolveMorphType(type: string): ModelClass;
41
+ /** `withDefault` support shared by the single-result relations (belongsTo, hasOne, morphOne). */
42
+ export type DefaultSpec<T extends Model> = true | Record<string, unknown> | ((model: T, parent: Model) => void);
21
43
  /** MorphMany relation. */
22
44
  export declare class MorphMany<T extends Model = Model> {
23
45
  #private;
@@ -34,6 +56,13 @@ export declare class MorphMany<T extends Model = Model> {
34
56
  getMorphType(): string;
35
57
  getLocalKeyName(): string;
36
58
  /** Constrained related query (`morphMany().where(…).get()`). */
59
+ /**
60
+ * `chaperone($inverse)` — set the parent on every loaded child (`post.user` without a query).
61
+ * The inverse name defaults to the parent class name in camelCase.
62
+ */
63
+ chaperone(inverse?: string): this;
64
+ /** Property the parent is set on, or `undefined` when `chaperone()` was not called. */
65
+ getInverse(): string | undefined;
37
66
  getQuery(): ModelQuery<T>;
38
67
  get(): Promise<OrmCollection<T>>;
39
68
  first(): Promise<T | null>;
@@ -60,6 +89,11 @@ export declare class MorphOne<T extends Model = Model> {
60
89
  latestOfMany(column?: string): this;
61
90
  oldestOfMany(column?: string): this;
62
91
  ofMany(column: string, aggregate: "min" | "max"): this;
92
+ /** `withDefault` — return an empty related model instead of `null` when nothing matches. */
93
+ withDefault(spec?: DefaultSpec<T>): this;
94
+ hasDefault(): boolean;
95
+ /** The default model for the owner this relation was built from. */
96
+ makeDefault(): T;
63
97
  getQuery(): ModelQuery<T>;
64
98
  get(): Promise<T | null>;
65
99
  first(): Promise<T | null>;
@@ -94,6 +128,13 @@ export declare class HasMany<T extends Model = Model> {
94
128
  getForeignKeyName(): string;
95
129
  getLocalKeyName(): string;
96
130
  /** Constrained related query (`hasMany().where(…).orderBy(…).get()`). */
131
+ /**
132
+ * `chaperone($inverse)` — set the parent on every loaded child (`post.user` without a query).
133
+ * The inverse name defaults to the parent class name in camelCase.
134
+ */
135
+ chaperone(inverse?: string): this;
136
+ /** Property the parent is set on, or `undefined` when `chaperone()` was not called. */
137
+ getInverse(): string | undefined;
97
138
  getQuery(): ModelQuery<T>;
98
139
  get(): Promise<OrmCollection<T>>;
99
140
  first(): Promise<T | null>;
@@ -121,6 +162,18 @@ export declare class HasOne<T extends Model = Model> {
121
162
  oldestOfMany(column?: string): this;
122
163
  /** Constrain to the related row with min/max of `column`. */
123
164
  ofMany(column: string, aggregate: "min" | "max"): this;
165
+ /** `withDefault` — return an empty related model instead of `null` when nothing matches. */
166
+ withDefault(spec?: DefaultSpec<T>): this;
167
+ hasDefault(): boolean;
168
+ /** The default model for the owner this relation was built from. */
169
+ makeDefault(): T;
170
+ /**
171
+ * `chaperone($inverse)` — set the parent on every loaded child (`post.user` without a query).
172
+ * The inverse name defaults to the parent class name in camelCase.
173
+ */
174
+ chaperone(inverse?: string): this;
175
+ /** Property the parent is set on, or `undefined` when `chaperone()` was not called. */
176
+ getInverse(): string | undefined;
124
177
  getQuery(): ModelQuery<T>;
125
178
  get(): Promise<T | null>;
126
179
  first(): Promise<T | null>;
@@ -178,6 +231,11 @@ export declare class BelongsTo<T extends Model = Model> {
178
231
  getRelated(): ModelClass;
179
232
  getForeignKeyName(): string;
180
233
  getOwnerKeyName(): string;
234
+ /** `withDefault` — return an empty related model instead of `null` when nothing matches. */
235
+ withDefault(spec?: DefaultSpec<T>): this;
236
+ hasDefault(): boolean;
237
+ /** The default model for the owner this relation was built from. */
238
+ makeDefault(): T;
181
239
  getQuery(): ModelQuery<T>;
182
240
  get(): Promise<T | null>;
183
241
  first(): Promise<T | null>;
@@ -194,6 +252,38 @@ export declare class BelongsTo<T extends Model = Model> {
194
252
  touchSync(): boolean;
195
253
  }
196
254
  /** BelongsToMany relation. */
255
+ /**
256
+ * Point `child[inverse]` at its parent without making it enumerable: a back-reference
257
+ * must not be serialized (`toJSON`) or treated as an attribute, or parent → child → parent recurses.
258
+ */
259
+ export declare function setInverse(child: unknown, inverse: string, parent: Model): void;
260
+ /** `wherePivot*` constraints on a many-to-many pivot table (AND only). */
261
+ export type PivotWhere = {
262
+ kind: "basic";
263
+ column: string;
264
+ op: string;
265
+ value: unknown;
266
+ } | {
267
+ kind: "in";
268
+ column: string;
269
+ values: unknown[];
270
+ not: boolean;
271
+ } | {
272
+ kind: "null";
273
+ column: string;
274
+ not: boolean;
275
+ } | {
276
+ kind: "between";
277
+ column: string;
278
+ values: [unknown, unknown];
279
+ not: boolean;
280
+ };
281
+ export type PivotOrder = {
282
+ column: string;
283
+ direction: "asc" | "desc";
284
+ };
285
+ /** Apply pivot wheres to a ModelQuery / QueryBuilder, qualified with the pivot table. */
286
+ export declare function applyPivotWheres(q: unknown, pivotTable: string, wheres: readonly PivotWhere[]): void;
197
287
  export declare class BelongsToMany<T extends Model = Model> {
198
288
  #private;
199
289
  private parent;
@@ -212,13 +302,58 @@ export declare class BelongsToMany<T extends Model = Model> {
212
302
  getQuery(): ModelQuery<T>;
213
303
  get(): Promise<OrmCollection<T>>;
214
304
  first(): Promise<T | null>;
215
- attach(ids: Array<string | number> | string | number): Promise<void>;
216
- detach(ids?: Array<string | number> | string | number): Promise<void>;
217
- sync(ids: Array<string | number>): Promise<void>;
218
- /** Attach missing ids only. */
219
- syncWithoutDetaching(ids: Array<string | number>): Promise<void>;
220
- /** Attach missing, detach present. */
221
- toggle(ids: Array<string | number> | string | number): Promise<void>;
305
+ /** `withTimestamps` — stamp `created_at` / `updated_at` on pivot writes. */
306
+ withTimestamps(): this;
307
+ /** `as('membership')` — expose the pivot under another property name than `pivot`. */
308
+ as(accessor: string): this;
309
+ /**
310
+ * `using(MembershipPivot)` — hydrate the pivot as a model (casts and methods apply)
311
+ * and run pivot writes (`attach`, `sync`, `updateExistingPivot`) through its "set" casts.
312
+ */
313
+ using(pivotClass: ModelClass): this;
314
+ getPivotAccessor(): string;
315
+ getPivotClass(): ModelClass | undefined;
316
+ getPivotWheres(): PivotWhere[];
317
+ getPivotOrders(): PivotOrder[];
318
+ /**
319
+ * `wherePivot` — constrain the pivot row. Also scopes `attach` defaults, `sync`,
320
+ * `detach` and `updateExistingPivot`, so two relations on one pivot table with
321
+ * different `wherePivot` values stay independent.
322
+ */
323
+ wherePivot(column: string, value: unknown): this;
324
+ wherePivot(column: string, op: string, value: unknown): this;
325
+ wherePivotIn(column: string, values: unknown[]): this;
326
+ wherePivotNotIn(column: string, values: unknown[]): this;
327
+ wherePivotNull(column: string): this;
328
+ wherePivotNotNull(column: string): this;
329
+ wherePivotBetween(column: string, values: [unknown, unknown]): this;
330
+ wherePivotNotBetween(column: string, values: [unknown, unknown]): this;
331
+ /** `orderByPivot` — order related rows by a pivot column. */
332
+ orderByPivot(column: string, direction?: "asc" | "desc"): this;
333
+ /** `attach` — one multi-row INSERT per chunk; optional pivot attributes. */
334
+ attach(ids: PivotIds, attributes?: Record<string, unknown>): Promise<void>;
335
+ /** `detach` — returns the number of deleted pivot rows. */
336
+ detach(ids?: PivotIds): Promise<number>;
337
+ /** `updateExistingPivot` — update pivot columns for one related id. Returns affected rows. */
338
+ updateExistingPivot(id: string | number | Model, attributes: Record<string, unknown>): Promise<number>;
339
+ /**
340
+ * `sync` — diff against existing rows: attach new, detach removed, and update
341
+ * pivot attributes of kept rows when the map form supplies them.
342
+ */
343
+ sync(ids: PivotIds, detaching?: boolean): Promise<PivotSyncResult>;
344
+ /** `syncWithoutDetaching`. */
345
+ syncWithoutDetaching(ids: PivotIds): Promise<PivotSyncResult>;
346
+ /** `toggle` — attach missing, detach present. */
347
+ toggle(ids: PivotIds): Promise<{
348
+ attached: Array<string | number>;
349
+ detached: Array<string | number>;
350
+ }>;
351
+ }
352
+ export type PivotIds = string | number | Model | Array<string | number | Model> | Record<string | number, Record<string, unknown>>;
353
+ export interface PivotSyncResult {
354
+ attached: Array<string | number>;
355
+ detached: Array<string | number>;
356
+ updated: Array<string | number>;
222
357
  }
223
358
  /** Options for `morphToMany` beyond the positional args. */
224
359
  export type MorphToManyOptions = {
@@ -260,8 +395,42 @@ export declare class MorphToMany<T extends Model = Model> {
260
395
  /** Primary morph type written on attach (first configured type). */
261
396
  getMorphType(): string;
262
397
  get(): Promise<OrmCollection<T>>;
398
+ /** One multi-row INSERT per chunk (was one INSERT per id). */
399
+ attach(ids: Array<string | number> | string | number): Promise<void>;
400
+ /** Returns the number of deleted pivot rows. */
401
+ detach(ids?: Array<string | number> | string | number): Promise<number>;
402
+ /** Diff-based: keeps existing rows, attaches new ids, detaches removed ones. */
403
+ sync(ids: Array<string | number>): Promise<PivotSyncResult>;
404
+ syncWithoutDetaching(ids: Array<string | number>): Promise<PivotSyncResult>;
405
+ toggle(ids: Array<string | number> | string | number): Promise<{
406
+ attached: Array<string | number>;
407
+ detached: Array<string | number>;
408
+ }>;
409
+ }
410
+ /**
411
+ * `morphedByMany` — inverse of a `morphToMany` (`taggables` pivot).
412
+ * Parent is the "tag" side; the pivot stores the related model's id and morph type.
413
+ */
414
+ export declare class MorphedByMany<T extends Model = Model> {
415
+ #private;
416
+ private parent;
417
+ private related;
418
+ private pivotTable;
419
+ private foreignPivotKey;
420
+ private relatedPivotKey;
421
+ private morphTypeColumn;
422
+ private morphType;
423
+ constructor(parent: Model, related: ModelClass, pivotTable: string, foreignPivotKey: string, relatedPivotKey: string, morphTypeColumn: string, morphType: string);
424
+ getRelated(): ModelClass;
425
+ getPivotTable(): string;
426
+ getForeignPivotKeyName(): string;
427
+ getRelatedPivotKeyName(): string;
428
+ getMorphTypeColumn(): string;
429
+ getMorphType(): string;
430
+ get(): Promise<OrmCollection<T>>;
263
431
  attach(ids: Array<string | number> | string | number): Promise<void>;
264
- detach(ids?: Array<string | number> | string | number): Promise<void>;
432
+ detach(ids?: Array<string | number> | string | number): Promise<number>;
433
+ /** Detach missing ids, attach new ones; keeps rows that stay */
265
434
  sync(ids: Array<string | number>): Promise<void>;
266
435
  syncWithoutDetaching(ids: Array<string | number>): Promise<void>;
267
436
  toggle(ids: Array<string | number> | string | number): Promise<void>;
@@ -271,6 +440,9 @@ export type RelationMeta = {
271
440
  related: ModelClass;
272
441
  foreignKey: string;
273
442
  localKey: string;
443
+ /** morphOne / morphMany: the related rows must also carry this morph type. */
444
+ typeColumn?: string;
445
+ morphType?: string;
274
446
  } | {
275
447
  kind: "belongsTo";
276
448
  related: ModelClass;
@@ -284,6 +456,7 @@ export type RelationMeta = {
284
456
  relatedPivotKey: string;
285
457
  localKey: string;
286
458
  pivotColumns: string[];
459
+ pivotWheres: PivotWhere[];
287
460
  } | {
288
461
  kind: "morphToMany";
289
462
  related: ModelClass;
@@ -313,6 +486,9 @@ export type RelationMeta = {
313
486
  secondLocalKey: string;
314
487
  };
315
488
  export declare function resolveRelation(model: ModelClass, relation: string): RelationMeta | null;
489
+ /** Relation metadata for aggregate subqueries (morphOne / morphMany resolve as `has` + morph type). */
490
+ export type AggregateRelationMeta = RelationMeta;
491
+ export declare function resolveAggregateRelation(model: ModelClass, relation: string): AggregateRelationMeta | null;
316
492
  export declare function countRelation(model: Model, relation: string): Promise<number>;
317
493
  /** Run COUNT/SUM/AVG/MIN/MAX/EXISTS for a relation on one model instance. */
318
494
  export declare function aggregateRelation(model: Model, relation: string, fn: "count" | "sum" | "avg" | "min" | "max" | "exists", column?: string): Promise<unknown>;