@sundaysf/cli-v2 0.0.1
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 -0
- package/dist/README.md +178 -0
- package/dist/bin/generators/class.js +2 -0
- package/dist/bin/generators/class.js.map +1 -0
- package/dist/bin/generators/postman.js +2 -0
- package/dist/bin/generators/postman.js.map +1 -0
- package/dist/bin/index.js +3 -0
- package/dist/bin/index.js.map +1 -0
- package/dist/templates/backend/.claude/agents/sundays-backend-builder.md +70 -0
- package/dist/templates/backend/.claude/settings.local.json +14 -0
- package/dist/templates/backend/.env.example +13 -0
- package/dist/templates/backend/.prettierignore +3 -0
- package/dist/templates/backend/.prettierrc +9 -0
- package/dist/templates/backend/CLAUDE.md +348 -0
- package/dist/templates/backend/Dockerfile +14 -0
- package/dist/templates/backend/README.md +18 -0
- package/dist/templates/backend/eslint.config.js +20 -0
- package/dist/templates/backend/src/app.ts +35 -0
- package/dist/templates/backend/src/common/config/origins/origins.config.ts +11 -0
- package/dist/templates/backend/src/common/utils/environment.resolver.ts +4 -0
- package/dist/templates/backend/src/common/utils/version.resolver.ts +5 -0
- package/dist/templates/backend/src/controllers/health/health.controller.ts +24 -0
- package/dist/templates/backend/src/middlewares/error/error.middleware.ts +21 -0
- package/dist/templates/backend/src/routes/health/health.router.ts +17 -0
- package/dist/templates/backend/src/routes/index.ts +57 -0
- package/dist/templates/backend/src/server.ts +16 -0
- package/dist/templates/backend/src/types.d.ts +10 -0
- package/dist/templates/backend/tsconfig.json +16 -0
- package/dist/templates/db-sql/.claude/agents/knex-table-implementer.md +113 -0
- package/dist/templates/db-sql/.claude/settings.local.json +11 -0
- package/dist/templates/db-sql/.env.example +8 -0
- package/dist/templates/db-sql/CLAUDE.md +106 -0
- package/dist/templates/db-sql/knexfile.ts +33 -0
- package/dist/templates/db-sql/migrations/.gitkeep +0 -0
- package/dist/templates/db-sql/migrations/001_create_sundays_package_version.ts +13 -0
- package/dist/templates/db-sql/seeds/001_sundays_package_version_seed.ts +11 -0
- package/dist/templates/db-sql/src/KnexConnection.ts +74 -0
- package/dist/templates/db-sql/src/d.types.ts +18 -0
- package/dist/templates/db-sql/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -0
- package/dist/templates/db-sql/src/index.ts +9 -0
- package/dist/templates/db-sql/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -0
- package/dist/templates/db-sql/tsconfig.json +16 -0
- package/dist/templates/module/CLAUDE.md +159 -0
- package/dist/templates/module/src/index.ts +9 -0
- package/dist/templates/module/tsconfig.json +19 -0
- package/package.json +40 -0
|
@@ -0,0 +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.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## File Structure Conventions
|
|
6
|
+
|
|
7
|
+
**Interfaces**: All interfaces should be placed in `src/interfaces/{entity}/{entity}.interfaces.ts`
|
|
8
|
+
- Example: `src/interfaces/company/company.interfaces.ts`
|
|
9
|
+
|
|
10
|
+
**DAOs**: All DAO classes should be placed in `src/dao/{entity}/{entity}.dao.ts`
|
|
11
|
+
- Example: `src/dao/company/company.dao.ts`
|
|
12
|
+
|
|
13
|
+
This convention maintains consistency with the existing structure (e.g., sundays-package-version).
|
|
14
|
+
|
|
15
|
+
## Common Development Commands
|
|
16
|
+
|
|
17
|
+
**Build**: `npm run build` - Compiles TypeScript to JavaScript in the `dist` directory
|
|
18
|
+
|
|
19
|
+
**Format**: `npm run format` - Formats code using Prettier
|
|
20
|
+
|
|
21
|
+
**Test**: `npm test` - Runs Jest tests
|
|
22
|
+
|
|
23
|
+
**Clean Build**: `npm run clean` - Removes dist, formats code, and rebuilds
|
|
24
|
+
|
|
25
|
+
**Database Migrations**:
|
|
26
|
+
- Create migration: `npm run migrate:create` - Creates a new migration file in TypeScript
|
|
27
|
+
- Run migrations: `npm run migrate:deploy` - Applies pending migrations
|
|
28
|
+
|
|
29
|
+
**Database Seeds**:
|
|
30
|
+
- Create seed: `npm run seed:create` - Creates a new seed file in TypeScript
|
|
31
|
+
- Run seeds: `npm run seed:run` - Executes seed files
|
|
32
|
+
|
|
33
|
+
## Architecture Overview
|
|
34
|
+
|
|
35
|
+
This is a Knex.js database module (`@dupin/knex-v2`) that provides a PostgreSQL connection manager and data access layer.
|
|
36
|
+
|
|
37
|
+
**Key Components**:
|
|
38
|
+
|
|
39
|
+
1. **KnexManager** (`src/KnexConnection.ts`): Singleton pattern for managing database connections
|
|
40
|
+
- Handles connection pooling with configurable min/max connections
|
|
41
|
+
- Uses environment variables for database configuration (SQL_HOST, SQL_USER, SQL_PASSWORD, SQL_DB_NAME, SQL_PORT)
|
|
42
|
+
- Supports SSL connections with `rejectUnauthorized: false`
|
|
43
|
+
|
|
44
|
+
2. **DAO Pattern**: Data Access Objects implement the `IBaseDAO<T>` interface
|
|
45
|
+
- Example: `SundaysPackageVersionDAO` provides CRUD operations
|
|
46
|
+
- All DAOs use the shared KnexManager connection
|
|
47
|
+
- Includes pagination support via `IDataPaginator`
|
|
48
|
+
|
|
49
|
+
3. **Database Configuration** (`knexfile.ts`): Supports development, staging, and production environments
|
|
50
|
+
- All environments use PostgreSQL client
|
|
51
|
+
- Configuration reads from environment variables
|
|
52
|
+
- Migration table: `knex_migrations`
|
|
53
|
+
|
|
54
|
+
4. **Type System**:
|
|
55
|
+
- Interfaces in `src/interfaces/` define data models
|
|
56
|
+
- Common types in `src/d.types.ts` (IBaseDAO, IDataPaginator)
|
|
57
|
+
- All exports centralized in `src/index.ts`
|
|
58
|
+
|
|
59
|
+
**Environment Requirements**:
|
|
60
|
+
- SQL_HOST: PostgreSQL host
|
|
61
|
+
- SQL_USER: Database user
|
|
62
|
+
- SQL_PASSWORD: Database password
|
|
63
|
+
- SQL_DB_NAME: Database name
|
|
64
|
+
- SQL_PORT: Database port (defaults to 5432)
|
|
65
|
+
|
|
66
|
+
## DAO Implementation Patterns
|
|
67
|
+
|
|
68
|
+
### Join Pattern for Related Entities
|
|
69
|
+
|
|
70
|
+
When implementing DAOs that need to include related entities, use PostgreSQL's `to_jsonb()` function for clean and efficient joins:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
async getById(id: number): Promise<IEntity | null> {
|
|
74
|
+
const result = await this._knex("entity as e")
|
|
75
|
+
.leftJoin("related as r", "e.relatedId", "r.id")
|
|
76
|
+
.select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
|
|
77
|
+
.where("e.id", id)
|
|
78
|
+
.first();
|
|
79
|
+
return result || null;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Key Points:**
|
|
84
|
+
- Use table aliases for clarity (`as e`, `as r`)
|
|
85
|
+
- `to_jsonb(r.*)` automatically converts the joined row to JSON
|
|
86
|
+
- Returns `null` if no related entity is found (with leftJoin)
|
|
87
|
+
- No manual mapping required - PostgreSQL handles the conversion
|
|
88
|
+
|
|
89
|
+
**For paginated results:**
|
|
90
|
+
```typescript
|
|
91
|
+
const query = this._knex("entity as e")
|
|
92
|
+
.leftJoin("related as r", "e.relatedId", "r.id")
|
|
93
|
+
.select("e.*", this._knex.raw("to_jsonb(r.*) as related"));
|
|
94
|
+
|
|
95
|
+
const data = await query
|
|
96
|
+
.clone()
|
|
97
|
+
.limit(limit)
|
|
98
|
+
.offset(offset)
|
|
99
|
+
.orderBy("e.id", "desc");
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Known Issues and Solutions
|
|
103
|
+
|
|
104
|
+
**Migration Error "require is not defined"**: The knexfile.ts uses ES module syntax. If you encounter this error, ensure the file uses:
|
|
105
|
+
- `import dotenv from "dotenv"` instead of `require("dotenv")`
|
|
106
|
+
- `export default config` instead of `module.exports = config`
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Knex } from "knex";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
const isLocalhost = process.env.SQL_HOST === 'localhost' || process.env.SQL_HOST === '127.0.0.1';
|
|
6
|
+
const rejectUnauthorized = process.env.SQL_REJECT_UNAUTHORIZED !== 'false';
|
|
7
|
+
|
|
8
|
+
const sharedConfig: Knex.Config = {
|
|
9
|
+
client: "postgresql",
|
|
10
|
+
connection: {
|
|
11
|
+
database: process.env.SQL_DB_NAME,
|
|
12
|
+
user: process.env.SQL_USER,
|
|
13
|
+
password: process.env.SQL_PASSWORD,
|
|
14
|
+
host: process.env.SQL_HOST,
|
|
15
|
+
port: process.env.SQL_PORT ? +process.env.SQL_PORT : 5432,
|
|
16
|
+
ssl: isLocalhost ? false : { rejectUnauthorized },
|
|
17
|
+
},
|
|
18
|
+
pool: {
|
|
19
|
+
min: 2,
|
|
20
|
+
max: 10,
|
|
21
|
+
},
|
|
22
|
+
migrations: {
|
|
23
|
+
tableName: "knex_migrations",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const config: { [key: string]: Knex.Config } = {
|
|
28
|
+
development: sharedConfig,
|
|
29
|
+
staging: sharedConfig,
|
|
30
|
+
production: sharedConfig,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default config;
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Knex } from "knex";
|
|
2
|
+
|
|
3
|
+
export async function up(knex: Knex): Promise<void> {
|
|
4
|
+
await knex.schema.createTable("sundays_package_version", (table) => {
|
|
5
|
+
table.increments("id").primary();
|
|
6
|
+
table.string("versionName").notNullable();
|
|
7
|
+
table.timestamps(true, true); // created_at, updated_at
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function down(knex: Knex): Promise<void> {
|
|
12
|
+
await knex.schema.dropTableIfExists("sundays_package_version");
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Knex } from "knex";
|
|
2
|
+
|
|
3
|
+
export async function seed(knex: Knex): Promise<void> {
|
|
4
|
+
// Deletes ALL existing entries
|
|
5
|
+
await knex("sundays_package_version").del();
|
|
6
|
+
|
|
7
|
+
// Inserts seed entries
|
|
8
|
+
await knex("sundays_package_version").insert([
|
|
9
|
+
{ versionName: "1.0.0" }
|
|
10
|
+
]);
|
|
11
|
+
}
|
|
@@ -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,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "CommonJS",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"sourceMap": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Module Template Overview
|
|
6
|
+
|
|
7
|
+
This is a basic npm module template created by the Sundays Framework SDK. It provides a minimal starting point for creating reusable Node.js modules.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
### Development
|
|
12
|
+
|
|
13
|
+
- `npm run build` - Compile TypeScript to JavaScript (output to dist/)
|
|
14
|
+
- `npm test` - Run tests (no test framework configured by default)
|
|
15
|
+
- `npm run format` - Format code (if Prettier is configured)
|
|
16
|
+
|
|
17
|
+
### Publishing
|
|
18
|
+
|
|
19
|
+
- `npm run npm:publish` - Publish the module to npm registry
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
module/
|
|
25
|
+
├── src/ # TypeScript source files
|
|
26
|
+
│ └── index.ts # Main entry point
|
|
27
|
+
├── dist/ # Compiled JavaScript output (generated)
|
|
28
|
+
├── tsconfig.json # TypeScript configuration
|
|
29
|
+
├── package.json # Module metadata and dependencies
|
|
30
|
+
└── .gitignore # Git ignore patterns
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
### TypeScript Setup
|
|
36
|
+
|
|
37
|
+
The module is configured with TypeScript for type safety:
|
|
38
|
+
- Source files in `src/`
|
|
39
|
+
- Compiled output to `dist/`
|
|
40
|
+
- Strict mode enabled
|
|
41
|
+
- ES2020 target with CommonJS modules
|
|
42
|
+
- Declaration files generated for TypeScript consumers
|
|
43
|
+
|
|
44
|
+
### Package.json
|
|
45
|
+
|
|
46
|
+
Key fields to configure:
|
|
47
|
+
- `name`: Your module name (scoped or unscoped)
|
|
48
|
+
- `version`: Semantic version (start with 0.1.0)
|
|
49
|
+
- `description`: Brief description of the module
|
|
50
|
+
- `main`: Entry point (usually `dist/index.js`)
|
|
51
|
+
- `types`: TypeScript declarations (usually `dist/index.d.ts`)
|
|
52
|
+
- `author`: Your name or organization
|
|
53
|
+
- `license`: License type (MIT, ISC, etc.)
|
|
54
|
+
|
|
55
|
+
## Getting Started
|
|
56
|
+
|
|
57
|
+
1. **Initialize the module**:
|
|
58
|
+
```bash
|
|
59
|
+
npm install
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
2. **Write your module code** in `src/index.ts`:
|
|
63
|
+
```typescript
|
|
64
|
+
export function myFunction() {
|
|
65
|
+
return "Hello from my module!";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class MyClass {
|
|
69
|
+
// Your implementation
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
3. **Build the module**:
|
|
74
|
+
```bash
|
|
75
|
+
npm run build
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
4. **Test locally** before publishing:
|
|
79
|
+
```bash
|
|
80
|
+
npm link
|
|
81
|
+
# In another project:
|
|
82
|
+
npm link your-module-name
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
5. **Publish to npm**:
|
|
86
|
+
```bash
|
|
87
|
+
npm run npm:publish
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Best Practices
|
|
91
|
+
|
|
92
|
+
1. **Export Strategy**: Use named exports for clarity
|
|
93
|
+
```typescript
|
|
94
|
+
export { MyClass, myFunction, MyInterface };
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
2. **Type Definitions**: Always include TypeScript definitions
|
|
98
|
+
- Helps consumers with IntelliSense
|
|
99
|
+
- Prevents type-related bugs
|
|
100
|
+
|
|
101
|
+
3. **Documentation**: Add JSDoc comments to exported functions/classes
|
|
102
|
+
```typescript
|
|
103
|
+
/**
|
|
104
|
+
* Calculates the sum of two numbers
|
|
105
|
+
* @param a First number
|
|
106
|
+
* @param b Second number
|
|
107
|
+
* @returns The sum of a and b
|
|
108
|
+
*/
|
|
109
|
+
export function add(a: number, b: number): number {
|
|
110
|
+
return a + b;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
4. **Versioning**: Follow semantic versioning (semver)
|
|
115
|
+
- MAJOR: Breaking changes
|
|
116
|
+
- MINOR: New features (backward compatible)
|
|
117
|
+
- PATCH: Bug fixes
|
|
118
|
+
|
|
119
|
+
5. **Testing**: Consider adding a test framework
|
|
120
|
+
- Jest for unit testing
|
|
121
|
+
- Include test scripts in package.json
|
|
122
|
+
|
|
123
|
+
6. **Dependencies**: Keep dependencies minimal
|
|
124
|
+
- Use `dependencies` for runtime requirements
|
|
125
|
+
- Use `devDependencies` for build/test tools
|
|
126
|
+
- Consider marking some as `peerDependencies`
|
|
127
|
+
|
|
128
|
+
## Common Module Patterns
|
|
129
|
+
|
|
130
|
+
### Singleton Pattern
|
|
131
|
+
```typescript
|
|
132
|
+
class MySingleton {
|
|
133
|
+
private static instance: MySingleton;
|
|
134
|
+
|
|
135
|
+
private constructor() {}
|
|
136
|
+
|
|
137
|
+
static getInstance(): MySingleton {
|
|
138
|
+
if (!MySingleton.instance) {
|
|
139
|
+
MySingleton.instance = new MySingleton();
|
|
140
|
+
}
|
|
141
|
+
return MySingleton.instance;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Factory Pattern
|
|
147
|
+
```typescript
|
|
148
|
+
export function createInstance(config: Config): MyClass {
|
|
149
|
+
return new MyClass(config);
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Plugin/Extension Pattern
|
|
154
|
+
```typescript
|
|
155
|
+
export interface Plugin {
|
|
156
|
+
name: string;
|
|
157
|
+
install(app: any): void;
|
|
158
|
+
}
|
|
159
|
+
```
|