@cleverbrush/orm 0.0.0-beta-20260424142030

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.
@@ -0,0 +1,181 @@
1
+ import type { ColumnRef, Entity, EntityRelations, EntitySchema, PrimaryKeyValueOf } from '@cleverbrush/knex-schema';
2
+ import { type SchemaQueryBuilder } from '@cleverbrush/knex-schema';
3
+ import type { Knex } from 'knex';
4
+ import type { EntityResult, RelKeyTree, SaveGraph, VariantInsertPayload, VariantResult, VariantUpdatePayload, WithIncluded, WithVariantIncluded } from './result-types.js';
5
+ /**
6
+ * The typed query handle returned by every {@link DbSet} method that
7
+ * initiates a query (e.g. `.where()`, `.include()`, `.first()`).
8
+ *
9
+ * Structurally it IS a {@link SchemaQueryBuilder} for the entity's schema,
10
+ * with typed `.include()` / `.includeVariant()` methods overlaid. All other
11
+ * `SchemaQueryBuilder` methods are available and `this`-returning chains
12
+ * preserve the typed wrapper.
13
+ *
14
+ * @public
15
+ */
16
+ export interface EntityQuery<TEntity extends Entity<any, any, any>, TResult> extends Omit<SchemaQueryBuilder<EntitySchema<TEntity>, TResult>, 'include' | 'includeVariant'> {
17
+ /**
18
+ * Eager-load a relation declared on `TEntity` via `.hasOne()` /
19
+ * `.hasMany()` / `.belongsTo()` / `.belongsToMany()`. Selector returns
20
+ * the relation key as a string literal.
21
+ */
22
+ include<K extends keyof EntityRelations<TEntity> & string>(sel: (t: RelKeyTree<TEntity>) => K, customize?: (q: SchemaQueryBuilder<any, any>) => void): EntityQuery<TEntity, WithIncluded<TEntity, TResult, K>>;
23
+ /**
24
+ * Eager-load a relation declared inside a polymorphic variant (CTI/STI).
25
+ * The relation is only populated on rows whose discriminator matches
26
+ * `variantKey`. Type-level: when `relationName` is a key of the
27
+ * entity's relations, it appears only on the matching branch of the
28
+ * discriminated-union result; otherwise the result type is unchanged.
29
+ */
30
+ includeVariant<TVariant extends string, TRel extends string>(variantKey: TVariant, relationName: TRel, customize?: (q: SchemaQueryBuilder<any, any>) => void): EntityQuery<TEntity, TRel extends keyof EntityRelations<TEntity> & string ? WithVariantIncluded<TEntity, TResult, TVariant, TRel> : TResult>;
31
+ /**
32
+ * Look up a single entity by primary key. Returns `undefined` when no
33
+ * row matches.
34
+ *
35
+ * The PK column(s) are resolved automatically from the entity's schema
36
+ * via the `primaryKey` / `hasPrimaryKey` extensions. Composite PKs are
37
+ * passed as a tuple in declaration order:
38
+ *
39
+ * ```ts
40
+ * await db.todos.find(42); // single PK
41
+ * await db.userRoles.find([userId, role]); // composite PK
42
+ * ```
43
+ *
44
+ * Any chained `.include()` / `.includeVariant()` calls on this query
45
+ * are honoured by the underlying `SELECT`.
46
+ */
47
+ find(pk: PrimaryKeyValueOf<EntitySchema<TEntity>>): Promise<TResult | undefined>;
48
+ /**
49
+ * Like {@link find} but throws {@link EntityNotFoundError} when no row
50
+ * matches.
51
+ */
52
+ findOrFail(pk: PrimaryKeyValueOf<EntitySchema<TEntity>>): Promise<TResult>;
53
+ /**
54
+ * Look up multiple entities by primary key in a single SQL statement.
55
+ * Returns rows in DB order (no ordering guarantee relative to `pks`).
56
+ *
57
+ * For composite PKs each element of `pks` is a tuple matching
58
+ * declaration order. Returns `[]` when `pks` is empty.
59
+ */
60
+ findMany(pks: ReadonlyArray<PrimaryKeyValueOf<EntitySchema<TEntity>>>): Promise<TResult[]>;
61
+ }
62
+ /**
63
+ * Typed query starter for a registered entity. Structurally it behaves like
64
+ * a fresh {@link EntityQuery} — every method invocation allocates a new
65
+ * underlying `SchemaQueryBuilder` so chains stay isolated.
66
+ *
67
+ * Additional members beyond `EntityQuery`:
68
+ *
69
+ * - `entity` — the wrapped {@link Entity} definition.
70
+ * - `query()` — explicit factory for a fresh `EntityQuery` (rarely needed).
71
+ * - `withTransaction(trx)` — return a new `DbSet` bound to a transaction.
72
+ *
73
+ * @public
74
+ */
75
+ export interface DbSet<TEntity extends Entity<any, any, any>> extends EntityQuery<TEntity, EntityResult<TEntity>> {
76
+ /** The wrapped entity definition. */
77
+ readonly entity: TEntity;
78
+ /**
79
+ * Allocate a fresh `EntityQuery` (rarely needed; method shortcuts on the
80
+ * `DbSet` itself already do this).
81
+ */
82
+ query(): EntityQuery<TEntity, EntityResult<TEntity>>;
83
+ /**
84
+ * Return a new {@link DbSet} bound to `trx`. All queries via the returned
85
+ * set execute inside the transaction.
86
+ */
87
+ withTransaction(trx: Knex.Transaction): DbSet<TEntity>;
88
+ /**
89
+ * Persist a nested write graph (root + related entities) in a single
90
+ * transaction. Each node is INSERTed when its primary-key fields are
91
+ * absent and UPDATEd when they are present (composite-PK aware).
92
+ *
93
+ * Topology:
94
+ * - `belongsTo` parents are saved first; their PKs feed the root's FK.
95
+ * - The root row is then written.
96
+ * - `hasOne` / `hasMany` / `belongsToMany` children inherit the root's
97
+ * PK into their FK column.
98
+ * - `belongsToMany` children may be passed as full nested graphs (new
99
+ * rows) or as `{ pk: value }` references (link existing rows).
100
+ *
101
+ * If called inside an existing transaction (via `withTransaction`), the
102
+ * outer transaction is reused; otherwise a fresh one is opened and
103
+ * automatically rolled back on failure.
104
+ */
105
+ save(graph: SaveGraph<TEntity>): Promise<EntityResult<TEntity>>;
106
+ /**
107
+ * Return a typed variant view pre-filtered to `variantKey`.
108
+ *
109
+ * Behaves like EF Core's `Set<DerivedType>()`: all reads automatically
110
+ * filter by the discriminator value, and `insert` / `update` / `delete`
111
+ * apply the correct single-table (STI) or two-table (CTI) logic.
112
+ *
113
+ * ```ts
114
+ * const assigned = db.activities.ofVariant('assigned');
115
+ *
116
+ * // Insert a new variant row (discriminator set automatically):
117
+ * await assigned.insert({ todoId: 42, userId: 7, assigneeId: 9 });
118
+ *
119
+ * // Update variant-specific columns for matching rows:
120
+ * await assigned.where(t => t.id, activityId).update({ assigneeId: 10 });
121
+ *
122
+ * // Delete variant rows (CTI: variant table first, then base):
123
+ * await assigned.where(t => t.id, activityId).delete();
124
+ *
125
+ * // Find by PK (result is narrowed to the variant branch):
126
+ * const a = await assigned.find(activityId);
127
+ * // a.type === 'assigned' and a.assigneeId is available
128
+ * ```
129
+ */
130
+ ofVariant<K extends string>(variantKey: K): VariantDbSet<TEntity, K>;
131
+ }
132
+ /**
133
+ * A `DbSet`-like handle scoped to a single polymorphic variant (one branch
134
+ * of a discriminated union entity). All reads are automatically pre-filtered
135
+ * by the discriminator; all writes apply the correct STI / CTI logic.
136
+ *
137
+ * Obtain via `DbSet.ofVariant('key')` — analogous to EF Core's
138
+ * `Set<DerivedType>()`.
139
+ *
140
+ * @public
141
+ */
142
+ export interface VariantDbSet<TEntity extends Entity<any, any, any>, K extends string> extends Omit<SchemaQueryBuilder<EntitySchema<TEntity>, VariantResult<TEntity, K>>, 'include' | 'includeVariant' | 'insert' | 'update' | 'delete' | 'where'> {
143
+ where(column: ColumnRef<EntitySchema<TEntity>>, operator: string, value: any): this;
144
+ where(column: ColumnRef<EntitySchema<TEntity>>, value: any): this;
145
+ where(raw: Knex.Raw, operator: string, value: any): this;
146
+ where(callback: (builder: Knex.QueryBuilder) => void): this;
147
+ where(record: Record<string, any>): this;
148
+ where(raw: Knex.Raw): this;
149
+ /**
150
+ * Eager-load a relation declared on `TEntity`. Identical to
151
+ * {@link EntityQuery.include}.
152
+ */
153
+ include<R extends keyof EntityRelations<TEntity> & string>(sel: (t: RelKeyTree<TEntity>) => R, customize?: (q: SchemaQueryBuilder<any, any>) => void): VariantDbSet<TEntity, K>;
154
+ /** Look up a single row by PK, typed to this variant. */
155
+ find(pk: PrimaryKeyValueOf<EntitySchema<TEntity>>): Promise<VariantResult<TEntity, K> | undefined>;
156
+ /** Like {@link find} but throws {@link EntityNotFoundError} when absent. */
157
+ findOrFail(pk: PrimaryKeyValueOf<EntitySchema<TEntity>>): Promise<VariantResult<TEntity, K>>;
158
+ /** Look up multiple rows by PK, typed to this variant. */
159
+ findMany(pks: ReadonlyArray<PrimaryKeyValueOf<EntitySchema<TEntity>>>): Promise<VariantResult<TEntity, K>[]>;
160
+ /**
161
+ * Insert a new variant row. The discriminator is set automatically.
162
+ *
163
+ * For CTI: inserts the base row first, then the variant row (transactional).
164
+ */
165
+ insert(payload: VariantInsertPayload<TEntity, K>): Promise<VariantResult<TEntity, K>>;
166
+ /**
167
+ * Update variant-specific columns for rows matched by the current
168
+ * WHERE clause. For CTI, only the variant table is updated.
169
+ */
170
+ update(patch: VariantUpdatePayload<TEntity, K>): Promise<void>;
171
+ /** Delete rows matched by the current WHERE clause (CTI: atomic). */
172
+ delete(): Promise<void>;
173
+ /** Return a new variant view bound to `trx`. */
174
+ withTransaction(trx: Knex.Transaction): VariantDbSet<TEntity, K>;
175
+ }
176
+ /**
177
+ * Construct a typed {@link DbSet} for an entity, bound to a Knex instance.
178
+ *
179
+ * @internal — use {@link createDb} for normal application code.
180
+ */
181
+ export declare function makeDbSet<TEntity extends Entity<any, any, any>>(knex: Knex, entity: TEntity, onResults?: (items: unknown[]) => unknown[] | undefined): DbSet<TEntity>;
@@ -0,0 +1,2 @@
1
+ export type { EntityPropSelector, EntityRelationKeys, EntityRelations, EntitySchema, RelationInfo, SchemaProps, UnwrapNavSchema, WithRelation } from '@cleverbrush/knex-schema';
2
+ export { defineEntity, Entity, POLYMORPHIC_TYPE_BRAND } from '@cleverbrush/knex-schema';
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Thrown by {@link DbSet.findOrFail} when no row matches the supplied
3
+ * primary key value(s). The `entity` field carries the SQL table name (or
4
+ * the entity's schema name when a table name is unavailable); `pk` is the
5
+ * exact value (scalar or tuple) that was looked up.
6
+ *
7
+ * @public
8
+ */
9
+ export declare class EntityNotFoundError extends Error {
10
+ readonly entity: string;
11
+ readonly pk: unknown;
12
+ constructor(entity: string, pk: unknown);
13
+ }
14
+ /**
15
+ * Thrown by {@link DbContext.saveChanges} when an UPDATE or DELETE detects
16
+ * that the row's row-version column (`.rowVersion()`) has changed since the
17
+ * entity was last loaded — indicating a concurrent modification.
18
+ *
19
+ * @public
20
+ */
21
+ export declare class ConcurrencyError extends Error {
22
+ /** Table name or entity label. */
23
+ readonly entity: string;
24
+ /** Primary-key value(s) of the conflicting row. */
25
+ readonly pk: unknown;
26
+ /** The row-version value the ORM expected. */
27
+ readonly expected: unknown;
28
+ constructor(entity: string, pk: unknown, expected: unknown);
29
+ }
30
+ /**
31
+ * Thrown by {@link DbContext.saveChanges} when an invariant is violated on
32
+ * a tracked entity — e.g. the primary key or discriminator column was mutated.
33
+ *
34
+ * @public
35
+ */
36
+ export declare class InvariantViolationError extends Error {
37
+ readonly entity: string;
38
+ readonly pk: unknown;
39
+ readonly field: string;
40
+ constructor(entity: string, pk: unknown, field: string, detail: string);
41
+ }
42
+ /**
43
+ * Thrown by the `[Symbol.asyncDispose]` implementation on a tracked
44
+ * {@link DbContext} when there are unsaved changes at scope exit.
45
+ *
46
+ * Callers must explicitly call `await db.saveChanges()` or
47
+ * `db.discardChanges()` before the `await using` block exits.
48
+ *
49
+ * @public
50
+ */
51
+ export declare class PendingChangesError extends Error {
52
+ /** Number of dirty / added / deleted entries. */
53
+ readonly pendingCount: number;
54
+ constructor(pendingCount: number, summary: string);
55
+ }
@@ -0,0 +1,7 @@
1
+ export * from '@cleverbrush/knex-schema';
2
+ export type { EntityEntry, EntryState, SavingChangesHook } from './change-tracker.js';
3
+ export type { DbContext, EntityMap, TrackedDbContext } from './dbcontext.js';
4
+ export { createDb } from './dbcontext.js';
5
+ export type { DbSet, EntityQuery, VariantDbSet } from './dbset.js';
6
+ export { ConcurrencyError, EntityNotFoundError, InvariantViolationError, PendingChangesError } from './errors.js';
7
+ export type { EntityResult, EntityResultByVariant, ExtractBranch, HasVariants, RelKeyTree, ResolvedRel, SaveGraph, VariantInsertPayload, VariantResult, VariantUpdatePayload, WithIncluded, WithVariantIncluded } from './result-types.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export*from"@cleverbrush/knex-schema";import{buildColumnMap as D,getPrimaryKeyColumns as A,getRowVersionColumn as en}from"@cleverbrush/knex-schema";var M=class extends Error{entity;pk;constructor(n,e){super(`Entity "${n}" not found for primary key ${JSON.stringify(e)}`),this.name="EntityNotFoundError",this.entity=n,this.pk=e}},S=class extends Error{entity;pk;expected;constructor(n,e,t){super(`Concurrency conflict on "${n}" (pk=${JSON.stringify(e)}): the row has been modified or deleted since it was loaded. Expected rowVersion = ${JSON.stringify(t)}.`),this.name="ConcurrencyError",this.entity=n,this.pk=e,this.expected=t}},v=class extends Error{entity;pk;field;constructor(n,e,t,i){super(`Invariant violation on "${n}" (pk=${JSON.stringify(e)}): ${i}`),this.name="InvariantViolationError",this.entity=n,this.pk=e,this.field=t}},j=class extends Error{pendingCount;constructor(n,e){super(`DbContext disposed with ${n} pending change(s). Call saveChanges() or discardChanges() before the context goes out of scope. `+e),this.name="PendingChangesError",this.pendingCount=n}};var U=class{#n=new Map;#e;constructor(){this.#e=new FinalizationRegistry(({setKey:n,pkKey:e})=>{let t=this.#n.get(n);t&&(t.delete(e),t.size===0&&this.#n.delete(n))})}set(n){let e=this.#n.get(n.entitySetKey);e||(e=new Map,this.#n.set(n.entitySetKey,e));let t=new WeakRef(n);e.set(n.pkKey,t),this.#e.register(n.entity,{setKey:n.entitySetKey,pkKey:n.pkKey})}get(n,e){let t=this.#n.get(n)?.get(e);if(!t)return null;let i=t.deref();return i||(this.#n.get(n)?.delete(e),null)}findByEntity(n){for(let e of this.#n.values())for(let t of e.values()){let i=t.deref();if(i?.entity===n)return i}return null}delete(n,e){this.#n.get(n)?.delete(e)}*entries(){for(let n of this.#n.values())for(let e of n.values()){let t=e.deref();t&&(yield t)}}byState(...n){let e=new Set(n),t=[];for(let i of this.entries())e.has(i.state)&&t.push(i);return t}clear(){this.#n.clear()}};function tn(o){return JSON.stringify(o)}function O(o,n){return A(o).propertyKeys.map(t=>n[t])}function R(o,n){let e=n.introspect?.(),t=new Set(Object.keys(e?.properties??{})),i={};for(let[c,f]of Object.entries(o))t.has(c)&&(i[c]=f);return Object.freeze(i)}function N(o,n){for(let e of Object.keys(o))if(!Object.is(o[e],n[e]))return!0;for(let e of Object.keys(n))if(!(e in o)&&n[e]!==void 0)return!0;return!1}var I=class{#n=new U;#e=new Set;#r=[];#t=new Map;registerEntitySet(n){this.#t.set(n.entitySetKey,n)}onSavingChanges(n){this.#r.push(n)}attach(n,e,t="Unchanged"){let i=this.#t.get(n);if(!i)throw new Error(`ChangeTracker.attach: unknown entity set "${n}". Make sure it was registered via registerEntitySet().`);let c=O(i.schema,e);if(c.some(r=>r==null)){if(e[x])return e;let s=R(e,i.schema),u=en(i.schema),l={entity:e,entitySetKey:n,variantKey:rn(i.schema,e),pkKey:"",originalSnapshot:s,state:"Added",rowVersion:u?{...u,snapshotValue:e[u.propertyKey]}:null};return e[x]=l,this.#e.add(l),e}let f=tn(c),g=this.#n.get(n,f);if(g)return g.entity!==e?g.entity:(t==="Unchanged"&&(g.originalSnapshot=R(e,i.schema),g.state="Unchanged",g.rowVersion&&(g.rowVersion.snapshotValue=e[g.rowVersion.propertyKey])),e);let y=R(e,i.schema),a=en(i.schema),d={entity:e,entitySetKey:n,variantKey:rn(i.schema,e),pkKey:f,originalSnapshot:y,state:t,rowVersion:a?{...a,snapshotValue:e[a.propertyKey]}:null};return this.#n.set(d),e[x]=d,e}detach(n){let e=this.#n.findByEntity(n);if(e)this.#n.delete(e.entitySetKey,e.pkKey);else{let t=n[x];t&&this.#e.delete(t)}delete n[x]}remove(n){let e=this.#n.findByEntity(n);if(!e)throw new Error("ChangeTracker.remove: entity is not tracked. Attach it first or use `db.attach()` before calling `remove()`.");e.state="Deleted"}entry(n){let e=this.#n.findByEntity(n)??n[x];if(!e)throw new Error("ChangeTracker.entry: entity is not tracked. Attach it first or load it via a tracked DbSet.");let t=this.#t.get(e.entitySetKey);return{get state(){return e.state},get originalValues(){return e.originalSnapshot},get currentValues(){return n},isModified(i){let c=n;return i!==void 0?!Object.is(e.originalSnapshot[i],c[i]):N(e.originalSnapshot,c)},reset(){let i=n;for(let[c,f]of Object.entries(e.originalSnapshot))i[c]=f;e.state=e.pkKey?"Unchanged":"Added"}}}discardChanges(){for(let n of this.#e)delete n.entity[x];this.#e.clear();for(let n of this.#n.entries())if(n.state==="Added")this.#n.delete(n.entitySetKey,n.pkKey);else if(n.state==="Modified"||n.state==="Deleted"||n.state==="Unchanged"&&N(n.originalSnapshot,n.entity)){let e=n.entity;for(let[t,i]of Object.entries(n.originalSnapshot))e[t]=i;n.state="Unchanged"}}async reload(n,e){let t=this.#n.findByEntity(n);if(!t)return;let i=this.#t.get(t.entitySetKey);if(!i)return;let c=A(i.schema),f=i.schema.getExtension?.("tableName");if(!f||c.propertyKeys.length===0)return;let{propToCol:g,colToProp:y}=D(i.schema),a=e(f),d=O(i.schema,n);for(let u=0;u<c.propertyKeys.length;u++){let l=g.get(c.propertyKeys[u])??c.propertyKeys[u];a.andWhere(l,d[u])}let r=await a.first();if(!r)return;let s=n;for(let[u,l]of Object.entries(r))s[y.get(u)??u]=l;t.originalSnapshot=R(n,i.schema),t.rowVersion&&(t.rowVersion.snapshotValue=n[t.rowVersion.propertyKey]),t.state="Unchanged"}hasPendingChanges(){if(this.#e.size>0)return!0;for(let n of this.#n.entries())if(n.state!=="Unchanged"||N(n.originalSnapshot,n.entity))return!0;return!1}async saveChanges(n){let e=0,t=0,i=0,c=[...this.#e,...this.#n.byState("Added")],f=[],g=this.#n.byState("Deleted");for(let y of this.#n.byState("Unchanged","Modified")){if(y.state==="Modified"){f.push(y);continue}N(y.originalSnapshot,y.entity)&&(y.state="Modified",f.push(y))}if(c.length===0&&f.length===0&&g.length===0)return{inserted:0,updated:0,deleted:0};for(let y of[...f,...g]){let a=this.#t.get(y.entitySetKey);if(!a)continue;let d=y.entity,r=A(a.schema),s=a.schema.getExtension?.("tableName");for(let u of r.propertyKeys)if(!Object.is(y.originalSnapshot[u],d[u]))throw new v(s,y.pkKey,u,`Primary key column "${u}" must not be changed on a tracked entity. Original: ${JSON.stringify(y.originalSnapshot[u])}, Current: ${JSON.stringify(d[u])}.`);if(y.variantKey!==void 0){let l=a.schema.getExtension?.("variants")?.discriminatorKey;if(l&&!Object.is(y.originalSnapshot[l],d[l]))throw new v(s,y.pkKey,l,`Discriminator column "${l}" must not be changed on a tracked entity. To change a polymorphic entity's type, delete and re-insert it as a new variant.`)}}for(let y of[...c,...f,...g]){if(!this.#t.get(y.entitySetKey))continue;let d=this.entry(y.entity);for(let r of this.#r)await r(d)}await n.transaction(async y=>{for(let a of c){let d=this.#t.get(a.entitySetKey);if(!d)continue;let r=d.schema.getExtension?.("tableName");if(!r)continue;let{propToCol:s}=D(d.schema),u=a.entity,l={};for(let[w,T]of Object.entries(u)){let h=s.get(w)??w;T!==void 0&&(l[h]=T)}let p=await y(r).insert(l).returning("*"),m=Array.isArray(p)&&p.length>0?p[0]:null;if(m&&typeof m=="object"){let{colToProp:w}=D(d.schema),T=a.entity;for(let[h,E]of Object.entries(m))T[w.get(h)??h]=E}e++}for(let a of f){let d=this.#t.get(a.entitySetKey);if(!d)continue;let r=d.schema.getExtension?.("tableName");if(!r)continue;let{propToCol:s}=D(d.schema),u=A(d.schema),l=a.entity,p=new Set(u.propertyKeys),m={};for(let h of Object.keys(a.originalSnapshot))p.has(h)||Object.is(a.originalSnapshot[h],l[h])||(m[s.get(h)??h]=l[h]);for(let[h,E]of Object.entries(l))p.has(h)||!(h in a.originalSnapshot)&&E!==void 0&&(m[s.get(h)??h]=E);if(a.rowVersion){let h=a.rowVersion,E=s.get(h.propertyKey)??h.propertyKey;if(h.strategy==="increment"){let K=Number(h.snapshotValue??0)+1;m[E]=K,l[h.propertyKey]=K}else if(h.strategy==="timestamp"){let K=new Date;m[E]=K,l[h.propertyKey]=K}}if(Object.keys(m).length===0)continue;let w=y(r);for(let h=0;h<u.propertyKeys.length;h++){let E=s.get(u.propertyKeys[h])??u.propertyKeys[h];w=w.andWhere(E,l[u.propertyKeys[h]])}if(a.rowVersion){let h=a.rowVersion,E=s.get(h.propertyKey)??h.propertyKey;w=w.andWhere(E,h.snapshotValue)}if(await w.update(m)===0&&a.rowVersion){let h=d.schema.getExtension?.("tableName");throw new S(h,O(d.schema,l),a.rowVersion.snapshotValue)}t++}for(let a of[...g].reverse()){let d=this.#t.get(a.entitySetKey);if(!d)continue;let r=d.schema.getExtension?.("tableName");if(!r)continue;let{propToCol:s}=D(d.schema),u=A(d.schema),l=a.entity,p=y(r);for(let w=0;w<u.propertyKeys.length;w++){let T=s.get(u.propertyKeys[w])??u.propertyKeys[w];p=p.andWhere(T,l[u.propertyKeys[w]])}if(a.rowVersion){let w=a.rowVersion,T=s.get(w.propertyKey)??w.propertyKey;p=p.andWhere(T,w.snapshotValue)}if(await p.delete()===0&&a.rowVersion)throw new S(r,O(d.schema,l),a.rowVersion?.snapshotValue);i++}});for(let y of c){y.originalSnapshot=R(y.entity,this.#t.get(y.entitySetKey).schema),y.state="Unchanged";let a=this.#t.get(y.entitySetKey),d=O(a.schema,y.entity);d.every(r=>r!=null)&&(y.pkKey=tn(d),this.#n.set(y)),this.#e.delete(y),y.rowVersion&&(y.rowVersion.snapshotValue=y.entity[y.rowVersion.propertyKey])}for(let y of f)y.originalSnapshot=R(y.entity,this.#t.get(y.entitySetKey).schema),y.state="Unchanged",y.rowVersion&&(y.rowVersion.snapshotValue=y.entity[y.rowVersion.propertyKey]);for(let y of g)this.#n.delete(y.entitySetKey,y.pkKey);return{inserted:e,updated:t,deleted:i}}pendingSummary(){let n={Added:0,Modified:0,Deleted:0,Unchanged:0};n.Added+=this.#e.size;for(let t of this.#n.entries())t.state!=="Unchanged"?n[t.state]++:N(t.originalSnapshot,t.entity)&&n.Modified++;let e=[];return n.Added&&e.push(`${n.Added} Added`),n.Modified&&e.push(`${n.Modified} Modified`),n.Deleted&&e.push(`${n.Deleted} Deleted`),`(${e.join(", ")})`}clear(){for(let n of this.#n.entries())delete n.entity[x];for(let n of this.#e)delete n.entity[x];this.#e.clear(),this.#n.clear()}},x=Symbol("@cleverbrush/orm:trackerEntry");function rn(o,n){let t=o?.getExtension?.("variants")?.discriminatorKey;if(t)return n[t]}import{getPrimaryKeyColumns as J,getVariants as dn,query as G}from"@cleverbrush/knex-schema";import{buildColumnMap as kn,getPrimaryKeyColumns as Q,query as Tn}from"@cleverbrush/knex-schema";function En(o){return o?.getExtension?.("relations")??[]}function Kn(o,n){let{propToCol:e}=kn(o);return e.get(n)??n}function bn(o,n){let e=Q(o);return e.propertyKeys.length===0?!1:e.propertyKeys.every(t=>n[t]!==void 0)}async function on(o,n,e,t){return t?V(t,n,e):o.transaction(i=>V(i,n,e))}async function V(o,n,e){let t=En(n),i=new Map;for(let r of t)i.set(r.name,r);let c={},f={};for(let[r,s]of Object.entries(e)){let u=i.get(r);u?f[r]={spec:u,value:s}:c[r]=s}for(let{spec:r,value:s}of Object.values(f)){if(r.type!=="belongsTo"||s==null)continue;if(typeof s!="object"||Array.isArray(s))throw new Error(`save: relation "${r.name}" expects an object (belongsTo).`);let u=await V(o,r.schema,s),l=Q(r.schema);if(l.propertyKeys.length!==1)throw new Error(`save: belongsTo "${r.name}" requires the parent entity to have a single-column primary key.`);let p=u[l.propertyKeys[0]];if(!r.foreignKey)throw new Error(`save: belongsTo "${r.name}" is missing a foreignKey.`);c[r.foreignKey]=p}let g=bn(n,c),y=Tn(o,n),a;if(g){let r=Q(n),s={...c};for(let l of r.propertyKeys)delete s[l];for(let l of r.propertyKeys)y.andWhere(l,"=",c[l]);let u=await y.update(s);Array.isArray(u)?a=u[0]??c:u&&typeof u=="object"?a=u:a=c;for(let l of r.propertyKeys)a[l]=c[l]}else a=await y.insert(c)??c;let d=Q(n);if(d.propertyKeys.length===1){let r=d.propertyKeys[0];a[r]===void 0&&c[r]!==void 0&&(a[r]=c[r])}for(let[r,{spec:s,value:u}]of Object.entries(f))if(s.type!=="belongsTo"&&u!=null){if(s.type==="hasOne"){if(typeof u!="object"||Array.isArray(u))throw new Error(`save: relation "${r}" expects a single object (hasOne).`);let l=d.propertyKeys[0];if(d.propertyKeys.length!==1)throw new Error(`save: hasOne "${r}" requires this entity to have a single-column primary key.`);let p={...u};s.foreignKey&&(p[s.foreignKey]=a[l]);let m=await V(o,s.schema,p);a[r]=m;continue}if(s.type==="hasMany"){if(!Array.isArray(u))throw new Error(`save: relation "${r}" expects an array (hasMany).`);let l=d.propertyKeys[0];if(d.propertyKeys.length!==1)throw new Error(`save: hasMany "${r}" requires this entity to have a single-column primary key.`);let p=[];for(let m of u){let w={...m};s.foreignKey&&(w[s.foreignKey]=a[l]),p.push(await V(o,s.schema,w))}a[r]=p;continue}if(s.type==="belongsToMany"){if(!s.through)throw new Error(`save: belongsToMany "${r}" is missing a through config.`);if(!Array.isArray(u))throw new Error(`save: relation "${r}" expects an array (belongsToMany).`);let l=d.propertyKeys[0];if(d.propertyKeys.length!==1)throw new Error(`save: belongsToMany "${r}" requires this entity to have a single-column primary key.`);let p=[];for(let m of u){let w=Q(s.schema);if(w.propertyKeys.length!==1)throw new Error(`save: belongsToMany "${r}" requires the child entity to have a single-column primary key.`);let T=w.propertyKeys[0],h;m[T]!==void 0?h={...m}:h=await V(o,s.schema,m),p.push(h),await o(s.through.table).insert({[s.through.localKey]:a[l],[s.through.foreignKey]:h[T]}).onConflict([s.through.localKey,s.through.foreignKey]).ignore()}a[r]=p}}return a}import{buildColumnMap as C,getPrimaryKeyColumns as _,getVariants as xn,query as Sn}from"@cleverbrush/knex-schema";function z(o,n){let e=xn(o);if(!e)throw new Error("insertVariant / deleteVariant / updateVariant / findVariant: entity schema is not polymorphic (no variants declared).");let t=e.variants[n];if(!t){let i=Object.keys(e.variants).join(", ");throw new Error(`Variant key "${n}" is unknown. Known variants: ${i}.`)}return{config:e,spec:t}}async function an(o,n,e,t,i){let{config:c,spec:f}=z(n,e),g=async y=>{let a=c.discriminatorKey,d=n.getExtension?.("tableName");if(!d)throw new Error("insertVariant: base schema has no table name.");if(f.storage==="sti"){let k={...t,[a]:e};return await Sn(y,n).insert(k)??k}let r=f.schema,s=f.tableName,u=f.foreignKey,l=n.introspect?.(),p=r.introspect?.(),m=new Set(Object.keys(l?.properties??{})),w=new Set(Object.keys(p?.properties??{})),T={[a]:e},h={};for(let[k,b]of Object.entries(t))k===a||k===u||(m.has(k)?T[k]=b:w.has(k)&&(h[k]=b));let{propToCol:E}=C(n),K={};for(let[k,b]of Object.entries(T))K[E.get(k)??k]=b;let P=await y(d).insert(K).returning("*"),W=Array.isArray(P)&&P.length>0?P[0]:K,$=_(n);if($.propertyKeys.length===0)throw new Error("insertVariant: base schema has no primary key declared.");if($.propertyKeys.length>1)throw new Error("insertVariant: composite primary keys are not yet supported for polymorphic CTI inserts.");let gn=$.propertyKeys[0],hn=$.columnNames[0],X=W[gn]??W[hn];if(X===void 0)throw new Error("insertVariant: could not resolve PK value from base insert result.");let{propToCol:Z}=C(r),F={[u]:X};for(let[k,b]of Object.entries(h)){let wn=Z.get(k)??k;F[wn]=b}let nn=Z.get(a);nn&&(F[nn]=e),await y(s).insert(F);let{colToProp:mn}=C(n),B={};for(let[k,b]of Object.entries(W))B[mn.get(k)??k]=b;B[a]=e;for(let[k,b]of Object.entries(h))B[k]=b;return B};return i?g(i):o.transaction(y=>g(y))}async function sn(o,n,e,t,i,c){if(i.length===0)return;let f=c??o,{config:g,spec:y}=z(n,e),a=g.discriminatorKey;if(y.storage==="sti"){let{propToCol:p}=C(n),m=p.get(a)??a,w=_(n);if(w.columnNames.length===0)throw new Error("updateVariant: base schema has no primary key declared.");if(w.columnNames.length>1)throw new Error("updateVariant: composite primary keys are not supported for STI updates.");let T=w.columnNames[0],h=n.getExtension?.("tableName"),E={};for(let[K,P]of Object.entries(t))E[p.get(K)??K]=P;await f(h).whereIn(T,i).andWhere(m,e).update(E);return}let d=y.schema,r=y.tableName,s=y.foreignKey,{propToCol:u}=C(d),l={};for(let[p,m]of Object.entries(t))p!==s&&(l[u.get(p)??p]=m);Object.keys(l).length!==0&&await f(r).whereIn(s,i).update(l)}async function cn(o,n,e,t,i){if(t.length===0)return;let c=async f=>{let{config:g,spec:y}=z(n,e),a=g.discriminatorKey,d=n.getExtension?.("tableName"),r=_(n);if(r.columnNames.length===0)throw new Error("deleteVariant: base schema has no primary key declared.");if(r.columnNames.length>1)throw new Error("deleteVariant: composite primary keys are not supported for variant deletes.");let s=r.columnNames[0];if(y.storage==="sti"){let{propToCol:p}=C(n),m=p.get(a)??a;await f(d).whereIn(s,t).andWhere(m,e).delete();return}let u=y.tableName,l=y.foreignKey;await f(u).whereIn(l,t).delete(),await f(d).whereIn(s,t).delete()};return i?c(i):o.transaction(f=>c(f))}var yn=new WeakMap;function ln(o){let n=yn.get(o);if(n)return n;let e=o.schema.introspect?.()?.properties??{},t={};for(let i of Object.keys(e))t[i]=i;return yn.set(o,t),t}function un(o,n,e,t){let i=new Proxy(o,{get(c,f,g){if(f==="include")return(a,d)=>{let r=a(ln(n));return o.include(r,d),i};if(f==="includeVariant")return(a,d,r)=>(o.includeVariant(a,d,r),i);if(f==="_sqb")return o;if(f==="_entity")return n;if(f==="find"||f==="findOrFail"||f==="findMany")return fn(f,o,n,i);if((f==="update"||f==="delete"||f==="insert")&&dn(n.schema))return()=>{throw new Error(`${String(f)}() cannot be called directly on a polymorphic entity set. Use db.set.ofVariant('key').${String(f)}() instead.`)};let y=Reflect.get(c,f,g);return typeof y!="function"?y:function(...a){let d=y.apply(o,a);return d===o?i:t!=null&&d!=null&&typeof d.then=="function"?d.then(r=>{if(Array.isArray(r)){let s=r.filter(u=>u!=null&&typeof u=="object");if(s.length>0){let u=t(s);if(u){let l=new Map;for(let p=0;p<s.length;p++)l.set(s[p],u[p]);return r.map(p=>l.get(p)??p)}}}else if(r!=null&&typeof r=="object"){let s=t([r]);if(s&&s.length>0)return s[0]}return r}):d}}});return i}function fn(o,n,e,t){return async(...i)=>{let c=J(e.schema);if(c.propertyKeys.length===0)throw new Error(`${o}(): entity has no primary key declared. Use \`.primaryKey()\` on a column or \`.hasPrimaryKey([...])\` on the schema.`);let f=c.propertyKeys,g=f.length>1,y=e.schema.getExtension?.("tableName")??"<entity>",a=String(y),d=s=>{let u=H(s,g,o);if(u.length!==f.length)throw new Error(`${o}(): expected ${f.length} primary-key value(s), got ${u.length}.`);for(let l=0;l<f.length;l++)t.andWhere(f[l],"=",u[l])};if(o==="findMany"){let s=i[0]??[];if(!Array.isArray(s))throw new Error("findMany(): expected an array of primary-key values.");if(s.length===0)return[];if(!g){let l=f[0],p=s.map(m=>H(m,!1,"findMany"));return t.whereIn(l,p.map(m=>m[0])),await t.execute()}let u=c.columnNames;return n.apply(l=>{l.andWhere(function(){for(let p of s){let m=H(p,!0,"findMany");this.orWhere(function(){for(let w=0;w<u.length;w++)this.andWhere(u[w],m[w])})}})}),await t.execute()}d(i[0]);let r=await t.first();if(r===void 0&&o==="findOrFail")throw new M(a,i[0]);return r}}function H(o,n,e){if(n){if(!Array.isArray(o))throw new Error(`${e}(): composite primary key requires a tuple argument.`);return o}return Array.isArray(o)?o:[o]}function q(o,n,e){return new Proxy({},{get(i,c){if(c==="entity")return n;if(c==="query")return()=>{let a=G(o,n.schema);return un(a,n,o,e)};if(c==="withTransaction")return a=>q(a,n,e);if(c==="save")return a=>{let d=o.isTransaction?o:void 0;return on(o,n.schema,a,d)};if(c==="ofVariant")return a=>{if(!dn(n.schema))throw new Error("ofVariant(): entity schema is not polymorphic (no variants declared).");return pn(o,n,a,e)};let f=G(o,n.schema),g=un(f,n,o,e),y=g[c];return typeof y!="function"?y:y.bind(g)}})}function vn(o,n,e,t,i){let c=new Proxy(o,{get(f,g,y){if(g==="include")return(d,r)=>{let s=d(ln(n));return o.include(s,r),c};if(g==="includeVariant")return(d,r,s)=>(o.includeVariant(d,r,s),c);if(g==="_sqb")return o;if(g==="_entity")return n;if(g==="find"||g==="findOrFail"||g==="findMany")return fn(g,o,n,c);if(g==="insert")return async d=>{let r=t.isTransaction?t:void 0,s=await an(t,n.schema,e,d,r);return i&&s!=null&&typeof s=="object"&&i([s]),s};if(g==="update")return async d=>{let r=J(n.schema),s=await c.execute(),u=r.propertyKeys[0],l=s.map(m=>m[u]).filter(m=>m!==void 0),p=t.isTransaction?t:void 0;await sn(t,n.schema,e,d,l,p)};if(g==="delete")return async()=>{let d=J(n.schema),r=await c.execute(),s=d.propertyKeys[0],u=r.map(p=>p[s]).filter(p=>p!==void 0),l=t.isTransaction?t:void 0;await cn(t,n.schema,e,u,l)};let a=Reflect.get(f,g,y);return typeof a!="function"?a:function(...d){let r=a.apply(o,d);return r===o?c:i!=null&&r!=null&&typeof r.then=="function"?r.then(s=>{if(Array.isArray(s)){let u=s.filter(l=>l!=null&&typeof l=="object");if(u.length>0){let l=i(u);if(l){let p=new Map;for(let m=0;m<u.length;m++)p.set(u[m],l[m]);return s.map(m=>p.get(m)??m)}}}else if(s!=null&&typeof s=="object"){let u=i([s]);if(u&&u.length>0)return u[0]}return s}):r}}});return c}function pn(o,n,e,t){return new Proxy({},{get(c,f){if(f==="withTransaction")return d=>pn(d,n,e,t);let g=G(o,n.schema);g.selectVariants?.([e]);let y=vn(g,n,e,o,t),a=y[f];return typeof a!="function"?a:a.bind(y)}})}function Y(o,n){let e={};for(let i of Object.keys(n))e[i]=q(o,n[i]);return{...e,knex:o,withTransaction(i){return Y(i,n)},async transaction(i){return o.transaction(async c=>i(Y(c,n)))}}}function L(o,n,e){let t={};for(let c of Object.keys(n)){let f=c,g=y=>y.map(a=>a!==null&&typeof a=="object"?e.attach(f,a,"Unchanged"):a);t[c]=q(o,n[c],g)}return{...t,knex:o,withTransaction(c){return L(c,n,e)},async transaction(c){return o.transaction(async f=>c(L(f,n,e)))},attach(c,f){return e.attach(c,f,"Unchanged")},detach(c){e.detach(c)},remove(c){e.remove(c)},entry(c){return e.entry(c)},onSavingChanges(c){e.onSavingChanges(c)},async reload(c){return e.reload(c,o)},discardChanges(){e.discardChanges()},async saveChanges(){return e.saveChanges(o)},async[Symbol.asyncDispose](){if(e.hasPendingChanges()){let c=e.pendingSummary(),f=parseInt(c.match(/\d+/)?.[0]??"1",10);throw e.clear(),new j(f,c)}e.clear()}}}function Rn(o,n,e){if(e?.tracking){let t=new I;for(let i of Object.keys(n)){let c={schema:n[i].schema,entitySetKey:i};t.registerEntitySet(c)}return L(o,n,t)}return Y(o,n)}export{S as ConcurrencyError,M as EntityNotFoundError,v as InvariantViolationError,j as PendingChangesError,Rn as createDb};
2
+ //# sourceMappingURL=index.js.map