@zintrust/core 0.1.15 → 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.
- package/package.json +1 -1
- package/src/cli/CLI.d.ts.map +1 -1
- package/src/cli/CLI.js +6 -0
- package/src/cli/commands/BroadcastWorkCommand.d.ts +10 -0
- package/src/cli/commands/BroadcastWorkCommand.d.ts.map +1 -0
- package/src/cli/commands/BroadcastWorkCommand.js +16 -0
- package/src/cli/commands/NotificationWorkCommand.d.ts +10 -0
- package/src/cli/commands/NotificationWorkCommand.d.ts.map +1 -0
- package/src/cli/commands/NotificationWorkCommand.js +16 -0
- package/src/cli/commands/QueueCommand.d.ts +10 -0
- package/src/cli/commands/QueueCommand.d.ts.map +1 -0
- package/src/cli/commands/QueueCommand.js +63 -0
- package/src/cli/commands/QueueWorkCommandUtils.d.ts +10 -0
- package/src/cli/commands/QueueWorkCommandUtils.d.ts.map +1 -0
- package/src/cli/commands/QueueWorkCommandUtils.js +43 -0
- package/src/cli/commands/createKindWorkCommand.d.ts +9 -0
- package/src/cli/commands/createKindWorkCommand.d.ts.map +1 -0
- package/src/cli/commands/createKindWorkCommand.js +33 -0
- package/src/cli/commands/index.d.ts +3 -0
- package/src/cli/commands/index.d.ts.map +1 -1
- package/src/cli/commands/index.js +3 -0
- package/src/cli/scaffolding/ModelGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/ModelGenerator.js +1 -0
- package/src/cli/workers/QueueWorkRunner.d.ts +23 -0
- package/src/cli/workers/QueueWorkRunner.d.ts.map +1 -0
- package/src/cli/workers/QueueWorkRunner.js +142 -0
- package/src/collections/Collection.d.ts +30 -0
- package/src/collections/Collection.d.ts.map +1 -0
- package/src/collections/Collection.js +146 -0
- package/src/collections/index.d.ts +3 -0
- package/src/collections/index.d.ts.map +1 -0
- package/src/collections/index.js +1 -0
- package/src/events/EventDispatcher.d.ts +16 -0
- package/src/events/EventDispatcher.d.ts.map +1 -0
- package/src/events/EventDispatcher.js +90 -0
- package/src/events/index.d.ts +3 -0
- package/src/events/index.d.ts.map +1 -0
- package/src/events/index.js +1 -0
- package/src/features/Queue.js +1 -1
- package/src/http/Response.d.ts +2 -2
- package/src/http/Response.d.ts.map +1 -1
- package/src/index.d.ts +11 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +11 -0
- package/src/middleware/CsrfMiddleware.d.ts.map +1 -1
- package/src/middleware/CsrfMiddleware.js +20 -25
- package/src/middleware/SessionMiddleware.d.ts +8 -0
- package/src/middleware/SessionMiddleware.d.ts.map +1 -0
- package/src/middleware/SessionMiddleware.js +15 -0
- package/src/orm/Model.d.ts +15 -0
- package/src/orm/Model.d.ts.map +1 -1
- package/src/orm/Model.js +57 -8
- package/src/orm/QueryBuilder.d.ts +9 -1
- package/src/orm/QueryBuilder.d.ts.map +1 -1
- package/src/orm/QueryBuilder.js +54 -2
- package/src/scripts/TemplateSync.js +23 -1
- package/src/security/PasswordResetTokenBroker.d.ts +39 -0
- package/src/security/PasswordResetTokenBroker.d.ts.map +1 -0
- package/src/security/PasswordResetTokenBroker.js +131 -0
- package/src/session/SessionManager.d.ts +39 -0
- package/src/session/SessionManager.d.ts.map +1 -0
- package/src/session/SessionManager.js +149 -0
- package/src/session/index.d.ts +3 -0
- package/src/session/index.d.ts.map +1 -0
- package/src/session/index.js +1 -0
- package/src/templates/features/Queue.ts.tpl +4 -3
- package/src/templates/project/basic/config/FileLogWriter.ts.tpl +4 -3
- package/src/templates/project/basic/config/SecretsManager.ts.tpl +1 -1
- package/src/templates/project/basic/config/broadcast.ts.tpl +2 -2
- package/src/templates/project/basic/config/cache.ts.tpl +2 -2
- package/src/templates/project/basic/config/database.ts.tpl +2 -2
- package/src/templates/project/basic/config/features.ts.tpl +2 -2
- package/src/templates/project/basic/config/logger.ts.tpl +0 -2
- package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +1 -1
- package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +1 -1
- package/src/templates/project/basic/config/mail.ts.tpl +2 -2
- package/src/templates/project/basic/config/microservices.ts.tpl +1 -1
- package/src/templates/project/basic/config/middleware.ts.tpl +6 -9
- package/src/templates/project/basic/config/notification.ts.tpl +2 -2
- package/src/templates/project/basic/config/security.ts.tpl +1 -2
- package/src/templates/project/basic/config/storage.ts.tpl +2 -2
- package/src/templates/project/basic/config/type.ts.tpl +2 -2
- package/src/tools/broadcast/Broadcast.d.ts +8 -0
- package/src/tools/broadcast/Broadcast.d.ts.map +1 -1
- package/src/tools/broadcast/Broadcast.js +23 -0
- package/src/tools/notification/Notification.d.ts +10 -0
- package/src/tools/notification/Notification.d.ts.map +1 -1
- package/src/tools/notification/Notification.js +21 -0
- package/src/workers/BroadcastWorker.d.ts +22 -0
- package/src/workers/BroadcastWorker.d.ts.map +1 -0
- package/src/workers/BroadcastWorker.js +24 -0
- package/src/workers/NotificationWorker.d.ts +22 -0
- package/src/workers/NotificationWorker.d.ts.map +1 -0
- package/src/workers/NotificationWorker.js +23 -0
- package/src/workers/createQueueWorker.d.ts +24 -0
- package/src/workers/createQueueWorker.d.ts.map +1 -0
- package/src/workers/createQueueWorker.js +114 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SessionManager } from './SessionManager.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// TEMPLATE_START
|
|
2
2
|
|
|
3
|
-
import { generateSecureJobId
|
|
3
|
+
import { generateSecureJobId } from '@common/uuid';
|
|
4
|
+
import { Logger } from '@config/logger';
|
|
4
5
|
|
|
5
6
|
export interface QueueJob {
|
|
6
7
|
id: string;
|
|
@@ -19,7 +20,7 @@ export const Queue = Object.freeze({
|
|
|
19
20
|
* Add a job to the queue
|
|
20
21
|
*/
|
|
21
22
|
async add<T>(data: T): Promise<string> {
|
|
22
|
-
const id =
|
|
23
|
+
const id = generateSecureJobId();
|
|
23
24
|
const job: QueueJob = {
|
|
24
25
|
id,
|
|
25
26
|
data,
|
|
@@ -43,4 +44,4 @@ export const Queue = Object.freeze({
|
|
|
43
44
|
},
|
|
44
45
|
});
|
|
45
46
|
|
|
46
|
-
// TEMPLATE_END
|
|
47
|
+
// TEMPLATE_END
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FileLogWriter (Node.js only)
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Provides best-effort file logging with daily + size-based rotation.
|
|
5
|
+
* This module imports Node built-ins and should be loaded only in Node environments.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
import { ensureDirSafe } from '@zintrust/core';
|
|
9
|
+
import { Env } from './env';
|
|
7
10
|
import * as fs from 'node:fs';
|
|
8
11
|
import * as path from 'node:path';
|
|
9
12
|
|
|
10
|
-
import { Env } from './env';
|
|
11
|
-
|
|
12
13
|
const getCwdSafe = (): string => {
|
|
13
14
|
try {
|
|
14
15
|
if (typeof process === 'undefined' || typeof process.cwd !== 'function') return '';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Driver selection must be dynamic (tests may mutate process.env).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Env } from '
|
|
8
|
+
import { Env } from './env';
|
|
9
9
|
import {
|
|
10
10
|
BroadcastConfigInput,
|
|
11
11
|
BroadcastDrivers,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
PusherBroadcastDriverConfig,
|
|
15
15
|
RedisBroadcastDriverConfig,
|
|
16
16
|
RedisHttpsBroadcastDriverConfig,
|
|
17
|
-
} from '
|
|
17
|
+
} from './type';
|
|
18
18
|
import { ErrorFactory } from '@zintrust/core';
|
|
19
19
|
|
|
20
20
|
const normalizeDriverName = (value: string): string => value.trim().toLowerCase();
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import { CacheConfigInput, CacheDriverConfig } from '
|
|
7
|
+
import { Env } from './env';
|
|
8
|
+
import { CacheConfigInput, CacheDriverConfig } from './type';
|
|
9
9
|
import { ErrorFactory } from '@zintrust/core';
|
|
10
10
|
|
|
11
11
|
const getCacheDriver = (config: CacheConfigInput, name?: string): CacheDriverConfig => {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import { DatabaseConfigShape, DatabaseConnectionConfig, DatabaseConnections } from '
|
|
7
|
+
import { Env } from './env';
|
|
8
|
+
import { DatabaseConfigShape, DatabaseConnectionConfig, DatabaseConnections } from './type';
|
|
9
9
|
import { ErrorFactory } from '@zintrust/core';
|
|
10
10
|
|
|
11
11
|
const hasOwn = (obj: Record<string, unknown>, key: string): boolean => {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import type { MailConfigInput, MailDriverConfig } from '
|
|
7
|
+
import { Env } from './env';
|
|
8
|
+
import type { MailConfigInput, MailDriverConfig } from './type';
|
|
9
9
|
import { ErrorFactory } from '@zintrust/core';
|
|
10
10
|
|
|
11
11
|
const isMailDriverConfig = (value: unknown): value is MailDriverConfig => {
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CsrfMiddleware,
|
|
3
|
-
ErrorHandlerMiddleware,
|
|
4
|
-
LoggingMiddleware,
|
|
5
|
-
RateLimiter,
|
|
6
|
-
SecurityMiddleware,
|
|
7
|
-
type Middleware,
|
|
8
|
-
} from '@zintrust/core';
|
|
9
|
-
|
|
10
1
|
import { MiddlewareConfigType } from './type';
|
|
2
|
+
import { CsrfMiddleware } from '@zintrust/core';
|
|
3
|
+
import { ErrorHandlerMiddleware } from '@zintrust/core';
|
|
4
|
+
import { LoggingMiddleware } from '@zintrust/core';
|
|
5
|
+
import type { Middleware } from '@zintrust/core';
|
|
6
|
+
import { RateLimiter } from '@zintrust/core';
|
|
7
|
+
import { SecurityMiddleware } from '@zintrust/core';
|
|
11
8
|
|
|
12
9
|
const shared = Object.freeze({
|
|
13
10
|
log: LoggingMiddleware.create(),
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* Driver selection must be dynamic (tests may mutate process.env).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Env } from '
|
|
8
|
+
import { Env } from './env';
|
|
9
9
|
import type {
|
|
10
10
|
KnownNotificationDriverConfig,
|
|
11
11
|
NotificationConfigInput,
|
|
12
12
|
NotificationDrivers,
|
|
13
13
|
NotificationProviders,
|
|
14
|
-
} from '
|
|
14
|
+
} from './type';
|
|
15
15
|
import { ErrorFactory } from '@zintrust/core';
|
|
16
16
|
|
|
17
17
|
const normalizeName = (value: string): string => value.trim().toLowerCase();
|
|
@@ -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
|
/**
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Sealed namespace for immutability
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Env } from '
|
|
8
|
-
import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from '
|
|
7
|
+
import { Env } from './env';
|
|
8
|
+
import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from './type';
|
|
9
9
|
import { ErrorFactory } from '@zintrust/core';
|
|
10
10
|
|
|
11
11
|
const hasOwn = <T extends object>(obj: T, key: PropertyKey): key is keyof T => {
|
|
@@ -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;
|
|
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) => {
|
|
@@ -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;;
|
|
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
|
}),
|
|
@@ -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
|
+
}
|