peta-orm 0.2.2 → 0.2.4
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 +49 -6
- package/dist/{index-zth6eamb.js → index-bv1vk6ec.js} +20 -3
- package/dist/{index-qwps5bne.js → index-gacxptb4.js} +286 -1
- package/dist/index.js +6 -286
- package/dist/migrations/cli.js +2 -2
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ bun add -d kysely-bun-sqlite
|
|
|
23
23
|
// db.ts
|
|
24
24
|
import { Database } from "bun:sqlite"
|
|
25
25
|
import { BunSqliteDialect } from "kysely-bun-sqlite"
|
|
26
|
+
import type { ColumnShape } from "peta-orm"
|
|
26
27
|
import { Peta, $t, ArkTypeSchemaConfig, Model, HasMany } from "peta-orm"
|
|
27
28
|
|
|
28
29
|
const t = $t({ schema: new ArkTypeSchemaConfig() })
|
|
@@ -32,8 +33,8 @@ class User extends Model {
|
|
|
32
33
|
static override columns = {
|
|
33
34
|
id: t.integer().primaryKey(),
|
|
34
35
|
name: t.string(255).min(2),
|
|
35
|
-
email: t.text().email(),
|
|
36
|
-
}
|
|
36
|
+
email: t.text().email().unique(),
|
|
37
|
+
} satisfies ColumnShape
|
|
37
38
|
static override relations = {
|
|
38
39
|
posts: new HasMany(() => Post),
|
|
39
40
|
}
|
|
@@ -43,9 +44,9 @@ class Post extends Model {
|
|
|
43
44
|
static override table = "posts"
|
|
44
45
|
static override columns = {
|
|
45
46
|
id: t.integer().primaryKey(),
|
|
46
|
-
userId: t.integer(),
|
|
47
|
+
userId: t.integer().references(() => User, ["id"]),
|
|
47
48
|
title: t.string(255),
|
|
48
|
-
}
|
|
49
|
+
} satisfies ColumnShape
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
const database = new Database("my-app.db")
|
|
@@ -88,18 +89,30 @@ export { peta, User, Post }
|
|
|
88
89
|
### Column Types & Validation
|
|
89
90
|
|
|
90
91
|
```ts
|
|
92
|
+
import type { ColumnShape } from "peta-orm"
|
|
93
|
+
|
|
91
94
|
const t = $t({ schema: new ArkTypeSchemaConfig() })
|
|
92
95
|
|
|
93
96
|
class User extends Model {
|
|
94
97
|
static override columns = {
|
|
95
98
|
id: t.integer().primaryKey(),
|
|
96
99
|
name: t.string(255).min(2), // min length
|
|
97
|
-
email: t.text().email(),
|
|
100
|
+
email: t.text().email().unique(), // email format + unique constraint
|
|
98
101
|
age: t.integer().nullable().min(0).max(150).default(0),
|
|
99
102
|
role: t.enum("admin", "user").default("user"),
|
|
100
103
|
score: t.double().nullable(),
|
|
101
104
|
...t.timestamps(), // createdAt, updatedAt
|
|
102
|
-
}
|
|
105
|
+
} satisfies ColumnShape
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
class Post extends Model {
|
|
109
|
+
static override columns = {
|
|
110
|
+
id: t.integer().primaryKey(),
|
|
111
|
+
userId: t.integer().references(() => User, ["id"]), // foreign key
|
|
112
|
+
title: t.string(255),
|
|
113
|
+
slug: t.string().unique(),
|
|
114
|
+
published: t.boolean().default(false),
|
|
115
|
+
} satisfies ColumnShape
|
|
103
116
|
}
|
|
104
117
|
```
|
|
105
118
|
|
|
@@ -132,6 +145,36 @@ const authors = await User.query().has("posts").execute()
|
|
|
132
145
|
const active = await User.query().whereHas("posts", (q) => q.where("published", true)).execute()
|
|
133
146
|
```
|
|
134
147
|
|
|
148
|
+
### ManyToMany
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
class Post extends Model {
|
|
152
|
+
static override columns = { id: t.integer().primaryKey(), title: t.string(255) } satisfies ColumnShape
|
|
153
|
+
static override relations = {
|
|
154
|
+
tags: new ManyToMany(() => Tag, {
|
|
155
|
+
through: "post_tags",
|
|
156
|
+
foreignPivotKey: "postId",
|
|
157
|
+
relatedPivotKey: "tagId",
|
|
158
|
+
}),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
class Tag extends Model {
|
|
163
|
+
static override columns = { id: t.integer().primaryKey(), name: t.string(255) } satisfies ColumnShape
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Pivot tables are regular Models — register them so the migration
|
|
167
|
+
// generator includes the pivot table automatically.
|
|
168
|
+
class PostTag extends Model {
|
|
169
|
+
static override table = "post_tags"
|
|
170
|
+
static override columns = {
|
|
171
|
+
id: t.integer().primaryKey(),
|
|
172
|
+
postId: t.integer().references(() => Post, ["id"]),
|
|
173
|
+
tagId: t.integer().references(() => Tag, ["id"]),
|
|
174
|
+
} satisfies ColumnShape
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
135
178
|
### CRUD & Pagination
|
|
136
179
|
|
|
137
180
|
```ts
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CreateViewNode,
|
|
6
6
|
IdentifierNode,
|
|
7
7
|
InsertQueryNode,
|
|
8
|
+
ManyToMany,
|
|
8
9
|
MergeQueryNode,
|
|
9
10
|
NOOP_QUERY_EXECUTOR,
|
|
10
11
|
OperatorNode,
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
parseTable,
|
|
31
32
|
parseValueExpression,
|
|
32
33
|
preventAwait
|
|
33
|
-
} from "./index-
|
|
34
|
+
} from "./index-gacxptb4.js";
|
|
34
35
|
import {
|
|
35
36
|
__require
|
|
36
37
|
} from "./index-k18nf2r7.js";
|
|
@@ -2091,6 +2092,8 @@ class MigrationGenerator {
|
|
|
2091
2092
|
const timestamp = new Date().toISOString().replace(/[-:T.Z]/g, "").slice(0, 14);
|
|
2092
2093
|
const parts = [];
|
|
2093
2094
|
const indexParts = [];
|
|
2095
|
+
const warnings = [];
|
|
2096
|
+
const registeredTables = new Set([...models.values()].map((m) => m.table).filter(Boolean));
|
|
2094
2097
|
for (const [, modelClass] of models) {
|
|
2095
2098
|
const table = modelClass.table;
|
|
2096
2099
|
if (!table)
|
|
@@ -2102,16 +2105,30 @@ class MigrationGenerator {
|
|
|
2102
2105
|
indexParts.push(this.#generateCreateIndex(table, colName));
|
|
2103
2106
|
}
|
|
2104
2107
|
}
|
|
2108
|
+
for (const [relName, rel] of Object.entries(modelClass.relations ?? {})) {
|
|
2109
|
+
if (rel instanceof ManyToMany) {
|
|
2110
|
+
const through = rel.throughTable;
|
|
2111
|
+
if (!registeredTables.has(through)) {
|
|
2112
|
+
warnings.push(`// \u26A0 Detected ManyToMany "${modelClass.name}.${relName}" references table "${through}" ` + `but no model is registered for it.
|
|
2113
|
+
` + `// Add a model class with static override table = "${through}" to include the pivot ` + `table in the generated migration.`);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2105
2117
|
}
|
|
2106
2118
|
const upBody = [...parts, ...indexParts].join(`
|
|
2107
2119
|
|
|
2108
2120
|
`);
|
|
2109
2121
|
const downTables = [...models.values()].filter((m) => m.table).map((m) => ` await db.schema.dropTable("${m.table}").ifExists().execute()`);
|
|
2122
|
+
const warningBlock = warnings.length > 0 ? ` // Warnings:
|
|
2123
|
+
${warnings.join(`
|
|
2124
|
+
`)}
|
|
2125
|
+
|
|
2126
|
+
` : "";
|
|
2110
2127
|
return `import type { Kysely } from "kysely"
|
|
2111
2128
|
import { sql } from "kysely"
|
|
2112
2129
|
|
|
2113
2130
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
2114
|
-
${upBody}
|
|
2131
|
+
${warningBlock}${upBody}
|
|
2115
2132
|
}
|
|
2116
2133
|
|
|
2117
2134
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
@@ -2122,7 +2139,7 @@ ${downTables.join(`
|
|
|
2122
2139
|
}
|
|
2123
2140
|
#generateCreateTable(table, columns) {
|
|
2124
2141
|
const lines = [];
|
|
2125
|
-
lines.push(` await db.schema.createTable("${table}")`);
|
|
2142
|
+
lines.push(` await db.schema.createTable("${table}").ifNotExists()`);
|
|
2126
2143
|
for (const [name, col] of Object.entries(columns)) {
|
|
2127
2144
|
const typeSql = this.#mapType(col);
|
|
2128
2145
|
const cb = this.#columnCallback(col);
|
|
@@ -7531,4 +7531,289 @@ class Peta {
|
|
|
7531
7531
|
}
|
|
7532
7532
|
}
|
|
7533
7533
|
|
|
7534
|
-
|
|
7534
|
+
// src/relations/relation.ts
|
|
7535
|
+
var thunkCache = new WeakMap;
|
|
7536
|
+
function resolve2(thunk) {
|
|
7537
|
+
let cls = thunkCache.get(thunk);
|
|
7538
|
+
if (!cls) {
|
|
7539
|
+
cls = thunk();
|
|
7540
|
+
thunkCache.set(thunk, cls);
|
|
7541
|
+
}
|
|
7542
|
+
return cls;
|
|
7543
|
+
}
|
|
7544
|
+
function guessForeignKey(modelClass) {
|
|
7545
|
+
const table = modelClass.table;
|
|
7546
|
+
const singular = table.endsWith("s") ? table.slice(0, -1) : table;
|
|
7547
|
+
return `${singular}Id`;
|
|
7548
|
+
}
|
|
7549
|
+
function groupByArray(items, key) {
|
|
7550
|
+
const result = {};
|
|
7551
|
+
for (const item of items) {
|
|
7552
|
+
const v = item.get(key);
|
|
7553
|
+
if (v == null)
|
|
7554
|
+
continue;
|
|
7555
|
+
const k = String(v);
|
|
7556
|
+
if (!result[k])
|
|
7557
|
+
result[k] = [];
|
|
7558
|
+
result[k].push(item);
|
|
7559
|
+
}
|
|
7560
|
+
return result;
|
|
7561
|
+
}
|
|
7562
|
+
|
|
7563
|
+
class Relation {
|
|
7564
|
+
type;
|
|
7565
|
+
#relatedThunk;
|
|
7566
|
+
constructor(type, relatedThunk) {
|
|
7567
|
+
this.type = type;
|
|
7568
|
+
this.#relatedThunk = relatedThunk;
|
|
7569
|
+
}
|
|
7570
|
+
get relatedModelClass() {
|
|
7571
|
+
return resolve2(this.#relatedThunk);
|
|
7572
|
+
}
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7575
|
+
class HasMany extends Relation {
|
|
7576
|
+
#options;
|
|
7577
|
+
constructor(relatedThunk, options = {}) {
|
|
7578
|
+
super("hasMany", relatedThunk);
|
|
7579
|
+
this.#options = options;
|
|
7580
|
+
}
|
|
7581
|
+
get foreignKey() {
|
|
7582
|
+
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
7583
|
+
}
|
|
7584
|
+
get localKey() {
|
|
7585
|
+
return this.#options.localKey ?? "id";
|
|
7586
|
+
}
|
|
7587
|
+
query(parent) {
|
|
7588
|
+
return this.relatedModelClass.query().where(this.foreignKey, "=", parent.get(this.localKey));
|
|
7589
|
+
}
|
|
7590
|
+
addEagerConstraints(query, models) {
|
|
7591
|
+
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
7592
|
+
if (keys.length > 0) {
|
|
7593
|
+
query.whereIn(this.foreignKey, keys);
|
|
7594
|
+
}
|
|
7595
|
+
}
|
|
7596
|
+
match(models, results, relationName) {
|
|
7597
|
+
const grouped = groupByArray(results, this.foreignKey);
|
|
7598
|
+
for (const model of models) {
|
|
7599
|
+
const key = String(model.get(this.localKey));
|
|
7600
|
+
model.$setRelation(relationName, grouped[key] ?? []);
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
async getResults(parent) {
|
|
7604
|
+
return await this.query(parent).execute();
|
|
7605
|
+
}
|
|
7606
|
+
}
|
|
7607
|
+
|
|
7608
|
+
class BelongsTo extends Relation {
|
|
7609
|
+
#options;
|
|
7610
|
+
constructor(relatedThunk, options = {}) {
|
|
7611
|
+
super("belongsTo", relatedThunk);
|
|
7612
|
+
this.#options = options;
|
|
7613
|
+
}
|
|
7614
|
+
get foreignKey() {
|
|
7615
|
+
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
7616
|
+
}
|
|
7617
|
+
get localKey() {
|
|
7618
|
+
return this.#options.localKey ?? "id";
|
|
7619
|
+
}
|
|
7620
|
+
query(parent) {
|
|
7621
|
+
return this.relatedModelClass.query().where(this.localKey, "=", parent.get(this.foreignKey));
|
|
7622
|
+
}
|
|
7623
|
+
addEagerConstraints(query, models) {
|
|
7624
|
+
const keys = models.map((m) => m.get(this.foreignKey)).filter((k) => k != null);
|
|
7625
|
+
if (keys.length > 0) {
|
|
7626
|
+
query.whereIn(this.localKey, keys);
|
|
7627
|
+
}
|
|
7628
|
+
}
|
|
7629
|
+
match(models, results, relationName) {
|
|
7630
|
+
const grouped = groupByArray(results, this.localKey);
|
|
7631
|
+
for (const model of models) {
|
|
7632
|
+
const key = String(model.get(this.foreignKey));
|
|
7633
|
+
model.$setRelation(relationName, grouped[key]?.[0] ?? null);
|
|
7634
|
+
}
|
|
7635
|
+
}
|
|
7636
|
+
async getResults(parent) {
|
|
7637
|
+
return await this.query(parent).executeTakeFirst() ?? null;
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
|
|
7641
|
+
class HasOne extends Relation {
|
|
7642
|
+
#options;
|
|
7643
|
+
constructor(relatedThunk, options = {}) {
|
|
7644
|
+
super("hasOne", relatedThunk);
|
|
7645
|
+
this.#options = options;
|
|
7646
|
+
}
|
|
7647
|
+
get foreignKey() {
|
|
7648
|
+
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
7649
|
+
}
|
|
7650
|
+
get localKey() {
|
|
7651
|
+
return this.#options.localKey ?? "id";
|
|
7652
|
+
}
|
|
7653
|
+
query(parent) {
|
|
7654
|
+
return this.relatedModelClass.query().where(this.foreignKey, "=", parent.get(this.localKey));
|
|
7655
|
+
}
|
|
7656
|
+
addEagerConstraints(query, models) {
|
|
7657
|
+
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
7658
|
+
if (keys.length > 0) {
|
|
7659
|
+
query.whereIn(this.foreignKey, keys);
|
|
7660
|
+
}
|
|
7661
|
+
}
|
|
7662
|
+
match(models, results, relationName) {
|
|
7663
|
+
const grouped = groupByArray(results, this.foreignKey);
|
|
7664
|
+
for (const model of models) {
|
|
7665
|
+
const key = String(model.get(this.localKey));
|
|
7666
|
+
model.$setRelation(relationName, grouped[key]?.[0] ?? null);
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
async getResults(parent) {
|
|
7670
|
+
return await this.query(parent).executeTakeFirst() ?? null;
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
|
|
7674
|
+
class ManyToMany extends Relation {
|
|
7675
|
+
#options;
|
|
7676
|
+
#pivotExtras;
|
|
7677
|
+
constructor(relatedThunk, options) {
|
|
7678
|
+
super("manyToMany", relatedThunk);
|
|
7679
|
+
this.#options = options;
|
|
7680
|
+
this.#pivotExtras = options.pivotExtras ?? [];
|
|
7681
|
+
}
|
|
7682
|
+
get foreignKey() {
|
|
7683
|
+
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
7684
|
+
}
|
|
7685
|
+
get localKey() {
|
|
7686
|
+
return this.#options.localKey ?? "id";
|
|
7687
|
+
}
|
|
7688
|
+
get throughTable() {
|
|
7689
|
+
return this.#options.through;
|
|
7690
|
+
}
|
|
7691
|
+
get foreignPivotKey() {
|
|
7692
|
+
return this.#options.foreignPivotKey ?? snakeCase(this.foreignKey);
|
|
7693
|
+
}
|
|
7694
|
+
get relatedPivotKey() {
|
|
7695
|
+
return this.#options.relatedPivotKey ?? snakeCase(guessForeignKey(this.relatedModelClass));
|
|
7696
|
+
}
|
|
7697
|
+
#hasExtras() {
|
|
7698
|
+
return this.#pivotExtras.length > 0;
|
|
7699
|
+
}
|
|
7700
|
+
query(parent) {
|
|
7701
|
+
const parentKey = parent.get(this.localKey);
|
|
7702
|
+
const peta = this.relatedModelClass.peta;
|
|
7703
|
+
if (!peta)
|
|
7704
|
+
return this.relatedModelClass.query();
|
|
7705
|
+
if (this.#hasExtras()) {
|
|
7706
|
+
const _relatedTable = this.relatedModelClass.table;
|
|
7707
|
+
const peta2 = this.relatedModelClass.peta;
|
|
7708
|
+
if (!peta2)
|
|
7709
|
+
return this.relatedModelClass.query();
|
|
7710
|
+
const subQb = peta2.kysely.selectFrom(this.throughTable).select(this.relatedPivotKey).where(this.foreignPivotKey, "=", parentKey);
|
|
7711
|
+
return this.relatedModelClass.query().whereIn("id", subQb);
|
|
7712
|
+
}
|
|
7713
|
+
const subquery = peta.kysely.selectFrom(this.throughTable).select(this.relatedPivotKey).where(this.foreignPivotKey, "=", parentKey);
|
|
7714
|
+
return this.relatedModelClass.query().whereIn("id", subquery);
|
|
7715
|
+
}
|
|
7716
|
+
addEagerConstraints(query, models) {
|
|
7717
|
+
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
7718
|
+
if (keys.length === 0) {
|
|
7719
|
+
query.whereIn("id", []);
|
|
7720
|
+
return;
|
|
7721
|
+
}
|
|
7722
|
+
const relatedTable = this.relatedModelClass.table;
|
|
7723
|
+
query.innerJoin(this.throughTable, `${this.throughTable}.${this.relatedPivotKey}`, `${relatedTable}.${this.localKey}`);
|
|
7724
|
+
query.whereIn(`${this.throughTable}.${this.foreignPivotKey}`, keys);
|
|
7725
|
+
}
|
|
7726
|
+
match(models, results, relationName) {
|
|
7727
|
+
const grouped = groupByArray(results, `_pivot_${this.foreignPivotKey}`);
|
|
7728
|
+
for (const model of models) {
|
|
7729
|
+
const key = String(model.get(this.localKey));
|
|
7730
|
+
const items = grouped[key] ?? [];
|
|
7731
|
+
for (const item of items) {
|
|
7732
|
+
const pivotData = {};
|
|
7733
|
+
for (const ek of Object.keys(item.attributes ?? {})) {
|
|
7734
|
+
if (ek.startsWith("_pivot_")) {
|
|
7735
|
+
pivotData[ek.slice(7)] = item.get(ek);
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
if (Object.keys(pivotData).length > 0) {
|
|
7739
|
+
item.$setRelation("_pivot", pivotData);
|
|
7740
|
+
}
|
|
7741
|
+
}
|
|
7742
|
+
model.$setRelation(relationName, items);
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
async getResults(parent) {
|
|
7746
|
+
return await this.query(parent).execute();
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
class HasManyThrough extends Relation {
|
|
7751
|
+
#options;
|
|
7752
|
+
#throughThunk;
|
|
7753
|
+
constructor(relatedThunk, throughThunk, options = {}) {
|
|
7754
|
+
super("hasManyThrough", relatedThunk);
|
|
7755
|
+
this.#throughThunk = throughThunk;
|
|
7756
|
+
this.#options = options;
|
|
7757
|
+
}
|
|
7758
|
+
get foreignKey() {
|
|
7759
|
+
return this.#options.foreignKey ?? guessForeignKey(resolve2(this.#throughThunk));
|
|
7760
|
+
}
|
|
7761
|
+
get localKey() {
|
|
7762
|
+
return this.#options.localKey ?? "id";
|
|
7763
|
+
}
|
|
7764
|
+
get throughModelClass() {
|
|
7765
|
+
return resolve2(this.#throughThunk);
|
|
7766
|
+
}
|
|
7767
|
+
get throughForeignKey() {
|
|
7768
|
+
return this.#options.throughForeignKey ?? guessForeignKey(this.relatedModelClass);
|
|
7769
|
+
}
|
|
7770
|
+
get throughLocalKey() {
|
|
7771
|
+
return this.#options.throughLocalKey ?? guessForeignKey(this.throughModelClass);
|
|
7772
|
+
}
|
|
7773
|
+
query(parent) {
|
|
7774
|
+
const parentKey = parent.get(this.localKey);
|
|
7775
|
+
const peta = this.relatedModelClass.peta;
|
|
7776
|
+
if (!peta)
|
|
7777
|
+
return this.relatedModelClass.query();
|
|
7778
|
+
const throughTable = this.throughModelClass.table;
|
|
7779
|
+
const _relatedTable = this.relatedModelClass.table;
|
|
7780
|
+
const subquery = peta.kysely.selectFrom(throughTable).select(this.throughForeignKey).where(this.foreignKey, "=", parentKey);
|
|
7781
|
+
return this.relatedModelClass.query().whereIn("id", subquery);
|
|
7782
|
+
}
|
|
7783
|
+
addEagerConstraints(query, models) {
|
|
7784
|
+
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
7785
|
+
if (keys.length === 0) {
|
|
7786
|
+
query.whereIn("id", []);
|
|
7787
|
+
return;
|
|
7788
|
+
}
|
|
7789
|
+
const throughTable = this.throughModelClass.table;
|
|
7790
|
+
const relatedTable = this.relatedModelClass.table;
|
|
7791
|
+
query.innerJoin(throughTable, `${throughTable}.${this.throughForeignKey}`, `${relatedTable}.${this.localKey}`);
|
|
7792
|
+
query.whereIn(`${throughTable}.${this.foreignKey}`, keys);
|
|
7793
|
+
}
|
|
7794
|
+
match(models, results, relationName) {
|
|
7795
|
+
const _throughTable = this.throughModelClass.table;
|
|
7796
|
+
const grouped = groupByArray(results, `_through_${this.foreignKey}`);
|
|
7797
|
+
for (const model of models) {
|
|
7798
|
+
const key = String(model.get(this.localKey));
|
|
7799
|
+
model.$setRelation(relationName, grouped[key] ?? []);
|
|
7800
|
+
}
|
|
7801
|
+
}
|
|
7802
|
+
async getResults(parent) {
|
|
7803
|
+
const parentKey = parent.get(this.localKey);
|
|
7804
|
+
const peta = this.relatedModelClass.peta;
|
|
7805
|
+
if (!peta)
|
|
7806
|
+
return [];
|
|
7807
|
+
const throughTable = this.throughModelClass.table;
|
|
7808
|
+
const rows = await peta.kysely.selectFrom(throughTable).select(this.throughForeignKey).where(this.foreignKey, "=", parentKey).execute();
|
|
7809
|
+
const ids = rows.map((r) => r[this.throughForeignKey]).filter(Boolean);
|
|
7810
|
+
if (ids.length === 0)
|
|
7811
|
+
return [];
|
|
7812
|
+
return await this.relatedModelClass.query().whereIn("id", ids).execute();
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
function snakeCase(str) {
|
|
7816
|
+
return str.charAt(0).toLowerCase() + str.slice(1).replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
7817
|
+
}
|
|
7818
|
+
|
|
7819
|
+
export { isString, isNumber, isBoolean, isNull, isDate, isBigInt, getLast, freeze, IdentifierNode, CreateTableNode, AliasNode, isOperationNodeSource, OperatorNode, RawNode, parseStringReference, ValueNode, parseValueExpression, ParensNode, preventAwait, InsertQueryNode, MergeQueryNode, QueryNode, createQueryId, WithSchemaPlugin, NOOP_QUERY_EXECUTOR, SetOperationNode, parseTable, CreateViewNode, isModelClass, Peta, HasMany, BelongsTo, HasOne, ManyToMany, HasManyThrough };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
+
BelongsTo,
|
|
4
|
+
HasMany,
|
|
5
|
+
HasManyThrough,
|
|
6
|
+
HasOne,
|
|
7
|
+
ManyToMany,
|
|
3
8
|
Peta
|
|
4
|
-
} from "./index-
|
|
9
|
+
} from "./index-gacxptb4.js";
|
|
5
10
|
import {
|
|
6
11
|
ArkTypeSchemaConfig,
|
|
7
12
|
Collection,
|
|
@@ -609,291 +614,6 @@ class Model {
|
|
|
609
614
|
};
|
|
610
615
|
}
|
|
611
616
|
}
|
|
612
|
-
// src/relations/relation.ts
|
|
613
|
-
var thunkCache = new WeakMap;
|
|
614
|
-
function resolve(thunk) {
|
|
615
|
-
let cls = thunkCache.get(thunk);
|
|
616
|
-
if (!cls) {
|
|
617
|
-
cls = thunk();
|
|
618
|
-
thunkCache.set(thunk, cls);
|
|
619
|
-
}
|
|
620
|
-
return cls;
|
|
621
|
-
}
|
|
622
|
-
function guessForeignKey(modelClass) {
|
|
623
|
-
const table = modelClass.table;
|
|
624
|
-
const singular = table.endsWith("s") ? table.slice(0, -1) : table;
|
|
625
|
-
return `${singular}Id`;
|
|
626
|
-
}
|
|
627
|
-
function groupByArray(items, key) {
|
|
628
|
-
const result = {};
|
|
629
|
-
for (const item of items) {
|
|
630
|
-
const v = item.get(key);
|
|
631
|
-
if (v == null)
|
|
632
|
-
continue;
|
|
633
|
-
const k = String(v);
|
|
634
|
-
if (!result[k])
|
|
635
|
-
result[k] = [];
|
|
636
|
-
result[k].push(item);
|
|
637
|
-
}
|
|
638
|
-
return result;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
class Relation {
|
|
642
|
-
type;
|
|
643
|
-
#relatedThunk;
|
|
644
|
-
constructor(type, relatedThunk) {
|
|
645
|
-
this.type = type;
|
|
646
|
-
this.#relatedThunk = relatedThunk;
|
|
647
|
-
}
|
|
648
|
-
get relatedModelClass() {
|
|
649
|
-
return resolve(this.#relatedThunk);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
class HasMany extends Relation {
|
|
654
|
-
#options;
|
|
655
|
-
constructor(relatedThunk, options = {}) {
|
|
656
|
-
super("hasMany", relatedThunk);
|
|
657
|
-
this.#options = options;
|
|
658
|
-
}
|
|
659
|
-
get foreignKey() {
|
|
660
|
-
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
661
|
-
}
|
|
662
|
-
get localKey() {
|
|
663
|
-
return this.#options.localKey ?? "id";
|
|
664
|
-
}
|
|
665
|
-
query(parent) {
|
|
666
|
-
return this.relatedModelClass.query().where(this.foreignKey, "=", parent.get(this.localKey));
|
|
667
|
-
}
|
|
668
|
-
addEagerConstraints(query, models) {
|
|
669
|
-
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
670
|
-
if (keys.length > 0) {
|
|
671
|
-
query.whereIn(this.foreignKey, keys);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
match(models, results, relationName) {
|
|
675
|
-
const grouped = groupByArray(results, this.foreignKey);
|
|
676
|
-
for (const model of models) {
|
|
677
|
-
const key = String(model.get(this.localKey));
|
|
678
|
-
model.$setRelation(relationName, grouped[key] ?? []);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
async getResults(parent) {
|
|
682
|
-
return await this.query(parent).execute();
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
class BelongsTo extends Relation {
|
|
687
|
-
#options;
|
|
688
|
-
constructor(relatedThunk, options = {}) {
|
|
689
|
-
super("belongsTo", relatedThunk);
|
|
690
|
-
this.#options = options;
|
|
691
|
-
}
|
|
692
|
-
get foreignKey() {
|
|
693
|
-
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
694
|
-
}
|
|
695
|
-
get localKey() {
|
|
696
|
-
return this.#options.localKey ?? "id";
|
|
697
|
-
}
|
|
698
|
-
query(parent) {
|
|
699
|
-
return this.relatedModelClass.query().where(this.localKey, "=", parent.get(this.foreignKey));
|
|
700
|
-
}
|
|
701
|
-
addEagerConstraints(query, models) {
|
|
702
|
-
const keys = models.map((m) => m.get(this.foreignKey)).filter((k) => k != null);
|
|
703
|
-
if (keys.length > 0) {
|
|
704
|
-
query.whereIn(this.localKey, keys);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
match(models, results, relationName) {
|
|
708
|
-
const grouped = groupByArray(results, this.localKey);
|
|
709
|
-
for (const model of models) {
|
|
710
|
-
const key = String(model.get(this.foreignKey));
|
|
711
|
-
model.$setRelation(relationName, grouped[key]?.[0] ?? null);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
async getResults(parent) {
|
|
715
|
-
return await this.query(parent).executeTakeFirst() ?? null;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
class HasOne extends Relation {
|
|
720
|
-
#options;
|
|
721
|
-
constructor(relatedThunk, options = {}) {
|
|
722
|
-
super("hasOne", relatedThunk);
|
|
723
|
-
this.#options = options;
|
|
724
|
-
}
|
|
725
|
-
get foreignKey() {
|
|
726
|
-
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
727
|
-
}
|
|
728
|
-
get localKey() {
|
|
729
|
-
return this.#options.localKey ?? "id";
|
|
730
|
-
}
|
|
731
|
-
query(parent) {
|
|
732
|
-
return this.relatedModelClass.query().where(this.foreignKey, "=", parent.get(this.localKey));
|
|
733
|
-
}
|
|
734
|
-
addEagerConstraints(query, models) {
|
|
735
|
-
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
736
|
-
if (keys.length > 0) {
|
|
737
|
-
query.whereIn(this.foreignKey, keys);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
match(models, results, relationName) {
|
|
741
|
-
const grouped = groupByArray(results, this.foreignKey);
|
|
742
|
-
for (const model of models) {
|
|
743
|
-
const key = String(model.get(this.localKey));
|
|
744
|
-
model.$setRelation(relationName, grouped[key]?.[0] ?? null);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
async getResults(parent) {
|
|
748
|
-
return await this.query(parent).executeTakeFirst() ?? null;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
class ManyToMany extends Relation {
|
|
753
|
-
#options;
|
|
754
|
-
#pivotExtras;
|
|
755
|
-
constructor(relatedThunk, options) {
|
|
756
|
-
super("manyToMany", relatedThunk);
|
|
757
|
-
this.#options = options;
|
|
758
|
-
this.#pivotExtras = options.pivotExtras ?? [];
|
|
759
|
-
}
|
|
760
|
-
get foreignKey() {
|
|
761
|
-
return this.#options.foreignKey ?? guessForeignKey(this.relatedModelClass);
|
|
762
|
-
}
|
|
763
|
-
get localKey() {
|
|
764
|
-
return this.#options.localKey ?? "id";
|
|
765
|
-
}
|
|
766
|
-
get throughTable() {
|
|
767
|
-
return this.#options.through;
|
|
768
|
-
}
|
|
769
|
-
get foreignPivotKey() {
|
|
770
|
-
return this.#options.foreignPivotKey ?? snakeCase(this.foreignKey);
|
|
771
|
-
}
|
|
772
|
-
get relatedPivotKey() {
|
|
773
|
-
return this.#options.relatedPivotKey ?? snakeCase(guessForeignKey(this.relatedModelClass));
|
|
774
|
-
}
|
|
775
|
-
#hasExtras() {
|
|
776
|
-
return this.#pivotExtras.length > 0;
|
|
777
|
-
}
|
|
778
|
-
query(parent) {
|
|
779
|
-
const parentKey = parent.get(this.localKey);
|
|
780
|
-
const peta = this.relatedModelClass.peta;
|
|
781
|
-
if (!peta)
|
|
782
|
-
return this.relatedModelClass.query();
|
|
783
|
-
if (this.#hasExtras()) {
|
|
784
|
-
const _relatedTable = this.relatedModelClass.table;
|
|
785
|
-
const peta2 = this.relatedModelClass.peta;
|
|
786
|
-
if (!peta2)
|
|
787
|
-
return this.relatedModelClass.query();
|
|
788
|
-
const subQb = peta2.kysely.selectFrom(this.throughTable).select(this.relatedPivotKey).where(this.foreignPivotKey, "=", parentKey);
|
|
789
|
-
return this.relatedModelClass.query().whereIn("id", subQb);
|
|
790
|
-
}
|
|
791
|
-
const subquery = peta.kysely.selectFrom(this.throughTable).select(this.relatedPivotKey).where(this.foreignPivotKey, "=", parentKey);
|
|
792
|
-
return this.relatedModelClass.query().whereIn("id", subquery);
|
|
793
|
-
}
|
|
794
|
-
addEagerConstraints(query, models) {
|
|
795
|
-
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
796
|
-
if (keys.length === 0) {
|
|
797
|
-
query.whereIn("id", []);
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
const relatedTable = this.relatedModelClass.table;
|
|
801
|
-
query.innerJoin(this.throughTable, `${this.throughTable}.${this.relatedPivotKey}`, `${relatedTable}.${this.localKey}`);
|
|
802
|
-
query.whereIn(`${this.throughTable}.${this.foreignPivotKey}`, keys);
|
|
803
|
-
}
|
|
804
|
-
match(models, results, relationName) {
|
|
805
|
-
const grouped = groupByArray(results, `_pivot_${this.foreignPivotKey}`);
|
|
806
|
-
for (const model of models) {
|
|
807
|
-
const key = String(model.get(this.localKey));
|
|
808
|
-
const items = grouped[key] ?? [];
|
|
809
|
-
for (const item of items) {
|
|
810
|
-
const pivotData = {};
|
|
811
|
-
for (const ek of Object.keys(item.attributes ?? {})) {
|
|
812
|
-
if (ek.startsWith("_pivot_")) {
|
|
813
|
-
pivotData[ek.slice(7)] = item.get(ek);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
if (Object.keys(pivotData).length > 0) {
|
|
817
|
-
item.$setRelation("_pivot", pivotData);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
model.$setRelation(relationName, items);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
async getResults(parent) {
|
|
824
|
-
return await this.query(parent).execute();
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
class HasManyThrough extends Relation {
|
|
829
|
-
#options;
|
|
830
|
-
#throughThunk;
|
|
831
|
-
constructor(relatedThunk, throughThunk, options = {}) {
|
|
832
|
-
super("hasManyThrough", relatedThunk);
|
|
833
|
-
this.#throughThunk = throughThunk;
|
|
834
|
-
this.#options = options;
|
|
835
|
-
}
|
|
836
|
-
get foreignKey() {
|
|
837
|
-
return this.#options.foreignKey ?? guessForeignKey(resolve(this.#throughThunk));
|
|
838
|
-
}
|
|
839
|
-
get localKey() {
|
|
840
|
-
return this.#options.localKey ?? "id";
|
|
841
|
-
}
|
|
842
|
-
get throughModelClass() {
|
|
843
|
-
return resolve(this.#throughThunk);
|
|
844
|
-
}
|
|
845
|
-
get throughForeignKey() {
|
|
846
|
-
return this.#options.throughForeignKey ?? guessForeignKey(this.relatedModelClass);
|
|
847
|
-
}
|
|
848
|
-
get throughLocalKey() {
|
|
849
|
-
return this.#options.throughLocalKey ?? guessForeignKey(this.throughModelClass);
|
|
850
|
-
}
|
|
851
|
-
query(parent) {
|
|
852
|
-
const parentKey = parent.get(this.localKey);
|
|
853
|
-
const peta = this.relatedModelClass.peta;
|
|
854
|
-
if (!peta)
|
|
855
|
-
return this.relatedModelClass.query();
|
|
856
|
-
const throughTable = this.throughModelClass.table;
|
|
857
|
-
const _relatedTable = this.relatedModelClass.table;
|
|
858
|
-
const subquery = peta.kysely.selectFrom(throughTable).select(this.throughForeignKey).where(this.foreignKey, "=", parentKey);
|
|
859
|
-
return this.relatedModelClass.query().whereIn("id", subquery);
|
|
860
|
-
}
|
|
861
|
-
addEagerConstraints(query, models) {
|
|
862
|
-
const keys = models.map((m) => m.get(this.localKey)).filter((k) => k != null);
|
|
863
|
-
if (keys.length === 0) {
|
|
864
|
-
query.whereIn("id", []);
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
const throughTable = this.throughModelClass.table;
|
|
868
|
-
const relatedTable = this.relatedModelClass.table;
|
|
869
|
-
query.innerJoin(throughTable, `${throughTable}.${this.throughForeignKey}`, `${relatedTable}.${this.localKey}`);
|
|
870
|
-
query.whereIn(`${throughTable}.${this.foreignKey}`, keys);
|
|
871
|
-
}
|
|
872
|
-
match(models, results, relationName) {
|
|
873
|
-
const _throughTable = this.throughModelClass.table;
|
|
874
|
-
const grouped = groupByArray(results, `_through_${this.foreignKey}`);
|
|
875
|
-
for (const model of models) {
|
|
876
|
-
const key = String(model.get(this.localKey));
|
|
877
|
-
model.$setRelation(relationName, grouped[key] ?? []);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
async getResults(parent) {
|
|
881
|
-
const parentKey = parent.get(this.localKey);
|
|
882
|
-
const peta = this.relatedModelClass.peta;
|
|
883
|
-
if (!peta)
|
|
884
|
-
return [];
|
|
885
|
-
const throughTable = this.throughModelClass.table;
|
|
886
|
-
const rows = await peta.kysely.selectFrom(throughTable).select(this.throughForeignKey).where(this.foreignKey, "=", parentKey).execute();
|
|
887
|
-
const ids = rows.map((r) => r[this.throughForeignKey]).filter(Boolean);
|
|
888
|
-
if (ids.length === 0)
|
|
889
|
-
return [];
|
|
890
|
-
return await this.relatedModelClass.query().whereIn("id", ids).execute();
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
function snakeCase(str) {
|
|
894
|
-
return str.charAt(0).toLowerCase() + str.slice(1).replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
617
|
// src/relations/morph.ts
|
|
898
618
|
class MorphTo extends BelongsTo {
|
|
899
619
|
morphType;
|
package/dist/migrations/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/migrations/generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/migrations/generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,qBAAa,kBAAkB;;IAC7B,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM;CAyJlG"}
|
package/dist/migrations/index.js
CHANGED