@stonyx/orm 0.2.1-beta.83 → 0.2.1-beta.85

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.
Files changed (150) hide show
  1. package/dist/aggregates.d.ts +21 -0
  2. package/dist/aggregates.js +90 -0
  3. package/dist/attr.d.ts +2 -0
  4. package/dist/attr.js +22 -0
  5. package/dist/belongs-to.d.ts +11 -0
  6. package/dist/belongs-to.js +59 -0
  7. package/dist/cli.d.ts +22 -0
  8. package/dist/cli.js +148 -0
  9. package/dist/commands.d.ts +7 -0
  10. package/dist/commands.js +146 -0
  11. package/dist/db.d.ts +21 -0
  12. package/dist/db.js +174 -0
  13. package/dist/exports/db.d.ts +7 -0
  14. package/{src → dist}/exports/db.js +2 -4
  15. package/dist/has-many.d.ts +11 -0
  16. package/dist/has-many.js +58 -0
  17. package/dist/hooks.d.ts +47 -0
  18. package/dist/hooks.js +106 -0
  19. package/dist/index.d.ts +14 -0
  20. package/dist/index.js +34 -0
  21. package/dist/main.d.ts +46 -0
  22. package/dist/main.js +179 -0
  23. package/dist/manage-record.d.ts +13 -0
  24. package/dist/manage-record.js +114 -0
  25. package/dist/meta-request.d.ts +6 -0
  26. package/dist/meta-request.js +52 -0
  27. package/dist/migrate.d.ts +2 -0
  28. package/dist/migrate.js +57 -0
  29. package/dist/model-property.d.ts +9 -0
  30. package/dist/model-property.js +29 -0
  31. package/dist/model.d.ts +15 -0
  32. package/dist/model.js +18 -0
  33. package/dist/mysql/connection.d.ts +14 -0
  34. package/dist/mysql/connection.js +24 -0
  35. package/dist/mysql/migration-generator.d.ts +45 -0
  36. package/dist/mysql/migration-generator.js +245 -0
  37. package/dist/mysql/migration-runner.d.ts +12 -0
  38. package/dist/mysql/migration-runner.js +83 -0
  39. package/dist/mysql/mysql-db.d.ts +100 -0
  40. package/dist/mysql/mysql-db.js +415 -0
  41. package/dist/mysql/query-builder.d.ts +10 -0
  42. package/dist/mysql/query-builder.js +44 -0
  43. package/dist/mysql/schema-introspector.d.ts +19 -0
  44. package/dist/mysql/schema-introspector.js +286 -0
  45. package/dist/mysql/type-map.d.ts +21 -0
  46. package/dist/mysql/type-map.js +36 -0
  47. package/dist/orm-request.d.ts +38 -0
  48. package/dist/orm-request.js +455 -0
  49. package/dist/plural-registry.d.ts +4 -0
  50. package/{src → dist}/plural-registry.js +3 -6
  51. package/dist/postgres/connection.d.ts +15 -0
  52. package/dist/postgres/connection.js +30 -0
  53. package/dist/postgres/migration-generator.d.ts +45 -0
  54. package/dist/postgres/migration-generator.js +257 -0
  55. package/dist/postgres/migration-runner.d.ts +10 -0
  56. package/dist/postgres/migration-runner.js +82 -0
  57. package/dist/postgres/postgres-db.d.ts +119 -0
  58. package/dist/postgres/postgres-db.js +476 -0
  59. package/dist/postgres/query-builder.d.ts +27 -0
  60. package/dist/postgres/query-builder.js +98 -0
  61. package/dist/postgres/schema-introspector.d.ts +29 -0
  62. package/dist/postgres/schema-introspector.js +309 -0
  63. package/dist/postgres/type-map.d.ts +23 -0
  64. package/dist/postgres/type-map.js +53 -0
  65. package/dist/record.d.ts +75 -0
  66. package/dist/record.js +115 -0
  67. package/dist/relationships.d.ts +10 -0
  68. package/dist/relationships.js +39 -0
  69. package/dist/serializer.d.ts +17 -0
  70. package/dist/serializer.js +136 -0
  71. package/dist/setup-rest-server.d.ts +1 -0
  72. package/dist/setup-rest-server.js +54 -0
  73. package/dist/standalone-db.d.ts +58 -0
  74. package/dist/standalone-db.js +142 -0
  75. package/dist/store.d.ts +62 -0
  76. package/dist/store.js +271 -0
  77. package/dist/timescale/query-builder.d.ts +41 -0
  78. package/dist/timescale/query-builder.js +87 -0
  79. package/dist/timescale/timescale-db.d.ts +45 -0
  80. package/dist/timescale/timescale-db.js +84 -0
  81. package/dist/transforms.d.ts +2 -0
  82. package/dist/transforms.js +17 -0
  83. package/dist/types/orm-types.d.ts +142 -0
  84. package/dist/types/orm-types.js +1 -0
  85. package/dist/utils.d.ts +5 -0
  86. package/dist/utils.js +13 -0
  87. package/dist/view-resolver.d.ts +8 -0
  88. package/dist/view-resolver.js +169 -0
  89. package/dist/view.d.ts +11 -0
  90. package/dist/view.js +18 -0
  91. package/package.json +34 -11
  92. package/src/{aggregates.js → aggregates.ts} +27 -13
  93. package/src/{attr.js → attr.ts} +2 -2
  94. package/src/belongs-to.ts +90 -0
  95. package/src/{cli.js → cli.ts} +17 -11
  96. package/src/{commands.js → commands.ts} +179 -170
  97. package/src/{db.js → db.ts} +35 -26
  98. package/src/exports/db.ts +7 -0
  99. package/src/has-many.ts +92 -0
  100. package/src/{hooks.js → hooks.ts} +23 -27
  101. package/src/{index.js → index.ts} +4 -4
  102. package/src/{main.js → main.ts} +60 -34
  103. package/src/{manage-record.js → manage-record.ts} +42 -22
  104. package/src/{meta-request.js → meta-request.ts} +17 -14
  105. package/src/{migrate.js → migrate.ts} +9 -9
  106. package/src/{model-property.js → model-property.ts} +12 -6
  107. package/src/{model.js → model.ts} +5 -4
  108. package/src/mysql/{connection.js → connection.ts} +43 -28
  109. package/src/mysql/{migration-generator.js → migration-generator.ts} +332 -286
  110. package/src/mysql/{migration-runner.js → migration-runner.ts} +116 -110
  111. package/src/mysql/{mysql-db.js → mysql-db.ts} +537 -473
  112. package/src/mysql/{query-builder.js → query-builder.ts} +69 -64
  113. package/src/mysql/{schema-introspector.js → schema-introspector.ts} +355 -325
  114. package/src/mysql/{type-map.js → type-map.ts} +42 -37
  115. package/src/{orm-request.js → orm-request.ts} +169 -97
  116. package/src/plural-registry.ts +12 -0
  117. package/src/postgres/{connection.js → connection.ts} +14 -5
  118. package/src/postgres/{migration-generator.js → migration-generator.ts} +82 -38
  119. package/src/postgres/{migration-runner.js → migration-runner.ts} +11 -10
  120. package/src/postgres/{postgres-db.js → postgres-db.ts} +198 -114
  121. package/src/postgres/{query-builder.js → query-builder.ts} +27 -28
  122. package/src/postgres/{schema-introspector.js → schema-introspector.ts} +87 -58
  123. package/src/postgres/{type-map.js → type-map.ts} +10 -6
  124. package/src/{record.js → record.ts} +73 -34
  125. package/src/relationships.ts +53 -0
  126. package/src/{serializer.js → serializer.ts} +52 -36
  127. package/src/{setup-rest-server.js → setup-rest-server.ts} +18 -13
  128. package/src/{standalone-db.js → standalone-db.ts} +33 -24
  129. package/src/{store.js → store.ts} +90 -68
  130. package/src/timescale/{query-builder.js → query-builder.ts} +33 -38
  131. package/src/timescale/timescale-db.ts +119 -0
  132. package/src/transforms.ts +20 -0
  133. package/src/types/mysql2.d.ts +30 -0
  134. package/src/types/orm-types.ts +146 -0
  135. package/src/types/pg.d.ts +28 -0
  136. package/src/types/stonyx-cron.d.ts +5 -0
  137. package/src/types/stonyx-events.d.ts +4 -0
  138. package/src/types/stonyx-rest-server.d.ts +11 -0
  139. package/src/types/stonyx-utils.d.ts +33 -0
  140. package/src/types/stonyx.d.ts +21 -0
  141. package/src/utils.ts +16 -0
  142. package/src/{view-resolver.js → view-resolver.ts} +51 -24
  143. package/src/view.ts +22 -0
  144. package/src/belongs-to.js +0 -70
  145. package/src/has-many.js +0 -68
  146. package/src/relationships.js +0 -43
  147. package/src/timescale/timescale-db.js +0 -111
  148. package/src/transforms.js +0 -20
  149. package/src/utils.js +0 -12
  150. package/src/view.js +0 -21
@@ -1,170 +1,179 @@
1
- import { fileToDirectory, directoryToFile } from './migrate.js';
2
-
3
- export default {
4
- 'db:migrate-to-directory': {
5
- description: 'Migrate DB from single file to directory mode',
6
- bootstrap: true,
7
- run: async () => {
8
- await fileToDirectory();
9
- console.log('DB migration to directory mode complete.');
10
- }
11
- },
12
- 'db:migrate-to-file': {
13
- description: 'Migrate DB from directory mode to single file',
14
- bootstrap: true,
15
- run: async () => {
16
- await directoryToFile();
17
- console.log('DB migration to file mode complete.');
18
- }
19
- },
20
- 'db:generate-migration': {
21
- description: 'Generate a MySQL migration from current model schemas',
22
- bootstrap: true,
23
- run: async (args) => {
24
- const description = args.join(' ') || 'migration';
25
- const { generateMigration } = await import('./mysql/migration-generator.js');
26
- const result = await generateMigration(description);
27
-
28
- if (result) {
29
- console.log(`Migration created: ${result.filename}`);
30
- } else {
31
- console.log('No schema changes detected. No migration generated.');
32
- }
33
- }
34
- },
35
- 'db:migrate': {
36
- description: 'Apply pending MySQL migrations',
37
- bootstrap: true,
38
- run: async () => {
39
- const config = (await import('stonyx/config')).default;
40
- const mysqlConfig = config.orm.mysql;
41
-
42
- if (!mysqlConfig) {
43
- console.error('MySQL is not configured. Set MYSQL_HOST to enable MySQL mode.');
44
- process.exit(1);
45
- }
46
-
47
- const { getPool, closePool } = await import('./mysql/connection.js');
48
- const { ensureMigrationsTable, getAppliedMigrations, getMigrationFiles, applyMigration, parseMigrationFile } = await import('./mysql/migration-runner.js');
49
- const { readFile } = await import('@stonyx/utils/file');
50
- const path = await import('path');
51
-
52
- const pool = await getPool(mysqlConfig);
53
- const migrationsPath = path.resolve(config.rootPath, mysqlConfig.migrationsDir);
54
-
55
- try {
56
- await ensureMigrationsTable(pool, mysqlConfig.migrationsTable);
57
-
58
- const applied = await getAppliedMigrations(pool, mysqlConfig.migrationsTable);
59
- const files = await getMigrationFiles(migrationsPath);
60
- const pending = files.filter(f => !applied.includes(f));
61
-
62
- if (pending.length === 0) {
63
- console.log('No pending migrations.');
64
- return;
65
- }
66
-
67
- console.log(`Applying ${pending.length} migration(s)...`);
68
-
69
- for (const filename of pending) {
70
- const content = await readFile(path.join(migrationsPath, filename));
71
- const { up } = parseMigrationFile(content);
72
-
73
- await applyMigration(pool, filename, up, mysqlConfig.migrationsTable);
74
- console.log(` Applied: ${filename}`);
75
- }
76
-
77
- console.log('All migrations applied.');
78
- } finally {
79
- await closePool();
80
- }
81
- }
82
- },
83
- 'db:migrate:rollback': {
84
- description: 'Rollback the most recent MySQL migration',
85
- bootstrap: true,
86
- run: async () => {
87
- const config = (await import('stonyx/config')).default;
88
- const mysqlConfig = config.orm.mysql;
89
-
90
- if (!mysqlConfig) {
91
- console.error('MySQL is not configured. Set MYSQL_HOST to enable MySQL mode.');
92
- process.exit(1);
93
- }
94
-
95
- const { getPool, closePool } = await import('./mysql/connection.js');
96
- const { ensureMigrationsTable, getAppliedMigrations, rollbackMigration, parseMigrationFile } = await import('./mysql/migration-runner.js');
97
- const { readFile } = await import('@stonyx/utils/file');
98
- const path = await import('path');
99
-
100
- const pool = await getPool(mysqlConfig);
101
- const migrationsPath = path.resolve(config.rootPath, mysqlConfig.migrationsDir);
102
-
103
- try {
104
- await ensureMigrationsTable(pool, mysqlConfig.migrationsTable);
105
-
106
- const applied = await getAppliedMigrations(pool, mysqlConfig.migrationsTable);
107
-
108
- if (applied.length === 0) {
109
- console.log('No migrations to rollback.');
110
- return;
111
- }
112
-
113
- const lastFilename = applied[applied.length - 1];
114
- const content = await readFile(path.join(migrationsPath, lastFilename));
115
- const { down } = parseMigrationFile(content);
116
-
117
- if (!down) {
118
- console.error(`No DOWN section found in ${lastFilename}. Cannot rollback.`);
119
- process.exit(1);
120
- }
121
-
122
- await rollbackMigration(pool, lastFilename, down, mysqlConfig.migrationsTable);
123
- console.log(`Rolled back: ${lastFilename}`);
124
- } finally {
125
- await closePool();
126
- }
127
- }
128
- },
129
- 'db:migrate:status': {
130
- description: 'Show status of MySQL migrations',
131
- bootstrap: true,
132
- run: async () => {
133
- const config = (await import('stonyx/config')).default;
134
- const mysqlConfig = config.orm.mysql;
135
-
136
- if (!mysqlConfig) {
137
- console.error('MySQL is not configured. Set MYSQL_HOST to enable MySQL mode.');
138
- process.exit(1);
139
- }
140
-
141
- const { getPool, closePool } = await import('./mysql/connection.js');
142
- const { ensureMigrationsTable, getAppliedMigrations, getMigrationFiles } = await import('./mysql/migration-runner.js');
143
- const path = await import('path');
144
-
145
- const pool = await getPool(mysqlConfig);
146
- const migrationsPath = path.resolve(config.rootPath, mysqlConfig.migrationsDir);
147
-
148
- try {
149
- await ensureMigrationsTable(pool, mysqlConfig.migrationsTable);
150
-
151
- const applied = new Set(await getAppliedMigrations(pool, mysqlConfig.migrationsTable));
152
- const files = await getMigrationFiles(migrationsPath);
153
-
154
- if (files.length === 0) {
155
- console.log('No migration files found.');
156
- return;
157
- }
158
-
159
- console.log('Migration status:');
160
-
161
- for (const filename of files) {
162
- const status = applied.has(filename) ? 'applied' : 'pending';
163
- console.log(` [${status}] ${filename}`);
164
- }
165
- } finally {
166
- await closePool();
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;
@@ -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, { createRecord, store } from '@stonyx/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
  }
@@ -0,0 +1,7 @@
1
+ import Orm from '@stonyx/orm';
2
+
3
+ const db = Orm.db as { record: unknown; save(): Promise<void> };
4
+
5
+ export default db;
6
+ export const data = db.record;
7
+ export const saveDB = db.save.bind(db);
@@ -0,0 +1,92 @@
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).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 recordWithId = typeof record === 'object' && record !== null && 'id' in record ? record as SourceRecord : undefined;
70
+ const otherSide = recordWithId ? getBelongsToRegistry()
71
+ .get(modelName)?.get(sourceModelName)?.get(recordWithId.id) : undefined;
72
+
73
+ if (otherSide) Object.assign(otherSide, sourceRecord);
74
+
75
+ return record;
76
+ }).filter((value: unknown) => value);
77
+
78
+ relationship.set(relationshipId, output);
79
+
80
+ // Assign global relationship
81
+ if (options.global || sourceModelName === dbKey) getOrSet(globalRelationships, modelName, []).push(output);
82
+
83
+ // Assign pending relationships
84
+ for (const { pendingRelationship, id } of pendingRelationshipQueue) getOrSet(pendingRelationship, id, []).push(output);
85
+
86
+ return output;
87
+ };
88
+
89
+ Object.defineProperty(fn, '__relatedModelName', { value: modelName });
90
+ Object.defineProperty(fn, '__relationshipType', { value: 'hasMany' as const });
91
+ return fn as RelationshipHandler;
92
+ }