@xacos/orm 1.0.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/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/XModel.d.ts +42 -0
- package/dist/XModel.d.ts.map +1 -0
- package/dist/XModel.js +240 -0
- package/dist/XModel.js.map +1 -0
- package/dist/XModel.test.d.ts +2 -0
- package/dist/XModel.test.d.ts.map +1 -0
- package/dist/XModel.test.js +119 -0
- package/dist/XModel.test.js.map +1 -0
- package/dist/XMongoModel.d.ts +25 -0
- package/dist/XMongoModel.d.ts.map +1 -0
- package/dist/XMongoModel.js +86 -0
- package/dist/XMongoModel.js.map +1 -0
- package/dist/XMongoModel.test.d.ts +2 -0
- package/dist/XMongoModel.test.d.ts.map +1 -0
- package/dist/XMongoModel.test.js +14 -0
- package/dist/XMongoModel.test.js.map +1 -0
- package/dist/__tests__/sqlite.integration.test.d.ts +2 -0
- package/dist/__tests__/sqlite.integration.test.d.ts.map +1 -0
- package/dist/__tests__/sqlite.integration.test.js +106 -0
- package/dist/__tests__/sqlite.integration.test.js.map +1 -0
- package/dist/connection/db.d.ts +25 -0
- package/dist/connection/db.d.ts.map +1 -0
- package/dist/connection/db.js +100 -0
- package/dist/connection/db.js.map +1 -0
- package/dist/connection/drivers.d.ts +3 -0
- package/dist/connection/drivers.d.ts.map +1 -0
- package/dist/connection/drivers.js +58 -0
- package/dist/connection/drivers.js.map +1 -0
- package/dist/connection/mongoUri.d.ts +6 -0
- package/dist/connection/mongoUri.d.ts.map +1 -0
- package/dist/connection/mongoUri.js +22 -0
- package/dist/connection/mongoUri.js.map +1 -0
- package/dist/decorators.d.ts +47 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +149 -0
- package/dist/decorators.js.map +1 -0
- package/dist/factories/Factory.d.ts +34 -0
- package/dist/factories/Factory.d.ts.map +1 -0
- package/dist/factories/Factory.js +48 -0
- package/dist/factories/Factory.js.map +1 -0
- package/dist/factories/Factory.test.d.ts +2 -0
- package/dist/factories/Factory.test.d.ts.map +1 -0
- package/dist/factories/Factory.test.js +16 -0
- package/dist/factories/Factory.test.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/Migration.d.ts +6 -0
- package/dist/migrations/Migration.d.ts.map +1 -0
- package/dist/migrations/Migration.js +3 -0
- package/dist/migrations/Migration.js.map +1 -0
- package/dist/migrations/MigrationRunner.d.ts +14 -0
- package/dist/migrations/MigrationRunner.d.ts.map +1 -0
- package/dist/migrations/MigrationRunner.js +142 -0
- package/dist/migrations/MigrationRunner.js.map +1 -0
- package/dist/migrations/MigrationRunner.test.d.ts +2 -0
- package/dist/migrations/MigrationRunner.test.d.ts.map +1 -0
- package/dist/migrations/MigrationRunner.test.js +26 -0
- package/dist/migrations/MigrationRunner.test.js.map +1 -0
- package/dist/migrations/Schema.d.ts +7 -0
- package/dist/migrations/Schema.d.ts.map +1 -0
- package/dist/migrations/Schema.js +17 -0
- package/dist/migrations/Schema.js.map +1 -0
- package/dist/migrations/columnHelpers.d.ts +9 -0
- package/dist/migrations/columnHelpers.d.ts.map +1 -0
- package/dist/migrations/columnHelpers.js +15 -0
- package/dist/migrations/columnHelpers.js.map +1 -0
- package/dist/seeders/Seeder.d.ts +8 -0
- package/dist/seeders/Seeder.d.ts.map +1 -0
- package/dist/seeders/Seeder.js +3 -0
- package/dist/seeders/Seeder.js.map +1 -0
- package/dist/seeders/SeederRunner.d.ts +20 -0
- package/dist/seeders/SeederRunner.d.ts.map +1 -0
- package/dist/seeders/SeederRunner.js +68 -0
- package/dist/seeders/SeederRunner.js.map +1 -0
- package/dist/seeders/SeederRunner.test.d.ts +2 -0
- package/dist/seeders/SeederRunner.test.d.ts.map +1 -0
- package/dist/seeders/SeederRunner.test.js +44 -0
- package/dist/seeders/SeederRunner.test.js.map +1 -0
- package/dist/utils/paginate.d.ts +14 -0
- package/dist/utils/paginate.d.ts.map +1 -0
- package/dist/utils/paginate.js +28 -0
- package/dist/utils/paginate.js.map +1 -0
- package/dist/utils/paginate.test.d.ts +2 -0
- package/dist/utils/paginate.test.d.ts.map +1 -0
- package/dist/utils/paginate.test.js +23 -0
- package/dist/utils/paginate.test.js.map +1 -0
- package/package.json +75 -0
- package/src/XModel.test.ts +147 -0
- package/src/XModel.ts +301 -0
- package/src/XMongoModel.test.ts +16 -0
- package/src/XMongoModel.ts +119 -0
- package/src/__tests__/sqlite.integration.test.ts +116 -0
- package/src/connection/db.ts +127 -0
- package/src/connection/drivers.ts +65 -0
- package/src/connection/mongoUri.ts +25 -0
- package/src/decorators.ts +200 -0
- package/src/factories/Factory.test.ts +18 -0
- package/src/factories/Factory.ts +61 -0
- package/src/index.ts +18 -0
- package/src/migrations/Migration.ts +8 -0
- package/src/migrations/MigrationRunner.test.ts +33 -0
- package/src/migrations/MigrationRunner.ts +171 -0
- package/src/migrations/Schema.ts +20 -0
- package/src/migrations/columnHelpers.ts +28 -0
- package/src/seeders/Seeder.ts +8 -0
- package/src/seeders/SeederRunner.test.ts +62 -0
- package/src/seeders/SeederRunner.ts +76 -0
- package/src/types/bun-test.d.ts +8 -0
- package/src/utils/paginate.test.ts +24 -0
- package/src/utils/paginate.ts +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 XAOCS Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @xacos/orm
|
|
2
|
+
|
|
3
|
+
> Part of the [XAOCS Framework](https://xaocs.dev) — full-stack TypeScript, built for AI-native development.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @xacos/orm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Docs
|
|
12
|
+
|
|
13
|
+
Full documentation at [xaocs.dev/docs/orm](https://xaocs.dev/docs/orm).
|
|
14
|
+
|
|
15
|
+
## License
|
|
16
|
+
|
|
17
|
+
MIT © XAOCS Team
|
package/dist/XModel.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Knex } from "knex";
|
|
2
|
+
import type { PaginatedResult } from "@xacos/shared";
|
|
3
|
+
export declare class RelationScope {
|
|
4
|
+
private readonly Model;
|
|
5
|
+
private readonly relations;
|
|
6
|
+
private base;
|
|
7
|
+
constructor(Model: typeof XModel, relations: readonly string[], base: Knex.QueryBuilder);
|
|
8
|
+
where(column: string, value: unknown): this;
|
|
9
|
+
whereIn(column: string, values: readonly unknown[]): this;
|
|
10
|
+
orderBy(column: string, direction?: "asc" | "desc"): this;
|
|
11
|
+
limit(value: number): this;
|
|
12
|
+
offset(value: number): this;
|
|
13
|
+
first(): Promise<Record<string, unknown> | null>;
|
|
14
|
+
get(): Promise<Record<string, unknown>[]>;
|
|
15
|
+
}
|
|
16
|
+
export declare abstract class XModel {
|
|
17
|
+
static table: string;
|
|
18
|
+
static softDeletes: boolean;
|
|
19
|
+
static query(): Knex.QueryBuilder;
|
|
20
|
+
static withTrashed(): Knex.QueryBuilder;
|
|
21
|
+
/**
|
|
22
|
+
* @security WARNING: Never pass user input directly to raw().
|
|
23
|
+
* Always use parameterized bindings: db.raw('WHERE id = ?', [userInput])
|
|
24
|
+
*/
|
|
25
|
+
static raw(sql: string, bindings?: readonly Knex.RawBinding[]): Knex.Raw;
|
|
26
|
+
static with(...relations: string[]): RelationScope;
|
|
27
|
+
static hydrateRelations(rows: Record<string, unknown>[], relations: readonly string[]): Promise<void>;
|
|
28
|
+
static find(id: number | string): Promise<Record<string, unknown> | null>;
|
|
29
|
+
static findOrFail(id: number | string): Promise<Record<string, unknown>>;
|
|
30
|
+
static all(): Promise<Record<string, unknown>[]>;
|
|
31
|
+
static where(column: string, value: unknown): Knex.QueryBuilder;
|
|
32
|
+
static where(criteria: Record<string, unknown>): Knex.QueryBuilder;
|
|
33
|
+
static create(data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
34
|
+
static update(id: number | string, data: Record<string, unknown>): Promise<void>;
|
|
35
|
+
static destroy(id: number | string): Promise<void>;
|
|
36
|
+
static paginate(page?: number, perPage?: number): Promise<PaginatedResult<Record<string, unknown>>>;
|
|
37
|
+
toJSON(): Record<string, unknown>;
|
|
38
|
+
save(): Promise<void>;
|
|
39
|
+
delete(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export default XModel;
|
|
42
|
+
//# sourceMappingURL=XModel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XModel.d.ts","sourceRoot":"","sources":["../src/XModel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAgErD,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,IAAI,CAAoB;gBAGb,KAAK,EAAE,OAAO,MAAM,EACpB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC7C,IAAI,EAAE,IAAI,CAAC,YAAY;IAKzB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAK3C,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAKzD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,KAAK,GAAG,MAAc,GAAG,IAAI;IAKhE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKrB,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAQhD,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAKhD;AAED,8BAAsB,MAAM;IAC1B,MAAM,CAAC,KAAK,SAAM;IAClB,MAAM,CAAC,WAAW,UAAS;IAE3B,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;IAcjC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,YAAY;IAQvC;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG;IAIxE,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,GAAG,aAAa;WAKrC,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;WAe9F,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;WAKlE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;WAUjE,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAKtD,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC,YAAY;IAC/D,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,YAAY;WASrD,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;WAmBvE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;WAKzE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAa3C,QAAQ,CAAC,IAAI,SAAI,EAAE,OAAO,SAAK,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAsBhG,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAe3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAW9B;AAED,eAAe,MAAM,CAAC"}
|
package/dist/XModel.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { getColumnOptions, getRelations } from "./decorators";
|
|
2
|
+
import { getKnex } from "./connection/db";
|
|
3
|
+
async function resolveInsertedId(knex, table, insertResult, payload) {
|
|
4
|
+
if (Array.isArray(insertResult)) {
|
|
5
|
+
const first = insertResult[0];
|
|
6
|
+
if (typeof first === "number" || typeof first === "string") {
|
|
7
|
+
return first;
|
|
8
|
+
}
|
|
9
|
+
if (first && typeof first === "object" && "id" in first) {
|
|
10
|
+
return first.id;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
if (typeof insertResult === "number" || typeof insertResult === "string") {
|
|
14
|
+
return insertResult;
|
|
15
|
+
}
|
|
16
|
+
if (payload.id != null) {
|
|
17
|
+
return payload.id;
|
|
18
|
+
}
|
|
19
|
+
const row = await knex(table).select("id").orderBy("id", "desc").first();
|
|
20
|
+
if (!row) {
|
|
21
|
+
throw new Error("[XAOCS ORM] Unable to resolve inserted primary key");
|
|
22
|
+
}
|
|
23
|
+
return row.id;
|
|
24
|
+
}
|
|
25
|
+
async function attachHasMany(rows, propertyKey, Related, foreignKey) {
|
|
26
|
+
if (rows.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
const ids = rows
|
|
29
|
+
.map((row) => row.id)
|
|
30
|
+
.filter((id) => id !== undefined && id !== null);
|
|
31
|
+
if (ids.length === 0)
|
|
32
|
+
return;
|
|
33
|
+
const relatedModel = Related;
|
|
34
|
+
const relatedRows = (await relatedModel.query().whereIn(foreignKey, ids).select());
|
|
35
|
+
const grouped = new Map();
|
|
36
|
+
for (const rel of relatedRows) {
|
|
37
|
+
const fk = rel[foreignKey];
|
|
38
|
+
const bucket = grouped.get(fk) ?? [];
|
|
39
|
+
bucket.push(rel);
|
|
40
|
+
grouped.set(fk, bucket);
|
|
41
|
+
}
|
|
42
|
+
for (const row of rows) {
|
|
43
|
+
const id = row.id;
|
|
44
|
+
row[propertyKey] = grouped.get(id) ?? [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class RelationScope {
|
|
48
|
+
constructor(Model, relations, base) {
|
|
49
|
+
this.Model = Model;
|
|
50
|
+
this.relations = relations;
|
|
51
|
+
this.base = base;
|
|
52
|
+
}
|
|
53
|
+
where(column, value) {
|
|
54
|
+
this.base = this.base.clone().where(column, value);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
whereIn(column, values) {
|
|
58
|
+
this.base = this.base.clone().whereIn(column, values);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
orderBy(column, direction = "asc") {
|
|
62
|
+
this.base = this.base.clone().orderBy(column, direction);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
limit(value) {
|
|
66
|
+
this.base = this.base.clone().limit(value);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
offset(value) {
|
|
70
|
+
this.base = this.base.clone().offset(value);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
async first() {
|
|
74
|
+
const row = (await this.base.clone().first());
|
|
75
|
+
if (!row)
|
|
76
|
+
return null;
|
|
77
|
+
const rows = [row];
|
|
78
|
+
await this.Model.hydrateRelations(rows, this.relations);
|
|
79
|
+
return rows[0] ?? null;
|
|
80
|
+
}
|
|
81
|
+
async get() {
|
|
82
|
+
const rows = (await this.base.clone().select());
|
|
83
|
+
await this.Model.hydrateRelations(rows, this.relations);
|
|
84
|
+
return rows;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export class XModel {
|
|
88
|
+
static { this.table = ""; }
|
|
89
|
+
static { this.softDeletes = false; }
|
|
90
|
+
static query() {
|
|
91
|
+
const knex = getKnex();
|
|
92
|
+
const Model = this;
|
|
93
|
+
if (!Model.table) {
|
|
94
|
+
throw new Error("[XAOCS ORM] static table must be set on the model");
|
|
95
|
+
}
|
|
96
|
+
let builder = knex(Model.table);
|
|
97
|
+
if (Model.softDeletes) {
|
|
98
|
+
builder = builder.whereNull("deleted_at");
|
|
99
|
+
}
|
|
100
|
+
return builder;
|
|
101
|
+
}
|
|
102
|
+
static withTrashed() {
|
|
103
|
+
const Model = this;
|
|
104
|
+
if (!Model.table) {
|
|
105
|
+
throw new Error("[XAOCS ORM] static table must be set on the model");
|
|
106
|
+
}
|
|
107
|
+
return getKnex()(Model.table);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @security WARNING: Never pass user input directly to raw().
|
|
111
|
+
* Always use parameterized bindings: db.raw('WHERE id = ?', [userInput])
|
|
112
|
+
*/
|
|
113
|
+
static raw(sql, bindings) {
|
|
114
|
+
return getKnex().raw(sql, bindings ?? []);
|
|
115
|
+
}
|
|
116
|
+
static with(...relations) {
|
|
117
|
+
const Model = this;
|
|
118
|
+
return new RelationScope(Model, relations, Model.query());
|
|
119
|
+
}
|
|
120
|
+
static async hydrateRelations(rows, relations) {
|
|
121
|
+
const ctor = this;
|
|
122
|
+
const metas = getRelations(ctor);
|
|
123
|
+
for (const relationName of relations) {
|
|
124
|
+
const meta = metas.find((m) => m.propertyKey === relationName);
|
|
125
|
+
if (!meta)
|
|
126
|
+
continue;
|
|
127
|
+
if (meta.kind === "hasMany") {
|
|
128
|
+
const Related = meta.related();
|
|
129
|
+
await attachHasMany(rows, meta.propertyKey, Related, meta.foreignKey);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
static async find(id) {
|
|
134
|
+
const row = await this.query().where("id", id).first();
|
|
135
|
+
return row ? row : null;
|
|
136
|
+
}
|
|
137
|
+
static async findOrFail(id) {
|
|
138
|
+
const row = await this.find(id);
|
|
139
|
+
if (!row) {
|
|
140
|
+
const err = new Error("Not found");
|
|
141
|
+
err.statusCode = 404;
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
return row;
|
|
145
|
+
}
|
|
146
|
+
static async all() {
|
|
147
|
+
const rows = await this.query().select();
|
|
148
|
+
return rows;
|
|
149
|
+
}
|
|
150
|
+
static where(column, value) {
|
|
151
|
+
const Model = this;
|
|
152
|
+
if (typeof column === "string") {
|
|
153
|
+
return Model.query().where(column, value);
|
|
154
|
+
}
|
|
155
|
+
return Model.query().where(column);
|
|
156
|
+
}
|
|
157
|
+
static async create(data) {
|
|
158
|
+
const Model = this;
|
|
159
|
+
const knex = getKnex();
|
|
160
|
+
const now = new Date();
|
|
161
|
+
const payload = {
|
|
162
|
+
...data,
|
|
163
|
+
created_at: data.created_at ?? now,
|
|
164
|
+
updated_at: data.updated_at ?? now,
|
|
165
|
+
};
|
|
166
|
+
const insertResult = await knex(Model.table).insert(payload);
|
|
167
|
+
const id = await resolveInsertedId(knex, Model.table, insertResult, payload);
|
|
168
|
+
const created = await Model.find(id);
|
|
169
|
+
if (!created) {
|
|
170
|
+
throw new Error("[XAOCS ORM] Row missing after insert");
|
|
171
|
+
}
|
|
172
|
+
return created;
|
|
173
|
+
}
|
|
174
|
+
static async update(id, data) {
|
|
175
|
+
const Model = this;
|
|
176
|
+
await Model.withTrashed().where("id", id).update({ ...data, updated_at: new Date() });
|
|
177
|
+
}
|
|
178
|
+
static async destroy(id) {
|
|
179
|
+
const Model = this;
|
|
180
|
+
const existing = await Model.withTrashed().where("id", id).first();
|
|
181
|
+
if (!existing)
|
|
182
|
+
return;
|
|
183
|
+
if (Model.softDeletes) {
|
|
184
|
+
await Model.withTrashed().where("id", id).update({ deleted_at: new Date(), updated_at: new Date() });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await Model.withTrashed().where("id", id).del();
|
|
188
|
+
}
|
|
189
|
+
static async paginate(page = 1, perPage = 15) {
|
|
190
|
+
const Model = this;
|
|
191
|
+
const offset = (page - 1) * perPage;
|
|
192
|
+
const countRows = (await Model.query()
|
|
193
|
+
.clone()
|
|
194
|
+
.clearOrder()
|
|
195
|
+
.count("* as count"));
|
|
196
|
+
const total = Number(countRows[0]?.count ?? 0);
|
|
197
|
+
const data = (await Model.query().clone().offset(offset).limit(perPage).select());
|
|
198
|
+
const lastPage = Math.max(1, Math.ceil(total / perPage));
|
|
199
|
+
return {
|
|
200
|
+
data,
|
|
201
|
+
total,
|
|
202
|
+
page,
|
|
203
|
+
perPage,
|
|
204
|
+
lastPage,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
toJSON() {
|
|
208
|
+
const ctor = this.constructor;
|
|
209
|
+
const source = this;
|
|
210
|
+
const result = { ...source };
|
|
211
|
+
for (const key of Object.keys(result)) {
|
|
212
|
+
const meta = getColumnOptions(ctor, key);
|
|
213
|
+
if (meta?.hidden) {
|
|
214
|
+
delete result[key];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
async save() {
|
|
220
|
+
const Model = this.constructor;
|
|
221
|
+
const row = this;
|
|
222
|
+
const id = row.id;
|
|
223
|
+
if (id === undefined || id === null) {
|
|
224
|
+
throw new Error("[XAOCS ORM] save() requires an id — use Model.create() for inserts");
|
|
225
|
+
}
|
|
226
|
+
const { id: _ignored, ...rest } = row;
|
|
227
|
+
await getKnex()(Model.table).where("id", id).update({ ...rest, updated_at: new Date() });
|
|
228
|
+
}
|
|
229
|
+
async delete() {
|
|
230
|
+
const Model = this.constructor;
|
|
231
|
+
const id = this.id;
|
|
232
|
+
if (Model.softDeletes) {
|
|
233
|
+
await Model.withTrashed().where("id", id).update({ deleted_at: new Date(), updated_at: new Date() });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
await Model.withTrashed().where("id", id).del();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
export default XModel;
|
|
240
|
+
//# sourceMappingURL=XModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XModel.js","sourceRoot":"","sources":["../src/XModel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XModel.test.d.ts","sourceRoot":"","sources":["../src/XModel.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
2
|
+
import { registerColumnMeta, registerRelation } from "./decorators";
|
|
3
|
+
import { closeDb, getKnex, initDb } from "./index";
|
|
4
|
+
import { XModel } from "./XModel";
|
|
5
|
+
class Post extends XModel {
|
|
6
|
+
static { this.table = "posts"; }
|
|
7
|
+
}
|
|
8
|
+
class User extends XModel {
|
|
9
|
+
static { this.table = "users"; }
|
|
10
|
+
}
|
|
11
|
+
registerRelation(User, {
|
|
12
|
+
kind: "hasMany",
|
|
13
|
+
related: () => Post,
|
|
14
|
+
foreignKey: "user_id",
|
|
15
|
+
propertyKey: "posts",
|
|
16
|
+
});
|
|
17
|
+
class UserSoft extends XModel {
|
|
18
|
+
static { this.table = "users"; }
|
|
19
|
+
static { this.softDeletes = true; }
|
|
20
|
+
}
|
|
21
|
+
class Profile extends XModel {
|
|
22
|
+
static { this.table = "profiles"; }
|
|
23
|
+
}
|
|
24
|
+
registerColumnMeta(Profile, "secret", { hidden: true });
|
|
25
|
+
describe("XModel", () => {
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
initDb({ __memorySqlite: true });
|
|
28
|
+
const knex = getKnex();
|
|
29
|
+
await knex.schema.dropTableIfExists("posts");
|
|
30
|
+
await knex.schema.dropTableIfExists("profiles");
|
|
31
|
+
await knex.schema.dropTableIfExists("users");
|
|
32
|
+
await knex.schema.createTable("users", (table) => {
|
|
33
|
+
table.increments("id").primary();
|
|
34
|
+
table.string("name", 255).notNullable();
|
|
35
|
+
table.timestamp("created_at").defaultTo(knex.fn.now());
|
|
36
|
+
table.timestamp("updated_at").defaultTo(knex.fn.now());
|
|
37
|
+
table.timestamp("deleted_at").nullable();
|
|
38
|
+
});
|
|
39
|
+
await knex.schema.createTable("posts", (table) => {
|
|
40
|
+
table.increments("id").primary();
|
|
41
|
+
table.integer("user_id").unsigned().notNullable();
|
|
42
|
+
table.string("title", 255).notNullable();
|
|
43
|
+
table.timestamp("created_at").defaultTo(knex.fn.now());
|
|
44
|
+
table.timestamp("updated_at").defaultTo(knex.fn.now());
|
|
45
|
+
});
|
|
46
|
+
await knex.schema.createTable("profiles", (table) => {
|
|
47
|
+
table.increments("id").primary();
|
|
48
|
+
table.string("secret", 255).notNullable();
|
|
49
|
+
table.timestamp("created_at").defaultTo(knex.fn.now());
|
|
50
|
+
table.timestamp("updated_at").defaultTo(knex.fn.now());
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
afterAll(async () => {
|
|
54
|
+
await closeDb();
|
|
55
|
+
});
|
|
56
|
+
it("creates, finds, and lists rows", async () => {
|
|
57
|
+
const created = await User.create({ name: "alice" });
|
|
58
|
+
expect(created.name).toBe("alice");
|
|
59
|
+
const found = await User.find(created.id);
|
|
60
|
+
expect(found?.name).toBe("alice");
|
|
61
|
+
const all = await User.all();
|
|
62
|
+
expect(all.length).toBeGreaterThan(0);
|
|
63
|
+
});
|
|
64
|
+
it("throws findOrFail when missing", async () => {
|
|
65
|
+
await expect(User.findOrFail(9_007_199_254_740_991)).rejects.toThrow("Not found");
|
|
66
|
+
});
|
|
67
|
+
it("supports where filters", async () => {
|
|
68
|
+
const rows = await User.where("name", "alice").select();
|
|
69
|
+
expect(rows.length).toBeGreaterThan(0);
|
|
70
|
+
});
|
|
71
|
+
it("updates rows via static update and instance save", async () => {
|
|
72
|
+
const created = await User.create({ name: "bob" });
|
|
73
|
+
await User.update(created.id, { name: "bobby" });
|
|
74
|
+
const updated = await User.find(created.id);
|
|
75
|
+
expect(updated?.name).toBe("bobby");
|
|
76
|
+
const instance = Object.assign(new User(), updated);
|
|
77
|
+
await instance.save();
|
|
78
|
+
const reloaded = await User.find(created.id);
|
|
79
|
+
expect(reloaded?.name).toBe("bobby");
|
|
80
|
+
});
|
|
81
|
+
it("destroys rows", async () => {
|
|
82
|
+
const created = await User.create({ name: "tmp" });
|
|
83
|
+
await User.destroy(created.id);
|
|
84
|
+
expect(await User.find(created.id)).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
it("paginates with the shared envelope shape", async () => {
|
|
87
|
+
const page = await User.paginate(1, 5);
|
|
88
|
+
expect(page.data.length).toBeGreaterThan(0);
|
|
89
|
+
expect(page.total).toBeGreaterThan(0);
|
|
90
|
+
expect(page.page).toBe(1);
|
|
91
|
+
expect(page.perPage).toBe(5);
|
|
92
|
+
expect(page.lastPage).toBeGreaterThan(0);
|
|
93
|
+
});
|
|
94
|
+
it("eager-loads hasMany relations", async () => {
|
|
95
|
+
const unique = `owner-${Math.random().toString(16).slice(2)}`;
|
|
96
|
+
const owner = await User.create({ name: unique });
|
|
97
|
+
await Post.create({ user_id: owner.id, title: "hello" });
|
|
98
|
+
const rows = await User.with("posts").where("name", unique).get();
|
|
99
|
+
expect(rows[0]?.posts).toBeDefined();
|
|
100
|
+
expect(Array.isArray(rows[0]?.posts)).toBe(true);
|
|
101
|
+
expect((rows[0]?.posts)[0]?.title).toBe("hello");
|
|
102
|
+
});
|
|
103
|
+
it("applies soft deletes", async () => {
|
|
104
|
+
const created = await UserSoft.create({ name: "ghost" });
|
|
105
|
+
const instance = Object.assign(new UserSoft(), created);
|
|
106
|
+
await instance.delete();
|
|
107
|
+
expect(await UserSoft.find(created.id)).toBeNull();
|
|
108
|
+
const trashed = await UserSoft.withTrashed().where("id", created.id).first();
|
|
109
|
+
expect(trashed).not.toBeNull();
|
|
110
|
+
});
|
|
111
|
+
it("strips hidden columns from toJSON", async () => {
|
|
112
|
+
const created = await Profile.create({ secret: "hide-me" });
|
|
113
|
+
const instance = Object.assign(new Profile(), created);
|
|
114
|
+
const json = instance.toJSON();
|
|
115
|
+
expect(json.secret).toBeUndefined();
|
|
116
|
+
expect(json.id).toBeDefined();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=XModel.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XModel.test.js","sourceRoot":"","sources":["../src/XModel.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import mongoose, { type FilterQuery, type Model, type Schema, type SortOrder, type UpdateQuery, Types } from "mongoose";
|
|
2
|
+
import type { PaginatedResult } from "@xacos/shared";
|
|
3
|
+
type MongoDoc = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Mongoose-backed persistence. With MongoDB you define a {@link Schema} on the subclass
|
|
6
|
+
* and use this API directly — **no Knex migrations** (`MigrationRunner` is SQL-only).
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class XMongoModel {
|
|
9
|
+
/** Registered name for `mongoose.model()` (must be unique per connection). */
|
|
10
|
+
static modelName: string;
|
|
11
|
+
/** Optional Mongo collection name (passed to the schema via `collection` option). */
|
|
12
|
+
static collection: string;
|
|
13
|
+
static getSchema(): Schema;
|
|
14
|
+
static get model(): Model<MongoDoc & mongoose.Document>;
|
|
15
|
+
static findById(id: string | Types.ObjectId): Promise<MongoDoc | null>;
|
|
16
|
+
static findByIdOrFail(id: string | Types.ObjectId): Promise<MongoDoc>;
|
|
17
|
+
static findOne(filter: FilterQuery<MongoDoc>): Promise<MongoDoc | null>;
|
|
18
|
+
static find(filter?: FilterQuery<MongoDoc>): Promise<MongoDoc[]>;
|
|
19
|
+
static create(data: MongoDoc): Promise<MongoDoc>;
|
|
20
|
+
static updateById(id: string | Types.ObjectId, update: UpdateQuery<MongoDoc>): Promise<MongoDoc | null>;
|
|
21
|
+
static deleteById(id: string | Types.ObjectId): Promise<boolean>;
|
|
22
|
+
static paginate(filter?: FilterQuery<MongoDoc>, page?: number, perPage?: number, sort?: Record<string, SortOrder>): Promise<PaginatedResult<MongoDoc>>;
|
|
23
|
+
}
|
|
24
|
+
export default XMongoModel;
|
|
25
|
+
//# sourceMappingURL=XMongoModel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XMongoModel.d.ts","sourceRoot":"","sources":["../src/XMongoModel.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EACf,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,EACN,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAExC;;;GAGG;AACH,8BAAsB,WAAW;IAC/B,8EAA8E;IAC9E,MAAM,CAAC,SAAS,SAAM;IACtB,qFAAqF;IACrF,MAAM,CAAC,UAAU,SAAM;IAEvB,MAAM,CAAC,SAAS,IAAI,MAAM;IAI1B,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAiBtD;WAEY,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;WAK/D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;WAU9D,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;WAKhE,IAAI,CAAC,MAAM,GAAE,WAAW,CAAC,QAAQ,CAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;WAI7D,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;WAUzC,UAAU,CACrB,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,EAC3B,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,GAC5B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;WAQd,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;WAKzD,QAAQ,CACnB,MAAM,GAAE,WAAW,CAAC,QAAQ,CAAM,EAClC,IAAI,SAAI,EACR,OAAO,SAAK,EACZ,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAe,GAC5C,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;CAetC;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
/**
|
|
3
|
+
* Mongoose-backed persistence. With MongoDB you define a {@link Schema} on the subclass
|
|
4
|
+
* and use this API directly — **no Knex migrations** (`MigrationRunner` is SQL-only).
|
|
5
|
+
*/
|
|
6
|
+
export class XMongoModel {
|
|
7
|
+
/** Registered name for `mongoose.model()` (must be unique per connection). */
|
|
8
|
+
static { this.modelName = ""; }
|
|
9
|
+
/** Optional Mongo collection name (passed to the schema via `collection` option). */
|
|
10
|
+
static { this.collection = ""; }
|
|
11
|
+
static getSchema() {
|
|
12
|
+
throw new Error("[XAOCS ORM] Override getSchema() and return a Mongoose Schema for this model.");
|
|
13
|
+
}
|
|
14
|
+
static get model() {
|
|
15
|
+
const ctor = this;
|
|
16
|
+
if (!ctor.modelName) {
|
|
17
|
+
throw new Error("[XAOCS ORM] static modelName is required on XMongoModel subclasses.");
|
|
18
|
+
}
|
|
19
|
+
const existing = mongoose.models[ctor.modelName];
|
|
20
|
+
if (existing) {
|
|
21
|
+
return existing;
|
|
22
|
+
}
|
|
23
|
+
const schema = ctor.getSchema();
|
|
24
|
+
if (ctor.collection) {
|
|
25
|
+
schema.set("collection", ctor.collection);
|
|
26
|
+
}
|
|
27
|
+
return mongoose.model(ctor.modelName, schema);
|
|
28
|
+
}
|
|
29
|
+
static async findById(id) {
|
|
30
|
+
const doc = await this.model.findById(id).lean().exec();
|
|
31
|
+
return doc ?? null;
|
|
32
|
+
}
|
|
33
|
+
static async findByIdOrFail(id) {
|
|
34
|
+
const row = await this.findById(id);
|
|
35
|
+
if (!row) {
|
|
36
|
+
const err = new Error("Not found");
|
|
37
|
+
err.statusCode = 404;
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
return row;
|
|
41
|
+
}
|
|
42
|
+
static async findOne(filter) {
|
|
43
|
+
const doc = await this.model.findOne(filter).lean().exec();
|
|
44
|
+
return doc ?? null;
|
|
45
|
+
}
|
|
46
|
+
static async find(filter = {}) {
|
|
47
|
+
return (await this.model.find(filter).lean().exec());
|
|
48
|
+
}
|
|
49
|
+
static async create(data) {
|
|
50
|
+
const created = await this.model.create(data);
|
|
51
|
+
const plain = created.toObject();
|
|
52
|
+
const id = plain["_id"];
|
|
53
|
+
if (id !== undefined) {
|
|
54
|
+
plain["id"] = String(id);
|
|
55
|
+
}
|
|
56
|
+
return plain;
|
|
57
|
+
}
|
|
58
|
+
static async updateById(id, update) {
|
|
59
|
+
const doc = await this.model
|
|
60
|
+
.findByIdAndUpdate(id, update, { new: true, runValidators: true })
|
|
61
|
+
.lean()
|
|
62
|
+
.exec();
|
|
63
|
+
return doc ?? null;
|
|
64
|
+
}
|
|
65
|
+
static async deleteById(id) {
|
|
66
|
+
const res = await this.model.findByIdAndDelete(id).exec();
|
|
67
|
+
return res !== null;
|
|
68
|
+
}
|
|
69
|
+
static async paginate(filter = {}, page = 1, perPage = 15, sort = { _id: -1 }) {
|
|
70
|
+
const skip = (page - 1) * perPage;
|
|
71
|
+
const [total, data] = await Promise.all([
|
|
72
|
+
this.model.countDocuments(filter).exec(),
|
|
73
|
+
this.model.find(filter).sort(sort).skip(skip).limit(perPage).lean().exec(),
|
|
74
|
+
]);
|
|
75
|
+
const lastPage = Math.max(1, Math.ceil(total / perPage));
|
|
76
|
+
return {
|
|
77
|
+
data: data,
|
|
78
|
+
total,
|
|
79
|
+
page,
|
|
80
|
+
perPage,
|
|
81
|
+
lastPage,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export default XMongoModel;
|
|
86
|
+
//# sourceMappingURL=XMongoModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XMongoModel.js","sourceRoot":"","sources":["../src/XMongoModel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XMongoModel.test.d.ts","sourceRoot":"","sources":["../src/XMongoModel.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { Schema } from "mongoose";
|
|
3
|
+
import { XMongoModel } from "./XMongoModel";
|
|
4
|
+
class MissingModelName extends XMongoModel {
|
|
5
|
+
static getSchema() {
|
|
6
|
+
return new Schema({ label: { type: String } });
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
describe("XMongoModel", () => {
|
|
10
|
+
it("requires modelName before accessing model", () => {
|
|
11
|
+
expect(() => MissingModelName.model).toThrow("modelName");
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=XMongoModel.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XMongoModel.test.js","sourceRoot":"","sources":["../src/XMongoModel.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,gBAAiB,SAAQ,WAAW;IACxC,MAAM,CAAC,SAAS;QACd,OAAO,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;CACF;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sqlite.integration.test.ts"],"names":[],"mappings":""}
|