peta-orm 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +23 -1
  2. package/bin/peta +1 -1
  3. package/dist/collection-Dv3sQPMx.mjs +173 -0
  4. package/dist/crud-BCWvg5MI.mjs +101 -0
  5. package/dist/errors-sfFJolfu.mjs +69 -0
  6. package/dist/factory-rIbPGjRg.mjs +173 -0
  7. package/dist/hooks-BD0xy7uw.mjs +77 -0
  8. package/dist/index-BdJnSMYi.d.mts +480 -0
  9. package/dist/index.d.mts +215 -0
  10. package/dist/index.mjs +2601 -0
  11. package/dist/migrations/cli.d.mts +4 -0
  12. package/dist/migrations/cli.mjs +74 -0
  13. package/dist/migrations/index.d.mts +53 -0
  14. package/dist/migrations/index.mjs +2 -0
  15. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  16. package/dist/runner-DQ7uT6LC.mjs +180 -0
  17. package/dist/save-B8rudcT5.mjs +331 -0
  18. package/dist/state-LtlHp6XV.mjs +56 -0
  19. package/package.json +25 -15
  20. package/dist/builder/delete-builder.d.ts +0 -9
  21. package/dist/builder/delete-builder.d.ts.map +0 -1
  22. package/dist/builder/eager-loader.d.ts +0 -13
  23. package/dist/builder/eager-loader.d.ts.map +0 -1
  24. package/dist/builder/index.d.ts +0 -6
  25. package/dist/builder/index.d.ts.map +0 -1
  26. package/dist/builder/query-builder.d.ts +0 -57
  27. package/dist/builder/query-builder.d.ts.map +0 -1
  28. package/dist/builder/update-builder.d.ts +0 -9
  29. package/dist/builder/update-builder.d.ts.map +0 -1
  30. package/dist/collection/collection.d.ts +0 -48
  31. package/dist/collection/collection.d.ts.map +0 -1
  32. package/dist/collection-wwtv7qmv.js +0 -8
  33. package/dist/columns/arktype-config.d.ts +0 -8
  34. package/dist/columns/arktype-config.d.ts.map +0 -1
  35. package/dist/columns/column-types.d.ts +0 -27
  36. package/dist/columns/column-types.d.ts.map +0 -1
  37. package/dist/columns/column.d.ts +0 -30
  38. package/dist/columns/column.d.ts.map +0 -1
  39. package/dist/columns/schema-config.d.ts +0 -10
  40. package/dist/columns/schema-config.d.ts.map +0 -1
  41. package/dist/errors/errors.d.ts +0 -26
  42. package/dist/errors/errors.d.ts.map +0 -1
  43. package/dist/hooks/lifecycle.d.ts +0 -11
  44. package/dist/hooks/lifecycle.d.ts.map +0 -1
  45. package/dist/index-4xnrys72.js +0 -56
  46. package/dist/index-ddxdqxz6.js +0 -636
  47. package/dist/index-k18nf2r7.js +0 -18
  48. package/dist/index-qb301480.js +0 -2424
  49. package/dist/index-sm1xx8gs.js +0 -7828
  50. package/dist/index.d.ts +0 -24
  51. package/dist/index.d.ts.map +0 -1
  52. package/dist/index.js +0 -9229
  53. package/dist/integrations/elysia.d.ts +0 -12
  54. package/dist/integrations/elysia.d.ts.map +0 -1
  55. package/dist/integrations/hono.d.ts +0 -7
  56. package/dist/integrations/hono.d.ts.map +0 -1
  57. package/dist/migrations/cli.d.ts +0 -2
  58. package/dist/migrations/cli.d.ts.map +0 -1
  59. package/dist/migrations/cli.js +0 -4079
  60. package/dist/migrations/config.d.ts +0 -5
  61. package/dist/migrations/config.d.ts.map +0 -1
  62. package/dist/migrations/generator.d.ts +0 -9
  63. package/dist/migrations/generator.d.ts.map +0 -1
  64. package/dist/migrations/index.d.ts +0 -5
  65. package/dist/migrations/index.d.ts.map +0 -1
  66. package/dist/migrations/index.js +0 -17
  67. package/dist/migrations/runner.d.ts +0 -12
  68. package/dist/migrations/runner.d.ts.map +0 -1
  69. package/dist/migrations/types.d.ts +0 -26
  70. package/dist/migrations/types.d.ts.map +0 -1
  71. package/dist/model/model-delete.d.ts +0 -7
  72. package/dist/model/model-delete.d.ts.map +0 -1
  73. package/dist/model/model-hooks.d.ts +0 -10
  74. package/dist/model/model-hooks.d.ts.map +0 -1
  75. package/dist/model/model-relation.d.ts +0 -7
  76. package/dist/model/model-relation.d.ts.map +0 -1
  77. package/dist/model/model-save.d.ts +0 -6
  78. package/dist/model/model-save.d.ts.map +0 -1
  79. package/dist/model/model-scope.d.ts +0 -6
  80. package/dist/model/model-scope.d.ts.map +0 -1
  81. package/dist/model/model-serialize.d.ts +0 -3
  82. package/dist/model/model-serialize.d.ts.map +0 -1
  83. package/dist/model/model-state.d.ts +0 -27
  84. package/dist/model/model-state.d.ts.map +0 -1
  85. package/dist/model/model.d.ts +0 -88
  86. package/dist/model/model.d.ts.map +0 -1
  87. package/dist/pagination/paginator.d.ts +0 -30
  88. package/dist/pagination/paginator.d.ts.map +0 -1
  89. package/dist/paginator-tmp4hxj5.js +0 -9
  90. package/dist/peta.d.ts +0 -20
  91. package/dist/peta.d.ts.map +0 -1
  92. package/dist/relations/morph.d.ts +0 -44
  93. package/dist/relations/morph.d.ts.map +0 -1
  94. package/dist/relations/relation.d.ts +0 -85
  95. package/dist/relations/relation.d.ts.map +0 -1
  96. package/dist/types.d.ts +0 -18
  97. package/dist/types.d.ts.map +0 -1
package/README.md CHANGED
@@ -200,6 +200,11 @@ await User.delete(1)
200
200
  // Paginate
201
201
  const page = await Post.query().orderBy("id", "asc").paginate(1, 20)
202
202
  // → { data: Post[], total, perPage, currentPage, lastPage, hasMorePages }
203
+
204
+ // Query results are plain T[] — standard, zero overhead
205
+ const posts = await Post.query().where("published", true).execute()
206
+ // posts: Post[]
207
+ posts[0] // direct index access
203
208
  ```
204
209
 
205
210
  ### Hooks & Timestamps
@@ -308,9 +313,13 @@ The error also carries the `table` name and the original driver error via `cause
308
313
  ### Collection Utilities
309
314
 
310
315
  ```ts
316
+ // .execute() returns a plain array — lightweight, direct index access
311
317
  const users = await User.query().execute()
312
- const col = new Collection(users)
318
+ users[0] // direct access
313
319
 
320
+ // .collect() returns a Collection with convenience methods
321
+ const col = await User.query().orderBy("id", "asc").collect()
322
+ col.toJSON() // all items serialized in one call
314
323
  col.pluck("name") // ["Alice", "Bob"]
315
324
  col.groupBy("role") // { admin: [...], user: [...] }
316
325
  col.load("posts") // eager load relations
@@ -319,6 +328,8 @@ col.avg("age")
319
328
  col.unique("role")
320
329
  col.sortBy("name")
321
330
  col.chunk(10) // split into batches
331
+ col.first() // first element
332
+ col.at(0) // same as [0] on plain arrays
322
333
  ```
323
334
 
324
335
  ---
@@ -363,6 +374,17 @@ bun run bin/peta migrate:status
363
374
  | 19 | [collections-deep](./examples/19-collections-deep.ts) | full Collection + Paginator API |
364
375
  | 20 | [advanced-relations](./examples/20-advanced-relations.ts) | HasManyThrough, polymorphic morphs, pivot extras |
365
376
  | 21 | [migrations](./examples/21-migrations.ts) | MigrationRunner, MigrationGenerator, CLI |
377
+ | 22 | [related-query-builder](./examples/22-related-query-builder.ts) | `$related()` — scoped query builder for relations |
378
+ | 23 | [attach-detach-sync](./examples/23-attach-detach-sync.ts) | Many-to-many pivot management via `$related()` |
379
+ | 24 | [computed-columns](./examples/24-computed-columns.ts) | Runtime + batch async computed columns |
380
+ | 25 | [static-hooks](./examples/25-static-hooks.ts) | `asFindQuery()` preview + `cancelQuery()` abort |
381
+ | 26 | [repository-pattern](./examples/26-repository-pattern.ts) | `createRepo()` — chainable custom query methods |
382
+ | 27 | [plugins-and-helpers](./examples/27-plugins-and-helpers.ts) | `.use()` plugin system + `makeHelper()` |
383
+ | 28 | [nested-create-update](./examples/28-nested-create-update.ts) | Create and update models with related data in a single call |
384
+ | 29 | [allow-graph](./examples/29-allow-graph.ts) | `allowGraph()` — recursive whitelist for eager loading |
385
+ | 30 | [polymorphic-relations](./examples/30-polymorphic-relations.ts) | Polymorphic MorphMany/MorphOne/MorphTo with runtime resolution |
386
+ | 31 | [graph-operations](./examples/31-graph-operations.ts) | `insertGraph()`/`upsertGraph()` with `#id`/`#ref` |
387
+ | 32 | [accessors-mutators](./examples/32-accessors-mutators.ts) | `Attribute.make({ get, set })` — accessors and mutators |
366
388
 
367
389
  ---
368
390
 
package/bin/peta CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env bun
2
- import { run } from "../dist/migrations/cli.js"
2
+ import { run } from "../dist/migrations/cli.mjs"
3
3
  await run()
@@ -0,0 +1,173 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ //#region src/collection/index.ts
3
+ var collection_exports = /* @__PURE__ */ __exportAll({ createCollection: () => createCollection });
4
+ function createCollection(items) {
5
+ const data = [...items ?? []];
6
+ const collection = {
7
+ get length() {
8
+ return data.length;
9
+ },
10
+ [Symbol.iterator]() {
11
+ return data[Symbol.iterator]();
12
+ },
13
+ at(index) {
14
+ return data[index];
15
+ },
16
+ first() {
17
+ return data[0];
18
+ },
19
+ last() {
20
+ return data[data.length - 1];
21
+ },
22
+ all() {
23
+ return [...data];
24
+ },
25
+ findBy(id) {
26
+ return data.find((d) => d.get("id") === id);
27
+ },
28
+ find(callback) {
29
+ return data.find(callback);
30
+ },
31
+ some(callback) {
32
+ return data.some(callback);
33
+ },
34
+ includes(item) {
35
+ return data.includes(item);
36
+ },
37
+ isEmpty() {
38
+ return data.length === 0;
39
+ },
40
+ isNotEmpty() {
41
+ return data.length > 0;
42
+ },
43
+ get(key) {
44
+ return data.map((d) => d.get(key));
45
+ },
46
+ pluck(key) {
47
+ return data.map((d) => d.get(key));
48
+ },
49
+ groupBy(key) {
50
+ const result = {};
51
+ for (const item of data) {
52
+ const v = String(item.get(key));
53
+ if (!result[v]) result[v] = [];
54
+ result[v].push(item);
55
+ }
56
+ return result;
57
+ },
58
+ keyBy(key) {
59
+ const result = {};
60
+ for (const item of data) result[String(item.get(key))] = item;
61
+ return result;
62
+ },
63
+ map(fn) {
64
+ return data.map(fn);
65
+ },
66
+ filter(fn) {
67
+ return createCollection(data.filter(fn));
68
+ },
69
+ reduce(fn, initial) {
70
+ return data.reduce(fn, initial);
71
+ },
72
+ forEach(fn) {
73
+ data.forEach(fn);
74
+ },
75
+ each(fn) {
76
+ data.forEach(fn);
77
+ return collection;
78
+ },
79
+ unique(key) {
80
+ if (!key) {
81
+ const seen = /* @__PURE__ */ new Set();
82
+ return createCollection(data.filter((d) => {
83
+ const id = d.get("id");
84
+ if (seen.has(id)) return false;
85
+ seen.add(id);
86
+ return true;
87
+ }));
88
+ }
89
+ const seen = /* @__PURE__ */ new Set();
90
+ return createCollection(data.filter((d) => {
91
+ const v = d.get(key);
92
+ if (seen.has(v)) return false;
93
+ seen.add(v);
94
+ return true;
95
+ }));
96
+ },
97
+ sortBy(key, direction = "asc") {
98
+ return createCollection([...data].sort((a, b) => {
99
+ const va = a.get(key);
100
+ const vb = b.get(key);
101
+ if (va < vb) return direction === "asc" ? -1 : 1;
102
+ if (va > vb) return direction === "asc" ? 1 : -1;
103
+ return 0;
104
+ }));
105
+ },
106
+ shuffle() {
107
+ const shuffled = [...data];
108
+ for (let i = shuffled.length - 1; i > 0; i--) {
109
+ const j = Math.floor(Math.random() * (i + 1));
110
+ const temp = shuffled[i];
111
+ shuffled[i] = shuffled[j];
112
+ shuffled[j] = temp;
113
+ }
114
+ return createCollection(shuffled);
115
+ },
116
+ take(n) {
117
+ return createCollection(data.slice(0, n));
118
+ },
119
+ skip(n) {
120
+ return createCollection(data.slice(n));
121
+ },
122
+ chunk(size) {
123
+ const chunks = [];
124
+ for (let i = 0; i < data.length; i += size) chunks.push(createCollection(data.slice(i, i + size)));
125
+ return chunks;
126
+ },
127
+ sum(key) {
128
+ return data.reduce((acc, d) => acc + (Number(d.get(key)) || 0), 0);
129
+ },
130
+ avg(key) {
131
+ return data.length === 0 ? 0 : this.sum(key) / data.length;
132
+ },
133
+ min(key) {
134
+ return Math.min(...data.map((d) => Number(d.get(key)) || 0));
135
+ },
136
+ max(key) {
137
+ return Math.max(...data.map((d) => Number(d.get(key)) || 0));
138
+ },
139
+ diff(other) {
140
+ const otherIds = new Set(other.pluck("id"));
141
+ return createCollection(data.filter((d) => !otherIds.has(d.get("id"))));
142
+ },
143
+ intersect(other) {
144
+ const otherIds = new Set(other.pluck("id"));
145
+ return createCollection(data.filter((d) => otherIds.has(d.get("id"))));
146
+ },
147
+ concat(other) {
148
+ return createCollection([...data, ...other.all()]);
149
+ },
150
+ push(...items) {
151
+ data.push(...items);
152
+ },
153
+ async load(...relations) {
154
+ if (data.length === 0) return collection;
155
+ const { EagerLoader } = await import("./index.mjs").then((n) => n.i);
156
+ const { getModelDefFromInstance } = await import("./factory-rIbPGjRg.mjs").then((n) => n.n);
157
+ const { getModelDef } = await import("./index.mjs").then((n) => n.n);
158
+ const first = data[0];
159
+ const def = getModelDefFromInstance(first) ?? getModelDef(first);
160
+ if (def) {
161
+ const loader = new EagerLoader();
162
+ for (const rel of relations) await loader.loadRelated(data, { name: rel }, def);
163
+ }
164
+ return collection;
165
+ },
166
+ toJSON() {
167
+ return data.map((d) => d.toJSON());
168
+ }
169
+ };
170
+ return collection;
171
+ }
172
+ //#endregion
173
+ export { createCollection as n, collection_exports as t };
@@ -0,0 +1,101 @@
1
+ import { r as DatabaseError, s as RelationNotFoundError } from "./errors-sfFJolfu.mjs";
2
+ //#region src/relations/crud.ts
3
+ function extractRelationData(def, data) {
4
+ const columnData = {};
5
+ const relationOps = {};
6
+ for (const [key, value] of Object.entries(data)) if (def.relations[key] && typeof value === "object" && value !== null && !Array.isArray(value)) relationOps[key] = value;
7
+ else if (def.relations[key] && Array.isArray(value)) relationOps[key] = { connect: value };
8
+ else if (def.relations[key] && typeof value === "object" && value !== null && "create" in value) relationOps[key] = value;
9
+ else columnData[key] = value;
10
+ return {
11
+ columnData,
12
+ relationOps
13
+ };
14
+ }
15
+ function getDb(def) {
16
+ if (!def._orm) throw new Error("Model not registered");
17
+ return def._orm.kysely;
18
+ }
19
+ /**
20
+ * Process relation operations after the parent model has been created.
21
+ * For belongsTo, the related model must be created FIRST, then its ID
22
+ * is set on the parent. For hasMany/hasOne/manyToMany, the parent
23
+ * is created first, then children are created with the parent's FK.
24
+ *
25
+ * This function handles the post-creation phase.
26
+ */
27
+ async function processCreateRelations(def, instance, relationOps) {
28
+ for (const [relName, op] of Object.entries(relationOps)) {
29
+ const relation = def.relations[relName];
30
+ if (!relation) throw new RelationNotFoundError(def.name, relName);
31
+ relation.relatedModelClass;
32
+ const pkValue = instance.get(relation.localKey);
33
+ if (pkValue == null) throw new DatabaseError("Cannot create relations without primary key", "MISSING_ID");
34
+ if (relation.type === "belongsTo") await processBelongsToCreate(instance, relation, op, pkValue);
35
+ else if (relation.type === "hasOne") await processHasManyCreate(instance, relation, op, pkValue);
36
+ else if (relation.type === "hasMany") await processHasManyCreate(instance, relation, op, pkValue);
37
+ else if (relation.type === "manyToMany") await processManyToManyCreate(instance, relation, op, pkValue);
38
+ }
39
+ }
40
+ async function processBelongsToCreate(instance, relation, op, _pkValue) {
41
+ const relatedDef = relation.relatedModelClass;
42
+ let relatedId = instance.get(relation.foreignKey);
43
+ if (op.create) relatedId = (await relatedDef.insert(op.create)).get(relation.localKey);
44
+ else if (op.connect) {
45
+ const related = await findRelated(relatedDef, op.connect);
46
+ if (related) relatedId = related.get(relation.localKey);
47
+ } else if (op.connectOrCreate) {
48
+ const existing = await findRelated(relatedDef, op.connectOrCreate.where);
49
+ if (existing) relatedId = existing.get(relation.localKey);
50
+ else relatedId = (await relatedDef.insert(op.connectOrCreate.create)).get(relation.localKey);
51
+ }
52
+ if (relatedId !== void 0) await getDb(relatedDef).updateTable(relatedDef.table).set({ [relation.foreignKey]: relatedId }).where(relation.localKey, "=", _pkValue).execute();
53
+ }
54
+ async function processHasManyCreate(_instance, relation, op, pkValue) {
55
+ const relatedDef = relation.relatedModelClass;
56
+ const fk = relation.foreignKey;
57
+ if (op.create) for (const data of op.create) await relatedDef.insert({
58
+ ...data,
59
+ [fk]: pkValue
60
+ });
61
+ }
62
+ async function processManyToManyCreate(_instance, relation, op, pkValue) {
63
+ const relatedDef = relation.relatedModelClass;
64
+ const { throughTable, foreignPivotKey, relatedPivotKey } = getPivotInfo(relation);
65
+ const db = getDb(relatedDef);
66
+ if (op.create) for (const data of op.create) {
67
+ const relatedId = (await relatedDef.insert(data)).get(relation.localKey ?? "id");
68
+ await db.insertInto(throughTable).values({
69
+ [foreignPivotKey]: pkValue,
70
+ [relatedPivotKey]: relatedId
71
+ }).execute();
72
+ }
73
+ if (op.connect) for (const target of op.connect) {
74
+ const relatedId = await resolveTargetId(relatedDef, target);
75
+ if (relatedId != null) try {
76
+ await db.insertInto(throughTable).values({
77
+ [foreignPivotKey]: pkValue,
78
+ [relatedPivotKey]: relatedId
79
+ }).execute();
80
+ } catch {}
81
+ }
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
+ //#endregion
101
+ export { extractRelationData, processCreateRelations };
@@ -0,0 +1,69 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ //#region src/errors/classes.ts
3
+ var ValidationError = class extends Error {
4
+ name = "ValidationError";
5
+ };
6
+ var ModelNotFoundError = class extends Error {
7
+ name = "ModelNotFoundError";
8
+ constructor(model, id) {
9
+ super(`${model} with id ${id} not found`);
10
+ }
11
+ };
12
+ var RelationNotFoundError = class extends Error {
13
+ name = "RelationNotFoundError";
14
+ constructor(model, relation) {
15
+ super(`Relation "${relation}" not found on ${model}`);
16
+ }
17
+ };
18
+ var RelationNotAllowedError = class extends Error {
19
+ name = "RelationNotAllowedError";
20
+ constructor(model, relation) {
21
+ super(`Relation "${relation}" is not in the allowGraph whitelist for ${model}`);
22
+ }
23
+ };
24
+ var ModelNotRegisteredError = class extends Error {
25
+ name = "ModelNotRegisteredError";
26
+ constructor(model) {
27
+ super(`Model "${model}" is not registered. Call orm.register() or pass it to createORM()`);
28
+ }
29
+ };
30
+ var DatabaseError = class extends Error {
31
+ name = "DatabaseError";
32
+ code;
33
+ table;
34
+ detail;
35
+ constructor(message, code, table, detail) {
36
+ super(message);
37
+ this.code = code;
38
+ this.table = table;
39
+ this.detail = detail;
40
+ }
41
+ };
42
+ //#endregion
43
+ //#region src/errors/normalizer.ts
44
+ function normalizeError(e, table) {
45
+ const raw = e;
46
+ const msg = raw.message ?? "";
47
+ if (raw.code === "SQLITE_CONSTRAINT_UNIQUE" || raw.code === "SQLITE_CONSTRAINT") return new DatabaseError(`Unique constraint violation on ${table}: ${msg}`, "UNIQUE_CONSTRAINT", table, msg);
48
+ if (raw.code === "SQLITE_CONSTRAINT_FOREIGNKEY" || raw.code === "SQLITE_CONSTRAINT_FOREIGN_KEY") return new DatabaseError(`Foreign key constraint violation on ${table}: ${msg}`, "FOREIGN_KEY_CONSTRAINT", table, msg);
49
+ if (raw.code === "SQLITE_CONSTRAINT_NOTNULL" || raw.code === "SQLITE_CONSTRAINT_NOT_NULL") return new DatabaseError(`Not null constraint violation on ${table}: ${msg}`, "NOT_NULL_CONSTRAINT", table, msg);
50
+ if (raw.code === "SQLITE_CONSTRAINT_CHECK") return new DatabaseError(`Check constraint violation on ${table}: ${msg}`, "CHECK_CONSTRAINT", table, msg);
51
+ if (raw.errno === 19 || raw.errno === 2067) {
52
+ if (msg.includes("UNIQUE")) return new DatabaseError(msg, "UNIQUE_CONSTRAINT", table, msg);
53
+ if (msg.includes("FOREIGN KEY")) return new DatabaseError(msg, "FOREIGN_KEY_CONSTRAINT", table, msg);
54
+ if (msg.includes("NOT NULL")) return new DatabaseError(msg, "NOT_NULL_CONSTRAINT", table, msg);
55
+ if (msg.includes("CHECK")) return new DatabaseError(msg, "CHECK_CONSTRAINT", table, msg);
56
+ }
57
+ if (raw.code === "23505") return new DatabaseError(msg, "UNIQUE_CONSTRAINT", table, msg);
58
+ if (raw.code === "23503") return new DatabaseError(msg, "FOREIGN_KEY_CONSTRAINT", table, msg);
59
+ if (raw.code === "23502") return new DatabaseError(msg, "NOT_NULL_CONSTRAINT", table, msg);
60
+ if (raw.code === "ER_DUP_ENTRY" || raw.errno === 1062) return new DatabaseError(`Unique constraint violation on ${table}: ${msg}`, "UNIQUE_CONSTRAINT", table, msg);
61
+ if (raw.code === "ER_NO_REFERENCED_ROW_2" || raw.errno === 1452) return new DatabaseError(`Foreign key constraint violation on ${table}: ${msg}`, "FOREIGN_KEY_CONSTRAINT", table, msg);
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
+ return new DatabaseError(msg || "Unknown database error", "UNKNOWN", table, msg);
64
+ }
65
+ //#endregion
66
+ //#region src/errors.ts
67
+ var errors_exports = /* @__PURE__ */ __exportAll({ normalizeError: () => normalizeError });
68
+ //#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 };
@@ -0,0 +1,173 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { a as getRawRelations, c as isDirty, f as syncOriginal, i as getExists, l as resetAttrs, n as getAttr, o as getState, r as getDirtyAttributes, s as initState, t as fillAttrs, u as setAttr } from "./state-LtlHp6XV.mjs";
3
+ //#region src/model/casts.ts
4
+ function castValue(value, type) {
5
+ if (value == null) return value;
6
+ switch (type) {
7
+ case "json":
8
+ case "object": return typeof value === "string" ? JSON.parse(value) : value;
9
+ case "boolean":
10
+ case "bool": return Boolean(value);
11
+ case "integer":
12
+ case "int": return Number(value);
13
+ case "float":
14
+ case "double": return Number(value);
15
+ case "date":
16
+ case "datetime": return typeof value === "string" ? value : value.toISOString();
17
+ default: return value;
18
+ }
19
+ }
20
+ function prepareForDb(value, type) {
21
+ if (value == null) return value;
22
+ switch (type) {
23
+ case "json":
24
+ case "object": return typeof value === "string" ? value : JSON.stringify(value);
25
+ case "boolean":
26
+ case "bool": return value ? 1 : 0;
27
+ default: return value;
28
+ }
29
+ }
30
+ function castForSet(value, type) {
31
+ if (value == null) return value;
32
+ switch (type) {
33
+ case "json":
34
+ case "object": return typeof value === "string" ? JSON.parse(value) : value;
35
+ case "boolean":
36
+ case "bool": return Boolean(value);
37
+ case "integer":
38
+ case "int": return Number(value);
39
+ default: return value;
40
+ }
41
+ }
42
+ function applyCastsToData(config, data, mode) {
43
+ if (!config.casts) return { ...data };
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);
46
+ return result;
47
+ }
48
+ //#endregion
49
+ //#region src/model/runtime.ts
50
+ let runtime = null;
51
+ function initRuntime(fns) {
52
+ runtime = fns;
53
+ }
54
+ function getRuntime() {
55
+ if (!runtime) throw new Error("Model runtime not initialized. Call initRuntime() first.");
56
+ return runtime;
57
+ }
58
+ //#endregion
59
+ //#region src/model/types.ts
60
+ const FORBIDDEN_KEYS = new Set([
61
+ "__proto__",
62
+ "constructor",
63
+ "prototype"
64
+ ]);
65
+ //#endregion
66
+ //#region src/model/factory.ts
67
+ var factory_exports = /* @__PURE__ */ __exportAll({
68
+ createInstance: () => createInstance,
69
+ getModelDefFromInstance: () => getModelDefFromInstance,
70
+ setModelDefOnInstance: () => setModelDefOnInstance
71
+ });
72
+ const instanceDefs = /* @__PURE__ */ new WeakMap();
73
+ function setModelDefOnInstance(instance, def) {
74
+ instanceDefs.set(instance, def);
75
+ }
76
+ function getModelDefFromInstance(instance) {
77
+ return instanceDefs.get(instance);
78
+ }
79
+ function createInstance(def, config, data, exists) {
80
+ const instance = {
81
+ get exists() {
82
+ return getExists(instance);
83
+ },
84
+ get attributes() {
85
+ return { ...getState(instance).attributes };
86
+ },
87
+ get dirtyAttributes() {
88
+ return getDirtyAttributes(instance);
89
+ },
90
+ isDirty(key) {
91
+ return isDirty(instance, key);
92
+ },
93
+ get(key) {
94
+ let result = getAttr(instance, key);
95
+ if (config.casts?.[key]) result = castValue(result, config.casts[key]);
96
+ const attrDef = config.attributes?.[key];
97
+ if (attrDef?.get) result = attrDef.get(result, instance);
98
+ return result;
99
+ },
100
+ set(key, value) {
101
+ if (FORBIDDEN_KEYS.has(key)) return;
102
+ let result = value;
103
+ const attrDef = config.attributes?.[key];
104
+ if (attrDef?.set) result = attrDef.set(result, instance);
105
+ setAttr(instance, key, config.casts?.[key] ? castForSet(result, config.casts[key]) : result);
106
+ },
107
+ fill(data) {
108
+ const safe = {};
109
+ for (const [key, value] of Object.entries(data)) if (!FORBIDDEN_KEYS.has(key)) {
110
+ let v = value;
111
+ const attrDef = config.attributes?.[key];
112
+ if (attrDef?.set) v = attrDef.set(v, instance);
113
+ safe[key] = config.casts?.[key] ? castForSet(v, config.casts[key]) : v;
114
+ }
115
+ fillAttrs(instance, safe);
116
+ },
117
+ reset() {
118
+ resetAttrs(instance);
119
+ },
120
+ $getRelation(name) {
121
+ return getRawRelations(instance)?.[name] ?? null;
122
+ },
123
+ $setRelation(name, value) {
124
+ getRawRelations(instance)[name] = value;
125
+ },
126
+ $hasRelation(name) {
127
+ return name in (getRawRelations(instance) ?? {});
128
+ },
129
+ $relationData() {
130
+ return { ...getRawRelations(instance) };
131
+ },
132
+ $load: (...relations) => {
133
+ return getRuntime().loadModelRelations(instance, def, ...relations);
134
+ },
135
+ $related: (name) => {
136
+ return getRuntime().createRelationQuery(instance, def, name);
137
+ },
138
+ $save: () => {
139
+ return getRuntime().saveModel(def, instance);
140
+ },
141
+ $delete: () => {
142
+ return getRuntime().deleteModel(def, instance);
143
+ },
144
+ $forceDelete: () => {
145
+ return getRuntime().forceDeleteModel(def, instance);
146
+ },
147
+ $restore: () => {
148
+ return getRuntime().restoreModel(def, instance);
149
+ },
150
+ $trashed() {
151
+ return getRuntime().trashedModel(def, instance);
152
+ },
153
+ $reload: () => {
154
+ return getRuntime().reloadModel(def, instance);
155
+ },
156
+ $toJSON() {
157
+ return getRuntime().modelToJSON(def, instance);
158
+ },
159
+ toJSON() {
160
+ return getRuntime().modelToJSON(def, instance);
161
+ }
162
+ };
163
+ if (exists) initState(instance, applyCastsToData(config, data || {}, "get"), true);
164
+ else {
165
+ initState(instance, {}, false);
166
+ if (data && Object.keys(data).length > 0) instance.fill(data);
167
+ syncOriginal(instance);
168
+ }
169
+ setModelDefOnInstance(instance, def);
170
+ return instance;
171
+ }
172
+ //#endregion
173
+ export { castValue as a, applyCastsToData as i, factory_exports as n, prepareForDb as o, initRuntime as r, createInstance as t };
@@ -0,0 +1,77 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ //#region src/hooks/index.ts
3
+ function createHookManager() {
4
+ const listeners = /* @__PURE__ */ new Map();
5
+ function on(event, callback) {
6
+ let cbs = listeners.get(event);
7
+ if (!cbs) {
8
+ cbs = [];
9
+ listeners.set(event, cbs);
10
+ }
11
+ cbs.push(callback);
12
+ return () => off(event, callback);
13
+ }
14
+ function off(event, callback) {
15
+ const cbs = listeners.get(event);
16
+ if (cbs) {
17
+ const idx = cbs.indexOf(callback);
18
+ if (idx !== -1) cbs.splice(idx, 1);
19
+ }
20
+ }
21
+ async function trigger(event, model) {
22
+ const cbs = listeners.get(event);
23
+ if (cbs) for (const cb of cbs) await cb(model);
24
+ }
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
+ return {
31
+ on,
32
+ off,
33
+ trigger,
34
+ clone
35
+ };
36
+ }
37
+ //#endregion
38
+ //#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
+ const hookManagers = /* @__PURE__ */ new WeakMap();
47
+ function getHooksFor(def) {
48
+ let hm = hookManagers.get(def);
49
+ if (!hm) {
50
+ hm = createHookManager();
51
+ hookManagers.set(def, hm);
52
+ }
53
+ return hm;
54
+ }
55
+ const SOFT_DELETE = /* @__PURE__ */ new WeakMap();
56
+ function hasSoftDelete(def) {
57
+ return SOFT_DELETE.has(def);
58
+ }
59
+ function getSoftDeleteConfig(def) {
60
+ return SOFT_DELETE.get(def);
61
+ }
62
+ function registerSoftDeletesFor(def, deletedAtColumn = "deletedAt") {
63
+ SOFT_DELETE.set(def, { column: deletedAtColumn });
64
+ }
65
+ function registerTimestampsFor(def, createdAtCol = "createdAt", updatedAtCol = "updatedAt") {
66
+ const hm = getHooksFor(def);
67
+ hm.on("beforeCreate", (model) => {
68
+ const now = (/* @__PURE__ */ new Date()).toISOString();
69
+ if (!model.get(createdAtCol)) model.set(createdAtCol, now);
70
+ model.set(updatedAtCol, now);
71
+ });
72
+ hm.on("beforeUpdate", (model) => {
73
+ model.set(updatedAtCol, (/* @__PURE__ */ new Date()).toISOString());
74
+ });
75
+ }
76
+ //#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 };