@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.
- package/README.md +17 -0
- package/package.json +1 -2
- package/src/cache/Cache.d.ts.map +1 -1
- package/src/cache/Cache.js +8 -2
- package/src/cache/CacheDriverRegistry.d.ts +15 -0
- package/src/cache/CacheDriverRegistry.d.ts.map +1 -0
- package/src/cache/CacheDriverRegistry.js +19 -0
- package/src/cli/commands/AddCommand.d.ts.map +1 -1
- package/src/cli/commands/AddCommand.js +20 -1
- package/src/cli/scaffolding/ControllerGenerator.js +2 -2
- package/src/cli/scaffolding/FeatureScaffolder.js +5 -5
- package/src/cli/scaffolding/RouteGenerator.js +1 -1
- package/src/cli/scaffolding/SeederGenerator.js +1 -1
- package/src/cli/scaffolding/ServiceIntegrationTestGenerator.js +1 -1
- package/src/cli/scaffolding/ServiceScaffolder.js +4 -4
- package/src/common/uuid.d.ts +1 -1
- package/src/common/uuid.d.ts.map +1 -1
- package/src/common/uuid.js +3 -3
- package/src/config/cache.js +1 -1
- package/src/config/mail.d.ts +8 -0
- package/src/config/mail.d.ts.map +1 -1
- package/src/config/mail.js +17 -0
- package/src/config/type.d.ts +11 -2
- package/src/config/type.d.ts.map +1 -1
- package/src/index.d.ts +4 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +7 -0
- package/src/microservices/MicroserviceGenerator.js +7 -7
- package/src/microservices/PostgresAdapter.d.ts.map +1 -1
- package/src/microservices/PostgresAdapter.js +18 -0
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +5 -0
- package/src/orm/DatabaseAdapterRegistry.d.ts +14 -0
- package/src/orm/DatabaseAdapterRegistry.d.ts.map +1 -0
- package/src/orm/DatabaseAdapterRegistry.js +19 -0
- package/src/orm/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLiteAdapter.js +33 -2
- package/src/runtime/PluginManager.d.ts.map +1 -1
- package/src/runtime/PluginManager.js +68 -0
- package/src/runtime/PluginRegistry.d.ts +6 -1
- package/src/runtime/PluginRegistry.d.ts.map +1 -1
- package/src/runtime/PluginRegistry.js +56 -34
- package/src/scripts/TemplateImportsCheck.d.ts +2 -0
- package/src/scripts/TemplateImportsCheck.d.ts.map +1 -0
- package/src/scripts/TemplateImportsCheck.js +111 -0
- package/src/templates/adapters/MySQLAdapter.ts.tpl +9 -5
- package/src/templates/adapters/PostgreSQLAdapter.ts.tpl +9 -5
- package/src/templates/adapters/SQLServerAdapter.ts.tpl +9 -5
- package/src/templates/adapters/SQLiteAdapter.ts.tpl +67 -18
- package/src/templates/features/Queue.ts.tpl +2 -3
- package/src/templates/project/basic/README.md.tpl +4 -0
- package/src/templates/project/basic/app/Controllers/UserController.ts.tpl +1 -1
- package/src/templates/project/basic/app/Models/Post.ts.tpl +1 -1
- package/src/templates/project/basic/app/Models/User.ts.tpl +1 -1
- package/src/templates/project/basic/config/FileLogWriter.ts.tpl +4 -3
- package/src/templates/project/basic/config/SecretsManager.ts.tpl +2 -2
- package/src/templates/project/basic/config/StartupConfigValidator.ts.tpl +2 -2
- package/src/templates/project/basic/config/app.ts.tpl +2 -2
- package/src/templates/project/basic/config/broadcast.ts.tpl +2 -2
- package/src/templates/project/basic/config/cache.ts.tpl +3 -3
- package/src/templates/project/basic/config/cloudflare.ts.tpl +1 -1
- package/src/templates/project/basic/config/database.ts.tpl +2 -2
- package/src/templates/project/basic/config/env.ts.tpl +1 -1
- package/src/templates/project/basic/config/features.ts.tpl +1 -1
- package/src/templates/project/basic/config/index.ts.tpl +16 -16
- package/src/templates/project/basic/config/logger.ts.tpl +9 -7
- package/src/templates/project/basic/config/logging/KvLogger.ts.tpl +2 -2
- package/src/templates/project/basic/config/mail.ts.tpl +17 -2
- package/src/templates/project/basic/config/middleware.ts.tpl +10 -7
- package/src/templates/project/basic/config/notification.ts.tpl +2 -2
- package/src/templates/project/basic/config/queue.ts.tpl +2 -2
- package/src/templates/project/basic/config/security.ts.tpl +4 -3
- package/src/templates/project/basic/config/startup.ts.tpl +1 -1
- package/src/templates/project/basic/config/storage.ts.tpl +2 -2
- package/src/templates/project/basic/config/type.ts.tpl +17 -5
- package/src/templates/project/basic/routes/api.ts.tpl +6 -6
- package/src/templates/project/basic/routes/health.ts.tpl +11 -7
- package/src/templates/project/basic/routes/storage.ts.tpl +8 -5
- package/src/templates/project/basic/src/index.ts.tpl +2 -0
- package/src/templates/project/basic/src/zintrust.plugins.ts.tpl +8 -0
- package/src/tools/broadcast/drivers/Redis.js +1 -1
- package/src/tools/mail/Mail.d.ts +1 -1
- package/src/tools/mail/Mail.d.ts.map +1 -1
- package/src/tools/mail/Mail.js +11 -0
- package/src/tools/mail/MailDriverRegistry.d.ts +16 -0
- package/src/tools/mail/MailDriverRegistry.d.ts.map +1 -0
- package/src/tools/mail/MailDriverRegistry.js +19 -0
- 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;
|
|
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"}
|
package/src/orm/Database.js
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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: ['
|
|
43
|
-
devDependencies: [
|
|
44
|
-
|
|
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: ['
|
|
89
|
+
aliases: ['a:mysql', 'mysql', 'db:mysql'],
|
|
90
|
+
dependencies: ['@zintrust/db-mysql'],
|
|
57
91
|
devDependencies: [],
|
|
58
|
-
|
|
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: ['
|
|
99
|
+
aliases: ['a:mssql', 'mssql', 'db:mssql'],
|
|
100
|
+
dependencies: ['@zintrust/db-sqlserver'],
|
|
71
101
|
devDependencies: [],
|
|
72
|
-
|
|
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: ['
|
|
85
|
-
devDependencies: [
|
|
86
|
-
|
|
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 @@
|
|
|
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 {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|