millas 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/bin/millas.js +6 -0
  4. package/package.json +56 -0
  5. package/src/admin/Admin.js +617 -0
  6. package/src/admin/index.js +13 -0
  7. package/src/admin/resources/AdminResource.js +317 -0
  8. package/src/auth/Auth.js +254 -0
  9. package/src/auth/AuthController.js +188 -0
  10. package/src/auth/AuthMiddleware.js +67 -0
  11. package/src/auth/Hasher.js +51 -0
  12. package/src/auth/JwtDriver.js +74 -0
  13. package/src/auth/RoleMiddleware.js +44 -0
  14. package/src/cache/Cache.js +231 -0
  15. package/src/cache/drivers/FileDriver.js +152 -0
  16. package/src/cache/drivers/MemoryDriver.js +158 -0
  17. package/src/cache/drivers/NullDriver.js +27 -0
  18. package/src/cache/index.js +8 -0
  19. package/src/cli.js +27 -0
  20. package/src/commands/make.js +61 -0
  21. package/src/commands/migrate.js +174 -0
  22. package/src/commands/new.js +50 -0
  23. package/src/commands/queue.js +92 -0
  24. package/src/commands/route.js +93 -0
  25. package/src/commands/serve.js +50 -0
  26. package/src/container/Application.js +177 -0
  27. package/src/container/Container.js +281 -0
  28. package/src/container/index.js +13 -0
  29. package/src/controller/Controller.js +367 -0
  30. package/src/errors/HttpError.js +29 -0
  31. package/src/events/Event.js +39 -0
  32. package/src/events/EventEmitter.js +151 -0
  33. package/src/events/Listener.js +46 -0
  34. package/src/events/index.js +15 -0
  35. package/src/index.js +93 -0
  36. package/src/mail/Mail.js +210 -0
  37. package/src/mail/MailMessage.js +196 -0
  38. package/src/mail/TemplateEngine.js +150 -0
  39. package/src/mail/drivers/LogDriver.js +36 -0
  40. package/src/mail/drivers/MailgunDriver.js +84 -0
  41. package/src/mail/drivers/SendGridDriver.js +97 -0
  42. package/src/mail/drivers/SmtpDriver.js +67 -0
  43. package/src/mail/index.js +19 -0
  44. package/src/middleware/AuthMiddleware.js +46 -0
  45. package/src/middleware/CorsMiddleware.js +59 -0
  46. package/src/middleware/LogMiddleware.js +61 -0
  47. package/src/middleware/Middleware.js +36 -0
  48. package/src/middleware/MiddlewarePipeline.js +94 -0
  49. package/src/middleware/ThrottleMiddleware.js +61 -0
  50. package/src/orm/drivers/DatabaseManager.js +135 -0
  51. package/src/orm/fields/index.js +132 -0
  52. package/src/orm/index.js +19 -0
  53. package/src/orm/migration/MigrationRunner.js +216 -0
  54. package/src/orm/migration/ModelInspector.js +338 -0
  55. package/src/orm/migration/SchemaBuilder.js +173 -0
  56. package/src/orm/model/Model.js +371 -0
  57. package/src/orm/query/QueryBuilder.js +197 -0
  58. package/src/providers/AdminServiceProvider.js +40 -0
  59. package/src/providers/AuthServiceProvider.js +53 -0
  60. package/src/providers/CacheStorageServiceProvider.js +71 -0
  61. package/src/providers/DatabaseServiceProvider.js +45 -0
  62. package/src/providers/EventServiceProvider.js +34 -0
  63. package/src/providers/MailServiceProvider.js +51 -0
  64. package/src/providers/ProviderRegistry.js +82 -0
  65. package/src/providers/QueueServiceProvider.js +52 -0
  66. package/src/providers/ServiceProvider.js +45 -0
  67. package/src/queue/Job.js +135 -0
  68. package/src/queue/Queue.js +147 -0
  69. package/src/queue/drivers/DatabaseDriver.js +194 -0
  70. package/src/queue/drivers/SyncDriver.js +72 -0
  71. package/src/queue/index.js +16 -0
  72. package/src/queue/workers/QueueWorker.js +140 -0
  73. package/src/router/MiddlewareRegistry.js +82 -0
  74. package/src/router/Route.js +255 -0
  75. package/src/router/RouteGroup.js +19 -0
  76. package/src/router/RouteRegistry.js +55 -0
  77. package/src/router/Router.js +138 -0
  78. package/src/router/index.js +15 -0
  79. package/src/scaffold/generator.js +34 -0
  80. package/src/scaffold/maker.js +272 -0
  81. package/src/scaffold/templates.js +350 -0
  82. package/src/storage/Storage.js +170 -0
  83. package/src/storage/drivers/LocalDriver.js +215 -0
  84. package/src/storage/index.js +6 -0
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const Cache = require('../cache/Cache');
5
+ const Storage = require('../storage/Storage');
6
+
7
+ /**
8
+ * CacheServiceProvider
9
+ *
10
+ * Configures the Cache facade with config/cache.js settings.
11
+ */
12
+ class CacheServiceProvider extends ServiceProvider {
13
+ register(container) {
14
+ container.instance('Cache', Cache);
15
+ }
16
+
17
+ async boot() {
18
+ let cacheConfig;
19
+ try {
20
+ cacheConfig = require(process.cwd() + '/config/cache');
21
+ } catch {
22
+ cacheConfig = {
23
+ default: process.env.CACHE_DRIVER || 'memory',
24
+ prefix: process.env.CACHE_PREFIX || '',
25
+ drivers: {
26
+ memory: {},
27
+ file: { path: 'storage/cache' },
28
+ null: {},
29
+ },
30
+ };
31
+ }
32
+ Cache.configure(cacheConfig);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * StorageServiceProvider
38
+ *
39
+ * Configures the Storage facade with config/storage.js settings.
40
+ */
41
+ class StorageServiceProvider extends ServiceProvider {
42
+ register(container) {
43
+ container.instance('Storage', Storage);
44
+ }
45
+
46
+ async boot() {
47
+ let storageConfig;
48
+ try {
49
+ storageConfig = require(process.cwd() + '/config/storage');
50
+ } catch {
51
+ storageConfig = {
52
+ default: process.env.STORAGE_DRIVER || 'local',
53
+ disks: {
54
+ local: {
55
+ driver: 'local',
56
+ root: 'storage/uploads',
57
+ baseUrl: '/storage',
58
+ },
59
+ public: {
60
+ driver: 'local',
61
+ root: 'public/storage',
62
+ baseUrl: '/storage',
63
+ },
64
+ },
65
+ };
66
+ }
67
+ Storage.configure(storageConfig);
68
+ }
69
+ }
70
+
71
+ module.exports = { CacheServiceProvider, StorageServiceProvider };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const DatabaseManager = require('../orm/drivers/DatabaseManager');
5
+ const SchemaBuilder = require('../orm/migration/SchemaBuilder');
6
+
7
+ /**
8
+ * DatabaseServiceProvider
9
+ *
10
+ * Configures the DatabaseManager with the app's database config
11
+ * and registers DB-related bindings into the container.
12
+ *
13
+ * Add to bootstrap/app.js:
14
+ * app.providers([DatabaseServiceProvider, AppServiceProvider])
15
+ */
16
+ class DatabaseServiceProvider extends ServiceProvider {
17
+ register(container) {
18
+ // Make DatabaseManager available as a singleton in the container
19
+ container.instance('db', DatabaseManager);
20
+ container.instance('DatabaseManager', DatabaseManager);
21
+ }
22
+
23
+ async boot(container) {
24
+ // Load the database config
25
+ let dbConfig;
26
+ try {
27
+ dbConfig = require(process.cwd() + '/config/database');
28
+ } catch {
29
+ // Fallback for tests / programmatic use
30
+ dbConfig = {
31
+ default: 'sqlite',
32
+ connections: {
33
+ sqlite: { driver: 'sqlite', database: ':memory:' },
34
+ },
35
+ };
36
+ }
37
+
38
+ DatabaseManager.configure(dbConfig);
39
+
40
+ // Register SchemaBuilder against the default connection
41
+ container.instance('schema', new SchemaBuilder(DatabaseManager.connection()));
42
+ }
43
+ }
44
+
45
+ module.exports = DatabaseServiceProvider;
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const EventEmitter = require('../events/EventEmitter');
5
+ const { emit } = require('../events/EventEmitter');
6
+
7
+ /**
8
+ * EventServiceProvider
9
+ *
10
+ * Registers the EventEmitter singleton in the container
11
+ * and connects it to the Queue for async listeners.
12
+ *
13
+ * Add to bootstrap/app.js:
14
+ * app.providers([..., EventServiceProvider, AppServiceProvider])
15
+ *
16
+ * Register event → listener mappings in AppServiceProvider.boot():
17
+ * EventEmitter.listen(UserRegistered, [SendWelcomeEmail, NotifyAdmin]);
18
+ */
19
+ class EventServiceProvider extends ServiceProvider {
20
+ register(container) {
21
+ container.instance('EventEmitter', EventEmitter);
22
+ container.instance('emit', emit);
23
+ }
24
+
25
+ async boot(container) {
26
+ // Connect queue to emitter for async listener support
27
+ if (container.has('Queue')) {
28
+ const Queue = container.make('Queue');
29
+ EventEmitter.setQueue(Queue);
30
+ }
31
+ }
32
+ }
33
+
34
+ module.exports = EventServiceProvider;
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const Mail = require('../mail/Mail');
5
+ const MailMessage = require('../mail/MailMessage');
6
+
7
+ /**
8
+ * MailServiceProvider
9
+ *
10
+ * Configures the Mail facade with config/mail.js settings.
11
+ *
12
+ * Add to bootstrap/app.js:
13
+ * app.providers([
14
+ * DatabaseServiceProvider,
15
+ * AuthServiceProvider,
16
+ * MailServiceProvider,
17
+ * AppServiceProvider,
18
+ * ])
19
+ */
20
+ class MailServiceProvider extends ServiceProvider {
21
+ register(container) {
22
+ container.instance('Mail', Mail);
23
+ container.instance('MailMessage', MailMessage);
24
+ }
25
+
26
+ async boot(container) {
27
+ let mailConfig;
28
+ try {
29
+ mailConfig = require(process.cwd() + '/config/mail');
30
+ } catch {
31
+ mailConfig = {
32
+ default: process.env.MAIL_DRIVER || 'log',
33
+ from: { address: 'noreply@millas.dev', name: 'Millas' },
34
+ drivers: {
35
+ log: {},
36
+ smtp: {
37
+ host: process.env.MAIL_HOST || 'localhost',
38
+ port: Number(process.env.MAIL_PORT) || 587,
39
+ username: process.env.MAIL_USERNAME || '',
40
+ password: process.env.MAIL_PASSWORD || '',
41
+ encryption: 'tls',
42
+ },
43
+ },
44
+ };
45
+ }
46
+
47
+ Mail.configure(mailConfig);
48
+ }
49
+ }
50
+
51
+ module.exports = MailServiceProvider;
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ProviderRegistry
5
+ *
6
+ * Manages the lifecycle of all service providers:
7
+ *
8
+ * 1. Load — require() each provider file
9
+ * 2. Register — call provider.register(container) on all providers
10
+ * 3. Boot — call provider.boot(container, app) on all providers
11
+ *
12
+ * This two-phase approach (register then boot) ensures every binding
13
+ * exists before any provider tries to resolve another.
14
+ *
15
+ * Usage (in bootstrap/app.js):
16
+ *
17
+ * const registry = new ProviderRegistry(container, app);
18
+ * registry.add(AppServiceProvider);
19
+ * registry.add('./providers/DatabaseServiceProvider');
20
+ * await registry.boot();
21
+ */
22
+ class ProviderRegistry {
23
+ constructor(container, expressApp) {
24
+ this._container = container;
25
+ this._app = expressApp;
26
+ this._providers = [];
27
+ }
28
+
29
+ /**
30
+ * Add a provider class or path to the registry.
31
+ * @param {Function|string} provider — class or file path
32
+ */
33
+ add(provider) {
34
+ let Cls = provider;
35
+
36
+ if (typeof provider === 'string') {
37
+ Cls = require(provider);
38
+ // Support ES module default export
39
+ if (Cls.default) Cls = Cls.default;
40
+ }
41
+
42
+ this._providers.push(new Cls());
43
+ return this;
44
+ }
45
+
46
+ /**
47
+ * Add multiple providers at once.
48
+ * @param {Array} providers
49
+ */
50
+ addMany(providers = []) {
51
+ for (const p of providers) this.add(p);
52
+ return this;
53
+ }
54
+
55
+ /**
56
+ * Run the full register → boot lifecycle.
57
+ */
58
+ async boot() {
59
+ // Phase 1: register all bindings
60
+ for (const provider of this._providers) {
61
+ if (typeof provider.register === 'function') {
62
+ provider.register(this._container);
63
+ }
64
+ }
65
+
66
+ // Phase 2: boot all providers (async-safe)
67
+ for (const provider of this._providers) {
68
+ if (typeof provider.boot === 'function') {
69
+ await provider.boot(this._container, this._app);
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Return a list of provider class names (for debugging).
76
+ */
77
+ list() {
78
+ return this._providers.map(p => p.constructor.name);
79
+ }
80
+ }
81
+
82
+ module.exports = ProviderRegistry;
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const Queue = require('../queue/Queue');
5
+ const { dispatch } = require('../queue/Queue');
6
+
7
+ /**
8
+ * QueueServiceProvider
9
+ *
10
+ * Configures the Queue facade and registers it in the container.
11
+ * Also connects the Mail facade to the queue for async sending.
12
+ *
13
+ * Add to bootstrap/app.js:
14
+ * app.providers([
15
+ * DatabaseServiceProvider,
16
+ * AuthServiceProvider,
17
+ * MailServiceProvider,
18
+ * QueueServiceProvider,
19
+ * AppServiceProvider,
20
+ * ])
21
+ */
22
+ class QueueServiceProvider extends ServiceProvider {
23
+ register(container) {
24
+ container.instance('Queue', Queue);
25
+ container.instance('dispatch', dispatch);
26
+ }
27
+
28
+ async boot(container) {
29
+ let queueConfig;
30
+ try {
31
+ queueConfig = require(process.cwd() + '/config/queue');
32
+ } catch {
33
+ queueConfig = {
34
+ default: process.env.QUEUE_DRIVER || 'sync',
35
+ drivers: {
36
+ sync: {},
37
+ database: { connection: null, table: 'millas_jobs' },
38
+ },
39
+ };
40
+ }
41
+
42
+ Queue.configure(queueConfig);
43
+
44
+ // Connect Mail to Queue so Mail.queue() works
45
+ if (container.has('Mail')) {
46
+ const Mail = container.make('Mail');
47
+ Mail.setQueue(Queue);
48
+ }
49
+ }
50
+ }
51
+
52
+ module.exports = QueueServiceProvider;
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ServiceProvider
5
+ *
6
+ * Base class for all Millas service providers.
7
+ *
8
+ * Providers have two lifecycle hooks:
9
+ *
10
+ * register(container)
11
+ * Called first. Bind things into the container.
12
+ * Do NOT use other bindings here — they may not exist yet.
13
+ *
14
+ * boot(container, app)
15
+ * Called after ALL providers have registered.
16
+ * Safe to resolve other bindings and set up routes,
17
+ * event listeners, middleware, etc.
18
+ *
19
+ * Usage:
20
+ * class AppServiceProvider extends ServiceProvider {
21
+ * register(container) {
22
+ * container.singleton('UserService', UserService);
23
+ * }
24
+ * async boot(container, app) {
25
+ * const logger = container.make('Logger');
26
+ * logger.info('AppServiceProvider booted');
27
+ * }
28
+ * }
29
+ */
30
+ class ServiceProvider {
31
+ /**
32
+ * Register bindings into the container.
33
+ * @param {import('./Container')} container
34
+ */
35
+ register(container) {}
36
+
37
+ /**
38
+ * Bootstrap services after all providers have registered.
39
+ * @param {import('./Container')} container
40
+ * @param {import('express').Application} app
41
+ */
42
+ async boot(container, app) {}
43
+ }
44
+
45
+ module.exports = ServiceProvider;
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Job
5
+ *
6
+ * Base class for all Millas background jobs.
7
+ *
8
+ * Usage:
9
+ * class SendEmailJob extends Job {
10
+ * static queue = 'emails'; // which queue to push to
11
+ * static tries = 3; // max attempts before failing
12
+ * static delay = 0; // seconds before first attempt
13
+ * static timeout = 60; // seconds before job times out
14
+ * static backoff = 'exponential'; // 'fixed' | 'exponential'
15
+ *
16
+ * constructor(user, subject) {
17
+ * super();
18
+ * this.user = user;
19
+ * this.subject = subject;
20
+ * }
21
+ *
22
+ * async handle() {
23
+ * await Mail.send({ to: this.user.email, subject: this.subject });
24
+ * }
25
+ *
26
+ * async failed(error) {
27
+ * console.error('SendEmailJob failed:', error.message);
28
+ * }
29
+ * }
30
+ *
31
+ * Dispatch:
32
+ * dispatch(new SendEmailJob(user, 'Welcome!'));
33
+ * dispatch(new SendEmailJob(user)).delay(60); // delay 60 seconds
34
+ * dispatch(new SendEmailJob(user)).onQueue('priority');
35
+ */
36
+ class Job {
37
+ // ─── Static config (override per job class) ───────────────────────────────
38
+
39
+ /** Queue name this job belongs to */
40
+ static queue = 'default';
41
+
42
+ /** Max attempts before marking as failed */
43
+ static tries = 3;
44
+
45
+ /** Seconds to wait before first execution */
46
+ static delay = 0;
47
+
48
+ /** Seconds before the job is considered timed out */
49
+ static timeout = 60;
50
+
51
+ /** Backoff strategy: 'fixed' | 'exponential' */
52
+ static backoff = 'exponential';
53
+
54
+ // ─── Instance ─────────────────────────────────────────────────────────────
55
+
56
+ constructor() {
57
+ this._queue = null; // runtime override
58
+ this._delay = null; // runtime override
59
+ this._attempts = 0;
60
+ }
61
+
62
+ /**
63
+ * Execute the job. Must be implemented by subclasses.
64
+ */
65
+ async handle() {
66
+ throw new Error(`${this.constructor.name} must implement handle()`);
67
+ }
68
+
69
+ /**
70
+ * Called when the job fails after all retries.
71
+ * Override to notify, log, or clean up.
72
+ */
73
+ async failed(error) {}
74
+
75
+ // ─── Fluent dispatch modifiers ────────────────────────────────────────────
76
+
77
+ /**
78
+ * Override the queue name at dispatch time.
79
+ */
80
+ onQueue(name) {
81
+ this._queue = name;
82
+ return this;
83
+ }
84
+
85
+ /**
86
+ * Delay execution by N seconds.
87
+ */
88
+ delay(seconds) {
89
+ this._delay = seconds;
90
+ return this;
91
+ }
92
+
93
+ // ─── Serialisation ────────────────────────────────────────────────────────
94
+
95
+ /**
96
+ * Serialize this job for storage in the queue driver.
97
+ */
98
+ serialize() {
99
+ return {
100
+ class: this.constructor.name,
101
+ queue: this._queue || this.constructor.queue || 'default',
102
+ tries: this.constructor.tries || 3,
103
+ timeout: this.constructor.timeout || 60,
104
+ backoff: this.constructor.backoff || 'exponential',
105
+ delay: this._delay !== null
106
+ ? this._delay
107
+ : (this.constructor.delay || 0),
108
+ payload: this._getPayload(),
109
+ createdAt: new Date().toISOString(),
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Get the serializable payload (all own non-private properties).
115
+ */
116
+ _getPayload() {
117
+ const payload = {};
118
+ for (const key of Object.keys(this)) {
119
+ if (!key.startsWith('_')) payload[key] = this[key];
120
+ }
121
+ return payload;
122
+ }
123
+
124
+ /**
125
+ * Restore a job instance from a serialized record.
126
+ */
127
+ static deserialize(record, JobClass) {
128
+ const instance = new JobClass();
129
+ Object.assign(instance, record.payload || {});
130
+ instance._attempts = record.attempts || 0;
131
+ return instance;
132
+ }
133
+ }
134
+
135
+ module.exports = Job;
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ const SyncDriver = require('./drivers/SyncDriver');
4
+
5
+ /**
6
+ * Queue
7
+ *
8
+ * The primary queue facade.
9
+ *
10
+ * Usage:
11
+ * const { Queue, dispatch } = require('millas/src');
12
+ *
13
+ * // Dispatch a job
14
+ * await dispatch(new SendEmailJob(user));
15
+ *
16
+ * // Dispatch with options
17
+ * await dispatch(new SendEmailJob(user).delay(60).onQueue('emails'));
18
+ *
19
+ * // Direct facade methods
20
+ * await Queue.push(new SendEmailJob(user));
21
+ * await Queue.size('default');
22
+ * await Queue.clear('default');
23
+ */
24
+ class Queue {
25
+ constructor() {
26
+ this._driver = null;
27
+ this._config = null;
28
+ this._registry = new Map(); // className → JobClass
29
+ }
30
+
31
+ // ─── Configuration ─────────────────────────────────────────────────────────
32
+
33
+ configure(config) {
34
+ this._config = config;
35
+ this._driver = null; // reset so driver is rebuilt with new config
36
+ }
37
+
38
+ /**
39
+ * Register a Job class so the worker can deserialize it.
40
+ */
41
+ register(JobClass) {
42
+ this._registry.set(JobClass.name, JobClass);
43
+ return this;
44
+ }
45
+
46
+ registerMany(classes = []) {
47
+ classes.forEach(c => this.register(c));
48
+ return this;
49
+ }
50
+
51
+ // ─── Core API ──────────────────────────────────────────────────────────────
52
+
53
+ /**
54
+ * Push a job onto the queue.
55
+ * Returns { id, status, queue }
56
+ */
57
+ async push(job) {
58
+ return this._getDriver().push(job);
59
+ }
60
+
61
+ /**
62
+ * Get the number of pending jobs on a queue.
63
+ */
64
+ async size(queue = 'default') {
65
+ return this._getDriver().size(queue);
66
+ }
67
+
68
+ /**
69
+ * Clear all pending jobs from a queue.
70
+ */
71
+ async clear(queue = 'default') {
72
+ return this._getDriver().clear(queue);
73
+ }
74
+
75
+ /**
76
+ * Get queue statistics (DatabaseDriver only).
77
+ */
78
+ async stats() {
79
+ const driver = this._getDriver();
80
+ if (typeof driver.stats === 'function') return driver.stats();
81
+ return {};
82
+ }
83
+
84
+ /**
85
+ * Get processed jobs (SyncDriver only — for testing).
86
+ */
87
+ processed() {
88
+ const driver = this._getDriver();
89
+ if (typeof driver.processed === 'function') return driver.processed();
90
+ return [];
91
+ }
92
+
93
+ /**
94
+ * Get the job registry Map (used by QueueWorker).
95
+ */
96
+ getRegistry() {
97
+ return this._registry;
98
+ }
99
+
100
+ getDriver() {
101
+ return this._getDriver();
102
+ }
103
+
104
+ // ─── Internal ──────────────────────────────────────────────────────────────
105
+
106
+ _getDriver() {
107
+ if (this._driver) return this._driver;
108
+
109
+ const driverName = this._config?.default
110
+ || process.env.QUEUE_DRIVER
111
+ || 'sync';
112
+
113
+ const driverConf = this._config?.drivers?.[driverName] || {};
114
+
115
+ switch (driverName) {
116
+ case 'database': {
117
+ const DatabaseDriver = require('./drivers/DatabaseDriver');
118
+ this._driver = new DatabaseDriver(driverConf);
119
+ break;
120
+ }
121
+ case 'sync':
122
+ default: {
123
+ this._driver = new SyncDriver();
124
+ break;
125
+ }
126
+ }
127
+
128
+ return this._driver;
129
+ }
130
+ }
131
+
132
+ // Singleton
133
+ const queue = new Queue();
134
+ module.exports = queue;
135
+ module.exports.Queue = Queue;
136
+
137
+ /**
138
+ * dispatch()
139
+ *
140
+ * Global helper — push a job onto the queue.
141
+ *
142
+ * const { dispatch } = require('millas/src');
143
+ * await dispatch(new SendEmailJob(user));
144
+ */
145
+ module.exports.dispatch = async function dispatch(job) {
146
+ return queue.push(job);
147
+ };