@h3ravel/core 1.21.6 → 1.22.0-alpha.1
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/dist/env.d.ts +1 -0
- package/dist/index.cjs +771 -320
- package/dist/index.d.ts +493 -148
- package/dist/index.js +746 -313
- package/package.json +6 -5
- package/dist/app.globals.d.ts +0 -84
package/dist/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import "reflect-metadata";
|
|
2
|
-
import { FileSystem, Logger, PathLoader } from "@h3ravel/shared";
|
|
3
|
-
import {
|
|
3
|
+
import { FileSystem, Logger, PathLoader, Resolver } from "@h3ravel/shared";
|
|
4
|
+
import { H3, serve } from "h3";
|
|
5
|
+
import { CKernel, IApplication, IContainer, IController, IKernel, IRegisterer } from "@h3ravel/contracts";
|
|
6
|
+
import { Arr, InvalidArgumentException, Obj, RuntimeException, ServiceProvider, Str, data_get, dd, dump, str } from "@h3ravel/support";
|
|
7
|
+
import { AppBuilder, ConfigException, Helpers, HttpException, Inject, Injectable, MiddlewareHandler, NotFoundHttpException } from "@h3ravel/foundation";
|
|
8
|
+
import { createRequire as createRequire$1 } from "module";
|
|
4
9
|
import fg from "fast-glob";
|
|
5
10
|
import path from "node:path";
|
|
6
11
|
import { detect } from "detect-port";
|
|
@@ -8,12 +13,90 @@ import dotenv from "dotenv";
|
|
|
8
13
|
import dotenvExpand from "dotenv-expand";
|
|
9
14
|
import { readFile } from "node:fs/promises";
|
|
10
15
|
import semver from "semver";
|
|
11
|
-
import {
|
|
16
|
+
import { Facades } from "@h3ravel/support/facades";
|
|
12
17
|
|
|
18
|
+
//#region src/Manager/ContainerResolver.ts
|
|
19
|
+
var ContainerResolver = class ContainerResolver {
|
|
20
|
+
constructor(app) {
|
|
21
|
+
this.app = app;
|
|
22
|
+
}
|
|
23
|
+
async resolveMethodParams(instance, method, ..._default) {
|
|
24
|
+
/**
|
|
25
|
+
* Get param types for instance method
|
|
26
|
+
*/
|
|
27
|
+
let params = Reflect.getMetadata("design:paramtypes", instance, String(method)) || [];
|
|
28
|
+
/**
|
|
29
|
+
* Ensure that the Application class is always available
|
|
30
|
+
*/
|
|
31
|
+
if (params.length < 1 && _default.length > 0) params = _default;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the bound dependencies
|
|
34
|
+
*/
|
|
35
|
+
const args = params.filter((e) => ContainerResolver.isClass(e) || e instanceof Application).map((type) => {
|
|
36
|
+
if (type instanceof Application) return type;
|
|
37
|
+
return this.app.make(type);
|
|
38
|
+
});
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
resolve(instance[method](...args));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
static isClass(C) {
|
|
44
|
+
return typeof C === "function" && C.prototype !== void 0 && Object.toString.call(C).substring(0, 5) === "class";
|
|
45
|
+
}
|
|
46
|
+
static isAbstract(C) {
|
|
47
|
+
return this.isClass(C) && C.name.startsWith("I");
|
|
48
|
+
}
|
|
49
|
+
static isCallable(C) {
|
|
50
|
+
return typeof C === "function" && !ContainerResolver.isClass(C);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
13
55
|
//#region src/Container.ts
|
|
14
|
-
var Container = class {
|
|
56
|
+
var Container = class Container extends IContainer {
|
|
15
57
|
bindings = /* @__PURE__ */ new Map();
|
|
16
58
|
singletons = /* @__PURE__ */ new Map();
|
|
59
|
+
middlewareHandler;
|
|
60
|
+
/**
|
|
61
|
+
* The current globally available container (if any).
|
|
62
|
+
*/
|
|
63
|
+
static instance;
|
|
64
|
+
/**
|
|
65
|
+
* All of the before resolving callbacks by class type.
|
|
66
|
+
*/
|
|
67
|
+
beforeResolvingCallbacks = /* @__PURE__ */ new Map();
|
|
68
|
+
/**
|
|
69
|
+
* All of the after resolving callbacks by class type.
|
|
70
|
+
*/
|
|
71
|
+
afterResolvingCallbacks = /* @__PURE__ */ new Map();
|
|
72
|
+
/**
|
|
73
|
+
* All of the registered rebound callbacks.
|
|
74
|
+
*/
|
|
75
|
+
reboundCallbacks = /* @__PURE__ */ new Map();
|
|
76
|
+
/**
|
|
77
|
+
* The container's shared instances.
|
|
78
|
+
*/
|
|
79
|
+
instances = /* @__PURE__ */ new Map();
|
|
80
|
+
/**
|
|
81
|
+
* The container's resolved instances.
|
|
82
|
+
*/
|
|
83
|
+
resolvedInstances = /* @__PURE__ */ new Map();
|
|
84
|
+
/**
|
|
85
|
+
* The registered type alias.
|
|
86
|
+
*/
|
|
87
|
+
aliases = /* @__PURE__ */ new Map();
|
|
88
|
+
/**
|
|
89
|
+
* The registered aliases keyed by the abstract name.
|
|
90
|
+
*/
|
|
91
|
+
abstractAliases = /* @__PURE__ */ new Map();
|
|
92
|
+
/**
|
|
93
|
+
* The registered aliases keyed by the abstract name.
|
|
94
|
+
*/
|
|
95
|
+
middlewares = /* @__PURE__ */ new Map();
|
|
96
|
+
/**
|
|
97
|
+
* The extension closures for services.
|
|
98
|
+
*/
|
|
99
|
+
extenders = /* @__PURE__ */ new Map();
|
|
17
100
|
static hasAnyDecorator(target) {
|
|
18
101
|
if (Reflect.getMetadataKeys(target).length > 0) return true;
|
|
19
102
|
const paramLength = target.length;
|
|
@@ -24,7 +107,22 @@ var Container = class {
|
|
|
24
107
|
this.bindings.set(key, factory);
|
|
25
108
|
}
|
|
26
109
|
/**
|
|
110
|
+
* Bind unregistered middlewares to the service container so we can use them later
|
|
111
|
+
*
|
|
112
|
+
* @param key
|
|
113
|
+
* @param middleware
|
|
114
|
+
*/
|
|
115
|
+
bindMiddleware(key, middleware) {
|
|
116
|
+
this.middlewares.set(key, middleware);
|
|
117
|
+
}
|
|
118
|
+
boundMiddlewares(key) {
|
|
119
|
+
if (key) return this.middlewares.get(key);
|
|
120
|
+
return this.middlewares.entries();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
27
123
|
* Remove one or more transient services from the container
|
|
124
|
+
*
|
|
125
|
+
* @param key
|
|
28
126
|
*/
|
|
29
127
|
unbind(key) {
|
|
30
128
|
if (Array.isArray(key)) for (let i = 0; i < key.length; i++) {
|
|
@@ -36,28 +134,127 @@ var Container = class {
|
|
|
36
134
|
this.singletons.delete(key);
|
|
37
135
|
}
|
|
38
136
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Bind a singleton service to the container
|
|
41
|
-
*/
|
|
42
137
|
singleton(key, factory) {
|
|
43
138
|
this.bindings.set(key, () => {
|
|
44
|
-
if (!this.singletons.has(key)) this.singletons.set(key, factory
|
|
139
|
+
if (!this.singletons.has(key)) this.singletons.set(key, this.call(factory));
|
|
45
140
|
return this.singletons.get(key);
|
|
46
141
|
});
|
|
47
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Read reflected param types, resolve dependencies from the container and
|
|
145
|
+
* optionally transform them, finally invoke the specified method on a class instance
|
|
146
|
+
*
|
|
147
|
+
* @param instance
|
|
148
|
+
* @param method
|
|
149
|
+
* @param defaultArgs
|
|
150
|
+
* @param handler
|
|
151
|
+
* @returns
|
|
152
|
+
*/
|
|
153
|
+
async invoke(instance, method, defaultArgs, handler) {
|
|
154
|
+
/**
|
|
155
|
+
* Get param types for the instance method
|
|
156
|
+
*/
|
|
157
|
+
const paramTypes = Reflect.getMetadata("design:paramtypes", instance, method) || [];
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the bound dependencies
|
|
160
|
+
*/
|
|
161
|
+
let args = await Promise.all(paramTypes.map(async (paramType) => {
|
|
162
|
+
const inst = this.make(paramType);
|
|
163
|
+
if (handler) return await handler(inst);
|
|
164
|
+
return inst;
|
|
165
|
+
}));
|
|
166
|
+
/**
|
|
167
|
+
* Ensure that the args is always filled
|
|
168
|
+
*/
|
|
169
|
+
if (args.length < 1) args = defaultArgs ?? [];
|
|
170
|
+
const fn = instance[method];
|
|
171
|
+
return Reflect.apply(fn, instance, args);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Read reflected param types, resolve dependencies from the container and return the result
|
|
175
|
+
*
|
|
176
|
+
* @param instance
|
|
177
|
+
* @param method
|
|
178
|
+
*/
|
|
179
|
+
resolveParams(instance, method) {
|
|
180
|
+
/**
|
|
181
|
+
* Resolve and return the bound dependencies
|
|
182
|
+
*/
|
|
183
|
+
return (Reflect.getMetadata("design:paramtypes", instance, method) || []).filter((e) => !!e).map((abstract) => {
|
|
184
|
+
if (typeof abstract === "function" && abstract.toString().startsWith("function Function")) return abstract;
|
|
185
|
+
return this.make(abstract);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
48
188
|
make(key) {
|
|
189
|
+
return this.resolve(key);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Resolve the gevein service from the container
|
|
193
|
+
*
|
|
194
|
+
* @param abstract
|
|
195
|
+
* @param raiseEvents
|
|
196
|
+
*/
|
|
197
|
+
resolve(abstract, raiseEvents = true) {
|
|
198
|
+
abstract = this.getAlias(abstract);
|
|
49
199
|
/**
|
|
50
200
|
* Direct factory binding
|
|
51
201
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
202
|
+
let resolved;
|
|
203
|
+
if (this.resolvedInstances.has(abstract)) return this.resolvedInstances.get(abstract);
|
|
204
|
+
if (raiseEvents) this.runBeforeResolvingCallbacks(abstract);
|
|
205
|
+
if (this.bindings.has(abstract)) resolved = this.bindings.get(abstract)();
|
|
206
|
+
else if (this.instances.has(abstract)) resolved = this.instances.get(abstract);
|
|
207
|
+
else if (typeof abstract === "function")
|
|
208
|
+
/**
|
|
54
209
|
* If this is a class constructor, auto-resolve via reflection
|
|
55
210
|
*/
|
|
56
|
-
|
|
57
|
-
throw new Error(`No binding found for key: ${typeof
|
|
211
|
+
resolved = this.build(abstract);
|
|
212
|
+
else throw new Error(`No binding found for key: ${typeof abstract === "string" ? abstract : abstract?.name}`);
|
|
213
|
+
/**
|
|
214
|
+
* If we defined any extenders for this type, we'll need to spin through them
|
|
215
|
+
* and apply them to the object being built. This allows for the extension
|
|
216
|
+
* of services, such as changing configuration or decorating the object.
|
|
217
|
+
*/
|
|
218
|
+
for (const extender of this.getExtenders(abstract)) resolved = extender(resolved, this);
|
|
219
|
+
if (raiseEvents) this.runAfterResolvingCallbacks(abstract, resolved);
|
|
220
|
+
this.resolvedInstances.set(abstract, resolved);
|
|
221
|
+
return resolved;
|
|
222
|
+
}
|
|
223
|
+
afterResolving(key, callback) {
|
|
224
|
+
const existing = this.afterResolvingCallbacks.get(key) || [];
|
|
225
|
+
existing.push(callback);
|
|
226
|
+
this.afterResolvingCallbacks.set(key, existing);
|
|
227
|
+
}
|
|
228
|
+
beforeResolving(key, callback) {
|
|
229
|
+
const existing = this.beforeResolvingCallbacks.get(key) || [];
|
|
230
|
+
existing.push(callback);
|
|
231
|
+
this.beforeResolvingCallbacks.set(key, existing);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Execute all registered beforeResolving callbacks for a given key
|
|
235
|
+
*
|
|
236
|
+
* @param key
|
|
237
|
+
* @param resolved
|
|
238
|
+
*/
|
|
239
|
+
runBeforeResolvingCallbacks(key) {
|
|
240
|
+
const callbacks = this.beforeResolvingCallbacks.get(key) || [];
|
|
241
|
+
for (let i = 0; i < callbacks.length; i++) callbacks[i](this);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Execute all registered afterResolving callbacks for a given key
|
|
245
|
+
*
|
|
246
|
+
* @param key
|
|
247
|
+
* @param resolved
|
|
248
|
+
*/
|
|
249
|
+
runAfterResolvingCallbacks(key, resolved) {
|
|
250
|
+
const callbacks = this.afterResolvingCallbacks.get(key) || [];
|
|
251
|
+
for (let i = 0; i < callbacks.length; i++) callbacks[i](resolved, this);
|
|
58
252
|
}
|
|
59
253
|
/**
|
|
60
254
|
* Automatically build a class with constructor dependency injection
|
|
255
|
+
*
|
|
256
|
+
* @param ClassType
|
|
257
|
+
* @returns
|
|
61
258
|
*/
|
|
62
259
|
build(ClassType) {
|
|
63
260
|
let dependencies = [];
|
|
@@ -65,44 +262,135 @@ var Container = class {
|
|
|
65
262
|
return this.make(alias);
|
|
66
263
|
});
|
|
67
264
|
else dependencies = (Reflect.getMetadata("design:paramtypes", ClassType) || []).map((dep) => this.make(dep));
|
|
265
|
+
if (dependencies.length === 0) dependencies = [this];
|
|
68
266
|
return new ClassType(...dependencies);
|
|
69
267
|
}
|
|
70
268
|
/**
|
|
71
|
-
*
|
|
269
|
+
* Determine if a given string is an alias.
|
|
270
|
+
*
|
|
271
|
+
* @param name
|
|
272
|
+
*/
|
|
273
|
+
isAlias(name) {
|
|
274
|
+
return this.aliases.has(name) && typeof this.aliases.get(name) !== "undefined";
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get the alias for an abstract if available.
|
|
278
|
+
*
|
|
279
|
+
* @param abstract
|
|
280
|
+
*/
|
|
281
|
+
getAlias(abstract) {
|
|
282
|
+
if (typeof abstract === "string" && this.aliases.has(abstract)) return this.getAlias(this.aliases.get(abstract));
|
|
283
|
+
if (abstract == null) return abstract;
|
|
284
|
+
return this.aliases.get(abstract) ?? abstract;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get the extender callbacks for a given type.
|
|
288
|
+
*
|
|
289
|
+
* @param abstract
|
|
72
290
|
*/
|
|
291
|
+
getExtenders(abstract) {
|
|
292
|
+
return this.extenders.get(this.getAlias(abstract)) ?? [];
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Remove all of the extender callbacks for a given type.
|
|
296
|
+
*
|
|
297
|
+
* @param abstract
|
|
298
|
+
*/
|
|
299
|
+
forgetExtenders(abstract) {
|
|
300
|
+
this.extenders.delete(this.getAlias(abstract));
|
|
301
|
+
}
|
|
302
|
+
alias(key, target) {
|
|
303
|
+
if (Array.isArray(key)) for (const [tokn, targ] of key) this.aliases.set(tokn, targ);
|
|
304
|
+
else this.aliases.set(key, target);
|
|
305
|
+
return this;
|
|
306
|
+
}
|
|
307
|
+
rebinding(abstract, callback) {
|
|
308
|
+
abstract = this.getAlias(abstract);
|
|
309
|
+
this.reboundCallbacks.set(abstract, this.reboundCallbacks.get(abstract)?.concat(callback) ?? [callback]);
|
|
310
|
+
if (this.bound(abstract)) return this.make(abstract);
|
|
311
|
+
}
|
|
312
|
+
bound(abstract) {
|
|
313
|
+
return this.bindings.has(abstract) || !!this.instances.get(abstract) || this.isAlias(abstract);
|
|
314
|
+
}
|
|
73
315
|
has(key) {
|
|
74
|
-
return this.
|
|
316
|
+
return this.bound(key);
|
|
75
317
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.
|
|
318
|
+
/**
|
|
319
|
+
* Determine if the given abstract type has been resolved.
|
|
320
|
+
*
|
|
321
|
+
* @param abstract
|
|
322
|
+
*/
|
|
323
|
+
resolved(abstract) {
|
|
324
|
+
if (this.isAlias(abstract)) abstract = this.getAlias(abstract);
|
|
325
|
+
return this.resolvedInstances.has(abstract) || this.instances.has(abstract);
|
|
83
326
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Resolve the bound dependencies
|
|
95
|
-
*/
|
|
96
|
-
const args = params.filter((e) => ContainerResolver.isClass(e) || e instanceof Application).map((type) => {
|
|
97
|
-
if (type instanceof Application) return type;
|
|
98
|
-
return this.app.make(type);
|
|
99
|
-
});
|
|
100
|
-
return new Promise((resolve) => {
|
|
101
|
-
resolve(instance[method](...args));
|
|
102
|
-
});
|
|
327
|
+
extend(abstract, closure) {
|
|
328
|
+
abstract = this.getAlias(abstract);
|
|
329
|
+
if (this.instances.has(abstract)) {
|
|
330
|
+
this.instances.set(abstract, closure(this.instances.get(abstract), this));
|
|
331
|
+
this.rebound(abstract);
|
|
332
|
+
} else {
|
|
333
|
+
this.extenders.set(abstract, this.extenders.get(abstract)?.concat(closure) ?? [closure]);
|
|
334
|
+
if (this.resolved(abstract)) this.rebound(abstract);
|
|
335
|
+
}
|
|
103
336
|
}
|
|
104
|
-
|
|
105
|
-
|
|
337
|
+
instance(abstract, instance) {
|
|
338
|
+
this.removeAbstractAlias(abstract);
|
|
339
|
+
const isBound = this.bound(abstract);
|
|
340
|
+
this.aliases.delete(abstract);
|
|
341
|
+
this.instances.set(abstract, instance);
|
|
342
|
+
if (isBound) this.rebound(abstract);
|
|
343
|
+
return instance;
|
|
344
|
+
}
|
|
345
|
+
call(callback) {
|
|
346
|
+
if (ContainerResolver.isClass(callback)) return this.make(callback);
|
|
347
|
+
return callback(this);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Fire the "rebound" callbacks for the given abstract type.
|
|
351
|
+
*
|
|
352
|
+
* @param abstract
|
|
353
|
+
*/
|
|
354
|
+
rebound(abstract) {
|
|
355
|
+
const callbacks = this.getReboundCallbacks(abstract);
|
|
356
|
+
if (!callbacks) return;
|
|
357
|
+
const instance = this.make(abstract);
|
|
358
|
+
for (const callback of callbacks) callback(this, instance);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get the rebound callbacks for a given type.
|
|
362
|
+
*
|
|
363
|
+
* @param abstract
|
|
364
|
+
*/
|
|
365
|
+
getReboundCallbacks(abstract) {
|
|
366
|
+
return this.reboundCallbacks.get(abstract) ?? [];
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Remove an alias from the contextual binding alias cache.
|
|
370
|
+
*
|
|
371
|
+
* @param searched
|
|
372
|
+
*/
|
|
373
|
+
removeAbstractAlias(searched) {
|
|
374
|
+
if (!this.aliases.has(searched)) return;
|
|
375
|
+
for (const [abstract, aliases] of this.abstractAliases.entries()) {
|
|
376
|
+
const filtered = aliases.filter((alias) => alias !== searched);
|
|
377
|
+
if (filtered.length > 0) this.abstractAliases.set(abstract, filtered);
|
|
378
|
+
else this.abstractAliases.delete(abstract);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get the globally available instance of the container.
|
|
383
|
+
*/
|
|
384
|
+
static getInstance() {
|
|
385
|
+
return this.instance ??= new Application(process.cwd(), "h3ravel");
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Set the shared instance of the container.
|
|
389
|
+
*
|
|
390
|
+
* @param container
|
|
391
|
+
*/
|
|
392
|
+
static setInstance(container) {
|
|
393
|
+
return Container.instance = container;
|
|
106
394
|
}
|
|
107
395
|
};
|
|
108
396
|
|
|
@@ -192,33 +480,25 @@ var ProviderRegistry = class {
|
|
|
192
480
|
* @returns
|
|
193
481
|
*/
|
|
194
482
|
static sort(providers) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
/**
|
|
203
|
-
* Handle before/after adjustments
|
|
204
|
-
*/
|
|
483
|
+
const makeKey = (Provider) => `${Provider.name}::${this.getKey(Provider)}`;
|
|
484
|
+
providers.sort((A, B) => (B.priority ?? 0) - (A.priority ?? 0));
|
|
485
|
+
const findIndex = (target) => {
|
|
486
|
+
if (target.includes("::")) return providers.findIndex((p) => makeKey(p) === target);
|
|
487
|
+
return providers.findIndex((p) => p.name === target);
|
|
488
|
+
};
|
|
205
489
|
providers.forEach((Provider) => {
|
|
206
490
|
const order = Provider.order;
|
|
207
491
|
if (!order) return;
|
|
208
|
-
const [direction,
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
*/
|
|
217
|
-
return providers.sort((A, B) => {
|
|
218
|
-
const keyA = this.getKey(A);
|
|
219
|
-
const keyB = this.getKey(B);
|
|
220
|
-
return (this.priorityMap.get(`${B.name}::${keyB}`) ?? 0) - (this.priorityMap.get(`${A.name}::${keyA}`) ?? 0);
|
|
492
|
+
const [direction, rawTarget] = order.split(":");
|
|
493
|
+
const targetIndex = findIndex(rawTarget);
|
|
494
|
+
if (targetIndex === -1) return;
|
|
495
|
+
const currentIndex = providers.indexOf(Provider);
|
|
496
|
+
if (currentIndex === -1) return;
|
|
497
|
+
providers.splice(currentIndex, 1);
|
|
498
|
+
const insertIndex = direction === "before" ? targetIndex : targetIndex + 1;
|
|
499
|
+
providers.splice(insertIndex, 0, Provider);
|
|
221
500
|
});
|
|
501
|
+
return providers;
|
|
222
502
|
}
|
|
223
503
|
/**
|
|
224
504
|
* Sort service providers
|
|
@@ -237,7 +517,8 @@ var ProviderRegistry = class {
|
|
|
237
517
|
*
|
|
238
518
|
* @param priorityMap
|
|
239
519
|
*/
|
|
240
|
-
static log(providers) {
|
|
520
|
+
static log(providers, enabled = true) {
|
|
521
|
+
if (!enabled) return;
|
|
241
522
|
const sorted = Array.from((providers ?? this.providers).values());
|
|
242
523
|
console.table(sorted.map((P) => ({
|
|
243
524
|
Name: P.constructor.name,
|
|
@@ -278,10 +559,10 @@ var ProviderRegistry = class {
|
|
|
278
559
|
const providers = [];
|
|
279
560
|
if (autoRegister) {
|
|
280
561
|
for (const manifestPath of manifests) {
|
|
281
|
-
const pkg =
|
|
562
|
+
const pkg = this.getManifest(path.resolve(manifestPath));
|
|
282
563
|
if (pkg.h3ravel?.providers) providers.push(...await Promise.all(pkg.h3ravel.providers.map(async (name) => (await import(path.resolve(path.dirname(manifestPath), "dist/index.js")))[name])));
|
|
283
564
|
}
|
|
284
|
-
for (const provider of providers) {
|
|
565
|
+
for (const provider of providers.filter((e) => typeof e !== "undefined")) {
|
|
285
566
|
const key = this.getKey(provider);
|
|
286
567
|
this.providers.set(key, provider);
|
|
287
568
|
}
|
|
@@ -294,22 +575,16 @@ var ProviderRegistry = class {
|
|
|
294
575
|
* @param manifestPath
|
|
295
576
|
* @returns
|
|
296
577
|
*/
|
|
297
|
-
static
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
pkg = (await import(manifestPath)).default;
|
|
301
|
-
} catch {
|
|
302
|
-
const { createRequire } = await import("module");
|
|
303
|
-
pkg = createRequire(import.meta.url)(manifestPath);
|
|
304
|
-
}
|
|
305
|
-
return pkg;
|
|
578
|
+
static getManifest(manifestPath) {
|
|
579
|
+
return createRequire$1(import.meta.url)(manifestPath);
|
|
306
580
|
}
|
|
307
581
|
};
|
|
308
582
|
|
|
309
583
|
//#endregion
|
|
310
584
|
//#region src/Registerer.ts
|
|
311
|
-
var Registerer = class Registerer {
|
|
585
|
+
var Registerer = class Registerer extends IRegisterer {
|
|
312
586
|
constructor(app) {
|
|
587
|
+
super();
|
|
313
588
|
this.app = app;
|
|
314
589
|
}
|
|
315
590
|
static register(app) {
|
|
@@ -341,31 +616,87 @@ var Registerer = class Registerer {
|
|
|
341
616
|
}
|
|
342
617
|
};
|
|
343
618
|
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/Providers/CoreServiceProvider.ts
|
|
621
|
+
/**
|
|
622
|
+
* Bootstraps core services and bindings.
|
|
623
|
+
*
|
|
624
|
+
* Bind essential services to the container (logger, config repository).
|
|
625
|
+
* Register app-level singletons.
|
|
626
|
+
* Set up exception handling.
|
|
627
|
+
*
|
|
628
|
+
* Auto-Registered
|
|
629
|
+
*/
|
|
630
|
+
var CoreServiceProvider = class extends ServiceProvider {
|
|
631
|
+
static priority = 999;
|
|
632
|
+
register() {
|
|
633
|
+
Object.assign(globalThis, { str });
|
|
634
|
+
this.app.alias(IApplication, Application);
|
|
635
|
+
}
|
|
636
|
+
boot() {}
|
|
637
|
+
};
|
|
638
|
+
|
|
344
639
|
//#endregion
|
|
345
640
|
//#region src/Application.ts
|
|
346
641
|
var Application = class Application extends Container {
|
|
642
|
+
/**
|
|
643
|
+
* Indicates if the application has "booted".
|
|
644
|
+
*/
|
|
645
|
+
#booted = false;
|
|
347
646
|
paths = new PathLoader();
|
|
348
647
|
context;
|
|
648
|
+
h3Event;
|
|
349
649
|
tries = 0;
|
|
350
|
-
booted = false;
|
|
351
650
|
basePath;
|
|
352
651
|
versions = {
|
|
353
652
|
app: "0.0.0",
|
|
354
653
|
ts: "0.0.0"
|
|
355
654
|
};
|
|
655
|
+
namespace;
|
|
356
656
|
static versions = {
|
|
357
657
|
app: "0.0.0",
|
|
358
658
|
ts: "0.0.0"
|
|
359
659
|
};
|
|
660
|
+
h3App;
|
|
360
661
|
providers = [];
|
|
361
662
|
externalProviders = [];
|
|
362
663
|
filteredProviders = [];
|
|
664
|
+
autoRegisterProviders = false;
|
|
665
|
+
/**
|
|
666
|
+
* The route resolver callback.
|
|
667
|
+
*/
|
|
668
|
+
uriResolver;
|
|
363
669
|
/**
|
|
364
670
|
* List of registered console commands
|
|
365
671
|
*/
|
|
366
672
|
registeredCommands = [];
|
|
367
|
-
|
|
673
|
+
/**
|
|
674
|
+
* The array of booted callbacks.
|
|
675
|
+
*/
|
|
676
|
+
bootedCallbacks = [];
|
|
677
|
+
/**
|
|
678
|
+
* The array of booting callbacks.
|
|
679
|
+
*/
|
|
680
|
+
bootingCallbacks = [];
|
|
681
|
+
/**
|
|
682
|
+
* The array of terminating callbacks.
|
|
683
|
+
*/
|
|
684
|
+
terminatingCallbacks = [];
|
|
685
|
+
/**
|
|
686
|
+
* Indicates if the application has been bootstrapped before.
|
|
687
|
+
*/
|
|
688
|
+
bootstrapped = false;
|
|
689
|
+
/**
|
|
690
|
+
* Controls logging
|
|
691
|
+
*/
|
|
692
|
+
logsDisabled = false;
|
|
693
|
+
/**
|
|
694
|
+
* The conrrent HttpContext
|
|
695
|
+
*/
|
|
696
|
+
httpContext;
|
|
697
|
+
constructor(basePath, initializer) {
|
|
368
698
|
super();
|
|
699
|
+
this.initializer = initializer;
|
|
369
700
|
dotenvExpand.expand(dotenv.config({ quiet: true }));
|
|
370
701
|
this.basePath = basePath;
|
|
371
702
|
this.setPath("base", basePath);
|
|
@@ -377,6 +708,7 @@ var Application = class Application extends Container {
|
|
|
377
708
|
* Register core bindings into the container
|
|
378
709
|
*/
|
|
379
710
|
registerBaseBindings() {
|
|
711
|
+
Application.setInstance(this);
|
|
380
712
|
this.bind(Application, () => this);
|
|
381
713
|
this.bind("path.base", () => this.basePath);
|
|
382
714
|
this.bind("load.paths", () => this.paths);
|
|
@@ -416,7 +748,7 @@ var Application = class Application extends Container {
|
|
|
416
748
|
* Full-Stack App: Installs database, mail, queue, cache → they self-register via their providers.
|
|
417
749
|
*/
|
|
418
750
|
async getConfiguredProviders() {
|
|
419
|
-
return [
|
|
751
|
+
return [CoreServiceProvider];
|
|
420
752
|
}
|
|
421
753
|
async getAllProviders() {
|
|
422
754
|
return [...await this.getConfiguredProviders(), ...this.externalProviders];
|
|
@@ -430,22 +762,30 @@ var Application = class Application extends Container {
|
|
|
430
762
|
*
|
|
431
763
|
* @returns
|
|
432
764
|
*/
|
|
433
|
-
|
|
765
|
+
initialize(providers, filtered = [], autoRegisterProviders = true) {
|
|
766
|
+
/**
|
|
767
|
+
* Bind HTTP APP to the service container
|
|
768
|
+
*/
|
|
769
|
+
this.singleton("http.app", () => {
|
|
770
|
+
return new H3();
|
|
771
|
+
});
|
|
772
|
+
/**
|
|
773
|
+
* Bind the HTTP server to the service container
|
|
774
|
+
*/
|
|
775
|
+
this.singleton("http.serve", () => serve);
|
|
434
776
|
this.registerProviders(providers, filtered);
|
|
435
|
-
|
|
436
|
-
return this
|
|
777
|
+
this.autoRegisterProviders = autoRegisterProviders;
|
|
778
|
+
return this;
|
|
437
779
|
}
|
|
438
780
|
/**
|
|
439
781
|
* Dynamically register all configured providers
|
|
440
|
-
*
|
|
441
|
-
* @param autoRegister If set to false, service providers will not be auto discovered and registered.
|
|
442
782
|
*/
|
|
443
|
-
async registerConfiguredProviders(
|
|
783
|
+
async registerConfiguredProviders() {
|
|
444
784
|
const providers = await this.getAllProviders();
|
|
445
785
|
ProviderRegistry.setSortable(false);
|
|
446
786
|
ProviderRegistry.setFiltered(this.filteredProviders);
|
|
447
787
|
ProviderRegistry.registerMany(providers);
|
|
448
|
-
if (
|
|
788
|
+
if (this.autoRegisterProviders) await ProviderRegistry.discoverProviders(this.autoRegisterProviders);
|
|
449
789
|
ProviderRegistry.doSort();
|
|
450
790
|
for (const ProviderClass of ProviderRegistry.all()) {
|
|
451
791
|
if (!ProviderClass) continue;
|
|
@@ -461,7 +801,7 @@ var Application = class Application extends Container {
|
|
|
461
801
|
*/
|
|
462
802
|
registerProviders(providers, filtered = []) {
|
|
463
803
|
this.externalProviders.push(...providers);
|
|
464
|
-
this.filteredProviders = filtered;
|
|
804
|
+
this.filteredProviders = Array.from(new Set(this.filteredProviders.concat(filtered)));
|
|
465
805
|
}
|
|
466
806
|
/**
|
|
467
807
|
* Register a provider
|
|
@@ -486,44 +826,237 @@ var Application = class Application extends Container {
|
|
|
486
826
|
runningInConsole() {
|
|
487
827
|
return typeof process !== "undefined" && !!process.stdout && !!process.stdin;
|
|
488
828
|
}
|
|
829
|
+
/**
|
|
830
|
+
* checks if the application is running in Unit Test
|
|
831
|
+
*/
|
|
832
|
+
runningUnitTests() {
|
|
833
|
+
return process.env.VITEST === "true";
|
|
834
|
+
}
|
|
489
835
|
getRuntimeEnv() {
|
|
490
836
|
if (typeof window !== "undefined" && typeof document !== "undefined") return "browser";
|
|
491
837
|
if (typeof process !== "undefined" && process.versions?.node) return "node";
|
|
492
838
|
return "unknown";
|
|
493
839
|
}
|
|
494
840
|
/**
|
|
841
|
+
* Determine if the application has booted.
|
|
842
|
+
*/
|
|
843
|
+
isBooted() {
|
|
844
|
+
return this.#booted;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Determine if the application has booted.
|
|
848
|
+
*/
|
|
849
|
+
logging(logging = true) {
|
|
850
|
+
this.logsDisabled = !logging;
|
|
851
|
+
return this;
|
|
852
|
+
}
|
|
853
|
+
logsEnabled() {
|
|
854
|
+
if (this.logsDisabled) return false;
|
|
855
|
+
return (process.env.APP_DEBUG === "true" && process.env.EXTENDED_DEBUG !== "false" || Number(process.env.VERBOSE) > 1) && !this.providers.some((e) => e.runsInConsole);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
495
858
|
* Boot all service providers after registration
|
|
496
859
|
*/
|
|
497
860
|
async boot() {
|
|
498
|
-
if (this
|
|
861
|
+
if (this.#booted) return this;
|
|
862
|
+
this.fireAppCallbacks(this.bootingCallbacks);
|
|
863
|
+
/**
|
|
864
|
+
* Register all the configured service providers
|
|
865
|
+
*/
|
|
866
|
+
await this.registerConfiguredProviders();
|
|
499
867
|
/**
|
|
500
868
|
* If debug is enabled, let's show the loaded service provider info
|
|
501
869
|
*/
|
|
502
|
-
|
|
503
|
-
for (const provider of this.providers) if (provider.boot)
|
|
870
|
+
ProviderRegistry.log(this.providers, this.logsEnabled());
|
|
871
|
+
for (const provider of this.providers) if (provider.boot) {
|
|
872
|
+
if (Container.hasAnyDecorator(provider.boot))
|
|
504
873
|
/**
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
874
|
+
* If the service provider is decorated use the IoC container
|
|
875
|
+
*/
|
|
876
|
+
await this.make(provider.boot);
|
|
877
|
+
else
|
|
509
878
|
/**
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
879
|
+
* Otherwise instantiate manually so that we can at least
|
|
880
|
+
* pass the app instance
|
|
881
|
+
*/
|
|
882
|
+
await provider.boot(this);
|
|
883
|
+
if (provider.callBootedCallbacks) await provider.callBootedCallbacks();
|
|
884
|
+
}
|
|
885
|
+
this.#booted = true;
|
|
886
|
+
this.fireAppCallbacks(this.bootedCallbacks);
|
|
887
|
+
return this;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Register a new boot listener.
|
|
891
|
+
*
|
|
892
|
+
* @param callable $callback
|
|
893
|
+
*/
|
|
894
|
+
booting(callback) {
|
|
895
|
+
this.bootingCallbacks.push(callback);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Register a new "booted" listener.
|
|
899
|
+
*
|
|
900
|
+
* @param callback
|
|
901
|
+
*/
|
|
902
|
+
booted(callback) {
|
|
903
|
+
this.bootedCallbacks.push(callback);
|
|
904
|
+
if (this.isBooted()) callback(this);
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Throw an HttpException with the given data.
|
|
908
|
+
*
|
|
909
|
+
* @param code
|
|
910
|
+
* @param message
|
|
911
|
+
* @param headers
|
|
912
|
+
*
|
|
913
|
+
* @throws {HttpException}
|
|
914
|
+
* @throws {NotFoundHttpException}
|
|
915
|
+
*/
|
|
916
|
+
abort(code, message = "", headers = {}) {
|
|
917
|
+
if (code == 404) throw new NotFoundHttpException(message, void 0, 0, headers);
|
|
918
|
+
throw new HttpException(code, message, void 0, headers);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Register a terminating callback with the application.
|
|
922
|
+
*
|
|
923
|
+
* @param callback
|
|
924
|
+
*/
|
|
925
|
+
terminating(callback) {
|
|
926
|
+
this.terminatingCallbacks.push(callback);
|
|
927
|
+
return this;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Terminate the application.
|
|
931
|
+
*/
|
|
932
|
+
terminate() {
|
|
933
|
+
let index = 0;
|
|
934
|
+
while (index < this.terminatingCallbacks.length) {
|
|
935
|
+
this.call(this.terminatingCallbacks[index]);
|
|
936
|
+
index++;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Call the booting callbacks for the application.
|
|
941
|
+
*
|
|
942
|
+
* @param callbacks
|
|
943
|
+
*/
|
|
944
|
+
fireAppCallbacks(callbacks) {
|
|
945
|
+
let index = 0;
|
|
946
|
+
while (index < callbacks.length) {
|
|
947
|
+
callbacks[index](this);
|
|
948
|
+
index++;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Handle the incoming HTTP request and send the response to the browser.
|
|
953
|
+
*
|
|
954
|
+
* @param config Configuration option to pass to the initializer
|
|
955
|
+
*/
|
|
956
|
+
async handleRequest(config) {
|
|
957
|
+
this.h3App?.all("/**", async (event) => {
|
|
958
|
+
this.context = (event$1) => this.buildContext(event$1, config);
|
|
959
|
+
this.h3Event = event;
|
|
960
|
+
const context = await this.context(event);
|
|
961
|
+
const kernel = this.make(IKernel);
|
|
962
|
+
this.bind("http.context", () => context);
|
|
963
|
+
this.bind("http.request", () => context.request);
|
|
964
|
+
this.bind("http.response", () => context.response);
|
|
965
|
+
const response = await kernel.handle(context.request);
|
|
966
|
+
if (response) this.bind("http.response", () => response);
|
|
967
|
+
kernel.terminate(context.request, response);
|
|
968
|
+
let finalResponse;
|
|
969
|
+
if (response && ["Response", "JsonResponse"].includes(response.constructor.name)) finalResponse = response.prepare(context.request).send();
|
|
970
|
+
else finalResponse = response;
|
|
971
|
+
return finalResponse;
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Build the http context
|
|
976
|
+
*
|
|
977
|
+
* @param event
|
|
978
|
+
* @param config
|
|
979
|
+
* @returns
|
|
980
|
+
*/
|
|
981
|
+
async buildContext(event, config, fresh = false) {
|
|
982
|
+
const { HttpContext, Request, Response } = await import("@h3ravel/http");
|
|
983
|
+
event = config?.h3Event ?? event;
|
|
984
|
+
if (!fresh && event._h3ravelContext) return event._h3ravelContext;
|
|
985
|
+
Request.enableHttpMethodParameterOverride();
|
|
986
|
+
const ctx = HttpContext.init({
|
|
987
|
+
app: this,
|
|
988
|
+
request: await Request.create(event, this),
|
|
989
|
+
response: new Response(this, event)
|
|
990
|
+
}, event);
|
|
991
|
+
event._h3ravelContext = ctx;
|
|
992
|
+
return ctx;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Handle the incoming Artisan command.
|
|
996
|
+
*/
|
|
997
|
+
async handleCommand() {
|
|
998
|
+
const kernel = this.make(CKernel);
|
|
999
|
+
const status = await kernel.handle();
|
|
1000
|
+
kernel.terminate(status);
|
|
1001
|
+
return status;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Get the URI resolver callback.
|
|
1005
|
+
*/
|
|
1006
|
+
getUriResolver() {
|
|
1007
|
+
return this.uriResolver ?? (() => void 0);
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Set the URI resolver callback.
|
|
1011
|
+
*
|
|
1012
|
+
* @param callback
|
|
1013
|
+
*/
|
|
1014
|
+
setUriResolver(callback) {
|
|
1015
|
+
this.uriResolver = callback;
|
|
515
1016
|
return this;
|
|
516
1017
|
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Determine if middleware has been disabled for the application.
|
|
1020
|
+
*/
|
|
1021
|
+
shouldSkipMiddleware() {
|
|
1022
|
+
return this.bound("middleware.disable") && this.make("middleware.disable") === true;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Provide safe overides for the app
|
|
1026
|
+
*/
|
|
1027
|
+
configure() {
|
|
1028
|
+
return new AppBuilder(this).withKernels().withCommands();
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Check if the current application environment matches the one provided
|
|
1032
|
+
*/
|
|
1033
|
+
environment(env$1) {
|
|
1034
|
+
return this.make("config").get("app.env") === env$1;
|
|
1035
|
+
}
|
|
517
1036
|
async fire(h3App, preferredPort) {
|
|
1037
|
+
if (h3App) this.h3App = h3App;
|
|
1038
|
+
if (!this?.h3App) throw new ConfigException("[Provide a H3 app instance in the config or install @h3ravel/http]");
|
|
1039
|
+
return await this.serve(this.h3App, preferredPort);
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Fire up the developement server using the user provided arguments
|
|
1043
|
+
*
|
|
1044
|
+
* Port will be auto assigned if provided one is not available
|
|
1045
|
+
*
|
|
1046
|
+
* @param h3App The current H3 app instance
|
|
1047
|
+
* @param preferedPort If provided, this will overide the port set in the evironment
|
|
1048
|
+
*/
|
|
1049
|
+
async serve(h3App, preferredPort) {
|
|
518
1050
|
if (!h3App) throw new InvalidArgumentException("No valid H3 app instance was provided.");
|
|
519
|
-
|
|
1051
|
+
await this.boot();
|
|
1052
|
+
const serve$1 = this.make("http.serve");
|
|
520
1053
|
const port = preferredPort ?? env("PORT", 3e3);
|
|
521
1054
|
const tries = env("RETRIES", 1);
|
|
522
1055
|
const hostname = env("HOSTNAME", "localhost");
|
|
523
1056
|
try {
|
|
524
1057
|
const realPort = await detect(port);
|
|
525
1058
|
if (port == realPort) {
|
|
526
|
-
const server = serve(h3App, {
|
|
1059
|
+
const server = serve$1(h3App, {
|
|
527
1060
|
port,
|
|
528
1061
|
hostname,
|
|
529
1062
|
silent: true
|
|
@@ -543,6 +1076,66 @@ var Application = class Application extends Container {
|
|
|
543
1076
|
return this;
|
|
544
1077
|
}
|
|
545
1078
|
/**
|
|
1079
|
+
* Run the given array of bootstrap classes.
|
|
1080
|
+
*
|
|
1081
|
+
* @param bootstrappers
|
|
1082
|
+
*/
|
|
1083
|
+
async bootstrapWith(bootstrappers) {
|
|
1084
|
+
for (const bootstrapper of bootstrappers) {
|
|
1085
|
+
if (this.has("app.events")) this.make("app.events").dispatch("bootstrapping: " + bootstrapper.name, [this]);
|
|
1086
|
+
await this.make(bootstrapper).bootstrap(this);
|
|
1087
|
+
if (this.has("app.events")) this.make("app.events").dispatch("bootstrapped: " + bootstrapper.name, [this]);
|
|
1088
|
+
}
|
|
1089
|
+
this.bootstrapped = true;
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Determine if the application has been bootstrapped before.
|
|
1093
|
+
*/
|
|
1094
|
+
hasBeenBootstrapped() {
|
|
1095
|
+
return this.bootstrapped;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Save the curretn H3 instance for possible future use.
|
|
1099
|
+
*
|
|
1100
|
+
* @param h3App The current H3 app instance
|
|
1101
|
+
* @returns
|
|
1102
|
+
*/
|
|
1103
|
+
setH3App(h3App) {
|
|
1104
|
+
this.h3App = h3App;
|
|
1105
|
+
return this;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Set the HttpContext.
|
|
1109
|
+
*
|
|
1110
|
+
* @param ctx
|
|
1111
|
+
*/
|
|
1112
|
+
setHttpContext(ctx) {
|
|
1113
|
+
this.httpContext = ctx;
|
|
1114
|
+
return this;
|
|
1115
|
+
}
|
|
1116
|
+
getHttpContext(key) {
|
|
1117
|
+
return key ? this.httpContext?.[key] : this.httpContext;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Get the application namespace.
|
|
1121
|
+
*
|
|
1122
|
+
* @throws {RuntimeException}
|
|
1123
|
+
*/
|
|
1124
|
+
getNamespace() {
|
|
1125
|
+
if (this.namespace != null) return this.namespace;
|
|
1126
|
+
const pkg = createRequire(import.meta.url)(path.join(process.cwd(), "package.json"));
|
|
1127
|
+
for (const [namespace, pathChoice] of Object.entries(data_get(pkg, "autoload.namespaces"))) if (this.getPath("app", "/") === this.getPath("src", pathChoice)) return this.namespace = namespace;
|
|
1128
|
+
throw new RuntimeException("Unable to detect application namespace.");
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Get the path of the app dir
|
|
1132
|
+
*
|
|
1133
|
+
* @returns
|
|
1134
|
+
*/
|
|
1135
|
+
path() {
|
|
1136
|
+
return this.getPath("app");
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
546
1139
|
* Get the base path of the app
|
|
547
1140
|
*
|
|
548
1141
|
* @returns
|
|
@@ -593,52 +1186,14 @@ var Application = class Application extends Container {
|
|
|
593
1186
|
/**
|
|
594
1187
|
* Base controller class
|
|
595
1188
|
*/
|
|
596
|
-
var Controller = class {
|
|
1189
|
+
var Controller = class extends IController {
|
|
597
1190
|
app;
|
|
598
1191
|
constructor(app) {
|
|
1192
|
+
super();
|
|
599
1193
|
this.app = app;
|
|
600
1194
|
}
|
|
601
1195
|
};
|
|
602
1196
|
|
|
603
|
-
//#endregion
|
|
604
|
-
//#region src/Di/Inject.ts
|
|
605
|
-
function Inject(...dependencies) {
|
|
606
|
-
return function(target) {
|
|
607
|
-
target.__inject__ = dependencies;
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Allows binding dependencies to both class and class methods
|
|
612
|
-
*
|
|
613
|
-
* @returns
|
|
614
|
-
*/
|
|
615
|
-
function Injectable() {
|
|
616
|
-
return (...args) => {
|
|
617
|
-
if (args.length === 1) args[0];
|
|
618
|
-
if (args.length === 3) {
|
|
619
|
-
args[0];
|
|
620
|
-
args[1];
|
|
621
|
-
args[2];
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
//#endregion
|
|
627
|
-
//#region src/Exceptions/ConfigException.ts
|
|
628
|
-
var ConfigException = class extends Error {
|
|
629
|
-
key;
|
|
630
|
-
constructor(key, type = "config", cause) {
|
|
631
|
-
const info = {
|
|
632
|
-
any: `${key} not configured`,
|
|
633
|
-
env: `${key} environment variable not configured`,
|
|
634
|
-
config: `${key} config not set`
|
|
635
|
-
};
|
|
636
|
-
const message = Logger.log([["ERROR:", "bgRed"], [info[type], "white"]], " ", false);
|
|
637
|
-
super(message, { cause });
|
|
638
|
-
this.key = key;
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
|
|
642
1197
|
//#endregion
|
|
643
1198
|
//#region src/H3ravel.ts
|
|
644
1199
|
/**
|
|
@@ -652,56 +1207,23 @@ const h3ravel = async (providers = [], basePath = process.cwd(), config = {
|
|
|
652
1207
|
initialize: false,
|
|
653
1208
|
autoload: false,
|
|
654
1209
|
filteredProviders: []
|
|
655
|
-
}
|
|
1210
|
+
}) => {
|
|
656
1211
|
let h3App;
|
|
657
|
-
const app = new Application(basePath);
|
|
658
|
-
|
|
1212
|
+
const app = new Application(basePath, "h3ravel");
|
|
1213
|
+
if (config.customPaths) for (const [name, path$1] of Object.entries(config.customPaths)) app.setPath(name, path$1);
|
|
1214
|
+
app.initialize(providers, config.filteredProviders, config.autoload);
|
|
659
1215
|
try {
|
|
660
1216
|
h3App = app.make("http.app");
|
|
661
|
-
app.
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
request: await Request.create(event, app),
|
|
667
|
-
response: new Response(event, app)
|
|
668
|
-
});
|
|
669
|
-
event._h3ravelContext = ctx;
|
|
670
|
-
return ctx;
|
|
671
|
-
};
|
|
672
|
-
const kernel = new Kernel(async (event) => app.context(event), [new LogRequests()]);
|
|
673
|
-
h3App.use((event) => kernel.handle(event, middleware));
|
|
1217
|
+
app.setH3App(h3App);
|
|
1218
|
+
app.singleton(IApplication, () => app);
|
|
1219
|
+
if (!Facades.getApplication()) Facades.setApplication(app);
|
|
1220
|
+
if (!Helpers.isLoaded()) Helpers.load(app);
|
|
1221
|
+
await app.handleRequest(config);
|
|
674
1222
|
} catch {
|
|
675
1223
|
if (!h3App && config.h3) h3App = config.h3;
|
|
676
1224
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
return new Proxy(appRef, {
|
|
680
|
-
get(target, prop, receiver) {
|
|
681
|
-
if (prop === "fire") return orig;
|
|
682
|
-
return Reflect.get(target, prop, receiver);
|
|
683
|
-
},
|
|
684
|
-
has(target, prop) {
|
|
685
|
-
if (prop === "fire") return true;
|
|
686
|
-
return Reflect.has(target, prop);
|
|
687
|
-
},
|
|
688
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
689
|
-
if (prop === "fire") return {
|
|
690
|
-
configurable: true,
|
|
691
|
-
enumerable: false,
|
|
692
|
-
writable: true,
|
|
693
|
-
value: orig
|
|
694
|
-
};
|
|
695
|
-
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
696
|
-
}
|
|
697
|
-
});
|
|
698
|
-
})(app, originalFire);
|
|
699
|
-
if (config.initialize && h3App) return await Reflect.apply(originalFire, app, [h3App]);
|
|
700
|
-
app.fire = async function() {
|
|
701
|
-
if (!h3App) throw new ConfigException("Provide a H3 app instance in the config or install @h3ravel/http");
|
|
702
|
-
return await Reflect.apply(originalFire, proxyThis, [h3App]);
|
|
703
|
-
};
|
|
704
|
-
return app;
|
|
1225
|
+
if (config.initialize && h3App) return await app.fire(h3App);
|
|
1226
|
+
return app.setH3App(h3App);
|
|
705
1227
|
};
|
|
706
1228
|
|
|
707
1229
|
//#endregion
|
|
@@ -712,12 +1234,23 @@ const h3ravel = async (providers = [], basePath = process.cwd(), config = {
|
|
|
712
1234
|
*/
|
|
713
1235
|
var Kernel = class {
|
|
714
1236
|
/**
|
|
715
|
-
*
|
|
1237
|
+
* The router instance.
|
|
1238
|
+
*/
|
|
1239
|
+
router;
|
|
1240
|
+
/**
|
|
1241
|
+
* A factory function that converts an H3Event into an HttpContext.
|
|
1242
|
+
*/
|
|
1243
|
+
context;
|
|
1244
|
+
applicationContext;
|
|
1245
|
+
/**
|
|
1246
|
+
* @param app - The current application instance
|
|
716
1247
|
* @param middleware - An array of middleware classes that will be executed in sequence.
|
|
717
1248
|
*/
|
|
718
|
-
constructor(
|
|
719
|
-
this.
|
|
1249
|
+
constructor(app, middleware = []) {
|
|
1250
|
+
this.app = app;
|
|
720
1251
|
this.middleware = middleware;
|
|
1252
|
+
this.router = app.make("router");
|
|
1253
|
+
this.context = async (event) => app.context(event);
|
|
721
1254
|
}
|
|
722
1255
|
/**
|
|
723
1256
|
* Handles an incoming request and passes it through middleware before invoking the next handler.
|
|
@@ -727,145 +1260,45 @@ var Kernel = class {
|
|
|
727
1260
|
* @returns A promise resolving to the result of the request pipeline.
|
|
728
1261
|
*/
|
|
729
1262
|
async handle(event, next) {
|
|
1263
|
+
const { request } = await this.app.context(event);
|
|
730
1264
|
/**
|
|
731
1265
|
* Convert the raw event into a standardized HttpContext
|
|
732
1266
|
*/
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
* Bind HTTP Response instance to the service container
|
|
737
|
-
*/
|
|
738
|
-
app.bind("http.response", () => {
|
|
739
|
-
return ctx.response;
|
|
740
|
-
});
|
|
741
|
-
/**
|
|
742
|
-
* Bind HTTP Request instance to the service container
|
|
1267
|
+
this.applicationContext = await this.context(event);
|
|
1268
|
+
/**
|
|
1269
|
+
* Bind HttpContext, request, and response to the container
|
|
743
1270
|
*/
|
|
744
|
-
app.bind("http.
|
|
745
|
-
|
|
746
|
-
|
|
1271
|
+
this.app.bind("http.context", () => this.applicationContext);
|
|
1272
|
+
this.app.bind("http.request", () => this.applicationContext.request);
|
|
1273
|
+
this.app.bind("http.response", () => this.applicationContext.response);
|
|
1274
|
+
this.app.middlewareHandler = this.app.has(MiddlewareHandler) ? this.app.make(MiddlewareHandler) : new MiddlewareHandler([], this.app);
|
|
1275
|
+
request.constructor.enableHttpMethodParameterOverride();
|
|
747
1276
|
/**
|
|
748
1277
|
* Run middleware stack and obtain result
|
|
749
1278
|
*/
|
|
750
|
-
const result = await this.
|
|
1279
|
+
const result = await this.app.middlewareHandler.register(this.middleware).run(this.applicationContext, next);
|
|
751
1280
|
/**
|
|
752
1281
|
* If a plain object is returned from a controller or middleware,
|
|
753
1282
|
* automatically set the JSON Content-Type header for the response.
|
|
754
1283
|
*/
|
|
755
|
-
if (result !== void 0 &&
|
|
1284
|
+
if (result !== void 0 && Obj.isPlainObject(result, true) && !result?.headers) event.res.headers.set("Content-Type", "application/json; charset=UTF-8");
|
|
756
1285
|
return result;
|
|
757
1286
|
}
|
|
758
1287
|
/**
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* @param context - The standardized HttpContext.
|
|
762
|
-
* @param next - Callback to execute when middleware completes.
|
|
763
|
-
* @returns A promise resolving to the final handler's result.
|
|
764
|
-
*/
|
|
765
|
-
async runMiddleware(context, next) {
|
|
766
|
-
let index = -1;
|
|
767
|
-
const runner = async (i) => {
|
|
768
|
-
if (i <= index) throw new Error("next() called multiple times");
|
|
769
|
-
index = i;
|
|
770
|
-
const middleware = this.middleware[i];
|
|
771
|
-
if (middleware)
|
|
772
|
-
/**
|
|
773
|
-
* Execute the current middleware and proceed to the next one
|
|
774
|
-
*/
|
|
775
|
-
return middleware.handle(context, () => runner(i + 1));
|
|
776
|
-
else
|
|
777
|
-
/**
|
|
778
|
-
* If no more middleware, call the final handler
|
|
779
|
-
*/
|
|
780
|
-
return next(context);
|
|
781
|
-
};
|
|
782
|
-
return runner(0);
|
|
783
|
-
}
|
|
784
|
-
/**
|
|
785
|
-
* Utility function to determine if a value is a plain object or array.
|
|
786
|
-
*
|
|
787
|
-
* @param value - The value to check.
|
|
788
|
-
* @returns True if the value is a plain object or array, otherwise false.
|
|
789
|
-
*/
|
|
790
|
-
isPlainObject(value) {
|
|
791
|
-
return typeof value === "object" && value !== null && (value.constructor === Object || value.constructor === Array);
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
|
|
795
|
-
//#endregion
|
|
796
|
-
//#region src/ServiceProvider.ts
|
|
797
|
-
const Inference = class {};
|
|
798
|
-
var ServiceProvider = class extends Inference {
|
|
799
|
-
/**
|
|
800
|
-
* The current app instance
|
|
801
|
-
*/
|
|
802
|
-
app;
|
|
803
|
-
/**
|
|
804
|
-
* Unique Identifier for the service providers
|
|
805
|
-
*/
|
|
806
|
-
static uid;
|
|
807
|
-
/**
|
|
808
|
-
* Sort order
|
|
809
|
-
*/
|
|
810
|
-
static order;
|
|
811
|
-
/**
|
|
812
|
-
* Sort priority
|
|
813
|
-
*/
|
|
814
|
-
static priority = 0;
|
|
815
|
-
/**
|
|
816
|
-
* Indicate that this service provider only runs in console
|
|
817
|
-
*/
|
|
818
|
-
static console = false;
|
|
819
|
-
/**
|
|
820
|
-
* List of registered console commands
|
|
821
|
-
*/
|
|
822
|
-
registeredCommands;
|
|
823
|
-
constructor(app) {
|
|
824
|
-
super();
|
|
825
|
-
this.app = app;
|
|
826
|
-
}
|
|
827
|
-
/**
|
|
828
|
-
* Register the listed service providers.
|
|
829
|
-
*
|
|
830
|
-
* @param commands An array of console commands to register.
|
|
831
|
-
*
|
|
832
|
-
* @deprecated since version 1.16.0. Will be removed in future versions, use `registerCommands` instead
|
|
833
|
-
*/
|
|
834
|
-
commands(commands) {
|
|
835
|
-
this.registerCommands(commands);
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Register the listed service providers.
|
|
839
|
-
*
|
|
840
|
-
* @param commands An array of console commands to register.
|
|
1288
|
+
* Resolve the provided callback using the current H3 event instance
|
|
841
1289
|
*/
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
* Bind essential services to the container (logger, config repository).
|
|
853
|
-
* Register app-level singletons.
|
|
854
|
-
* Set up exception handling.
|
|
855
|
-
*
|
|
856
|
-
* Auto-Registered
|
|
857
|
-
*/
|
|
858
|
-
var CoreServiceProvider = class extends ServiceProvider {
|
|
859
|
-
static priority = 999;
|
|
860
|
-
register() {
|
|
861
|
-
Object.assign(globalThis, { str });
|
|
862
|
-
}
|
|
863
|
-
boot() {
|
|
864
|
-
try {
|
|
865
|
-
Object.assign(globalThis, { asset: this.app.make("asset") });
|
|
866
|
-
} catch {}
|
|
1290
|
+
async resolve(event, middleware, handler) {
|
|
1291
|
+
const { Response } = await import("@h3ravel/http");
|
|
1292
|
+
this.middleware = Array.from(new Set([...this.middleware, ...Arr.wrap(middleware)]));
|
|
1293
|
+
return this.handle(event, (ctx) => new Promise((resolve) => {
|
|
1294
|
+
if (Resolver.isAsyncFunction(handler)) handler(ctx).then((response) => {
|
|
1295
|
+
if (response instanceof Response) resolve(response.prepare(ctx.request).send());
|
|
1296
|
+
else resolve(response);
|
|
1297
|
+
});
|
|
1298
|
+
else resolve(handler(ctx));
|
|
1299
|
+
}));
|
|
867
1300
|
}
|
|
868
1301
|
};
|
|
869
1302
|
|
|
870
1303
|
//#endregion
|
|
871
|
-
export { Application,
|
|
1304
|
+
export { Application, Container, ContainerResolver, Controller, CoreServiceProvider, Inject, Injectable, Kernel, ProviderRegistry, Registerer, ServiceProvider, h3ravel };
|