create-phoenixjs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +196 -0
- package/package.json +31 -0
- package/template/README.md +62 -0
- package/template/app/controllers/ExampleController.ts +61 -0
- package/template/artisan +2 -0
- package/template/bootstrap/app.ts +44 -0
- package/template/bunfig.toml +7 -0
- package/template/config/database.ts +25 -0
- package/template/config/plugins.ts +7 -0
- package/template/config/security.ts +158 -0
- package/template/framework/cli/Command.ts +17 -0
- package/template/framework/cli/ConsoleApplication.ts +55 -0
- package/template/framework/cli/artisan.ts +16 -0
- package/template/framework/cli/commands/MakeControllerCommand.ts +41 -0
- package/template/framework/cli/commands/MakeMiddlewareCommand.ts +41 -0
- package/template/framework/cli/commands/MakeModelCommand.ts +36 -0
- package/template/framework/cli/commands/MakeValidatorCommand.ts +42 -0
- package/template/framework/controller/Controller.ts +222 -0
- package/template/framework/core/Application.ts +208 -0
- package/template/framework/core/Container.ts +100 -0
- package/template/framework/core/Kernel.ts +297 -0
- package/template/framework/database/DatabaseAdapter.ts +18 -0
- package/template/framework/database/PrismaAdapter.ts +65 -0
- package/template/framework/database/SqlAdapter.ts +117 -0
- package/template/framework/gateway/Gateway.ts +109 -0
- package/template/framework/gateway/GatewayManager.ts +150 -0
- package/template/framework/gateway/WebSocketAdapter.ts +159 -0
- package/template/framework/gateway/WebSocketGateway.ts +182 -0
- package/template/framework/http/Request.ts +608 -0
- package/template/framework/http/Response.ts +525 -0
- package/template/framework/http/Server.ts +161 -0
- package/template/framework/http/UploadedFile.ts +145 -0
- package/template/framework/middleware/Middleware.ts +50 -0
- package/template/framework/middleware/Pipeline.ts +89 -0
- package/template/framework/plugin/Plugin.ts +26 -0
- package/template/framework/plugin/PluginManager.ts +61 -0
- package/template/framework/routing/RouteRegistry.ts +185 -0
- package/template/framework/routing/Router.ts +280 -0
- package/template/framework/security/CorsMiddleware.ts +151 -0
- package/template/framework/security/CsrfMiddleware.ts +121 -0
- package/template/framework/security/HelmetMiddleware.ts +138 -0
- package/template/framework/security/InputSanitizerMiddleware.ts +134 -0
- package/template/framework/security/RateLimiterMiddleware.ts +189 -0
- package/template/framework/security/SecurityManager.ts +128 -0
- package/template/framework/validation/Validator.ts +482 -0
- package/template/package.json +24 -0
- package/template/routes/api.ts +56 -0
- package/template/server.ts +29 -0
- package/template/tsconfig.json +49 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from '../Command';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export class MakeControllerCommand extends Command {
|
|
6
|
+
signature = 'make:controller {name}';
|
|
7
|
+
description = 'Create a new controller class';
|
|
8
|
+
|
|
9
|
+
async handle(args: Record<string, any>): Promise<void> {
|
|
10
|
+
const name = args.name;
|
|
11
|
+
if (!name) {
|
|
12
|
+
console.error('Error: Controller name is required.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const className = path.basename(name);
|
|
17
|
+
// Handle nested directories e.g. user/UserController
|
|
18
|
+
const relativePath = name.endsWith('.ts') ? name : `${name}.ts`;
|
|
19
|
+
const fullPath = path.join(process.cwd(), 'app', 'controllers', relativePath);
|
|
20
|
+
const directory = path.dirname(fullPath);
|
|
21
|
+
|
|
22
|
+
if (fs.existsSync(fullPath)) {
|
|
23
|
+
console.error(`Error: Controller "${name}" already exists.`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
28
|
+
|
|
29
|
+
const template = `import { Controller } from '@framework/controller/Controller';
|
|
30
|
+
|
|
31
|
+
export class ${className} extends Controller {
|
|
32
|
+
async index() {
|
|
33
|
+
return this.json({ message: 'Hello from ${className}' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
fs.writeFileSync(fullPath, template);
|
|
39
|
+
console.log(`Controller created successfully: app/controllers/${relativePath}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from '../Command';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export class MakeMiddlewareCommand extends Command {
|
|
6
|
+
signature = 'make:middleware {name}';
|
|
7
|
+
description = 'Create a new middleware class';
|
|
8
|
+
|
|
9
|
+
async handle(args: Record<string, any>): Promise<void> {
|
|
10
|
+
const name = args.name;
|
|
11
|
+
if (!name) {
|
|
12
|
+
console.error('Error: Middleware name is required.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const className = path.basename(name);
|
|
17
|
+
const relativePath = name.endsWith('.ts') ? name : `${name}.ts`;
|
|
18
|
+
const fullPath = path.join(process.cwd(), 'app', 'middlewares', relativePath);
|
|
19
|
+
const directory = path.dirname(fullPath);
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(fullPath)) {
|
|
22
|
+
console.error(`Error: Middleware "${name}" already exists.`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
27
|
+
|
|
28
|
+
const template = `import { Middleware, MiddlewareHandler, NextFunction, Request, Response } from '@framework/middleware/Middleware';
|
|
29
|
+
|
|
30
|
+
export class ${className} implements Middleware {
|
|
31
|
+
async handle(request: Request, next: NextFunction): Promise<Response | any> {
|
|
32
|
+
// middleware logic
|
|
33
|
+
return next(request);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
fs.writeFileSync(fullPath, template);
|
|
39
|
+
console.log(`Middleware created successfully: app/middlewares/${relativePath}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Command } from '../Command';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export class MakeModelCommand extends Command {
|
|
6
|
+
signature = 'make:model {name}';
|
|
7
|
+
description = 'Create a new model class';
|
|
8
|
+
|
|
9
|
+
async handle(args: Record<string, any>): Promise<void> {
|
|
10
|
+
const name = args.name;
|
|
11
|
+
if (!name) {
|
|
12
|
+
console.error('Error: Model name is required.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const className = path.basename(name);
|
|
17
|
+
const relativePath = name.endsWith('.ts') ? name : `${name}.ts`;
|
|
18
|
+
const fullPath = path.join(process.cwd(), 'app', 'models', relativePath);
|
|
19
|
+
const directory = path.dirname(fullPath);
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(fullPath)) {
|
|
22
|
+
console.error(`Error: Model "${name}" already exists.`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
27
|
+
|
|
28
|
+
const template = `export class ${className} {
|
|
29
|
+
// Model definition
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
fs.writeFileSync(fullPath, template);
|
|
34
|
+
console.log(`Model created successfully: app/models/${relativePath}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Command } from '../Command';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export class MakeValidatorCommand extends Command {
|
|
6
|
+
signature = 'make:validator {name}';
|
|
7
|
+
description = 'Create a new validator class';
|
|
8
|
+
|
|
9
|
+
async handle(args: Record<string, any>): Promise<void> {
|
|
10
|
+
const name = args.name;
|
|
11
|
+
if (!name) {
|
|
12
|
+
console.error('Error: Validator name is required.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const className = path.basename(name);
|
|
17
|
+
const relativePath = name.endsWith('.ts') ? name : `${name}.ts`;
|
|
18
|
+
const fullPath = path.join(process.cwd(), 'app', 'validators', relativePath);
|
|
19
|
+
const directory = path.dirname(fullPath);
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(fullPath)) {
|
|
22
|
+
console.error(`Error: Validator "${name}" already exists.`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
27
|
+
|
|
28
|
+
const template = `import { Validator } from '@framework/validation/Validator';
|
|
29
|
+
|
|
30
|
+
export class ${className} extends Validator {
|
|
31
|
+
rules() {
|
|
32
|
+
return {
|
|
33
|
+
// 'field': ['required', 'string']
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
fs.writeFileSync(fullPath, template);
|
|
40
|
+
console.log(`Validator created successfully: app/validators/${relativePath}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhoenixJS - Base Controller
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for all controllers.
|
|
5
|
+
* Provides request injection and response helpers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { FrameworkRequest } from '@framework/http/Request';
|
|
9
|
+
import { FrameworkResponse, ResponseBuilder } from '@framework/http/Response';
|
|
10
|
+
import type { ValidationResult } from '@framework/validation/Validator';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Controller constructor type for dependency injection
|
|
14
|
+
*/
|
|
15
|
+
export interface ControllerConstructor {
|
|
16
|
+
new(): Controller;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Base Controller class
|
|
21
|
+
*
|
|
22
|
+
* All application controllers should extend this class.
|
|
23
|
+
* Provides access to the current request and response helpers.
|
|
24
|
+
*/
|
|
25
|
+
export abstract class Controller {
|
|
26
|
+
/**
|
|
27
|
+
* The current request instance (injected by Kernel)
|
|
28
|
+
*/
|
|
29
|
+
protected request!: FrameworkRequest;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Route parameters (injected by Kernel)
|
|
33
|
+
*/
|
|
34
|
+
protected params: Record<string, string> = {};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Set the request instance (called by Kernel)
|
|
38
|
+
*/
|
|
39
|
+
setRequest(request: FrameworkRequest): void {
|
|
40
|
+
this.request = request;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Set route parameters (called by Kernel)
|
|
45
|
+
*/
|
|
46
|
+
setParams(params: Record<string, string>): void {
|
|
47
|
+
this.params = params;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get a route parameter
|
|
52
|
+
*/
|
|
53
|
+
protected param(key: string, defaultValue?: string): string | undefined {
|
|
54
|
+
return this.params[key] ?? defaultValue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ==========================================
|
|
58
|
+
// Response Helpers
|
|
59
|
+
// ==========================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Return a JSON response
|
|
63
|
+
*/
|
|
64
|
+
protected json(data: unknown, status = 200): Response {
|
|
65
|
+
return FrameworkResponse.json(data, status);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Return a text response
|
|
70
|
+
*/
|
|
71
|
+
protected text(content: string, status = 200): Response {
|
|
72
|
+
return FrameworkResponse.text(content, status);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Return an HTML response
|
|
77
|
+
*/
|
|
78
|
+
protected html(content: string, status = 200): Response {
|
|
79
|
+
return FrameworkResponse.html(content, status);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Return an XML response
|
|
84
|
+
*/
|
|
85
|
+
protected xml(content: string, status = 200): Response {
|
|
86
|
+
return FrameworkResponse.xml(content, status);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Return a redirect response
|
|
91
|
+
*/
|
|
92
|
+
protected redirect(url: string, permanent = false): Response {
|
|
93
|
+
return FrameworkResponse.redirect(url, permanent ? 301 : 302);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Return a 204 No Content response
|
|
98
|
+
*/
|
|
99
|
+
protected noContent(): Response {
|
|
100
|
+
return FrameworkResponse.noContent();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Return an error response
|
|
105
|
+
*/
|
|
106
|
+
protected error(message: string, status = 500, details?: Record<string, unknown>): Response {
|
|
107
|
+
return FrameworkResponse.error(message, status, details);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Return a 400 Bad Request response
|
|
112
|
+
*/
|
|
113
|
+
protected badRequest(message: string, details?: Record<string, unknown>): Response {
|
|
114
|
+
return FrameworkResponse.badRequest(message, details);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Return a 401 Unauthorized response
|
|
119
|
+
*/
|
|
120
|
+
protected unauthorized(message = 'Unauthorized'): Response {
|
|
121
|
+
return FrameworkResponse.unauthorized(message);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Return a 403 Forbidden response
|
|
126
|
+
*/
|
|
127
|
+
protected forbidden(message = 'Forbidden'): Response {
|
|
128
|
+
return FrameworkResponse.forbidden(message);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Return a 404 Not Found response
|
|
133
|
+
*/
|
|
134
|
+
protected notFound(message = 'Not Found'): Response {
|
|
135
|
+
return FrameworkResponse.notFound(message);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Return a 422 Validation Error response
|
|
140
|
+
*/
|
|
141
|
+
protected validationError(errors: Record<string, string[]>): Response {
|
|
142
|
+
return FrameworkResponse.validationError(errors);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Create a fluent response builder
|
|
147
|
+
*/
|
|
148
|
+
protected response(): ResponseBuilder {
|
|
149
|
+
return FrameworkResponse.create();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ==========================================
|
|
153
|
+
// Validation
|
|
154
|
+
// ==========================================
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Validate the request input using a Validator class
|
|
158
|
+
*
|
|
159
|
+
* @param validatorClass - The Validator class to use
|
|
160
|
+
* @returns ValidationResult with passes/fails and errors
|
|
161
|
+
*/
|
|
162
|
+
protected async validate<T extends { new(): { validate(data: Record<string, unknown>): ValidationResult } }>(
|
|
163
|
+
validatorClass: T
|
|
164
|
+
): Promise<ValidationResult> {
|
|
165
|
+
const validator = new validatorClass();
|
|
166
|
+
const data = await this.request.all();
|
|
167
|
+
return validator.validate(data);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Validate data and return error response if validation fails
|
|
172
|
+
* Returns null if validation passes, Response if fails
|
|
173
|
+
*/
|
|
174
|
+
protected async validateOrFail<T extends { new(): { validate(data: Record<string, unknown>): ValidationResult } }>(
|
|
175
|
+
validatorClass: T
|
|
176
|
+
): Promise<Response | null> {
|
|
177
|
+
const result = await this.validate(validatorClass);
|
|
178
|
+
if (!result.passes) {
|
|
179
|
+
return this.validationError(result.errors);
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ==========================================
|
|
185
|
+
// Request Helpers
|
|
186
|
+
// ==========================================
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get the current request
|
|
190
|
+
*/
|
|
191
|
+
protected getRequest(): FrameworkRequest {
|
|
192
|
+
return this.request;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get all input data
|
|
197
|
+
*/
|
|
198
|
+
protected async all(): Promise<Record<string, unknown>> {
|
|
199
|
+
return this.request.all();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get an input value
|
|
204
|
+
*/
|
|
205
|
+
protected async input<T = unknown>(key?: string, defaultValue?: T): Promise<T | Record<string, unknown>> {
|
|
206
|
+
return this.request.input(key, defaultValue);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get only specified input keys
|
|
211
|
+
*/
|
|
212
|
+
protected async only(...keys: string[]): Promise<Record<string, unknown>> {
|
|
213
|
+
return this.request.only(...keys);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get all input except specified keys
|
|
218
|
+
*/
|
|
219
|
+
protected async except(...keys: string[]): Promise<Record<string, unknown>> {
|
|
220
|
+
return this.request.except(...keys);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhoenixJS - Application
|
|
3
|
+
*
|
|
4
|
+
* The main application class that bootstraps the framework.
|
|
5
|
+
* Extends Container to provide dependency injection capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Container } from '@framework/core/Container';
|
|
9
|
+
import { PluginManager } from '@framework/plugin/PluginManager';
|
|
10
|
+
import { Plugin } from '@framework/plugin/Plugin';
|
|
11
|
+
import { GatewayManager } from '@framework/gateway/GatewayManager';
|
|
12
|
+
import { SecurityManager } from '@framework/security/SecurityManager';
|
|
13
|
+
import type { Gateway } from '@framework/gateway/Gateway';
|
|
14
|
+
import type { SecurityConfig } from '@/config/security';
|
|
15
|
+
|
|
16
|
+
export interface AppConfig {
|
|
17
|
+
name: string;
|
|
18
|
+
env: 'development' | 'production' | 'testing';
|
|
19
|
+
debug: boolean;
|
|
20
|
+
port: number;
|
|
21
|
+
host: string;
|
|
22
|
+
plugins?: Plugin[];
|
|
23
|
+
gateways?: Gateway[];
|
|
24
|
+
security?: Partial<SecurityConfig>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const defaultConfig: AppConfig = {
|
|
28
|
+
name: 'PhoenixJS',
|
|
29
|
+
env: 'development',
|
|
30
|
+
debug: true,
|
|
31
|
+
port: 4000,
|
|
32
|
+
host: 'localhost',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class Application extends Container {
|
|
36
|
+
private static instance: Application | null = null;
|
|
37
|
+
private config: AppConfig;
|
|
38
|
+
private booted = false;
|
|
39
|
+
private basePath: string;
|
|
40
|
+
private pluginManager: PluginManager;
|
|
41
|
+
private gatewayManager: GatewayManager;
|
|
42
|
+
private securityManager: SecurityManager;
|
|
43
|
+
|
|
44
|
+
constructor(basePath: string = process.cwd()) {
|
|
45
|
+
super();
|
|
46
|
+
this.basePath = basePath;
|
|
47
|
+
this.config = { ...defaultConfig };
|
|
48
|
+
this.pluginManager = new PluginManager(this);
|
|
49
|
+
this.gatewayManager = new GatewayManager(this);
|
|
50
|
+
this.securityManager = new SecurityManager(this);
|
|
51
|
+
this.registerBaseBindings();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get the singleton application instance
|
|
56
|
+
*/
|
|
57
|
+
static getInstance(): Application {
|
|
58
|
+
if (!Application.instance) {
|
|
59
|
+
throw new Error('Application has not been initialized');
|
|
60
|
+
}
|
|
61
|
+
return Application.instance;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a new application instance
|
|
66
|
+
*/
|
|
67
|
+
static create(basePath?: string): Application {
|
|
68
|
+
Application.instance = new Application(basePath);
|
|
69
|
+
return Application.instance;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Register base bindings
|
|
74
|
+
*/
|
|
75
|
+
private registerBaseBindings(): void {
|
|
76
|
+
this.instance('app', this);
|
|
77
|
+
this.instance('config', this.config);
|
|
78
|
+
this.instance('pluginManager', this.pluginManager);
|
|
79
|
+
this.instance('gatewayManager', this.gatewayManager);
|
|
80
|
+
this.instance('securityManager', this.securityManager);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Configure the application
|
|
85
|
+
*/
|
|
86
|
+
configure(config: Partial<AppConfig>): this {
|
|
87
|
+
this.config = { ...this.config, ...config };
|
|
88
|
+
this.instance('config', this.config);
|
|
89
|
+
|
|
90
|
+
// Register plugins from config
|
|
91
|
+
if (this.config.plugins) {
|
|
92
|
+
this.config.plugins.forEach(plugin => {
|
|
93
|
+
this.registerPlugin(plugin);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Register gateways from config
|
|
98
|
+
if (this.config.gateways) {
|
|
99
|
+
this.config.gateways.forEach(gateway => {
|
|
100
|
+
this.registerGateway(gateway);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Configure security from config
|
|
105
|
+
if (this.config.security) {
|
|
106
|
+
this.securityManager.configure(this.config.security);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Register a plugin
|
|
114
|
+
*/
|
|
115
|
+
registerPlugin(plugin: Plugin): this {
|
|
116
|
+
this.pluginManager.register(plugin);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get the plugin manager
|
|
122
|
+
*/
|
|
123
|
+
getPluginManager(): PluginManager {
|
|
124
|
+
return this.pluginManager;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Register a gateway
|
|
129
|
+
*/
|
|
130
|
+
registerGateway(gateway: Gateway): this {
|
|
131
|
+
this.gatewayManager.register(gateway);
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get the gateway manager
|
|
137
|
+
*/
|
|
138
|
+
getGatewayManager(): GatewayManager {
|
|
139
|
+
return this.gatewayManager;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get the security manager
|
|
144
|
+
*/
|
|
145
|
+
getSecurityManager(): SecurityManager {
|
|
146
|
+
return this.securityManager;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get configuration value
|
|
151
|
+
*/
|
|
152
|
+
getConfig<K extends keyof AppConfig>(key: K): AppConfig[K] {
|
|
153
|
+
return this.config[key];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get full configuration
|
|
158
|
+
*/
|
|
159
|
+
getAllConfig(): AppConfig {
|
|
160
|
+
return { ...this.config };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get the base path
|
|
165
|
+
*/
|
|
166
|
+
getBasePath(): string {
|
|
167
|
+
return this.basePath;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if application is booted
|
|
172
|
+
*/
|
|
173
|
+
isBooted(): boolean {
|
|
174
|
+
return this.booted;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Boot the application
|
|
179
|
+
*/
|
|
180
|
+
boot(): this {
|
|
181
|
+
if (this.booted) {
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Boot all plugins
|
|
186
|
+
this.pluginManager.boot();
|
|
187
|
+
|
|
188
|
+
// Boot all gateways
|
|
189
|
+
this.gatewayManager.boot();
|
|
190
|
+
|
|
191
|
+
this.booted = true;
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get environment
|
|
197
|
+
*/
|
|
198
|
+
environment(): string {
|
|
199
|
+
return this.config.env;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Check if in debug mode
|
|
204
|
+
*/
|
|
205
|
+
isDebug(): boolean {
|
|
206
|
+
return this.config.debug;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhoenixJS - Dependency Injection Container
|
|
3
|
+
*
|
|
4
|
+
* A simple IoC container for managing dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
type Factory<T> = () => T;
|
|
8
|
+
type Constructor<T> = new (...args: unknown[]) => T;
|
|
9
|
+
|
|
10
|
+
interface Binding<T> {
|
|
11
|
+
resolver: Factory<T>;
|
|
12
|
+
singleton: boolean;
|
|
13
|
+
instance?: T;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Container {
|
|
17
|
+
private bindings: Map<string, Binding<unknown>> = new Map();
|
|
18
|
+
private instances: Map<string, unknown> = new Map();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Register a binding in the container
|
|
22
|
+
*/
|
|
23
|
+
bind<T>(abstract: string, resolver: Factory<T>, singleton = false): this {
|
|
24
|
+
this.bindings.set(abstract, {
|
|
25
|
+
resolver,
|
|
26
|
+
singleton,
|
|
27
|
+
instance: undefined,
|
|
28
|
+
});
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Register a singleton binding
|
|
34
|
+
*/
|
|
35
|
+
singleton<T>(abstract: string, resolver: Factory<T>): this {
|
|
36
|
+
return this.bind(abstract, resolver, true);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Register an existing instance in the container
|
|
41
|
+
*/
|
|
42
|
+
instance<T>(abstract: string, instance: T): this {
|
|
43
|
+
this.instances.set(abstract, instance);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a binding from the container
|
|
49
|
+
*/
|
|
50
|
+
make<T>(abstract: string): T {
|
|
51
|
+
// Check if we have a direct instance
|
|
52
|
+
if (this.instances.has(abstract)) {
|
|
53
|
+
return this.instances.get(abstract) as T;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if we have a binding
|
|
57
|
+
const binding = this.bindings.get(abstract);
|
|
58
|
+
if (!binding) {
|
|
59
|
+
throw new Error(`No binding found for: ${abstract}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// If it's a singleton and we have an instance, return it
|
|
63
|
+
if (binding.singleton && binding.instance !== undefined) {
|
|
64
|
+
return binding.instance as T;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Resolve the binding
|
|
68
|
+
const instance = binding.resolver() as T;
|
|
69
|
+
|
|
70
|
+
// Store singleton instance
|
|
71
|
+
if (binding.singleton) {
|
|
72
|
+
binding.instance = instance;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return instance;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if a binding exists
|
|
80
|
+
*/
|
|
81
|
+
has(abstract: string): boolean {
|
|
82
|
+
return this.bindings.has(abstract) || this.instances.has(abstract);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Remove a binding from the container
|
|
87
|
+
*/
|
|
88
|
+
forget(abstract: string): void {
|
|
89
|
+
this.bindings.delete(abstract);
|
|
90
|
+
this.instances.delete(abstract);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Clear all bindings
|
|
95
|
+
*/
|
|
96
|
+
flush(): void {
|
|
97
|
+
this.bindings.clear();
|
|
98
|
+
this.instances.clear();
|
|
99
|
+
}
|
|
100
|
+
}
|