@stonyx/orm 0.2.1-beta.82 → 0.2.1-beta.84
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/config/environment.js +17 -0
- package/dist/aggregates.d.ts +21 -0
- package/dist/aggregates.js +90 -0
- package/dist/attr.d.ts +2 -0
- package/dist/attr.js +22 -0
- package/dist/belongs-to.d.ts +11 -0
- package/dist/belongs-to.js +58 -0
- package/dist/cli.d.ts +22 -0
- package/dist/cli.js +148 -0
- package/dist/commands.d.ts +7 -0
- package/dist/commands.js +146 -0
- package/dist/db.d.ts +21 -0
- package/dist/db.js +174 -0
- package/dist/exports/db.d.ts +7 -0
- package/{src → dist}/exports/db.js +2 -4
- package/dist/has-many.d.ts +11 -0
- package/dist/has-many.js +57 -0
- package/dist/hooks.d.ts +47 -0
- package/dist/hooks.js +106 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +34 -0
- package/dist/main.d.ts +46 -0
- package/dist/main.js +178 -0
- package/dist/manage-record.d.ts +13 -0
- package/dist/manage-record.js +113 -0
- package/dist/meta-request.d.ts +6 -0
- package/dist/meta-request.js +52 -0
- package/dist/migrate.d.ts +2 -0
- package/dist/migrate.js +57 -0
- package/dist/model-property.d.ts +9 -0
- package/dist/model-property.js +29 -0
- package/dist/model.d.ts +15 -0
- package/dist/model.js +18 -0
- package/dist/mysql/connection.d.ts +14 -0
- package/dist/mysql/connection.js +24 -0
- package/dist/mysql/migration-generator.d.ts +45 -0
- package/dist/mysql/migration-generator.js +245 -0
- package/dist/mysql/migration-runner.d.ts +12 -0
- package/dist/mysql/migration-runner.js +83 -0
- package/dist/mysql/mysql-db.d.ts +100 -0
- package/dist/mysql/mysql-db.js +411 -0
- package/dist/mysql/query-builder.d.ts +10 -0
- package/dist/mysql/query-builder.js +44 -0
- package/dist/mysql/schema-introspector.d.ts +19 -0
- package/dist/mysql/schema-introspector.js +286 -0
- package/dist/mysql/type-map.d.ts +21 -0
- package/dist/mysql/type-map.js +36 -0
- package/dist/orm-request.d.ts +38 -0
- package/dist/orm-request.js +453 -0
- package/dist/plural-registry.d.ts +4 -0
- package/{src → dist}/plural-registry.js +3 -6
- package/dist/postgres/connection.d.ts +15 -0
- package/dist/postgres/connection.js +30 -0
- package/dist/postgres/migration-generator.d.ts +45 -0
- package/dist/postgres/migration-generator.js +257 -0
- package/dist/postgres/migration-runner.d.ts +10 -0
- package/dist/postgres/migration-runner.js +82 -0
- package/dist/postgres/postgres-db.d.ts +119 -0
- package/dist/postgres/postgres-db.js +473 -0
- package/dist/postgres/query-builder.d.ts +27 -0
- package/dist/postgres/query-builder.js +98 -0
- package/dist/postgres/schema-introspector.d.ts +29 -0
- package/dist/postgres/schema-introspector.js +309 -0
- package/dist/postgres/type-map.d.ts +23 -0
- package/dist/postgres/type-map.js +53 -0
- package/dist/record.d.ts +75 -0
- package/dist/record.js +115 -0
- package/dist/relationships.d.ts +10 -0
- package/dist/relationships.js +35 -0
- package/dist/serializer.d.ts +17 -0
- package/dist/serializer.js +130 -0
- package/dist/setup-rest-server.d.ts +1 -0
- package/dist/setup-rest-server.js +54 -0
- package/dist/standalone-db.d.ts +58 -0
- package/dist/standalone-db.js +142 -0
- package/dist/store.d.ts +62 -0
- package/dist/store.js +271 -0
- package/dist/timescale/query-builder.d.ts +41 -0
- package/dist/timescale/query-builder.js +87 -0
- package/dist/timescale/timescale-db.d.ts +44 -0
- package/dist/timescale/timescale-db.js +81 -0
- package/dist/transforms.d.ts +2 -0
- package/dist/transforms.js +17 -0
- package/dist/types/orm-types.d.ts +142 -0
- package/dist/types/orm-types.js +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +13 -0
- package/dist/view-resolver.d.ts +8 -0
- package/dist/view-resolver.js +165 -0
- package/dist/view.d.ts +11 -0
- package/dist/view.js +18 -0
- package/package.json +34 -11
- package/src/{aggregates.js → aggregates.ts} +27 -13
- package/src/{attr.js → attr.ts} +2 -2
- package/src/{belongs-to.js → belongs-to.ts} +36 -17
- package/src/{cli.js → cli.ts} +17 -11
- package/src/{commands.js → commands.ts} +179 -170
- package/src/{db.js → db.ts} +35 -26
- package/src/exports/db.ts +7 -0
- package/src/has-many.ts +91 -0
- package/src/{hooks.js → hooks.ts} +23 -27
- package/src/{index.js → index.ts} +4 -4
- package/src/{main.js → main.ts} +64 -34
- package/src/{manage-record.js → manage-record.ts} +41 -22
- package/src/{meta-request.js → meta-request.ts} +17 -14
- package/src/{migrate.js → migrate.ts} +9 -9
- package/src/{model-property.js → model-property.ts} +12 -6
- package/src/{model.js → model.ts} +5 -4
- package/src/mysql/{connection.js → connection.ts} +43 -28
- package/src/mysql/{migration-generator.js → migration-generator.ts} +332 -286
- package/src/mysql/{migration-runner.js → migration-runner.ts} +116 -110
- package/src/mysql/{mysql-db.js → mysql-db.ts} +533 -473
- package/src/mysql/{query-builder.js → query-builder.ts} +69 -64
- package/src/mysql/{schema-introspector.js → schema-introspector.ts} +355 -325
- package/src/mysql/{type-map.js → type-map.ts} +42 -37
- package/src/{orm-request.js → orm-request.ts} +165 -95
- package/src/plural-registry.ts +12 -0
- package/src/postgres/connection.ts +46 -0
- package/src/postgres/{migration-generator.js → migration-generator.ts} +82 -38
- package/src/postgres/{migration-runner.js → migration-runner.ts} +11 -10
- package/src/postgres/{postgres-db.js → postgres-db.ts} +199 -111
- package/src/postgres/{query-builder.js → query-builder.ts} +27 -28
- package/src/postgres/{schema-introspector.js → schema-introspector.ts} +87 -58
- package/src/postgres/{type-map.js → type-map.ts} +10 -6
- package/src/{record.js → record.ts} +73 -34
- package/src/relationships.ts +48 -0
- package/src/{serializer.js → serializer.ts} +44 -36
- package/src/{setup-rest-server.js → setup-rest-server.ts} +18 -13
- package/src/{standalone-db.js → standalone-db.ts} +33 -24
- package/src/{store.js → store.ts} +90 -68
- package/src/timescale/query-builder.ts +137 -0
- package/src/timescale/timescale-db.ts +107 -0
- package/src/transforms.ts +20 -0
- package/src/types/mysql2.d.ts +30 -0
- package/src/types/orm-types.ts +146 -0
- package/src/types/pg.d.ts +28 -0
- package/src/types/stonyx-cron.d.ts +5 -0
- package/src/types/stonyx-events.d.ts +4 -0
- package/src/types/stonyx-rest-server.d.ts +11 -0
- package/src/types/stonyx-utils.d.ts +33 -0
- package/src/types/stonyx.d.ts +21 -0
- package/src/utils.ts +16 -0
- package/src/{view-resolver.js → view-resolver.ts} +53 -28
- package/src/view.ts +22 -0
- package/src/has-many.js +0 -68
- package/src/postgres/connection.js +0 -30
- package/src/relationships.js +0 -43
- package/src/transforms.js +0 -20
- package/src/utils.js +0 -12
- package/src/view.js +0 -21
|
@@ -1,170 +1,179 @@
|
|
|
1
|
-
import { fileToDirectory, directoryToFile } from './migrate.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
};
|
|
1
|
+
import { fileToDirectory, directoryToFile } from './migrate.js';
|
|
2
|
+
import type { MysqlConfig } from './mysql/connection.js';
|
|
3
|
+
|
|
4
|
+
interface Command {
|
|
5
|
+
description: string;
|
|
6
|
+
bootstrap: boolean;
|
|
7
|
+
run: (args?: string[]) => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const commands: Record<string, Command> = {
|
|
11
|
+
'db:migrate-to-directory': {
|
|
12
|
+
description: 'Migrate DB from single file to directory mode',
|
|
13
|
+
bootstrap: true,
|
|
14
|
+
run: async () => {
|
|
15
|
+
await fileToDirectory();
|
|
16
|
+
console.log('DB migration to directory mode complete.');
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
'db:migrate-to-file': {
|
|
20
|
+
description: 'Migrate DB from directory mode to single file',
|
|
21
|
+
bootstrap: true,
|
|
22
|
+
run: async () => {
|
|
23
|
+
await directoryToFile();
|
|
24
|
+
console.log('DB migration to file mode complete.');
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
'db:generate-migration': {
|
|
28
|
+
description: 'Generate a MySQL migration from current model schemas',
|
|
29
|
+
bootstrap: true,
|
|
30
|
+
run: async (args) => {
|
|
31
|
+
const description = args?.join(' ') || 'migration';
|
|
32
|
+
const { generateMigration } = await import('./mysql/migration-generator.js');
|
|
33
|
+
const result = await generateMigration(description);
|
|
34
|
+
|
|
35
|
+
if (result) {
|
|
36
|
+
console.log(`Migration created: ${result.filename}`);
|
|
37
|
+
} else {
|
|
38
|
+
console.log('No schema changes detected. No migration generated.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
'db:migrate': {
|
|
43
|
+
description: 'Apply pending MySQL migrations',
|
|
44
|
+
bootstrap: true,
|
|
45
|
+
run: async () => {
|
|
46
|
+
const config = (await import('stonyx/config')).default;
|
|
47
|
+
const mysqlConfig = config.orm.mysql;
|
|
48
|
+
|
|
49
|
+
if (!mysqlConfig) {
|
|
50
|
+
console.error('MySQL is not configured. Set MYSQL_HOST to enable MySQL mode.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { getPool, closePool } = await import('./mysql/connection.js');
|
|
55
|
+
const { ensureMigrationsTable, getAppliedMigrations, getMigrationFiles, applyMigration, parseMigrationFile } = await import('./mysql/migration-runner.js');
|
|
56
|
+
const { readFile } = await import('@stonyx/utils/file');
|
|
57
|
+
const path = await import('path');
|
|
58
|
+
|
|
59
|
+
const pool = await getPool(mysqlConfig as MysqlConfig);
|
|
60
|
+
const migrationsPath = path.resolve(config.rootPath, mysqlConfig.migrationsDir as string);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await ensureMigrationsTable(pool, mysqlConfig.migrationsTable as string);
|
|
64
|
+
|
|
65
|
+
const applied = await getAppliedMigrations(pool, mysqlConfig.migrationsTable as string);
|
|
66
|
+
const files = await getMigrationFiles(migrationsPath);
|
|
67
|
+
const pending = files.filter((f: string) => !applied.includes(f));
|
|
68
|
+
|
|
69
|
+
if (pending.length === 0) {
|
|
70
|
+
console.log('No pending migrations.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(`Applying ${pending.length} migration(s)...`);
|
|
75
|
+
|
|
76
|
+
for (const filename of pending) {
|
|
77
|
+
const content = await readFile(path.join(migrationsPath, filename) as string) as string;
|
|
78
|
+
const { up } = parseMigrationFile(content);
|
|
79
|
+
|
|
80
|
+
await applyMigration(pool, filename, up, mysqlConfig.migrationsTable as string);
|
|
81
|
+
console.log(` Applied: ${filename}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('All migrations applied.');
|
|
85
|
+
} finally {
|
|
86
|
+
await closePool();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
'db:migrate:rollback': {
|
|
91
|
+
description: 'Rollback the most recent MySQL migration',
|
|
92
|
+
bootstrap: true,
|
|
93
|
+
run: async () => {
|
|
94
|
+
const config = (await import('stonyx/config')).default;
|
|
95
|
+
const mysqlConfig = config.orm.mysql;
|
|
96
|
+
|
|
97
|
+
if (!mysqlConfig) {
|
|
98
|
+
console.error('MySQL is not configured. Set MYSQL_HOST to enable MySQL mode.');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const { getPool, closePool } = await import('./mysql/connection.js');
|
|
103
|
+
const { ensureMigrationsTable, getAppliedMigrations, rollbackMigration, parseMigrationFile } = await import('./mysql/migration-runner.js');
|
|
104
|
+
const { readFile } = await import('@stonyx/utils/file');
|
|
105
|
+
const path = await import('path');
|
|
106
|
+
|
|
107
|
+
const pool = await getPool(mysqlConfig as MysqlConfig);
|
|
108
|
+
const migrationsPath = path.resolve(config.rootPath, mysqlConfig.migrationsDir as string);
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await ensureMigrationsTable(pool, mysqlConfig.migrationsTable as string);
|
|
112
|
+
|
|
113
|
+
const applied = await getAppliedMigrations(pool, mysqlConfig.migrationsTable as string);
|
|
114
|
+
|
|
115
|
+
if (applied.length === 0) {
|
|
116
|
+
console.log('No migrations to rollback.');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const lastFilename = applied[applied.length - 1];
|
|
121
|
+
const content = await readFile(path.join(migrationsPath, lastFilename) as string) as string;
|
|
122
|
+
const { down } = parseMigrationFile(content);
|
|
123
|
+
|
|
124
|
+
if (!down) {
|
|
125
|
+
console.error(`No DOWN section found in ${lastFilename}. Cannot rollback.`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await rollbackMigration(pool, lastFilename, down, mysqlConfig.migrationsTable as string);
|
|
130
|
+
console.log(`Rolled back: ${lastFilename}`);
|
|
131
|
+
} finally {
|
|
132
|
+
await closePool();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
'db:migrate:status': {
|
|
137
|
+
description: 'Show status of MySQL migrations',
|
|
138
|
+
bootstrap: true,
|
|
139
|
+
run: async () => {
|
|
140
|
+
const config = (await import('stonyx/config')).default;
|
|
141
|
+
const mysqlConfig = config.orm.mysql;
|
|
142
|
+
|
|
143
|
+
if (!mysqlConfig) {
|
|
144
|
+
console.error('MySQL is not configured. Set MYSQL_HOST to enable MySQL mode.');
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const { getPool, closePool } = await import('./mysql/connection.js');
|
|
149
|
+
const { ensureMigrationsTable, getAppliedMigrations, getMigrationFiles } = await import('./mysql/migration-runner.js');
|
|
150
|
+
const path = await import('path');
|
|
151
|
+
|
|
152
|
+
const pool = await getPool(mysqlConfig as MysqlConfig);
|
|
153
|
+
const migrationsPath = path.resolve(config.rootPath, mysqlConfig.migrationsDir as string);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await ensureMigrationsTable(pool, mysqlConfig.migrationsTable as string);
|
|
157
|
+
|
|
158
|
+
const applied = new Set(await getAppliedMigrations(pool, mysqlConfig.migrationsTable as string));
|
|
159
|
+
const files = await getMigrationFiles(migrationsPath);
|
|
160
|
+
|
|
161
|
+
if (files.length === 0) {
|
|
162
|
+
console.log('No migration files found.');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log('Migration status:');
|
|
167
|
+
|
|
168
|
+
for (const filename of files) {
|
|
169
|
+
const status = applied.has(filename) ? 'applied' : 'pending';
|
|
170
|
+
console.log(` [${status}] ${filename}`);
|
|
171
|
+
}
|
|
172
|
+
} finally {
|
|
173
|
+
await closePool();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export default commands;
|
package/src/{db.js → db.ts}
RENAMED
|
@@ -17,20 +17,29 @@
|
|
|
17
17
|
import Cron from '@stonyx/cron';
|
|
18
18
|
import config from 'stonyx/config';
|
|
19
19
|
import log from 'stonyx/log';
|
|
20
|
-
import Orm, {
|
|
20
|
+
import Orm, { store } from '@stonyx/orm';
|
|
21
|
+
import { createRecord } from './manage-record.js';
|
|
21
22
|
import { createFile, createDirectory, updateFile, readFile, fileExists } from '@stonyx/utils/file';
|
|
22
23
|
import path from 'path';
|
|
23
24
|
|
|
24
25
|
export const dbKey = '__db';
|
|
25
26
|
|
|
27
|
+
interface DBRecord {
|
|
28
|
+
format(): Record<string, unknown>;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
export default class DB {
|
|
33
|
+
static instance: DB;
|
|
34
|
+
record!: DBRecord;
|
|
35
|
+
|
|
27
36
|
constructor() {
|
|
28
37
|
if (DB.instance) return DB.instance;
|
|
29
38
|
|
|
30
39
|
DB.instance = this;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
async getSchema() {
|
|
42
|
+
async getSchema(): Promise<unknown> {
|
|
34
43
|
const { rootPath } = config;
|
|
35
44
|
const { file, schema } = config.orm.db;
|
|
36
45
|
|
|
@@ -39,10 +48,10 @@ export default class DB {
|
|
|
39
48
|
return (await import(`${rootPath}/${schema}`)).default;
|
|
40
49
|
}
|
|
41
50
|
|
|
42
|
-
getCollectionKeys() {
|
|
43
|
-
const SchemaClass = Orm.instance.models[`${dbKey}Model`]
|
|
51
|
+
getCollectionKeys(): string[] {
|
|
52
|
+
const SchemaClass = (Orm.instance as Orm).models[`${dbKey}Model`] as new () => Record<string, unknown>;
|
|
44
53
|
const instance = new SchemaClass();
|
|
45
|
-
const keys = [];
|
|
54
|
+
const keys: string[] = [];
|
|
46
55
|
|
|
47
56
|
for (const key of Object.keys(instance)) {
|
|
48
57
|
if (key === '__name' || key === 'id') continue;
|
|
@@ -52,7 +61,7 @@ export default class DB {
|
|
|
52
61
|
return keys;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
getDirPath() {
|
|
64
|
+
getDirPath(): string {
|
|
56
65
|
const { rootPath } = config;
|
|
57
66
|
const { file, directory } = config.orm.db;
|
|
58
67
|
const dbDir = path.dirname(path.resolve(`${rootPath}/${file}`));
|
|
@@ -60,7 +69,7 @@ export default class DB {
|
|
|
60
69
|
return path.join(dbDir, directory);
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
async validateMode() {
|
|
72
|
+
async validateMode(): Promise<void> {
|
|
64
73
|
const { rootPath } = config;
|
|
65
74
|
const { file, mode } = config.orm.db;
|
|
66
75
|
const collectionKeys = this.getCollectionKeys();
|
|
@@ -71,11 +80,11 @@ export default class DB {
|
|
|
71
80
|
const exists = await fileExists(dbFilePath);
|
|
72
81
|
|
|
73
82
|
if (exists) {
|
|
74
|
-
const data = await readFile(dbFilePath, { json: true })
|
|
83
|
+
const data = await readFile(dbFilePath, { json: true }) as Record<string, unknown[]>;
|
|
75
84
|
const hasData = collectionKeys.some(key => Array.isArray(data[key]) && data[key].length > 0);
|
|
76
85
|
|
|
77
86
|
if (hasData) {
|
|
78
|
-
log.error(`DB mode mismatch: db.json contains data but mode is set to 'directory'. Run migration first:\n\n stonyx db:migrate-to-directory\n`);
|
|
87
|
+
log.error!(`DB mode mismatch: db.json contains data but mode is set to 'directory'. Run migration first:\n\n stonyx db:migrate-to-directory\n`);
|
|
79
88
|
process.exit(1);
|
|
80
89
|
}
|
|
81
90
|
}
|
|
@@ -88,18 +97,18 @@ export default class DB {
|
|
|
88
97
|
)).some(Boolean);
|
|
89
98
|
|
|
90
99
|
if (hasCollectionFiles) {
|
|
91
|
-
log.error(`DB mode mismatch: directory '${config.orm.db.directory}/' contains collection files but mode is set to 'file'. Run migration first:\n\n stonyx db:migrate-to-file\n`);
|
|
100
|
+
log.error!(`DB mode mismatch: directory '${config.orm.db.directory}/' contains collection files but mode is set to 'file'. Run migration first:\n\n stonyx db:migrate-to-file\n`);
|
|
92
101
|
process.exit(1);
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
async init() {
|
|
107
|
+
async init(): Promise<void> {
|
|
99
108
|
const { autosave, saveInterval } = config.orm.db;
|
|
100
109
|
|
|
101
110
|
store.set(dbKey, new Map());
|
|
102
|
-
Orm.instance.models[`${dbKey}Model`] = await this.getSchema();
|
|
111
|
+
(Orm.instance as Orm).models[`${dbKey}Model`] = await this.getSchema();
|
|
103
112
|
|
|
104
113
|
await this.validateMode();
|
|
105
114
|
this.record = await this.getRecord();
|
|
@@ -109,7 +118,7 @@ export default class DB {
|
|
|
109
118
|
new Cron().register('save', this.save.bind(this), saveInterval);
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
async create() {
|
|
121
|
+
async create(): Promise<Record<string, unknown>> {
|
|
113
122
|
const { rootPath } = config;
|
|
114
123
|
const { file, mode } = config.orm.db;
|
|
115
124
|
|
|
@@ -124,7 +133,7 @@ export default class DB {
|
|
|
124
133
|
));
|
|
125
134
|
|
|
126
135
|
// Write empty-array skeleton to db.json
|
|
127
|
-
const skeleton = {};
|
|
136
|
+
const skeleton: Record<string, unknown[]> = {};
|
|
128
137
|
for (const key of collectionKeys) skeleton[key] = [];
|
|
129
138
|
|
|
130
139
|
await createFile(`${rootPath}/${file}`, skeleton, { json: true });
|
|
@@ -137,9 +146,9 @@ export default class DB {
|
|
|
137
146
|
return {};
|
|
138
147
|
}
|
|
139
148
|
|
|
140
|
-
async save() {
|
|
149
|
+
async save(): Promise<void> {
|
|
141
150
|
const { file, mode } = config.orm.db;
|
|
142
|
-
const jsonData = this.record.format()
|
|
151
|
+
const jsonData = this.record.format() as Record<string, unknown>;
|
|
143
152
|
delete jsonData.id; // Don't save id
|
|
144
153
|
|
|
145
154
|
if (mode === 'directory') {
|
|
@@ -158,7 +167,7 @@ export default class DB {
|
|
|
158
167
|
}));
|
|
159
168
|
|
|
160
169
|
// Write empty-array skeleton to db.json
|
|
161
|
-
const skeleton = {};
|
|
170
|
+
const skeleton: Record<string, unknown[]> = {};
|
|
162
171
|
for (const key of collectionKeys) skeleton[key] = [];
|
|
163
172
|
|
|
164
173
|
const dbFilePath = `${config.rootPath}/${file}`;
|
|
@@ -167,16 +176,16 @@ export default class DB {
|
|
|
167
176
|
if (dbFileExists) await updateFile(dbFilePath, skeleton, { json: true });
|
|
168
177
|
else await createFile(dbFilePath, skeleton, { json: true });
|
|
169
178
|
|
|
170
|
-
log.db(`DB has been successfully saved to ${config.orm.db.directory}/ directory`);
|
|
179
|
+
log.db!(`DB has been successfully saved to ${config.orm.db.directory}/ directory`);
|
|
171
180
|
return;
|
|
172
181
|
}
|
|
173
182
|
|
|
174
183
|
await updateFile(`${config.rootPath}/${file}`, jsonData, { json: true });
|
|
175
184
|
|
|
176
|
-
log.db(`DB has been successfully saved to ${file}`);
|
|
185
|
+
log.db!(`DB has been successfully saved to ${file}`);
|
|
177
186
|
}
|
|
178
187
|
|
|
179
|
-
async getRecord() {
|
|
188
|
+
async getRecord(): Promise<DBRecord> {
|
|
180
189
|
const { mode } = config.orm.db;
|
|
181
190
|
|
|
182
191
|
if (mode === 'directory') return this.getRecordFromDirectory();
|
|
@@ -184,25 +193,25 @@ export default class DB {
|
|
|
184
193
|
return this.getRecordFromFile();
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
async getRecordFromFile() {
|
|
196
|
+
async getRecordFromFile(): Promise<DBRecord> {
|
|
188
197
|
const { file } = config.orm.db;
|
|
189
198
|
|
|
190
199
|
const data = await readFile(file, { json: true, missingFileCallback: this.create.bind(this) });
|
|
191
200
|
|
|
192
|
-
return createRecord(dbKey, data, { isDbRecord: true, serialize: false, transform: false });
|
|
201
|
+
return createRecord(dbKey, data as Record<string, unknown>, { isDbRecord: true, serialize: false, transform: false }) as unknown as DBRecord;
|
|
193
202
|
}
|
|
194
203
|
|
|
195
|
-
async getRecordFromDirectory() {
|
|
204
|
+
async getRecordFromDirectory(): Promise<DBRecord> {
|
|
196
205
|
const dirPath = this.getDirPath();
|
|
197
206
|
const collectionKeys = this.getCollectionKeys();
|
|
198
207
|
const dirExists = await fileExists(dirPath);
|
|
199
208
|
|
|
200
209
|
if (!dirExists) {
|
|
201
210
|
const data = await this.create();
|
|
202
|
-
return createRecord(dbKey, data, { isDbRecord: true, serialize: false, transform: false });
|
|
211
|
+
return createRecord(dbKey, data, { isDbRecord: true, serialize: false, transform: false }) as unknown as DBRecord;
|
|
203
212
|
}
|
|
204
213
|
|
|
205
|
-
const assembled = {};
|
|
214
|
+
const assembled: Record<string, unknown> = {};
|
|
206
215
|
|
|
207
216
|
await Promise.all(collectionKeys.map(async key => {
|
|
208
217
|
const filePath = path.join(dirPath, `${key}.json`);
|
|
@@ -211,6 +220,6 @@ export default class DB {
|
|
|
211
220
|
assembled[key] = exists ? await readFile(filePath, { json: true }) : [];
|
|
212
221
|
}));
|
|
213
222
|
|
|
214
|
-
return createRecord(dbKey, assembled, { isDbRecord: true, serialize: false, transform: false });
|
|
223
|
+
return createRecord(dbKey, assembled, { isDbRecord: true, serialize: false, transform: false }) as unknown as DBRecord;
|
|
215
224
|
}
|
|
216
225
|
}
|
package/src/has-many.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createRecord, store } from '@stonyx/orm';
|
|
2
|
+
import { getRelationships, getGlobalRegistry, getPendingRegistry, getBelongsToRegistry } from './relationships.js';
|
|
3
|
+
import { getOrSet, makeArray } from '@stonyx/utils/object';
|
|
4
|
+
import { dbKey } from './db.js';
|
|
5
|
+
import type { SourceRecord } from './types/orm-types.js';
|
|
6
|
+
|
|
7
|
+
interface HasManyOptions {
|
|
8
|
+
global?: boolean;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface PendingItem {
|
|
13
|
+
pendingRelationship: Map<unknown, unknown[][]>;
|
|
14
|
+
id: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type RelationshipHandler = ((sourceRecord: SourceRecord, rawData: unknown, options: HasManyOptions) => unknown[]) & {
|
|
18
|
+
__relatedModelName: string;
|
|
19
|
+
__relationshipType: 'hasMany';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function queuePendingRelationship(
|
|
23
|
+
pendingRelationshipQueue: PendingItem[],
|
|
24
|
+
pendingRelationships: Map<string, Map<unknown, unknown[][]>>,
|
|
25
|
+
modelName: string,
|
|
26
|
+
id: unknown
|
|
27
|
+
): null {
|
|
28
|
+
pendingRelationshipQueue.push({
|
|
29
|
+
pendingRelationship: getOrSet(pendingRelationships, modelName, new Map()),
|
|
30
|
+
id
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default function hasMany(modelName: string): RelationshipHandler {
|
|
37
|
+
const globalRelationships = getGlobalRegistry();
|
|
38
|
+
const pendingRelationships = getPendingRegistry();
|
|
39
|
+
|
|
40
|
+
const fn = (sourceRecord: SourceRecord, rawData: unknown, options: HasManyOptions): unknown[] => {
|
|
41
|
+
const { __name: sourceModelName } = sourceRecord.__model;
|
|
42
|
+
const relationshipId = sourceRecord.id;
|
|
43
|
+
const relationship = getRelationships('hasMany', sourceModelName, modelName, relationshipId as string) as Map<unknown, unknown[]>;
|
|
44
|
+
const modelStore = store.get(modelName);
|
|
45
|
+
const pendingRelationshipQueue: PendingItem[] = [];
|
|
46
|
+
|
|
47
|
+
const output: unknown[] = !rawData ? [] : (makeArray(rawData) as unknown[]).map((elementData: unknown) => {
|
|
48
|
+
let record: unknown;
|
|
49
|
+
|
|
50
|
+
if (typeof elementData !== 'object') {
|
|
51
|
+
if (!modelStore) {
|
|
52
|
+
return queuePendingRelationship(pendingRelationshipQueue, pendingRelationships, modelName, elementData);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
record = modelStore.get(elementData as number | string);
|
|
56
|
+
|
|
57
|
+
if (!record) {
|
|
58
|
+
return queuePendingRelationship(pendingRelationshipQueue, pendingRelationships, modelName, elementData);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
if (elementData !== Object(elementData)) {
|
|
62
|
+
return queuePendingRelationship(pendingRelationshipQueue, pendingRelationships, modelName, elementData);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
record = createRecord(modelName, elementData as Record<string, unknown>, options);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Populate belongTo side if the relationship is defined
|
|
69
|
+
const otherSide = getBelongsToRegistry()
|
|
70
|
+
.get(modelName)?.get(sourceModelName)?.get((record as SourceRecord).id);
|
|
71
|
+
|
|
72
|
+
if (otherSide) Object.assign(otherSide, sourceRecord);
|
|
73
|
+
|
|
74
|
+
return record;
|
|
75
|
+
}).filter((value: unknown) => value);
|
|
76
|
+
|
|
77
|
+
relationship.set(relationshipId, output);
|
|
78
|
+
|
|
79
|
+
// Assign global relationship
|
|
80
|
+
if (options.global || sourceModelName === dbKey) getOrSet(globalRelationships, modelName, []).push(output);
|
|
81
|
+
|
|
82
|
+
// Assign pending relationships
|
|
83
|
+
for (const { pendingRelationship, id } of pendingRelationshipQueue) getOrSet(pendingRelationship, id, []).push(output);
|
|
84
|
+
|
|
85
|
+
return output;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
Object.defineProperty(fn, '__relatedModelName', { value: modelName });
|
|
89
|
+
Object.defineProperty(fn, '__relationshipType', { value: 'hasMany' as const });
|
|
90
|
+
return fn as RelationshipHandler;
|
|
91
|
+
}
|