create-phoenixjs 0.1.4 → 0.1.5

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 (51) hide show
  1. package/package.json +1 -1
  2. package/template/config/database.ts +13 -1
  3. package/template/database/migrations/2024_01_01_000000_create_test_users_cli_table.ts +16 -0
  4. package/template/database/migrations/20260108164611_TestCliMigration.ts +16 -0
  5. package/template/database/migrations/2026_01_08_16_46_11_CreateTestMigrationsTable.ts +21 -0
  6. package/template/framework/cli/artisan.ts +12 -0
  7. package/template/framework/database/DatabaseManager.ts +133 -0
  8. package/template/framework/database/connection/Connection.ts +71 -0
  9. package/template/framework/database/connection/ConnectionFactory.ts +30 -0
  10. package/template/framework/database/connection/PostgresConnection.ts +159 -0
  11. package/template/framework/database/console/MakeMigrationCommand.ts +58 -0
  12. package/template/framework/database/console/MigrateCommand.ts +32 -0
  13. package/template/framework/database/console/MigrateResetCommand.ts +31 -0
  14. package/template/framework/database/console/MigrateRollbackCommand.ts +31 -0
  15. package/template/framework/database/console/MigrateStatusCommand.ts +38 -0
  16. package/template/framework/database/migrations/DatabaseMigrationRepository.ts +122 -0
  17. package/template/framework/database/migrations/Migration.ts +5 -0
  18. package/template/framework/database/migrations/MigrationRepository.ts +46 -0
  19. package/template/framework/database/migrations/Migrator.ts +249 -0
  20. package/template/framework/database/migrations/index.ts +4 -0
  21. package/template/framework/database/orm/BelongsTo.ts +246 -0
  22. package/template/framework/database/orm/BelongsToMany.ts +570 -0
  23. package/template/framework/database/orm/Builder.ts +160 -0
  24. package/template/framework/database/orm/EagerLoadingBuilder.ts +324 -0
  25. package/template/framework/database/orm/HasMany.ts +303 -0
  26. package/template/framework/database/orm/HasManyThrough.ts +282 -0
  27. package/template/framework/database/orm/HasOne.ts +201 -0
  28. package/template/framework/database/orm/HasOneThrough.ts +281 -0
  29. package/template/framework/database/orm/Model.ts +1766 -0
  30. package/template/framework/database/orm/Relation.ts +342 -0
  31. package/template/framework/database/orm/Scope.ts +14 -0
  32. package/template/framework/database/orm/SoftDeletes.ts +160 -0
  33. package/template/framework/database/orm/index.ts +54 -0
  34. package/template/framework/database/orm/scopes/SoftDeletingScope.ts +58 -0
  35. package/template/framework/database/pagination/LengthAwarePaginator.ts +55 -0
  36. package/template/framework/database/pagination/Paginator.ts +110 -0
  37. package/template/framework/database/pagination/index.ts +2 -0
  38. package/template/framework/database/query/Builder.ts +918 -0
  39. package/template/framework/database/query/DB.ts +139 -0
  40. package/template/framework/database/query/grammars/Grammar.ts +430 -0
  41. package/template/framework/database/query/grammars/PostgresGrammar.ts +224 -0
  42. package/template/framework/database/query/grammars/index.ts +6 -0
  43. package/template/framework/database/query/index.ts +8 -0
  44. package/template/framework/database/query/types.ts +196 -0
  45. package/template/framework/database/schema/Blueprint.ts +478 -0
  46. package/template/framework/database/schema/Schema.ts +149 -0
  47. package/template/framework/database/schema/SchemaBuilder.ts +152 -0
  48. package/template/framework/database/schema/grammars/PostgresSchemaGrammar.ts +293 -0
  49. package/template/framework/database/schema/grammars/index.ts +5 -0
  50. package/template/framework/database/schema/index.ts +9 -0
  51. package/template/package.json +4 -1
@@ -0,0 +1,478 @@
1
+ /**
2
+ * PhoenixJS ORM - Blueprint
3
+ *
4
+ * Defines the structure of a database table. Used to create or modify
5
+ * table schemas via the Schema Builder.
6
+ */
7
+
8
+ /**
9
+ * Column definition representing a single column in the blueprint
10
+ */
11
+ export interface ColumnDefinition {
12
+ name: string;
13
+ type: string;
14
+ length?: number;
15
+ precision?: number;
16
+ scale?: number;
17
+ nullable: boolean;
18
+ primary: boolean;
19
+ unique: boolean;
20
+ autoIncrement: boolean;
21
+ defaultValue?: string | number | boolean | null;
22
+ references?: {
23
+ column: string;
24
+ table: string;
25
+ onDelete?: string;
26
+ onUpdate?: string;
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Index definition
32
+ */
33
+ export interface IndexDefinition {
34
+ name: string;
35
+ columns: string[];
36
+ unique: boolean;
37
+ }
38
+
39
+ /**
40
+ * Foreign key definition
41
+ */
42
+ export interface ForeignKeyDefinition {
43
+ columns: string[];
44
+ references: string[];
45
+ on: string;
46
+ onDelete?: string;
47
+ onUpdate?: string;
48
+ }
49
+
50
+ /**
51
+ * Column builder for fluent column configuration
52
+ */
53
+ export class ColumnBuilder {
54
+ constructor(private column: ColumnDefinition) { }
55
+
56
+ /**
57
+ * Allow NULL values for this column
58
+ */
59
+ nullable(): this {
60
+ this.column.nullable = true;
61
+ return this;
62
+ }
63
+
64
+ /**
65
+ * Add UNIQUE constraint to this column
66
+ */
67
+ unique(): this {
68
+ this.column.unique = true;
69
+ return this;
70
+ }
71
+
72
+ /**
73
+ * Set this column as PRIMARY KEY
74
+ */
75
+ primary(): this {
76
+ this.column.primary = true;
77
+ return this;
78
+ }
79
+
80
+ /**
81
+ * Set a default value for this column
82
+ */
83
+ default(value: string | number | boolean | null): this {
84
+ this.column.defaultValue = value;
85
+ return this;
86
+ }
87
+
88
+ /**
89
+ * Set foreign key reference
90
+ */
91
+ references(column: string): this & { on(table: string): this } {
92
+ this.column.references = { column, table: '' };
93
+ const self = this;
94
+ return Object.assign(this, {
95
+ on(table: string): ColumnBuilder {
96
+ self.column.references!.table = table;
97
+ return self;
98
+ },
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Set ON DELETE action for foreign key
104
+ */
105
+ onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): this {
106
+ if (this.column.references) {
107
+ this.column.references.onDelete = action;
108
+ }
109
+ return this;
110
+ }
111
+
112
+ /**
113
+ * Set ON UPDATE action for foreign key
114
+ */
115
+ onUpdate(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): this {
116
+ if (this.column.references) {
117
+ this.column.references.onUpdate = action;
118
+ }
119
+ return this;
120
+ }
121
+
122
+ /**
123
+ * Get the column definition
124
+ */
125
+ getColumn(): ColumnDefinition {
126
+ return this.column;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Blueprint for defining table structure
132
+ */
133
+ export class Blueprint {
134
+ private columns: ColumnDefinition[] = [];
135
+ private indexes: IndexDefinition[] = [];
136
+ private foreignKeys: ForeignKeyDefinition[] = [];
137
+ private droppedColumns: string[] = [];
138
+ private droppedIndexes: string[] = [];
139
+
140
+ constructor(private tableName: string) { }
141
+
142
+ /**
143
+ * Create a new column with base definition
144
+ */
145
+ private addColumn(name: string, type: string, options: Partial<ColumnDefinition> = {}): ColumnBuilder {
146
+ const column: ColumnDefinition = {
147
+ name,
148
+ type,
149
+ nullable: false,
150
+ primary: false,
151
+ unique: false,
152
+ autoIncrement: false,
153
+ ...options,
154
+ };
155
+ this.columns.push(column);
156
+ return new ColumnBuilder(column);
157
+ }
158
+
159
+ // ============================================
160
+ // Primary Key / ID Columns
161
+ // ============================================
162
+
163
+ /**
164
+ * Auto-incrementing SERIAL primary key
165
+ */
166
+ id(column: string = 'id'): ColumnBuilder {
167
+ return this.addColumn(column, 'SERIAL', { primary: true, autoIncrement: true });
168
+ }
169
+
170
+ /**
171
+ * Auto-incrementing BIGSERIAL primary key
172
+ */
173
+ bigId(column: string = 'id'): ColumnBuilder {
174
+ return this.addColumn(column, 'BIGSERIAL', { primary: true, autoIncrement: true });
175
+ }
176
+
177
+ /**
178
+ * UUID primary key
179
+ */
180
+ uuid(column: string = 'id'): ColumnBuilder {
181
+ return this.addColumn(column, 'UUID', { primary: true });
182
+ }
183
+
184
+ // ============================================
185
+ // String Columns
186
+ // ============================================
187
+
188
+ /**
189
+ * VARCHAR column with optional length
190
+ */
191
+ string(column: string, length: number = 255): ColumnBuilder {
192
+ return this.addColumn(column, 'VARCHAR', { length });
193
+ }
194
+
195
+ /**
196
+ * CHAR column with fixed length
197
+ */
198
+ char(column: string, length: number = 255): ColumnBuilder {
199
+ return this.addColumn(column, 'CHAR', { length });
200
+ }
201
+
202
+ /**
203
+ * TEXT column for long strings
204
+ */
205
+ text(column: string): ColumnBuilder {
206
+ return this.addColumn(column, 'TEXT');
207
+ }
208
+
209
+ // ============================================
210
+ // Numeric Columns
211
+ // ============================================
212
+
213
+ /**
214
+ * INTEGER column
215
+ */
216
+ integer(column: string): ColumnBuilder {
217
+ return this.addColumn(column, 'INTEGER');
218
+ }
219
+
220
+ /**
221
+ * SMALLINT column
222
+ */
223
+ smallInteger(column: string): ColumnBuilder {
224
+ return this.addColumn(column, 'SMALLINT');
225
+ }
226
+
227
+ /**
228
+ * BIGINT column
229
+ */
230
+ bigInteger(column: string): ColumnBuilder {
231
+ return this.addColumn(column, 'BIGINT');
232
+ }
233
+
234
+ /**
235
+ * REAL/FLOAT column
236
+ */
237
+ float(column: string, precision?: number): ColumnBuilder {
238
+ return this.addColumn(column, 'REAL', { precision });
239
+ }
240
+
241
+ /**
242
+ * DOUBLE PRECISION column
243
+ */
244
+ double(column: string): ColumnBuilder {
245
+ return this.addColumn(column, 'DOUBLE PRECISION');
246
+ }
247
+
248
+ /**
249
+ * NUMERIC/DECIMAL column
250
+ */
251
+ decimal(column: string, precision: number = 8, scale: number = 2): ColumnBuilder {
252
+ return this.addColumn(column, 'NUMERIC', { precision, scale });
253
+ }
254
+
255
+ // ============================================
256
+ // Boolean Column
257
+ // ============================================
258
+
259
+ /**
260
+ * BOOLEAN column
261
+ */
262
+ boolean(column: string): ColumnBuilder {
263
+ return this.addColumn(column, 'BOOLEAN');
264
+ }
265
+
266
+ // ============================================
267
+ // Date/Time Columns
268
+ // ============================================
269
+
270
+ /**
271
+ * DATE column
272
+ */
273
+ date(column: string): ColumnBuilder {
274
+ return this.addColumn(column, 'DATE');
275
+ }
276
+
277
+ /**
278
+ * TIME column
279
+ */
280
+ time(column: string): ColumnBuilder {
281
+ return this.addColumn(column, 'TIME');
282
+ }
283
+
284
+ /**
285
+ * TIMESTAMP column
286
+ */
287
+ timestamp(column: string): ColumnBuilder {
288
+ return this.addColumn(column, 'TIMESTAMP');
289
+ }
290
+
291
+ /**
292
+ * TIMESTAMP WITH TIME ZONE column
293
+ */
294
+ timestampTz(column: string): ColumnBuilder {
295
+ return this.addColumn(column, 'TIMESTAMPTZ');
296
+ }
297
+
298
+ /**
299
+ * Add created_at and updated_at TIMESTAMP columns
300
+ */
301
+ timestamps(): void {
302
+ this.timestamp('created_at').nullable().default('CURRENT_TIMESTAMP');
303
+ this.timestamp('updated_at').nullable().default('CURRENT_TIMESTAMP');
304
+ }
305
+
306
+ /**
307
+ * Add soft delete deleted_at column
308
+ */
309
+ softDeletes(column: string = 'deleted_at'): ColumnBuilder {
310
+ return this.timestamp(column).nullable();
311
+ }
312
+
313
+ // ============================================
314
+ // JSON Columns
315
+ // ============================================
316
+
317
+ /**
318
+ * JSONB column (PostgreSQL optimized JSON)
319
+ */
320
+ json(column: string): ColumnBuilder {
321
+ return this.addColumn(column, 'JSONB');
322
+ }
323
+
324
+ /**
325
+ * JSON column (plain JSON storage)
326
+ */
327
+ jsonPlain(column: string): ColumnBuilder {
328
+ return this.addColumn(column, 'JSON');
329
+ }
330
+
331
+ // ============================================
332
+ // Binary Columns
333
+ // ============================================
334
+
335
+ /**
336
+ * BYTEA column for binary data
337
+ */
338
+ binary(column: string): ColumnBuilder {
339
+ return this.addColumn(column, 'BYTEA');
340
+ }
341
+
342
+ // ============================================
343
+ // Special Columns
344
+ // ============================================
345
+
346
+ /**
347
+ * INET column for IP addresses
348
+ */
349
+ ipAddress(column: string = 'ip_address'): ColumnBuilder {
350
+ return this.addColumn(column, 'INET');
351
+ }
352
+
353
+ /**
354
+ * MACADDR column for MAC addresses
355
+ */
356
+ macAddress(column: string = 'mac_address'): ColumnBuilder {
357
+ return this.addColumn(column, 'MACADDR');
358
+ }
359
+
360
+ // ============================================
361
+ // Indexes
362
+ // ============================================
363
+
364
+ /**
365
+ * Add an index on columns
366
+ */
367
+ index(columns: string | string[], name?: string): void {
368
+ const cols = Array.isArray(columns) ? columns : [columns];
369
+ const indexName = name || `${this.tableName}_${cols.join('_')}_index`;
370
+ this.indexes.push({ name: indexName, columns: cols, unique: false });
371
+ }
372
+
373
+ /**
374
+ * Add a unique index
375
+ */
376
+ uniqueIndex(columns: string | string[], name?: string): void {
377
+ const cols = Array.isArray(columns) ? columns : [columns];
378
+ const indexName = name || `${this.tableName}_${cols.join('_')}_unique`;
379
+ this.indexes.push({ name: indexName, columns: cols, unique: true });
380
+ }
381
+
382
+ // ============================================
383
+ // Foreign Keys
384
+ // ============================================
385
+
386
+ /**
387
+ * Add a foreign key constraint
388
+ */
389
+ foreign(columns: string | string[]): {
390
+ references: (refs: string | string[]) => {
391
+ on: (table: string) => {
392
+ onDelete: (action: string) => void;
393
+ onUpdate: (action: string) => void;
394
+ };
395
+ };
396
+ } {
397
+ const cols = Array.isArray(columns) ? columns : [columns];
398
+ const fk: ForeignKeyDefinition = {
399
+ columns: cols,
400
+ references: [],
401
+ on: '',
402
+ };
403
+
404
+ return {
405
+ references: (refs: string | string[]) => {
406
+ fk.references = Array.isArray(refs) ? refs : [refs];
407
+ return {
408
+ on: (table: string) => {
409
+ fk.on = table;
410
+ this.foreignKeys.push(fk);
411
+ return {
412
+ onDelete: (action: string) => {
413
+ fk.onDelete = action;
414
+ },
415
+ onUpdate: (action: string) => {
416
+ fk.onUpdate = action;
417
+ },
418
+ };
419
+ },
420
+ };
421
+ },
422
+ };
423
+ }
424
+
425
+ // ============================================
426
+ // Drop Operations (for alter table)
427
+ // ============================================
428
+
429
+ /**
430
+ * Drop a column
431
+ */
432
+ dropColumn(column: string | string[]): void {
433
+ const cols = Array.isArray(column) ? column : [column];
434
+ this.droppedColumns.push(...cols);
435
+ }
436
+
437
+ /**
438
+ * Drop an index
439
+ */
440
+ dropIndex(name: string): void {
441
+ this.droppedIndexes.push(name);
442
+ }
443
+
444
+ // ============================================
445
+ // Getters
446
+ // ============================================
447
+
448
+ getTableName(): string {
449
+ return this.tableName;
450
+ }
451
+
452
+ getColumns(): ColumnDefinition[] {
453
+ return this.columns;
454
+ }
455
+
456
+ getIndexes(): IndexDefinition[] {
457
+ return this.indexes;
458
+ }
459
+
460
+ getForeignKeys(): ForeignKeyDefinition[] {
461
+ return this.foreignKeys;
462
+ }
463
+
464
+ getDroppedColumns(): string[] {
465
+ return this.droppedColumns;
466
+ }
467
+
468
+ getDroppedIndexes(): string[] {
469
+ return this.droppedIndexes;
470
+ }
471
+
472
+ /**
473
+ * Check if this is a create table operation
474
+ */
475
+ isCreating(): boolean {
476
+ return this.columns.length > 0 && this.droppedColumns.length === 0;
477
+ }
478
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * PhoenixJS ORM - Schema Facade
3
+ *
4
+ * Static facade for schema operations, providing a simple API for
5
+ * creating and modifying database tables.
6
+ */
7
+
8
+ import type { Connection } from '../connection/Connection';
9
+ import { Blueprint } from './Blueprint';
10
+ import { SchemaBuilder } from './SchemaBuilder';
11
+ import { PostgresSchemaGrammar } from './grammars/PostgresSchemaGrammar';
12
+
13
+ /**
14
+ * Schema - Static facade for database schema operations
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * Schema.init(connection);
19
+ *
20
+ * await Schema.create('users', (table) => {
21
+ * table.id();
22
+ * table.string('name');
23
+ * table.string('email').unique();
24
+ * table.timestamps();
25
+ * });
26
+ *
27
+ * await Schema.dropIfExists('users');
28
+ * ```
29
+ */
30
+ export class Schema {
31
+ private static builder: SchemaBuilder | null = null;
32
+
33
+ /**
34
+ * Initialize the Schema facade with a connection
35
+ */
36
+ static init(connection: Connection, grammar?: PostgresSchemaGrammar): void {
37
+ Schema.builder = new SchemaBuilder(connection, grammar);
38
+ }
39
+
40
+ /**
41
+ * Get the schema builder instance
42
+ */
43
+ static getBuilder(): SchemaBuilder {
44
+ if (!Schema.builder) {
45
+ throw new Error('Schema not initialized. Call Schema.init(connection) first.');
46
+ }
47
+ return Schema.builder;
48
+ }
49
+
50
+ /**
51
+ * Create a new table
52
+ */
53
+ static async create(table: string, callback: (blueprint: Blueprint) => void): Promise<void> {
54
+ return Schema.getBuilder().create(table, callback);
55
+ }
56
+
57
+ /**
58
+ * Modify an existing table
59
+ */
60
+ static async table(table: string, callback: (blueprint: Blueprint) => void): Promise<void> {
61
+ return Schema.getBuilder().table(table, callback);
62
+ }
63
+
64
+ /**
65
+ * Drop a table
66
+ */
67
+ static async drop(table: string): Promise<void> {
68
+ return Schema.getBuilder().drop(table);
69
+ }
70
+
71
+ /**
72
+ * Drop a table if it exists
73
+ */
74
+ static async dropIfExists(table: string): Promise<void> {
75
+ return Schema.getBuilder().dropIfExists(table);
76
+ }
77
+
78
+ /**
79
+ * Rename a table
80
+ */
81
+ static async rename(from: string, to: string): Promise<void> {
82
+ return Schema.getBuilder().rename(from, to);
83
+ }
84
+
85
+ /**
86
+ * Check if a table exists
87
+ */
88
+ static async hasTable(table: string): Promise<boolean> {
89
+ return Schema.getBuilder().hasTable(table);
90
+ }
91
+
92
+ /**
93
+ * Check if a column exists in a table
94
+ */
95
+ static async hasColumn(table: string, column: string): Promise<boolean> {
96
+ return Schema.getBuilder().hasColumn(table, column);
97
+ }
98
+
99
+ /**
100
+ * Get all tables in the database
101
+ */
102
+ static async getTables(): Promise<string[]> {
103
+ return Schema.getBuilder().getTables();
104
+ }
105
+
106
+ /**
107
+ * Get all columns for a table
108
+ */
109
+ static async getColumns(
110
+ table: string
111
+ ): Promise<
112
+ {
113
+ column_name: string;
114
+ data_type: string;
115
+ is_nullable: string;
116
+ column_default: string | null;
117
+ }[]
118
+ > {
119
+ return Schema.getBuilder().getColumns(table);
120
+ }
121
+
122
+ /**
123
+ * Truncate a table
124
+ */
125
+ static async truncate(table: string): Promise<void> {
126
+ return Schema.getBuilder().truncate(table);
127
+ }
128
+
129
+ /**
130
+ * Drop all tables (use with caution!)
131
+ */
132
+ static async dropAllTables(): Promise<void> {
133
+ return Schema.getBuilder().dropAllTables();
134
+ }
135
+
136
+ /**
137
+ * Create a new SchemaBuilder instance without using the facade
138
+ */
139
+ static connection(connection: Connection, grammar?: PostgresSchemaGrammar): SchemaBuilder {
140
+ return new SchemaBuilder(connection, grammar);
141
+ }
142
+
143
+ /**
144
+ * Reset the facade (useful for testing)
145
+ */
146
+ static reset(): void {
147
+ Schema.builder = null;
148
+ }
149
+ }