peta-orm 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -310,25 +310,10 @@ const posts = await Post.query()
310
310
 
311
311
  ## Migrations
312
312
 
313
- Generate and run migrations from model definitions:
313
+ See the [peta-migrate](../migrate/README.md) package for migration generation and running.
314
314
 
315
315
  ```ts
316
- import { createMigrationRunner, createMigrationGenerator } from "peta-orm/migrator"
317
-
318
- const runner = createMigrationRunner(kysely)
319
- const gen = createMigrationGenerator()
320
-
321
- const code = gen.generateInitialMigration(models)
322
- await runner.up(migrationFiles)
323
- ```
324
-
325
- Or via the CLI:
326
-
327
- ```bash
328
- bun run bin/peta migrate:init
329
- bun run bin/peta migrate:generate
330
- bun run bin/peta migrate:up
331
- bun run bin/peta migrate:status
316
+ import { createMigrationRunner, createMigrationGenerator } from "peta-migrate"
332
317
  ```
333
318
 
334
319
  ---
@@ -394,7 +379,16 @@ cd packages/orm
394
379
  bun test test/integration/
395
380
  ```
396
381
 
397
- Set `INTEGRATION_SKIP_PG=1` or `INTEGRATION_SKIP_MYSQL=1` to skip specific databases.
382
+ ### Environment Variables
383
+
384
+ | Variable | Default | Description |
385
+ |----------|---------|-------------|
386
+ | `INTEGRATION_PG_URL` | `postgres://postgres:postgres@localhost:5432/peta_orm_test` | PostgreSQL connection string |
387
+ | `INTEGRATION_MYSQL_URL` | `mysql://root:mysqlroot@localhost:3306/peta_orm_test` | MySQL connection string |
388
+ | `INTEGRATION_SKIP_PG` | — | Set to `1` to skip PostgreSQL integration tests |
389
+ | `INTEGRATION_SKIP_MYSQL` | — | Set to `1` to skip MySQL integration tests |
390
+
391
+ See [`.env.example`](./.env.example) for a copyable template.
398
392
 
399
393
  ---
400
394
 
@@ -44,7 +44,7 @@ function createCollection(items) {
44
44
  return data.map((d) => d.get(key));
45
45
  },
46
46
  pluck(key) {
47
- return data.map((d) => d.get(key));
47
+ return this.get(key);
48
48
  },
49
49
  groupBy(key) {
50
50
  const result = {};
@@ -72,10 +72,6 @@ function createCollection(items) {
72
72
  forEach(fn) {
73
73
  data.forEach(fn);
74
74
  },
75
- each(fn) {
76
- data.forEach(fn);
77
- return collection;
78
- },
79
75
  unique(key) {
80
76
  if (!key) {
81
77
  const seen = /* @__PURE__ */ new Set();
@@ -152,11 +148,10 @@ function createCollection(items) {
152
148
  },
153
149
  async load(...relations) {
154
150
  if (data.length === 0) return collection;
155
- const { EagerLoader } = await import("./index.mjs").then((n) => n.i);
156
- const { getModelDefFromInstance } = await import("./factory-BBvIMQuc.mjs").then((n) => n.n);
157
- const { getModelDef } = await import("./index.mjs").then((n) => n.n);
151
+ const { EagerLoader } = await import("./index.mjs").then((n) => n.n);
152
+ const { getModelDefFromInstance } = await import("./factory-_JPR5fVl.mjs").then((n) => n.n);
158
153
  const first = data[0];
159
- const def = getModelDefFromInstance(first) ?? getModelDef(first);
154
+ const def = getModelDefFromInstance(first);
160
155
  if (def) {
161
156
  const loader = new EagerLoader();
162
157
  for (const rel of relations) await loader.loadRelated(data, { name: rel }, def);
@@ -1,4 +1,6 @@
1
- import { r as DatabaseError, s as RelationNotFoundError } from "./errors-sfFJolfu.mjs";
1
+ import { c as RelationNotFoundError, i as DatabaseError, n as isUniqueConstraintError, r as normalizeError } from "./errors-i-gCZnlW.mjs";
2
+ import { t as getDb } from "./model-helpers-BBpD3qdv.mjs";
3
+ import { i as resolveTargetId, n as getPivotInfo, t as findRelated } from "./helpers-CcxHFhtz.mjs";
2
4
  //#region src/relations/crud.ts
3
5
  function extractRelationData(def, data) {
4
6
  const columnData = {};
@@ -12,10 +14,6 @@ function extractRelationData(def, data) {
12
14
  relationOps
13
15
  };
14
16
  }
15
- function getDb(def) {
16
- if (!def._orm) throw new Error("Model not registered");
17
- return def._orm.kysely;
18
- }
19
17
  /**
20
18
  * Process relation operations after the parent model has been created.
21
19
  * For belongsTo, the related model must be created FIRST, then its ID
@@ -77,25 +75,10 @@ async function processManyToManyCreate(_instance, relation, op, pkValue) {
77
75
  [foreignPivotKey]: pkValue,
78
76
  [relatedPivotKey]: relatedId
79
77
  }).execute();
80
- } catch {}
78
+ } catch (e) {
79
+ if (!isUniqueConstraintError(e)) throw normalizeError(e, throughTable);
80
+ }
81
81
  }
82
82
  }
83
- function getPivotInfo(relation) {
84
- if (relation.type !== "manyToMany" || !relation.throughTable) throw new Error("Not a many-to-many relation");
85
- return {
86
- throughTable: relation.throughTable,
87
- foreignPivotKey: relation.foreignPivotKey ?? "",
88
- relatedPivotKey: relation.relatedPivotKey ?? ""
89
- };
90
- }
91
- async function findRelated(def, conditions) {
92
- const key = Object.keys(conditions)[0];
93
- return def.query().where(key, "=", conditions[key]).executeTakeFirst();
94
- }
95
- async function resolveTargetId(def, target) {
96
- if (typeof target === "number" || typeof target === "string") return target;
97
- const found = await findRelated(def, target);
98
- if (found) return found.get("id");
99
- }
100
83
  //#endregion
101
84
  export { extractRelationData, processCreateRelations };
@@ -62,8 +62,21 @@ function normalizeError(e, table) {
62
62
  if (raw.code === "ER_BAD_NULL_ERROR" || raw.errno === 1048) return new DatabaseError(`Not null constraint violation on ${table}: ${msg}`, "NOT_NULL_CONSTRAINT", table, msg);
63
63
  return new DatabaseError(msg || "Unknown database error", "UNKNOWN", table, msg);
64
64
  }
65
+ function isUniqueConstraintError(error) {
66
+ if (error instanceof Error) {
67
+ const msg = error.message.toLowerCase();
68
+ if (msg.includes("unique") || msg.includes("sqlite_constraint")) return true;
69
+ if (msg.includes("23505")) return true;
70
+ if (msg.includes("1062") || msg.includes("duplicate entry")) return true;
71
+ }
72
+ if (error && typeof error === "object" && "code" in error) {
73
+ const code = error.code;
74
+ if (code === "23505" || code === "ER_DUP_ENTRY" || code === "SQLITE_CONSTRAINT_UNIQUE") return true;
75
+ }
76
+ return false;
77
+ }
65
78
  //#endregion
66
79
  //#region src/errors.ts
67
80
  var errors_exports = /* @__PURE__ */ __exportAll({ normalizeError: () => normalizeError });
68
81
  //#endregion
69
- export { ModelNotRegisteredError as a, ValidationError as c, ModelNotFoundError as i, normalizeError as n, RelationNotAllowedError as o, DatabaseError as r, RelationNotFoundError as s, errors_exports as t };
82
+ export { ModelNotFoundError as a, RelationNotFoundError as c, DatabaseError as i, ValidationError as l, isUniqueConstraintError as n, ModelNotRegisteredError as o, normalizeError as r, RelationNotAllowedError as s, errors_exports as t };
@@ -39,10 +39,10 @@ function castForSet(value, type) {
39
39
  default: return value;
40
40
  }
41
41
  }
42
- function applyCastsToData(config, data, mode) {
42
+ function applyCastsToData(config, data) {
43
43
  if (!config.casts) return { ...data };
44
44
  const result = { ...data };
45
- for (const [key, type] of Object.entries(config.casts)) if (key in result) result[key] = mode === "get" ? castValue(result[key], type) : prepareForDb(result[key], type);
45
+ for (const [key, type] of Object.entries(config.casts)) if (key in result) result[key] = castValue(result[key], type);
46
46
  return result;
47
47
  }
48
48
  //#endregion
@@ -77,6 +77,7 @@ function getModelDefFromInstance(instance) {
77
77
  return instanceDefs.get(instance);
78
78
  }
79
79
  function createInstance(def, config, data, exists) {
80
+ const validColumns = new Set(Object.keys(def.columns));
80
81
  const instance = {
81
82
  get exists() {
82
83
  return getExists(instance);
@@ -106,7 +107,7 @@ function createInstance(def, config, data, exists) {
106
107
  },
107
108
  fill(data) {
108
109
  const safe = {};
109
- for (const [key, value] of Object.entries(data)) if (!FORBIDDEN_KEYS.has(key)) {
110
+ for (const [key, value] of Object.entries(data)) if (!FORBIDDEN_KEYS.has(key) && validColumns.has(key)) {
110
111
  let v = value;
111
112
  const attrDef = config.attributes?.[key];
112
113
  if (attrDef?.set) v = attrDef.set(v, instance);
@@ -160,7 +161,7 @@ function createInstance(def, config, data, exists) {
160
161
  return getRuntime().modelToJSON(def, instance);
161
162
  }
162
163
  };
163
- if (exists) initState(instance, applyCastsToData(config, data || {}, "get"), true);
164
+ if (exists) initState(instance, applyCastsToData(config, data || {}), true);
164
165
  else {
165
166
  initState(instance, {}, false);
166
167
  if (data && Object.keys(data).length > 0) instance.fill(data);
@@ -0,0 +1,40 @@
1
+ //#region src/relations/helpers.ts
2
+ const THUNK_CACHE = /* @__PURE__ */ new WeakMap();
3
+ function resolveThunk(thunk) {
4
+ let cls = THUNK_CACHE.get(thunk);
5
+ if (!cls) {
6
+ cls = thunk();
7
+ THUNK_CACHE.set(thunk, cls);
8
+ }
9
+ return cls;
10
+ }
11
+ function groupByArray(items, key) {
12
+ const result = {};
13
+ for (const item of items) {
14
+ const v = item.get(key);
15
+ if (v == null) continue;
16
+ const k = String(v);
17
+ if (!result[k]) result[k] = [];
18
+ result[k].push(item);
19
+ }
20
+ return result;
21
+ }
22
+ function getPivotInfo(relation) {
23
+ if (relation.type !== "manyToMany" || !relation.throughTable) throw new Error("Not a many-to-many relation");
24
+ return {
25
+ throughTable: relation.throughTable,
26
+ foreignPivotKey: relation.foreignPivotKey ?? "",
27
+ relatedPivotKey: relation.relatedPivotKey ?? ""
28
+ };
29
+ }
30
+ async function findRelated(def, conditions) {
31
+ const key = Object.keys(conditions)[0];
32
+ return def.query().where(key, "=", conditions[key]).executeTakeFirst();
33
+ }
34
+ async function resolveTargetId(def, target) {
35
+ if (typeof target === "number" || typeof target === "string") return target;
36
+ const found = await findRelated(def, target);
37
+ if (found) return found.get("id");
38
+ }
39
+ //#endregion
40
+ export { resolveThunk as a, resolveTargetId as i, getPivotInfo as n, groupByArray as r, findRelated as t };
@@ -1,4 +1,3 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
1
  //#region src/hooks/index.ts
3
2
  function createHookManager() {
4
3
  const listeners = /* @__PURE__ */ new Map();
@@ -22,27 +21,14 @@ function createHookManager() {
22
21
  const cbs = listeners.get(event);
23
22
  if (cbs) for (const cb of cbs) await cb(model);
24
23
  }
25
- function clone() {
26
- const cloned = createHookManager();
27
- for (const [event, cbs] of listeners) for (const cb of cbs) cloned.on(event, cb);
28
- return cloned;
29
- }
30
24
  return {
31
25
  on,
32
26
  off,
33
- trigger,
34
- clone
27
+ trigger
35
28
  };
36
29
  }
37
30
  //#endregion
38
31
  //#region src/model/hooks.ts
39
- var hooks_exports = /* @__PURE__ */ __exportAll({
40
- getHooksFor: () => getHooksFor,
41
- getSoftDeleteConfig: () => getSoftDeleteConfig,
42
- hasSoftDelete: () => hasSoftDelete,
43
- registerSoftDeletesFor: () => registerSoftDeletesFor,
44
- registerTimestampsFor: () => registerTimestampsFor
45
- });
46
32
  const hookManagers = /* @__PURE__ */ new WeakMap();
47
33
  function getHooksFor(def) {
48
34
  let hm = hookManagers.get(def);
@@ -74,4 +60,4 @@ function registerTimestampsFor(def, createdAtCol = "createdAt", updatedAtCol = "
74
60
  });
75
61
  }
76
62
  //#endregion
77
- export { registerSoftDeletesFor as a, hooks_exports as i, getSoftDeleteConfig as n, registerTimestampsFor as o, hasSoftDelete as r, createHookManager as s, getHooksFor as t };
63
+ export { registerTimestampsFor as a, registerSoftDeletesFor as i, getSoftDeleteConfig as n, createHookManager as o, hasSoftDelete as r, getHooksFor as t };
package/dist/index.d.mts CHANGED
@@ -40,8 +40,33 @@ declare function createColumn<T>(schema: SchemaConfig, dataType: string, args?:
40
40
  type ColumnShape = Record<string, Column>;
41
41
  type ColumnValue<C> = C extends Column<infer T> ? T : never;
42
42
  //#endregion
43
- //#region src/lib/kysely.d.ts
43
+ //#region src/types.d.ts
44
44
  type Database = Kysely<Record<string, never>>;
45
+ interface ModelLike<TColumns extends ColumnShape = ColumnShape> {
46
+ get<K extends keyof TColumns>(key: K): ColumnValue<TColumns[K]>;
47
+ get(key: string): unknown;
48
+ set(key: string, value: unknown): void;
49
+ }
50
+ interface ORMLike {
51
+ readonly kysely: Database;
52
+ register(model: ModelDefinition<any>): void;
53
+ registerAll(...models: (ModelDefinition<any> | ModelDefinition<any>[])[]): void;
54
+ destroy(): Promise<void>;
55
+ transaction<T>(fn: (orm: ORMLike) => Promise<T>): Promise<T>;
56
+ readonly models: ReadonlyMap<string, ModelDefinition<any>>;
57
+ getModel<T extends ColumnShape = ColumnShape>(name: string): ModelDefinition<T> | undefined;
58
+ /**
59
+ * Discover model definitions by scanning files matching a glob pattern.
60
+ *
61
+ * Uses `fast-glob` to resolve the pattern relative to `cwd`, then dynamically
62
+ * imports each matching file and collects exported `ModelDefinition` values.
63
+ * Does **not** auto-register — use `registerAll(...result)` to register them.
64
+ *
65
+ * @param pattern Glob pattern (e.g. `"./src/models/**\/*.ts"`)
66
+ * @returns Array of discovered model definitions
67
+ */
68
+ discover(pattern: string): Promise<ModelDefinition<any>[]>;
69
+ }
45
70
  //#endregion
46
71
  //#region src/pagination/index.d.ts
47
72
  interface Paginator<TColumns extends ColumnShape = ColumnShape> {
@@ -56,8 +81,6 @@ interface Paginator<TColumns extends ColumnShape = ColumnShape> {
56
81
  readonly lastItem: number;
57
82
  readonly onFirstPage: boolean;
58
83
  readonly onLastPage: boolean;
59
- readonly count: number;
60
- map<T>(fn: (item: ModelInstance<TColumns>) => T): T[];
61
84
  toJSON(): PaginatorJson<TColumns>;
62
85
  }
63
86
  interface PaginatorJson<TColumns extends ColumnShape = ColumnShape> {
@@ -73,9 +96,14 @@ interface PaginatorJson<TColumns extends ColumnShape = ColumnShape> {
73
96
  onFirstPage: boolean;
74
97
  onLastPage: boolean;
75
98
  }
76
- type PaginatedResult = PaginatorJson;
77
99
  declare function createPaginator<TColumns extends ColumnShape = ColumnShape>(items: ModelInstance<TColumns>[], total: number, perPage: number, currentPage: number): Paginator<TColumns>;
78
100
  //#endregion
101
+ //#region src/relations/graph/delete.d.ts
102
+ interface DeleteGraphOptions {
103
+ allowedRelations?: string[];
104
+ }
105
+ declare function deleteGraph(def: ModelDefinition, model: ModelInstance, options?: DeleteGraphOptions): Promise<void>;
106
+ //#endregion
79
107
  //#region src/relations/base.d.ts
80
108
  type RelationType = "hasMany" | "belongsTo" | "hasOne" | "manyToMany" | "hasManyThrough";
81
109
  interface RelationOptions {
@@ -156,14 +184,11 @@ interface QueryBuilder<TColumns extends ColumnShape = ColumnShape> extends Promi
156
184
  max(column: string): Promise<number>;
157
185
  withCount(relation: string): QueryBuilder<TColumns>;
158
186
  withSum(relation: string, column: string): QueryBuilder<TColumns>;
159
- withAvg(relation: string, column: string): QueryBuilder<TColumns>;
160
- withMin(relation: string, column: string): QueryBuilder<TColumns>;
161
- withMax(relation: string, column: string): QueryBuilder<TColumns>;
162
- withExists(relation: string): QueryBuilder<TColumns>;
163
187
  chunk(size: number, callback: (chunk: ModelInstance<TColumns>[]) => Promise<void>): Promise<void>;
164
188
  paginate(page: number, perPage?: number): Promise<Paginator<TColumns>>;
165
189
  insertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: InsertGraphOptions): Promise<any>;
166
190
  upsertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: UpsertGraphOptions): Promise<any>;
191
+ upsert(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
167
192
  with(...relations: (string | Record<string, (qb: QueryBuilder<TColumns>) => void>)[]): QueryBuilder<TColumns>;
168
193
  /**
169
194
  * Whitelist allowed relations (and nested paths) for eager loading.
@@ -182,10 +207,18 @@ interface QueryBuilder<TColumns extends ColumnShape = ColumnShape> extends Promi
182
207
  withTrashed(): QueryBuilder<TColumns>;
183
208
  onlyTrashed(): QueryBuilder<TColumns>;
184
209
  whereIn(column: string, values: unknown[]): QueryBuilder<TColumns>;
185
- whereInPivot(column: string, values: unknown[]): QueryBuilder<TColumns>;
186
210
  has(relationName: string): QueryBuilder<TColumns>;
187
- whereHas(relationName: string, callback?: (qb: QueryBuilder<TColumns>) => void): QueryBuilder<TColumns>;
188
- whereDoesntHave(relationName: string, callback?: (qb: QueryBuilder<TColumns>) => void): QueryBuilder<TColumns>;
211
+ /**
212
+ * Filter rows by related model existence with optional subquery filtering.
213
+ * The callback receives a raw Kysely subquery builder for the related table,
214
+ * not a peta-orm QueryBuilder. Chain `.where(...)` etc. directly on it.
215
+ */
216
+ whereHas(relationName: string, callback?: (subQb: any) => any): QueryBuilder<TColumns>;
217
+ /**
218
+ * Filter rows by absence of related model, with optional subquery filtering.
219
+ * Same callback semantics as `whereHas`.
220
+ */
221
+ whereDoesntHave(relationName: string, callback?: (subQb: any) => any): QueryBuilder<TColumns>;
189
222
  where(column: string, operator: unknown, value?: unknown): QueryBuilder<TColumns>;
190
223
  whereRef(col1: string, operator: string, col2: string): QueryBuilder<TColumns>;
191
224
  orWhere(column: string, operator: unknown, value?: unknown): QueryBuilder<TColumns>;
@@ -242,59 +275,6 @@ interface RelationQuery extends QueryBuilder {
242
275
  */
243
276
  type Plugin = (def: ModelDefinition) => undefined | ModelDefinition;
244
277
  //#endregion
245
- //#region src/types.d.ts
246
- type ModelId = number & {
247
- readonly __brand: "ModelId";
248
- };
249
- interface ModelLike<TColumns extends ColumnShape = ColumnShape> {
250
- get<K extends keyof TColumns>(key: K): ColumnValue<TColumns[K]>;
251
- get(key: string): unknown;
252
- set(key: string, value: unknown): void;
253
- }
254
- interface ORMLike {
255
- readonly kysely: Database;
256
- register(model: ModelDefinition$1<any>): void;
257
- registerAll(...models: (ModelDefinition$1<any> | ModelDefinition$1<any>[])[]): void;
258
- destroy(): Promise<void>;
259
- transaction<T>(fn: (trx: import("kysely").Kysely<Record<string, never>>) => Promise<T>): Promise<T>;
260
- readonly models: ReadonlyMap<string, ModelDefinition$1<any>>;
261
- getModel<T extends ColumnShape = ColumnShape>(name: string): ModelDefinition$1<T> | undefined;
262
- /**
263
- * Discover model definitions by scanning files matching a glob pattern.
264
- *
265
- * Uses `fast-glob` to resolve the pattern relative to `cwd`, then dynamically
266
- * imports each matching file and collects exported `ModelDefinition` values.
267
- * Does **not** auto-register — use `registerAll(...result)` to register them.
268
- *
269
- * @param pattern Glob pattern (e.g. `"./src/models/**\/*.ts"`)
270
- * @returns Array of discovered model definitions
271
- */
272
- discover(pattern: string): Promise<ModelDefinition$1<any>[]>;
273
- }
274
- interface ModelDefinition$1<TColumns extends ColumnShape = ColumnShape> {
275
- readonly table: string;
276
- readonly columns: TColumns;
277
- readonly relations: Record<string, Relation>;
278
- readonly name: string;
279
- _orm: ORMLike | null;
280
- query(): QueryBuilder<TColumns>;
281
- find(id: number | string): Promise<ModelInstance<TColumns> | undefined>;
282
- findOrFail(id: number | string): Promise<ModelInstance<TColumns>>;
283
- first(): Promise<ModelInstance<TColumns> | undefined>;
284
- create(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
285
- insert(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
286
- insertMany(dataArray: Record<string, unknown>[]): Promise<ModelInstance<TColumns>[]>;
287
- update(id: number | string, data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
288
- delete(id: number | string): Promise<void>;
289
- hydrate(row: Record<string, unknown>): ModelInstance<TColumns>;
290
- on(event: string, callback: (model: ModelInstance<TColumns>) => void | Promise<void>): () => void;
291
- getHooks(): HookManager;
292
- addGlobalScope(name: string, callback: (qb: QueryBuilder) => void): void;
293
- removeGlobalScope(name: string): void;
294
- getGlobalScopes(): Map<string, (qb: QueryBuilder) => void> | undefined;
295
- _init(orm: ORMLike): void;
296
- }
297
- //#endregion
298
278
  //#region src/hooks/index.d.ts
299
279
  type LifecycleEvent = "beforeCreate" | "afterCreate" | "beforeUpdate" | "afterUpdate" | "beforeSave" | "afterSave" | "beforeDelete" | "afterDelete" | "beforeRestore" | "afterRestore" | "beforeForceDelete" | "afterForceDelete";
300
280
  type HookCallback = (model: ModelLike) => void | Promise<void>;
@@ -302,7 +282,6 @@ interface HookManager {
302
282
  on(event: LifecycleEvent, callback: HookCallback): () => void;
303
283
  off(event: LifecycleEvent, callback: HookCallback): void;
304
284
  trigger(event: LifecycleEvent, model: ModelLike): Promise<void>;
305
- clone(): HookManager;
306
285
  }
307
286
  declare function createHookManager(): HookManager;
308
287
  //#endregion
@@ -319,14 +298,12 @@ type StaticHookCallback = (args: StaticHookArgs) => void | Promise<void>;
319
298
  //#endregion
320
299
  //#region src/model/computed.d.ts
321
300
  interface ComputedColumn<T = unknown> {
322
- readonly type: "sql" | "runtime" | "batch";
301
+ readonly type: "runtime" | "batch";
323
302
  readonly dependencies: string[];
324
303
  /** Compute a value for a single record (runtime) */
325
304
  compute?: (record: ModelInstance) => T;
326
305
  /** Compute values for a batch of records (batch async) */
327
306
  batchCompute?: (records: ModelInstance[]) => Promise<T[]>;
328
- /** Raw SQL expression to inline in SELECT */
329
- sql?: string;
330
307
  }
331
308
  //#endregion
332
309
  //#region src/model/attribute.d.ts
@@ -410,10 +387,14 @@ interface ModelDefinition<TColumns extends ColumnShape = ColumnShape> {
410
387
  create(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
411
388
  insert(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
412
389
  insertMany(dataArray: Record<string, unknown>[]): Promise<ModelInstance<TColumns>[]>;
390
+ updateMany(data: Record<string, unknown>, where: Record<string, unknown>[]): Promise<number>;
391
+ deleteMany(where: Record<string, unknown>[]): Promise<number>;
413
392
  update(id: number | string, data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
414
393
  delete(id: number | string): Promise<void>;
394
+ deleteGraph(idOrInstance: any, options?: DeleteGraphOptions): Promise<void>;
415
395
  insertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: InsertGraphOptions): Promise<any>;
416
396
  upsertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: UpsertGraphOptions): Promise<any>;
397
+ upsert(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
417
398
  hydrate(row: Record<string, unknown>): ModelInstance<TColumns>;
418
399
  use(plugin: Plugin): ModelDefinition<TColumns>;
419
400
  makeHelper<A extends any[], R>(fn: (qb: QueryBuilder, ...args: A) => R): (...args: A) => R;
@@ -422,11 +403,6 @@ interface ModelDefinition<TColumns extends ColumnShape = ColumnShape> {
422
403
  beforeDelete(callback: StaticHookCallback): () => void;
423
404
  afterDelete(callback: StaticHookCallback): () => void;
424
405
  beforeUpdate(callback: StaticHookCallback): () => void;
425
- afterUpdate(callback: StaticHookCallback): () => void;
426
- beforeCreate(callback: StaticHookCallback): () => void;
427
- afterCreate(callback: StaticHookCallback): () => void;
428
- beforeFind(callback: StaticHookCallback): () => void;
429
- afterFind(callback: StaticHookCallback): () => void;
430
406
  addGlobalScope(name: string, callback: (qb: QueryBuilder) => void): void;
431
407
  removeGlobalScope(name: string): void;
432
408
  getGlobalScopes(): Map<string, (qb: QueryBuilder) => void> | undefined;
@@ -468,7 +444,6 @@ interface Collection<TColumns extends ColumnShape = ColumnShape> {
468
444
  filter(fn: (item: ModelInstance<TColumns>, index: number) => boolean): Collection<TColumns>;
469
445
  reduce<T>(fn: (acc: T, item: ModelInstance<TColumns>, index: number) => T, initial: T): T;
470
446
  forEach(fn: (item: ModelInstance<TColumns>, index: number) => void): void;
471
- each(fn: (item: ModelInstance<TColumns>, index: number) => void): Collection<TColumns>;
472
447
  unique(key?: string): Collection<TColumns>;
473
448
  sortBy(key: string, direction?: "asc" | "desc"): Collection<TColumns>;
474
449
  shuffle(): Collection<TColumns>;
@@ -601,6 +576,24 @@ declare function normalizeError(e: unknown, table?: string): DatabaseError;
601
576
  */
602
577
  declare function createDb<T>(factory: () => Promise<T>): () => Promise<T>;
603
578
  //#endregion
579
+ //#region src/integrations/elysia.d.ts
580
+ interface PetaElysiaPluginOptions {
581
+ peta: ORMLike;
582
+ }
583
+ /**
584
+ * Elysia.js plugin that attaches the ORM instance to the app context.
585
+ */
586
+ declare function petaPlugin(options: PetaElysiaPluginOptions): (app: any) => any;
587
+ //#endregion
588
+ //#region src/integrations/hono.d.ts
589
+ interface PetaHonoMiddlewareOptions {
590
+ peta: ORMLike;
591
+ }
592
+ /**
593
+ * Hono middleware that sets the ORM instance on the context.
594
+ */
595
+ declare function petaMiddleware(options: PetaHonoMiddlewareOptions): (c: any, next: any) => Promise<void>;
596
+ //#endregion
604
597
  //#region src/model/define.d.ts
605
598
  declare function defineModel<TColumns extends ColumnShape>(table: string, config: ModelConfig<TColumns>): ModelDefinition<TColumns>;
606
599
  //#endregion
@@ -669,6 +662,42 @@ declare function timestamps(opts?: {
669
662
  */
670
663
  declare function ulid(): Plugin;
671
664
  //#endregion
665
+ //#region src/relations/crud.d.ts
666
+ interface BelongsToOp {
667
+ create?: Record<string, unknown>;
668
+ connect?: Record<string, unknown>;
669
+ connectOrCreate?: {
670
+ where: Record<string, unknown>;
671
+ create: Record<string, unknown>;
672
+ };
673
+ update?: Record<string, unknown>;
674
+ upsert?: {
675
+ update: Record<string, unknown>;
676
+ create: Record<string, unknown>;
677
+ };
678
+ delete?: boolean;
679
+ disconnect?: boolean;
680
+ set?: Record<string, unknown>;
681
+ }
682
+ interface HasManyOp {
683
+ create?: Record<string, unknown>[];
684
+ connect?: (number | string | Record<string, unknown>)[];
685
+ set?: (number | string | Record<string, unknown>)[];
686
+ delete?: Record<string, unknown> | Record<string, unknown>[];
687
+ update?: {
688
+ where: Record<string, unknown> | Record<string, unknown>[];
689
+ data: Record<string, unknown>;
690
+ };
691
+ }
692
+ interface ManyToManyOp {
693
+ create?: Record<string, unknown>[];
694
+ connect?: (number | string | Record<string, unknown>)[];
695
+ disconnect?: Record<string, unknown> | Record<string, unknown>[];
696
+ set?: (number | string | Record<string, unknown>)[];
697
+ add?: (number | string | Record<string, unknown>)[];
698
+ }
699
+ type RelationData = BelongsToOp | HasManyOp | ManyToManyOp;
700
+ //#endregion
672
701
  //#region src/relations/has-many.d.ts
673
702
  declare function hasMany(relatedThunk: () => ModelDefinition, options?: RelationOptions): Relation;
674
703
  declare function hasOne(relatedThunk: () => ModelDefinition, options?: RelationOptions): Relation;
@@ -709,13 +738,6 @@ interface MorphManyOptions {
709
738
  */
710
739
  typeValue?: string;
711
740
  }
712
- interface MorphOneOptions {
713
- name: string;
714
- related: () => ModelDefinition;
715
- type?: string;
716
- id?: string;
717
- typeValue?: string;
718
- }
719
741
  /**
720
742
  * Resolve the related model for a MorphTo relation given a parent instance.
721
743
  * Looks up the parent's `{name}Type` column value in the relation's morphMap
@@ -756,10 +778,6 @@ declare function defineMorphTo(options: MorphToOptions): Relation;
756
778
  * The related table stores the parent's type and id.
757
779
  */
758
780
  declare function defineMorphMany(options: MorphManyOptions): Relation;
759
- /**
760
- * Define a polymorphic hasOne relationship.
761
- */
762
- declare function defineMorphOne(options: MorphOneOptions): Relation;
763
781
  //#endregion
764
782
  //#region src/repo/index.d.ts
765
783
  type QueryMethod = (qb: QueryBuilder, ...args: any[]) => QueryBuilder;
@@ -783,4 +801,4 @@ interface RepoMethods {
783
801
  */
784
802
  declare function createRepo<TMethods extends RepoMethods>(model: ModelDefinition, methods: TMethods): Record<string, never>;
785
803
  //#endregion
786
- export { Attribute, type Collection, type Column, type ColumnShape, type ColumnTypes, type ColumnValue, type Constraint, DatabaseError, type DatabaseErrorCode, type HookCallback, type HookManager, type InsertGraphOptions, type LifecycleEvent, type ModelConfig, type ModelDefinition, type ModelId, type ModelInstance, ModelNotFoundError, ModelNotRegisteredError, type MorphManyOptions, type MorphOneOptions, type MorphToOptions, type ORMConfig, type ORMLike, type PaginatedResult, type Paginator, type PaginatorJson, type Plugin, type QueryBuilder, type QueryMethod, type Relation, RelationNotAllowedError, RelationNotFoundError, type RelationOptions, type RelationType, type RepoMethods, type SchemaConfig, type SerializedShape, type UpsertGraphOptions, ValidationError, belongsTo, createArkTypeSchemaConfig, createCollection, createColumn, createColumnTypes, createDb, createHookManager, createORM, createORM as createPeta, createPaginator, createQueryBuilder, createRepo, defineModel, defineMorphMany, defineMorphOne, defineMorphTo, hasMany, hasManyThrough, hasOne, manyToMany, normalizeError, resolveMorphRelation, softDeletes, t, timestamps, ulid };
804
+ export { Attribute, type BelongsToOp, type Collection, type Column, type ColumnShape, type ColumnTypes, type ColumnValue, type Constraint, DatabaseError, type DatabaseErrorCode, type DeleteGraphOptions, type HasManyOp, type HookCallback, type HookManager, type InsertGraphOptions, type LifecycleEvent, type ManyToManyOp, type ModelConfig, type ModelDefinition, type ModelInstance, ModelNotFoundError, ModelNotRegisteredError, type MorphManyOptions, type MorphToOptions, type ORMConfig, type ORMLike, type Paginator, type PaginatorJson, type Plugin, type QueryBuilder, type QueryMethod, type Relation, type RelationData, RelationNotAllowedError, RelationNotFoundError, type RelationOptions, type RelationType, type RepoMethods, type SchemaConfig, type SerializedShape, type UpsertGraphOptions, ValidationError, belongsTo, createArkTypeSchemaConfig, createCollection, createColumn, createColumnTypes, createDb, createHookManager, createORM, createORM as createPeta, createPaginator, createQueryBuilder, createRepo, defineModel, defineMorphMany, defineMorphTo, deleteGraph, hasMany, hasManyThrough, hasOne, manyToMany, normalizeError, petaMiddleware, petaPlugin, resolveMorphRelation, softDeletes, t, timestamps, ulid };