@zintrust/core 0.4.68 → 0.4.70

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.4.68",
3
+ "version": "0.4.70",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -9,6 +9,7 @@ import { appConfig } from '../config/app.js';
9
9
  import { Cloudflare } from '../config/cloudflare.js';
10
10
  import { Env } from '../config/env.js';
11
11
  import { Logger } from '../config/logger.js';
12
+ import { shutdownRedisConnections } from '../config/workers.js';
12
13
  import { ErrorFactory } from '../exceptions/ZintrustError.js';
13
14
  import { ProjectRuntime } from '../runtime/ProjectRuntime.js';
14
15
  import { WorkerProjectAutoImports } from '../runtime/WorkerProjectAutoImports.js';
@@ -152,6 +153,13 @@ const gracefulShutdown = async (signal) => {
152
153
  Logger.warn('App shutdown failed or timed out, forcing exit', error);
153
154
  }
154
155
  }
156
+ try {
157
+ const redisBudgetMs = Math.max(250, Math.min(3000, remainingMs()));
158
+ await withTimeout(shutdownRedisConnections(), redisBudgetMs, 'Redis connection shutdown timed out');
159
+ }
160
+ catch (error) {
161
+ Logger.warn('Redis connection shutdown failed (continuing with app shutdown)', error);
162
+ }
155
163
  })(), shutdownBudgetMs, 'Graceful shutdown timed out');
156
164
  globalThis.clearTimeout(forceExitTimer);
157
165
  process.exit(0);
@@ -284,6 +292,9 @@ const BootstrapFunctions = Object.freeze({
284
292
  process.on('SIGINT', async () => {
285
293
  await gracefulShutdown('SIGINT');
286
294
  });
295
+ process.on('SIGUSR2', async () => {
296
+ await gracefulShutdown('SIGUSR2');
297
+ });
287
298
  },
288
299
  });
289
300
  // Run bootstrap
@@ -215,7 +215,7 @@ const warnIfAdapterMissing = (cmd, conn) => {
215
215
  if (conn.driver === 'mysql' && DatabaseAdapterRegistry.get('mysql') === undefined) {
216
216
  cmd.warn('MySQL adapter is not installed/registered; migrations may not hit a real MySQL DB.');
217
217
  cmd.warn('Install via `zin plugin install adapter:mysql` (or `zin add db:mysql`).');
218
- cmd.debug('[debug] Expected a side-effect import in src/zintrust.plugins.ts like: import "../../../packages/db-mysql/src/register";');
218
+ cmd.debug('[debug] Expected a side-effect import in src/zintrust.plugins.ts like: import "@zintrust/db-mysql/register";');
219
219
  }
220
220
  if (conn.driver === 'postgresql' && DatabaseAdapterRegistry.get('postgresql') === undefined) {
221
221
  cmd.warn('PostgreSQL adapter is not installed/registered; migrations may not hit a real PostgreSQL DB.');
@@ -7,6 +7,7 @@ import { type RedisTransportOptions } from '../tools/redis/RedisTransport';
7
7
  import type { RedisConfig, WorkersGlobalConfig } from './type';
8
8
  import type IORedis from 'ioredis';
9
9
  export declare const createRedisConnection: (config: RedisConfig, maxRetries?: number, options?: RedisTransportOptions) => IORedis;
10
+ export declare const shutdownRedisConnections: () => Promise<void>;
10
11
  declare const createWorkersConfig: () => WorkersGlobalConfig;
11
12
  export type WorkersConfig = ReturnType<typeof createWorkersConfig>;
12
13
  export declare const workersConfig: WorkersConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"workers.d.ts","sourceRoot":"","sources":["../../../src/config/workers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAItC,OAAO,KAAK,EACV,WAAW,EAIX,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAoMnC,eAAO,MAAM,qBAAqB,GAChC,QAAQ,WAAW,EACnB,mBAAc,EACd,UAAU,qBAAqB,KAC9B,OA6BF,CAAC;AAgFF,QAAA,MAAM,mBAAmB,QAAO,mBA2D/B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAqBnE,eAAO,MAAM,aAAa,EAAE,aAY1B,CAAC"}
1
+ {"version":3,"file":"workers.d.ts","sourceRoot":"","sources":["../../../src/config/workers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAItC,OAAO,KAAK,EACV,WAAW,EAIX,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAiTnC,eAAO,MAAM,qBAAqB,GAChC,QAAQ,WAAW,EACnB,mBAAc,EACd,UAAU,qBAAqB,KAC9B,OA6CF,CAAC;AAEF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CA4B7D,CAAC;AAgFF,QAAA,MAAM,mBAAmB,QAAO,mBA2D/B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAqBnE,eAAO,MAAM,aAAa,EAAE,aAY1B,CAAC"}
@@ -12,6 +12,78 @@ import { createRequire } from '../node-singletons/module.js';
12
12
  import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
13
13
  let redisModule;
14
14
  let warnedRedisProxyMismatch = false;
15
+ const getRedisConnectionRegistry = () => {
16
+ const registryGlobal = globalThis;
17
+ registryGlobal.__zintrustRedisConnectionRegistry__ ??= {
18
+ activeConnections: new Set(),
19
+ cachedConnections: new Map(),
20
+ };
21
+ return registryGlobal.__zintrustRedisConnectionRegistry__;
22
+ };
23
+ const unregisterRedisConnection = (client) => {
24
+ const registry = getRedisConnectionRegistry();
25
+ registry.activeConnections.delete(client);
26
+ const registryKey = client.__zintrustRedisRegistryKey__;
27
+ if (registryKey !== undefined && registry.cachedConnections.get(registryKey) === client) {
28
+ registry.cachedConnections.delete(registryKey);
29
+ }
30
+ };
31
+ const hasReusableRedisStatus = (client) => {
32
+ return client.status !== 'end' && client.status !== 'close';
33
+ };
34
+ const shouldCacheRedisConnection = (isWorkersRuntime, options) => {
35
+ const subsystem = options?.subsystem?.trim() ?? '';
36
+ if (subsystem === '')
37
+ return false;
38
+ if (isWorkersRuntime)
39
+ return false;
40
+ return Env.NODE_ENV !== 'production';
41
+ };
42
+ const createRedisConnectionCacheKey = (config, maxRetries, options) => {
43
+ return JSON.stringify({
44
+ subsystem: options?.subsystem?.trim() ?? 'redis',
45
+ host: config.host,
46
+ port: config.port,
47
+ db: config.db,
48
+ password: config.password ?? '',
49
+ maxRetries,
50
+ });
51
+ };
52
+ const trackRedisConnection = (client, registryKey) => {
53
+ const managedClient = client;
54
+ const registry = getRedisConnectionRegistry();
55
+ registry.activeConnections.add(managedClient);
56
+ if (registryKey !== undefined) {
57
+ managedClient.__zintrustRedisRegistryKey__ = registryKey;
58
+ registry.cachedConnections.set(registryKey, managedClient);
59
+ }
60
+ if (managedClient.__zintrustRedisLifecycleManaged__ === true) {
61
+ return managedClient;
62
+ }
63
+ managedClient.__zintrustRedisLifecycleManaged__ = true;
64
+ const originalQuit = typeof managedClient.quit === 'function' ? managedClient.quit.bind(managedClient) : undefined;
65
+ if (originalQuit) {
66
+ managedClient.quit = async (...args) => {
67
+ unregisterRedisConnection(managedClient);
68
+ return originalQuit(...args);
69
+ };
70
+ }
71
+ const originalDisconnect = typeof managedClient.disconnect === 'function'
72
+ ? managedClient.disconnect.bind(managedClient)
73
+ : undefined;
74
+ if (originalDisconnect) {
75
+ managedClient.disconnect = (...args) => {
76
+ unregisterRedisConnection(managedClient);
77
+ return originalDisconnect(...args);
78
+ };
79
+ }
80
+ const clearConnection = () => {
81
+ unregisterRedisConnection(managedClient);
82
+ };
83
+ managedClient.on?.('end', clearConnection);
84
+ managedClient.on?.('close', clearConnection);
85
+ return managedClient;
86
+ };
15
87
  const parseHttpProxyEndpoint = (proxyUrl) => {
16
88
  try {
17
89
  const url = new URL(proxyUrl);
@@ -159,6 +231,19 @@ export const createRedisConnection = (config, maxRetries = 3, options) => {
159
231
  const proxySettings = getProxySettings();
160
232
  const effectiveConfig = resolveEffectiveRedisConfig(config, isWorkersRuntime, proxySettings);
161
233
  validateRedisConfig(config, effectiveConfig, isWorkersRuntime, proxySettings);
234
+ const shouldCache = shouldCacheRedisConnection(isWorkersRuntime, options);
235
+ const cacheKey = shouldCache
236
+ ? createRedisConnectionCacheKey(effectiveConfig, maxRetries, options)
237
+ : undefined;
238
+ if (cacheKey !== undefined) {
239
+ const cachedClient = getRedisConnectionRegistry().cachedConnections.get(cacheKey);
240
+ if (cachedClient && hasReusableRedisStatus(cachedClient)) {
241
+ return cachedClient;
242
+ }
243
+ if (cachedClient) {
244
+ unregisterRedisConnection(cachedClient);
245
+ }
246
+ }
162
247
  const RedisCtor = resolveRedisConstructor();
163
248
  const client = new RedisCtor({
164
249
  host: effectiveConfig.host,
@@ -173,7 +258,32 @@ export const createRedisConnection = (config, maxRetries = 3, options) => {
173
258
  },
174
259
  });
175
260
  setupRedisErrorHandler(client);
176
- return client;
261
+ return trackRedisConnection(client, cacheKey);
262
+ };
263
+ export const shutdownRedisConnections = async () => {
264
+ const registry = getRedisConnectionRegistry();
265
+ const trackedConnections = Array.from(registry.activeConnections);
266
+ if (trackedConnections.length === 0)
267
+ return;
268
+ registry.activeConnections.clear();
269
+ registry.cachedConnections.clear();
270
+ Logger.info('Shutting down tracked Redis connections', {
271
+ count: trackedConnections.length,
272
+ });
273
+ await Promise.allSettled(trackedConnections.map(async (client) => {
274
+ try {
275
+ await client.quit();
276
+ }
277
+ catch (error) {
278
+ Logger.warn('Tracked Redis graceful shutdown failed, forcing disconnect', error);
279
+ try {
280
+ client.disconnect();
281
+ }
282
+ catch (disconnectError) {
283
+ Logger.error('Tracked Redis forced disconnect failed', disconnectError);
284
+ }
285
+ }
286
+ }));
177
287
  };
178
288
  const createIntervalConfig = () => Env.SSE_SNAPSHOT_INTERVAL;
179
289
  /**
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.68
2
+ * @zintrust/core v0.4.70
3
3
  *
4
4
  * ZinTrust Framework - Production-Grade TypeScript Backend
5
5
  * Built for performance, type safety, and exceptional developer experience
6
6
  *
7
7
  * Build Information:
8
- * Built: 2026-04-06T11:26:22.690Z
8
+ * Built: 2026-04-06T16:03:35.272Z
9
9
  * Node: >=20.0.0
10
10
  * License: MIT
11
11
  *
@@ -21,7 +21,7 @@
21
21
  * Available at runtime for debugging and health checks
22
22
  */
23
23
  export const ZINTRUST_VERSION = '0.1.41';
24
- export const ZINTRUST_BUILD_DATE = '2026-04-06T11:26:22.656Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-04-06T16:03:35.239Z'; // Replaced during build
25
25
  export { Application } from './boot/Application.js';
26
26
  export { AwsSigV4 } from './common/index.js';
27
27
  export { SignedRequest } from './security/SignedRequest.js';
@@ -1,12 +1,8 @@
1
1
  /**
2
- * ZinTrust plugin auto-imports
3
- *
4
- * In real projects, this file is managed by `zin plugin install` and contains
5
- * side-effect imports (e.g. `@zintrust/db-sqlite/register`) that register
6
- * optional adapters/drivers into core registries.
7
- *
2
+ * Auto-generated fallback module.
3
+ * This file is created by scripts/ensure-worker-plugins.mjs when missing.
4
+ * It allows optional runtime plugin imports to resolve in CI/scaffolded setups.
8
5
  */
9
- export type {};
10
6
  export declare const __zintrustGeneratedPluginStub = "zintrust.plugins.ts";
11
7
  declare const _default: {};
12
8
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,YAAY,EAAE,CAAC;AAgBf,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;;AACnE,wBAAkB"}
1
+ {"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;;AACnE,wBAAkB"}
@@ -1,13 +1,7 @@
1
1
  /**
2
- * ZinTrust plugin auto-imports
3
- *
4
- * In real projects, this file is managed by `zin plugin install` and contains
5
- * side-effect imports (e.g. `@zintrust/db-sqlite/register`) that register
6
- * optional adapters/drivers into core registries.
7
- *
2
+ * Auto-generated fallback module.
3
+ * This file is created by scripts/ensure-worker-plugins.mjs when missing.
4
+ * It allows optional runtime plugin imports to resolve in CI/scaffolded setups.
8
5
  */
9
- import * as SystemDebuggerRuntime from './runtime/plugins/system-debugger-runtime.js';
10
- globalThis.__zintrust_system_debugger_plugin_requested__ = true;
11
- globalThis.__zintrust_system_debugger_runtime__ = SystemDebuggerRuntime;
12
6
  export const __zintrustGeneratedPluginStub = 'zintrust.plugins.ts';
13
7
  export default {};
@@ -3,7 +3,6 @@
3
3
  * This file is created by scripts/ensure-worker-plugins.mjs when missing.
4
4
  * It allows optional runtime plugin imports to resolve in CI/scaffolded setups.
5
5
  */
6
- export type {};
7
6
  export declare const __zintrustGeneratedPluginStub = "zintrust.plugins.wg.ts";
8
7
  declare const _default: {};
9
8
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"zintrust.plugins.wg.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.wg.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,YAAY,EAAE,CAAC;AAgBf,eAAO,MAAM,6BAA6B,2BAA2B,CAAC;;AACtE,wBAAkB"}
1
+ {"version":3,"file":"zintrust.plugins.wg.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.wg.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,6BAA6B,2BAA2B,CAAC;;AACtE,wBAAkB"}
@@ -3,8 +3,5 @@
3
3
  * This file is created by scripts/ensure-worker-plugins.mjs when missing.
4
4
  * It allows optional runtime plugin imports to resolve in CI/scaffolded setups.
5
5
  */
6
- import * as SystemDebuggerRuntime from './runtime/plugins/system-debugger-runtime.js';
7
- globalThis.__zintrust_system_debugger_plugin_requested__ = true;
8
- globalThis.__zintrust_system_debugger_runtime__ = SystemDebuggerRuntime;
9
6
  export const __zintrustGeneratedPluginStub = 'zintrust.plugins.wg.ts';
10
7
  export default {};
@@ -1,11 +0,0 @@
1
- import '../packages/db-d1/src/register.js';
2
- import '../packages/db-mysql/src/register.js';
3
- import '../packages/db-postgres/src/register.js';
4
- import '../packages/db-sqlite/src/register.js';
5
- import '../packages/db-sqlserver/src/register.js';
6
- import '../packages/mail-sendgrid/src/register.js';
7
- import '../packages/mail-smtp/src/register.js';
8
- import '@zintrust/queue-monitor';
9
- import '../packages/queue-redis/src/register.js';
10
- import '../packages/workers/src/register.js';
11
- //# sourceMappingURL=zintrust.comon.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"zintrust.comon.d.ts","sourceRoot":"","sources":["../../src/zintrust.comon.ts"],"names":[],"mappings":"AAQA,OAAO,mCAAmC,CAAC;AAC3C,OAAO,sCAAsC,CAAC;AAC9C,OAAO,yCAAyC,CAAC;AACjD,OAAO,uCAAuC,CAAC;AAC/C,OAAO,0CAA0C,CAAC;AAClD,OAAO,2CAA2C,CAAC;AACnD,OAAO,uCAAuC,CAAC;AAC/C,OAAO,wCAAwC,CAAC;AAChD,OAAO,yCAAyC,CAAC;AACjD,OAAO,qCAAqC,CAAC"}
@@ -1,17 +0,0 @@
1
- /* eslint-disable no-restricted-imports */
2
- // /**
3
- // * ZinTrust comon plugin auto-imports
4
- // *
5
- // * This file is managed by `zin plugin install` and contains side-effect
6
- // * imports that register optional adapters/drivers into core registries.
7
- // */
8
- import '../packages/db-d1/src/register.js';
9
- import '../packages/db-mysql/src/register.js';
10
- import '../packages/db-postgres/src/register.js';
11
- import '../packages/db-sqlite/src/register.js';
12
- import '../packages/db-sqlserver/src/register.js';
13
- import '../packages/mail-sendgrid/src/register.js';
14
- import '../packages/mail-smtp/src/register.js';
15
- import '@zintrust/queue-monitor';
16
- import '../packages/queue-redis/src/register.js';
17
- import '../packages/workers/src/register.js';