@engjts/nexus 0.1.8 → 0.1.9

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 (205) hide show
  1. package/package.json +1 -1
  2. package/BENCHMARK_REPORT.md +0 -343
  3. package/documentation/01-getting-started.md +0 -240
  4. package/documentation/02-context.md +0 -335
  5. package/documentation/03-routing.md +0 -397
  6. package/documentation/04-middleware.md +0 -483
  7. package/documentation/05-validation.md +0 -514
  8. package/documentation/06-error-handling.md +0 -465
  9. package/documentation/07-performance.md +0 -364
  10. package/documentation/08-adapters.md +0 -470
  11. package/documentation/09-api-reference.md +0 -548
  12. package/documentation/10-examples.md +0 -582
  13. package/documentation/11-deployment.md +0 -477
  14. package/documentation/12-sentry.md +0 -620
  15. package/documentation/13-sentry-data-storage.md +0 -996
  16. package/documentation/14-sentry-data-reference.md +0 -457
  17. package/documentation/15-sentry-summary.md +0 -409
  18. package/documentation/16-alerts-system.md +0 -745
  19. package/documentation/17-alert-adapters.md +0 -696
  20. package/documentation/18-alerts-implementation-summary.md +0 -385
  21. package/documentation/19-class-based-routing.md +0 -840
  22. package/documentation/20-websocket-realtime.md +0 -813
  23. package/documentation/21-cache-system.md +0 -510
  24. package/documentation/22-job-queue.md +0 -772
  25. package/documentation/23-sentry-plugin.md +0 -551
  26. package/documentation/24-testing-utilities.md +0 -1287
  27. package/documentation/25-api-versioning.md +0 -533
  28. package/documentation/26-context-store.md +0 -607
  29. package/documentation/27-dependency-injection.md +0 -329
  30. package/documentation/28-lifecycle-hooks.md +0 -521
  31. package/documentation/29-package-structure.md +0 -196
  32. package/documentation/30-plugin-system.md +0 -414
  33. package/documentation/31-jwt-authentication.md +0 -597
  34. package/documentation/32-cli.md +0 -268
  35. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  36. package/documentation/ALERTS-INDEX.md +0 -330
  37. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  38. package/documentation/README.md +0 -178
  39. package/documentation/index.html +0 -34
  40. package/modern_framework_paper.md +0 -1870
  41. package/public/css/style.css +0 -87
  42. package/public/index.html +0 -34
  43. package/public/js/app.js +0 -27
  44. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  45. package/src/advanced/cache/MultiTierCache.ts +0 -194
  46. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  47. package/src/advanced/cache/index.ts +0 -5
  48. package/src/advanced/cache/types.ts +0 -40
  49. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  50. package/src/advanced/graphql/index.ts +0 -22
  51. package/src/advanced/graphql/server.ts +0 -252
  52. package/src/advanced/graphql/types.ts +0 -42
  53. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  54. package/src/advanced/jobs/JobQueue.ts +0 -556
  55. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  56. package/src/advanced/jobs/index.ts +0 -5
  57. package/src/advanced/jobs/types.ts +0 -70
  58. package/src/advanced/observability/APMManager.ts +0 -163
  59. package/src/advanced/observability/AlertManager.ts +0 -109
  60. package/src/advanced/observability/MetricRegistry.ts +0 -151
  61. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  62. package/src/advanced/observability/StructuredLogger.ts +0 -154
  63. package/src/advanced/observability/TracingManager.ts +0 -117
  64. package/src/advanced/observability/adapters.ts +0 -304
  65. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  66. package/src/advanced/observability/index.ts +0 -11
  67. package/src/advanced/observability/types.ts +0 -174
  68. package/src/advanced/playground/extractPathParams.ts +0 -6
  69. package/src/advanced/playground/generateFieldExample.ts +0 -31
  70. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  71. package/src/advanced/playground/generateSummary.ts +0 -19
  72. package/src/advanced/playground/getTagFromPath.ts +0 -9
  73. package/src/advanced/playground/index.ts +0 -8
  74. package/src/advanced/playground/playground.ts +0 -250
  75. package/src/advanced/playground/types.ts +0 -49
  76. package/src/advanced/playground/zodToExample.ts +0 -16
  77. package/src/advanced/playground/zodToParams.ts +0 -15
  78. package/src/advanced/postman/buildAuth.ts +0 -31
  79. package/src/advanced/postman/buildBody.ts +0 -15
  80. package/src/advanced/postman/buildQueryParams.ts +0 -27
  81. package/src/advanced/postman/buildRequestItem.ts +0 -36
  82. package/src/advanced/postman/buildResponses.ts +0 -11
  83. package/src/advanced/postman/buildUrl.ts +0 -33
  84. package/src/advanced/postman/capitalize.ts +0 -4
  85. package/src/advanced/postman/generateCollection.ts +0 -59
  86. package/src/advanced/postman/generateEnvironment.ts +0 -34
  87. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  88. package/src/advanced/postman/generateFieldExample.ts +0 -45
  89. package/src/advanced/postman/generateName.ts +0 -20
  90. package/src/advanced/postman/generateUUID.ts +0 -11
  91. package/src/advanced/postman/getTagFromPath.ts +0 -10
  92. package/src/advanced/postman/index.ts +0 -28
  93. package/src/advanced/postman/postman.ts +0 -156
  94. package/src/advanced/postman/slugify.ts +0 -7
  95. package/src/advanced/postman/types.ts +0 -140
  96. package/src/advanced/realtime/index.ts +0 -18
  97. package/src/advanced/realtime/websocket.ts +0 -231
  98. package/src/advanced/sentry/index.ts +0 -1236
  99. package/src/advanced/sentry/types.ts +0 -355
  100. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  101. package/src/advanced/static/generateETag.ts +0 -7
  102. package/src/advanced/static/getMimeType.ts +0 -9
  103. package/src/advanced/static/index.ts +0 -32
  104. package/src/advanced/static/isSafePath.ts +0 -13
  105. package/src/advanced/static/publicDir.ts +0 -21
  106. package/src/advanced/static/serveStatic.ts +0 -225
  107. package/src/advanced/static/spa.ts +0 -24
  108. package/src/advanced/static/types.ts +0 -159
  109. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  110. package/src/advanced/swagger/buildOperation.ts +0 -61
  111. package/src/advanced/swagger/buildParameters.ts +0 -61
  112. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  113. package/src/advanced/swagger/buildResponses.ts +0 -54
  114. package/src/advanced/swagger/capitalize.ts +0 -5
  115. package/src/advanced/swagger/convertPath.ts +0 -9
  116. package/src/advanced/swagger/createSwagger.ts +0 -12
  117. package/src/advanced/swagger/generateOperationId.ts +0 -21
  118. package/src/advanced/swagger/generateSpec.ts +0 -105
  119. package/src/advanced/swagger/generateSummary.ts +0 -24
  120. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  121. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  122. package/src/advanced/swagger/index.ts +0 -25
  123. package/src/advanced/swagger/swagger.ts +0 -237
  124. package/src/advanced/swagger/types.ts +0 -206
  125. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  126. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  127. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  128. package/src/advanced/testing/factory.ts +0 -509
  129. package/src/advanced/testing/harness.ts +0 -612
  130. package/src/advanced/testing/index.ts +0 -430
  131. package/src/advanced/testing/load-test.ts +0 -618
  132. package/src/advanced/testing/mock-server.ts +0 -498
  133. package/src/advanced/testing/mock.ts +0 -670
  134. package/src/cli/bin.ts +0 -9
  135. package/src/cli/cli.ts +0 -158
  136. package/src/cli/commands/add.ts +0 -178
  137. package/src/cli/commands/build.ts +0 -73
  138. package/src/cli/commands/create.ts +0 -166
  139. package/src/cli/commands/dev.ts +0 -85
  140. package/src/cli/commands/generate.ts +0 -99
  141. package/src/cli/commands/help.ts +0 -95
  142. package/src/cli/commands/init.ts +0 -91
  143. package/src/cli/commands/version.ts +0 -38
  144. package/src/cli/index.ts +0 -6
  145. package/src/cli/templates/generators.ts +0 -359
  146. package/src/cli/templates/index.ts +0 -680
  147. package/src/cli/utils/exec.ts +0 -52
  148. package/src/cli/utils/file-system.ts +0 -78
  149. package/src/cli/utils/logger.ts +0 -111
  150. package/src/core/adapter.ts +0 -88
  151. package/src/core/application.ts +0 -1453
  152. package/src/core/context-pool.ts +0 -79
  153. package/src/core/context.ts +0 -856
  154. package/src/core/index.ts +0 -94
  155. package/src/core/middleware.ts +0 -272
  156. package/src/core/performance/buffer-pool.ts +0 -108
  157. package/src/core/performance/middleware-optimizer.ts +0 -162
  158. package/src/core/plugin/PluginManager.ts +0 -435
  159. package/src/core/plugin/builder.ts +0 -358
  160. package/src/core/plugin/index.ts +0 -50
  161. package/src/core/plugin/types.ts +0 -214
  162. package/src/core/router/file-router.ts +0 -623
  163. package/src/core/router/index.ts +0 -260
  164. package/src/core/router/radix-tree.ts +0 -242
  165. package/src/core/serializer.ts +0 -397
  166. package/src/core/store/index.ts +0 -30
  167. package/src/core/store/registry.ts +0 -178
  168. package/src/core/store/request-store.ts +0 -240
  169. package/src/core/store/types.ts +0 -233
  170. package/src/core/types.ts +0 -616
  171. package/src/database/adapter.ts +0 -35
  172. package/src/database/adapters/index.ts +0 -1
  173. package/src/database/adapters/mysql.ts +0 -669
  174. package/src/database/database.ts +0 -70
  175. package/src/database/dialect.ts +0 -388
  176. package/src/database/index.ts +0 -12
  177. package/src/database/migrations.ts +0 -86
  178. package/src/database/optimizer.ts +0 -125
  179. package/src/database/query-builder.ts +0 -404
  180. package/src/database/realtime.ts +0 -53
  181. package/src/database/schema.ts +0 -71
  182. package/src/database/transactions.ts +0 -56
  183. package/src/database/types.ts +0 -87
  184. package/src/deployment/cluster.ts +0 -471
  185. package/src/deployment/config.ts +0 -454
  186. package/src/deployment/docker.ts +0 -599
  187. package/src/deployment/graceful-shutdown.ts +0 -373
  188. package/src/deployment/index.ts +0 -56
  189. package/src/index.ts +0 -281
  190. package/src/security/adapter.ts +0 -318
  191. package/src/security/auth/JWTPlugin.ts +0 -234
  192. package/src/security/auth/JWTProvider.ts +0 -316
  193. package/src/security/auth/adapter.ts +0 -12
  194. package/src/security/auth/jwt.ts +0 -234
  195. package/src/security/auth/middleware.ts +0 -188
  196. package/src/security/csrf.ts +0 -220
  197. package/src/security/headers.ts +0 -108
  198. package/src/security/index.ts +0 -60
  199. package/src/security/rate-limit/adapter.ts +0 -7
  200. package/src/security/rate-limit/memory.ts +0 -108
  201. package/src/security/rate-limit/middleware.ts +0 -181
  202. package/src/security/sanitization.ts +0 -75
  203. package/src/security/types.ts +0 -240
  204. package/src/security/utils.ts +0 -52
  205. package/tsconfig.json +0 -39
@@ -1,70 +0,0 @@
1
- import { DatabaseAdapter, MigrationAdapter } from './adapter';
2
- import { MigrationCLI } from './migrations';
3
- import { QueryOptimizer, QueryOptimizerOptions } from './optimizer';
4
- import { QueryBuilder } from './query-builder';
5
- import { RealtimeConfig, RealtimeDatabase } from './realtime';
6
- import { SchemaDefinition, TableDefinition, DatabaseOptions, QueryContext } from './types';
7
- import { TransactionManager } from './transactions';
8
-
9
- export interface DatabaseConfig extends DatabaseOptions {
10
- optimizer?: QueryOptimizerOptions;
11
- realtime?: RealtimeConfig;
12
- }
13
-
14
- export class Database<TSchema extends SchemaDefinition> {
15
- private readonly optimizer: QueryOptimizer;
16
- private readonly realtime: RealtimeDatabase;
17
- private readonly transactions: TransactionManager;
18
-
19
- constructor(
20
- private readonly schema: TSchema,
21
- private readonly adapter: DatabaseAdapter,
22
- options: DatabaseConfig = {}
23
- ) {
24
- this.optimizer = new QueryOptimizer(schema, options.optimizer);
25
- this.realtime = new RealtimeDatabase(options.realtime);
26
- this.transactions = new TransactionManager(adapter);
27
- }
28
-
29
- table<TName extends keyof TSchema & string>(name: TName): QueryBuilder<TSchema, TName> {
30
- const definition = this.getTable(name);
31
- return new QueryBuilder<TSchema, TName>({
32
- schema: this.schema,
33
- table: definition,
34
- adapter: this.adapter,
35
- optimizer: this.optimizer,
36
- realtime: this.realtime
37
- });
38
- }
39
-
40
- async transaction<T>(callback: (db: this) => Promise<T>, context?: QueryContext) {
41
- return this.transactions.run(async () => callback(this), {
42
- label: context?.label
43
- });
44
- }
45
-
46
- migrations(options?: { directory?: string }) {
47
- const adapter = this.adapter as MigrationAdapter;
48
- if (!('runMigration' in adapter)) {
49
- throw new Error('Adapter does not implement migration capabilities');
50
- }
51
- return new MigrationCLI(adapter, options);
52
- }
53
-
54
- realtimeChanges() {
55
- return this.realtime;
56
- }
57
-
58
- getOptimizer() {
59
- return this.optimizer;
60
- }
61
-
62
- private getTable<TName extends keyof TSchema & string>(name: TName): TableDefinition {
63
- const table = this.schema[name];
64
- if (!table) {
65
- throw new Error(`Table "${name}" is not registered in the schema`);
66
- }
67
- return table;
68
- }
69
- }
70
-
@@ -1,388 +0,0 @@
1
- /**
2
- * SQL Dialect abstraction for cross-database compatibility.
3
- * Handles differences in SQL syntax between MySQL and PostgreSQL.
4
- */
5
-
6
- export interface SQLDialect {
7
- /** Dialect name identifier */
8
- readonly name: 'mysql' | 'postgresql' | 'sqlite';
9
-
10
- /** Quote an identifier (table/column name) */
11
- quoteIdentifier(identifier: string): string;
12
-
13
- /** Create a parameter placeholder for the given index (1-based) */
14
- paramPlaceholder(index: number): string;
15
-
16
- /** Format a LIMIT/OFFSET clause */
17
- limitOffset(limit?: number, offset?: number): string;
18
-
19
- /** Format RETURNING clause for INSERT/UPDATE/DELETE */
20
- returning(columns?: string[]): string;
21
-
22
- /** Whether the dialect supports RETURNING */
23
- supportsReturning: boolean;
24
-
25
- /** Format boolean value */
26
- formatBoolean(value: boolean): string;
27
-
28
- /** Format date/timestamp for insertion */
29
- formatDate(date: Date): string;
30
-
31
- /** Format JSON value */
32
- formatJson(value: unknown): string;
33
-
34
- /** Get current timestamp expression */
35
- currentTimestamp(): string;
36
-
37
- /** Get auto-increment column definition */
38
- autoIncrement(): string;
39
-
40
- /** Get primary key column type */
41
- serialType(): string;
42
-
43
- /** String concatenation operator or function */
44
- concat(...parts: string[]): string;
45
-
46
- /** ILIKE or case-insensitive LIKE support */
47
- ilike(column: string, pattern: string): string;
48
-
49
- /** JSON extraction operator/function */
50
- jsonExtract(column: string, path: string): string;
51
-
52
- /** Upsert (INSERT ... ON CONFLICT/DUPLICATE) syntax */
53
- upsert(
54
- table: string,
55
- columns: string[],
56
- values: string[],
57
- conflictColumns: string[],
58
- updateColumns: string[]
59
- ): string;
60
- }
61
-
62
- /**
63
- * MySQL Dialect implementation
64
- */
65
- export class MySQLDialect implements SQLDialect {
66
- readonly name = 'mysql' as const;
67
- readonly supportsReturning = false; // Standard MySQL doesn't support RETURNING
68
-
69
- quoteIdentifier(identifier: string): string {
70
- if (identifier.includes('.')) {
71
- return identifier
72
- .split('.')
73
- .map((part) => `\`${part.replace(/`/g, '``')}\``)
74
- .join('.');
75
- }
76
- return `\`${identifier.replace(/`/g, '``')}\``;
77
- }
78
-
79
- paramPlaceholder(_index: number): string {
80
- return '?';
81
- }
82
-
83
- limitOffset(limit?: number, offset?: number): string {
84
- const parts: string[] = [];
85
- if (limit !== undefined) {
86
- parts.push(`LIMIT ${limit}`);
87
- }
88
- if (offset !== undefined) {
89
- // MySQL requires LIMIT when using OFFSET
90
- if (limit === undefined) {
91
- parts.push('LIMIT 18446744073709551615'); // Max BIGINT
92
- }
93
- parts.push(`OFFSET ${offset}`);
94
- }
95
- return parts.join(' ');
96
- }
97
-
98
- returning(_columns?: string[]): string {
99
- // MySQL doesn't support RETURNING, use LAST_INSERT_ID() instead
100
- return '';
101
- }
102
-
103
- formatBoolean(value: boolean): string {
104
- return value ? '1' : '0';
105
- }
106
-
107
- formatDate(date: Date): string {
108
- return date.toISOString().slice(0, 19).replace('T', ' ');
109
- }
110
-
111
- formatJson(value: unknown): string {
112
- return JSON.stringify(value);
113
- }
114
-
115
- currentTimestamp(): string {
116
- return 'CURRENT_TIMESTAMP';
117
- }
118
-
119
- autoIncrement(): string {
120
- return 'AUTO_INCREMENT';
121
- }
122
-
123
- serialType(): string {
124
- return 'INT AUTO_INCREMENT';
125
- }
126
-
127
- concat(...parts: string[]): string {
128
- return `CONCAT(${parts.join(', ')})`;
129
- }
130
-
131
- ilike(column: string, pattern: string): string {
132
- // MySQL LIKE is case-insensitive for non-binary strings by default
133
- return `${column} LIKE ${pattern}`;
134
- }
135
-
136
- jsonExtract(column: string, path: string): string {
137
- // MySQL 5.7+ JSON support
138
- return `JSON_EXTRACT(${column}, '${path}')`;
139
- }
140
-
141
- upsert(
142
- table: string,
143
- columns: string[],
144
- values: string[],
145
- _conflictColumns: string[],
146
- updateColumns: string[]
147
- ): string {
148
- const columnList = columns.join(', ');
149
- const valueList = values.join(', ');
150
- const updateList = updateColumns
151
- .map((col) => `${col} = VALUES(${col})`)
152
- .join(', ');
153
-
154
- return `INSERT INTO ${table} (${columnList}) VALUES (${valueList}) ON DUPLICATE KEY UPDATE ${updateList}`;
155
- }
156
- }
157
-
158
- /**
159
- * PostgreSQL Dialect implementation
160
- */
161
- export class PostgreSQLDialect implements SQLDialect {
162
- readonly name = 'postgresql' as const;
163
- readonly supportsReturning = true;
164
-
165
- quoteIdentifier(identifier: string): string {
166
- if (identifier.includes('.')) {
167
- return identifier
168
- .split('.')
169
- .map((part) => `"${part.replace(/"/g, '""')}"`)
170
- .join('.');
171
- }
172
- return `"${identifier.replace(/"/g, '""')}"`;
173
- }
174
-
175
- paramPlaceholder(index: number): string {
176
- return `$${index}`;
177
- }
178
-
179
- limitOffset(limit?: number, offset?: number): string {
180
- const parts: string[] = [];
181
- if (limit !== undefined) {
182
- parts.push(`LIMIT ${limit}`);
183
- }
184
- if (offset !== undefined) {
185
- parts.push(`OFFSET ${offset}`);
186
- }
187
- return parts.join(' ');
188
- }
189
-
190
- returning(columns?: string[]): string {
191
- if (!columns || columns.length === 0) {
192
- return 'RETURNING *';
193
- }
194
- return `RETURNING ${columns.join(', ')}`;
195
- }
196
-
197
- formatBoolean(value: boolean): string {
198
- return value ? 'TRUE' : 'FALSE';
199
- }
200
-
201
- formatDate(date: Date): string {
202
- return date.toISOString();
203
- }
204
-
205
- formatJson(value: unknown): string {
206
- return JSON.stringify(value);
207
- }
208
-
209
- currentTimestamp(): string {
210
- return 'NOW()';
211
- }
212
-
213
- autoIncrement(): string {
214
- return ''; // PostgreSQL uses SERIAL type instead
215
- }
216
-
217
- serialType(): string {
218
- return 'SERIAL';
219
- }
220
-
221
- concat(...parts: string[]): string {
222
- return parts.join(' || ');
223
- }
224
-
225
- ilike(column: string, pattern: string): string {
226
- return `${column} ILIKE ${pattern}`;
227
- }
228
-
229
- jsonExtract(column: string, path: string): string {
230
- // PostgreSQL JSONB operators
231
- const pathParts = path.split('.');
232
- if (pathParts.length === 1) {
233
- return `${column}->>'${pathParts[0]}'`;
234
- }
235
- const intermediatePath = pathParts.slice(0, -1).map((p) => `'${p}'`).join('->');
236
- const lastPart = pathParts[pathParts.length - 1];
237
- return `${column}->${intermediatePath}->>'${lastPart}'`;
238
- }
239
-
240
- upsert(
241
- table: string,
242
- columns: string[],
243
- values: string[],
244
- conflictColumns: string[],
245
- updateColumns: string[]
246
- ): string {
247
- const columnList = columns.join(', ');
248
- const valueList = values.join(', ');
249
- const conflictList = conflictColumns.join(', ');
250
- const updateList = updateColumns
251
- .map((col) => `${col} = EXCLUDED.${col}`)
252
- .join(', ');
253
-
254
- return `INSERT INTO ${table} (${columnList}) VALUES (${valueList}) ON CONFLICT (${conflictList}) DO UPDATE SET ${updateList}`;
255
- }
256
- }
257
-
258
- /**
259
- * SQLite Dialect implementation (for testing/development)
260
- */
261
- export class SQLiteDialect implements SQLDialect {
262
- readonly name = 'sqlite' as const;
263
- readonly supportsReturning = true; // SQLite 3.35.0+ supports RETURNING
264
-
265
- quoteIdentifier(identifier: string): string {
266
- if (identifier.includes('.')) {
267
- return identifier
268
- .split('.')
269
- .map((part) => `"${part.replace(/"/g, '""')}"`)
270
- .join('.');
271
- }
272
- return `"${identifier.replace(/"/g, '""')}"`;
273
- }
274
-
275
- paramPlaceholder(index: number): string {
276
- return `?${index}`;
277
- }
278
-
279
- limitOffset(limit?: number, offset?: number): string {
280
- const parts: string[] = [];
281
- if (limit !== undefined) {
282
- parts.push(`LIMIT ${limit}`);
283
- }
284
- if (offset !== undefined) {
285
- parts.push(`OFFSET ${offset}`);
286
- }
287
- return parts.join(' ');
288
- }
289
-
290
- returning(columns?: string[]): string {
291
- if (!columns || columns.length === 0) {
292
- return 'RETURNING *';
293
- }
294
- return `RETURNING ${columns.join(', ')}`;
295
- }
296
-
297
- formatBoolean(value: boolean): string {
298
- return value ? '1' : '0';
299
- }
300
-
301
- formatDate(date: Date): string {
302
- return date.toISOString();
303
- }
304
-
305
- formatJson(value: unknown): string {
306
- return JSON.stringify(value);
307
- }
308
-
309
- currentTimestamp(): string {
310
- return "datetime('now')";
311
- }
312
-
313
- autoIncrement(): string {
314
- return 'AUTOINCREMENT';
315
- }
316
-
317
- serialType(): string {
318
- return 'INTEGER PRIMARY KEY AUTOINCREMENT';
319
- }
320
-
321
- concat(...parts: string[]): string {
322
- return parts.join(' || ');
323
- }
324
-
325
- ilike(column: string, pattern: string): string {
326
- // SQLite LIKE is case-insensitive for ASCII by default
327
- return `${column} LIKE ${pattern}`;
328
- }
329
-
330
- jsonExtract(column: string, path: string): string {
331
- return `json_extract(${column}, '$.${path}')`;
332
- }
333
-
334
- upsert(
335
- table: string,
336
- columns: string[],
337
- values: string[],
338
- conflictColumns: string[],
339
- updateColumns: string[]
340
- ): string {
341
- const columnList = columns.join(', ');
342
- const valueList = values.join(', ');
343
- const conflictList = conflictColumns.join(', ');
344
- const updateList = updateColumns
345
- .map((col) => `${col} = EXCLUDED.${col}`)
346
- .join(', ');
347
-
348
- return `INSERT INTO ${table} (${columnList}) VALUES (${valueList}) ON CONFLICT (${conflictList}) DO UPDATE SET ${updateList}`;
349
- }
350
- }
351
-
352
- /**
353
- * Get dialect instance by name
354
- */
355
- export function getDialect(name: 'mysql' | 'postgresql' | 'sqlite'): SQLDialect {
356
- switch (name) {
357
- case 'mysql':
358
- return new MySQLDialect();
359
- case 'postgresql':
360
- return new PostgreSQLDialect();
361
- case 'sqlite':
362
- return new SQLiteDialect();
363
- default:
364
- throw new Error(`Unknown SQL dialect: ${name}`);
365
- }
366
- }
367
-
368
- /**
369
- * Detect dialect from adapter name
370
- */
371
- export function detectDialect(adapterName: string): SQLDialect {
372
- const normalized = adapterName.toLowerCase();
373
-
374
- if (normalized.includes('mysql') || normalized.includes('mariadb')) {
375
- return new MySQLDialect();
376
- }
377
-
378
- if (normalized.includes('postgres') || normalized.includes('pg')) {
379
- return new PostgreSQLDialect();
380
- }
381
-
382
- if (normalized.includes('sqlite')) {
383
- return new SQLiteDialect();
384
- }
385
-
386
- // Default to PostgreSQL dialect
387
- return new PostgreSQLDialect();
388
- }
@@ -1,12 +0,0 @@
1
- export * from './types';
2
- export * from './adapter';
3
- export * from './schema';
4
- export * from './query-builder';
5
- export * from './database';
6
- export * from './optimizer';
7
- export * from './transactions';
8
- export * from './migrations';
9
- export * from './realtime';
10
- export * from './dialect';
11
- export * from './adapters';
12
-
@@ -1,86 +0,0 @@
1
- import { mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';
2
- import path from 'path';
3
- import { MigrationAdapter } from './adapter';
4
-
5
- export interface MigrationFile {
6
- id: string;
7
- name: string;
8
- up: string;
9
- down: string;
10
- }
11
-
12
- export interface MigrationCLIOptions {
13
- directory?: string;
14
- }
15
-
16
- export class MigrationCLI {
17
- private readonly directory: string;
18
-
19
- constructor(private readonly adapter: MigrationAdapter, options: MigrationCLIOptions = {}) {
20
- this.directory = options.directory ?? path.join(process.cwd(), 'migrations');
21
- mkdirSync(this.directory, { recursive: true });
22
- }
23
-
24
- async create(name: string) {
25
- const timestamp = this.formatTimestamp(new Date());
26
- const filename = `${timestamp}_${name}.sql`;
27
- const template = [
28
- '-- +up',
29
- '-- Write SQL for migrating up here',
30
- '',
31
- '-- +down',
32
- '-- Write SQL for migrating down here'
33
- ].join('\n');
34
-
35
- writeFileSync(path.join(this.directory, filename), template, 'utf-8');
36
- return filename;
37
- }
38
-
39
- list(): string[] {
40
- return readdirSync(this.directory)
41
- .filter((file) => file.endsWith('.sql'))
42
- .sort();
43
- }
44
-
45
- private parse(file: string): MigrationFile {
46
- const content = readFileSync(path.join(this.directory, file), 'utf-8');
47
- const [upSection, downSection] = content.split('-- +down');
48
- const up = upSection.replace('-- +up', '').trim();
49
- const down = (downSection ?? '').trim();
50
- const [id, name] = file.replace('.sql', '').split('_');
51
-
52
- return { id, name, up, down };
53
- }
54
-
55
- async up() {
56
- await this.adapter.ensureMigrationsTable();
57
- for (const file of this.list()) {
58
- const migration = this.parse(file);
59
- await this.adapter.runMigration(migration.up);
60
- }
61
- }
62
-
63
- async down(step = 1) {
64
- await this.adapter.ensureMigrationsTable();
65
- const files = this.list().slice(-step).reverse();
66
- for (const file of files) {
67
- const migration = this.parse(file);
68
- if (!migration.down) {
69
- throw new Error(`Migration "${file}" does not define a down section`);
70
- }
71
- await this.adapter.runMigration(migration.down);
72
- }
73
- }
74
-
75
- private formatTimestamp(date: Date) {
76
- const pad = (value: number) => value.toString().padStart(2, '0');
77
- const year = date.getFullYear();
78
- const month = pad(date.getMonth() + 1);
79
- const day = pad(date.getDate());
80
- const hours = pad(date.getHours());
81
- const minutes = pad(date.getMinutes());
82
- const seconds = pad(date.getSeconds());
83
- return `${year}${month}${day}${hours}${minutes}${seconds}`;
84
- }
85
- }
86
-
@@ -1,125 +0,0 @@
1
- import crypto from 'crypto';
2
- import { QueryMetrics, SchemaDefinition, TableDefinition } from './types';
3
-
4
- export interface OptimizationHint {
5
- type: 'n-plus-one' | 'missing-index' | 'slow-query';
6
- table: string;
7
- message: string;
8
- context?: Record<string, unknown>;
9
- }
10
-
11
- interface TrackedQuery {
12
- pattern: string;
13
- occurrences: number;
14
- lastParams?: unknown[];
15
- lastTimestamp: number;
16
- table: string;
17
- }
18
-
19
- export interface QueryOptimizerOptions {
20
- nPlusOneThreshold?: number;
21
- slowQueryThresholdMs?: number;
22
- }
23
-
24
- export class QueryOptimizer {
25
- private trackedQueries = new Map<string, TrackedQuery>();
26
- private hints: OptimizationHint[] = [];
27
- private readonly tablesByName: Record<string, TableDefinition>;
28
-
29
- constructor(
30
- schema: SchemaDefinition,
31
- private readonly options: QueryOptimizerOptions = {}
32
- ) {
33
- this.tablesByName = schema;
34
- }
35
-
36
- analyze(metrics: QueryMetrics): void {
37
- this.detectSlowQuery(metrics);
38
- this.detectNPlusOne(metrics);
39
- this.detectMissingIndexes(metrics);
40
- }
41
-
42
- private detectSlowQuery(metrics: QueryMetrics) {
43
- const threshold = this.options.slowQueryThresholdMs ?? 25;
44
- if (metrics.duration > threshold) {
45
- this.hints.push({
46
- type: 'slow-query',
47
- table: metrics.table,
48
- message: `Query exceeded ${threshold}ms (${metrics.duration.toFixed(2)}ms)`,
49
- context: { sql: metrics.sql }
50
- });
51
- }
52
- }
53
-
54
- private detectNPlusOne(metrics: QueryMetrics) {
55
- const hash = this.hashSql(metrics.sql);
56
- const existing = this.trackedQueries.get(hash);
57
- const threshold = this.options.nPlusOneThreshold ?? 5;
58
-
59
- if (!existing) {
60
- this.trackedQueries.set(hash, {
61
- pattern: metrics.sql,
62
- occurrences: 1,
63
- lastParams: metrics.params,
64
- lastTimestamp: Date.now(),
65
- table: metrics.table
66
- });
67
- return;
68
- }
69
-
70
- existing.occurrences += 1;
71
- existing.lastParams = metrics.params;
72
- existing.lastTimestamp = Date.now();
73
-
74
- if (existing.occurrences >= threshold) {
75
- this.hints.push({
76
- type: 'n-plus-one',
77
- table: metrics.table,
78
- message: `Potential N+1 detected for query "${metrics.sql.slice(0, 60)}..."`,
79
- context: { occurrences: existing.occurrences }
80
- });
81
- existing.occurrences = 0;
82
- }
83
- }
84
-
85
- private detectMissingIndexes(metrics: QueryMetrics) {
86
- const table = this.tablesByName[metrics.table];
87
- if (!table) return;
88
-
89
- const whereMatches = metrics.sql.match(/where\s+([a-zA-Z0-9_." =<>$]+)/i);
90
- if (!whereMatches) return;
91
-
92
- const columns = table.indexes.flatMap((idx) => idx.columns);
93
- const missingColumns: string[] = [];
94
-
95
- whereMatches[1]
96
- .split(/\s+and\s+/i)
97
- .map((condition) => condition.trim().replace(/["`]/g, ''))
98
- .forEach((condition) => {
99
- const column = condition.split(/\s+/)[0];
100
- if (!columns.includes(column)) {
101
- missingColumns.push(column);
102
- }
103
- });
104
-
105
- if (missingColumns.length > 0) {
106
- this.hints.push({
107
- type: 'missing-index',
108
- table: table.name,
109
- message: `Consider adding index on columns: ${missingColumns.join(', ')}`,
110
- context: { whereClause: whereMatches[0] }
111
- });
112
- }
113
- }
114
-
115
- consumeHints(): OptimizationHint[] {
116
- const hints = [...this.hints];
117
- this.hints.length = 0;
118
- return hints;
119
- }
120
-
121
- private hashSql(sql: string): string {
122
- return crypto.createHash('sha1').update(sql).digest('hex');
123
- }
124
- }
125
-