@sundaysf/cli-v2 1.0.1 → 1.0.3
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 +178 -178
- package/dist/README.md +178 -178
- package/dist/bin/generators/class.js.map +1 -1
- package/dist/bin/generators/postman.js.map +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/templates/backend/.claude/agents/knex-table-implementer.md +113 -113
- package/dist/templates/backend/.claude/agents/sundays-backend-builder.md +70 -70
- package/dist/templates/backend/.claude/settings.local.json +13 -13
- package/dist/templates/backend/.env.example +13 -13
- package/dist/templates/backend/.prettierignore +2 -2
- package/dist/templates/backend/.prettierrc +9 -9
- package/dist/templates/backend/CLAUDE.md +348 -348
- package/dist/templates/backend/Dockerfile +14 -14
- package/dist/templates/backend/README.md +18 -18
- package/dist/templates/backend/eslint.config.js +20 -20
- package/dist/templates/backend/src/app.ts +34 -34
- package/dist/templates/backend/src/common/config/origins/origins.config.ts +11 -11
- package/dist/templates/backend/src/common/utils/environment.resolver.ts +3 -3
- package/dist/templates/backend/src/common/utils/version.resolver.ts +4 -4
- package/dist/templates/backend/src/controllers/health/health.controller.ts +23 -23
- package/dist/templates/backend/src/middlewares/error/error.middleware.ts +21 -21
- package/dist/templates/backend/src/routes/health/health.router.ts +16 -16
- package/dist/templates/backend/src/routes/index.ts +57 -57
- package/dist/templates/backend/src/server.ts +16 -16
- package/dist/templates/backend/src/types.d.ts +10 -10
- package/dist/templates/backend/tsconfig.json +16 -16
- package/dist/templates/backend-db-sql/.claude/agents/knex-table-implementer.md +114 -114
- package/dist/templates/backend-db-sql/.claude/agents/sundays-backend-builder.md +70 -70
- package/dist/templates/backend-db-sql/.claude/settings.local.json +19 -19
- package/dist/templates/backend-db-sql/.env.example +13 -13
- package/dist/templates/backend-db-sql/.prettierignore +2 -2
- package/dist/templates/backend-db-sql/.prettierrc +9 -9
- package/dist/templates/backend-db-sql/CLAUDE.md +374 -374
- package/dist/templates/backend-db-sql/Dockerfile +17 -17
- package/dist/templates/backend-db-sql/README.md +34 -34
- package/dist/templates/backend-db-sql/db/knexfile.ts +33 -33
- package/dist/templates/backend-db-sql/db/migrations/001_create_sundays_package_version.ts +12 -12
- package/dist/templates/backend-db-sql/db/seeds/001_sundays_package_version_seed.ts +10 -10
- package/dist/templates/backend-db-sql/db/src/KnexConnection.ts +74 -74
- package/dist/templates/backend-db-sql/db/src/d.types.ts +18 -18
- package/dist/templates/backend-db-sql/db/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
- package/dist/templates/backend-db-sql/db/src/index.ts +9 -9
- package/dist/templates/backend-db-sql/db/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
- package/dist/templates/backend-db-sql/db/tsconfig.json +16 -16
- package/dist/templates/backend-db-sql/eslint.config.js +20 -20
- package/dist/templates/backend-db-sql/src/app.ts +34 -34
- package/dist/templates/backend-db-sql/src/common/config/origins/origins.config.ts +11 -11
- package/dist/templates/backend-db-sql/src/common/utils/environment.resolver.ts +3 -3
- package/dist/templates/backend-db-sql/src/common/utils/version.resolver.ts +4 -4
- package/dist/templates/backend-db-sql/src/controllers/health/health.controller.ts +23 -23
- package/dist/templates/backend-db-sql/src/middlewares/error/error.middleware.ts +21 -21
- package/dist/templates/backend-db-sql/src/routes/health/health.router.ts +16 -16
- package/dist/templates/backend-db-sql/src/routes/index.ts +57 -57
- package/dist/templates/backend-db-sql/src/server.ts +18 -18
- package/dist/templates/backend-db-sql/src/types.d.ts +10 -10
- package/dist/templates/backend-db-sql/tsconfig.json +16 -16
- package/dist/templates/backend-embedded-db-sql/.claude/agents/knex-table-implementer.md +116 -0
- package/dist/templates/backend-embedded-db-sql/.claude/agents/sundays-backend-builder.md +70 -0
- package/dist/templates/backend-embedded-db-sql/.claude/settings.local.json +18 -0
- package/dist/templates/backend-embedded-db-sql/.env.example +14 -0
- package/dist/templates/backend-embedded-db-sql/.prettierignore +3 -0
- package/dist/templates/backend-embedded-db-sql/.prettierrc +9 -0
- package/dist/templates/backend-embedded-db-sql/CLAUDE.md +371 -0
- package/dist/templates/backend-embedded-db-sql/Dockerfile +14 -0
- package/dist/templates/backend-embedded-db-sql/README.md +32 -0
- package/dist/templates/backend-embedded-db-sql/eslint.config.js +20 -0
- package/dist/templates/backend-embedded-db-sql/knexfile.ts +37 -0
- package/dist/templates/backend-embedded-db-sql/migrations/.gitkeep +0 -0
- package/dist/templates/backend-embedded-db-sql/migrations/001_create_sundays_package_version.ts +13 -0
- package/dist/templates/backend-embedded-db-sql/seeds/001_sundays_package_version_seed.ts +11 -0
- package/dist/templates/backend-embedded-db-sql/src/app.ts +35 -0
- package/dist/templates/backend-embedded-db-sql/src/common/config/origins/origins.config.ts +11 -0
- package/dist/templates/backend-embedded-db-sql/src/common/utils/environment.resolver.ts +4 -0
- package/dist/templates/backend-embedded-db-sql/src/common/utils/version.resolver.ts +5 -0
- package/dist/templates/backend-embedded-db-sql/src/controllers/health/health.controller.ts +24 -0
- package/dist/templates/backend-embedded-db-sql/src/db/KnexConnection.ts +74 -0
- package/dist/templates/backend-embedded-db-sql/src/db/d.types.ts +18 -0
- package/dist/templates/backend-embedded-db-sql/src/db/dao/sundays-package-version/sundays-package-version.dao.ts +71 -0
- package/dist/templates/backend-embedded-db-sql/src/db/index.ts +9 -0
- package/dist/templates/backend-embedded-db-sql/src/db/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -0
- package/dist/templates/backend-embedded-db-sql/src/middlewares/error/error.middleware.ts +21 -0
- package/dist/templates/backend-embedded-db-sql/src/routes/health/health.router.ts +17 -0
- package/dist/templates/backend-embedded-db-sql/src/routes/index.ts +57 -0
- package/dist/templates/backend-embedded-db-sql/src/server.ts +18 -0
- package/dist/templates/backend-embedded-db-sql/src/types.d.ts +10 -0
- package/dist/templates/backend-embedded-db-sql/tsconfig.json +16 -0
- package/dist/templates/db-sql/.claude/agents/knex-table-implementer.md +113 -113
- package/dist/templates/db-sql/.claude/agents/sundays-backend-builder.md +70 -70
- package/dist/templates/db-sql/.claude/settings.local.json +10 -10
- package/dist/templates/db-sql/.env.example +8 -8
- package/dist/templates/db-sql/CLAUDE.md +105 -105
- package/dist/templates/db-sql/knexfile.ts +33 -33
- package/dist/templates/db-sql/migrations/001_create_sundays_package_version.ts +12 -12
- package/dist/templates/db-sql/seeds/001_sundays_package_version_seed.ts +10 -10
- package/dist/templates/db-sql/src/KnexConnection.ts +74 -74
- package/dist/templates/db-sql/src/d.types.ts +18 -18
- package/dist/templates/db-sql/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
- package/dist/templates/db-sql/src/index.ts +9 -9
- package/dist/templates/db-sql/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
- package/dist/templates/db-sql/tsconfig.json +16 -16
- package/dist/templates/module/.claude/agents/knex-table-implementer.md +113 -113
- package/dist/templates/module/.claude/agents/sundays-backend-builder.md +70 -70
- package/dist/templates/module/.claude/settings.local.json +10 -10
- package/dist/templates/module/CLAUDE.md +158 -158
- package/dist/templates/module/src/index.ts +9 -9
- package/dist/templates/module/tsconfig.json +19 -19
- package/package.json +40 -40
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { knex, Knex } from 'knex';
|
|
2
|
+
|
|
3
|
+
class KnexManager {
|
|
4
|
+
private static knexInstance: Knex<any, unknown[]> | null = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Open a new connection. Reuse the already existing one if there's any.
|
|
8
|
+
*/
|
|
9
|
+
static async connect(
|
|
10
|
+
config?: Knex.Config,
|
|
11
|
+
connections?: number
|
|
12
|
+
): Promise<Knex<any, unknown[]>> {
|
|
13
|
+
if (!KnexManager.knexInstance) {
|
|
14
|
+
const isLocalhost = process.env.SQL_HOST === 'localhost' || process.env.SQL_HOST === '127.0.0.1';
|
|
15
|
+
const rejectUnauthorized = process.env.SQL_REJECT_UNAUTHORIZED !== 'false';
|
|
16
|
+
const defaultConfig = {
|
|
17
|
+
client: 'pg',
|
|
18
|
+
connection: {
|
|
19
|
+
host: process.env.SQL_HOST,
|
|
20
|
+
user: process.env.SQL_USER,
|
|
21
|
+
password: process.env.SQL_PASSWORD,
|
|
22
|
+
database: process.env.SQL_DB_NAME,
|
|
23
|
+
port: Number(process.env.SQL_PORT) || 5432,
|
|
24
|
+
ssl: isLocalhost ? false : { rejectUnauthorized },
|
|
25
|
+
},
|
|
26
|
+
pool: {
|
|
27
|
+
min: 1,
|
|
28
|
+
max: connections || 15,
|
|
29
|
+
idleTimeoutMillis: 20000,
|
|
30
|
+
acquireTimeoutMillis: 30000,
|
|
31
|
+
},
|
|
32
|
+
migrations: {
|
|
33
|
+
tableName: 'knex_migrations',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
KnexManager.knexInstance = knex(config || defaultConfig);
|
|
37
|
+
try {
|
|
38
|
+
await KnexManager.knexInstance.raw('SELECT 1');
|
|
39
|
+
console.info(`Knex connection established`);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error(`Failed to establish Knex connection:`, error);
|
|
42
|
+
KnexManager.knexInstance = null;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return KnexManager.knexInstance;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Returns the active connection.
|
|
52
|
+
*/
|
|
53
|
+
static getConnection(): Knex<any, unknown[]> {
|
|
54
|
+
if (!KnexManager.knexInstance) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'Knex connection has not been established. Call connect() first.'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return KnexManager.knexInstance;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Closes the connection and destroys the instance.
|
|
64
|
+
*/
|
|
65
|
+
static async disconnect(): Promise<void> {
|
|
66
|
+
if (KnexManager.knexInstance) {
|
|
67
|
+
await KnexManager.knexInstance.destroy();
|
|
68
|
+
KnexManager.knexInstance = null;
|
|
69
|
+
console.info(`Knex connection closed`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default KnexManager;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface IBaseDAO<T> {
|
|
2
|
+
create(item: T): Promise<T>;
|
|
3
|
+
getById(id: number): Promise<T | null>;
|
|
4
|
+
getByUuid(uuid: string): Promise<T | null>;
|
|
5
|
+
update(id: number, item: Partial<T>): Promise<T | null>;
|
|
6
|
+
delete(id: number): Promise<boolean>;
|
|
7
|
+
getAll(page: number, limit: number): Promise<IDataPaginator<T>>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IDataPaginator<T> {
|
|
11
|
+
success: boolean;
|
|
12
|
+
data: T[];
|
|
13
|
+
page: number;
|
|
14
|
+
limit: number;
|
|
15
|
+
count: number;
|
|
16
|
+
totalCount: number;
|
|
17
|
+
totalPages: number;
|
|
18
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Knex } from "knex";
|
|
2
|
+
import { IBaseDAO, IDataPaginator } from "../../d.types";
|
|
3
|
+
import { ISundaysPackageVersion } from "../../interfaces/sundays-package-version/sundays-package-version.interfaces";
|
|
4
|
+
import KnexManager from "../../KnexConnection";
|
|
5
|
+
|
|
6
|
+
export class SundaysPackageVersionDAO implements IBaseDAO<ISundaysPackageVersion> {
|
|
7
|
+
private _knex: Knex<any, unknown[]> = KnexManager.getConnection();
|
|
8
|
+
|
|
9
|
+
async create(item: ISundaysPackageVersion): Promise<ISundaysPackageVersion> {
|
|
10
|
+
const [created] = await this._knex("sundays_package_version").insert(item).returning("*");
|
|
11
|
+
return created;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async getById(id: number): Promise<ISundaysPackageVersion | null> {
|
|
15
|
+
const result = await this._knex("sundays_package_version")
|
|
16
|
+
.select("*")
|
|
17
|
+
.where("id", id)
|
|
18
|
+
.first();
|
|
19
|
+
return result || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getByUuid(uuid: string): Promise<ISundaysPackageVersion | null> {
|
|
23
|
+
const result = await this._knex("sundays_package_version")
|
|
24
|
+
.select("*")
|
|
25
|
+
.where("uuid", uuid)
|
|
26
|
+
.first();
|
|
27
|
+
return result || null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async update(id: number, item: Partial<ISundaysPackageVersion>): Promise<ISundaysPackageVersion | null> {
|
|
31
|
+
const [updated] = await this._knex("sundays_package_version")
|
|
32
|
+
.where({ id })
|
|
33
|
+
.update(item)
|
|
34
|
+
.returning("*");
|
|
35
|
+
return updated || null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async delete(id: number): Promise<boolean> {
|
|
39
|
+
const result = await this._knex("sundays_package_version").where({ id }).del();
|
|
40
|
+
return result > 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async getAll(page: number, limit: number): Promise<IDataPaginator<ISundaysPackageVersion>> {
|
|
44
|
+
const safeLimit = Math.max(limit, 1);
|
|
45
|
+
const offset = (page - 1) * safeLimit;
|
|
46
|
+
|
|
47
|
+
const query = this._knex("sundays_package_version").select("*");
|
|
48
|
+
|
|
49
|
+
const [countResult] = await query.clone().clearSelect().count("* as count");
|
|
50
|
+
const totalCount = +countResult.count;
|
|
51
|
+
const data = await query.clone().limit(safeLimit).offset(offset).orderBy("id", "desc");
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
data,
|
|
56
|
+
page,
|
|
57
|
+
limit: safeLimit,
|
|
58
|
+
count: data.length,
|
|
59
|
+
totalCount,
|
|
60
|
+
totalPages: Math.ceil(totalCount / safeLimit),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getLatestVersion(): Promise<ISundaysPackageVersion | null> {
|
|
65
|
+
const result = await this._knex("sundays_package_version")
|
|
66
|
+
.select("*")
|
|
67
|
+
.orderBy("id", "desc")
|
|
68
|
+
.first();
|
|
69
|
+
return result || null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// DAOs
|
|
2
|
+
export { SundaysPackageVersionDAO } from "./dao/sundays-package-version/sundays-package-version.dao";
|
|
3
|
+
|
|
4
|
+
// Interfaces
|
|
5
|
+
export { IDataPaginator } from "./d.types";
|
|
6
|
+
export { ISundaysPackageVersion } from "./interfaces/sundays-package-version/sundays-package-version.interfaces";
|
|
7
|
+
|
|
8
|
+
import KnexManager from './KnexConnection';
|
|
9
|
+
export { KnexManager };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NextFunction, Response, Request } from 'express';
|
|
2
|
+
export const errorMiddleware = (
|
|
3
|
+
err: any,
|
|
4
|
+
req: Request | any,
|
|
5
|
+
res: Response,
|
|
6
|
+
_next: NextFunction
|
|
7
|
+
) => {
|
|
8
|
+
console.error(err);
|
|
9
|
+
const statusError: number =
|
|
10
|
+
err.statusError ||
|
|
11
|
+
err.statusCode ||
|
|
12
|
+
err.status ||
|
|
13
|
+
req.statusCode ||
|
|
14
|
+
req.statusError ||
|
|
15
|
+
500;
|
|
16
|
+
const isProduction = process.env.ENVIRONMENT === 'production';
|
|
17
|
+
res.status(statusError).json({
|
|
18
|
+
success: false,
|
|
19
|
+
message: isProduction && statusError === 500 ? 'Internal server error' : err.message,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { HealthController } from '../../controllers/health/health.controller';
|
|
3
|
+
|
|
4
|
+
export class HealthRouter {
|
|
5
|
+
public router: Router = Router();
|
|
6
|
+
private readonly healthController: HealthController = new HealthController();
|
|
7
|
+
constructor() {
|
|
8
|
+
this.initRoutes();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private initRoutes(): void {
|
|
12
|
+
this.router.get(
|
|
13
|
+
'/',
|
|
14
|
+
this.healthController.getHealthStatus.bind(this.healthController)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export class IndexRouter {
|
|
6
|
+
private _router: Router;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this._router = Router();
|
|
10
|
+
this.loadRoutes();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private loadRoutes(): void {
|
|
14
|
+
const routesPath = path.join(__dirname);
|
|
15
|
+
|
|
16
|
+
const folders = fs.readdirSync(routesPath).filter(file =>
|
|
17
|
+
fs.statSync(path.join(routesPath, file)).isDirectory()
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
folders.forEach(folder => {
|
|
21
|
+
const baseName = `${folder}.router`;
|
|
22
|
+
const tsPath = path.join(routesPath, folder, `${baseName}.ts`);
|
|
23
|
+
const jsPath = path.join(routesPath, folder, `${baseName}.js`);
|
|
24
|
+
|
|
25
|
+
let filePath = '';
|
|
26
|
+
if (fs.existsSync(tsPath)) {
|
|
27
|
+
filePath = tsPath;
|
|
28
|
+
} else if (fs.existsSync(jsPath)) {
|
|
29
|
+
filePath = jsPath;
|
|
30
|
+
} else {
|
|
31
|
+
console.warn(`[⚠] No route file found for: ${folder}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const routeModule = require(filePath);
|
|
37
|
+
const RouterClass =
|
|
38
|
+
routeModule.default || Object.values(routeModule).find((e) => typeof e === 'function');
|
|
39
|
+
|
|
40
|
+
if (RouterClass) {
|
|
41
|
+
const instance = new (RouterClass as any)();
|
|
42
|
+
this._router.use(`/${folder}`, instance.router);
|
|
43
|
+
console.log(`[✔] Route mounted: /${folder} → ${path.basename(filePath)}`);
|
|
44
|
+
} else {
|
|
45
|
+
console.warn(`[⚠] No class exported in: ${filePath}`);
|
|
46
|
+
}
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error(`[❌] Failed to load router at: ${filePath}`);
|
|
49
|
+
console.error(err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public get router(): Router {
|
|
55
|
+
return this._router;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
import KnexManager from './db/KnexConnection';
|
|
4
|
+
|
|
5
|
+
const envPort: string = process.env.PORT || '3005';
|
|
6
|
+
|
|
7
|
+
if (isNaN(parseInt(envPort))) {
|
|
8
|
+
throw new Error('The port must to be a number');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const PORT: number = parseInt(envPort);
|
|
12
|
+
|
|
13
|
+
(async () => {
|
|
14
|
+
await KnexManager.connect();
|
|
15
|
+
})().then(async () => {
|
|
16
|
+
const { default: app } = await import('./app');
|
|
17
|
+
app.listen(PORT, () => console.info(`Server up and running on port ${PORT}`));
|
|
18
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
export interface IBaseController {
|
|
4
|
+
getAll(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
5
|
+
getByUuid(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
6
|
+
create(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
7
|
+
update(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
8
|
+
patch(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
9
|
+
delete(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"skipLibCheck": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: knex-table-implementer
|
|
3
|
-
description: Use this agent when you need to create new database table implementations in the Knex project, including migrations, DAOs, interfaces, and exports. This agent should be triggered when: 1) A new database table needs to be added to the system, 2) You need to implement the complete data access layer for a new entity, 3) You want to ensure consistency with the existing project structure and patterns. Examples: <example>Context: User needs to add a new 'product' table to the database. user: "I need to add a product table with id, name, price, and categoryId fields" assistant: "I'll use the knex-table-implementer agent to create the complete implementation for the product table including migration, DAO, interfaces, and exports" <commentary>Since the user needs a new table implementation in the Knex project, use the Task tool to launch the knex-table-implementer agent.</commentary></example> <example>Context: User wants to add a user management system. user: "Create a users table with authentication fields" assistant: "Let me use the knex-table-implementer agent to create the full users table implementation following the project patterns" <commentary>The user is requesting a new table implementation, so the knex-table-implementer agent should be used via the Task tool.</commentary></example>
|
|
4
|
-
model: sonnet
|
|
5
|
-
color: red
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
You are an expert Knex.js database architect specializing in implementing consistent, production-ready database table structures following established project patterns.
|
|
9
|
-
|
|
10
|
-
**Your Core Responsibilities:**
|
|
11
|
-
|
|
12
|
-
You will create complete table implementations in the Knex project by:
|
|
13
|
-
1. Creating database migrations using the project's migration patterns
|
|
14
|
-
2. Implementing DAO classes following the established DAO pattern
|
|
15
|
-
3. Defining TypeScript interfaces for the entities
|
|
16
|
-
4. Ensuring all exports are properly added to index.ts
|
|
17
|
-
|
|
18
|
-
**Implementation Workflow:**
|
|
19
|
-
|
|
20
|
-
1. **Migration Creation**:
|
|
21
|
-
- Inform the user to run `npm run migrate:create` to generate the migration file
|
|
22
|
-
- Write the migration with all database properties in camelCase
|
|
23
|
-
- Include proper up() and down() methods
|
|
24
|
-
- Follow the existing migration patterns in the project
|
|
25
|
-
|
|
26
|
-
2. **DAO Implementation**:
|
|
27
|
-
- Create the DAO file at `src/dao/{entityName}/{entityName}.dao.ts`
|
|
28
|
-
- Extend from IBaseDAO interface
|
|
29
|
-
- Implement standard CRUD operations (getById, getAll with pagination, create, update, delete)
|
|
30
|
-
- Use KnexManager.getConnection() for database connections
|
|
31
|
-
- For related entities, use PostgreSQL's to_jsonb() function for joins
|
|
32
|
-
- Follow the exact pattern from existing DAOs like SundaysPackageVersionDAO
|
|
33
|
-
|
|
34
|
-
3. **Interface Definition**:
|
|
35
|
-
- Create the interface file at `src/interfaces/{entityName}/{entityName}.interfaces.ts`
|
|
36
|
-
- Define the main entity interface with all properties
|
|
37
|
-
- Include any related entity interfaces if needed
|
|
38
|
-
- Ensure TypeScript types are properly defined
|
|
39
|
-
|
|
40
|
-
4. **Export Configuration**:
|
|
41
|
-
- Add the new DAO export to src/index.ts
|
|
42
|
-
- Add the new interface export to src/index.ts
|
|
43
|
-
- Maintain alphabetical ordering in exports when possible
|
|
44
|
-
|
|
45
|
-
**Critical Standards You Must Follow**:
|
|
46
|
-
|
|
47
|
-
- **Naming Conventions**:
|
|
48
|
-
- Database columns: camelCase (e.g., createdAt, userId)
|
|
49
|
-
- Table names: snake_case or lowercase
|
|
50
|
-
- Class names: PascalCase with DAO suffix
|
|
51
|
-
- Interface names: Start with 'I' prefix
|
|
52
|
-
|
|
53
|
-
- **DAO Pattern Requirements**:
|
|
54
|
-
- Always implement IBaseDAO<T> interface
|
|
55
|
-
- Include pagination using IDataPaginator
|
|
56
|
-
- Use async/await for all database operations
|
|
57
|
-
- Return null for not found scenarios
|
|
58
|
-
- Use leftJoin with to_jsonb() for related entities
|
|
59
|
-
|
|
60
|
-
- **Code Structure**:
|
|
61
|
-
- One DAO class per file
|
|
62
|
-
- One interface file per entity
|
|
63
|
-
- Keep related logic together
|
|
64
|
-
- Use the singleton KnexManager for connections
|
|
65
|
-
|
|
66
|
-
**Example Patterns to Follow**:
|
|
67
|
-
|
|
68
|
-
For DAO methods with joins:
|
|
69
|
-
```typescript
|
|
70
|
-
async getById(id: number): Promise<IEntity | null> {
|
|
71
|
-
const result = await this._knex("entity as e")
|
|
72
|
-
.leftJoin("related as r", "e.relatedId", "r.id")
|
|
73
|
-
.select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
|
|
74
|
-
.where("e.id", id)
|
|
75
|
-
.first();
|
|
76
|
-
return result || null;
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
For paginated results:
|
|
81
|
-
```typescript
|
|
82
|
-
async getAll(limit: number, offset: number): Promise<IDataPaginator<IEntity>> {
|
|
83
|
-
const query = this._knex("entity");
|
|
84
|
-
const total = await query.clone().count("* as count").first();
|
|
85
|
-
const data = await query.clone().limit(limit).offset(offset).orderBy("id", "desc");
|
|
86
|
-
return {
|
|
87
|
-
data,
|
|
88
|
-
total: parseInt(total?.count as string) || 0,
|
|
89
|
-
limit,
|
|
90
|
-
offset
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Quality Checks**:
|
|
96
|
-
|
|
97
|
-
Before completing any implementation, verify:
|
|
98
|
-
1. Migration file uses camelCase for all properties
|
|
99
|
-
2. DAO follows the exact structure of existing DAOs
|
|
100
|
-
3. Interface properly types all entity properties
|
|
101
|
-
4. All new exports are added to src/index.ts
|
|
102
|
-
5. File paths follow the convention exactly
|
|
103
|
-
6. No unnecessary files are created
|
|
104
|
-
7. Code is consistent with existing patterns
|
|
105
|
-
|
|
106
|
-
**Important Reminders**:
|
|
107
|
-
- Only edit existing files when possible
|
|
108
|
-
- Never create documentation files unless explicitly requested
|
|
109
|
-
- Follow the CLAUDE.md instructions precisely
|
|
110
|
-
- Maintain consistency with the existing codebase structure
|
|
111
|
-
- Always use the established patterns from sundays-package-version as reference
|
|
112
|
-
|
|
113
|
-
When you receive a request, first analyze the entity structure needed, then systematically create each component following the established patterns. If any clarification is needed about field types or relationships, ask before proceeding.
|
|
1
|
+
---
|
|
2
|
+
name: knex-table-implementer
|
|
3
|
+
description: Use this agent when you need to create new database table implementations in the Knex project, including migrations, DAOs, interfaces, and exports. This agent should be triggered when: 1) A new database table needs to be added to the system, 2) You need to implement the complete data access layer for a new entity, 3) You want to ensure consistency with the existing project structure and patterns. Examples: <example>Context: User needs to add a new 'product' table to the database. user: "I need to add a product table with id, name, price, and categoryId fields" assistant: "I'll use the knex-table-implementer agent to create the complete implementation for the product table including migration, DAO, interfaces, and exports" <commentary>Since the user needs a new table implementation in the Knex project, use the Task tool to launch the knex-table-implementer agent.</commentary></example> <example>Context: User wants to add a user management system. user: "Create a users table with authentication fields" assistant: "Let me use the knex-table-implementer agent to create the full users table implementation following the project patterns" <commentary>The user is requesting a new table implementation, so the knex-table-implementer agent should be used via the Task tool.</commentary></example>
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: red
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Knex.js database architect specializing in implementing consistent, production-ready database table structures following established project patterns.
|
|
9
|
+
|
|
10
|
+
**Your Core Responsibilities:**
|
|
11
|
+
|
|
12
|
+
You will create complete table implementations in the Knex project by:
|
|
13
|
+
1. Creating database migrations using the project's migration patterns
|
|
14
|
+
2. Implementing DAO classes following the established DAO pattern
|
|
15
|
+
3. Defining TypeScript interfaces for the entities
|
|
16
|
+
4. Ensuring all exports are properly added to index.ts
|
|
17
|
+
|
|
18
|
+
**Implementation Workflow:**
|
|
19
|
+
|
|
20
|
+
1. **Migration Creation**:
|
|
21
|
+
- Inform the user to run `npm run migrate:create` to generate the migration file
|
|
22
|
+
- Write the migration with all database properties in camelCase
|
|
23
|
+
- Include proper up() and down() methods
|
|
24
|
+
- Follow the existing migration patterns in the project
|
|
25
|
+
|
|
26
|
+
2. **DAO Implementation**:
|
|
27
|
+
- Create the DAO file at `src/dao/{entityName}/{entityName}.dao.ts`
|
|
28
|
+
- Extend from IBaseDAO interface
|
|
29
|
+
- Implement standard CRUD operations (getById, getAll with pagination, create, update, delete)
|
|
30
|
+
- Use KnexManager.getConnection() for database connections
|
|
31
|
+
- For related entities, use PostgreSQL's to_jsonb() function for joins
|
|
32
|
+
- Follow the exact pattern from existing DAOs like SundaysPackageVersionDAO
|
|
33
|
+
|
|
34
|
+
3. **Interface Definition**:
|
|
35
|
+
- Create the interface file at `src/interfaces/{entityName}/{entityName}.interfaces.ts`
|
|
36
|
+
- Define the main entity interface with all properties
|
|
37
|
+
- Include any related entity interfaces if needed
|
|
38
|
+
- Ensure TypeScript types are properly defined
|
|
39
|
+
|
|
40
|
+
4. **Export Configuration**:
|
|
41
|
+
- Add the new DAO export to src/index.ts
|
|
42
|
+
- Add the new interface export to src/index.ts
|
|
43
|
+
- Maintain alphabetical ordering in exports when possible
|
|
44
|
+
|
|
45
|
+
**Critical Standards You Must Follow**:
|
|
46
|
+
|
|
47
|
+
- **Naming Conventions**:
|
|
48
|
+
- Database columns: camelCase (e.g., createdAt, userId)
|
|
49
|
+
- Table names: snake_case or lowercase
|
|
50
|
+
- Class names: PascalCase with DAO suffix
|
|
51
|
+
- Interface names: Start with 'I' prefix
|
|
52
|
+
|
|
53
|
+
- **DAO Pattern Requirements**:
|
|
54
|
+
- Always implement IBaseDAO<T> interface
|
|
55
|
+
- Include pagination using IDataPaginator
|
|
56
|
+
- Use async/await for all database operations
|
|
57
|
+
- Return null for not found scenarios
|
|
58
|
+
- Use leftJoin with to_jsonb() for related entities
|
|
59
|
+
|
|
60
|
+
- **Code Structure**:
|
|
61
|
+
- One DAO class per file
|
|
62
|
+
- One interface file per entity
|
|
63
|
+
- Keep related logic together
|
|
64
|
+
- Use the singleton KnexManager for connections
|
|
65
|
+
|
|
66
|
+
**Example Patterns to Follow**:
|
|
67
|
+
|
|
68
|
+
For DAO methods with joins:
|
|
69
|
+
```typescript
|
|
70
|
+
async getById(id: number): Promise<IEntity | null> {
|
|
71
|
+
const result = await this._knex("entity as e")
|
|
72
|
+
.leftJoin("related as r", "e.relatedId", "r.id")
|
|
73
|
+
.select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
|
|
74
|
+
.where("e.id", id)
|
|
75
|
+
.first();
|
|
76
|
+
return result || null;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For paginated results:
|
|
81
|
+
```typescript
|
|
82
|
+
async getAll(limit: number, offset: number): Promise<IDataPaginator<IEntity>> {
|
|
83
|
+
const query = this._knex("entity");
|
|
84
|
+
const total = await query.clone().count("* as count").first();
|
|
85
|
+
const data = await query.clone().limit(limit).offset(offset).orderBy("id", "desc");
|
|
86
|
+
return {
|
|
87
|
+
data,
|
|
88
|
+
total: parseInt(total?.count as string) || 0,
|
|
89
|
+
limit,
|
|
90
|
+
offset
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Quality Checks**:
|
|
96
|
+
|
|
97
|
+
Before completing any implementation, verify:
|
|
98
|
+
1. Migration file uses camelCase for all properties
|
|
99
|
+
2. DAO follows the exact structure of existing DAOs
|
|
100
|
+
3. Interface properly types all entity properties
|
|
101
|
+
4. All new exports are added to src/index.ts
|
|
102
|
+
5. File paths follow the convention exactly
|
|
103
|
+
6. No unnecessary files are created
|
|
104
|
+
7. Code is consistent with existing patterns
|
|
105
|
+
|
|
106
|
+
**Important Reminders**:
|
|
107
|
+
- Only edit existing files when possible
|
|
108
|
+
- Never create documentation files unless explicitly requested
|
|
109
|
+
- Follow the CLAUDE.md instructions precisely
|
|
110
|
+
- Maintain consistency with the existing codebase structure
|
|
111
|
+
- Always use the established patterns from sundays-package-version as reference
|
|
112
|
+
|
|
113
|
+
When you receive a request, first analyze the entity structure needed, then systematically create each component following the established patterns. If any clarification is needed about field types or relationships, ask before proceeding.
|