@zintrust/core 0.1.13 → 0.1.14

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 (108) hide show
  1. package/package.json +1 -1
  2. package/src/boot/Application.d.ts.map +1 -1
  3. package/src/boot/Application.js +142 -11
  4. package/src/cache/Cache.d.ts +13 -0
  5. package/src/cache/Cache.d.ts.map +1 -1
  6. package/src/cache/Cache.js +48 -6
  7. package/src/cache/CacheRuntimeRegistration.d.ts +11 -0
  8. package/src/cache/CacheRuntimeRegistration.d.ts.map +1 -0
  9. package/src/cache/CacheRuntimeRegistration.js +21 -0
  10. package/src/config/broadcast.d.ts +18 -4
  11. package/src/config/broadcast.d.ts.map +1 -1
  12. package/src/config/broadcast.js +60 -12
  13. package/src/config/cache.d.ts +1 -1
  14. package/src/config/cache.d.ts.map +1 -1
  15. package/src/config/cache.js +19 -8
  16. package/src/config/database.d.ts +1 -1
  17. package/src/config/database.d.ts.map +1 -1
  18. package/src/config/database.js +59 -43
  19. package/src/config/index.d.ts +23 -2
  20. package/src/config/index.d.ts.map +1 -1
  21. package/src/config/index.js +6 -0
  22. package/src/config/mail.d.ts +3 -3
  23. package/src/config/mail.d.ts.map +1 -1
  24. package/src/config/mail.js +24 -9
  25. package/src/config/notification.d.ts +25 -29
  26. package/src/config/notification.d.ts.map +1 -1
  27. package/src/config/notification.js +75 -20
  28. package/src/config/storage.d.ts +8 -0
  29. package/src/config/storage.d.ts.map +1 -1
  30. package/src/config/storage.js +29 -7
  31. package/src/config/type.d.ts +32 -31
  32. package/src/config/type.d.ts.map +1 -1
  33. package/src/index.d.ts +5 -0
  34. package/src/index.d.ts.map +1 -1
  35. package/src/index.js +5 -0
  36. package/src/node.d.ts +1 -3
  37. package/src/node.d.ts.map +1 -1
  38. package/src/node.js +1 -3
  39. package/src/orm/Database.d.ts.map +1 -1
  40. package/src/orm/Database.js +4 -0
  41. package/src/orm/DatabaseRuntimeRegistration.d.ts +17 -0
  42. package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -0
  43. package/src/orm/DatabaseRuntimeRegistration.js +55 -0
  44. package/src/orm/Model.d.ts +3 -9
  45. package/src/orm/Model.d.ts.map +1 -1
  46. package/src/orm/Model.js +9 -7
  47. package/src/templates/project/basic/config/broadcast.ts.tpl +79 -11
  48. package/src/templates/project/basic/config/cache.ts.tpl +21 -9
  49. package/src/templates/project/basic/config/database.ts.tpl +67 -50
  50. package/src/templates/project/basic/config/index.ts.tpl +6 -0
  51. package/src/templates/project/basic/config/mail.ts.tpl +24 -11
  52. package/src/templates/project/basic/config/notification.ts.tpl +98 -25
  53. package/src/templates/project/basic/config/storage.ts.tpl +33 -17
  54. package/src/templates/project/basic/config/type.ts.tpl +46 -33
  55. package/src/templates/project/basic/routes/health.ts.tpl +21 -11
  56. package/src/tools/broadcast/Broadcast.d.ts +5 -3
  57. package/src/tools/broadcast/Broadcast.d.ts.map +1 -1
  58. package/src/tools/broadcast/Broadcast.js +48 -24
  59. package/src/tools/broadcast/BroadcastRegistry.d.ts +15 -0
  60. package/src/tools/broadcast/BroadcastRegistry.d.ts.map +1 -0
  61. package/src/tools/broadcast/BroadcastRegistry.js +29 -0
  62. package/src/tools/broadcast/BroadcastRuntimeRegistration.d.ts +11 -0
  63. package/src/tools/broadcast/BroadcastRuntimeRegistration.d.ts.map +1 -0
  64. package/src/tools/broadcast/BroadcastRuntimeRegistration.js +20 -0
  65. package/src/tools/broadcast/index.d.ts +2 -0
  66. package/src/tools/broadcast/index.d.ts.map +1 -1
  67. package/src/tools/broadcast/index.js +2 -0
  68. package/src/tools/mail/Mail.d.ts +8 -0
  69. package/src/tools/mail/Mail.d.ts.map +1 -1
  70. package/src/tools/mail/Mail.js +15 -2
  71. package/src/tools/mail/MailDriverRegistry.d.ts +2 -0
  72. package/src/tools/mail/MailDriverRegistry.d.ts.map +1 -1
  73. package/src/tools/mail/MailDriverRegistry.js +4 -0
  74. package/src/tools/notification/Notification.d.ts +3 -0
  75. package/src/tools/notification/Notification.d.ts.map +1 -1
  76. package/src/tools/notification/Notification.js +3 -0
  77. package/src/tools/notification/NotificationChannelRegistry.d.ts +15 -0
  78. package/src/tools/notification/NotificationChannelRegistry.d.ts.map +1 -0
  79. package/src/tools/notification/NotificationChannelRegistry.js +36 -0
  80. package/src/tools/notification/NotificationRuntimeRegistration.d.ts +13 -0
  81. package/src/tools/notification/NotificationRuntimeRegistration.d.ts.map +1 -0
  82. package/src/tools/notification/NotificationRuntimeRegistration.js +22 -0
  83. package/src/tools/notification/Registry.d.ts.map +1 -1
  84. package/src/tools/notification/Registry.js +4 -0
  85. package/src/tools/notification/Service.d.ts +1 -0
  86. package/src/tools/notification/Service.d.ts.map +1 -1
  87. package/src/tools/notification/Service.js +77 -4
  88. package/src/tools/notification/drivers/SlackNotification.d.ts +8 -0
  89. package/src/tools/notification/drivers/SlackNotification.d.ts.map +1 -0
  90. package/src/tools/notification/drivers/SlackNotification.js +13 -0
  91. package/src/tools/notification/drivers/TwilioNotification.d.ts +8 -0
  92. package/src/tools/notification/drivers/TwilioNotification.d.ts.map +1 -0
  93. package/src/tools/notification/drivers/TwilioNotification.js +13 -0
  94. package/src/tools/queue/Queue.d.ts +1 -0
  95. package/src/tools/queue/Queue.d.ts.map +1 -1
  96. package/src/tools/queue/Queue.js +3 -0
  97. package/src/tools/queue/QueueRuntimeRegistration.d.ts +11 -0
  98. package/src/tools/queue/QueueRuntimeRegistration.d.ts.map +1 -0
  99. package/src/tools/queue/QueueRuntimeRegistration.js +28 -0
  100. package/src/tools/storage/StorageDiskRegistry.d.ts +14 -0
  101. package/src/tools/storage/StorageDiskRegistry.d.ts.map +1 -0
  102. package/src/tools/storage/StorageDiskRegistry.js +36 -0
  103. package/src/tools/storage/StorageRuntimeRegistration.d.ts +10 -0
  104. package/src/tools/storage/StorageRuntimeRegistration.d.ts.map +1 -0
  105. package/src/tools/storage/StorageRuntimeRegistration.js +23 -0
  106. package/src/tools/storage/index.d.ts +1 -0
  107. package/src/tools/storage/index.d.ts.map +1 -1
  108. package/src/tools/storage/index.js +40 -6
@@ -5,71 +5,88 @@
5
5
  */
6
6
 
7
7
  import { Env } from './env';
8
- import {
9
- DatabaseConfigShape,
10
- DatabaseConnectionConfig,
11
- DatabaseConnectionName,
12
- } from './type';
13
-
14
- const isDatabaseConnectionName = (value: string): value is DatabaseConnectionName => {
15
- return value === 'sqlite' || value === 'postgresql' || value === 'mysql';
8
+ import { DatabaseConfigShape, DatabaseConnectionConfig, DatabaseConnections } from './type';
9
+ import { ErrorFactory } from '@zintrust/core';
10
+
11
+ const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
12
+ return Object.prototype.hasOwnProperty.call(obj, key);
16
13
  };
17
14
 
18
- const getDefaultConnection = (): DatabaseConnectionName => {
19
- const value = Env.DB_CONNECTION;
20
- return isDatabaseConnectionName(value) ? value : 'sqlite';
15
+ const getDefaultConnection = (connections: DatabaseConnections): string => {
16
+ const value = String(Env.DB_CONNECTION || '').trim();
17
+
18
+ if (value.length > 0 && hasOwn(connections, value)) {
19
+ return value;
20
+ }
21
+
22
+ // Backwards-compatible default.
23
+ return hasOwn(connections, 'sqlite') ? 'sqlite' : (Object.keys(connections)[0] ?? 'sqlite');
21
24
  };
22
25
 
23
26
  const getDatabaseConnection = (config: DatabaseConfigShape): DatabaseConnectionConfig => {
24
- const connName: DatabaseConnectionName = config.default;
25
- return config.connections[connName];
27
+ const connName = config.default;
28
+ const resolved = config.connections[connName];
29
+ if (resolved !== undefined) return resolved;
30
+
31
+ // Backwards-compatible fallback.
32
+ const sqliteFallback = config.connections['sqlite'];
33
+ if (sqliteFallback !== undefined) return sqliteFallback;
34
+
35
+ const first = Object.values(config.connections)[0];
36
+ if (first !== undefined) return first;
37
+
38
+ throw ErrorFactory.createConfigError(
39
+ `No database connections are configured (default='${connName}').`
40
+ );
26
41
  };
27
42
 
43
+ const connections = {
44
+ sqlite: {
45
+ driver: 'sqlite' as const,
46
+ database: Env.DB_DATABASE,
47
+ migrations: 'database/migrations',
48
+ },
49
+ postgresql: {
50
+ driver: 'postgresql' as const,
51
+ host: Env.DB_HOST,
52
+ port: Env.DB_PORT,
53
+ database: Env.DB_DATABASE,
54
+ username: Env.DB_USERNAME,
55
+ password: Env.DB_PASSWORD,
56
+ ssl: Env.getBool('DB_SSL', false),
57
+ pooling: {
58
+ enabled: Env.getBool('DB_POOLING', true),
59
+ min: Env.getInt('DB_POOL_MIN', 5),
60
+ max: Env.getInt('DB_POOL_MAX', 20),
61
+ idleTimeout: Env.getInt('DB_IDLE_TIMEOUT', 30000),
62
+ connectionTimeout: Env.getInt('DB_CONNECTION_TIMEOUT', 10000),
63
+ },
64
+ },
65
+ mysql: {
66
+ driver: 'mysql' as const,
67
+ host: Env.DB_HOST,
68
+ port: Env.DB_PORT,
69
+ database: Env.DB_DATABASE,
70
+ username: Env.DB_USERNAME,
71
+ password: Env.DB_PASSWORD,
72
+ pooling: {
73
+ enabled: Env.getBool('DB_POOLING', true),
74
+ min: Env.getInt('DB_POOL_MIN', 5),
75
+ max: Env.getInt('DB_POOL_MAX', 20),
76
+ },
77
+ },
78
+ } satisfies DatabaseConnections;
79
+
28
80
  const databaseConfigObj = {
29
81
  /**
30
82
  * Default database connection
31
83
  */
32
- default: getDefaultConnection(),
84
+ default: getDefaultConnection(connections),
33
85
 
34
86
  /**
35
87
  * Database connections
36
88
  */
37
- connections: {
38
- sqlite: {
39
- driver: 'sqlite' as const,
40
- database: Env.DB_DATABASE,
41
- migrations: 'database/migrations',
42
- },
43
- postgresql: {
44
- driver: 'postgresql' as const,
45
- host: Env.DB_HOST,
46
- port: Env.DB_PORT,
47
- database: Env.DB_DATABASE,
48
- username: Env.DB_USERNAME,
49
- password: Env.DB_PASSWORD,
50
- ssl: Env.getBool('DB_SSL', false),
51
- pooling: {
52
- enabled: Env.getBool('DB_POOLING', true),
53
- min: Env.getInt('DB_POOL_MIN', 5),
54
- max: Env.getInt('DB_POOL_MAX', 20),
55
- idleTimeout: Env.getInt('DB_IDLE_TIMEOUT', 30000),
56
- connectionTimeout: Env.getInt('DB_CONNECTION_TIMEOUT', 10000),
57
- },
58
- },
59
- mysql: {
60
- driver: 'mysql' as const,
61
- host: Env.DB_HOST,
62
- port: Env.DB_PORT,
63
- database: Env.DB_DATABASE,
64
- username: Env.DB_USERNAME,
65
- password: Env.DB_PASSWORD,
66
- pooling: {
67
- enabled: Env.getBool('DB_POOLING', true),
68
- min: Env.getInt('DB_POOL_MIN', 5),
69
- max: Env.getInt('DB_POOL_MAX', 20),
70
- },
71
- },
72
- },
89
+ connections,
73
90
 
74
91
  /**
75
92
  * Get current connection config
@@ -4,19 +4,23 @@
4
4
  */
5
5
 
6
6
  import { appConfig } from './app';
7
+ import broadcastConfig from './broadcast';
7
8
  import { cacheConfig } from './cache';
8
9
  import { databaseConfig } from './database';
9
10
  import { microservicesConfig } from './microservices';
10
11
  import { middlewareConfig } from './middleware';
12
+ import notificationConfig from './notification';
11
13
  import { queueConfig } from './queue';
12
14
  import { securityConfig } from './security';
13
15
  import { storageConfig } from './storage';
14
16
 
15
17
  export { appConfig, type AppConfig } from './app';
18
+ export { default as broadcastConfig } from './broadcast';
16
19
  export { cacheConfig, type CacheConfig } from './cache';
17
20
  export { databaseConfig, type DatabaseConfig } from './database';
18
21
  export { microservicesConfig, type MicroservicesConfig } from './microservices';
19
22
  export { middlewareConfig } from './middleware';
23
+ export { notificationConfig, type NotificationConfig } from './notification';
20
24
  export { queueConfig, type QueueConfig } from './queue';
21
25
  export { securityConfig } from './security';
22
26
  export { storageConfig, type StorageConfig } from './storage';
@@ -27,8 +31,10 @@ export { storageConfig, type StorageConfig } from './storage';
27
31
  */
28
32
  export const config = Object.freeze({
29
33
  app: appConfig,
34
+ broadcast: broadcastConfig,
30
35
  database: databaseConfig,
31
36
  storage: storageConfig,
37
+ notification: notificationConfig,
32
38
  security: securityConfig,
33
39
  middleware: middlewareConfig,
34
40
  microservices: microservicesConfig,
@@ -4,25 +4,38 @@
4
4
  * Sealed namespace for immutability
5
5
  */
6
6
 
7
- import { Env } from './env';
8
- import type { MailConfigInput, MailDriverConfig, MailDriverName, MailDrivers } from './type';
7
+ import { Env } from '@config/env';
8
+ import type { MailConfigInput, MailDriverConfig } from '@config/type';
9
+ import { ErrorFactory } from '@zintrust/core';
9
10
 
10
- const getMailDriver = (config: MailConfigInput): MailDriverConfig => {
11
- const defaultDriver = config.default;
11
+ const getMailDriver = (config: MailConfigInput, name?: string): MailDriverConfig => {
12
+ const selected = (name ?? config.default).toString().trim();
13
+ if (selected.length === 0) {
14
+ const disabled = config.drivers['disabled'];
15
+ if (disabled !== undefined) return disabled;
16
+ throw ErrorFactory.createConfigError('Mail driver not configured: disabled');
17
+ }
18
+
19
+ if (Object.hasOwn(config.drivers, selected)) {
20
+ const resolved = config.drivers[selected];
21
+ if (resolved !== undefined) return resolved;
22
+ }
12
23
 
13
- if (Object.hasOwn(config.drivers, defaultDriver)) {
14
- const driverName = defaultDriver as keyof MailDrivers;
15
- return config.drivers[driverName];
24
+ // Backward-compatible fallback: if the default is misconfigured, treat mail as disabled.
25
+ if (name === undefined) {
26
+ const disabled = config.drivers['disabled'];
27
+ if (disabled !== undefined) return disabled;
28
+ throw ErrorFactory.createConfigError('Mail driver not configured: disabled');
16
29
  }
17
30
 
18
- return config.drivers.disabled;
31
+ throw ErrorFactory.createConfigError(`Mail driver not configured: ${selected}`);
19
32
  };
20
33
 
21
34
  const mailConfigObj = {
22
35
  /**
23
36
  * Default mail driver
24
37
  */
25
- default: (Env.get('MAIL_DRIVER', 'disabled') as MailDriverName) ?? 'disabled',
38
+ default: Env.get('MAIL_DRIVER', 'disabled').trim().toLowerCase() || 'disabled',
26
39
 
27
40
  /**
28
41
  * Default "From" identity
@@ -91,8 +104,8 @@ const mailConfigObj = {
91
104
  /**
92
105
  * Get selected driver config
93
106
  */
94
- getDriver(): MailDriverConfig {
95
- return getMailDriver(this);
107
+ getDriver(name?: string): MailDriverConfig {
108
+ return getMailDriver(this, name);
96
109
  },
97
110
  } as const;
98
111
 
@@ -1,51 +1,124 @@
1
1
  /**
2
2
  * Notification Configuration
3
3
  *
4
- * Config-first mapping of notification providers.
5
- * Keeps runtime driver selection in one place and uses Env for safe access.
4
+ * Config-first mapping of notification providers/channels.
5
+ * Driver selection must be dynamic (tests may mutate process.env).
6
6
  */
7
7
 
8
- import { Env } from './env';
9
- import type { NotificationProviders } from './type';
8
+ import { Env } from '@config/env';
9
+ import type {
10
+ KnownNotificationDriverConfig,
11
+ NotificationConfigInput,
12
+ NotificationDrivers,
13
+ NotificationProviders,
14
+ } from '@config/type';
15
+ import { ErrorFactory } from '@zintrust/core';
10
16
 
11
- const notificationConfigObj = {
12
- /**
13
- * Normalized notification driver name.
14
- *
15
- * NOTE: This intentionally supports custom driver names (e.g. project-specific drivers),
16
- * so it returns a string rather than a strict union.
17
- */
18
- getDriverName(): string {
19
- return Env.get('NOTIFICATION_DRIVER', 'console').trim().toLowerCase();
20
- },
17
+ const normalizeName = (value: string): string => value.trim().toLowerCase();
21
18
 
22
- /**
23
- * Provider configs.
24
- */
25
- providers: {
26
- console: {
27
- driver: 'console' as const,
28
- },
19
+ const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
20
+ return Object.prototype.hasOwnProperty.call(obj, key);
21
+ };
22
+
23
+ const getDefaultChannel = (drivers: NotificationDrivers): string => {
24
+ const value = normalizeName(Env.get('NOTIFICATION_DRIVER', 'console'));
25
+ if (value.length > 0 && hasOwn(drivers, value)) return value;
26
+ return hasOwn(drivers, 'console') ? 'console' : (Object.keys(drivers)[0] ?? 'console');
27
+ };
28
+
29
+ const getNotificationDriver = (
30
+ config: NotificationConfigInput,
31
+ name?: string
32
+ ): KnownNotificationDriverConfig => {
33
+ const selected = normalizeName(String(name ?? config.default));
34
+ const channelName = selected === 'default' ? normalizeName(config.default) : selected;
29
35
 
36
+ const isExplicitSelection =
37
+ name !== undefined &&
38
+ String(name).trim().length > 0 &&
39
+ normalizeName(String(name)) !== 'default';
40
+
41
+ if (channelName.length > 0 && hasOwn(config.drivers, channelName)) {
42
+ const resolved = config.drivers[channelName];
43
+ if (resolved !== undefined) return resolved;
44
+ }
45
+
46
+ if (isExplicitSelection) {
47
+ throw ErrorFactory.createConfigError(`Notification channel not configured: ${channelName}`);
48
+ }
49
+
50
+ const fallback = config.drivers['console'] ?? Object.values(config.drivers)[0];
51
+ if (fallback !== undefined) return fallback;
52
+
53
+ throw ErrorFactory.createConfigError('No notification channels are configured');
54
+ };
55
+
56
+ const getBaseProviders = (): NotificationProviders => {
57
+ return {
58
+ console: { driver: 'console' as const },
30
59
  termii: {
31
60
  driver: 'termii' as const,
32
61
  apiKey: Env.get('TERMII_API_KEY', ''),
33
62
  sender: Env.get('TERMII_SENDER', 'Zintrust'),
34
63
  endpoint: Env.get('TERMII_ENDPOINT', 'https://api.termii.com/sms/send'),
35
64
  },
36
-
37
65
  twilio: {
38
66
  driver: 'twilio' as const,
39
67
  accountSid: Env.get('TWILIO_ACCOUNT_SID', ''),
40
68
  authToken: Env.get('TWILIO_AUTH_TOKEN', ''),
41
69
  fromNumber: Env.get('TWILIO_FROM_NUMBER', ''),
42
70
  },
43
-
44
71
  slack: {
45
72
  driver: 'slack' as const,
46
73
  webhookUrl: Env.get('SLACK_WEBHOOK_URL', ''),
47
74
  },
48
- } satisfies NotificationProviders,
75
+ };
76
+ };
77
+
78
+ const notificationConfigObj = {
79
+ /**
80
+ * Default notification channel name (normalized).
81
+ */
82
+ get default(): string {
83
+ return getDefaultChannel(this.drivers);
84
+ },
85
+
86
+ /**
87
+ * Notification channels.
88
+ *
89
+ * You may add custom named channels (e.g. `opsSlack`, `smsMarketing`) that
90
+ * point to any known driver config.
91
+ */
92
+ get drivers(): NotificationDrivers {
93
+ // Return a record of channels; can be extended by app-level config.
94
+ return getBaseProviders() as unknown as NotificationDrivers;
95
+ },
96
+
97
+ /**
98
+ * Legacy provider configs (kept for backwards compatibility with wrappers).
99
+ */
100
+ get providers(): NotificationProviders {
101
+ return getBaseProviders();
102
+ },
103
+
104
+ /**
105
+ * Normalized notification channel name.
106
+ */
107
+ getDriverName(): string {
108
+ return normalizeName(this.default);
109
+ },
110
+
111
+ /**
112
+ * Resolve a channel config.
113
+ * - Unknown names throw when explicitly selected.
114
+ * - `default` is a reserved alias of the configured default.
115
+ */
116
+ getDriverConfig(name?: string): KnownNotificationDriverConfig {
117
+ return getNotificationDriver(this, name);
118
+ },
49
119
  } as const;
50
120
 
51
- export default Object.freeze(notificationConfigObj);
121
+ export const notificationConfig = Object.freeze(notificationConfigObj);
122
+ export type NotificationConfig = typeof notificationConfig;
123
+
124
+ export default notificationConfig;
@@ -5,28 +5,33 @@
5
5
  */
6
6
 
7
7
  import { Env } from './env';
8
- import type {
9
- StorageConfigRuntime,
10
- StorageDriverConfig,
11
- StorageDriverName,
12
- StorageDrivers,
13
- } from './type';
14
-
15
- const isStorageDriverName = (
16
- value: string,
17
- drivers: StorageDrivers
18
- ): value is StorageDriverName => {
19
- return value in drivers;
8
+ import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from './type';
9
+ import { ErrorFactory } from '@zintrust/core';
10
+
11
+ const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
12
+ return Object.prototype.hasOwnProperty.call(obj, key);
20
13
  };
21
14
 
22
- const getStorageDriver = (config: StorageConfigRuntime): StorageDriverConfig => {
23
- const driverName = config.default;
15
+ const getStorageDriver = (config: StorageConfigRuntime, name?: string): StorageDriverConfig => {
16
+ const selected = String(name ?? config.default).trim();
17
+ const diskName = selected === 'default' ? String(config.default).trim() : selected;
18
+ const isExplicitSelection =
19
+ name !== undefined && String(name).trim().length > 0 && String(name).trim() !== 'default';
20
+
21
+ if (diskName !== '' && hasOwn(config.drivers, diskName)) {
22
+ const resolved = config.drivers[diskName];
23
+ if (resolved !== undefined) return resolved;
24
+ }
24
25
 
25
- if (isStorageDriverName(driverName, config.drivers)) {
26
- return config.drivers[driverName];
26
+ if (isExplicitSelection) {
27
+ throw ErrorFactory.createConfigError(`Storage disk not configured: ${diskName}`);
27
28
  }
28
29
 
29
- return config.drivers.local;
30
+ // Backwards-compatible fallback.
31
+ const fallback = config.drivers['local'] ?? Object.values(config.drivers)[0];
32
+ if (fallback !== undefined) return fallback;
33
+
34
+ throw ErrorFactory.createConfigError('No storage disks are configured');
30
35
  };
31
36
 
32
37
  const getDrivers = (): StorageDrivers => ({
@@ -86,6 +91,17 @@ const storageConfigObj = {
86
91
  return getStorageDriver(this);
87
92
  },
88
93
 
94
+ /**
95
+ * Get a storage disk configuration by name.
96
+ *
97
+ * - When `name` is provided and not configured, this throws.
98
+ * - When `name` is omitted, it resolves the configured default with a backwards-compatible fallback.
99
+ * - Reserved name `default` aliases the configured default.
100
+ */
101
+ getDriverConfig(this: StorageConfigRuntime, name?: string): StorageDriverConfig {
102
+ return getStorageDriver(this, name);
103
+ },
104
+
89
105
  /**
90
106
  * Temporary file settings
91
107
  */
@@ -1,5 +1,5 @@
1
- import { Env } from './env';
2
- import type { Middleware as MiddlewareFn } from '../middleware/MiddlewareStack';
1
+ import { Env } from '@config/env';
2
+ import type { Middleware as MiddlewareFn } from '@middleware/MiddlewareStack';
3
3
 
4
4
  export type Environment =
5
5
  | 'development'
@@ -50,15 +50,15 @@ export type GcsStorageDriverConfig = {
50
50
  url: EnvGetValue;
51
51
  };
52
52
 
53
- export type StorageDrivers = {
54
- local: LocalStorageDriverConfig;
55
- s3: S3StorageDriverConfig;
56
- r2: R2StorageDriverConfig;
57
- gcs: GcsStorageDriverConfig;
58
- };
53
+ export type StorageDriverConfig =
54
+ | LocalStorageDriverConfig
55
+ | S3StorageDriverConfig
56
+ | R2StorageDriverConfig
57
+ | GcsStorageDriverConfig;
58
+
59
+ export type StorageDrivers = Record<string, StorageDriverConfig>;
59
60
 
60
61
  export type StorageDriverName = keyof StorageDrivers;
61
- export type StorageDriverConfig = StorageDrivers[StorageDriverName];
62
62
 
63
63
  export type StorageConfigRuntime = {
64
64
  readonly default: string;
@@ -194,6 +194,13 @@ export type KnownNotificationDriverConfig =
194
194
  | TwilioNotificationDriverConfig
195
195
  | SlackNotificationDriverConfig;
196
196
 
197
+ export type NotificationDrivers = Record<string, KnownNotificationDriverConfig>;
198
+
199
+ export type NotificationConfigInput = {
200
+ default: string;
201
+ drivers: NotificationDrivers;
202
+ };
203
+
197
204
  export type NotificationProviders = {
198
205
  console: ConsoleNotificationDriverConfig;
199
206
  termii: TermiiNotificationDriverConfig;
@@ -256,17 +263,16 @@ export type MailDriverConfig =
256
263
  | NodemailerMailDriverConfig
257
264
  | SesMailDriverConfig;
258
265
 
259
- export type MailDrivers = {
260
- disabled: DisabledMailDriverConfig;
261
- sendgrid: SendGridMailDriverConfig;
262
- mailgun: MailgunMailDriverConfig;
263
- smtp: SmtpMailDriverConfig;
264
- nodemailer: NodemailerMailDriverConfig;
265
- ses: SesMailDriverConfig;
266
- };
266
+ export type MailDrivers = Record<string, MailDriverConfig>;
267
267
 
268
268
  export type MailConfigInput = {
269
- default: MailDriverName;
269
+ /**
270
+ * Default mailer key name.
271
+ *
272
+ * This is intentionally a string to support named mailers (e.g. 'transactional', 'marketing').
273
+ * The underlying driver is selected by the `driver` field within each mailer config.
274
+ */
275
+ default: string;
270
276
  from: {
271
277
  address: string;
272
278
  name: string;
@@ -317,17 +323,23 @@ export type MysqlConnectionConfig = {
317
323
  };
318
324
  };
319
325
 
320
- export type DatabaseConnections = {
321
- sqlite: SqliteConnectionConfig;
322
- postgresql: PostgresqlConnectionConfig;
323
- mysql: MysqlConnectionConfig;
324
- };
326
+ export type DatabaseConnectionConfig =
327
+ | SqliteConnectionConfig
328
+ | PostgresqlConnectionConfig
329
+ | MysqlConnectionConfig;
325
330
 
326
- export type DatabaseConnectionName = keyof DatabaseConnections;
327
- export type DatabaseConnectionConfig = DatabaseConnections[DatabaseConnectionName];
331
+ /**
332
+ * Named database connection configs.
333
+ *
334
+ * Keys represent connection names (e.g. 'default', 'auth', 'tasks', 'db1').
335
+ */
336
+ export type DatabaseConnections = Record<string, DatabaseConnectionConfig>;
328
337
 
338
+ /**
339
+ * Default connection name.
340
+ */
329
341
  export type DatabaseConfigShape = {
330
- default: DatabaseConnectionName;
342
+ default: string;
331
343
  connections: DatabaseConnections;
332
344
  };
333
345
 
@@ -382,13 +394,7 @@ export type CacheDriverConfig =
382
394
  | KvCacheDriverConfig
383
395
  | KvRemoteCacheDriverConfig;
384
396
 
385
- export type CacheDrivers = {
386
- memory: MemoryCacheDriverConfig;
387
- redis: RedisCacheDriverConfig;
388
- mongodb: MongoCacheDriverConfig;
389
- kv: KvCacheDriverConfig;
390
- 'kv-remote': KvRemoteCacheDriverConfig;
391
- };
397
+ export type CacheDrivers = Record<string, CacheDriverConfig>;
392
398
 
393
399
  export type CacheConfigInput = {
394
400
  default: string;
@@ -430,3 +436,10 @@ export type KnownBroadcastDriverConfig =
430
436
  | PusherBroadcastDriverConfig
431
437
  | RedisBroadcastDriverConfig
432
438
  | RedisHttpsBroadcastDriverConfig;
439
+
440
+ export type BroadcastDrivers = Record<string, KnownBroadcastDriverConfig>;
441
+
442
+ export type BroadcastConfigInput = {
443
+ default: string;
444
+ drivers: BroadcastDrivers;
445
+ };
@@ -3,17 +3,13 @@
3
3
  * Provides health, liveness, and readiness endpoints.
4
4
  */
5
5
 
6
- import {
7
- Env,
8
- Logger,
9
- QueryBuilder,
10
- RuntimeHealthProbes,
11
- type IRouter,
12
- Router,
13
- useDatabase,
14
- } from '@zintrust/core';
15
-
16
- import { appConfig } from '../config';
6
+ import { appConfig } from '../config/app';
7
+ import { Env } from '../config/env';
8
+ import { Logger } from '../config/logger';
9
+ import { RuntimeHealthProbes } from '@zintrust/core';
10
+ import { useDatabase } from '@zintrust/core';
11
+ import { QueryBuilder } from '@zintrust/core';
12
+ import { type IRouter, Router } from '@zintrust/core';
17
13
 
18
14
  export function registerHealthRoutes(router: IRouter): void {
19
15
  registerHealthRoute(router);
@@ -27,6 +23,13 @@ function registerHealthRoute(router: IRouter): void {
27
23
 
28
24
  try {
29
25
  const db = useDatabase();
26
+ const maybeIsConnected = (db as unknown as { isConnected?: unknown }).isConnected;
27
+ const maybeConnect = (db as unknown as { connect?: unknown }).connect;
28
+ if (typeof maybeIsConnected === 'function' && maybeIsConnected.call(db) === false) {
29
+ if (typeof maybeConnect === 'function') {
30
+ await maybeConnect.call(db);
31
+ }
32
+ }
30
33
  await QueryBuilder.ping(db);
31
34
 
32
35
  const uptime =
@@ -79,6 +82,13 @@ function registerHealthReadyRoute(router: IRouter): void {
79
82
 
80
83
  try {
81
84
  const db = useDatabase();
85
+ const maybeIsConnected = (db as unknown as { isConnected?: unknown }).isConnected;
86
+ const maybeConnect = (db as unknown as { connect?: unknown }).connect;
87
+ if (typeof maybeIsConnected === 'function' && maybeIsConnected.call(db) === false) {
88
+ if (typeof maybeConnect === 'function') {
89
+ await maybeConnect.call(db);
90
+ }
91
+ }
82
92
  await QueryBuilder.ping(db);
83
93
 
84
94
  databaseResponseTime = Date.now() - startTime;
@@ -1,7 +1,9 @@
1
+ type Broadcaster = Readonly<{
2
+ send: (channel: string, event: string, data: unknown) => Promise<unknown>;
3
+ }>;
1
4
  export declare const Broadcast: Readonly<{
2
- send(channel: string, event: string, data: unknown): Promise<{
3
- ok: boolean;
4
- }>;
5
+ send(channel: string, event: string, data: unknown): Promise<unknown>;
6
+ broadcaster(name?: string): Broadcaster;
5
7
  }>;
6
8
  export default Broadcast;
7
9
  //# sourceMappingURL=Broadcast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Broadcast.d.ts","sourceRoot":"","sources":["../../../../src/tools/broadcast/Broadcast.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,SAAS;kBACA,MAAM,SAAS,MAAM,QAAQ,OAAO;;;EAmCxD,CAAC;AAEH,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"Broadcast.d.ts","sourceRoot":"","sources":["../../../../src/tools/broadcast/Broadcast.ts"],"names":[],"mappings":"AAQA,KAAK,WAAW,GAAG,QAAQ,CAAC;IAC1B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3E,CAAC,CAAC;AAyDH,eAAO,MAAM,SAAS;kBACA,MAAM,SAAS,MAAM,QAAQ,OAAO;uBAKrC,MAAM,GAAG,WAAW;EAQvC,CAAC;AAEH,eAAe,SAAS,CAAC"}