@zintrust/core 0.1.15 → 0.1.17

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 (120) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/public/index.html +1 -1
  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/scaffolding/ProjectScaffolder.d.ts.map +1 -1
  27. package/src/cli/scaffolding/ProjectScaffolder.js +2 -1
  28. package/src/cli/workers/QueueWorkRunner.d.ts +23 -0
  29. package/src/cli/workers/QueueWorkRunner.d.ts.map +1 -0
  30. package/src/cli/workers/QueueWorkRunner.js +142 -0
  31. package/src/collections/Collection.d.ts +30 -0
  32. package/src/collections/Collection.d.ts.map +1 -0
  33. package/src/collections/Collection.js +146 -0
  34. package/src/collections/index.d.ts +3 -0
  35. package/src/collections/index.d.ts.map +1 -0
  36. package/src/collections/index.js +1 -0
  37. package/src/config/env.d.ts +2 -0
  38. package/src/config/env.d.ts.map +1 -1
  39. package/src/config/env.js +4 -0
  40. package/src/config/index.d.ts +3 -0
  41. package/src/config/index.d.ts.map +1 -1
  42. package/src/config/security.d.ts +4 -1
  43. package/src/config/security.d.ts.map +1 -1
  44. package/src/config/security.js +9 -1
  45. package/src/events/EventDispatcher.d.ts +16 -0
  46. package/src/events/EventDispatcher.d.ts.map +1 -0
  47. package/src/events/EventDispatcher.js +90 -0
  48. package/src/events/index.d.ts +3 -0
  49. package/src/events/index.d.ts.map +1 -0
  50. package/src/events/index.js +1 -0
  51. package/src/features/Queue.d.ts +1 -1
  52. package/src/features/Queue.d.ts.map +1 -1
  53. package/src/features/Queue.js +2 -2
  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 +12 -0
  57. package/src/index.d.ts.map +1 -1
  58. package/src/index.js +12 -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/node-singletons/crypto.d.ts +1 -1
  65. package/src/node-singletons/crypto.d.ts.map +1 -1
  66. package/src/node-singletons/crypto.js +1 -1
  67. package/src/orm/Model.d.ts +15 -0
  68. package/src/orm/Model.d.ts.map +1 -1
  69. package/src/orm/Model.js +57 -8
  70. package/src/orm/QueryBuilder.d.ts +9 -1
  71. package/src/orm/QueryBuilder.d.ts.map +1 -1
  72. package/src/orm/QueryBuilder.js +54 -2
  73. package/src/scripts/TemplateSync.js +23 -1
  74. package/src/security/EncryptedEnvelope.d.ts +77 -0
  75. package/src/security/EncryptedEnvelope.d.ts.map +1 -0
  76. package/src/security/EncryptedEnvelope.js +256 -0
  77. package/src/security/PasswordResetTokenBroker.d.ts +39 -0
  78. package/src/security/PasswordResetTokenBroker.d.ts.map +1 -0
  79. package/src/security/PasswordResetTokenBroker.js +131 -0
  80. package/src/security/StartupSecretValidation.d.ts.map +1 -1
  81. package/src/security/StartupSecretValidation.js +72 -0
  82. package/src/session/SessionManager.d.ts +39 -0
  83. package/src/session/SessionManager.d.ts.map +1 -0
  84. package/src/session/SessionManager.js +149 -0
  85. package/src/session/index.d.ts +3 -0
  86. package/src/session/index.d.ts.map +1 -0
  87. package/src/session/index.js +1 -0
  88. package/src/templates/features/Queue.ts.tpl +5 -4
  89. package/src/templates/project/basic/config/FileLogWriter.ts.tpl +4 -3
  90. package/src/templates/project/basic/config/SecretsManager.ts.tpl +1 -1
  91. package/src/templates/project/basic/config/broadcast.ts.tpl +2 -2
  92. package/src/templates/project/basic/config/cache.ts.tpl +2 -2
  93. package/src/templates/project/basic/config/database.ts.tpl +2 -2
  94. package/src/templates/project/basic/config/env.ts.tpl +5 -0
  95. package/src/templates/project/basic/config/features.ts.tpl +2 -2
  96. package/src/templates/project/basic/config/logger.ts.tpl +0 -2
  97. package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +1 -1
  98. package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +1 -1
  99. package/src/templates/project/basic/config/mail.ts.tpl +2 -2
  100. package/src/templates/project/basic/config/microservices.ts.tpl +1 -1
  101. package/src/templates/project/basic/config/middleware.ts.tpl +6 -9
  102. package/src/templates/project/basic/config/notification.ts.tpl +2 -2
  103. package/src/templates/project/basic/config/security.ts.tpl +12 -3
  104. package/src/templates/project/basic/config/storage.ts.tpl +2 -2
  105. package/src/templates/project/basic/config/type.ts.tpl +2 -2
  106. package/src/tools/broadcast/Broadcast.d.ts +8 -0
  107. package/src/tools/broadcast/Broadcast.d.ts.map +1 -1
  108. package/src/tools/broadcast/Broadcast.js +23 -0
  109. package/src/tools/notification/Notification.d.ts +10 -0
  110. package/src/tools/notification/Notification.d.ts.map +1 -1
  111. package/src/tools/notification/Notification.js +21 -0
  112. package/src/workers/BroadcastWorker.d.ts +22 -0
  113. package/src/workers/BroadcastWorker.d.ts.map +1 -0
  114. package/src/workers/BroadcastWorker.js +24 -0
  115. package/src/workers/NotificationWorker.d.ts +22 -0
  116. package/src/workers/NotificationWorker.d.ts.map +1 -0
  117. package/src/workers/NotificationWorker.js +23 -0
  118. package/src/workers/createQueueWorker.d.ts +24 -0
  119. package/src/workers/createQueueWorker.d.ts.map +1 -0
  120. package/src/workers/createQueueWorker.js +114 -0
@@ -0,0 +1,149 @@
1
+ import { generateSecureJobId } from '../common/uuid.js';
2
+ const DEFAULT_OPTIONS = {
3
+ cookieName: 'ZIN_SESSION_ID',
4
+ headerName: 'x-session-id',
5
+ ttlMs: 7 * 24 * 60 * 60 * 1000,
6
+ };
7
+ function parseCookies(cookieHeader) {
8
+ const list = {};
9
+ if (cookieHeader.length === 0)
10
+ return list;
11
+ cookieHeader.split(';').forEach((cookie) => {
12
+ const parts = cookie.split('=');
13
+ const name = parts.shift()?.trim();
14
+ const value = parts.join('=');
15
+ if (name !== null && name !== undefined)
16
+ list[name] = decodeURIComponent(value);
17
+ });
18
+ return list;
19
+ }
20
+ function appendSetCookie(res, cookie) {
21
+ const existing = res.getHeader('Set-Cookie');
22
+ if (existing === undefined) {
23
+ res.setHeader('Set-Cookie', cookie);
24
+ return;
25
+ }
26
+ if (Array.isArray(existing)) {
27
+ const existingCookies = existing.map(String);
28
+ res.setHeader('Set-Cookie', [...existingCookies, cookie]);
29
+ return;
30
+ }
31
+ if (typeof existing === 'string') {
32
+ res.setHeader('Set-Cookie', [existing, cookie]);
33
+ return;
34
+ }
35
+ res.setHeader('Set-Cookie', cookie);
36
+ }
37
+ function buildSessionCookie(cookieName, sessionId) {
38
+ // Keep this minimal; callers can override behavior later.
39
+ // HttpOnly prevents JS access; SameSite=Lax is a reasonable default for app sessions.
40
+ return `${cookieName}=${encodeURIComponent(sessionId)}; Path=/; HttpOnly; SameSite=Lax`;
41
+ }
42
+ function createSessionApi(sessions, sessionId, ttlMs) {
43
+ const withoutKey = (data, key) => {
44
+ if (!Object.prototype.hasOwnProperty.call(data, key))
45
+ return data;
46
+ const record = data;
47
+ const { [key]: _removed, ...rest } = record;
48
+ return rest;
49
+ };
50
+ const ensureStored = () => {
51
+ const existing = sessions.get(sessionId);
52
+ const now = Date.now();
53
+ if (existing !== undefined && existing.expiresAt > now) {
54
+ return existing;
55
+ }
56
+ const created = { data: {}, expiresAt: now + ttlMs };
57
+ sessions.set(sessionId, created);
58
+ return created;
59
+ };
60
+ return {
61
+ id: sessionId,
62
+ get(key) {
63
+ return ensureStored().data[key];
64
+ },
65
+ set(key, value) {
66
+ const stored = ensureStored();
67
+ stored.data[key] = value;
68
+ stored.expiresAt = Date.now() + ttlMs;
69
+ },
70
+ has(key) {
71
+ return Object.prototype.hasOwnProperty.call(ensureStored().data, key);
72
+ },
73
+ forget(key) {
74
+ const stored = ensureStored();
75
+ stored.data = withoutKey(stored.data, key);
76
+ stored.expiresAt = Date.now() + ttlMs;
77
+ },
78
+ all() {
79
+ return { ...ensureStored().data };
80
+ },
81
+ clear() {
82
+ const stored = ensureStored();
83
+ stored.data = {};
84
+ stored.expiresAt = Date.now() + ttlMs;
85
+ },
86
+ };
87
+ }
88
+ export const SessionManager = Object.freeze({
89
+ create(options = {}) {
90
+ const config = { ...DEFAULT_OPTIONS, ...options };
91
+ const sessions = new Map();
92
+ return {
93
+ getIdFromCookieHeader(cookieHeader) {
94
+ if (cookieHeader === undefined || cookieHeader.length === 0)
95
+ return undefined;
96
+ const cookies = parseCookies(cookieHeader);
97
+ return cookies[config.cookieName];
98
+ },
99
+ getIdFromRequest(req) {
100
+ const cookieHeader = req.getHeader('cookie');
101
+ if (typeof cookieHeader === 'string') {
102
+ const fromCookie = this.getIdFromCookieHeader(cookieHeader);
103
+ if (fromCookie !== undefined)
104
+ return fromCookie;
105
+ }
106
+ const fromHeader = req.getHeader(config.headerName);
107
+ if (typeof fromHeader === 'string' && fromHeader.length > 0)
108
+ return fromHeader;
109
+ if (typeof req.sessionId === 'string' && req.sessionId.length > 0)
110
+ return req.sessionId;
111
+ const fromContext = req.context?.['sessionId'];
112
+ if (typeof fromContext === 'string' && fromContext.length > 0)
113
+ return fromContext;
114
+ return undefined;
115
+ },
116
+ async ensureSessionId(req, res) {
117
+ const existing = this.getIdFromRequest(req);
118
+ const sessionId = existing ??
119
+ (await Promise.resolve(generateSecureJobId('SessionManager: secure crypto API not available to generate a session id')));
120
+ req.context['sessionId'] = sessionId;
121
+ // If the cookie is missing, set it.
122
+ const cookieHeader = req.getHeader('cookie');
123
+ const fromCookie = typeof cookieHeader === 'string' ? this.getIdFromCookieHeader(cookieHeader) : undefined;
124
+ if (fromCookie === undefined) {
125
+ appendSetCookie(res, buildSessionCookie(config.cookieName, sessionId));
126
+ }
127
+ return sessionId;
128
+ },
129
+ get(sessionId) {
130
+ return createSessionApi(sessions, sessionId, config.ttlMs);
131
+ },
132
+ destroy(sessionId) {
133
+ sessions.delete(sessionId);
134
+ },
135
+ cleanup() {
136
+ const now = Date.now();
137
+ let removed = 0;
138
+ for (const [id, stored] of sessions.entries()) {
139
+ if (stored.expiresAt <= now) {
140
+ sessions.delete(id);
141
+ removed++;
142
+ }
143
+ }
144
+ return removed;
145
+ },
146
+ };
147
+ },
148
+ });
149
+ export default SessionManager;
@@ -0,0 +1,3 @@
1
+ export { SessionManager } from './SessionManager';
2
+ export type { ISession, ISessionManager, SessionData, SessionManagerOptions } from './SessionManager';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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, Logger } from '@zintrust/core';
3
+ import { generateSecureJobId } from '@common/uuid';
4
+ import { Logger } from '@config/logger';
4
5
 
5
6
  export interface QueueJob {
6
7
  id: string;
@@ -18,8 +19,8 @@ export const Queue = Object.freeze({
18
19
  /**
19
20
  * Add a job to the queue
20
21
  */
21
- async add<T>(data: T): Promise<string> {
22
- const id = await generateSecureJobId();
22
+ add<T>(data: T): string {
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
- * Re-exports core FileLogWriter for optional use
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 '';
@@ -4,7 +4,7 @@
4
4
  * Supports: AWS Secrets Manager, Parameter Store, Cloudflare KV, Deno env
5
5
  */
6
6
 
7
- import { Logger } from '@zintrust/core';
7
+ import { Logger } from './logger';
8
8
  import type {
9
9
  GetSecretOptions,
10
10
  SecretConfig,
@@ -5,7 +5,7 @@
5
5
  * Driver selection must be dynamic (tests may mutate process.env).
6
6
  */
7
7
 
8
- import { Env } from '@config/env';
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 '@config/type';
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 '@config/env';
8
- import { CacheConfigInput, CacheDriverConfig } from '@config/type';
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 '@config/env';
8
- import { DatabaseConfigShape, DatabaseConnectionConfig, DatabaseConnections } from '@config/type';
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 => {
@@ -64,6 +64,8 @@ export const Env = Object.freeze({
64
64
  HOST: get('HOST', 'localhost'),
65
65
  APP_NAME: get('APP_NAME', 'ZinTrust'),
66
66
  APP_KEY: get('APP_KEY', ''),
67
+ // Optional key rotation support (comma-separated or JSON array of keys)
68
+ APP_PREVIOUS_KEYS: get('APP_PREVIOUS_KEYS', ''),
67
69
 
68
70
  // Database
69
71
  DB_CONNECTION: get('DB_CONNECTION', 'sqlite'),
@@ -124,6 +126,9 @@ export const Env = Object.freeze({
124
126
  TOKEN_TTL: getInt('TOKEN_TTL', 3600000),
125
127
  TOKEN_LENGTH: getInt('TOKEN_LENGTH', 32),
126
128
 
129
+ // Encryption interop
130
+ ENCRYPTION_CIPHER: get('ENCRYPTION_CIPHER', ''),
131
+
127
132
  // Deployment
128
133
  ENVIRONMENT: get('ENVIRONMENT', 'development'),
129
134
  REQUEST_TIMEOUT: getInt('REQUEST_TIMEOUT', 30000),
@@ -1,5 +1,5 @@
1
- import { Env } from '@zintrust/core';
2
- import { Logger } from '@zintrust/core';
1
+ import { Env } from './env';
2
+ import { Logger } from './logger';
3
3
 
4
4
  /**
5
5
  * Feature Flags State
@@ -3,8 +3,6 @@
3
3
  * Sealed namespace pattern - all exports through Logger namespace
4
4
  * Replaces console.* calls throughout the codebase
5
5
  */
6
- import { Logger } from '@zintrust/core';
7
-
8
6
  import { appConfig } from './app';
9
7
  import { Env } from './env';
10
8
 
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { delay } from '@zintrust/core';
13
- import { Env } from '@zintrust/core';
13
+ import { Env } from '../env';
14
14
  import { ErrorFactory } from '@zintrust/core';
15
15
  import { HttpClient } from '@zintrust/core';
16
16
 
@@ -9,7 +9,7 @@
9
9
  * - SLACK_LOG_BATCH_WINDOW_MS (default: 5000)
10
10
  */
11
11
 
12
- import { Env } from '@zintrust/core';
12
+ import { Env } from '../env';
13
13
  import { ErrorFactory } from '@zintrust/core';
14
14
  import { HttpClient } from '@zintrust/core';
15
15
 
@@ -4,8 +4,8 @@
4
4
  * Sealed namespace for immutability
5
5
  */
6
6
 
7
- import { Env } from '@config/env';
8
- import type { MailConfigInput, MailDriverConfig } from '@config/type';
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 => {
@@ -4,7 +4,7 @@
4
4
  * Sealed namespace for immutability
5
5
  */
6
6
 
7
- import { Env } from '@zintrust/core';
7
+ import { Env } from './env';
8
8
 
9
9
  const microservicesConfigObj = {
10
10
  /**
@@ -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 '@config/env';
8
+ import { Env } from './env';
9
9
  import type {
10
10
  KnownNotificationDriverConfig,
11
11
  NotificationConfigInput,
12
12
  NotificationDrivers,
13
13
  NotificationProviders,
14
- } from '@config/type';
14
+ } from './type';
15
15
  import { ErrorFactory } from '@zintrust/core';
16
16
 
17
17
  const normalizeName = (value: string): string => value.trim().toLowerCase();
@@ -9,17 +9,16 @@
9
9
  * Security keys can be configured per domain:
10
10
  * - APP_KEY: Default encryption key for all operations (auto-generated)
11
11
  * - API_KEY_SECRET: Optional API key authentication (if API_KEY_ENABLED=true)
12
- * - ENCRYPTION_KEY: Optional separate encryption key (overrides APP_KEY if set)
12
+ * - ENCRYPTION_CIPHER: Cipher for encrypted envelope interoperability
13
13
  * - JWT_SECRET: JWT token signing key
14
14
  *
15
15
  * Developers can use a single APP_KEY or configure separate keys for different
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
  /**
@@ -77,6 +76,16 @@ const securityConfigObj = {
77
76
  * Encryption
78
77
  */
79
78
  encryption: {
79
+ // Required for framework-compatible encrypted payloads.
80
+ // Supported values: aes-256-cbc | aes-256-gcm (case-insensitive)
81
+ cipher: Env.get('ENCRYPTION_CIPHER', ''),
82
+
83
+ // Primary key used for encryption interoperability (framework-compatible envelopes).
84
+ // APP_KEY supports both `base64:...` and raw base64.
85
+ appKey: Env.get('APP_KEY', ''),
86
+ appPreviousKeys: Env.get('APP_PREVIOUS_KEYS', ''),
87
+
88
+ // Back-compat fields (not used by EncryptedEnvelope)
80
89
  algorithm: Env.get('ENCRYPTION_ALGORITHM', 'aes-256-cbc'),
81
90
  key: Env.get('ENCRYPTION_KEY', 'your-encryption-key'),
82
91
  },
@@ -4,8 +4,8 @@
4
4
  * Sealed namespace for immutability
5
5
  */
6
6
 
7
- import { Env } from '@config/env';
8
- import type { StorageConfigRuntime, StorageDriverConfig, StorageDrivers } from '@config/type';
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 => {
@@ -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) => {
@@ -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
  }),
@@ -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;