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.
Files changed (116) hide show
  1. package/package.json +3 -16
  2. package/src/admin/ActivityLog.js +153 -52
  3. package/src/admin/Admin.js +400 -167
  4. package/src/admin/AdminAuth.js +213 -98
  5. package/src/admin/FormGenerator.js +372 -0
  6. package/src/admin/HookRegistry.js +256 -0
  7. package/src/admin/QueryEngine.js +263 -0
  8. package/src/admin/ViewContext.js +309 -0
  9. package/src/admin/WidgetRegistry.js +406 -0
  10. package/src/admin/index.js +17 -0
  11. package/src/admin/resources/AdminResource.js +383 -97
  12. package/src/admin/static/admin.css +1341 -0
  13. package/src/admin/static/date-picker.css +157 -0
  14. package/src/admin/static/date-picker.js +316 -0
  15. package/src/admin/static/json-editor.css +649 -0
  16. package/src/admin/static/json-editor.js +1429 -0
  17. package/src/admin/static/ui.js +1044 -0
  18. package/src/admin/views/layouts/base.njk +65 -1013
  19. package/src/admin/views/pages/detail.njk +40 -16
  20. package/src/admin/views/pages/form.njk +47 -599
  21. package/src/admin/views/pages/list.njk +145 -62
  22. package/src/admin/views/partials/form-field.njk +53 -0
  23. package/src/admin/views/partials/form-footer.njk +28 -0
  24. package/src/admin/views/partials/form-readonly.njk +114 -0
  25. package/src/admin/views/partials/form-scripts.njk +476 -0
  26. package/src/admin/views/partials/form-widget.njk +296 -0
  27. package/src/admin/views/partials/json-dialog.njk +80 -0
  28. package/src/admin/views/partials/json-editor.njk +37 -0
  29. package/src/admin.zip +0 -0
  30. package/src/auth/Auth.js +31 -10
  31. package/src/auth/AuthController.js +3 -1
  32. package/src/auth/AuthUser.js +119 -0
  33. package/src/cli.js +4 -2
  34. package/src/commands/createsuperuser.js +254 -0
  35. package/src/commands/lang.js +589 -0
  36. package/src/commands/migrate.js +154 -81
  37. package/src/commands/serve.js +82 -110
  38. package/src/container/AppInitializer.js +215 -0
  39. package/src/container/Application.js +278 -253
  40. package/src/container/HttpServer.js +156 -0
  41. package/src/container/MillasApp.js +29 -279
  42. package/src/container/MillasConfig.js +192 -0
  43. package/src/core/admin.js +5 -0
  44. package/src/core/auth.js +9 -0
  45. package/src/core/db.js +9 -0
  46. package/src/core/foundation.js +59 -0
  47. package/src/core/http.js +11 -0
  48. package/src/core/lang.js +1 -0
  49. package/src/core/mail.js +6 -0
  50. package/src/core/queue.js +7 -0
  51. package/src/core/validation.js +29 -0
  52. package/src/facades/Admin.js +1 -1
  53. package/src/facades/Auth.js +22 -39
  54. package/src/facades/Cache.js +21 -10
  55. package/src/facades/Database.js +1 -1
  56. package/src/facades/Events.js +18 -17
  57. package/src/facades/Facade.js +197 -0
  58. package/src/facades/Http.js +42 -45
  59. package/src/facades/Log.js +25 -49
  60. package/src/facades/Mail.js +27 -32
  61. package/src/facades/Queue.js +22 -15
  62. package/src/facades/Storage.js +18 -10
  63. package/src/facades/Url.js +53 -0
  64. package/src/http/HttpClient.js +673 -0
  65. package/src/http/ResponseDispatcher.js +18 -111
  66. package/src/http/UrlGenerator.js +375 -0
  67. package/src/http/WelcomePage.js +273 -0
  68. package/src/http/adapters/ExpressAdapter.js +315 -0
  69. package/src/http/adapters/HttpAdapter.js +168 -0
  70. package/src/http/adapters/index.js +9 -0
  71. package/src/i18n/I18nServiceProvider.js +91 -0
  72. package/src/i18n/Translator.js +635 -0
  73. package/src/i18n/defaults.js +122 -0
  74. package/src/i18n/index.js +164 -0
  75. package/src/i18n/locales/en.js +55 -0
  76. package/src/i18n/locales/sw.js +48 -0
  77. package/src/index.js +5 -144
  78. package/src/logger/formatters/PrettyFormatter.js +103 -57
  79. package/src/logger/internal.js +2 -2
  80. package/src/logger/patchConsole.js +91 -81
  81. package/src/middleware/MiddlewareRegistry.js +62 -82
  82. package/src/migrations/system/0001_users.js +21 -0
  83. package/src/migrations/system/0002_admin_log.js +25 -0
  84. package/src/migrations/system/0003_sessions.js +23 -0
  85. package/src/orm/fields/index.js +210 -188
  86. package/src/orm/migration/DefaultValueParser.js +325 -0
  87. package/src/orm/migration/InteractiveResolver.js +191 -0
  88. package/src/orm/migration/Makemigrations.js +312 -0
  89. package/src/orm/migration/MigrationGraph.js +227 -0
  90. package/src/orm/migration/MigrationRunner.js +202 -108
  91. package/src/orm/migration/MigrationWriter.js +463 -0
  92. package/src/orm/migration/ModelInspector.js +412 -344
  93. package/src/orm/migration/ModelScanner.js +225 -0
  94. package/src/orm/migration/ProjectState.js +213 -0
  95. package/src/orm/migration/RenameDetector.js +175 -0
  96. package/src/orm/migration/SchemaBuilder.js +8 -81
  97. package/src/orm/migration/operations/base.js +57 -0
  98. package/src/orm/migration/operations/column.js +191 -0
  99. package/src/orm/migration/operations/fields.js +252 -0
  100. package/src/orm/migration/operations/index.js +55 -0
  101. package/src/orm/migration/operations/models.js +152 -0
  102. package/src/orm/migration/operations/registry.js +131 -0
  103. package/src/orm/migration/operations/special.js +51 -0
  104. package/src/orm/migration/utils.js +208 -0
  105. package/src/orm/model/Model.js +81 -13
  106. package/src/providers/AdminServiceProvider.js +66 -9
  107. package/src/providers/AuthServiceProvider.js +46 -7
  108. package/src/providers/CacheStorageServiceProvider.js +5 -3
  109. package/src/providers/DatabaseServiceProvider.js +3 -2
  110. package/src/providers/EventServiceProvider.js +2 -1
  111. package/src/providers/LogServiceProvider.js +7 -3
  112. package/src/providers/MailServiceProvider.js +4 -3
  113. package/src/providers/QueueServiceProvider.js +4 -3
  114. package/src/router/Router.js +119 -152
  115. package/src/scaffold/templates.js +83 -26
  116. package/src/facades/Validation.js +0 -69
package/src/core/db.js ADDED
@@ -0,0 +1,9 @@
1
+ const {
2
+ Model,
3
+ fields
4
+ } = require("../orm");
5
+ const { migrations } = require("../orm/migration/operations");
6
+
7
+ module.exports = {
8
+ Model, fields,migrations
9
+ }
@@ -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
+ };
@@ -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
+ }
@@ -0,0 +1 @@
1
+ module.exports = require('../i18n')
@@ -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
+ }
@@ -34,7 +34,7 @@
34
34
  * Admin.register(PostResource);
35
35
  */
36
36
 
37
- const { Admin, AdminResource, AdminField, AdminFilter, AdminServiceProvider } = require('../index');
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 {}
@@ -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
- * millas/facades/Auth
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
- * // Login
14
- * const { user, token } = await Auth.login(email, password);
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
- * // Verify JWT
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
- const {
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 };
@@ -1,17 +1,28 @@
1
1
  'use strict';
2
2
 
3
+ const {createFacade} = require('./Facade');
4
+
3
5
  /**
4
- * millas/facades/Cache
6
+ * Cache facade.
5
7
  *
6
- * const { Cache } = require('millas/facades/Cache');
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
- * await Cache.put('key', value, 300); // 300 second TTL
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
- const { Cache, MemoryDriver, FileDriver, NullDriver, CacheServiceProvider } = require('../index');
16
-
17
- module.exports = { Cache, MemoryDriver, FileDriver, NullDriver, CacheServiceProvider };
28
+ module.exports = Cache;
@@ -29,7 +29,7 @@ const {
29
29
  MigrationRunner,
30
30
  ModelInspector,
31
31
  DatabaseServiceProvider,
32
- } = require('../index');
32
+ } = require('../core');
33
33
 
34
34
  module.exports = {
35
35
  Model,
@@ -1,24 +1,25 @@
1
1
  'use strict';
2
2
 
3
+ const { createFacade } = require('./Facade');
4
+
3
5
  /**
4
- * millas/facades/Events
5
- *
6
- * const { EventEmitter, emit } = require('millas/facades/Events');
6
+ * Events facade.
7
7
  *
8
- * // Listen
9
- * EventEmitter.on('user.created', async (event) => {
10
- * await Mail.to(event.user.email).subject('Welcome').send();
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
- * // Emit
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
- const { EventEmitter, Event, Listener, emit, EventServiceProvider } = require('../index');
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;
@@ -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
- * millas/facades/Http
7
+ * Fluent outbound HTTP client facade.
8
+ * Resolved from the DI container — every method creates a fresh PendingRequest.
9
+ *
10
+ * @class
5
11
  *
6
- * Response builders and HTTP helpers.
7
- * These are the primary tools for returning responses from route handlers.
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
- * const { jsonify, redirect, abort } = require('millas/facades/Http');
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
- * Route.get('/users', async ({ query }) => {
12
- * const users = await User.all();
13
- * return jsonify(users);
14
- * });
40
+ * Concurrent
41
+ * @property {function(function): Promise<HttpResponse[]>} pool
15
42
  *
16
- * Route.post('/users', async ({ body }) => {
17
- * const user = await User.create(body);
18
- * return jsonify(user, { status: 201 });
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
- const {
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;