peta-orm 0.4.1 → 0.5.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 +47 -5
- package/dist/index.d.mts +247 -152
- package/dist/index.mjs +147 -6
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -20,24 +20,67 @@ const page = await Post.query().with("author").orderBy("id", "asc").paginate(1,
|
|
|
20
20
|
bun add peta-orm arktype kysely @libsql/kysely-libsql @libsql/client
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
### Simple setup (examples, scripts)
|
|
24
|
+
|
|
23
25
|
```ts
|
|
24
26
|
import { createClient } from "@libsql/client"
|
|
25
27
|
import { LibsqlDialect } from "@libsql/kysely-libsql"
|
|
26
28
|
import { createORM, defineModel, t } from "peta-orm"
|
|
27
29
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
+
const User = defineModel("users", {
|
|
31
|
+
columns: { id: t.integer().primaryKey(), name: t.string(255), email: t.text().unique() },
|
|
30
32
|
})
|
|
31
33
|
|
|
34
|
+
// Eager init — fine for scripts, one-off tasks
|
|
35
|
+
const client = createClient({ url: "file::memory:?cache=shared" })
|
|
36
|
+
await client.execute(
|
|
37
|
+
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE)",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const orm = createORM({ dialect: new LibsqlDialect({ client }), models: { User } })
|
|
41
|
+
|
|
42
|
+
const user = await User.insert({ name: "Alice", email: "alice@test.com" })
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Production setup (apps, servers) — no module-level side effects
|
|
46
|
+
|
|
47
|
+
Module-level side effects (database connections, schema init, ORM setup at import time) cause problems with testing, HMR, and error recovery. Use `createDb()` for lazy, safe initialization:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { createClient } from "@libsql/client"
|
|
51
|
+
import { LibsqlDialect } from "@libsql/kysely-libsql"
|
|
52
|
+
import { createDb, createORM, defineModel, t } from "peta-orm"
|
|
53
|
+
|
|
32
54
|
const User = defineModel("users", {
|
|
33
55
|
columns: { id: t.integer().primaryKey(), name: t.string(255), email: t.text().unique() },
|
|
34
56
|
})
|
|
35
57
|
|
|
36
|
-
|
|
58
|
+
async function setup() {
|
|
59
|
+
const client = createClient({ url: "file:my-app.db" })
|
|
60
|
+
await client.execute(
|
|
61
|
+
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE)",
|
|
62
|
+
)
|
|
63
|
+
const orm = createORM({ dialect: new LibsqlDialect({ client }) })
|
|
64
|
+
orm.registerAll(User)
|
|
65
|
+
return orm
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Lazy singleton — first call creates the connection, subsequent calls reuse it. */
|
|
69
|
+
export const db = createDb(setup)
|
|
37
70
|
|
|
38
|
-
|
|
71
|
+
// In route handlers:
|
|
72
|
+
// const orm = await db()
|
|
73
|
+
// const users = await User.query().execute()
|
|
39
74
|
```
|
|
40
75
|
|
|
76
|
+
The factory function runs **once** on the first `await db()` call. Importing models has zero side effects — no connection, no schema init, no unhandled promises.
|
|
77
|
+
|
|
78
|
+
> [!TIP]
|
|
79
|
+
> For an existing Kysely instance (e.g. from a migration runner), pass it via the `kysely` config option:
|
|
80
|
+
> ```ts
|
|
81
|
+
> const orm = createORM({ kysely: existingKysely })
|
|
82
|
+
> ```
|
|
83
|
+
|
|
41
84
|
> [!TIP]
|
|
42
85
|
> See the 32 [runnable examples](./examples) for every feature. Run them with `bun run examples/XX-*.ts`.
|
|
43
86
|
|
|
@@ -70,7 +113,6 @@ const user = await User.insert({ name: "Alice", email: "alice@test.com" })
|
|
|
70
113
|
Column definitions double as validation schemas — no separate validation step needed.
|
|
71
114
|
|
|
72
115
|
```ts
|
|
73
|
-
const t = columnTypes({ schema: createArkTypeSchemaConfig() })
|
|
74
116
|
|
|
75
117
|
const User = defineModel("users", {
|
|
76
118
|
columns: {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,51 @@
|
|
|
1
1
|
import { Dialect, Kysely } from "kysely";
|
|
2
2
|
|
|
3
|
+
//#region src/columns/schema.d.ts
|
|
4
|
+
interface Constraint {
|
|
5
|
+
type: string;
|
|
6
|
+
args: unknown[];
|
|
7
|
+
}
|
|
8
|
+
interface SchemaConfig {
|
|
9
|
+
compile(dataType: string, args: unknown[], constraints: Constraint[]): unknown;
|
|
10
|
+
parse<T>(schema: unknown, value: unknown): T;
|
|
11
|
+
assert<T>(schema: unknown, value: unknown): T;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/columns/column.d.ts
|
|
15
|
+
interface Column<out T = unknown> {
|
|
16
|
+
readonly arkType: unknown;
|
|
17
|
+
readonly dataType: string;
|
|
18
|
+
readonly args: readonly unknown[];
|
|
19
|
+
readonly constraints: readonly Constraint[];
|
|
20
|
+
readonly isNullable: boolean;
|
|
21
|
+
readonly isPrimaryKey: boolean;
|
|
22
|
+
readonly isUnique: boolean;
|
|
23
|
+
readonly defaultValue: unknown;
|
|
24
|
+
hasConstraint(type: string): boolean;
|
|
25
|
+
parse(value: unknown): T;
|
|
26
|
+
assert(value: unknown): T;
|
|
27
|
+
primaryKey(): Column<T>;
|
|
28
|
+
nullable(): Column<T | null>;
|
|
29
|
+
default<V>(value: V): Column<T>;
|
|
30
|
+
unique(): Column<T>;
|
|
31
|
+
index(): Column<T>;
|
|
32
|
+
min(n: number): Column<T>;
|
|
33
|
+
max(n: number): Column<T>;
|
|
34
|
+
email(): Column<T>;
|
|
35
|
+
url(): Column<T>;
|
|
36
|
+
pattern(regex: RegExp | string): Column<T>;
|
|
37
|
+
references(table: () => unknown, columns: string[]): Column<T>;
|
|
38
|
+
}
|
|
39
|
+
declare function createColumn<T>(schema: SchemaConfig, dataType: string, args?: unknown[], constraints?: Constraint[]): Column<T>;
|
|
40
|
+
type ColumnShape = Record<string, Column>;
|
|
41
|
+
type ColumnValue<C> = C extends Column<infer T> ? T : never;
|
|
42
|
+
//#endregion
|
|
3
43
|
//#region src/lib/kysely.d.ts
|
|
4
44
|
type Database = Kysely<Record<string, never>>;
|
|
5
45
|
//#endregion
|
|
6
46
|
//#region src/pagination/index.d.ts
|
|
7
|
-
interface Paginator {
|
|
8
|
-
readonly data: Collection
|
|
47
|
+
interface Paginator<TColumns extends ColumnShape = ColumnShape> {
|
|
48
|
+
readonly data: Collection<TColumns>;
|
|
9
49
|
readonly total: number;
|
|
10
50
|
readonly perPage: number;
|
|
11
51
|
readonly currentPage: number;
|
|
@@ -17,11 +57,11 @@ interface Paginator {
|
|
|
17
57
|
readonly onFirstPage: boolean;
|
|
18
58
|
readonly onLastPage: boolean;
|
|
19
59
|
readonly count: number;
|
|
20
|
-
map<T>(fn: (item: ModelInstance) => T): T[];
|
|
21
|
-
toJSON(): PaginatorJson
|
|
60
|
+
map<T>(fn: (item: ModelInstance<TColumns>) => T): T[];
|
|
61
|
+
toJSON(): PaginatorJson<TColumns>;
|
|
22
62
|
}
|
|
23
|
-
interface PaginatorJson {
|
|
24
|
-
data:
|
|
63
|
+
interface PaginatorJson<TColumns extends ColumnShape = ColumnShape> {
|
|
64
|
+
data: SerializedShape<TColumns>[];
|
|
25
65
|
total: number;
|
|
26
66
|
perPage: number;
|
|
27
67
|
currentPage: number;
|
|
@@ -34,7 +74,7 @@ interface PaginatorJson {
|
|
|
34
74
|
onLastPage: boolean;
|
|
35
75
|
}
|
|
36
76
|
type PaginatedResult = PaginatorJson;
|
|
37
|
-
declare function createPaginator(items: ModelInstance[], total: number, perPage: number, currentPage: number): Paginator
|
|
77
|
+
declare function createPaginator<TColumns extends ColumnShape = ColumnShape>(items: ModelInstance<TColumns>[], total: number, perPage: number, currentPage: number): Paginator<TColumns>;
|
|
38
78
|
//#endregion
|
|
39
79
|
//#region src/relations/base.d.ts
|
|
40
80
|
type RelationType = "hasMany" | "belongsTo" | "hasOne" | "manyToMany" | "hasManyThrough";
|
|
@@ -97,14 +137,14 @@ interface UpsertGraphOptions extends InsertGraphOptions {
|
|
|
97
137
|
}
|
|
98
138
|
//#endregion
|
|
99
139
|
//#region src/query/types.d.ts
|
|
100
|
-
interface QueryBuilder extends PromiseLike<ModelInstance[]> {
|
|
101
|
-
execute(): Promise<ModelInstance[]>;
|
|
102
|
-
collect(): Promise<Collection
|
|
103
|
-
executeTakeFirst(): Promise<ModelInstance | undefined>;
|
|
104
|
-
executeTakeFirstOrThrow(): Promise<ModelInstance
|
|
105
|
-
find(id: number | string): Promise<ModelInstance | undefined>;
|
|
106
|
-
findOrFail(id: number | string): Promise<ModelInstance
|
|
107
|
-
first(): Promise<ModelInstance | undefined>;
|
|
140
|
+
interface QueryBuilder<TColumns extends ColumnShape = ColumnShape> extends PromiseLike<ModelInstance<TColumns>[]> {
|
|
141
|
+
execute(): Promise<ModelInstance<TColumns>[]>;
|
|
142
|
+
collect(): Promise<Collection<TColumns>>;
|
|
143
|
+
executeTakeFirst(): Promise<ModelInstance<TColumns> | undefined>;
|
|
144
|
+
executeTakeFirstOrThrow(): Promise<ModelInstance<TColumns>>;
|
|
145
|
+
find(id: number | string): Promise<ModelInstance<TColumns> | undefined>;
|
|
146
|
+
findOrFail(id: number | string): Promise<ModelInstance<TColumns>>;
|
|
147
|
+
first(): Promise<ModelInstance<TColumns> | undefined>;
|
|
108
148
|
toSQL(): {
|
|
109
149
|
sql: string;
|
|
110
150
|
parameters: readonly unknown[];
|
|
@@ -114,17 +154,17 @@ interface QueryBuilder extends PromiseLike<ModelInstance[]> {
|
|
|
114
154
|
avg(column: string): Promise<number>;
|
|
115
155
|
min(column: string): Promise<number>;
|
|
116
156
|
max(column: string): Promise<number>;
|
|
117
|
-
withCount(relation: string): QueryBuilder
|
|
118
|
-
withSum(relation: string, column: string): QueryBuilder
|
|
119
|
-
withAvg(relation: string, column: string): QueryBuilder
|
|
120
|
-
withMin(relation: string, column: string): QueryBuilder
|
|
121
|
-
withMax(relation: string, column: string): QueryBuilder
|
|
122
|
-
withExists(relation: string): QueryBuilder
|
|
123
|
-
chunk(size: number, callback: (chunk: ModelInstance[]) => Promise<void>): Promise<void>;
|
|
124
|
-
paginate(page: number, perPage?: number): Promise<Paginator
|
|
157
|
+
withCount(relation: string): QueryBuilder<TColumns>;
|
|
158
|
+
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
|
+
chunk(size: number, callback: (chunk: ModelInstance<TColumns>[]) => Promise<void>): Promise<void>;
|
|
164
|
+
paginate(page: number, perPage?: number): Promise<Paginator<TColumns>>;
|
|
125
165
|
insertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: InsertGraphOptions): Promise<any>;
|
|
126
166
|
upsertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: UpsertGraphOptions): Promise<any>;
|
|
127
|
-
with(...relations: (string | Record<string, (qb: QueryBuilder) => void>)[]): QueryBuilder
|
|
167
|
+
with(...relations: (string | Record<string, (qb: QueryBuilder<TColumns>) => void>)[]): QueryBuilder<TColumns>;
|
|
128
168
|
/**
|
|
129
169
|
* Whitelist allowed relations (and nested paths) for eager loading.
|
|
130
170
|
* Throws if a relation path is not in the allow list.
|
|
@@ -136,42 +176,42 @@ interface QueryBuilder extends PromiseLike<ModelInstance[]> {
|
|
|
136
176
|
*
|
|
137
177
|
* Multiple arguments are merged: `allowGraph("posts", "profile")`
|
|
138
178
|
*/
|
|
139
|
-
allowGraph(...expressions: string[]): QueryBuilder
|
|
179
|
+
allowGraph(...expressions: string[]): QueryBuilder<TColumns>;
|
|
140
180
|
updateMany(data: Record<string, unknown>): Promise<number>;
|
|
141
181
|
deleteMany(): Promise<number>;
|
|
142
|
-
withTrashed(): QueryBuilder
|
|
143
|
-
onlyTrashed(): QueryBuilder
|
|
144
|
-
whereIn(column: string, values: unknown[]): QueryBuilder
|
|
145
|
-
whereInPivot(column: string, values: unknown[]): QueryBuilder
|
|
146
|
-
has(relationName: string): QueryBuilder
|
|
147
|
-
whereHas(relationName: string, callback?: (qb: QueryBuilder) => void): QueryBuilder
|
|
148
|
-
whereDoesntHave(relationName: string, callback?: (qb: QueryBuilder) => void): QueryBuilder
|
|
149
|
-
where(column: string, operator: unknown, value?: unknown): QueryBuilder
|
|
150
|
-
whereRef(col1: string, operator: string, col2: string): QueryBuilder
|
|
151
|
-
orWhere(column: string, operator: unknown, value?: unknown): QueryBuilder
|
|
152
|
-
orderBy(column: string, direction?: "asc" | "desc"): QueryBuilder
|
|
153
|
-
limit(n: number): QueryBuilder
|
|
154
|
-
offset(n: number): QueryBuilder
|
|
155
|
-
select(...columns: string[]): QueryBuilder
|
|
156
|
-
selectAll(table?: string): QueryBuilder
|
|
157
|
-
innerJoin(table: string, lhs: string, rhs: string): QueryBuilder
|
|
158
|
-
leftJoin(table: string, lhs: string, rhs: string): QueryBuilder
|
|
159
|
-
groupBy(...columns: string[]): QueryBuilder
|
|
160
|
-
having(column: string, operator: string, value: unknown): QueryBuilder
|
|
161
|
-
withoutGlobalScope(name: string): QueryBuilder
|
|
162
|
-
all(): QueryBuilder
|
|
182
|
+
withTrashed(): QueryBuilder<TColumns>;
|
|
183
|
+
onlyTrashed(): QueryBuilder<TColumns>;
|
|
184
|
+
whereIn(column: string, values: unknown[]): QueryBuilder<TColumns>;
|
|
185
|
+
whereInPivot(column: string, values: unknown[]): QueryBuilder<TColumns>;
|
|
186
|
+
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>;
|
|
189
|
+
where(column: string, operator: unknown, value?: unknown): QueryBuilder<TColumns>;
|
|
190
|
+
whereRef(col1: string, operator: string, col2: string): QueryBuilder<TColumns>;
|
|
191
|
+
orWhere(column: string, operator: unknown, value?: unknown): QueryBuilder<TColumns>;
|
|
192
|
+
orderBy(column: string, direction?: "asc" | "desc"): QueryBuilder<TColumns>;
|
|
193
|
+
limit(n: number): QueryBuilder<TColumns>;
|
|
194
|
+
offset(n: number): QueryBuilder<TColumns>;
|
|
195
|
+
select(...columns: string[]): QueryBuilder<TColumns>;
|
|
196
|
+
selectAll(table?: string): QueryBuilder<TColumns>;
|
|
197
|
+
innerJoin(table: string, lhs: string, rhs: string): QueryBuilder<TColumns>;
|
|
198
|
+
leftJoin(table: string, lhs: string, rhs: string): QueryBuilder<TColumns>;
|
|
199
|
+
groupBy(...columns: string[]): QueryBuilder<TColumns>;
|
|
200
|
+
having(column: string, operator: string, value: unknown): QueryBuilder<TColumns>;
|
|
201
|
+
withoutGlobalScope(name: string): QueryBuilder<TColumns>;
|
|
202
|
+
all(): QueryBuilder<TColumns>;
|
|
163
203
|
/** @internal Access underlying Kysely builder for raw SQL operations */
|
|
164
204
|
_getKyselyQb(): any;
|
|
165
205
|
/** @internal Replace the underlying Kysely builder */
|
|
166
206
|
_replaceKyselyQb(newQb: any): void;
|
|
167
|
-
when(condition: unknown, callback: (q: QueryBuilder) => QueryBuilder): QueryBuilder
|
|
168
|
-
unless(condition: unknown, callback: (q: QueryBuilder) => QueryBuilder): QueryBuilder
|
|
207
|
+
when(condition: unknown, callback: (q: QueryBuilder<TColumns>) => QueryBuilder<TColumns>): QueryBuilder<TColumns>;
|
|
208
|
+
unless(condition: unknown, callback: (q: QueryBuilder<TColumns>) => QueryBuilder<TColumns>): QueryBuilder<TColumns>;
|
|
169
209
|
}
|
|
170
210
|
//#endregion
|
|
171
211
|
//#region src/query/builder.d.ts
|
|
172
|
-
declare function createQueryBuilder(def: ModelDefinition
|
|
212
|
+
declare function createQueryBuilder<TColumns extends ColumnShape = ColumnShape>(def: ModelDefinition<TColumns>, peta?: {
|
|
173
213
|
kysely: Database;
|
|
174
|
-
}): QueryBuilder
|
|
214
|
+
}): QueryBuilder<TColumns>;
|
|
175
215
|
//#endregion
|
|
176
216
|
//#region src/relations/related-query.d.ts
|
|
177
217
|
interface RelationQuery extends QueryBuilder {
|
|
@@ -202,52 +242,13 @@ interface RelationQuery extends QueryBuilder {
|
|
|
202
242
|
*/
|
|
203
243
|
type Plugin = (def: ModelDefinition) => undefined | ModelDefinition;
|
|
204
244
|
//#endregion
|
|
205
|
-
//#region src/columns/schema.d.ts
|
|
206
|
-
interface Constraint {
|
|
207
|
-
type: string;
|
|
208
|
-
args: unknown[];
|
|
209
|
-
}
|
|
210
|
-
interface SchemaConfig {
|
|
211
|
-
compile(dataType: string, args: unknown[], constraints: Constraint[]): unknown;
|
|
212
|
-
parse<T>(schema: unknown, value: unknown): T;
|
|
213
|
-
assert<T>(schema: unknown, value: unknown): T;
|
|
214
|
-
}
|
|
215
|
-
//#endregion
|
|
216
|
-
//#region src/columns/column.d.ts
|
|
217
|
-
interface Column<out T = unknown> {
|
|
218
|
-
readonly arkType: unknown;
|
|
219
|
-
readonly dataType: string;
|
|
220
|
-
readonly args: readonly unknown[];
|
|
221
|
-
readonly constraints: readonly Constraint[];
|
|
222
|
-
readonly isNullable: boolean;
|
|
223
|
-
readonly isPrimaryKey: boolean;
|
|
224
|
-
readonly isUnique: boolean;
|
|
225
|
-
readonly defaultValue: unknown;
|
|
226
|
-
hasConstraint(type: string): boolean;
|
|
227
|
-
parse(value: unknown): T;
|
|
228
|
-
assert(value: unknown): T;
|
|
229
|
-
primaryKey(): Column<T>;
|
|
230
|
-
nullable(): Column<T | null>;
|
|
231
|
-
default<V>(value: V): Column<T>;
|
|
232
|
-
unique(): Column<T>;
|
|
233
|
-
index(): Column<T>;
|
|
234
|
-
min(n: number): Column<T>;
|
|
235
|
-
max(n: number): Column<T>;
|
|
236
|
-
email(): Column<T>;
|
|
237
|
-
url(): Column<T>;
|
|
238
|
-
pattern(regex: RegExp | string): Column<T>;
|
|
239
|
-
references(table: () => unknown, columns: string[]): Column<T>;
|
|
240
|
-
}
|
|
241
|
-
declare function createColumn<T>(schema: SchemaConfig, dataType: string, args?: unknown[], constraints?: Constraint[]): Column<T>;
|
|
242
|
-
type ColumnShape = Record<string, Column>;
|
|
243
|
-
type ColumnValue<C> = C extends Column<infer T> ? T : never;
|
|
244
|
-
//#endregion
|
|
245
245
|
//#region src/types.d.ts
|
|
246
246
|
type ModelId = number & {
|
|
247
247
|
readonly __brand: "ModelId";
|
|
248
248
|
};
|
|
249
|
-
interface ModelLike {
|
|
250
|
-
get<
|
|
249
|
+
interface ModelLike<TColumns extends ColumnShape = ColumnShape> {
|
|
250
|
+
get<K extends keyof TColumns>(key: K): ColumnValue<TColumns[K]>;
|
|
251
|
+
get(key: string): unknown;
|
|
251
252
|
set(key: string, value: unknown): void;
|
|
252
253
|
}
|
|
253
254
|
interface ORMLike {
|
|
@@ -258,6 +259,17 @@ interface ORMLike {
|
|
|
258
259
|
transaction<T>(fn: (trx: import("kysely").Kysely<Record<string, never>>) => Promise<T>): Promise<T>;
|
|
259
260
|
readonly models: ReadonlyMap<string, ModelDefinition$1<any>>;
|
|
260
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>[]>;
|
|
261
273
|
}
|
|
262
274
|
interface ModelDefinition$1<TColumns extends ColumnShape = ColumnShape> {
|
|
263
275
|
readonly table: string;
|
|
@@ -265,17 +277,17 @@ interface ModelDefinition$1<TColumns extends ColumnShape = ColumnShape> {
|
|
|
265
277
|
readonly relations: Record<string, Relation>;
|
|
266
278
|
readonly name: string;
|
|
267
279
|
_orm: ORMLike | null;
|
|
268
|
-
query(): QueryBuilder
|
|
269
|
-
find(id: number | string): Promise<ModelInstance | undefined>;
|
|
270
|
-
findOrFail(id: number | string): Promise<ModelInstance
|
|
271
|
-
first(): Promise<ModelInstance | undefined>;
|
|
272
|
-
create(data: Record<string, unknown>): Promise<ModelInstance
|
|
273
|
-
insert(data: Record<string, unknown>): Promise<ModelInstance
|
|
274
|
-
insertMany(dataArray: Record<string, unknown>[]): Promise<ModelInstance[]>;
|
|
275
|
-
update(id: number | string, data: Record<string, unknown>): Promise<ModelInstance
|
|
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>>;
|
|
276
288
|
delete(id: number | string): Promise<void>;
|
|
277
|
-
hydrate(row: Record<string, unknown>): ModelInstance
|
|
278
|
-
on(event: string, callback: (model: ModelInstance) => void | Promise<void>): () => void;
|
|
289
|
+
hydrate(row: Record<string, unknown>): ModelInstance<TColumns>;
|
|
290
|
+
on(event: string, callback: (model: ModelInstance<TColumns>) => void | Promise<void>): () => void;
|
|
279
291
|
getHooks(): HookManager;
|
|
280
292
|
addGlobalScope(name: string, callback: (qb: QueryBuilder) => void): void;
|
|
281
293
|
removeGlobalScope(name: string): void;
|
|
@@ -358,12 +370,15 @@ declare class Attribute<T = any> {
|
|
|
358
370
|
}
|
|
359
371
|
//#endregion
|
|
360
372
|
//#region src/model/types.d.ts
|
|
361
|
-
|
|
373
|
+
/** Mapped column shape — resolves each column to its JS value type for $toJSON(). */
|
|
374
|
+
type SerializedShape<TColumns extends ColumnShape> = { [K in keyof TColumns]: TColumns[K] extends Column<infer T> ? T : unknown } & Record<string, unknown>;
|
|
375
|
+
interface ModelInstance<TColumns extends ColumnShape = ColumnShape> {
|
|
362
376
|
readonly exists: boolean;
|
|
363
377
|
readonly attributes: Record<string, unknown>;
|
|
364
378
|
readonly dirtyAttributes: Record<string, unknown>;
|
|
365
379
|
isDirty(key?: string): boolean;
|
|
366
|
-
get<
|
|
380
|
+
get<K extends keyof TColumns>(key: K): ColumnValue<TColumns[K]>;
|
|
381
|
+
get(key: string): unknown;
|
|
367
382
|
set(key: string, value: unknown): void;
|
|
368
383
|
fill(data: Record<string, unknown>): void;
|
|
369
384
|
reset(): void;
|
|
@@ -379,8 +394,8 @@ interface ModelInstance {
|
|
|
379
394
|
$restore(): Promise<void>;
|
|
380
395
|
$trashed(): boolean;
|
|
381
396
|
$reload(): Promise<void>;
|
|
382
|
-
$toJSON():
|
|
383
|
-
toJSON():
|
|
397
|
+
$toJSON(): SerializedShape<TColumns>;
|
|
398
|
+
toJSON(): SerializedShape<TColumns>;
|
|
384
399
|
}
|
|
385
400
|
interface ModelDefinition<TColumns extends ColumnShape = ColumnShape> {
|
|
386
401
|
readonly table: string;
|
|
@@ -388,21 +403,21 @@ interface ModelDefinition<TColumns extends ColumnShape = ColumnShape> {
|
|
|
388
403
|
readonly relations: Record<string, Relation>;
|
|
389
404
|
readonly name: string;
|
|
390
405
|
_orm: ORMLike | null;
|
|
391
|
-
query(): QueryBuilder
|
|
392
|
-
find(id: number | string): Promise<ModelInstance | undefined>;
|
|
393
|
-
findOrFail(id: number | string): Promise<ModelInstance
|
|
394
|
-
first(): Promise<ModelInstance | undefined>;
|
|
395
|
-
create(data: Record<string, unknown>): Promise<ModelInstance
|
|
396
|
-
insert(data: Record<string, unknown>): Promise<ModelInstance
|
|
397
|
-
insertMany(dataArray: Record<string, unknown>[]): Promise<ModelInstance[]>;
|
|
398
|
-
update(id: number | string, data: Record<string, unknown>): Promise<ModelInstance
|
|
406
|
+
query(): QueryBuilder<TColumns>;
|
|
407
|
+
find(id: number | string): Promise<ModelInstance<TColumns> | undefined>;
|
|
408
|
+
findOrFail(id: number | string): Promise<ModelInstance<TColumns>>;
|
|
409
|
+
first(): Promise<ModelInstance<TColumns> | undefined>;
|
|
410
|
+
create(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
|
|
411
|
+
insert(data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
|
|
412
|
+
insertMany(dataArray: Record<string, unknown>[]): Promise<ModelInstance<TColumns>[]>;
|
|
413
|
+
update(id: number | string, data: Record<string, unknown>): Promise<ModelInstance<TColumns>>;
|
|
399
414
|
delete(id: number | string): Promise<void>;
|
|
400
415
|
insertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: InsertGraphOptions): Promise<any>;
|
|
401
416
|
upsertGraph(data: Record<string, unknown> | Record<string, unknown>[], options?: UpsertGraphOptions): Promise<any>;
|
|
402
|
-
hydrate(row: Record<string, unknown>): ModelInstance
|
|
403
|
-
use(plugin: Plugin): ModelDefinition
|
|
417
|
+
hydrate(row: Record<string, unknown>): ModelInstance<TColumns>;
|
|
418
|
+
use(plugin: Plugin): ModelDefinition<TColumns>;
|
|
404
419
|
makeHelper<A extends any[], R>(fn: (qb: QueryBuilder, ...args: A) => R): (...args: A) => R;
|
|
405
|
-
on(event: string, callback: (model: ModelInstance) => void | Promise<void>): () => void;
|
|
420
|
+
on(event: string, callback: (model: ModelInstance<TColumns>) => void | Promise<void>): () => void;
|
|
406
421
|
getHooks(): HookManager;
|
|
407
422
|
beforeDelete(callback: StaticHookCallback): () => void;
|
|
408
423
|
afterDelete(callback: StaticHookCallback): () => void;
|
|
@@ -417,7 +432,6 @@ interface ModelDefinition<TColumns extends ColumnShape = ColumnShape> {
|
|
|
417
432
|
getGlobalScopes(): Map<string, (qb: QueryBuilder) => void> | undefined;
|
|
418
433
|
registerTimestamps?(createdAtCol?: string, updatedAtCol?: string): void;
|
|
419
434
|
registerSoftDeletes?(deletedAtCol?: string): void;
|
|
420
|
-
discover?(): Promise<never>;
|
|
421
435
|
_init(orm: ORMLike): void;
|
|
422
436
|
}
|
|
423
437
|
interface ModelConfig<TColumns extends ColumnShape = ColumnShape> {
|
|
@@ -433,46 +447,46 @@ interface ModelConfig<TColumns extends ColumnShape = ColumnShape> {
|
|
|
433
447
|
}
|
|
434
448
|
//#endregion
|
|
435
449
|
//#region src/collection/index.d.ts
|
|
436
|
-
interface Collection {
|
|
450
|
+
interface Collection<TColumns extends ColumnShape = ColumnShape> {
|
|
437
451
|
readonly length: number;
|
|
438
|
-
[Symbol.iterator](): Iterator<ModelInstance
|
|
439
|
-
at(index: number): ModelInstance | undefined;
|
|
440
|
-
first(): ModelInstance | undefined;
|
|
441
|
-
last(): ModelInstance | undefined;
|
|
442
|
-
all(): ModelInstance[];
|
|
443
|
-
findBy(id: number | string): ModelInstance | undefined;
|
|
444
|
-
find(callback: (item: ModelInstance
|
|
445
|
-
some(callback: (item: ModelInstance
|
|
446
|
-
includes(item: ModelInstance): boolean;
|
|
452
|
+
[Symbol.iterator](): Iterator<ModelInstance<TColumns>>;
|
|
453
|
+
at(index: number): ModelInstance<TColumns> | undefined;
|
|
454
|
+
first(): ModelInstance<TColumns> | undefined;
|
|
455
|
+
last(): ModelInstance<TColumns> | undefined;
|
|
456
|
+
all(): ModelInstance<TColumns>[];
|
|
457
|
+
findBy(id: number | string): ModelInstance<TColumns> | undefined;
|
|
458
|
+
find(callback: (item: ModelInstance<TColumns>, index: number) => boolean): ModelInstance<TColumns> | undefined;
|
|
459
|
+
some(callback: (item: ModelInstance<TColumns>, index: number) => boolean): boolean;
|
|
460
|
+
includes(item: ModelInstance<TColumns>): boolean;
|
|
447
461
|
isEmpty(): boolean;
|
|
448
462
|
isNotEmpty(): boolean;
|
|
449
463
|
get(key: string): unknown[];
|
|
450
464
|
pluck(key: string): unknown[];
|
|
451
|
-
groupBy(key: string): Record<string, ModelInstance[]>;
|
|
452
|
-
keyBy(key: string): Record<string, ModelInstance
|
|
453
|
-
map<T>(fn: (item: ModelInstance
|
|
454
|
-
filter(fn: (item: ModelInstance
|
|
455
|
-
reduce<T>(fn: (acc: T, item: ModelInstance
|
|
456
|
-
forEach(fn: (item: ModelInstance
|
|
457
|
-
each(fn: (item: ModelInstance
|
|
458
|
-
unique(key?: string): Collection
|
|
459
|
-
sortBy(key: string, direction?: "asc" | "desc"): Collection
|
|
460
|
-
shuffle(): Collection
|
|
461
|
-
take(n: number): Collection
|
|
462
|
-
skip(n: number): Collection
|
|
463
|
-
chunk(size: number): Collection[];
|
|
465
|
+
groupBy(key: string): Record<string, ModelInstance<TColumns>[]>;
|
|
466
|
+
keyBy(key: string): Record<string, ModelInstance<TColumns>>;
|
|
467
|
+
map<T>(fn: (item: ModelInstance<TColumns>, index: number) => T): T[];
|
|
468
|
+
filter(fn: (item: ModelInstance<TColumns>, index: number) => boolean): Collection<TColumns>;
|
|
469
|
+
reduce<T>(fn: (acc: T, item: ModelInstance<TColumns>, index: number) => T, initial: T): T;
|
|
470
|
+
forEach(fn: (item: ModelInstance<TColumns>, index: number) => void): void;
|
|
471
|
+
each(fn: (item: ModelInstance<TColumns>, index: number) => void): Collection<TColumns>;
|
|
472
|
+
unique(key?: string): Collection<TColumns>;
|
|
473
|
+
sortBy(key: string, direction?: "asc" | "desc"): Collection<TColumns>;
|
|
474
|
+
shuffle(): Collection<TColumns>;
|
|
475
|
+
take(n: number): Collection<TColumns>;
|
|
476
|
+
skip(n: number): Collection<TColumns>;
|
|
477
|
+
chunk(size: number): Collection<TColumns>[];
|
|
464
478
|
sum(key: string): number;
|
|
465
479
|
avg(key: string): number;
|
|
466
480
|
min(key: string): number;
|
|
467
481
|
max(key: string): number;
|
|
468
|
-
diff(other: Collection): Collection
|
|
469
|
-
intersect(other: Collection): Collection
|
|
470
|
-
concat(other: Collection): Collection
|
|
471
|
-
push(...items: ModelInstance[]): void;
|
|
472
|
-
load(...relations: string[]): Promise<Collection
|
|
473
|
-
toJSON():
|
|
474
|
-
}
|
|
475
|
-
declare function createCollection(items?: ModelInstance[]): Collection
|
|
482
|
+
diff(other: Collection<TColumns>): Collection<TColumns>;
|
|
483
|
+
intersect(other: Collection<TColumns>): Collection<TColumns>;
|
|
484
|
+
concat(other: Collection<TColumns>): Collection<TColumns>;
|
|
485
|
+
push(...items: ModelInstance<TColumns>[]): void;
|
|
486
|
+
load(...relations: string[]): Promise<Collection<TColumns>>;
|
|
487
|
+
toJSON(): SerializedShape<TColumns>[];
|
|
488
|
+
}
|
|
489
|
+
declare function createCollection<TColumns extends ColumnShape = ColumnShape>(items?: ModelInstance<TColumns>[]): Collection<TColumns>;
|
|
476
490
|
//#endregion
|
|
477
491
|
//#region src/columns/arktype.d.ts
|
|
478
492
|
declare function createArkTypeSchemaConfig(): SchemaConfig;
|
|
@@ -499,7 +513,27 @@ interface ColumnTypes {
|
|
|
499
513
|
updatedAt: Column<string>;
|
|
500
514
|
};
|
|
501
515
|
}
|
|
502
|
-
|
|
516
|
+
/**
|
|
517
|
+
* Pre-configured column type factory backed by ArkType validation.
|
|
518
|
+
*
|
|
519
|
+
* The most common usage — just import and use:
|
|
520
|
+
* ```ts
|
|
521
|
+
* import { t } from "peta-orm"
|
|
522
|
+
* const id = t.integer().primaryKey()
|
|
523
|
+
* ```
|
|
524
|
+
*
|
|
525
|
+
* For a custom validation backend, use `createColumnTypes({ schema })` instead.
|
|
526
|
+
*/
|
|
527
|
+
declare const t: ColumnTypes;
|
|
528
|
+
/**
|
|
529
|
+
* Create a column type factory with a custom validation schema backend.
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```ts
|
|
533
|
+
* const t = createColumnTypes({ schema: myCustomSchemaConfig })
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
declare function createColumnTypes(config: {
|
|
503
537
|
schema: SchemaConfig;
|
|
504
538
|
}): ColumnTypes;
|
|
505
539
|
//#endregion
|
|
@@ -536,17 +570,56 @@ declare class DatabaseError extends Error {
|
|
|
536
570
|
//#region src/errors/normalizer.d.ts
|
|
537
571
|
declare function normalizeError(e: unknown, table?: string): DatabaseError;
|
|
538
572
|
//#endregion
|
|
573
|
+
//#region src/init.d.ts
|
|
574
|
+
/**
|
|
575
|
+
* Create a lazy-initialized singleton factory.
|
|
576
|
+
*
|
|
577
|
+
* The factory function is called only once — on the first call to the returned
|
|
578
|
+
* function. Subsequent calls return the same resolved promise. This avoids
|
|
579
|
+
* module-level side effects: importing a model file won't trigger database
|
|
580
|
+
* connection or schema initialization until the first explicit `await db()`.
|
|
581
|
+
*
|
|
582
|
+
* @example
|
|
583
|
+
* ```ts
|
|
584
|
+
* import { createClient } from "@libsql/client"
|
|
585
|
+
* import { LibsqlDialect } from "@libsql/kysely-libsql"
|
|
586
|
+
* import { createDb, createORM, defineModel, t } from "peta-orm"
|
|
587
|
+
*
|
|
588
|
+
* const User = defineModel("users", { columns: { ... } })
|
|
589
|
+
*
|
|
590
|
+
* async function setup() {
|
|
591
|
+
* const client = createClient({ url: "file:my-app.db" })
|
|
592
|
+
* await client.execute("CREATE TABLE IF NOT EXISTS users (...)") // schema init
|
|
593
|
+
* const orm = createORM({ dialect: new LibsqlDialect({ client }) })
|
|
594
|
+
* orm.registerAll(User)
|
|
595
|
+
* return orm
|
|
596
|
+
* }
|
|
597
|
+
*
|
|
598
|
+
* export const db = createDb(setup)
|
|
599
|
+
* // Usage: const orm = await db()
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
declare function createDb<T>(factory: () => Promise<T>): () => Promise<T>;
|
|
603
|
+
//#endregion
|
|
539
604
|
//#region src/model/define.d.ts
|
|
540
605
|
declare function defineModel<TColumns extends ColumnShape>(table: string, config: ModelConfig<TColumns>): ModelDefinition<TColumns>;
|
|
541
606
|
//#endregion
|
|
542
607
|
//#region src/orm/index.d.ts
|
|
543
608
|
interface ORMConfig {
|
|
544
|
-
dialect
|
|
609
|
+
/** Kysely dialect to create an internal Kysely instance. Required unless `kysely` is provided. */
|
|
610
|
+
dialect?: Dialect;
|
|
611
|
+
/** A pre-existing Kysely instance to reuse. Required unless `dialect` is provided. */
|
|
612
|
+
kysely?: Kysely<any>;
|
|
613
|
+
/** Optional map of model definitions to register immediately. */
|
|
545
614
|
models?: Record<string, ModelDefinition>;
|
|
546
615
|
}
|
|
547
616
|
/**
|
|
548
617
|
* Create an ORM instance — the central registry that wires Kysely to model definitions.
|
|
549
618
|
* Replaces createPeta() from v0.x.
|
|
619
|
+
*
|
|
620
|
+
* Pass either `dialect` (to auto-create a Kysely instance) or `kysely` (to reuse one).
|
|
621
|
+
* Passing a pre-existing Kysely instance avoids creating a second connection for
|
|
622
|
+
* migration runners or other tools that already have their own Kysely.
|
|
550
623
|
*/
|
|
551
624
|
declare function createORM(config: ORMConfig): ORMLike & {
|
|
552
625
|
kysely: Database;
|
|
@@ -688,4 +761,26 @@ declare function defineMorphMany(options: MorphManyOptions): Relation;
|
|
|
688
761
|
*/
|
|
689
762
|
declare function defineMorphOne(options: MorphOneOptions): Relation;
|
|
690
763
|
//#endregion
|
|
691
|
-
|
|
764
|
+
//#region src/repo/index.d.ts
|
|
765
|
+
type QueryMethod = (qb: QueryBuilder, ...args: any[]) => QueryBuilder;
|
|
766
|
+
interface RepoMethods {
|
|
767
|
+
queryMethods?: Record<string, QueryMethod>;
|
|
768
|
+
methods?: Record<string, (...args: any[]) => any>;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Create a repository — a composable set of chainable query methods.
|
|
772
|
+
*
|
|
773
|
+
* ```ts
|
|
774
|
+
* const userRepo = createRepo(User, {
|
|
775
|
+
* queryMethods: {
|
|
776
|
+
* search(q, query: string) {
|
|
777
|
+
* return q.where('name', 'like', `%${query}%`)
|
|
778
|
+
* },
|
|
779
|
+
* },
|
|
780
|
+
* })
|
|
781
|
+
* const users = await userRepo.search('john').paginate(1, 20)
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
declare function createRepo<TMethods extends RepoMethods>(model: ModelDefinition, methods: TMethods): Record<string, never>;
|
|
785
|
+
//#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 };
|
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { a as getRawRelations, d as setExists, o as getState } from "./state-Ltl
|
|
|
7
7
|
import { a as setConfig$1, n as reloadModel, r as saveModel, t as getConfig$1 } from "./save-D5UKXvqC.mjs";
|
|
8
8
|
import { type } from "arktype";
|
|
9
9
|
import { Kysely, sql } from "kysely";
|
|
10
|
+
import { pathToFileURL } from "url";
|
|
10
11
|
import { ulid as ulid$1 } from "ulid";
|
|
11
12
|
//#region src/columns/arktype.ts
|
|
12
13
|
function createArkTypeSchemaConfig() {
|
|
@@ -189,7 +190,27 @@ function createColumn(schema, dataType, args = [], constraints = []) {
|
|
|
189
190
|
}
|
|
190
191
|
//#endregion
|
|
191
192
|
//#region src/columns/types.ts
|
|
192
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Pre-configured column type factory backed by ArkType validation.
|
|
195
|
+
*
|
|
196
|
+
* The most common usage — just import and use:
|
|
197
|
+
* ```ts
|
|
198
|
+
* import { t } from "peta-orm"
|
|
199
|
+
* const id = t.integer().primaryKey()
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* For a custom validation backend, use `createColumnTypes({ schema })` instead.
|
|
203
|
+
*/
|
|
204
|
+
const t = createColumnTypes({ schema: createArkTypeSchemaConfig() });
|
|
205
|
+
/**
|
|
206
|
+
* Create a column type factory with a custom validation schema backend.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* const t = createColumnTypes({ schema: myCustomSchemaConfig })
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
function createColumnTypes(config) {
|
|
193
214
|
const schema = config.schema;
|
|
194
215
|
function col(dataType, args) {
|
|
195
216
|
return createColumn(schema, dataType, args);
|
|
@@ -217,6 +238,43 @@ function t(config) {
|
|
|
217
238
|
};
|
|
218
239
|
}
|
|
219
240
|
//#endregion
|
|
241
|
+
//#region src/init.ts
|
|
242
|
+
/**
|
|
243
|
+
* Create a lazy-initialized singleton factory.
|
|
244
|
+
*
|
|
245
|
+
* The factory function is called only once — on the first call to the returned
|
|
246
|
+
* function. Subsequent calls return the same resolved promise. This avoids
|
|
247
|
+
* module-level side effects: importing a model file won't trigger database
|
|
248
|
+
* connection or schema initialization until the first explicit `await db()`.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```ts
|
|
252
|
+
* import { createClient } from "@libsql/client"
|
|
253
|
+
* import { LibsqlDialect } from "@libsql/kysely-libsql"
|
|
254
|
+
* import { createDb, createORM, defineModel, t } from "peta-orm"
|
|
255
|
+
*
|
|
256
|
+
* const User = defineModel("users", { columns: { ... } })
|
|
257
|
+
*
|
|
258
|
+
* async function setup() {
|
|
259
|
+
* const client = createClient({ url: "file:my-app.db" })
|
|
260
|
+
* await client.execute("CREATE TABLE IF NOT EXISTS users (...)") // schema init
|
|
261
|
+
* const orm = createORM({ dialect: new LibsqlDialect({ client }) })
|
|
262
|
+
* orm.registerAll(User)
|
|
263
|
+
* return orm
|
|
264
|
+
* }
|
|
265
|
+
*
|
|
266
|
+
* export const db = createDb(setup)
|
|
267
|
+
* // Usage: const orm = await db()
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
function createDb(factory) {
|
|
271
|
+
let promise = null;
|
|
272
|
+
return () => {
|
|
273
|
+
if (!promise) promise = factory();
|
|
274
|
+
return promise;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
220
278
|
//#region src/hooks/static.ts
|
|
221
279
|
var static_exports = /* @__PURE__ */ __exportAll({
|
|
222
280
|
addStaticHook: () => addStaticHook,
|
|
@@ -1483,9 +1541,6 @@ function defineModel(table, config) {
|
|
|
1483
1541
|
def.registerSoftDeletes = (deletedAtCol) => {
|
|
1484
1542
|
registerSoftDeletesFor(def, deletedAtCol);
|
|
1485
1543
|
};
|
|
1486
|
-
def.discover = async () => {
|
|
1487
|
-
throw new Error("discover() not yet implemented in v2");
|
|
1488
|
-
};
|
|
1489
1544
|
return def;
|
|
1490
1545
|
}
|
|
1491
1546
|
//#endregion
|
|
@@ -1895,9 +1950,14 @@ initRuntime({
|
|
|
1895
1950
|
/**
|
|
1896
1951
|
* Create an ORM instance — the central registry that wires Kysely to model definitions.
|
|
1897
1952
|
* Replaces createPeta() from v0.x.
|
|
1953
|
+
*
|
|
1954
|
+
* Pass either `dialect` (to auto-create a Kysely instance) or `kysely` (to reuse one).
|
|
1955
|
+
* Passing a pre-existing Kysely instance avoids creating a second connection for
|
|
1956
|
+
* migration runners or other tools that already have their own Kysely.
|
|
1898
1957
|
*/
|
|
1899
1958
|
function createORM(config) {
|
|
1900
|
-
|
|
1959
|
+
if (!config.dialect && !config.kysely) throw new Error("createORM: provide either `dialect` (to create a Kysely instance) or `kysely` (to reuse one)");
|
|
1960
|
+
const kysely = config.kysely ?? new Kysely({ dialect: config.dialect });
|
|
1901
1961
|
const modelMap = /* @__PURE__ */ new Map();
|
|
1902
1962
|
const orm = {
|
|
1903
1963
|
kysely,
|
|
@@ -1923,6 +1983,26 @@ function createORM(config) {
|
|
|
1923
1983
|
},
|
|
1924
1984
|
getModel(name) {
|
|
1925
1985
|
return modelMap.get(name);
|
|
1986
|
+
},
|
|
1987
|
+
async discover(pattern) {
|
|
1988
|
+
const entries = await (await import("fast-glob")).glob(pattern, {
|
|
1989
|
+
absolute: true,
|
|
1990
|
+
onlyFiles: true
|
|
1991
|
+
});
|
|
1992
|
+
if (entries.length === 0) throw new Error(`discover: no files matched pattern "${pattern}"`);
|
|
1993
|
+
const models = [];
|
|
1994
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1995
|
+
for (const fp of entries) {
|
|
1996
|
+
const mod = await import(pathToFileURL(fp).href);
|
|
1997
|
+
for (const val of Object.values(mod)) if (val && typeof val === "object" && "columns" in val && "table" in val && typeof val.table === "string" && val.table.length > 0) {
|
|
1998
|
+
const def = val;
|
|
1999
|
+
if (!seen.has(def.table)) {
|
|
2000
|
+
seen.add(def.table);
|
|
2001
|
+
models.push(def);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
return models;
|
|
1926
2006
|
}
|
|
1927
2007
|
};
|
|
1928
2008
|
if (config.models) for (const [_name, model] of Object.entries(config.models)) orm.register(model);
|
|
@@ -2601,4 +2681,65 @@ function defName(instance) {
|
|
|
2601
2681
|
return instance.constructor?.name ?? "model";
|
|
2602
2682
|
}
|
|
2603
2683
|
//#endregion
|
|
2604
|
-
|
|
2684
|
+
//#region src/repo/index.ts
|
|
2685
|
+
/**
|
|
2686
|
+
* Create a repository — a composable set of chainable query methods.
|
|
2687
|
+
*
|
|
2688
|
+
* ```ts
|
|
2689
|
+
* const userRepo = createRepo(User, {
|
|
2690
|
+
* queryMethods: {
|
|
2691
|
+
* search(q, query: string) {
|
|
2692
|
+
* return q.where('name', 'like', `%${query}%`)
|
|
2693
|
+
* },
|
|
2694
|
+
* },
|
|
2695
|
+
* })
|
|
2696
|
+
* const users = await userRepo.search('john').paginate(1, 20)
|
|
2697
|
+
* ```
|
|
2698
|
+
*/
|
|
2699
|
+
function createRepo(model, methods) {
|
|
2700
|
+
const customMethods = /* @__PURE__ */ new Map();
|
|
2701
|
+
if (methods.queryMethods) for (const [name, fn] of Object.entries(methods.queryMethods)) customMethods.set(name, fn);
|
|
2702
|
+
/**
|
|
2703
|
+
* Wrap a QueryBuilder so custom methods are available for chaining.
|
|
2704
|
+
* Custom methods operate on the CURRENT QB (not a fresh one), so
|
|
2705
|
+
* chaining carries forward previous conditions.
|
|
2706
|
+
*/
|
|
2707
|
+
function wrapQB(qb) {
|
|
2708
|
+
return new Proxy(qb, { get(target, prop) {
|
|
2709
|
+
if (typeof prop === "string") {
|
|
2710
|
+
if (customMethods.has(prop)) {
|
|
2711
|
+
const fn = customMethods.get(prop);
|
|
2712
|
+
return (...args) => {
|
|
2713
|
+
return wrapQB(fn(target, ...args));
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
if (methods.methods?.[prop]) return (...args) => methods.methods[prop](...args);
|
|
2717
|
+
}
|
|
2718
|
+
const val = target[prop];
|
|
2719
|
+
if (typeof val === "function") return function(...args) {
|
|
2720
|
+
const result = val.apply(target, args);
|
|
2721
|
+
return result === target ? wrapQB(result) : result;
|
|
2722
|
+
};
|
|
2723
|
+
return val;
|
|
2724
|
+
} });
|
|
2725
|
+
}
|
|
2726
|
+
return new Proxy({}, { get(_target, prop) {
|
|
2727
|
+
if (typeof prop === "string") {
|
|
2728
|
+
if (customMethods.has(prop)) {
|
|
2729
|
+
const fn = customMethods.get(prop);
|
|
2730
|
+
return (...args) => {
|
|
2731
|
+
return wrapQB(fn(model.query(), ...args));
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
if (methods.methods?.[prop]) return (...args) => methods.methods[prop](...args);
|
|
2735
|
+
}
|
|
2736
|
+
const qb = model.query();
|
|
2737
|
+
const val = qb[prop];
|
|
2738
|
+
if (typeof val === "function") return (...args) => {
|
|
2739
|
+
return wrapQB(val.apply(qb, args));
|
|
2740
|
+
};
|
|
2741
|
+
return val;
|
|
2742
|
+
} });
|
|
2743
|
+
}
|
|
2744
|
+
//#endregion
|
|
2745
|
+
export { Attribute, DatabaseError, ModelNotFoundError, ModelNotRegisteredError, RelationNotAllowedError, RelationNotFoundError, ValidationError, belongsTo, createArkTypeSchemaConfig, createCollection, createColumn, createColumnTypes, createDb, createHookManager, createORM, createORM as createPeta, createPaginator, createQueryBuilder, createRepo, defineModel, defineMorphMany, defineMorphOne, defineMorphTo, hasMany, hasManyThrough, hasOne, eager_exports as i, manyToMany, relation_exports as n, normalizeError, delete_exports as r, resolveMorphRelation, softDeletes, t, timestamps, ulid };
|
package/package.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"name": "peta-orm",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.5.0",
|
|
6
6
|
"description": "ORM for Bun, built on Kysely",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/zfadhli/peta-stack.git"
|
|
11
11
|
},
|
|
12
|
-
"module": "
|
|
12
|
+
"module": "./dist/index.mjs",
|
|
13
13
|
"main": "./dist/index.mjs",
|
|
14
14
|
"types": "./dist/index.d.mts",
|
|
15
15
|
"exports": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"arktype": "^2.2.0",
|
|
28
28
|
"cac": "^7.0.0",
|
|
29
|
+
"fast-glob": "^3.3.3",
|
|
29
30
|
"ora": "^9.4.0",
|
|
30
31
|
"ulid": "^3.0.2"
|
|
31
32
|
},
|
|
@@ -55,6 +56,6 @@
|
|
|
55
56
|
"build": "tsdown",
|
|
56
57
|
"lint": "bunx biome check src test examples",
|
|
57
58
|
"lint:fix": "bunx biome check --write --unsafe src test examples",
|
|
58
|
-
"
|
|
59
|
+
"prepublishOnly": "bun run build"
|
|
59
60
|
}
|
|
60
61
|
}
|