create-phoenixjs 0.1.3 → 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 (59) hide show
  1. package/package.json +1 -1
  2. package/template/app/controllers/HealthController.ts +22 -0
  3. package/template/app/gateways/EchoGateway.ts +58 -0
  4. package/template/bootstrap/app.ts +2 -7
  5. package/template/config/app.ts +12 -0
  6. package/template/config/database.ts +13 -1
  7. package/template/database/migrations/2024_01_01_000000_create_test_users_cli_table.ts +16 -0
  8. package/template/database/migrations/20260108164611_TestCliMigration.ts +16 -0
  9. package/template/database/migrations/2026_01_08_16_46_11_CreateTestMigrationsTable.ts +21 -0
  10. package/template/framework/cli/artisan.ts +12 -0
  11. package/template/framework/core/Application.ts +15 -0
  12. package/template/framework/database/DatabaseManager.ts +133 -0
  13. package/template/framework/database/connection/Connection.ts +71 -0
  14. package/template/framework/database/connection/ConnectionFactory.ts +30 -0
  15. package/template/framework/database/connection/PostgresConnection.ts +159 -0
  16. package/template/framework/database/console/MakeMigrationCommand.ts +58 -0
  17. package/template/framework/database/console/MigrateCommand.ts +32 -0
  18. package/template/framework/database/console/MigrateResetCommand.ts +31 -0
  19. package/template/framework/database/console/MigrateRollbackCommand.ts +31 -0
  20. package/template/framework/database/console/MigrateStatusCommand.ts +38 -0
  21. package/template/framework/database/migrations/DatabaseMigrationRepository.ts +122 -0
  22. package/template/framework/database/migrations/Migration.ts +5 -0
  23. package/template/framework/database/migrations/MigrationRepository.ts +46 -0
  24. package/template/framework/database/migrations/Migrator.ts +249 -0
  25. package/template/framework/database/migrations/index.ts +4 -0
  26. package/template/framework/database/orm/BelongsTo.ts +246 -0
  27. package/template/framework/database/orm/BelongsToMany.ts +570 -0
  28. package/template/framework/database/orm/Builder.ts +160 -0
  29. package/template/framework/database/orm/EagerLoadingBuilder.ts +324 -0
  30. package/template/framework/database/orm/HasMany.ts +303 -0
  31. package/template/framework/database/orm/HasManyThrough.ts +282 -0
  32. package/template/framework/database/orm/HasOne.ts +201 -0
  33. package/template/framework/database/orm/HasOneThrough.ts +281 -0
  34. package/template/framework/database/orm/Model.ts +1766 -0
  35. package/template/framework/database/orm/Relation.ts +342 -0
  36. package/template/framework/database/orm/Scope.ts +14 -0
  37. package/template/framework/database/orm/SoftDeletes.ts +160 -0
  38. package/template/framework/database/orm/index.ts +54 -0
  39. package/template/framework/database/orm/scopes/SoftDeletingScope.ts +58 -0
  40. package/template/framework/database/pagination/LengthAwarePaginator.ts +55 -0
  41. package/template/framework/database/pagination/Paginator.ts +110 -0
  42. package/template/framework/database/pagination/index.ts +2 -0
  43. package/template/framework/database/query/Builder.ts +918 -0
  44. package/template/framework/database/query/DB.ts +139 -0
  45. package/template/framework/database/query/grammars/Grammar.ts +430 -0
  46. package/template/framework/database/query/grammars/PostgresGrammar.ts +224 -0
  47. package/template/framework/database/query/grammars/index.ts +6 -0
  48. package/template/framework/database/query/index.ts +8 -0
  49. package/template/framework/database/query/types.ts +196 -0
  50. package/template/framework/database/schema/Blueprint.ts +478 -0
  51. package/template/framework/database/schema/Schema.ts +149 -0
  52. package/template/framework/database/schema/SchemaBuilder.ts +152 -0
  53. package/template/framework/database/schema/grammars/PostgresSchemaGrammar.ts +293 -0
  54. package/template/framework/database/schema/grammars/index.ts +5 -0
  55. package/template/framework/database/schema/index.ts +9 -0
  56. package/template/framework/log/Logger.ts +195 -0
  57. package/template/package.json +4 -1
  58. package/template/routes/api.ts +13 -35
  59. package/template/app/controllers/ExampleController.ts +0 -61
@@ -0,0 +1,224 @@
1
+ /**
2
+ * PhoenixJS ORM - PostgreSQL Grammar
3
+ *
4
+ * PostgreSQL-specific grammar implementation for query compilation.
5
+ * Handles PostgreSQL parameter binding syntax ($1, $2, $3...) and
6
+ * PostgreSQL-specific SQL generation.
7
+ */
8
+
9
+ import { Grammar } from './Grammar';
10
+ import type {
11
+ QueryComponents,
12
+ CompiledQuery,
13
+ InsertValues,
14
+ UpdateValues,
15
+ Binding,
16
+ WhereClause,
17
+ } from '../types';
18
+
19
+ /**
20
+ * PostgreSQL Grammar implementation
21
+ *
22
+ * Compiles query builder representations into PostgreSQL-compatible SQL.
23
+ */
24
+ export class PostgresGrammar extends Grammar {
25
+ /**
26
+ * Wrap a value in PostgreSQL keyword identifiers (double quotes)
27
+ */
28
+ wrap(value: string): string {
29
+ // Handle * without wrapping
30
+ if (value === '*') {
31
+ return value;
32
+ }
33
+
34
+ // Handle table.column format
35
+ if (value.includes('.')) {
36
+ return value
37
+ .split('.')
38
+ .map((segment) => (segment === '*' ? '*' : `"${segment}"`))
39
+ .join('.');
40
+ }
41
+
42
+ // Handle column aliases (column AS alias)
43
+ if (value.toLowerCase().includes(' as ')) {
44
+ const parts = value.split(/\s+as\s+/i);
45
+ return `${this.wrap(parts[0])} AS ${this.wrap(parts[1])}`;
46
+ }
47
+
48
+ // Handle raw expressions wrapped in parentheses
49
+ if (value.startsWith('(') && value.endsWith(')')) {
50
+ return value;
51
+ }
52
+
53
+ return `"${value}"`;
54
+ }
55
+
56
+ /**
57
+ * Get the grammar's parameter prefix
58
+ */
59
+ getParameterPrefix(): string {
60
+ return '$';
61
+ }
62
+
63
+ /**
64
+ * Create query parameter placeholders for PostgreSQL ($1, $2, $3...)
65
+ */
66
+ parameterize(values: Binding[], startIndex: number = 1): string {
67
+ return values.map((_, index) => `$${startIndex + index}`).join(', ');
68
+ }
69
+
70
+ /**
71
+ * Compile a basic where clause
72
+ */
73
+ protected compileBasicWhere(where: WhereClause, bindings: Binding[]): string {
74
+ const column = this.wrap(where.column!);
75
+ bindings.push(where.value as Binding);
76
+ const paramIndex = bindings.length;
77
+ return `${column} ${where.operator} $${paramIndex}`;
78
+ }
79
+
80
+ /**
81
+ * Compile a where in clause
82
+ */
83
+ protected compileInWhere(where: WhereClause, bindings: Binding[]): string {
84
+ const column = this.wrap(where.column!);
85
+ const values = where.value as Binding[];
86
+ const startIndex = bindings.length + 1;
87
+
88
+ values.forEach((val) => bindings.push(val));
89
+
90
+ const placeholders = values.map((_, i) => `$${startIndex + i}`).join(', ');
91
+ const operator = where.not ? 'NOT IN' : 'IN';
92
+
93
+ return `${column} ${operator} (${placeholders})`;
94
+ }
95
+
96
+ /**
97
+ * Compile a where between clause
98
+ */
99
+ protected compileBetweenWhere(where: WhereClause, bindings: Binding[]): string {
100
+ const column = this.wrap(where.column!);
101
+ const values = where.values!;
102
+
103
+ bindings.push(values[0], values[1]);
104
+ const param1 = bindings.length - 1;
105
+ const param2 = bindings.length;
106
+
107
+ const operator = where.not ? 'NOT BETWEEN' : 'BETWEEN';
108
+ return `${column} ${operator} $${param1} AND $${param2}`;
109
+ }
110
+
111
+ /**
112
+ * Compile an INSERT statement
113
+ */
114
+ compileInsert(query: QueryComponents, values: InsertValues | InsertValues[]): CompiledQuery {
115
+ const table = this.wrapTable(query.table);
116
+ const valueArray = Array.isArray(values) ? values : [values];
117
+
118
+ if (valueArray.length === 0) {
119
+ return { sql: `INSERT INTO ${table} DEFAULT VALUES`, bindings: [] };
120
+ }
121
+
122
+ const columns = Object.keys(valueArray[0]);
123
+ const columnList = this.columnize(columns);
124
+
125
+ const bindings: Binding[] = [];
126
+ const valueSets = valueArray.map((row) => {
127
+ const placeholders = columns.map((col) => {
128
+ bindings.push(row[col]);
129
+ return `$${bindings.length}`;
130
+ });
131
+ return `(${placeholders.join(', ')})`;
132
+ });
133
+
134
+ const sql = `INSERT INTO ${table} (${columnList}) VALUES ${valueSets.join(', ')}`;
135
+ return { sql, bindings };
136
+ }
137
+
138
+ /**
139
+ * Compile an INSERT statement with RETURNING clause
140
+ */
141
+ compileInsertReturning(
142
+ query: QueryComponents,
143
+ values: InsertValues | InsertValues[],
144
+ returning: string[]
145
+ ): CompiledQuery {
146
+ const { sql, bindings } = this.compileInsert(query, values);
147
+ const returningColumns = returning.length === 0 ? '*' : this.columnize(returning);
148
+ return { sql: `${sql} RETURNING ${returningColumns}`, bindings };
149
+ }
150
+
151
+ /**
152
+ * Compile an UPDATE statement
153
+ */
154
+ compileUpdate(query: QueryComponents, values: UpdateValues): CompiledQuery {
155
+ const table = this.wrapTable(query.table);
156
+ const bindings: Binding[] = [];
157
+
158
+ // Compile SET clause
159
+ const setClause = Object.entries(values)
160
+ .map(([column, value]) => {
161
+ bindings.push(value);
162
+ return `${this.wrap(column)} = $${bindings.length}`;
163
+ })
164
+ .join(', ');
165
+
166
+ // Compile WHERE clause
167
+ const whereClause = this.compileWheres(query, bindings);
168
+
169
+ const sql = `UPDATE ${table} SET ${setClause}${whereClause ? ` ${whereClause}` : ''}`;
170
+ return { sql, bindings };
171
+ }
172
+
173
+ /**
174
+ * Compile a DELETE statement
175
+ */
176
+ compileDelete(query: QueryComponents): CompiledQuery {
177
+ const table = this.wrapTable(query.table);
178
+ const bindings: Binding[] = [];
179
+
180
+ // Compile WHERE clause
181
+ const whereClause = this.compileWheres(query, bindings);
182
+
183
+ const sql = `DELETE FROM ${table}${whereClause ? ` ${whereClause}` : ''}`;
184
+ return { sql, bindings };
185
+ }
186
+
187
+ /**
188
+ * Compile a TRUNCATE TABLE statement
189
+ */
190
+ compileTruncate(table: string): string {
191
+ return `TRUNCATE TABLE ${this.wrapTable(table)} RESTART IDENTITY CASCADE`;
192
+ }
193
+
194
+ /**
195
+ * Compile an upsert (INSERT ... ON CONFLICT) statement
196
+ */
197
+ compileUpsert(
198
+ query: QueryComponents,
199
+ values: InsertValues | InsertValues[],
200
+ uniqueColumns: string[],
201
+ updateColumns?: string[]
202
+ ): CompiledQuery {
203
+ const { sql, bindings } = this.compileInsert(query, values);
204
+
205
+ const conflictColumns = this.columnize(uniqueColumns);
206
+ const valueArray = Array.isArray(values) ? values : [values];
207
+ const allColumns = Object.keys(valueArray[0]);
208
+ const columnsToUpdate = updateColumns ?? allColumns.filter((col) => !uniqueColumns.includes(col));
209
+
210
+ const updateClause = columnsToUpdate
211
+ .map((col) => `${this.wrap(col)} = EXCLUDED.${this.wrap(col)}`)
212
+ .join(', ');
213
+
214
+ const upsertSql = `${sql} ON CONFLICT (${conflictColumns}) DO UPDATE SET ${updateClause}`;
215
+ return { sql: upsertSql, bindings };
216
+ }
217
+
218
+ /**
219
+ * Compile a raw expression
220
+ */
221
+ raw(expression: string): string {
222
+ return expression;
223
+ }
224
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * PhoenixJS ORM - Grammar Exports
3
+ */
4
+
5
+ export { Grammar } from './Grammar';
6
+ export { PostgresGrammar } from './PostgresGrammar';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * PhoenixJS ORM - Query Exports
3
+ */
4
+
5
+ export * from './types';
6
+ export * from './grammars';
7
+ export { Builder } from './Builder';
8
+ export { DB } from './DB';
@@ -0,0 +1,196 @@
1
+ /**
2
+ * PhoenixJS ORM - Query Builder Types
3
+ *
4
+ * Type definitions for the query builder internals that the Grammar compiles.
5
+ */
6
+
7
+ /**
8
+ * Supported WHERE clause operators
9
+ */
10
+ export type WhereOperator =
11
+ | '='
12
+ | '!='
13
+ | '<>'
14
+ | '<'
15
+ | '<='
16
+ | '>'
17
+ | '>='
18
+ | 'LIKE'
19
+ | 'NOT LIKE'
20
+ | 'ILIKE'
21
+ | 'NOT ILIKE'
22
+ | 'IN'
23
+ | 'NOT IN'
24
+ | 'BETWEEN'
25
+ | 'NOT BETWEEN'
26
+ | 'IS NULL'
27
+ | 'IS NOT NULL';
28
+
29
+ /**
30
+ * Binding value type - values that can be bound to query parameters
31
+ */
32
+ export type Binding = string | number | boolean | null | Date;
33
+
34
+ /**
35
+ * WHERE clause representation
36
+ */
37
+ export interface WhereClause {
38
+ type: 'basic' | 'in' | 'null' | 'between' | 'raw' | 'nested';
39
+ column?: string;
40
+ operator?: WhereOperator | string;
41
+ value?: Binding | Binding[];
42
+ values?: Binding[]; // For BETWEEN
43
+ boolean: 'and' | 'or';
44
+ not?: boolean;
45
+ sql?: string; // For raw WHERE
46
+ bindings?: Binding[]; // For raw WHERE
47
+ nested?: WhereClause[]; // For nested WHERE
48
+ }
49
+
50
+ /**
51
+ * JOIN clause types
52
+ */
53
+ export type JoinType = 'inner' | 'left' | 'right' | 'cross';
54
+
55
+ /**
56
+ * JOIN clause representation
57
+ */
58
+ export interface JoinClause {
59
+ type: JoinType;
60
+ table: string;
61
+ first: string; // First column (from main table)
62
+ operator: string;
63
+ second: string; // Second column (from joined table)
64
+ }
65
+
66
+ /**
67
+ * ORDER BY direction
68
+ */
69
+ export type OrderDirection = 'asc' | 'desc';
70
+
71
+ /**
72
+ * Raw SQL expression with bindings
73
+ * Used for selectRaw, orderByRaw, etc.
74
+ */
75
+ export class RawExpression {
76
+ constructor(
77
+ public readonly expression: string,
78
+ public readonly bindings: Binding[] = []
79
+ ) { }
80
+
81
+ toString(): string {
82
+ return this.expression;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * ORDER BY clause representation
88
+ */
89
+ export interface OrderClause {
90
+ column: string;
91
+ direction: OrderDirection;
92
+ raw?: boolean; // If true, column is a raw SQL expression
93
+ }
94
+
95
+ /**
96
+ * Aggregate function types
97
+ */
98
+ export type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max';
99
+
100
+ /**
101
+ * Aggregate representation
102
+ */
103
+ export interface AggregateClause {
104
+ function: AggregateFunction;
105
+ column: string;
106
+ }
107
+
108
+ /**
109
+ * Full query state object representing all components of a query
110
+ */
111
+ export interface QueryComponents {
112
+ /** Table name */
113
+ table: string;
114
+
115
+ /** Table alias */
116
+ alias?: string;
117
+
118
+ /** SELECT columns - empty means SELECT * */
119
+ columns: string[];
120
+
121
+ /** Raw SELECT expressions */
122
+ rawSelects: { expression: string; bindings: Binding[] }[];
123
+
124
+ /** DISTINCT flag */
125
+ distinct: boolean;
126
+
127
+ /** WHERE clauses */
128
+ wheres: WhereClause[];
129
+
130
+ /** JOIN clauses */
131
+ joins: JoinClause[];
132
+
133
+ /** ORDER BY clauses */
134
+ orders: OrderClause[];
135
+
136
+ /** GROUP BY columns */
137
+ groups: string[];
138
+
139
+ /** Raw GROUP BY expressions */
140
+ rawGroups: { expression: string; bindings: Binding[] }[];
141
+
142
+ /** ORDER BY bindings (for raw order expressions) */
143
+ orderBindings: Binding[];
144
+
145
+ /** HAVING conditions (similar to WHERE) */
146
+ havings: WhereClause[];
147
+
148
+ /** LIMIT value */
149
+ limit?: number;
150
+
151
+ /** OFFSET value */
152
+ offset?: number;
153
+
154
+ /** Aggregate function */
155
+ aggregate?: AggregateClause;
156
+
157
+ /** LOCK mode */
158
+ lock?: 'for update' | 'for share';
159
+ }
160
+
161
+ /**
162
+ * Result of compiling a query - SQL string and bindings
163
+ */
164
+ export interface CompiledQuery {
165
+ sql: string;
166
+ bindings: Binding[];
167
+ }
168
+
169
+ /**
170
+ * Insert values - each key is a column name
171
+ */
172
+ export type InsertValues = Record<string, Binding>;
173
+
174
+ /**
175
+ * Update values - each key is a column name
176
+ */
177
+ export type UpdateValues = Record<string, Binding>;
178
+
179
+ /**
180
+ * Create empty query components with defaults
181
+ */
182
+ export function createQueryComponents(table: string): QueryComponents {
183
+ return {
184
+ table,
185
+ columns: [],
186
+ rawSelects: [],
187
+ distinct: false,
188
+ wheres: [],
189
+ joins: [],
190
+ orders: [],
191
+ groups: [],
192
+ rawGroups: [],
193
+ orderBindings: [],
194
+ havings: [],
195
+ };
196
+ }