millas 0.2.12-beta → 0.2.12-beta-2
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/package.json +3 -16
- package/src/admin/ActivityLog.js +153 -52
- package/src/admin/Admin.js +400 -167
- package/src/admin/AdminAuth.js +213 -98
- package/src/admin/FormGenerator.js +372 -0
- package/src/admin/HookRegistry.js +256 -0
- package/src/admin/QueryEngine.js +263 -0
- package/src/admin/ViewContext.js +309 -0
- package/src/admin/WidgetRegistry.js +406 -0
- package/src/admin/index.js +17 -0
- package/src/admin/resources/AdminResource.js +383 -97
- package/src/admin/static/admin.css +1341 -0
- package/src/admin/static/date-picker.css +157 -0
- package/src/admin/static/date-picker.js +316 -0
- package/src/admin/static/json-editor.css +649 -0
- package/src/admin/static/json-editor.js +1429 -0
- package/src/admin/static/ui.js +1044 -0
- package/src/admin/views/layouts/base.njk +65 -1013
- package/src/admin/views/pages/detail.njk +40 -16
- package/src/admin/views/pages/form.njk +47 -599
- package/src/admin/views/pages/list.njk +145 -62
- package/src/admin/views/partials/form-field.njk +53 -0
- package/src/admin/views/partials/form-footer.njk +28 -0
- package/src/admin/views/partials/form-readonly.njk +114 -0
- package/src/admin/views/partials/form-scripts.njk +476 -0
- package/src/admin/views/partials/form-widget.njk +296 -0
- package/src/admin/views/partials/json-dialog.njk +80 -0
- package/src/admin/views/partials/json-editor.njk +37 -0
- package/src/admin.zip +0 -0
- package/src/auth/Auth.js +31 -10
- package/src/auth/AuthController.js +3 -1
- package/src/auth/AuthUser.js +119 -0
- package/src/cli.js +4 -2
- package/src/commands/createsuperuser.js +254 -0
- package/src/commands/lang.js +589 -0
- package/src/commands/migrate.js +154 -81
- package/src/commands/serve.js +82 -110
- package/src/container/AppInitializer.js +215 -0
- package/src/container/Application.js +278 -253
- package/src/container/HttpServer.js +156 -0
- package/src/container/MillasApp.js +29 -279
- package/src/container/MillasConfig.js +192 -0
- package/src/core/admin.js +5 -0
- package/src/core/auth.js +9 -0
- package/src/core/db.js +9 -0
- package/src/core/foundation.js +59 -0
- package/src/core/http.js +11 -0
- package/src/core/lang.js +1 -0
- package/src/core/mail.js +6 -0
- package/src/core/queue.js +7 -0
- package/src/core/validation.js +29 -0
- package/src/facades/Admin.js +1 -1
- package/src/facades/Auth.js +22 -39
- package/src/facades/Cache.js +21 -10
- package/src/facades/Database.js +1 -1
- package/src/facades/Events.js +18 -17
- package/src/facades/Facade.js +197 -0
- package/src/facades/Http.js +42 -45
- package/src/facades/Log.js +25 -49
- package/src/facades/Mail.js +27 -32
- package/src/facades/Queue.js +22 -15
- package/src/facades/Storage.js +18 -10
- package/src/facades/Url.js +53 -0
- package/src/http/HttpClient.js +673 -0
- package/src/http/ResponseDispatcher.js +18 -111
- package/src/http/UrlGenerator.js +375 -0
- package/src/http/WelcomePage.js +273 -0
- package/src/http/adapters/ExpressAdapter.js +315 -0
- package/src/http/adapters/HttpAdapter.js +168 -0
- package/src/http/adapters/index.js +9 -0
- package/src/i18n/I18nServiceProvider.js +91 -0
- package/src/i18n/Translator.js +635 -0
- package/src/i18n/defaults.js +122 -0
- package/src/i18n/index.js +164 -0
- package/src/i18n/locales/en.js +55 -0
- package/src/i18n/locales/sw.js +48 -0
- package/src/index.js +5 -144
- package/src/logger/formatters/PrettyFormatter.js +103 -57
- package/src/logger/internal.js +2 -2
- package/src/logger/patchConsole.js +91 -81
- package/src/middleware/MiddlewareRegistry.js +62 -82
- package/src/migrations/system/0001_users.js +21 -0
- package/src/migrations/system/0002_admin_log.js +25 -0
- package/src/migrations/system/0003_sessions.js +23 -0
- package/src/orm/fields/index.js +210 -188
- package/src/orm/migration/DefaultValueParser.js +325 -0
- package/src/orm/migration/InteractiveResolver.js +191 -0
- package/src/orm/migration/Makemigrations.js +312 -0
- package/src/orm/migration/MigrationGraph.js +227 -0
- package/src/orm/migration/MigrationRunner.js +202 -108
- package/src/orm/migration/MigrationWriter.js +463 -0
- package/src/orm/migration/ModelInspector.js +412 -344
- package/src/orm/migration/ModelScanner.js +225 -0
- package/src/orm/migration/ProjectState.js +213 -0
- package/src/orm/migration/RenameDetector.js +175 -0
- package/src/orm/migration/SchemaBuilder.js +8 -81
- package/src/orm/migration/operations/base.js +57 -0
- package/src/orm/migration/operations/column.js +191 -0
- package/src/orm/migration/operations/fields.js +252 -0
- package/src/orm/migration/operations/index.js +55 -0
- package/src/orm/migration/operations/models.js +152 -0
- package/src/orm/migration/operations/registry.js +131 -0
- package/src/orm/migration/operations/special.js +51 -0
- package/src/orm/migration/utils.js +208 -0
- package/src/orm/model/Model.js +81 -13
- package/src/providers/AdminServiceProvider.js +66 -9
- package/src/providers/AuthServiceProvider.js +46 -7
- package/src/providers/CacheStorageServiceProvider.js +5 -3
- package/src/providers/DatabaseServiceProvider.js +3 -2
- package/src/providers/EventServiceProvider.js +2 -1
- package/src/providers/LogServiceProvider.js +7 -3
- package/src/providers/MailServiceProvider.js +4 -3
- package/src/providers/QueueServiceProvider.js +4 -3
- package/src/router/Router.js +119 -152
- package/src/scaffold/templates.js +83 -26
- package/src/facades/Validation.js +0 -69
package/src/core/db.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── HTTP Layer ────────────────────────────────────────────────────
|
|
4
|
+
const {
|
|
5
|
+
MillasRequest,
|
|
6
|
+
MillasResponse,
|
|
7
|
+
ResponseDispatcher,
|
|
8
|
+
jsonify,
|
|
9
|
+
view,
|
|
10
|
+
redirect,
|
|
11
|
+
text,
|
|
12
|
+
file,
|
|
13
|
+
empty,
|
|
14
|
+
abort,
|
|
15
|
+
notFound,
|
|
16
|
+
unauthorized,
|
|
17
|
+
forbidden,
|
|
18
|
+
} = require('../http/index');
|
|
19
|
+
const RequestContext = require('../http/RequestContext');
|
|
20
|
+
|
|
21
|
+
// ── DI Container ─────────────────────────────────────────────────
|
|
22
|
+
const Container = require('../container/Container');
|
|
23
|
+
const Application = require('../container/Application');
|
|
24
|
+
const ServiceProvider = require('../providers/ServiceProvider');
|
|
25
|
+
const ProviderRegistry = require('../providers/ProviderRegistry');
|
|
26
|
+
|
|
27
|
+
// ── Events ────────────────────────────────────────────────────────
|
|
28
|
+
const EventEmitter = require('../events/EventEmitter');
|
|
29
|
+
const Event = require('../events/Event');
|
|
30
|
+
const Listener = require('../events/Listener');
|
|
31
|
+
const { emit } = require('../events/EventEmitter');
|
|
32
|
+
const EventServiceProvider = require('../providers/EventServiceProvider');
|
|
33
|
+
|
|
34
|
+
// ── Cache ─────────────────────────────────────────────────────────
|
|
35
|
+
const Cache = require('../cache/Cache');
|
|
36
|
+
const MemoryDriver = require('../cache/drivers/MemoryDriver');
|
|
37
|
+
const FileDriver = require('../cache/drivers/FileDriver');
|
|
38
|
+
const NullDriver = require('../cache/drivers/NullDriver');
|
|
39
|
+
const { CacheServiceProvider, StorageServiceProvider } = require('../providers/CacheStorageServiceProvider');
|
|
40
|
+
|
|
41
|
+
// ── Storage ───────────────────────────────────────────────────────
|
|
42
|
+
const Storage = require('../storage/Storage');
|
|
43
|
+
const LocalDriver = require('../storage/drivers/LocalDriver');
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
// ── Millas HTTP layer ──────────────────────────────────────────
|
|
47
|
+
MillasRequest, MillasResponse, ResponseDispatcher, RequestContext,
|
|
48
|
+
jsonify, view, redirect, text, send_file:file, empty,
|
|
49
|
+
abort, notFound, unauthorized, forbidden,
|
|
50
|
+
|
|
51
|
+
// DI
|
|
52
|
+
Container, Application, ServiceProvider, ProviderRegistry,
|
|
53
|
+
// Events
|
|
54
|
+
EventEmitter, Event, Listener, emit, EventServiceProvider,
|
|
55
|
+
// Cache
|
|
56
|
+
Cache, MemoryDriver, FileDriver, NullDriver, CacheServiceProvider,
|
|
57
|
+
// Storage
|
|
58
|
+
Storage, LocalDriver, StorageServiceProvider,
|
|
59
|
+
};
|
package/src/core/http.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const Controller = require("../controller/Controller");
|
|
2
|
+
const Middleware = require("../middleware/Middleware");
|
|
3
|
+
const MiddlewarePipeline = require("../middleware/MiddlewarePipeline");
|
|
4
|
+
const CorsMiddleware = require("../middleware/CorsMiddleware");
|
|
5
|
+
const ThrottleMiddleware = require("../middleware/ThrottleMiddleware");
|
|
6
|
+
const LogMiddleware = require("../middleware/LogMiddleware");
|
|
7
|
+
const HttpError = require("../errors/HttpError");
|
|
8
|
+
module.exports = {
|
|
9
|
+
Controller, Middleware, MiddlewarePipeline,
|
|
10
|
+
CorsMiddleware, ThrottleMiddleware, LogMiddleware, HttpError,
|
|
11
|
+
}
|
package/src/core/lang.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../i18n')
|
package/src/core/mail.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const {MailMessage, TemplateEngine, SmtpDriver, SendGridDriver, MailgunDriver, LogDriver} = require("../mail");
|
|
2
|
+
const MailServiceProvider = require("../providers/MailServiceProvider");
|
|
3
|
+
module.exports = {
|
|
4
|
+
MailMessage, TemplateEngine,
|
|
5
|
+
SmtpDriver, SendGridDriver, MailgunDriver, MailServiceProvider,
|
|
6
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const Job = require("../queue/Job");
|
|
2
|
+
const QueueWorker = require("../queue/workers/QueueWorker");
|
|
3
|
+
const {dispatch} = require("../queue/Queue");
|
|
4
|
+
const QueueServiceProvider = require("../providers/QueueServiceProvider");
|
|
5
|
+
module.exports = {
|
|
6
|
+
Job, QueueWorker, dispatch, QueueServiceProvider,
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const {
|
|
2
|
+
Validator,
|
|
3
|
+
BaseValidator,
|
|
4
|
+
StringValidator,
|
|
5
|
+
EmailValidator,
|
|
6
|
+
NumberValidator,
|
|
7
|
+
BooleanValidator,
|
|
8
|
+
DateValidator,
|
|
9
|
+
ArrayValidator,
|
|
10
|
+
ObjectValidator,
|
|
11
|
+
FileValidator,
|
|
12
|
+
string,
|
|
13
|
+
email,
|
|
14
|
+
number,
|
|
15
|
+
boolean,
|
|
16
|
+
date,
|
|
17
|
+
array,
|
|
18
|
+
objectField,
|
|
19
|
+
fileField
|
|
20
|
+
} = require("../validation/Validator");
|
|
21
|
+
module.exports = {
|
|
22
|
+
Validator,
|
|
23
|
+
BaseValidator,
|
|
24
|
+
StringValidator, EmailValidator, NumberValidator, BooleanValidator,
|
|
25
|
+
DateValidator, ArrayValidator, ObjectValidator, FileValidator,
|
|
26
|
+
string, email, number, boolean, date, array,
|
|
27
|
+
object: objectField,
|
|
28
|
+
file: fileField,
|
|
29
|
+
}
|
package/src/facades/Admin.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* Admin.register(PostResource);
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
const { Admin, AdminResource, AdminField, AdminFilter, AdminServiceProvider } = require('../
|
|
37
|
+
const { Admin, AdminResource, AdminField, AdminFilter, AdminServiceProvider } = require('../core');
|
|
38
38
|
|
|
39
39
|
let AdminInline;
|
|
40
40
|
try { AdminInline = require('../admin/resources/AdminResource').AdminInline; } catch {}
|
package/src/facades/Auth.js
CHANGED
|
@@ -1,46 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createFacade } = require('./Facade');
|
|
4
|
+
const { AuthUser, Hasher, JwtDriver, AuthMiddleware, RoleMiddleware, AuthController, AuthServiceProvider } = require('../core');
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Authentication, JWT, and access control.
|
|
7
|
-
*
|
|
8
|
-
* const { Auth, Hasher } = require('millas/facades/Auth');
|
|
9
|
-
*
|
|
10
|
-
* // Hash a password
|
|
11
|
-
* const hash = await Hasher.make('secret123');
|
|
7
|
+
* Auth facade.
|
|
12
8
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* @class
|
|
10
|
+
* @property {function(object): Promise<object>} register
|
|
11
|
+
* @property {function(string, string): Promise<{user, token, refreshToken}>} login
|
|
12
|
+
* @property {function(string): object} verify
|
|
13
|
+
* @property {function(object): Promise<object|null>} user
|
|
14
|
+
* @property {function(object): Promise<object>} userOrFail
|
|
15
|
+
* @property {function(string, string): Promise<boolean>} checkPassword
|
|
16
|
+
* @property {function(string): Promise<string>} hashPassword
|
|
17
|
+
* @property {function(object, object=): string} issueToken
|
|
18
|
+
* @property {function(string): object} decode
|
|
19
|
+
* @property {function(object): string} generateResetToken
|
|
20
|
+
* @property {function(string): object} verifyResetToken
|
|
21
|
+
* @property {function(function): void} setUserModel
|
|
22
|
+
* @property {function(object): void} swap
|
|
23
|
+
* @property {function(): void} restore
|
|
15
24
|
*
|
|
16
|
-
*
|
|
17
|
-
* const payload = Auth.verify(token);
|
|
18
|
-
*
|
|
19
|
-
* // Protect routes
|
|
20
|
-
* Route.prefix('/api').middleware(['auth']).group(() => {
|
|
21
|
-
* Route.get('/me', ({ user }) => jsonify(user));
|
|
22
|
-
* });
|
|
23
|
-
*
|
|
24
|
-
* // Role-based access
|
|
25
|
-
* middlewareRegistry.register('admin', new RoleMiddleware(['admin']));
|
|
25
|
+
* @see src/auth/Auth.js
|
|
26
26
|
*/
|
|
27
|
+
class Auth extends createFacade('auth') {}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
Auth,
|
|
30
|
-
Hasher,
|
|
31
|
-
JwtDriver,
|
|
32
|
-
AuthMiddleware,
|
|
33
|
-
RoleMiddleware,
|
|
34
|
-
AuthController,
|
|
35
|
-
AuthServiceProvider,
|
|
36
|
-
} = require('../index');
|
|
37
|
-
|
|
38
|
-
module.exports = {
|
|
39
|
-
Auth,
|
|
40
|
-
Hasher,
|
|
41
|
-
JwtDriver,
|
|
42
|
-
AuthMiddleware,
|
|
43
|
-
RoleMiddleware,
|
|
44
|
-
AuthController,
|
|
45
|
-
AuthServiceProvider,
|
|
46
|
-
};
|
|
29
|
+
module.exports = { Auth, AuthUser, Hasher, JwtDriver, AuthMiddleware, RoleMiddleware, AuthController, AuthServiceProvider };
|
package/src/facades/Cache.js
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const {createFacade} = require('./Facade');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
6
|
+
* Cache facade.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
8
|
+
* @class
|
|
9
|
+
* @property {function(string, *, number=): Promise<void>} set
|
|
10
|
+
* @property {function(string, *=): Promise<*>} get
|
|
11
|
+
* @property {function(string): Promise<void>} delete
|
|
12
|
+
* @property {function(string): Promise<void>} deletePattern
|
|
13
|
+
* @property {function(): Promise<void>} flush
|
|
14
|
+
* @property {function(string, number, function): Promise<*>} remember
|
|
15
|
+
* @property {function(string, number=): Promise<number>} increment
|
|
16
|
+
* @property {function(string, number=): Promise<number>} decrement
|
|
17
|
+
* @property {function(string[]): Promise<object>} getMany
|
|
18
|
+
* @property {function(object, number=): Promise<void>} setMany
|
|
19
|
+
* @property {function(...string): TaggedCache} tags
|
|
20
|
+
* @property {function(object): void} swap
|
|
21
|
+
* @property {function(): void} restore
|
|
7
22
|
*
|
|
8
|
-
*
|
|
9
|
-
* const val = await Cache.get('key');
|
|
10
|
-
* const val = await Cache.remember('key', 300, () => expensive());
|
|
11
|
-
* await Cache.forget('key');
|
|
12
|
-
* await Cache.flush();
|
|
23
|
+
* @see src/cache/Cache.js
|
|
13
24
|
*/
|
|
25
|
+
class Cache extends createFacade('cache') {
|
|
26
|
+
}
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
module.exports = { Cache, MemoryDriver, FileDriver, NullDriver, CacheServiceProvider };
|
|
28
|
+
module.exports = Cache;
|
package/src/facades/Database.js
CHANGED
package/src/facades/Events.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createFacade } = require('./Facade');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* const { EventEmitter, emit } = require('millas/facades/Events');
|
|
6
|
+
* Events facade.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* @class
|
|
9
|
+
* @property {function(string|function, function[]|function): EventEmitter} listen
|
|
10
|
+
* @property {function(string|function, function): EventEmitter} on
|
|
11
|
+
* @property {function(string|function, function): EventEmitter} once
|
|
12
|
+
* @property {function(string|function, function): EventEmitter} off
|
|
13
|
+
* @property {function(string, function): EventEmitter} onWildcard
|
|
14
|
+
* @property {function(string|Event, object=): Promise<void>} emit
|
|
15
|
+
* @property {function(string|function): function[]} getListeners
|
|
16
|
+
* @property {function(string|function): EventEmitter} removeAll
|
|
17
|
+
* @property {function(): EventEmitter} flush
|
|
18
|
+
* @property {function(object): void} swap
|
|
19
|
+
* @property {function(): void} restore
|
|
12
20
|
*
|
|
13
|
-
*
|
|
14
|
-
* await emit('user.created', { user });
|
|
15
|
-
*
|
|
16
|
-
* // Wildcard
|
|
17
|
-
* EventEmitter.onWildcard('user.*', async (event) => {
|
|
18
|
-
* Log.i('Events', event._name, event);
|
|
19
|
-
* });
|
|
21
|
+
* @see src/events/EventEmitter.js
|
|
20
22
|
*/
|
|
23
|
+
class Events extends createFacade('events') {}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
module.exports = { EventEmitter, Event, Listener, emit, EventServiceProvider };
|
|
25
|
+
module.exports = Events;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Facade
|
|
5
|
+
*
|
|
6
|
+
* Base class for all Millas facades.
|
|
7
|
+
*
|
|
8
|
+
* A facade is a static proxy to a service that lives in the DI container.
|
|
9
|
+
* Instead of importing the service directly, developers use the facade and
|
|
10
|
+
* the framework resolves the correct instance transparently.
|
|
11
|
+
*
|
|
12
|
+
* ── How it works ─────────────────────────────────────────────────────────────
|
|
13
|
+
*
|
|
14
|
+
* class Cache extends Facade {
|
|
15
|
+
* static get facadeAccessor() { return 'cache'; }
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* Cache.get('key')
|
|
19
|
+
* // ↓ Facade.__get intercepts 'get'
|
|
20
|
+
* // ↓ container.make('cache') → the Cache singleton
|
|
21
|
+
* // ↓ singleton.get('key')
|
|
22
|
+
*
|
|
23
|
+
* ── Benefits over direct singleton re-export ─────────────────────────────────
|
|
24
|
+
*
|
|
25
|
+
* 1. Swappable — rebind 'cache' in the container and every call site
|
|
26
|
+
* automatically uses the new implementation, zero changes.
|
|
27
|
+
*
|
|
28
|
+
* 2. Testable — swap the binding to a mock/fake in tests:
|
|
29
|
+
* Facade.swap('cache', fakeCacheInstance);
|
|
30
|
+
*
|
|
31
|
+
* 3. Lazy — the service is only resolved from the container on first call,
|
|
32
|
+
* not at require() time. Safe to import facades before boot.
|
|
33
|
+
*
|
|
34
|
+
* 4. Consistent — all services have the same access pattern regardless of
|
|
35
|
+
* how they were registered (singleton, instance, factory).
|
|
36
|
+
*
|
|
37
|
+
* ── Usage ────────────────────────────────────────────────────────────────────
|
|
38
|
+
*
|
|
39
|
+
* const { Cache } = require('millas/facades/Cache');
|
|
40
|
+
*
|
|
41
|
+
* await Cache.get('user:1');
|
|
42
|
+
* await Cache.set('user:1', user, 300);
|
|
43
|
+
* await Cache.remember('stats', 60, () => Stats.compute());
|
|
44
|
+
*/
|
|
45
|
+
class Facade {
|
|
46
|
+
/**
|
|
47
|
+
* The container key this facade resolves to.
|
|
48
|
+
* Every subclass must override this.
|
|
49
|
+
*
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
static get facadeAccessor() {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Facade "${this.name}" must define a static facadeAccessor getter.\n` +
|
|
55
|
+
` static get facadeAccessor() { return 'myService'; }`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── Container binding ──────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The shared container instance.
|
|
63
|
+
* Set by AppInitialiser once the container is fully booted.
|
|
64
|
+
* All facades share a single reference.
|
|
65
|
+
*/
|
|
66
|
+
static _container = null;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Per-facade instance overrides — used by swap() for testing.
|
|
70
|
+
* key: facadeAccessor string → value: override instance
|
|
71
|
+
*/
|
|
72
|
+
static _overrides = new Map();
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Called by AppInitialiser after the container is booted.
|
|
76
|
+
* Wires all facades to the live container in one call.
|
|
77
|
+
*
|
|
78
|
+
* @param {import('../container/Container')} container
|
|
79
|
+
*/
|
|
80
|
+
static setContainer(container) {
|
|
81
|
+
Facade._container = container;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the underlying service instance.
|
|
86
|
+
* Uses the override if one has been swapped in (for testing).
|
|
87
|
+
*
|
|
88
|
+
* @returns {object}
|
|
89
|
+
*/
|
|
90
|
+
static _resolveInstance() {
|
|
91
|
+
const accessor = this.facadeAccessor;
|
|
92
|
+
|
|
93
|
+
if (Facade._overrides.has(accessor)) {
|
|
94
|
+
return Facade._overrides.get(accessor);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!Facade._container) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`[Millas] Facade "${this.name}" used before the application was booted.\n` +
|
|
100
|
+
`The container is not yet available. ` +
|
|
101
|
+
`Make sure you're not calling facade methods at module load time ` +
|
|
102
|
+
`(outside of route handlers, controllers, or provider boot methods).`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return Facade._container.make(accessor);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Testing helpers ────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Swap the facade's underlying instance for testing.
|
|
113
|
+
* The swap is scoped to the accessor key so it only affects this facade.
|
|
114
|
+
*
|
|
115
|
+
* Cache.swap({ get: async () => 'mocked' });
|
|
116
|
+
* const val = await Cache.get('key'); // → 'mocked'
|
|
117
|
+
* Cache.restore();
|
|
118
|
+
*
|
|
119
|
+
* @param {object} instance
|
|
120
|
+
*/
|
|
121
|
+
static swap(instance) {
|
|
122
|
+
Facade._overrides.set(this.facadeAccessor, instance);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Remove a test swap and return to the real container binding.
|
|
127
|
+
*/
|
|
128
|
+
static restore() {
|
|
129
|
+
Facade._overrides.delete(this.facadeAccessor);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Remove all test swaps.
|
|
134
|
+
*/
|
|
135
|
+
static restoreAll() {
|
|
136
|
+
Facade._overrides.clear();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Proxy handler ─────────────────────────────────────────────────────────────
|
|
141
|
+
//
|
|
142
|
+
// Intercepts every static property access on a Facade subclass.
|
|
143
|
+
// If the property isn't a real static member of the class, it's assumed
|
|
144
|
+
// to be a method or property on the underlying service instance.
|
|
145
|
+
//
|
|
146
|
+
// This means Cache.get → resolves the 'cache' service → calls .get on it.
|
|
147
|
+
// It also means Cache.set, Cache.remember, Cache.tags, etc. all work
|
|
148
|
+
// without listing them explicitly.
|
|
149
|
+
|
|
150
|
+
const FACADE_OWN_PROPS = new Set([
|
|
151
|
+
'facadeAccessor',
|
|
152
|
+
'_container',
|
|
153
|
+
'_overrides',
|
|
154
|
+
'setContainer',
|
|
155
|
+
'_resolveInstance',
|
|
156
|
+
'swap',
|
|
157
|
+
'restore',
|
|
158
|
+
'restoreAll',
|
|
159
|
+
'name',
|
|
160
|
+
'prototype',
|
|
161
|
+
'length',
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
const FacadeProxyHandler = {
|
|
165
|
+
get(target, prop, receiver) {
|
|
166
|
+
// Let real static members through untouched
|
|
167
|
+
if (FACADE_OWN_PROPS.has(prop) || prop in target) {
|
|
168
|
+
return Reflect.get(target, prop, receiver);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Symbol access (Symbol.toPrimitive, Symbol.iterator, etc.) — pass through
|
|
172
|
+
if (typeof prop === 'symbol') {
|
|
173
|
+
return Reflect.get(target, prop, receiver);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Proxy the call to the resolved service instance
|
|
177
|
+
return (...args) => target._resolveInstance()[prop](...args);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create a Facade subclass that is automatically wrapped in a Proxy.
|
|
183
|
+
* Used internally — subclasses extend this instead of Facade directly.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* class Cache extends createFacade('cache') {}
|
|
187
|
+
*/
|
|
188
|
+
function createFacade(accessor) {
|
|
189
|
+
class BoundFacade extends Facade {
|
|
190
|
+
static get facadeAccessor() { return accessor; }
|
|
191
|
+
}
|
|
192
|
+
return new Proxy(BoundFacade, FacadeProxyHandler);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
module.exports = Facade;
|
|
196
|
+
module.exports.createFacade = createFacade;
|
|
197
|
+
module.exports.FacadeProxyHandler = FacadeProxyHandler;
|
package/src/facades/Http.js
CHANGED
|
@@ -1,54 +1,51 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { createFacade } = require('./Facade');
|
|
4
|
+
// const {PendingRequest,HttpResponse} = require('../http/HttpClient');
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
7
|
+
* Fluent outbound HTTP client facade.
|
|
8
|
+
* Resolved from the DI container — every method creates a fresh PendingRequest.
|
|
9
|
+
*
|
|
10
|
+
* @class
|
|
5
11
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
12
|
+
* — Verbs
|
|
13
|
+
* @property {function(string, object=): Promise<HttpResponse>} get
|
|
14
|
+
* @property {function(string, object=): Promise<HttpResponse>} post
|
|
15
|
+
* @property {function(string, object=): Promise<HttpResponse>} put
|
|
16
|
+
* @property {function(string, object=): Promise<HttpResponse>} patch
|
|
17
|
+
* @property {function(string, object=): Promise<HttpResponse>} delete
|
|
18
|
+
* @property {function(string, object=): Promise<HttpResponse>} head
|
|
19
|
+
* @property {function(string): Promise<HttpResponse>} options
|
|
8
20
|
*
|
|
9
|
-
*
|
|
21
|
+
* — Builder (each returns PendingRequest for chaining)
|
|
22
|
+
* @property {function(string): PendingRequest} baseUrl
|
|
23
|
+
* @property {function(object): PendingRequest} withHeaders
|
|
24
|
+
* @property {function(string, string=): PendingRequest} withToken
|
|
25
|
+
* @property {function(string, string): PendingRequest} withBasicAuth
|
|
26
|
+
* @property {function(object): PendingRequest} withCookies
|
|
27
|
+
* @property {function(string): PendingRequest} withUserAgent
|
|
28
|
+
* @property {function(string, string=): PendingRequest} withBody
|
|
29
|
+
* @property {function(string): PendingRequest} accept
|
|
30
|
+
* @property {function(): PendingRequest} acceptJson
|
|
31
|
+
* @property {function(): PendingRequest} asJson
|
|
32
|
+
* @property {function(): PendingRequest} asForm
|
|
33
|
+
* @property {function(): PendingRequest} asMultipart
|
|
34
|
+
* @property {function(number): PendingRequest} timeout
|
|
35
|
+
* @property {function(number, number=): PendingRequest} retry
|
|
36
|
+
* @property {function(): PendingRequest} throwOnFailure
|
|
37
|
+
* @property {function(function): PendingRequest} beforeSending
|
|
38
|
+
* @property {function(function): PendingRequest} afterReceiving
|
|
10
39
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* return jsonify(users);
|
|
14
|
-
* });
|
|
40
|
+
* — Concurrent
|
|
41
|
+
* @property {function(function): Promise<HttpResponse[]>} pool
|
|
15
42
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
43
|
+
* — Testing
|
|
44
|
+
* @property {function(object): void} swap
|
|
45
|
+
* @property {function(): void} restore
|
|
46
|
+
*
|
|
47
|
+
* @see src/http/HttpClient.js
|
|
20
48
|
*/
|
|
49
|
+
class Http extends createFacade('http') {}
|
|
21
50
|
|
|
22
|
-
|
|
23
|
-
MillasRequest,
|
|
24
|
-
MillasResponse,
|
|
25
|
-
ResponseDispatcher,
|
|
26
|
-
RequestContext,
|
|
27
|
-
jsonify,
|
|
28
|
-
view,
|
|
29
|
-
redirect,
|
|
30
|
-
text,
|
|
31
|
-
file,
|
|
32
|
-
empty,
|
|
33
|
-
abort,
|
|
34
|
-
notFound,
|
|
35
|
-
unauthorized,
|
|
36
|
-
forbidden,
|
|
37
|
-
} = require('../index');
|
|
38
|
-
|
|
39
|
-
module.exports = {
|
|
40
|
-
MillasRequest,
|
|
41
|
-
MillasResponse,
|
|
42
|
-
ResponseDispatcher,
|
|
43
|
-
RequestContext,
|
|
44
|
-
jsonify,
|
|
45
|
-
view,
|
|
46
|
-
redirect,
|
|
47
|
-
text,
|
|
48
|
-
send_file:file,
|
|
49
|
-
empty,
|
|
50
|
-
abort,
|
|
51
|
-
notFound,
|
|
52
|
-
unauthorized,
|
|
53
|
-
forbidden,
|
|
54
|
-
};
|
|
51
|
+
module.exports = Http;
|