@ghom/orm 1.10.0 → 2.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/.github/workflows/test.yml +9 -9
- package/biome.json +45 -0
- package/bun.lock +488 -0
- package/dist/app/backup.d.ts +3 -3
- package/dist/app/backup.js +10 -11
- package/dist/app/column.d.ts +225 -0
- package/dist/app/column.js +342 -0
- package/dist/app/orm.d.ts +5 -4
- package/dist/app/orm.js +19 -15
- package/dist/app/table.d.ts +51 -21
- package/dist/app/table.js +35 -18
- package/dist/app/util.d.ts +1 -1
- package/dist/app/util.js +4 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +43 -47
- package/tests/{test.js → orm.test.ts} +112 -53
- package/tests/tables/a.ts +16 -0
- package/tests/tables/b.ts +16 -0
- package/tests/tables/c.ts +14 -0
- package/tests/tables/d.ts +32 -0
- package/tsconfig.json +1 -0
- package/tests/tables/a.js +0 -25
- package/tests/tables/b.js +0 -30
- package/tests/tables/c.js +0 -17
package/dist/app/orm.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import url from "node:url";
|
|
2
2
|
import { Handler } from "@ghom/handler";
|
|
3
|
+
import { CachedQuery } from "@ghom/query";
|
|
3
4
|
import { default as knex } from "knex";
|
|
4
|
-
import {
|
|
5
|
+
import { backupTable, disableForeignKeys, enableForeignKeys, restoreBackup } from "./backup.js";
|
|
5
6
|
import { Table } from "./table.js";
|
|
6
|
-
import {
|
|
7
|
-
import { backupTable, restoreBackup, disableForeignKeys, enableForeignKeys, } from "./backup.js";
|
|
7
|
+
import { isCJS } from "./util.js";
|
|
8
8
|
/**
|
|
9
9
|
* The main ORM class that manages database connections, tables, and caching.
|
|
10
10
|
*
|
|
@@ -24,7 +24,7 @@ import { backupTable, restoreBackup, disableForeignKeys, enableForeignKeys, } fr
|
|
|
24
24
|
export class ORM {
|
|
25
25
|
config;
|
|
26
26
|
_ready = false;
|
|
27
|
-
|
|
27
|
+
_client;
|
|
28
28
|
handler;
|
|
29
29
|
_rawCache;
|
|
30
30
|
/**
|
|
@@ -43,7 +43,7 @@ export class ORM {
|
|
|
43
43
|
this.config = config;
|
|
44
44
|
if (config === false)
|
|
45
45
|
return;
|
|
46
|
-
this.
|
|
46
|
+
this._client = knex(config.database ?? {
|
|
47
47
|
client: "sqlite3",
|
|
48
48
|
useNullAsDefault: true,
|
|
49
49
|
connection: {
|
|
@@ -62,14 +62,18 @@ export class ORM {
|
|
|
62
62
|
this._rawCache = new CachedQuery(async (raw) => await this.raw(raw), config.caching ?? Infinity);
|
|
63
63
|
}
|
|
64
64
|
requireClient() {
|
|
65
|
-
if (!this.
|
|
65
|
+
if (!this._client)
|
|
66
66
|
throw new Error("ORM client is not initialized. Cannot use this method without a database connection.");
|
|
67
67
|
}
|
|
68
|
+
get client() {
|
|
69
|
+
this.requireClient();
|
|
70
|
+
return this._client;
|
|
71
|
+
}
|
|
68
72
|
/**
|
|
69
73
|
* Returns true if the ORM has a database client connected.
|
|
70
74
|
*/
|
|
71
75
|
get isConnected() {
|
|
72
|
-
return this.
|
|
76
|
+
return this._client !== undefined;
|
|
73
77
|
}
|
|
74
78
|
get cachedTables() {
|
|
75
79
|
if (!this.handler)
|
|
@@ -84,7 +88,7 @@ export class ORM {
|
|
|
84
88
|
}
|
|
85
89
|
async hasTable(name) {
|
|
86
90
|
this.requireClient();
|
|
87
|
-
return this.
|
|
91
|
+
return this._client.schema.hasTable(name);
|
|
88
92
|
}
|
|
89
93
|
/**
|
|
90
94
|
* Handle the table files and create the tables in the database.
|
|
@@ -99,10 +103,10 @@ export class ORM {
|
|
|
99
103
|
this.handler.elements.set("migration", new Table({
|
|
100
104
|
name: "migration",
|
|
101
105
|
priority: Infinity,
|
|
102
|
-
|
|
103
|
-
table.string(
|
|
104
|
-
|
|
105
|
-
},
|
|
106
|
+
columns: (col) => ({
|
|
107
|
+
table: col.string().unique(),
|
|
108
|
+
version: col.integer(),
|
|
109
|
+
}),
|
|
106
110
|
}));
|
|
107
111
|
const sortedTables = this.cachedTables.toSorted((a, b) => (b.options.priority ?? 0) - (a.options.priority ?? 0));
|
|
108
112
|
for (const table of sortedTables) {
|
|
@@ -114,7 +118,7 @@ export class ORM {
|
|
|
114
118
|
this.requireClient();
|
|
115
119
|
if (this._ready)
|
|
116
120
|
this.cache.invalidate();
|
|
117
|
-
return this.
|
|
121
|
+
return this._client.raw(sql);
|
|
118
122
|
}
|
|
119
123
|
cache = {
|
|
120
124
|
raw: (sql, anyDataUpdated) => {
|
|
@@ -140,7 +144,7 @@ export class ORM {
|
|
|
140
144
|
*/
|
|
141
145
|
async createBackup(dirname) {
|
|
142
146
|
this.requireClient();
|
|
143
|
-
for (
|
|
147
|
+
for (const table of this.cachedTables) {
|
|
144
148
|
await backupTable(table, dirname);
|
|
145
149
|
}
|
|
146
150
|
console.log("Database backup created.");
|
|
@@ -152,7 +156,7 @@ export class ORM {
|
|
|
152
156
|
async restoreBackup(dirname) {
|
|
153
157
|
this.requireClient();
|
|
154
158
|
await disableForeignKeys(this, async (trx) => {
|
|
155
|
-
for (
|
|
159
|
+
for (const table of this.cachedTables) {
|
|
156
160
|
await restoreBackup(table, trx, dirname);
|
|
157
161
|
}
|
|
158
162
|
});
|
package/dist/app/table.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { Knex } from "knex";
|
|
2
|
-
import { ORM } from "./orm.js";
|
|
3
1
|
import { CachedQuery } from "@ghom/query";
|
|
2
|
+
import type { Knex } from "knex";
|
|
3
|
+
import { type ColumnDef, type InferColumns } from "./column.js";
|
|
4
|
+
import type { ORM } from "./orm.js";
|
|
4
5
|
export interface MigrationData {
|
|
5
6
|
table: string;
|
|
6
7
|
version: number;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Table options with typed columns.
|
|
11
|
+
* Type is automatically inferred from the column definitions.
|
|
12
|
+
*/
|
|
13
|
+
export interface TableOptions<Columns extends Record<string, ColumnDef<any, any>>> {
|
|
9
14
|
name: string;
|
|
10
15
|
description?: string;
|
|
11
16
|
priority?: number;
|
|
@@ -15,23 +20,48 @@ export interface TableOptions<Type extends object = object> {
|
|
|
15
20
|
*/
|
|
16
21
|
caching?: number;
|
|
17
22
|
migrations?: {
|
|
18
|
-
[version: number]: (
|
|
23
|
+
[version: number]: (builder: Knex.CreateTableBuilder) => void;
|
|
19
24
|
};
|
|
20
|
-
then?: (this: Table<
|
|
21
|
-
|
|
25
|
+
then?: (this: Table<Columns>, table: Table<Columns>) => unknown;
|
|
26
|
+
/**
|
|
27
|
+
* Typed columns definition with automatic type inference.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* columns: (col) => ({
|
|
31
|
+
* id: col.increments(),
|
|
32
|
+
* username: col.string().unique(),
|
|
33
|
+
* age: col.integer().nullable(),
|
|
34
|
+
* role: col.enum(["admin", "user"]),
|
|
35
|
+
* })
|
|
36
|
+
*/
|
|
37
|
+
columns: (col: typeof import("./column.js").col) => Columns;
|
|
22
38
|
}
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Represents a database table with typed columns.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const userTable = new Table({
|
|
44
|
+
* name: "user",
|
|
45
|
+
* columns: (col) => ({
|
|
46
|
+
* id: col.increments(),
|
|
47
|
+
* username: col.string().unique(),
|
|
48
|
+
* age: col.integer().nullable(),
|
|
49
|
+
* }),
|
|
50
|
+
* })
|
|
51
|
+
* // Type is automatically inferred as { id: number; username: string; age: number | null }
|
|
52
|
+
*/
|
|
53
|
+
export declare class Table<Columns extends Record<string, ColumnDef<any, any>> = Record<string, ColumnDef<any, any>>> {
|
|
54
|
+
readonly options: TableOptions<Columns>;
|
|
25
55
|
orm?: ORM;
|
|
26
|
-
_whereCache?: CachedQuery<[
|
|
27
|
-
cb: (query: Table<Type>["query"]) => unknown
|
|
28
|
-
], unknown>;
|
|
56
|
+
_whereCache?: CachedQuery<[cb: (query: Table<Columns>["query"]) => unknown], unknown>;
|
|
29
57
|
_countCache?: CachedQuery<[where: string | null], number>;
|
|
30
|
-
constructor(options: TableOptions<
|
|
58
|
+
constructor(options: TableOptions<Columns>);
|
|
59
|
+
/** The inferred TypeScript type for rows of this table */
|
|
60
|
+
readonly $type: InferColumns<Columns>;
|
|
31
61
|
private requireOrm;
|
|
32
|
-
get
|
|
33
|
-
get query(): Knex.QueryBuilder<
|
|
34
|
-
_base:
|
|
62
|
+
get client(): Knex;
|
|
63
|
+
get query(): Knex.QueryBuilder<InferColumns<Columns>, {
|
|
64
|
+
_base: InferColumns<Columns>;
|
|
35
65
|
_hasSelection: false;
|
|
36
66
|
_keys: never;
|
|
37
67
|
_aliases: {};
|
|
@@ -40,16 +70,16 @@ export declare class Table<Type extends object = object> {
|
|
|
40
70
|
_unionProps: never;
|
|
41
71
|
}[]>;
|
|
42
72
|
get cache(): {
|
|
43
|
-
get: <Return>(id: string, cb: (table: Pick<Table<
|
|
44
|
-
set: <Return>(cb: (table: Pick<Table<
|
|
73
|
+
get: <Return>(id: string, cb: (table: Pick<Table<Columns>["query"], "select" | "count" | "avg" | "sum" | "countDistinct" | "avgDistinct" | "sumDistinct">) => Return) => Return;
|
|
74
|
+
set: <Return>(cb: (table: Pick<Table<Columns>["query"], "update" | "delete" | "insert" | "upsert" | "truncate" | "jsonInsert">) => Return) => Return;
|
|
45
75
|
count: (where?: string) => Promise<number>;
|
|
46
76
|
invalidate: () => void;
|
|
47
77
|
};
|
|
48
78
|
count(where?: string): Promise<number>;
|
|
49
|
-
hasColumn(name: keyof
|
|
50
|
-
getColumn(name: keyof
|
|
51
|
-
getColumns(): Promise<Record<keyof
|
|
52
|
-
getColumnNames(): Promise<Array<keyof
|
|
79
|
+
hasColumn(name: keyof InferColumns<Columns> & string): Promise<boolean>;
|
|
80
|
+
getColumn(name: keyof InferColumns<Columns> & string): Promise<Knex.ColumnInfo>;
|
|
81
|
+
getColumns(): Promise<Record<keyof InferColumns<Columns> & string, Knex.ColumnInfo>>;
|
|
82
|
+
getColumnNames(): Promise<Array<keyof InferColumns<Columns> & string>>;
|
|
53
83
|
isEmpty(): Promise<boolean>;
|
|
54
84
|
make(orm: ORM): Promise<this>;
|
|
55
85
|
private migrate;
|
package/dist/app/table.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import { styled } from "./util.js";
|
|
2
1
|
import { CachedQuery } from "@ghom/query";
|
|
2
|
+
import { buildColumnsSchema, col } from "./column.js";
|
|
3
|
+
import { styled } from "./util.js";
|
|
4
|
+
/**
|
|
5
|
+
* Represents a database table with typed columns.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const userTable = new Table({
|
|
9
|
+
* name: "user",
|
|
10
|
+
* columns: (col) => ({
|
|
11
|
+
* id: col.increments(),
|
|
12
|
+
* username: col.string().unique(),
|
|
13
|
+
* age: col.integer().nullable(),
|
|
14
|
+
* }),
|
|
15
|
+
* })
|
|
16
|
+
* // Type is automatically inferred as { id: number; username: string; age: number | null }
|
|
17
|
+
*/
|
|
3
18
|
export class Table {
|
|
4
19
|
options;
|
|
5
20
|
orm;
|
|
@@ -11,15 +26,15 @@ export class Table {
|
|
|
11
26
|
requireOrm() {
|
|
12
27
|
if (!this.orm)
|
|
13
28
|
throw new Error("missing ORM");
|
|
14
|
-
if (!this.orm.
|
|
29
|
+
if (!this.orm._client)
|
|
15
30
|
throw new Error("ORM client is not initialized");
|
|
16
31
|
}
|
|
17
|
-
get
|
|
32
|
+
get client() {
|
|
18
33
|
this.requireOrm();
|
|
19
34
|
return this.orm.client;
|
|
20
35
|
}
|
|
21
36
|
get query() {
|
|
22
|
-
return this.
|
|
37
|
+
return this.client(this.options.name);
|
|
23
38
|
}
|
|
24
39
|
get cache() {
|
|
25
40
|
if (!this._whereCache || !this._countCache)
|
|
@@ -46,18 +61,18 @@ export class Table {
|
|
|
46
61
|
}
|
|
47
62
|
async count(where) {
|
|
48
63
|
return this.query
|
|
49
|
-
.select(this.
|
|
64
|
+
.select(this.client.raw("count(*) as total"))
|
|
50
65
|
.whereRaw(where ?? "1=1")
|
|
51
66
|
.then((rows) => +(rows?.[0] ?? { total: 0 }).total);
|
|
52
67
|
}
|
|
53
68
|
async hasColumn(name) {
|
|
54
|
-
return this.
|
|
69
|
+
return this.client.schema.hasColumn(this.options.name, name);
|
|
55
70
|
}
|
|
56
71
|
async getColumn(name) {
|
|
57
|
-
return this.
|
|
72
|
+
return this.client(this.options.name).columnInfo(name);
|
|
58
73
|
}
|
|
59
74
|
async getColumns() {
|
|
60
|
-
return this.
|
|
75
|
+
return this.client(this.options.name).columnInfo();
|
|
61
76
|
}
|
|
62
77
|
async getColumnNames() {
|
|
63
78
|
return this.getColumns().then(Object.keys);
|
|
@@ -74,12 +89,15 @@ export class Table {
|
|
|
74
89
|
? ` ${styled(this.orm, this.options.description, "description")}`
|
|
75
90
|
: ""}`;
|
|
76
91
|
try {
|
|
77
|
-
await this.
|
|
92
|
+
await this.client.schema.createTable(this.options.name, (builder) => {
|
|
93
|
+
const columns = this.options.columns(col);
|
|
94
|
+
buildColumnsSchema(builder, columns);
|
|
95
|
+
});
|
|
78
96
|
this.orm.config.logger?.log(`created table ${tableNameLog}`);
|
|
79
97
|
}
|
|
80
98
|
catch (error) {
|
|
81
99
|
if (error.toString().includes("syntax error")) {
|
|
82
|
-
this.orm.config.logger?.error(`you need to implement the "
|
|
100
|
+
this.orm.config.logger?.error(`you need to implement the "columns" callback in options of your ${styled(this.orm, this.options.name, "highlight")} table!`);
|
|
83
101
|
throw error;
|
|
84
102
|
}
|
|
85
103
|
else {
|
|
@@ -96,8 +114,10 @@ export class Table {
|
|
|
96
114
|
this.orm.config.logger?.error(error);
|
|
97
115
|
throw error;
|
|
98
116
|
}
|
|
99
|
-
if ((await this.count()) === 0)
|
|
100
|
-
|
|
117
|
+
if ((await this.count()) === 0) {
|
|
118
|
+
const thenFn = this.options.then;
|
|
119
|
+
await thenFn?.bind(this)(this);
|
|
120
|
+
}
|
|
101
121
|
return this;
|
|
102
122
|
}
|
|
103
123
|
async migrate() {
|
|
@@ -106,7 +126,7 @@ export class Table {
|
|
|
106
126
|
const migrations = new Map(Object.entries(this.options.migrations)
|
|
107
127
|
.sort((a, b) => Number(a[0]) - Number(b[0]))
|
|
108
128
|
.map((entry) => [Number(entry[0]), entry[1]]));
|
|
109
|
-
const fromDatabase = await this.
|
|
129
|
+
const fromDatabase = await this.client("migration")
|
|
110
130
|
.where("table", this.options.name)
|
|
111
131
|
.first();
|
|
112
132
|
const data = fromDatabase || {
|
|
@@ -115,17 +135,14 @@ export class Table {
|
|
|
115
135
|
};
|
|
116
136
|
const baseVersion = data.version;
|
|
117
137
|
for (const [version, migration] of migrations) {
|
|
118
|
-
await this.
|
|
138
|
+
await this.client.schema.alterTable(this.options.name, (builder) => {
|
|
119
139
|
if (version <= data.version)
|
|
120
140
|
return;
|
|
121
141
|
migration(builder);
|
|
122
142
|
data.version = version;
|
|
123
143
|
});
|
|
124
144
|
}
|
|
125
|
-
await this.
|
|
126
|
-
.insert(data)
|
|
127
|
-
.onConflict("table")
|
|
128
|
-
.merge();
|
|
145
|
+
await this.client("migration").insert(data).onConflict("table").merge();
|
|
129
146
|
return baseVersion === data.version ? false : data.version;
|
|
130
147
|
}
|
|
131
148
|
}
|
package/dist/app/util.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type TextStyle = Parameters<typeof util.styleText>[0];
|
|
|
4
4
|
export declare const DEFAULT_BACKUP_LOCATION: string;
|
|
5
5
|
export declare const DEFAULT_BACKUP_CHUNK_SIZE: number;
|
|
6
6
|
export declare const DEFAULT_LOGGER_HIGHLIGHT = "blueBright";
|
|
7
|
-
export declare const DEFAULT_LOGGER_DESCRIPTION = "
|
|
7
|
+
export declare const DEFAULT_LOGGER_DESCRIPTION = "gray";
|
|
8
8
|
export declare const DEFAULT_LOGGER_RAW_VALUE = "magentaBright";
|
|
9
9
|
declare let isCJS: boolean;
|
|
10
10
|
export { isCJS };
|
package/dist/app/util.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import util from "node:util";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import util from "node:util";
|
|
4
4
|
export const DEFAULT_BACKUP_LOCATION = path.join(process.cwd(), "backup");
|
|
5
5
|
export const DEFAULT_BACKUP_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
|
|
6
6
|
export const DEFAULT_LOGGER_HIGHLIGHT = "blueBright";
|
|
7
|
-
export const DEFAULT_LOGGER_DESCRIPTION = "
|
|
7
|
+
export const DEFAULT_LOGGER_DESCRIPTION = "gray";
|
|
8
8
|
export const DEFAULT_LOGGER_RAW_VALUE = "magentaBright";
|
|
9
9
|
let isCJS = false;
|
|
10
10
|
try {
|
|
11
11
|
const pack = JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8"));
|
|
12
|
-
isCJS = pack.type === "commonjs" || pack.type
|
|
12
|
+
isCJS = pack.type === "commonjs" || pack.type === void 0;
|
|
13
13
|
}
|
|
14
14
|
catch {
|
|
15
15
|
throw new Error("Missing package.json: Can't detect the type of modules.\n" +
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,47 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ghom/orm",
|
|
3
|
-
"version": "
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"description": "TypeScript KnexJS ORM & handler",
|
|
9
|
-
"homepage": "https://github.com/GhomKrosmonaute/orm",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"@types/
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
"url": "https://github.com/GhomKrosmonaute/orm.git",
|
|
45
|
-
"type": "git"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ghom/orm",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"description": "TypeScript KnexJS ORM & handler",
|
|
9
|
+
"homepage": "https://github.com/GhomKrosmonaute/orm",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"format": "biome format --write src",
|
|
12
|
+
"lint": "biome lint .",
|
|
13
|
+
"check": "biome check --write .",
|
|
14
|
+
"build": "rimraf dist && tsc",
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"prepublishOnly": "npm run check && npm run build && npm test"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@biomejs/biome": "^2.3.13",
|
|
20
|
+
"@types/bun": "^1.1.0",
|
|
21
|
+
"rimraf": "^6.0.1",
|
|
22
|
+
"typescript": "^5.2.2"
|
|
23
|
+
},
|
|
24
|
+
"optionalDependencies": {
|
|
25
|
+
"mysql2": "^3.6.2",
|
|
26
|
+
"pg": "^8.11.3",
|
|
27
|
+
"sqlite3": "^5.1.6"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@ghom/handler": "^3.1.0",
|
|
31
|
+
"@ghom/query": "1.0.0",
|
|
32
|
+
"csv-parser": "^3.0.0",
|
|
33
|
+
"json-2-csv": "^5.5.6",
|
|
34
|
+
"knex": "^3.0.1"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.0.0"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"url": "https://github.com/GhomKrosmonaute/orm.git",
|
|
41
|
+
"type": "git"
|
|
42
|
+
}
|
|
43
|
+
}
|