@zintrust/core 0.1.8 → 0.1.9

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 (88) hide show
  1. package/README.md +17 -0
  2. package/package.json +1 -2
  3. package/src/cache/Cache.d.ts.map +1 -1
  4. package/src/cache/Cache.js +8 -2
  5. package/src/cache/CacheDriverRegistry.d.ts +15 -0
  6. package/src/cache/CacheDriverRegistry.d.ts.map +1 -0
  7. package/src/cache/CacheDriverRegistry.js +19 -0
  8. package/src/cli/commands/AddCommand.d.ts.map +1 -1
  9. package/src/cli/commands/AddCommand.js +20 -1
  10. package/src/cli/scaffolding/ControllerGenerator.js +2 -2
  11. package/src/cli/scaffolding/FeatureScaffolder.js +5 -5
  12. package/src/cli/scaffolding/RouteGenerator.js +1 -1
  13. package/src/cli/scaffolding/SeederGenerator.js +1 -1
  14. package/src/cli/scaffolding/ServiceIntegrationTestGenerator.js +1 -1
  15. package/src/cli/scaffolding/ServiceScaffolder.js +4 -4
  16. package/src/common/uuid.d.ts +1 -1
  17. package/src/common/uuid.d.ts.map +1 -1
  18. package/src/common/uuid.js +3 -3
  19. package/src/config/cache.js +1 -1
  20. package/src/config/mail.d.ts +8 -0
  21. package/src/config/mail.d.ts.map +1 -1
  22. package/src/config/mail.js +17 -0
  23. package/src/config/type.d.ts +11 -2
  24. package/src/config/type.d.ts.map +1 -1
  25. package/src/index.d.ts +4 -0
  26. package/src/index.d.ts.map +1 -1
  27. package/src/index.js +7 -0
  28. package/src/microservices/MicroserviceGenerator.js +7 -7
  29. package/src/microservices/PostgresAdapter.d.ts.map +1 -1
  30. package/src/microservices/PostgresAdapter.js +18 -0
  31. package/src/orm/Database.d.ts.map +1 -1
  32. package/src/orm/Database.js +5 -0
  33. package/src/orm/DatabaseAdapterRegistry.d.ts +14 -0
  34. package/src/orm/DatabaseAdapterRegistry.d.ts.map +1 -0
  35. package/src/orm/DatabaseAdapterRegistry.js +19 -0
  36. package/src/orm/adapters/SQLiteAdapter.d.ts.map +1 -1
  37. package/src/orm/adapters/SQLiteAdapter.js +33 -2
  38. package/src/runtime/PluginManager.d.ts.map +1 -1
  39. package/src/runtime/PluginManager.js +68 -0
  40. package/src/runtime/PluginRegistry.d.ts +6 -1
  41. package/src/runtime/PluginRegistry.d.ts.map +1 -1
  42. package/src/runtime/PluginRegistry.js +56 -34
  43. package/src/scripts/TemplateImportsCheck.d.ts +2 -0
  44. package/src/scripts/TemplateImportsCheck.d.ts.map +1 -0
  45. package/src/scripts/TemplateImportsCheck.js +111 -0
  46. package/src/templates/adapters/MySQLAdapter.ts.tpl +9 -5
  47. package/src/templates/adapters/PostgreSQLAdapter.ts.tpl +9 -5
  48. package/src/templates/adapters/SQLServerAdapter.ts.tpl +9 -5
  49. package/src/templates/adapters/SQLiteAdapter.ts.tpl +67 -18
  50. package/src/templates/features/Queue.ts.tpl +2 -3
  51. package/src/templates/project/basic/README.md.tpl +4 -0
  52. package/src/templates/project/basic/app/Controllers/UserController.ts.tpl +1 -1
  53. package/src/templates/project/basic/app/Models/Post.ts.tpl +1 -1
  54. package/src/templates/project/basic/app/Models/User.ts.tpl +1 -1
  55. package/src/templates/project/basic/config/FileLogWriter.ts.tpl +4 -3
  56. package/src/templates/project/basic/config/SecretsManager.ts.tpl +2 -2
  57. package/src/templates/project/basic/config/StartupConfigValidator.ts.tpl +2 -2
  58. package/src/templates/project/basic/config/app.ts.tpl +2 -2
  59. package/src/templates/project/basic/config/broadcast.ts.tpl +2 -2
  60. package/src/templates/project/basic/config/cache.ts.tpl +3 -3
  61. package/src/templates/project/basic/config/cloudflare.ts.tpl +1 -1
  62. package/src/templates/project/basic/config/database.ts.tpl +2 -2
  63. package/src/templates/project/basic/config/env.ts.tpl +1 -1
  64. package/src/templates/project/basic/config/features.ts.tpl +1 -1
  65. package/src/templates/project/basic/config/index.ts.tpl +16 -16
  66. package/src/templates/project/basic/config/logger.ts.tpl +9 -7
  67. package/src/templates/project/basic/config/logging/KvLogger.ts.tpl +2 -2
  68. package/src/templates/project/basic/config/mail.ts.tpl +17 -2
  69. package/src/templates/project/basic/config/middleware.ts.tpl +10 -7
  70. package/src/templates/project/basic/config/notification.ts.tpl +2 -2
  71. package/src/templates/project/basic/config/queue.ts.tpl +2 -2
  72. package/src/templates/project/basic/config/security.ts.tpl +4 -3
  73. package/src/templates/project/basic/config/startup.ts.tpl +1 -1
  74. package/src/templates/project/basic/config/storage.ts.tpl +2 -2
  75. package/src/templates/project/basic/config/type.ts.tpl +17 -5
  76. package/src/templates/project/basic/routes/api.ts.tpl +6 -6
  77. package/src/templates/project/basic/routes/health.ts.tpl +11 -7
  78. package/src/templates/project/basic/routes/storage.ts.tpl +8 -5
  79. package/src/templates/project/basic/src/index.ts.tpl +2 -0
  80. package/src/templates/project/basic/src/zintrust.plugins.ts.tpl +8 -0
  81. package/src/tools/broadcast/drivers/Redis.js +1 -1
  82. package/src/tools/mail/Mail.d.ts +1 -1
  83. package/src/tools/mail/Mail.d.ts.map +1 -1
  84. package/src/tools/mail/Mail.js +11 -0
  85. package/src/tools/mail/MailDriverRegistry.d.ts +16 -0
  86. package/src/tools/mail/MailDriverRegistry.d.ts.map +1 -0
  87. package/src/tools/mail/MailDriverRegistry.js +19 -0
  88. package/src/tools/queue/drivers/Redis.js +1 -1
@@ -119,6 +119,21 @@ async function runDisconnectAll(pools) {
119
119
  pools.clear();
120
120
  Logger.info('🔌 All PostgreSQL pools disconnected');
121
121
  }
122
+ function isMissingEsmPackage(error, packageName) {
123
+ if (typeof error !== 'object' || error === null)
124
+ return false;
125
+ const maybe = error;
126
+ const code = typeof maybe.code === 'string' ? maybe.code : '';
127
+ const message = typeof maybe.message === 'string' ? maybe.message : '';
128
+ // Some runners/wrappers preserve `code` but sanitize/omit the message.
129
+ if (code === 'ERR_MODULE_NOT_FOUND' && message.length === 0)
130
+ return true;
131
+ if (code === 'ERR_MODULE_NOT_FOUND' && message.includes(`'${packageName}'`))
132
+ return true;
133
+ if (message.includes(`Cannot find package '${packageName}'`))
134
+ return true;
135
+ return false;
136
+ }
122
137
  /**
123
138
  * Internal function to connect to PostgreSQL
124
139
  */
@@ -152,6 +167,9 @@ async function runConnect(adapterConfig, pools, getPoolKey) {
152
167
  Logger.info(`🐘 PostgreSQL pool initialized: ${poolKey}`);
153
168
  }
154
169
  catch (error) {
170
+ if (isMissingEsmPackage(error, 'pg')) {
171
+ throw ErrorFactory.createConfigError("PostgreSQL pool requires the 'pg' package (run `zin add db:postgres` or `zin plugin install db:postgres`).");
172
+ }
155
173
  throw ErrorFactory.createTryCatchError(`Failed to initialize PostgreSQL pool: ${error.message}`, error);
156
174
  }
157
175
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAgB,MAAM,mBAAmB,CAAC;AAEhE,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,MAAM,CAAC;IAClB,SAAS,IAAI,cAAc,CAAC;CAC7B;AA6ED,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAwE1C,CAAC;AAIH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAStF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAUnD"}
1
+ {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,aAAa,EAAgB,MAAM,mBAAmB,CAAC;AAEhE,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,MAAM,CAAC;IAClB,SAAS,IAAI,cAAc,CAAC;CAC7B;AAkFD,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAwE1C,CAAC;AAIH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAStF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAUnD"}
@@ -9,6 +9,7 @@ import { MySQLAdapter } from './adapters/MySQLAdapter.js';
9
9
  import { PostgreSQLAdapter } from './adapters/PostgreSQLAdapter.js';
10
10
  import { SQLiteAdapter } from './adapters/SQLiteAdapter.js';
11
11
  import { SQLServerAdapter } from './adapters/SQLServerAdapter.js';
12
+ import { DatabaseAdapterRegistry } from './DatabaseAdapterRegistry.js';
12
13
  import { QueryBuilder } from './QueryBuilder.js';
13
14
  /**
14
15
  * Database Manager
@@ -18,6 +19,10 @@ import { QueryBuilder } from './QueryBuilder.js';
18
19
  * Create appropriate adapter based on driver
19
20
  */
20
21
  const createAdapter = (cfg) => {
22
+ const registered = DatabaseAdapterRegistry.get(cfg.driver);
23
+ if (registered !== undefined) {
24
+ return registered(cfg);
25
+ }
21
26
  switch (cfg.driver) {
22
27
  case 'postgresql':
23
28
  return PostgreSQLAdapter.create(cfg);
@@ -0,0 +1,14 @@
1
+ import type { DatabaseConfig, IDatabaseAdapter } from './DatabaseAdapter';
2
+ export type AdapterFactory = (config: DatabaseConfig) => IDatabaseAdapter;
3
+ declare function register(driver: DatabaseConfig['driver'], factory: AdapterFactory): void;
4
+ declare function get(driver: DatabaseConfig['driver']): AdapterFactory | undefined;
5
+ declare function has(driver: DatabaseConfig['driver']): boolean;
6
+ declare function list(): Array<DatabaseConfig['driver']>;
7
+ export declare const DatabaseAdapterRegistry: Readonly<{
8
+ register: typeof register;
9
+ get: typeof get;
10
+ has: typeof has;
11
+ list: typeof list;
12
+ }>;
13
+ export {};
14
+ //# sourceMappingURL=DatabaseAdapterRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatabaseAdapterRegistry.d.ts","sourceRoot":"","sources":["../../../src/orm/DatabaseAdapterRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7E,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,gBAAgB,CAAC;AAI1E,iBAAS,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAEjF;AAED,iBAAS,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,cAAc,GAAG,SAAS,CAEzE;AAED,iBAAS,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,OAAO,CAEtD;AAED,iBAAS,IAAI,IAAI,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAE/C;AAED,eAAO,MAAM,uBAAuB;;;;;EAKlC,CAAC"}
@@ -0,0 +1,19 @@
1
+ const registry = new Map();
2
+ function register(driver, factory) {
3
+ registry.set(driver, factory);
4
+ }
5
+ function get(driver) {
6
+ return registry.get(driver);
7
+ }
8
+ function has(driver) {
9
+ return registry.has(driver);
10
+ }
11
+ function list() {
12
+ return Array.from(registry.keys());
13
+ }
14
+ export const DatabaseAdapterRegistry = Object.freeze({
15
+ register,
16
+ get,
17
+ has,
18
+ list,
19
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"SQLiteAdapter.d.ts","sourceRoot":"","sources":["../../../../src/orm/adapters/SQLiteAdapter.ts"],"names":[],"mappings":"AACA;;;GAGG;AAMH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAe,MAAM,sBAAsB,CAAC;AA6HrF,iBAAS,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,gBAAgB,CAoDrE;AAED,eAAO,MAAM,aAAa;;EAExB,CAAC"}
1
+ {"version":3,"file":"SQLiteAdapter.d.ts","sourceRoot":"","sources":["../../../../src/orm/adapters/SQLiteAdapter.ts"],"names":[],"mappings":"AACA;;;GAGG;AAMH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAe,MAAM,sBAAsB,CAAC;AAyKrF,iBAAS,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,gBAAgB,CAoDrE;AAED,eAAO,MAAM,aAAa;;EAExB,CAAC"}
@@ -8,7 +8,37 @@ import { Logger } from '../../config/logger.js';
8
8
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
9
9
  import { performance } from '../../node-singletons/perf-hooks.js';
10
10
  import { QueryBuilder } from '../QueryBuilder.js';
11
- import Database from 'better-sqlite3';
11
+ function isMissingEsmPackage(error, packageName) {
12
+ if (typeof error !== 'object' || error === null)
13
+ return false;
14
+ const maybe = error;
15
+ const code = typeof maybe.code === 'string' ? maybe.code : '';
16
+ const message = typeof maybe.message === 'string' ? maybe.message : '';
17
+ // Some runners/wrappers preserve `code` but sanitize/omit the message.
18
+ if (code === 'ERR_MODULE_NOT_FOUND' && message.length === 0)
19
+ return true;
20
+ if (code === 'ERR_MODULE_NOT_FOUND' && message.includes(`'${packageName}'`))
21
+ return true;
22
+ if (message.includes(`Cannot find package '${packageName}'`))
23
+ return true;
24
+ return false;
25
+ }
26
+ async function importSqliteDatabaseConstructor() {
27
+ try {
28
+ const mod = (await import('better-sqlite3'));
29
+ const ctor = mod.default;
30
+ if (typeof ctor === 'function')
31
+ return ctor;
32
+ // Some CJS packages may not present a `default` export under certain loaders.
33
+ return mod;
34
+ }
35
+ catch (error) {
36
+ if (isMissingEsmPackage(error, 'better-sqlite3')) {
37
+ throw ErrorFactory.createConfigError("SQLite adapter requires the 'better-sqlite3' package (run `zin add db:sqlite` or `zin plugin install db:sqlite`).");
38
+ }
39
+ throw ErrorFactory.createTryCatchError('Failed to load SQLite driver', { cause: error });
40
+ }
41
+ }
12
42
  function normalizeFilename(database) {
13
43
  const value = (database ?? '').trim();
14
44
  return value.length > 0 ? value : ':memory:';
@@ -44,7 +74,8 @@ async function connectSQLite(state) {
44
74
  if (state.db !== null)
45
75
  return;
46
76
  const filename = normalizeFilename(state.config.database);
47
- state.db = new Database(filename);
77
+ const SqliteDatabaseCtor = await importSqliteDatabaseConstructor();
78
+ state.db = new SqliteDatabaseCtor(filename);
48
79
  // Enable WAL mode for better concurrency
49
80
  state.db.pragma('journal_mode = WAL');
50
81
  Logger.info(`✓ SQLite connected (${filename})`);
@@ -1 +1 @@
1
- {"version":3,"file":"PluginManager.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginManager.ts"],"names":[],"mappings":"AAEA;;;GAGG;AASH,OAAO,EAAE,gBAAgB,EAAkB,MAAM,yBAAyB,CAAC;AAoN3E,eAAO,MAAM,aAAa;IACxB;;OAEG;YACK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIxC;;OAEG;yBACkB,MAAM,GAAG,MAAM,GAAG,IAAI;IAW3C;;OAEG;0BACyB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqCrD;;OAEG;sBACqB,MAAM,YAAY;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BrF;;;;OAIG;wBACuB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EA2ChD,CAAC"}
1
+ {"version":3,"file":"PluginManager.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginManager.ts"],"names":[],"mappings":"AAEA;;;GAGG;AASH,OAAO,EAAE,gBAAgB,EAAkB,MAAM,yBAAyB,CAAC;AA6Q3E,eAAO,MAAM,aAAa;IACxB;;OAEG;YACK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIxC;;OAEG;yBACkB,MAAM,GAAG,MAAM,GAAG,IAAI;IAW3C;;OAEG;0BACyB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0DrD;;OAEG;sBACqB,MAAM,YAAY;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BrF;;;;OAIG;wBACuB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EA2ChD,CAAC"}
@@ -61,6 +61,9 @@ function isStringRecord(value) {
61
61
  }
62
62
  return true;
63
63
  }
64
+ function isStringArray(value) {
65
+ return Array.isArray(value) && value.every((v) => typeof v === 'string');
66
+ }
64
67
  function parsePackageJsonDeps(text) {
65
68
  const parsed = JSON.parse(text);
66
69
  if (typeof parsed !== 'object' || parsed === null)
@@ -153,6 +156,55 @@ async function copyPluginTemplates(plugin) {
153
156
  }
154
157
  }
155
158
  }
159
+ async function ensurePluginAutoImports(plugin) {
160
+ const rawImports = plugin.autoImports;
161
+ if (!isStringArray(rawImports) || rawImports.length === 0)
162
+ return;
163
+ const imports = rawImports;
164
+ const projectRoot = resolveProjectRoot();
165
+ const pluginFile = path.join(projectRoot, 'src', 'zintrust.plugins.ts');
166
+ await fs.mkdir(path.dirname(pluginFile), { recursive: true });
167
+ const header = '/**\n' +
168
+ ' * Zintrust plugin auto-imports\n' +
169
+ ' *\n' +
170
+ ' * This file is managed by `zin plugin install` and contains side-effect\n' +
171
+ ' * imports that register optional adapters/drivers into core registries.\n' +
172
+ ' */\n\n';
173
+ let current = '';
174
+ try {
175
+ current = await fs.readFile(pluginFile, 'utf-8');
176
+ }
177
+ catch {
178
+ current = '';
179
+ }
180
+ const lines = [];
181
+ if (current.trim().length === 0) {
182
+ lines.push(header.trimEnd());
183
+ }
184
+ else {
185
+ lines.push(current.trimEnd());
186
+ }
187
+ const existing = new Set();
188
+ for (const line of lines.join('\n').split(/\r?\n/)) {
189
+ const m = new RegExp(/^\s*import\s+['"]([^'"]+)['"];\s*$/).exec(line);
190
+ if (m)
191
+ existing.add(m[1]);
192
+ }
193
+ let appended = 0;
194
+ for (const spec of imports) {
195
+ if (typeof spec !== 'string' || spec.trim() === '')
196
+ continue;
197
+ if (existing.has(spec))
198
+ continue;
199
+ lines.push(`import '${spec}';`);
200
+ existing.add(spec);
201
+ appended += 1;
202
+ }
203
+ if (appended === 0)
204
+ return;
205
+ const next = lines.join('\n') + '\n';
206
+ await fs.writeFile(pluginFile, next, 'utf-8');
207
+ }
156
208
  function runPostInstall(plugin) {
157
209
  if (!plugin.postInstall)
158
210
  return;
@@ -213,6 +265,20 @@ export const PluginManager = Object.freeze({
213
265
  }
214
266
  const plugin = PluginRegistry[resolvedId];
215
267
  const projectRoot = resolveProjectRoot();
268
+ // Dependency-only plugins (no templates): consider installed when required deps are present.
269
+ if (plugin.templates.length === 0) {
270
+ try {
271
+ const packageJsonPath = path.join(projectRoot, 'package.json');
272
+ const packageJsonText = await fs.readFile(packageJsonPath, 'utf-8');
273
+ const packageJson = parsePackageJsonDeps(packageJsonText);
274
+ const hasDeps = plugin.dependencies.every((dep) => packageJson.dependencies?.[dep] ?? packageJson.devDependencies?.[dep] ?? '');
275
+ const hasDevDeps = plugin.devDependencies.every((dep) => packageJson.devDependencies?.[dep] ?? packageJson.dependencies?.[dep] ?? '');
276
+ return hasDeps && hasDevDeps;
277
+ }
278
+ catch {
279
+ return false;
280
+ }
281
+ }
216
282
  // Check if the main template file exists in the destination
217
283
  // We assume if the first template exists, the plugin is "installed"
218
284
  if (plugin.templates.length > 0) {
@@ -252,6 +318,8 @@ export const PluginManager = Object.freeze({
252
318
  });
253
319
  // 2. Copy templates
254
320
  await copyPluginTemplates(plugin);
321
+ // 2b. Ensure plugin-side effects are imported by the project
322
+ await ensurePluginAutoImports(plugin);
255
323
  // 3. Post-Install (still executed via execSync - opt-in)
256
324
  runPostInstall(plugin);
257
325
  Logger.info(`✓ Plugin ${plugin.name} installed successfully`);
@@ -5,10 +5,15 @@
5
5
  export interface PluginDefinition {
6
6
  name: string;
7
7
  description: string;
8
- type: 'database-adapter' | 'feature';
8
+ type: 'database-adapter' | 'feature' | 'driver';
9
9
  aliases: string[];
10
10
  dependencies: string[];
11
11
  devDependencies: string[];
12
+ /**
13
+ * Optional module specifiers to auto-import inside the project.
14
+ * Used to register adapters/drivers via side-effect imports.
15
+ */
16
+ autoImports?: string[];
12
17
  templates: {
13
18
  source: string;
14
19
  destination: string;
@@ -1 +1 @@
1
- {"version":3,"file":"PluginRegistry.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginRegistry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;KACrB,EAAE,CAAC;IACJ,WAAW,CAAC,EAAE;QACZ,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAwF3D,CAAC"}
1
+ {"version":3,"file":"PluginRegistry.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginRegistry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,kBAAkB,GAAG,SAAS,GAAG,QAAQ,CAAC;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;KACrB,EAAE,CAAC;IACJ,WAAW,CAAC,EAAE;QACZ,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CA8G3D,CAAC"}
@@ -34,60 +34,82 @@ export const PluginRegistry = {
34
34
  },
35
35
  ],
36
36
  },
37
+ 'driver:queue-redis': {
38
+ name: 'Redis Queue Driver',
39
+ description: 'Redis-backed queue driver (installs redis client dependency)',
40
+ type: 'driver',
41
+ aliases: ['queue:redis'],
42
+ dependencies: ['redis'],
43
+ devDependencies: [],
44
+ templates: [],
45
+ },
46
+ 'driver:broadcast-redis': {
47
+ name: 'Redis Broadcast Driver',
48
+ description: 'Redis-backed broadcast driver (installs redis client dependency)',
49
+ type: 'driver',
50
+ aliases: ['broadcast:redis'],
51
+ dependencies: ['redis'],
52
+ devDependencies: [],
53
+ templates: [],
54
+ },
55
+ 'driver:cache-redis': {
56
+ name: 'Redis Cache Driver',
57
+ description: 'Redis-backed cache driver (installs @zintrust/cache-redis)',
58
+ type: 'driver',
59
+ aliases: ['cache:redis'],
60
+ dependencies: ['@zintrust/cache-redis'],
61
+ devDependencies: [],
62
+ autoImports: ['@zintrust/cache-redis/register'],
63
+ templates: [],
64
+ },
65
+ 'driver:mail-nodemailer': {
66
+ name: 'Nodemailer Mail Driver',
67
+ description: 'Nodemailer-based mail driver (installs @zintrust/mail-nodemailer)',
68
+ type: 'driver',
69
+ aliases: ['mail:nodemailer'],
70
+ dependencies: ['@zintrust/mail-nodemailer'],
71
+ devDependencies: [],
72
+ autoImports: ['@zintrust/mail-nodemailer/register'],
73
+ templates: [],
74
+ },
37
75
  'adapter:postgres': {
38
76
  name: 'PostgreSQL Adapter',
39
77
  description: 'Production-ready PostgreSQL database adapter using pg',
40
78
  type: 'database-adapter',
41
- aliases: ['a:postgres', 'pg'],
42
- dependencies: ['pg'],
43
- devDependencies: ['@types/pg'],
44
- templates: [
45
- {
46
- source: 'adapters/PostgreSQLAdapter.ts.tpl',
47
- destination: 'src/orm/adapters/PostgreSQLAdapter.ts',
48
- },
49
- ],
79
+ aliases: ['a:postgres', 'pg', 'db:postgres', 'postgresql', 'db:postgresql'],
80
+ dependencies: ['@zintrust/db-postgres'],
81
+ devDependencies: [],
82
+ autoImports: ['@zintrust/db-postgres/register'],
83
+ templates: [],
50
84
  },
51
85
  'adapter:mysql': {
52
86
  name: 'MySQL Adapter',
53
87
  description: 'Production-ready MySQL database adapter using mysql2',
54
88
  type: 'database-adapter',
55
- aliases: ['a:mysql', 'mysql'],
56
- dependencies: ['mysql2'],
89
+ aliases: ['a:mysql', 'mysql', 'db:mysql'],
90
+ dependencies: ['@zintrust/db-mysql'],
57
91
  devDependencies: [],
58
- templates: [
59
- {
60
- source: 'adapters/MySQLAdapter.ts.tpl',
61
- destination: 'src/orm/adapters/MySQLAdapter.ts',
62
- },
63
- ],
92
+ autoImports: ['@zintrust/db-mysql/register'],
93
+ templates: [],
64
94
  },
65
95
  'adapter:mssql': {
66
96
  name: 'SQL Server Adapter',
67
97
  description: 'Production-ready SQL Server database adapter using mssql',
68
98
  type: 'database-adapter',
69
- aliases: ['a:mssql', 'mssql'],
70
- dependencies: ['mssql'],
99
+ aliases: ['a:mssql', 'mssql', 'db:mssql'],
100
+ dependencies: ['@zintrust/db-sqlserver'],
71
101
  devDependencies: [],
72
- templates: [
73
- {
74
- source: 'adapters/SQLServerAdapter.ts.tpl',
75
- destination: 'src/orm/adapters/SQLServerAdapter.ts',
76
- },
77
- ],
102
+ autoImports: ['@zintrust/db-sqlserver/register'],
103
+ templates: [],
78
104
  },
79
105
  'adapter:sqlite': {
80
106
  name: 'SQLite Adapter',
81
107
  description: 'Production-ready SQLite database adapter using better-sqlite3',
82
108
  type: 'database-adapter',
83
- aliases: ['a:sqlite', 'sqlite'],
84
- dependencies: ['better-sqlite3'],
85
- devDependencies: ['@types/better-sqlite3'],
86
- templates: [
87
- {
88
- source: 'adapters/SQLiteAdapter.ts.tpl',
89
- destination: 'src/orm/adapters/SQLiteAdapter.ts',
90
- },
91
- ],
109
+ aliases: ['a:sqlite', 'sqlite', 'db:sqlite'],
110
+ dependencies: ['@zintrust/db-sqlite'],
111
+ devDependencies: [],
112
+ autoImports: ['@zintrust/db-sqlite/register'],
113
+ templates: [],
92
114
  },
93
115
  };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=TemplateImportsCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateImportsCheck.d.ts","sourceRoot":"","sources":["../../../src/scripts/TemplateImportsCheck.ts"],"names":[],"mappings":""}
@@ -0,0 +1,111 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ const TEMPLATES_ROOT = path.resolve(process.cwd(), 'src/templates');
4
+ const bannedPrefixes = [
5
+ '@config/',
6
+ '@exceptions/',
7
+ '@orm/',
8
+ '@routing/',
9
+ '@middleware/',
10
+ '@boot/',
11
+ '@container/',
12
+ '@http/',
13
+ '@httpClient/',
14
+ '@security/',
15
+ '@validation/',
16
+ '@profiling/',
17
+ '@tools/',
18
+ '@cache/',
19
+ '@mail/',
20
+ '@storage/',
21
+ '@node-singletons/',
22
+ '@app/',
23
+ '@routes/',
24
+ '@common/',
25
+ '@/',
26
+ ];
27
+ function listFilesRecursive(root) {
28
+ const out = [];
29
+ const entries = fs.readdirSync(root, { withFileTypes: true });
30
+ for (const entry of entries) {
31
+ const full = path.join(root, entry.name);
32
+ if (entry.isDirectory()) {
33
+ out.push(...listFilesRecursive(full));
34
+ }
35
+ else {
36
+ out.push(full);
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+ function isTemplateFile(filePath) {
42
+ return filePath.endsWith('.tpl');
43
+ }
44
+ function checkFile(filePath) {
45
+ const text = fs.readFileSync(filePath, 'utf8');
46
+ const lines = text.split(/\r?\n/);
47
+ const offenses = [];
48
+ const addIfBanned = (lineNo, spec, lineText) => {
49
+ const trimmed = spec.trim();
50
+ if (trimmed === '@zintrust/core' || trimmed === '@zintrust/core/node')
51
+ return;
52
+ if (trimmed.startsWith('node:'))
53
+ return;
54
+ if (trimmed.startsWith('./') || trimmed.startsWith('../'))
55
+ return;
56
+ for (const prefix of bannedPrefixes) {
57
+ if (trimmed.startsWith(prefix)) {
58
+ offenses.push({ line: lineNo, spec: trimmed, text: lineText });
59
+ return;
60
+ }
61
+ }
62
+ };
63
+ const importFromRe = /\bfrom\s+['"]([^'"]+)['"]/g;
64
+ const dynamicImportRe = /\bimport\(\s*['"]([^'"]+)['"]\s*\)/g;
65
+ for (let i = 0; i < lines.length; i++) {
66
+ const lineText = lines[i] ?? '';
67
+ // from '...'
68
+ for (const m of lineText.matchAll(importFromRe)) {
69
+ const spec = m[1];
70
+ if (typeof spec === 'string')
71
+ addIfBanned(i + 1, spec, lineText);
72
+ }
73
+ // import('...')
74
+ for (const m of lineText.matchAll(dynamicImportRe)) {
75
+ const spec = m[1];
76
+ if (typeof spec === 'string')
77
+ addIfBanned(i + 1, spec, lineText);
78
+ }
79
+ }
80
+ return offenses;
81
+ }
82
+ function main() {
83
+ if (!fs.existsSync(TEMPLATES_ROOT)) {
84
+ process.stderr.write(`Templates root not found: ${TEMPLATES_ROOT}\n`);
85
+ process.exit(1);
86
+ }
87
+ const files = listFilesRecursive(TEMPLATES_ROOT).filter(isTemplateFile);
88
+ const allOffenses = [];
89
+ for (const file of files) {
90
+ const offenses = checkFile(file);
91
+ for (const o of offenses) {
92
+ allOffenses.push({
93
+ file: path.relative(process.cwd(), file),
94
+ line: o.line,
95
+ spec: o.spec,
96
+ text: o.text,
97
+ });
98
+ }
99
+ }
100
+ if (allOffenses.length > 0) {
101
+ process.stderr.write('Template import check failed. Disallowed import specifiers found:\n');
102
+ for (const o of allOffenses) {
103
+ process.stderr.write(`- ${o.file}:${o.line} -> ${o.spec}\n`);
104
+ process.stderr.write(` ${o.text.trim()}\n`);
105
+ }
106
+ process.stderr.write("\nAllowed: '@zintrust/core', '@zintrust/core/node', 'node:*', and relative imports (./, ../).\n");
107
+ process.exit(1);
108
+ }
109
+ process.stdout.write(`✓ Template import check passed (${files.length} templates)\n`);
110
+ }
111
+ main();
@@ -3,11 +3,15 @@
3
3
  * MySQL Database Adapter
4
4
  */
5
5
 
6
- import { FeatureFlags } from '@config/features';
7
- import { Logger } from '@config/logger';
8
- import { ErrorFactory } from '@exceptions/ZintrustError';
9
- import { DatabaseConfig, IDatabaseAdapter, QueryResult } from '@orm/DatabaseAdapter';
10
- import { QueryBuilder } from '@orm/QueryBuilder';
6
+ import {
7
+ ErrorFactory,
8
+ FeatureFlags,
9
+ Logger,
10
+ QueryBuilder,
11
+ type DatabaseConfig,
12
+ type IDatabaseAdapter,
13
+ type QueryResult,
14
+ } from '@zintrust/core';
11
15
 
12
16
  /**
13
17
  * MySQL adapter implementation
@@ -3,11 +3,15 @@
3
3
  * PostgreSQL Database Adapter
4
4
  */
5
5
 
6
- import { FeatureFlags } from '@config/features';
7
- import { Logger } from '@config/logger';
8
- import { ErrorFactory } from '@exceptions/ZintrustError';
9
- import { DatabaseConfig, IDatabaseAdapter, QueryResult } from '@orm/DatabaseAdapter';
10
- import { QueryBuilder } from '@orm/QueryBuilder';
6
+ import {
7
+ ErrorFactory,
8
+ FeatureFlags,
9
+ Logger,
10
+ QueryBuilder,
11
+ type DatabaseConfig,
12
+ type IDatabaseAdapter,
13
+ type QueryResult,
14
+ } from '@zintrust/core';
11
15
 
12
16
  /**
13
17
  * PostgreSQL adapter implementation
@@ -3,11 +3,15 @@
3
3
  * SQL Server Database Adapter
4
4
  */
5
5
 
6
- import { FeatureFlags } from '@config/features';
7
- import { Logger } from '@config/logger';
8
- import { ErrorFactory } from '@exceptions/ZintrustError';
9
- import { DatabaseConfig, IDatabaseAdapter, QueryResult } from '@orm/DatabaseAdapter';
10
- import { QueryBuilder } from '@orm/QueryBuilder';
6
+ import {
7
+ ErrorFactory,
8
+ FeatureFlags,
9
+ Logger,
10
+ QueryBuilder,
11
+ type DatabaseConfig,
12
+ type IDatabaseAdapter,
13
+ type QueryResult,
14
+ } from '@zintrust/core';
11
15
 
12
16
  /**
13
17
  * SQL Server adapter implementation