@zintrust/core 0.1.14 → 0.1.16

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 (123) hide show
  1. package/package.json +1 -1
  2. package/src/boot/Application.d.ts.map +1 -1
  3. package/src/boot/Application.js +29 -61
  4. package/src/cli/CLI.d.ts.map +1 -1
  5. package/src/cli/CLI.js +6 -0
  6. package/src/cli/commands/BroadcastWorkCommand.d.ts +10 -0
  7. package/src/cli/commands/BroadcastWorkCommand.d.ts.map +1 -0
  8. package/src/cli/commands/BroadcastWorkCommand.js +16 -0
  9. package/src/cli/commands/NotificationWorkCommand.d.ts +10 -0
  10. package/src/cli/commands/NotificationWorkCommand.d.ts.map +1 -0
  11. package/src/cli/commands/NotificationWorkCommand.js +16 -0
  12. package/src/cli/commands/QueueCommand.d.ts +10 -0
  13. package/src/cli/commands/QueueCommand.d.ts.map +1 -0
  14. package/src/cli/commands/QueueCommand.js +63 -0
  15. package/src/cli/commands/QueueWorkCommandUtils.d.ts +10 -0
  16. package/src/cli/commands/QueueWorkCommandUtils.d.ts.map +1 -0
  17. package/src/cli/commands/QueueWorkCommandUtils.js +43 -0
  18. package/src/cli/commands/createKindWorkCommand.d.ts +9 -0
  19. package/src/cli/commands/createKindWorkCommand.d.ts.map +1 -0
  20. package/src/cli/commands/createKindWorkCommand.js +33 -0
  21. package/src/cli/commands/index.d.ts +3 -0
  22. package/src/cli/commands/index.d.ts.map +1 -1
  23. package/src/cli/commands/index.js +3 -0
  24. package/src/cli/scaffolding/ModelGenerator.d.ts.map +1 -1
  25. package/src/cli/scaffolding/ModelGenerator.js +1 -0
  26. package/src/cli/workers/QueueWorkRunner.d.ts +23 -0
  27. package/src/cli/workers/QueueWorkRunner.d.ts.map +1 -0
  28. package/src/cli/workers/QueueWorkRunner.js +142 -0
  29. package/src/collections/Collection.d.ts +30 -0
  30. package/src/collections/Collection.d.ts.map +1 -0
  31. package/src/collections/Collection.js +146 -0
  32. package/src/collections/index.d.ts +3 -0
  33. package/src/collections/index.d.ts.map +1 -0
  34. package/src/collections/index.js +1 -0
  35. package/src/config/broadcast.d.ts.map +1 -1
  36. package/src/config/broadcast.js +5 -3
  37. package/src/config/cache.d.ts.map +1 -1
  38. package/src/config/cache.js +12 -6
  39. package/src/config/database.d.ts.map +1 -1
  40. package/src/config/database.js +5 -3
  41. package/src/config/mail.d.ts.map +1 -1
  42. package/src/config/mail.js +21 -14
  43. package/src/config/notification.d.ts.map +1 -1
  44. package/src/config/notification.js +10 -5
  45. package/src/config/storage.d.ts.map +1 -1
  46. package/src/config/storage.js +5 -6
  47. package/src/events/EventDispatcher.d.ts +16 -0
  48. package/src/events/EventDispatcher.d.ts.map +1 -0
  49. package/src/events/EventDispatcher.js +90 -0
  50. package/src/events/index.d.ts +3 -0
  51. package/src/events/index.d.ts.map +1 -0
  52. package/src/events/index.js +1 -0
  53. package/src/features/Queue.js +1 -1
  54. package/src/http/Response.d.ts +2 -2
  55. package/src/http/Response.d.ts.map +1 -1
  56. package/src/index.d.ts +11 -0
  57. package/src/index.d.ts.map +1 -1
  58. package/src/index.js +11 -0
  59. package/src/middleware/CsrfMiddleware.d.ts.map +1 -1
  60. package/src/middleware/CsrfMiddleware.js +20 -25
  61. package/src/middleware/SessionMiddleware.d.ts +8 -0
  62. package/src/middleware/SessionMiddleware.d.ts.map +1 -0
  63. package/src/middleware/SessionMiddleware.js +15 -0
  64. package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -1
  65. package/src/orm/DatabaseRuntimeRegistration.js +4 -2
  66. package/src/orm/Model.d.ts +15 -0
  67. package/src/orm/Model.d.ts.map +1 -1
  68. package/src/orm/Model.js +57 -8
  69. package/src/orm/QueryBuilder.d.ts +9 -1
  70. package/src/orm/QueryBuilder.d.ts.map +1 -1
  71. package/src/orm/QueryBuilder.js +54 -2
  72. package/src/scripts/TemplateSync.js +23 -1
  73. package/src/security/PasswordResetTokenBroker.d.ts +39 -0
  74. package/src/security/PasswordResetTokenBroker.d.ts.map +1 -0
  75. package/src/security/PasswordResetTokenBroker.js +131 -0
  76. package/src/session/SessionManager.d.ts +39 -0
  77. package/src/session/SessionManager.d.ts.map +1 -0
  78. package/src/session/SessionManager.js +149 -0
  79. package/src/session/index.d.ts +3 -0
  80. package/src/session/index.d.ts.map +1 -0
  81. package/src/session/index.js +1 -0
  82. package/src/templates/features/Queue.ts.tpl +4 -3
  83. package/src/templates/project/basic/config/FileLogWriter.ts.tpl +4 -3
  84. package/src/templates/project/basic/config/SecretsManager.ts.tpl +1 -1
  85. package/src/templates/project/basic/config/broadcast.ts.tpl +6 -4
  86. package/src/templates/project/basic/config/cache.ts.tpl +17 -5
  87. package/src/templates/project/basic/config/database.ts.tpl +6 -4
  88. package/src/templates/project/basic/config/features.ts.tpl +2 -2
  89. package/src/templates/project/basic/config/logger.ts.tpl +0 -2
  90. package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +1 -1
  91. package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +1 -1
  92. package/src/templates/project/basic/config/mail.ts.tpl +26 -16
  93. package/src/templates/project/basic/config/microservices.ts.tpl +1 -1
  94. package/src/templates/project/basic/config/middleware.ts.tpl +6 -9
  95. package/src/templates/project/basic/config/notification.ts.tpl +19 -7
  96. package/src/templates/project/basic/config/security.ts.tpl +1 -2
  97. package/src/templates/project/basic/config/storage.ts.tpl +8 -6
  98. package/src/templates/project/basic/config/type.ts.tpl +2 -2
  99. package/src/tools/broadcast/Broadcast.d.ts +8 -0
  100. package/src/tools/broadcast/Broadcast.d.ts.map +1 -1
  101. package/src/tools/broadcast/Broadcast.js +23 -0
  102. package/src/tools/broadcast/BroadcastRuntimeRegistration.d.ts.map +1 -1
  103. package/src/tools/broadcast/BroadcastRuntimeRegistration.js +7 -4
  104. package/src/tools/notification/Notification.d.ts +10 -0
  105. package/src/tools/notification/Notification.d.ts.map +1 -1
  106. package/src/tools/notification/Notification.js +21 -0
  107. package/src/tools/notification/NotificationRuntimeRegistration.d.ts.map +1 -1
  108. package/src/tools/notification/NotificationRuntimeRegistration.js +7 -4
  109. package/src/tools/queue/Queue.d.ts.map +1 -1
  110. package/src/tools/queue/Queue.js +4 -1
  111. package/src/tools/queue/QueueRuntimeRegistration.d.ts.map +1 -1
  112. package/src/tools/queue/QueueRuntimeRegistration.js +5 -8
  113. package/src/tools/storage/StorageRuntimeRegistration.d.ts.map +1 -1
  114. package/src/tools/storage/StorageRuntimeRegistration.js +8 -10
  115. package/src/workers/BroadcastWorker.d.ts +22 -0
  116. package/src/workers/BroadcastWorker.d.ts.map +1 -0
  117. package/src/workers/BroadcastWorker.js +24 -0
  118. package/src/workers/NotificationWorker.d.ts +22 -0
  119. package/src/workers/NotificationWorker.d.ts.map +1 -0
  120. package/src/workers/NotificationWorker.js +23 -0
  121. package/src/workers/createQueueWorker.d.ts +24 -0
  122. package/src/workers/createQueueWorker.d.ts.map +1 -0
  123. package/src/workers/createQueueWorker.js +114 -0
@@ -16,10 +16,9 @@
16
16
  * security domains (e.g., different keys for different microservices).
17
17
  */
18
18
 
19
- import { Logger } from '@zintrust/core';
20
-
21
19
  import { appConfig } from './app';
22
20
  import { Env } from './env';
21
+ import { Logger } from './logger';
23
22
  import { ErrorFactory } from '@zintrust/core';
24
23
 
25
24
  /**
@@ -8,7 +8,7 @@ import { Env } from './env';
8
8
  import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from './type';
9
9
  import { ErrorFactory } from '@zintrust/core';
10
10
 
11
- const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
11
+ const hasOwn = <T extends object>(obj: T, key: PropertyKey): key is keyof T => {
12
12
  return Object.prototype.hasOwnProperty.call(obj, key);
13
13
  };
14
14
 
@@ -27,11 +27,13 @@ const getStorageDriver = (config: StorageConfigRuntime, name?: string): StorageD
27
27
  throw ErrorFactory.createConfigError(`Storage disk not configured: ${diskName}`);
28
28
  }
29
29
 
30
- // Backwards-compatible fallback.
31
- const fallback = config.drivers['local'] ?? Object.values(config.drivers)[0];
32
- if (fallback !== undefined) return fallback;
30
+ if (Object.keys(config.drivers ?? {}).length === 0) {
31
+ throw ErrorFactory.createConfigError('No storage disks are configured');
32
+ }
33
33
 
34
- throw ErrorFactory.createConfigError('No storage disks are configured');
34
+ throw ErrorFactory.createConfigError(
35
+ `Storage default disk not configured: ${diskName || '<empty>'}`
36
+ );
35
37
  };
36
38
 
37
39
  const getDrivers = (): StorageDrivers => ({
@@ -74,7 +76,7 @@ const storageConfigObj = {
74
76
  * Default storage driver (dynamic; tests may mutate process.env)
75
77
  */
76
78
  get default(): string {
77
- return Env.get('STORAGE_DRIVER', 'local');
79
+ return Env.get('STORAGE_CONNECTION', Env.get('STORAGE_DRIVER', 'local')).trim().toLowerCase();
78
80
  },
79
81
 
80
82
  /**
@@ -1,5 +1,5 @@
1
- import { Env } from '@config/env';
2
- import type { Middleware as MiddlewareFn } from '@middleware/MiddlewareStack';
1
+ import { Env } from './env';
2
+ import type { Middleware as MiddlewareFn } from '@zintrust/core';
3
3
 
4
4
  export type Environment =
5
5
  | 'development'
@@ -3,6 +3,14 @@ type Broadcaster = Readonly<{
3
3
  }>;
4
4
  export declare const Broadcast: Readonly<{
5
5
  send(channel: string, event: string, data: unknown): Promise<unknown>;
6
+ broadcastNow(channel: string, event: string, data: unknown): Promise<unknown>;
7
+ BroadcastLater(channel: string, event: string, data: unknown, options?: {
8
+ queueName?: string;
9
+ timestamp?: number;
10
+ }): Promise<string>;
11
+ queue(queueName: string): Readonly<{
12
+ BroadcastLater: (channel: string, event: string, data: unknown, options?: {}) => Promise<string>;
13
+ }>;
6
14
  broadcaster(name?: string): Broadcaster;
7
15
  }>;
8
16
  export default Broadcast;
@@ -1 +1 @@
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"}
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;0BAM5B,MAAM,SAAS,MAAM,QAAQ,OAAO;4BAMrD,MAAM,SACR,MAAM,QACP,OAAO,YACJ;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;qBAepC,MAAM;kCAEa,MAAM,SAAS,MAAM,QAAQ,OAAO;;uBAKrD,MAAM,GAAG,WAAW;EAQvC,CAAC;AAEH,eAAe,SAAS,CAAC"}
@@ -49,6 +49,29 @@ export const Broadcast = Object.freeze({
49
49
  const config = await resolveBroadcasterConfig();
50
50
  return sendWithConfig(config, channel, event, data);
51
51
  },
52
+ // Alias for send() - explicit intent for immediate broadcast
53
+ async broadcastNow(channel, event, data) {
54
+ return this.send(channel, event, data);
55
+ },
56
+ // Queue broadcast for async processing
57
+ async BroadcastLater(channel, event, data, options = {}) {
58
+ const { queueName = 'broadcasts', timestamp = Date.now() } = options;
59
+ const { Queue } = await import('../queue/Queue.js');
60
+ const messageId = await Queue.enqueue(queueName, {
61
+ type: 'broadcast',
62
+ channel,
63
+ event,
64
+ data,
65
+ timestamp,
66
+ attempts: 0,
67
+ });
68
+ return messageId;
69
+ },
70
+ queue(queueName) {
71
+ return Object.freeze({
72
+ BroadcastLater: async (channel, event, data, options = {}) => Broadcast.BroadcastLater(channel, event, data, { ...options, queueName }),
73
+ });
74
+ },
52
75
  broadcaster(name) {
53
76
  return Object.freeze({
54
77
  send: async (channel, event, data) => {
@@ -1 +1 @@
1
- {"version":3,"file":"BroadcastRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/broadcast/BroadcastRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAIzD;;;;;;;GAOG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,GAAG,SAAS,CAAC,GACxD,IAAI,CAWN"}
1
+ {"version":3,"file":"BroadcastRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/broadcast/BroadcastRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKzD;;;;;;;GAOG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,GAAG,SAAS,CAAC,GACxD,IAAI,CAeN"}
@@ -1,3 +1,4 @@
1
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
1
2
  import { BroadcastRegistry } from './BroadcastRegistry.js';
2
3
  /**
3
4
  * Register broadcasters from runtime config.
@@ -12,9 +13,11 @@ export function registerBroadcastersFromRuntimeConfig(config) {
12
13
  BroadcastRegistry.register(name, driverConfig);
13
14
  }
14
15
  const defaultName = (config.default ?? '').toString().trim().toLowerCase();
15
- if (defaultName.length === 0)
16
- return;
17
- if (BroadcastRegistry.has(defaultName)) {
18
- BroadcastRegistry.register('default', BroadcastRegistry.get(defaultName));
16
+ if (defaultName.length === 0) {
17
+ throw ErrorFactory.createConfigError('Broadcast default driver is not configured');
19
18
  }
19
+ if (!BroadcastRegistry.has(defaultName)) {
20
+ throw ErrorFactory.createConfigError(`Broadcast default driver not configured: ${defaultName}`);
21
+ }
22
+ BroadcastRegistry.register('default', BroadcastRegistry.get(defaultName));
20
23
  }
@@ -5,6 +5,16 @@
5
5
  */
6
6
  export declare const Notification: Readonly<{
7
7
  send: (recipient: string, message: string, options?: Record<string, unknown>) => Promise<unknown>;
8
+ NotifyNow: (recipient: string, message: string, options?: Record<string, unknown>) => Promise<unknown>;
9
+ NotifyLater(recipient: string, message: string, notifyOptions?: Record<string, unknown>, queueOptions?: {
10
+ queueName?: string;
11
+ timestamp?: number;
12
+ }): Promise<string>;
13
+ queue(queueName: string): Readonly<{
14
+ NotifyLater: (recipient: string, message: string, notifyOptions?: Record<string, unknown>, queueOptions?: {
15
+ timestamp?: number;
16
+ }) => Promise<string>;
17
+ }>;
8
18
  channel: (name: string) => Readonly<{
9
19
  send: (recipient: string, message: string, options?: Record<string, unknown>) => Promise<unknown>;
10
20
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"Notification.d.ts","sourceRoot":"","sources":["../../../../src/tools/notification/Notification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,YAAY;;oBAEP,MAAM;0BAEM,MAAM,WAAW,MAAM,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;EAItF,CAAC;AAEH,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"Notification.d.ts","sourceRoot":"","sources":["../../../../src/tools/notification/Notification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,YAAY;;;2BAQV,MAAM,WACR,MAAM,kBACA,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,iBACxB;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC,MAAM,CAAC;qBAcD,MAAM;iCAGN,MAAM,WACR,MAAM,kBACA,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,iBACxB;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE;;oBAM1B,MAAM;0BAEM,MAAM,WAAW,MAAM,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;EAItF,CAAC;AAEH,eAAe,YAAY,CAAC"}
@@ -6,6 +6,27 @@
6
6
  import { NotificationService } from './Service.js';
7
7
  export const Notification = Object.freeze({
8
8
  send: NotificationService.send,
9
+ // Alias for send() - explicit intent for immediate notification
10
+ NotifyNow: NotificationService.send,
11
+ // Queue notification for async processing
12
+ async NotifyLater(recipient, message, notifyOptions = {}, queueOptions = {}) {
13
+ const { queueName = 'notifications', timestamp = Date.now() } = queueOptions;
14
+ const { Queue } = await import('../queue/Queue.js');
15
+ const messageId = await Queue.enqueue(queueName, {
16
+ type: 'notification',
17
+ recipient,
18
+ message,
19
+ options: notifyOptions,
20
+ timestamp,
21
+ attempts: 0,
22
+ });
23
+ return messageId;
24
+ },
25
+ queue(queueName) {
26
+ return Object.freeze({
27
+ NotifyLater: async (recipient, message, notifyOptions = {}, queueOptions = {}) => Notification.NotifyLater(recipient, message, notifyOptions, { ...queueOptions, queueName }),
28
+ });
29
+ },
9
30
  channel: (name) => Object.freeze({
10
31
  send: async (recipient, message, options) => NotificationService.sendVia(name, recipient, message, options),
11
32
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/notification/NotificationRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAG5D;;;;;GAKG;AACH,wBAAgB,6CAA6C,CAC3D,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,SAAS,GAAG,SAAS,CAAC,GAC3D,IAAI,CAWN;AAED,eAAO,MAAM,+BAA+B;;EAE1C,CAAC;AAEH,eAAe,+BAA+B,CAAC"}
1
+ {"version":3,"file":"NotificationRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/notification/NotificationRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAI5D;;;;;GAKG;AACH,wBAAgB,6CAA6C,CAC3D,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,SAAS,GAAG,SAAS,CAAC,GAC3D,IAAI,CAiBN;AAED,eAAO,MAAM,+BAA+B;;EAE1C,CAAC;AAEH,eAAe,+BAA+B,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
1
2
  import { NotificationChannelRegistry } from './NotificationChannelRegistry.js';
2
3
  /**
3
4
  * Register notification channels from runtime config.
@@ -10,11 +11,13 @@ export function registerNotificationChannelsFromRuntimeConfig(config) {
10
11
  NotificationChannelRegistry.register(name, driverConfig);
11
12
  }
12
13
  const defaultName = (config.default ?? '').toString().trim().toLowerCase();
13
- if (defaultName.length === 0)
14
- return;
15
- if (NotificationChannelRegistry.has(defaultName)) {
16
- NotificationChannelRegistry.register('default', NotificationChannelRegistry.get(defaultName));
14
+ if (defaultName.length === 0) {
15
+ throw ErrorFactory.createConfigError('Notification default channel is not configured');
17
16
  }
17
+ if (!NotificationChannelRegistry.has(defaultName)) {
18
+ throw ErrorFactory.createConfigError(`Notification default channel not configured: ${defaultName}`);
19
+ }
20
+ NotificationChannelRegistry.register('default', NotificationChannelRegistry.get(defaultName));
18
21
  }
19
22
  export const NotificationRuntimeRegistration = Object.freeze({
20
23
  registerNotificationChannelsFromRuntimeConfig,
@@ -1 +1 @@
1
- {"version":3,"file":"Queue.d.ts","sourceRoot":"","sources":["../../../../src/tools/queue/Queue.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,UAAU,YAAY;IACpB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjE,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1E,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAID,eAAO,MAAM,KAAK;mBACD,MAAM,UAAU,YAAY;aAIlC,IAAI;eAIF,MAAM,GAAG,YAAY;YAUlB,CAAC,mBAAmB,MAAM,WAAW,CAAC,eAAe,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAK7E,CAAC,mBACN,MAAM,eACA,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;eAKtB,MAAM,MAAM,MAAM,eAAe,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;kBAKpD,MAAM,eAAe,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;iBAK9C,MAAM,eAAe,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAI9D,CAAC;AAEH,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"Queue.d.ts","sourceRoot":"","sources":["../../../../src/tools/queue/Queue.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,UAAU,YAAY;IACpB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjE,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1E,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAID,eAAO,MAAM,KAAK;mBACD,MAAM,UAAU,YAAY;aAIlC,IAAI;eAIF,MAAM,GAAG,YAAY;YAelB,CAAC,mBAAmB,MAAM,WAAW,CAAC,eAAe,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAK7E,CAAC,mBACN,MAAM,eACA,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;eAKtB,MAAM,MAAM,MAAM,eAAe,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;kBAKpD,MAAM,eAAe,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;iBAK9C,MAAM,eAAe,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAI9D,CAAC;AAEH,eAAe,KAAK,CAAC"}
@@ -8,7 +8,10 @@ export const Queue = Object.freeze({
8
8
  drivers.clear();
9
9
  },
10
10
  get(name) {
11
- const driverName = (name ?? process.env['QUEUE_DRIVER'] ?? 'inmemory')
11
+ const driverName = (name ??
12
+ process.env['QUEUE_CONNECTION'] ??
13
+ process.env['QUEUE_DRIVER'] ??
14
+ 'inmemory')
12
15
  .toString()
13
16
  .trim()
14
17
  .toLowerCase();
@@ -1 +1 @@
1
- {"version":3,"file":"QueueRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/queue/QueueRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAMjD;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAgBzE"}
1
+ {"version":3,"file":"QueueRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/queue/QueueRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAOjD;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAczE"}
@@ -1,3 +1,4 @@
1
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
1
2
  import { InMemoryQueue } from '../queue/drivers/InMemory.js';
2
3
  import { RedisQueue } from '../queue/drivers/Redis.js';
3
4
  import { Queue } from '../queue/Queue.js';
@@ -16,13 +17,9 @@ export function registerQueuesFromRuntimeConfig(config) {
16
17
  Queue.register('sync', InMemoryQueue);
17
18
  Queue.register('redis', RedisQueue);
18
19
  const defaultName = (config.default ?? '').toString().trim().toLowerCase();
19
- if (defaultName.length === 0)
20
- return;
21
- try {
22
- const drv = Queue.get(defaultName);
23
- Queue.register('default', drv);
24
- }
25
- catch {
26
- // Best-effort: external drivers may be registered by optional packages.
20
+ if (defaultName.length === 0) {
21
+ throw ErrorFactory.createConfigError('Queue default driver is not configured');
27
22
  }
23
+ const drv = Queue.get(defaultName);
24
+ Queue.register('default', drv);
28
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"StorageRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/storage/StorageRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9E,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,GAAG;IACxD,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,mBAAmB,CAAC;CAC1D,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAmBjF;AAED,eAAO,MAAM,0BAA0B;;EAErC,CAAC;AAEH,eAAe,0BAA0B,CAAC"}
1
+ {"version":3,"file":"StorageRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../../src/tools/storage/StorageRuntimeRegistration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAI9E,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,GAAG;IACxD,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,mBAAmB,CAAC;CAC1D,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAiBjF;AAED,eAAO,MAAM,0BAA0B;;EAErC,CAAC;AAEH,eAAe,0BAA0B,CAAC"}
@@ -1,21 +1,19 @@
1
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
1
2
  import { StorageDiskRegistry } from './StorageDiskRegistry.js';
2
3
  export function registerDisksFromRuntimeConfig(config) {
3
4
  for (const [name, driverConfig] of Object.entries(config.drivers)) {
4
5
  StorageDiskRegistry.register(name, driverConfig);
5
6
  }
6
7
  // Alias reserved name `default` to the configured default.
7
- // Prefer config.getDriverConfig() so we preserve its fallback semantics.
8
- let resolvedDefault;
9
- if (typeof config.getDriverConfig === 'function') {
10
- resolvedDefault = config.getDriverConfig('default');
8
+ const defaultName = String(config.default ?? '').trim();
9
+ if (defaultName.length === 0) {
10
+ throw ErrorFactory.createConfigError('Storage default disk is not configured');
11
11
  }
12
- else {
13
- const values = Object.values(config.drivers);
14
- resolvedDefault = config.drivers[config.default] ?? values[0];
15
- }
16
- if (resolvedDefault !== undefined) {
17
- StorageDiskRegistry.register('default', resolvedDefault);
12
+ const resolvedDefault = config.drivers[defaultName];
13
+ if (resolvedDefault === undefined) {
14
+ throw ErrorFactory.createConfigError(`Storage default disk not configured: ${defaultName}`);
18
15
  }
16
+ StorageDiskRegistry.register('default', resolvedDefault);
19
17
  }
20
18
  export const StorageRuntimeRegistration = Object.freeze({
21
19
  registerDisksFromRuntimeConfig,
@@ -0,0 +1,22 @@
1
+ /**
2
+ * BroadcastWorker - Processes queued broadcasts
3
+ *
4
+ * This worker dequeues broadcast messages and sends them using the Broadcast service.
5
+ * Use with Queue.dequeue() in a background process or cron job.
6
+ */
7
+ export declare const BroadcastWorker: Readonly<{
8
+ processOne: (queueName?: string, driverName?: string) => Promise<boolean>;
9
+ processAll: (queueName?: string, driverName?: string) => Promise<number>;
10
+ runOnce: (opts?: {
11
+ queueName?: string;
12
+ driverName?: string;
13
+ maxItems?: number;
14
+ }) => Promise<number>;
15
+ startWorker: (opts?: {
16
+ queueName?: string;
17
+ driverName?: string;
18
+ signal?: AbortSignal;
19
+ }) => Promise<number>;
20
+ }>;
21
+ export default BroadcastWorker;
22
+ //# sourceMappingURL=BroadcastWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BroadcastWorker.d.ts","sourceRoot":"","sources":["../../../src/workers/BroadcastWorker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,eAAO,MAAM,eAAe;0BAbrB,CAAC,oBAAoB,CAAC;0BAGf,CAAC,oBAAoB,CAAC;kBACnB,CAAC;iBAAiB,CAAC;kBAE3B,CAAC;gBACC,CAAA;;sBAGE,CAAC;iBAGP,CAAA;kBAAwB,CAAC;cAC3B,CAAC;;EAaJ,CAAC;AAEH,eAAe,eAAe,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * BroadcastWorker - Processes queued broadcasts
3
+ *
4
+ * This worker dequeues broadcast messages and sends them using the Broadcast service.
5
+ * Use with Queue.dequeue() in a background process or cron job.
6
+ */
7
+ import { createQueueWorker } from '../workers/createQueueWorker.js';
8
+ import { Broadcast } from '../tools/broadcast/Broadcast.js';
9
+ export const BroadcastWorker = Object.freeze({
10
+ ...createQueueWorker({
11
+ kindLabel: 'broadcast',
12
+ defaultQueueName: 'broadcasts',
13
+ maxAttempts: 3,
14
+ getLogFields: (payload) => ({
15
+ channel: payload.channel,
16
+ event: payload.event,
17
+ queuedAt: payload.timestamp,
18
+ }),
19
+ handle: async (payload) => {
20
+ await Broadcast.send(payload.channel, payload.event, payload.data);
21
+ },
22
+ }),
23
+ });
24
+ export default BroadcastWorker;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * NotificationWorker - Processes queued notifications
3
+ *
4
+ * This worker dequeues notification messages and sends them using the Notification service.
5
+ * Use with Queue.dequeue() in a background process or cron job.
6
+ */
7
+ export declare const NotificationWorker: Readonly<{
8
+ processOne: (queueName?: string, driverName?: string) => Promise<boolean>;
9
+ processAll: (queueName?: string, driverName?: string) => Promise<number>;
10
+ runOnce: (opts?: {
11
+ queueName?: string;
12
+ driverName?: string;
13
+ maxItems?: number;
14
+ }) => Promise<number>;
15
+ startWorker: (opts?: {
16
+ queueName?: string;
17
+ driverName?: string;
18
+ signal?: AbortSignal;
19
+ }) => Promise<number>;
20
+ }>;
21
+ export default NotificationWorker;
22
+ //# sourceMappingURL=NotificationWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationWorker.d.ts","sourceRoot":"","sources":["../../../src/workers/NotificationWorker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,eAAO,MAAM,kBAAkB;0BAdyD,CAAC,oBACzE,CAAC;0BAGf,CAAC,oBAAoB,CAAC;kBACnB,CAAC;iBAAkB,CAAA;kBAAwB,CAAC;gBAEtC,CAAC;;sBAGV,CAAC;iBAAiB,CAAC;kBACZ,CAAC;cAGL,CAAC;;EAaJ,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * NotificationWorker - Processes queued notifications
3
+ *
4
+ * This worker dequeues notification messages and sends them using the Notification service.
5
+ * Use with Queue.dequeue() in a background process or cron job.
6
+ */
7
+ import { createQueueWorker } from '../workers/createQueueWorker.js';
8
+ import { Notification } from '../tools/notification/Notification.js';
9
+ export const NotificationWorker = Object.freeze({
10
+ ...createQueueWorker({
11
+ kindLabel: 'notification',
12
+ defaultQueueName: 'notifications',
13
+ maxAttempts: 3,
14
+ getLogFields: (payload) => ({
15
+ recipient: payload.recipient,
16
+ queuedAt: payload.timestamp,
17
+ }),
18
+ handle: async (payload) => {
19
+ await Notification.send(payload.recipient, payload.message, payload.options);
20
+ },
21
+ }),
22
+ });
23
+ export default NotificationWorker;
@@ -0,0 +1,24 @@
1
+ type QueueWorker = {
2
+ processOne: (queueName?: string, driverName?: string) => Promise<boolean>;
3
+ processAll: (queueName?: string, driverName?: string) => Promise<number>;
4
+ runOnce: (opts?: {
5
+ queueName?: string;
6
+ driverName?: string;
7
+ maxItems?: number;
8
+ }) => Promise<number>;
9
+ startWorker: (opts?: {
10
+ queueName?: string;
11
+ driverName?: string;
12
+ signal?: AbortSignal;
13
+ }) => Promise<number>;
14
+ };
15
+ export type CreateQueueWorkerOptions<TPayload> = {
16
+ kindLabel: string;
17
+ defaultQueueName: string;
18
+ maxAttempts: number;
19
+ getLogFields: (payload: TPayload) => Record<string, unknown>;
20
+ handle: (payload: TPayload) => Promise<void>;
21
+ };
22
+ export declare function createQueueWorker<TPayload>(options: CreateQueueWorkerOptions<TPayload>): QueueWorker;
23
+ export {};
24
+ //# sourceMappingURL=createQueueWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createQueueWorker.d.ts","sourceRoot":"","sources":["../../../src/workers/createQueueWorker.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1E,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtB,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,wBAAwB,CAAC,QAAQ,IAAI;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C,CAAC;AA4IF,wBAAgB,iBAAiB,CAAC,QAAQ,EACxC,OAAO,EAAE,wBAAwB,CAAC,QAAQ,CAAC,GAC1C,WAAW,CAOb"}
@@ -0,0 +1,114 @@
1
+ import { Logger } from '../config/logger.js';
2
+ import { Queue } from '../tools/queue/Queue.js';
3
+ const buildBaseLogFields = (message, getLogFields) => {
4
+ return {
5
+ messageId: message.id,
6
+ ...getLogFields(message.payload),
7
+ };
8
+ };
9
+ const createProcessOne = (options) => {
10
+ return async (queueName = options.defaultQueueName, driverName) => {
11
+ const message = await Queue.dequeue(queueName, driverName);
12
+ if (!message)
13
+ return false;
14
+ const baseLogFields = buildBaseLogFields(message, options.getLogFields);
15
+ // Check for delayed execution
16
+ const payload = message.payload;
17
+ const rawTimestamp = 'timestamp' in payload ? payload['timestamp'] : 0;
18
+ const timestamp = typeof rawTimestamp === 'number' ? rawTimestamp : 0;
19
+ if (timestamp > Date.now()) {
20
+ Logger.info(`${options.kindLabel} not due yet, re-queueing`, {
21
+ ...baseLogFields,
22
+ dueAt: new Date(timestamp).toISOString(),
23
+ });
24
+ // Re-queue original payload
25
+ await Queue.enqueue(queueName, message.payload, driverName);
26
+ await Queue.ack(queueName, message.id, driverName);
27
+ return false;
28
+ }
29
+ try {
30
+ Logger.info(`Processing queued ${options.kindLabel}`, baseLogFields);
31
+ await options.handle(message.payload);
32
+ await Queue.ack(queueName, message.id, driverName);
33
+ Logger.info(`${options.kindLabel} processed successfully`, baseLogFields);
34
+ return true;
35
+ }
36
+ catch (error) {
37
+ const attempts = message.attempts ?? 0;
38
+ Logger.error(`Failed to process ${options.kindLabel}`, {
39
+ ...baseLogFields,
40
+ error,
41
+ attempts,
42
+ });
43
+ if (attempts < options.maxAttempts) {
44
+ await Queue.enqueue(queueName, message.payload, driverName);
45
+ Logger.info(`${options.kindLabel} re-queued for retry`, {
46
+ ...baseLogFields,
47
+ attempts: attempts + 1,
48
+ });
49
+ }
50
+ await Queue.ack(queueName, message.id, driverName);
51
+ return false;
52
+ }
53
+ };
54
+ };
55
+ const createProcessAll = (defaultQueueName, processOne) => {
56
+ return async (queueName = defaultQueueName, driverName) => {
57
+ let processed = 0;
58
+ let hasMore = true;
59
+ while (hasMore) {
60
+ // eslint-disable-next-line no-await-in-loop
61
+ hasMore = await processOne(queueName, driverName);
62
+ if (hasMore)
63
+ processed++;
64
+ }
65
+ return processed;
66
+ };
67
+ };
68
+ const createRunOnce = (defaultQueueName, processOne) => {
69
+ return async (opts = {}) => {
70
+ const { queueName = defaultQueueName, driverName, maxItems } = opts;
71
+ let processed = 0;
72
+ if (maxItems === undefined) {
73
+ while (true) {
74
+ // eslint-disable-next-line no-await-in-loop
75
+ const didProcess = await processOne(queueName, driverName);
76
+ if (!didProcess)
77
+ break;
78
+ processed++;
79
+ }
80
+ return processed;
81
+ }
82
+ for (let i = 0; i < maxItems; i++) {
83
+ // eslint-disable-next-line no-await-in-loop
84
+ const didProcess = await processOne(queueName, driverName);
85
+ if (!didProcess)
86
+ break;
87
+ processed++;
88
+ }
89
+ return processed;
90
+ };
91
+ };
92
+ const createStartWorker = (kindLabel, defaultQueueName, processOne) => {
93
+ return async (opts = {}) => {
94
+ const { queueName = defaultQueueName, driverName, signal } = opts;
95
+ Logger.info(`Starting ${kindLabel} worker (drain-until-empty)`, { queueName });
96
+ let processedCount = 0;
97
+ while (signal?.aborted !== true) {
98
+ // eslint-disable-next-line no-await-in-loop
99
+ const didProcess = await processOne(queueName, driverName);
100
+ if (!didProcess)
101
+ break;
102
+ processedCount++;
103
+ }
104
+ Logger.info(`${kindLabel} worker finished (queue drained)`, { queueName, processedCount });
105
+ return processedCount;
106
+ };
107
+ };
108
+ export function createQueueWorker(options) {
109
+ const processOne = createProcessOne(options);
110
+ const processAll = createProcessAll(options.defaultQueueName, processOne);
111
+ const runOnce = createRunOnce(options.defaultQueueName, processOne);
112
+ const startWorker = createStartWorker(options.kindLabel, options.defaultQueueName, processOne);
113
+ return Object.freeze({ processOne, processAll, runOnce, startWorker });
114
+ }