@akanjs/cli 0.9.48 → 0.9.49
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/cjs/index.js +63 -52
- package/cjs/src/guidelines/___library/sharedUiStructureDescription.en.md +1 -1
- package/cjs/src/guidelines/databaseModule/databaseModule.instruction.md +9 -21
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -5
- package/cjs/src/guidelines/framework/framework.instruction.md +10 -12
- package/cjs/src/guidelines/modelConstant/modelConstant.generate.json +0 -7
- package/cjs/src/guidelines/modelConstant/modelConstant.instruction.md +14 -19
- package/cjs/src/guidelines/modelDocument/modelDocument.instruction.md +0 -24
- package/cjs/src/guidelines/modelService/modelService.generate.json +4 -11
- package/cjs/src/guidelines/modelService/modelService.instruction.md +12 -75
- package/cjs/src/guidelines/modelSignal/modelSignal.generate.json +0 -1
- package/cjs/src/guidelines/modelSignal/modelSignal.instruction.md +2 -38
- package/cjs/src/guidelines/modelStore/modelStore.instruction.md +1 -3
- package/cjs/src/guidelines/modelTemplate/modelTemplate.instruction.md +2 -2
- package/cjs/src/guidelines/modelUnit/modelUnit.instruction.md +2 -2
- package/cjs/src/guidelines/scalarConstant/scalarConstant.instruction.md +6 -11
- package/cjs/src/templates/__scalar/__model__/__model__.constant.js +5 -6
- package/cjs/src/templates/__scalar/__model__/__model__.dictionary.js +1 -1
- package/cjs/src/templates/app/app/[lang]/layout.js +0 -1
- package/cjs/src/templates/app/app/csr.js +3 -1
- package/cjs/src/templates/app/lib/___appName__/__appName__.dictionary.js +1 -1
- package/cjs/src/templates/app/lib/___appName__/__appName__.service.js +2 -3
- package/cjs/src/templates/app/lib/___appName__/__appName__.signal.js +2 -3
- package/cjs/src/templates/app/lib/___appName__/__appName__.store.js +2 -3
- package/cjs/src/templates/client.js +4 -4
- package/cjs/src/templates/crudPages/[__model__Id]/edit/page.js +1 -1
- package/cjs/src/templates/crudPages/[__model__Id]/page.js +1 -1
- package/cjs/src/templates/crudPages/new/page.js +1 -1
- package/cjs/src/templates/crudPages/page.js +1 -1
- package/cjs/src/templates/index.js +0 -1
- package/cjs/src/templates/lib/__lib/lib.constant.js +0 -1
- package/cjs/src/templates/lib/__lib/lib.dictionary.js +7 -4
- package/cjs/src/templates/lib/__lib/lib.document.js +4 -3
- package/cjs/src/templates/lib/__lib/lib.service.js +1 -1
- package/cjs/src/templates/lib/__lib/lib.signal.js +23 -4
- package/cjs/src/templates/lib/__lib/lib.store.js +19 -2
- package/cjs/src/templates/lib/cnst.js +12 -6
- package/cjs/src/templates/lib/db.js +10 -3
- package/cjs/src/templates/lib/dict.js +12 -4
- package/cjs/src/templates/lib/sig.js +20 -9
- package/cjs/src/templates/lib/srv.js +7 -6
- package/cjs/src/templates/lib/st.js +10 -8
- package/cjs/src/templates/lib/useClient.js +39 -0
- package/cjs/src/templates/lib/{usePage.js → useServer.js} +6 -8
- package/cjs/src/templates/libRoot/lib/___libName__/__libName__.service.js +2 -3
- package/cjs/src/templates/libRoot/lib/___libName__/__libName__.store.js +2 -3
- package/cjs/src/templates/module/__model__.constant.js +10 -30
- package/cjs/src/templates/module/__model__.dictionary.js +2 -2
- package/cjs/src/templates/module/__model__.document.js +2 -8
- package/cjs/src/templates/module/__model__.service.js +2 -3
- package/cjs/src/templates/module/__model__.signal.js +4 -5
- package/cjs/src/templates/module/__model__.store.js +3 -4
- package/cjs/src/templates/server.js +5 -4
- package/cjs/src/templates/workspaceRoot/.gitignore.template +5 -3
- package/esm/index.js +63 -52
- package/esm/src/guidelines/___library/sharedUiStructureDescription.en.md +1 -1
- package/esm/src/guidelines/databaseModule/databaseModule.instruction.md +9 -21
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -5
- package/esm/src/guidelines/framework/framework.instruction.md +10 -12
- package/esm/src/guidelines/modelConstant/modelConstant.generate.json +0 -7
- package/esm/src/guidelines/modelConstant/modelConstant.instruction.md +14 -19
- package/esm/src/guidelines/modelDocument/modelDocument.instruction.md +0 -24
- package/esm/src/guidelines/modelService/modelService.generate.json +4 -11
- package/esm/src/guidelines/modelService/modelService.instruction.md +12 -75
- package/esm/src/guidelines/modelSignal/modelSignal.generate.json +0 -1
- package/esm/src/guidelines/modelSignal/modelSignal.instruction.md +2 -38
- package/esm/src/guidelines/modelStore/modelStore.instruction.md +1 -3
- package/esm/src/guidelines/modelTemplate/modelTemplate.instruction.md +2 -2
- package/esm/src/guidelines/modelUnit/modelUnit.instruction.md +2 -2
- package/esm/src/guidelines/scalarConstant/scalarConstant.instruction.md +6 -11
- package/esm/src/templates/__scalar/__model__/__model__.constant.js +5 -6
- package/esm/src/templates/__scalar/__model__/__model__.dictionary.js +1 -1
- package/esm/src/templates/app/app/[lang]/layout.js +0 -1
- package/esm/src/templates/app/app/csr.js +3 -1
- package/esm/src/templates/app/lib/___appName__/__appName__.dictionary.js +1 -1
- package/esm/src/templates/app/lib/___appName__/__appName__.service.js +2 -3
- package/esm/src/templates/app/lib/___appName__/__appName__.signal.js +2 -3
- package/esm/src/templates/app/lib/___appName__/__appName__.store.js +2 -3
- package/esm/src/templates/client.js +4 -4
- package/esm/src/templates/crudPages/[__model__Id]/edit/page.js +1 -1
- package/esm/src/templates/crudPages/[__model__Id]/page.js +1 -1
- package/esm/src/templates/crudPages/new/page.js +1 -1
- package/esm/src/templates/crudPages/page.js +1 -1
- package/esm/src/templates/index.js +0 -1
- package/esm/src/templates/lib/__lib/lib.constant.js +0 -1
- package/esm/src/templates/lib/__lib/lib.dictionary.js +7 -4
- package/esm/src/templates/lib/__lib/lib.document.js +4 -3
- package/esm/src/templates/lib/__lib/lib.service.js +1 -1
- package/esm/src/templates/lib/__lib/lib.signal.js +23 -4
- package/esm/src/templates/lib/__lib/lib.store.js +19 -2
- package/esm/src/templates/lib/cnst.js +12 -6
- package/esm/src/templates/lib/db.js +10 -3
- package/esm/src/templates/lib/dict.js +12 -4
- package/esm/src/templates/lib/sig.js +20 -9
- package/esm/src/templates/lib/srv.js +7 -6
- package/esm/src/templates/lib/st.js +10 -8
- package/esm/src/templates/lib/useClient.js +19 -0
- package/esm/src/templates/lib/useServer.js +11 -0
- package/esm/src/templates/libRoot/lib/___libName__/__libName__.service.js +2 -3
- package/esm/src/templates/libRoot/lib/___libName__/__libName__.store.js +2 -3
- package/esm/src/templates/module/__model__.constant.js +10 -30
- package/esm/src/templates/module/__model__.dictionary.js +2 -2
- package/esm/src/templates/module/__model__.document.js +2 -8
- package/esm/src/templates/module/__model__.service.js +2 -3
- package/esm/src/templates/module/__model__.signal.js +4 -5
- package/esm/src/templates/module/__model__.store.js +3 -4
- package/esm/src/templates/server.js +5 -4
- package/esm/src/templates/workspaceRoot/.gitignore.template +5 -3
- package/package.json +1 -1
- package/src/application/application.command.d.ts +7 -7
- package/src/application/application.script.d.ts +3 -1
- package/src/guidelines/___library/sharedUiStructureDescription.en.md +1 -1
- package/src/guidelines/databaseModule/databaseModule.instruction.md +9 -21
- package/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -5
- package/src/guidelines/framework/framework.instruction.md +10 -12
- package/src/guidelines/modelConstant/modelConstant.instruction.md +14 -19
- package/src/guidelines/modelDocument/modelDocument.instruction.md +0 -24
- package/src/guidelines/modelService/modelService.instruction.md +12 -75
- package/src/guidelines/modelSignal/modelSignal.instruction.md +2 -38
- package/src/guidelines/modelStore/modelStore.instruction.md +1 -3
- package/src/guidelines/modelTemplate/modelTemplate.instruction.md +2 -2
- package/src/guidelines/modelUnit/modelUnit.instruction.md +2 -2
- package/src/guidelines/scalarConstant/scalarConstant.instruction.md +6 -11
- package/src/templates/lib/useServer.d.ts +4 -0
- package/src/workspace/workspace.command.d.ts +1 -0
- package/src/workspace/workspace.script.d.ts +1 -0
- package/esm/src/templates/lib/usePage.js +0 -13
- /package/src/templates/lib/{usePage.d.ts → useClient.d.ts} +0 -0
|
@@ -47,7 +47,7 @@ export const UserRole = enumOf(["user", "admin"] as const);
|
|
|
47
47
|
export type UserRole = enumOf<typeof UserRole>;
|
|
48
48
|
|
|
49
49
|
// Define input model (for create/update operations)
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
export class UserInput {
|
|
52
52
|
@Field.Prop(() => String, { validate: validate.email, type: "email", example: "user@example.com" })
|
|
53
53
|
email: string;
|
|
@@ -57,7 +57,7 @@ export class UserInput {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Define object model (full data model)
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
export class UserObject extends via(UserInput) {
|
|
62
62
|
@Field.Prop(() => [String], [{ enum: UserRole, example: ["user"] }])
|
|
63
63
|
roles: (typeof UserRole.value)[];
|
|
@@ -67,7 +67,7 @@ export class UserObject extends via(UserInput) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// Define light model (client-side model with essential fields)
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
export class LightUser extends via(UserObject, ["email", "roles"] as const) {
|
|
72
72
|
hasAccess(role: UserRole) {
|
|
73
73
|
return this.roles.includes(role);
|
|
@@ -75,11 +75,11 @@ export class LightUser extends via(UserObject, ["email", "roles"] as const) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// Define full model (server-side model with all fields)
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
export class User extends via(UserObject, LightUser) {}
|
|
80
80
|
|
|
81
81
|
// Define filter model (for queries)
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
export class UserFilter extends sortOf(User, {}) {
|
|
84
84
|
@Filter.Mongo()
|
|
85
85
|
byEmail(@Filter.Arg("email", () => String) email: string) {
|
|
@@ -99,12 +99,8 @@ import { hashPassword } from "@shared/nest";
|
|
|
99
99
|
|
|
100
100
|
import * as cnst from "../cnst";
|
|
101
101
|
|
|
102
|
-
// Input model for database operations
|
|
103
|
-
@Database.Input(() => cnst.UserInput)
|
|
104
|
-
export class UserInput extends by(cnst.UserInput) {}
|
|
105
|
-
|
|
106
102
|
// Document model with methods
|
|
107
|
-
|
|
103
|
+
|
|
108
104
|
export class User extends by(cnst.User) {
|
|
109
105
|
addRole(role: cnst.UserRole) {
|
|
110
106
|
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
|
|
@@ -118,7 +114,7 @@ export class User extends by(cnst.User) {
|
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
// Model with database operations
|
|
121
|
-
|
|
117
|
+
|
|
122
118
|
export class UserModel extends into(User, cnst.userCnst, ...user.models) {
|
|
123
119
|
@Loader.ByField("email") userEmailLoader: Loader<string, User>;
|
|
124
120
|
|
|
@@ -132,7 +128,7 @@ export class UserModel extends into(User, cnst.userCnst, ...user.models) {
|
|
|
132
128
|
}
|
|
133
129
|
|
|
134
130
|
// Middleware for hooks and indexes
|
|
135
|
-
|
|
131
|
+
|
|
136
132
|
export class UserMiddleware extends beyond(UserModel, User) {
|
|
137
133
|
onSchema(schema: SchemaOf<UserModel, User>) {
|
|
138
134
|
// Hash password before saving
|
|
@@ -158,7 +154,7 @@ export class UserMiddleware extends beyond(UserModel, User) {
|
|
|
158
154
|
Implement business logic in the service:
|
|
159
155
|
|
|
160
156
|
```typescript
|
|
161
|
-
import { DbService,
|
|
157
|
+
import { DbService, Srv, Use } from "@akanjs/service";
|
|
162
158
|
import { Account, type Me } from "@akanjs/signal";
|
|
163
159
|
import { isPasswordMatch } from "@shared/nest";
|
|
164
160
|
|
|
@@ -167,7 +163,6 @@ import * as db from "../db";
|
|
|
167
163
|
import type * as option from "../option";
|
|
168
164
|
import type * as srv from "../srv";
|
|
169
165
|
|
|
170
|
-
@Service("UserService")
|
|
171
166
|
export class UserService extends DbService(db.userDb) {
|
|
172
167
|
@Use() protected readonly config: option.SecurityConfig;
|
|
173
168
|
@Srv() protected readonly securityService: srv.util.SecurityService;
|
|
@@ -211,7 +206,6 @@ import { Account, Arg, DbSignal, Me, Mutation, Query, resolve, Signal } from "@a
|
|
|
211
206
|
import * as cnst from "../cnst";
|
|
212
207
|
import type * as db from "../db";
|
|
213
208
|
|
|
214
|
-
@Signal(() => cnst.User)
|
|
215
209
|
export class UserSignal extends DbSignal(cnst.userCnst, cnst.Srvs, {
|
|
216
210
|
guards: { get: Query.Public, cru: Mutation.Admin },
|
|
217
211
|
}) {
|
|
@@ -584,36 +578,30 @@ export const Layout = () => (
|
|
|
584
578
|
## Best Practices
|
|
585
579
|
|
|
586
580
|
1. **Naming Conventions**
|
|
587
|
-
|
|
588
581
|
- Use PascalCase for classes and components (e.g., `UserService`, `User.Unit.tsx`)
|
|
589
582
|
- Use camelCase for files (e.g., `user.service.ts`, `user.document.ts`)
|
|
590
583
|
|
|
591
584
|
2. **Security**
|
|
592
|
-
|
|
593
585
|
- Use `@Field.Secret` for sensitive data like passwords
|
|
594
586
|
- Apply proper permission guards to queries and mutations (`@Query.Admin`, `@Mutation.Public`)
|
|
595
587
|
- Validate input data using the `validate` option in `@Field.Prop`
|
|
596
588
|
|
|
597
589
|
3. **Code Organization**
|
|
598
|
-
|
|
599
590
|
- Keep business logic in service files
|
|
600
591
|
- Use signals for API calls only, not for business logic
|
|
601
592
|
- Define reusable utility methods in document models
|
|
602
593
|
|
|
603
594
|
4. **Performance**
|
|
604
|
-
|
|
605
595
|
- Use dataloader pattern (`@Loader.ByField`) for efficient database access
|
|
606
596
|
- Create proper indexes in the middleware
|
|
607
597
|
- Use projections to limit returned fields when appropriate
|
|
608
598
|
|
|
609
599
|
5. **Testing**
|
|
610
|
-
|
|
611
600
|
- Create signal tests for each API endpoint
|
|
612
601
|
- Mock services for unit testing signals
|
|
613
602
|
- Use integration tests for testing complex workflows
|
|
614
603
|
|
|
615
604
|
6. **UI Components**
|
|
616
|
-
|
|
617
605
|
- Follow the separation between Template, Unit, View, and Zone components
|
|
618
606
|
- Make components reusable across different parts of the application
|
|
619
607
|
- Use dictionary files for all UI text to support internationalization
|
|
@@ -348,7 +348,6 @@ code: string;
|
|
|
348
348
|
### Input Models
|
|
349
349
|
|
|
350
350
|
```typescript
|
|
351
|
-
@Model.Input("ProductInput")
|
|
352
351
|
export class ProductInput {
|
|
353
352
|
@Field.Prop(() => String)
|
|
354
353
|
name: string;
|
|
@@ -361,7 +360,6 @@ export class ProductInput {
|
|
|
361
360
|
### Object Models
|
|
362
361
|
|
|
363
362
|
```typescript
|
|
364
|
-
@Model.Object("ProductObject")
|
|
365
363
|
export class ProductObject extends via(ProductInput) {
|
|
366
364
|
@Field.Prop(() => Date, { default: dayjs() })
|
|
367
365
|
createdAt: Dayjs;
|
|
@@ -371,21 +369,18 @@ export class ProductObject extends via(ProductInput) {
|
|
|
371
369
|
### Light Models
|
|
372
370
|
|
|
373
371
|
```typescript
|
|
374
|
-
@Model.Light("LightProduct")
|
|
375
372
|
export class LightProduct extends via(ProductObject, ["name", "price", "status"] as const) {}
|
|
376
373
|
```
|
|
377
374
|
|
|
378
375
|
### Full Models
|
|
379
376
|
|
|
380
377
|
```typescript
|
|
381
|
-
@Model.Full("Product")
|
|
382
378
|
export class Product extends via(ProductObject, LightProduct) {}
|
|
383
379
|
```
|
|
384
380
|
|
|
385
381
|
### Scalar Models
|
|
386
382
|
|
|
387
383
|
```typescript
|
|
388
|
-
@Model.Scalar("Address")
|
|
389
384
|
export class Address {
|
|
390
385
|
@Field.Prop(() => String)
|
|
391
386
|
street: string;
|
|
@@ -203,12 +203,10 @@ import { Project } from "./project.constant";
|
|
|
203
203
|
import { projectService } from "./project.service";
|
|
204
204
|
|
|
205
205
|
export class ProjectSignal {
|
|
206
|
-
@Signal()
|
|
207
206
|
async projectList() {
|
|
208
207
|
return projectService.getProjects();
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
@Signal()
|
|
212
210
|
async createProject(data: Partial<Project>) {
|
|
213
211
|
return projectService.createProject(data);
|
|
214
212
|
}
|
|
@@ -229,7 +227,11 @@ export const ProjectView = () => {
|
|
|
229
227
|
return (
|
|
230
228
|
<div>
|
|
231
229
|
<h1>{l("project.modelName")}</h1>
|
|
232
|
-
<div className="grid gap-4">
|
|
230
|
+
<div className="grid gap-4">
|
|
231
|
+
{data?.map((project) => (
|
|
232
|
+
<ProjectUnit key={project.id} project={project} />
|
|
233
|
+
))}
|
|
234
|
+
</div>
|
|
233
235
|
</div>
|
|
234
236
|
);
|
|
235
237
|
};
|
|
@@ -322,22 +324,18 @@ import { Task, TaskFilter } from "./task.constant";
|
|
|
322
324
|
import { taskService } from "./task.service";
|
|
323
325
|
|
|
324
326
|
export class TaskSignal {
|
|
325
|
-
@Signal()
|
|
326
327
|
async taskList(query: TaskFilter, skip = 0, limit = 20) {
|
|
327
328
|
return taskService.getTasks(query);
|
|
328
329
|
}
|
|
329
330
|
|
|
330
|
-
@Signal()
|
|
331
331
|
async task(taskId: string) {
|
|
332
332
|
return taskService.getTask(taskId);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
@Signal()
|
|
336
335
|
async createTask(data: Partial<Task>) {
|
|
337
336
|
return taskService.createTask(data);
|
|
338
337
|
}
|
|
339
338
|
|
|
340
|
-
@Signal()
|
|
341
339
|
async updateTaskStatus(taskId: string, status: Task["status"]) {
|
|
342
340
|
return taskService.updateTaskStatus(taskId, status);
|
|
343
341
|
}
|
|
@@ -581,22 +579,18 @@ import { Product, ProductFilter } from "./product.constant";
|
|
|
581
579
|
import { productService } from "./product.service";
|
|
582
580
|
|
|
583
581
|
export class ProductSignal {
|
|
584
|
-
@Signal()
|
|
585
582
|
async productList(query: ProductFilter, skip = 0, limit = 20) {
|
|
586
583
|
return productService.getProducts(query, skip, limit);
|
|
587
584
|
}
|
|
588
585
|
|
|
589
|
-
@Signal()
|
|
590
586
|
async product(productId: string) {
|
|
591
587
|
return productService.getProduct(productId);
|
|
592
588
|
}
|
|
593
589
|
|
|
594
|
-
@Signal()
|
|
595
590
|
async createProduct(data: Partial<Product>) {
|
|
596
591
|
return productService.createProduct(data);
|
|
597
592
|
}
|
|
598
593
|
|
|
599
|
-
@Signal()
|
|
600
594
|
async updateProduct(productId: string, data: Partial<Product>) {
|
|
601
595
|
return productService.updateProduct(productId, data);
|
|
602
596
|
}
|
|
@@ -625,7 +619,11 @@ export const ProductList = () => {
|
|
|
625
619
|
) : error ? (
|
|
626
620
|
<div>Error: {error.message}</div>
|
|
627
621
|
) : (
|
|
628
|
-
<ul>
|
|
622
|
+
<ul>
|
|
623
|
+
{products?.map((product) => (
|
|
624
|
+
<li key={product.id}>{product.name}</li>
|
|
625
|
+
))}
|
|
626
|
+
</ul>
|
|
629
627
|
)}
|
|
630
628
|
|
|
631
629
|
<button onClick={() => createProduct({ name: "New Product" })} disabled={isCreating}>
|
|
@@ -63,13 +63,6 @@
|
|
|
63
63
|
"filterText": "enumOf",
|
|
64
64
|
"sample": 3
|
|
65
65
|
},
|
|
66
|
-
{
|
|
67
|
-
"type": "example",
|
|
68
|
-
"description": "Models with custom Filter methods",
|
|
69
|
-
"path": "{apps,libs}/*/lib/*/*.constant.ts",
|
|
70
|
-
"filterText": "@Filter.Mongo",
|
|
71
|
-
"sample": 2
|
|
72
|
-
},
|
|
73
66
|
{
|
|
74
67
|
"type": "example",
|
|
75
68
|
"description": "Models with reference fields",
|
|
@@ -48,21 +48,21 @@ export const StatusEnum = enumOf(["active", "inactive"] as const);
|
|
|
48
48
|
export type StatusEnum = enumOf<typeof StatusEnum>;
|
|
49
49
|
|
|
50
50
|
// 3. Input Model
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
export class DroneInput {
|
|
53
53
|
@Field.Prop(() => String)
|
|
54
54
|
name: string;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// 4. Object Model
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
export class DroneObject extends BaseModel(DroneInput) {
|
|
60
60
|
@Field.Prop(() => String, { enum: StatusEnum, default: "offline" })
|
|
61
61
|
status: StatusEnum;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// 5. Light Model
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
export class LightDrone extends Light(DroneObject, ["id", "name", "status"] as const) {
|
|
67
67
|
isConnected() {
|
|
68
68
|
return this.status !== "offline";
|
|
@@ -70,7 +70,7 @@ export class LightDrone extends Light(DroneObject, ["id", "name", "status"] as c
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// 6. Full Model
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
export class Drone extends Full(DroneObject, LightDrone) {
|
|
75
75
|
isAvailable() {
|
|
76
76
|
return this.isConnected() && this.wsUri.startsWith("ws://");
|
|
@@ -78,7 +78,7 @@ export class Drone extends Full(DroneObject, LightDrone) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// 7. Insight Model
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
export class DroneInsight {
|
|
83
83
|
@Field.Prop(() => Int, { default: 0, accumulate: { $sum: 1 } })
|
|
84
84
|
count: number;
|
|
@@ -113,7 +113,6 @@ import { LightCategory } from "../category/category.constant";
|
|
|
113
113
|
The Input model defines fields required for data creation:
|
|
114
114
|
|
|
115
115
|
```typescript
|
|
116
|
-
@Model.Input("DroneInput")
|
|
117
116
|
export class DroneInput {
|
|
118
117
|
@Field.Prop(() => String)
|
|
119
118
|
name: string;
|
|
@@ -144,7 +143,6 @@ The Object model adds system-managed fields:
|
|
|
144
143
|
export const droneStatuses = ["active", "offline", "inactive"] as const;
|
|
145
144
|
export type DroneStatus = (typeof droneStatuses)[number];
|
|
146
145
|
|
|
147
|
-
@Model.Object("DroneObject")
|
|
148
146
|
export class DroneObject extends BaseModel(DroneInput) {
|
|
149
147
|
@Field.Prop(() => String, { enum: droneStatuses, default: "offline" })
|
|
150
148
|
status: DroneStatus;
|
|
@@ -172,7 +170,6 @@ const drone = st.use.drone(); // drone.status
|
|
|
172
170
|
Light model defines lightweight schema for list queries:
|
|
173
171
|
|
|
174
172
|
```typescript
|
|
175
|
-
@Model.Light("LightDrone")
|
|
176
173
|
export class LightDrone extends Light(DroneObject, ["name", "status"] as const) {
|
|
177
174
|
isConnected() {
|
|
178
175
|
return this.status !== "offline";
|
|
@@ -196,7 +193,6 @@ const droneMap = st.use.droneMap(); // Map<string, LightDrone>
|
|
|
196
193
|
Full model adds convenience functions:
|
|
197
194
|
|
|
198
195
|
```typescript
|
|
199
|
-
@Model.Full("Drone")
|
|
200
196
|
export class Drone extends Full(DroneObject, LightDrone) {
|
|
201
197
|
static isDronesAllConnected(droneList: LightDrone[]) {
|
|
202
198
|
return droneList.every((drone) => drone.isConnected());
|
|
@@ -226,7 +222,6 @@ const isAllConnected = cnst.Drone.isDroneAllConnected(droneList);
|
|
|
226
222
|
Insight model defines statistical fields:
|
|
227
223
|
|
|
228
224
|
```typescript
|
|
229
|
-
@Model.Insight("DroneInsight")
|
|
230
225
|
export class DroneInsight {
|
|
231
226
|
@Field.Prop(() => Int, { default: 0, accumulate: { $sum: 1 } })
|
|
232
227
|
count: number;
|
|
@@ -371,13 +366,11 @@ graph LR
|
|
|
371
366
|
### 1. Scalar Embedded Style
|
|
372
367
|
|
|
373
368
|
```typescript
|
|
374
|
-
@Model.Scalar("DronePhysicalState")
|
|
375
369
|
export class DronePhysicalState {
|
|
376
370
|
@Field.Prop(() => [Float], { default: [0, 0, 0] })
|
|
377
371
|
rpy: [number, number, number];
|
|
378
372
|
}
|
|
379
373
|
|
|
380
|
-
@Model.Object("DroneObject")
|
|
381
374
|
export class DroneObject {
|
|
382
375
|
@Field.Prop(() => DronePhysicalState)
|
|
383
376
|
physicalState: DronePhysicalState;
|
|
@@ -387,7 +380,6 @@ export class DroneObject {
|
|
|
387
380
|
### 2. Reference ID Style
|
|
388
381
|
|
|
389
382
|
```typescript
|
|
390
|
-
@Model.Input("MissionInput")
|
|
391
383
|
export class MissionInput {
|
|
392
384
|
@Field.Prop(() => ID, { ref: "drone" })
|
|
393
385
|
drone: string;
|
|
@@ -397,7 +389,6 @@ export class MissionInput {
|
|
|
397
389
|
### 3. Resolved Reference Style
|
|
398
390
|
|
|
399
391
|
```typescript
|
|
400
|
-
@Model.Object("DroneObject")
|
|
401
392
|
export class DroneObject {
|
|
402
393
|
@Field.Prop(() => LightMission, { nullable: true })
|
|
403
394
|
mission: LightMission | null;
|
|
@@ -417,6 +408,7 @@ import { LightMission } from "../cnst_";
|
|
|
417
408
|
## Best Practices for Maintainable Models
|
|
418
409
|
|
|
419
410
|
1. **Consistent Naming**:
|
|
411
|
+
|
|
420
412
|
- Input: `DroneInput`
|
|
421
413
|
- Object: `DroneObject`
|
|
422
414
|
- Light: `LightDrone`
|
|
@@ -424,16 +416,19 @@ import { LightMission } from "../cnst_";
|
|
|
424
416
|
- Sort: `droneSort`
|
|
425
417
|
|
|
426
418
|
2. **Reference Handling**:
|
|
419
|
+
|
|
427
420
|
- Use Light models for references
|
|
428
421
|
- Avoid circular dependencies
|
|
429
422
|
- Prefer direct imports over barrel files
|
|
430
423
|
|
|
431
424
|
3. **Field Design**:
|
|
425
|
+
|
|
432
426
|
- Use proper field types (Prop, Hidden, Secret, Resolve)
|
|
433
427
|
- Set appropriate defaults (especially for arrays)
|
|
434
428
|
- Add validation where needed
|
|
435
429
|
|
|
436
430
|
4. **Performance Optimization**:
|
|
431
|
+
|
|
437
432
|
- Use Light models for list queries
|
|
438
433
|
- Keep Insight/Summary models lean
|
|
439
434
|
- Use appropriate indexes for queries
|
|
@@ -450,21 +445,21 @@ import { ID, Int, String } from "@akanjs/base";
|
|
|
450
445
|
import { Field, Model } from "@akanjs/constant";
|
|
451
446
|
|
|
452
447
|
// Input Model
|
|
453
|
-
|
|
448
|
+
|
|
454
449
|
export class DroneInput {
|
|
455
450
|
@Field.Prop(() => String)
|
|
456
451
|
name: string;
|
|
457
452
|
}
|
|
458
453
|
|
|
459
454
|
// Object Model
|
|
460
|
-
|
|
455
|
+
|
|
461
456
|
export class DroneObject extends BaseModel(DroneInput) {
|
|
462
457
|
@Field.Prop(() => String, { enum: ["active", "offline"], default: "offline" })
|
|
463
458
|
status: string;
|
|
464
459
|
}
|
|
465
460
|
|
|
466
461
|
// Light Model
|
|
467
|
-
|
|
462
|
+
|
|
468
463
|
export class LightDrone extends Light(DroneObject, ["id", "name", "status"] as const) {
|
|
469
464
|
isConnected() {
|
|
470
465
|
return this.status === "active";
|
|
@@ -472,7 +467,7 @@ export class LightDrone extends Light(DroneObject, ["id", "name", "status"] as c
|
|
|
472
467
|
}
|
|
473
468
|
|
|
474
469
|
// Full Model
|
|
475
|
-
|
|
470
|
+
|
|
476
471
|
export class Drone extends Full(DroneObject, LightDrone) {
|
|
477
472
|
isAvailable() {
|
|
478
473
|
return this.isConnected() && this.battery > 20;
|
|
@@ -480,7 +475,7 @@ export class Drone extends Full(DroneObject, LightDrone) {
|
|
|
480
475
|
}
|
|
481
476
|
|
|
482
477
|
// Insight Model
|
|
483
|
-
|
|
478
|
+
|
|
484
479
|
export class DroneInsight {
|
|
485
480
|
@Field.Prop(() => Int, { accumulate: { $sum: 1 } })
|
|
486
481
|
count: number;
|
|
@@ -16,21 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
A `model.document.ts` file is structured into four distinct classes, each with a specific responsibility:
|
|
18
18
|
|
|
19
|
-
### 1. Input Class
|
|
20
|
-
|
|
21
|
-
Defines the data structure for create/update operations:
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
@Database.Input(() => cnst.ExampleInput)
|
|
25
|
-
export class ExampleInput extends by(cnst.ExampleInput) {}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
19
|
### 2. Document Class
|
|
29
20
|
|
|
30
21
|
Represents a MongoDB document with instance methods:
|
|
31
22
|
|
|
32
23
|
```typescript
|
|
33
|
-
@Database.Document(() => cnst.Example)
|
|
34
24
|
export class Example extends by(cnst.Example) {
|
|
35
25
|
// Document methods here
|
|
36
26
|
}
|
|
@@ -41,7 +31,6 @@ export class Example extends by(cnst.Example) {
|
|
|
41
31
|
Represents the MongoDB collection with static methods:
|
|
42
32
|
|
|
43
33
|
```typescript
|
|
44
|
-
@Database.Model(() => cnst.Example)
|
|
45
34
|
export class ExampleModel extends into(Example, cnst.exampleCnst) {
|
|
46
35
|
// Model statics here
|
|
47
36
|
}
|
|
@@ -52,7 +41,6 @@ export class ExampleModel extends into(Example, cnst.exampleCnst) {
|
|
|
52
41
|
Defines schema hooks and indexes:
|
|
53
42
|
|
|
54
43
|
```typescript
|
|
55
|
-
@Database.Middleware(() => cnst.Example)
|
|
56
44
|
export class ExampleMiddleware extends beyond(ExampleModel, Example) {
|
|
57
45
|
onSchema(schema) {
|
|
58
46
|
// Middleware and indexes here
|
|
@@ -74,7 +62,6 @@ Document methods operate on individual document instances and typically follow t
|
|
|
74
62
|
### Example of Document Methods:
|
|
75
63
|
|
|
76
64
|
```typescript
|
|
77
|
-
@Database.Document(() => cnst.User)
|
|
78
65
|
export class User extends by(cnst.User) {
|
|
79
66
|
// Simple transformation method (synchronous)
|
|
80
67
|
addRole(role: string) {
|
|
@@ -113,7 +100,6 @@ Model statics operate on the entire collection and are typically asynchronous. T
|
|
|
113
100
|
### Example of Model Statics:
|
|
114
101
|
|
|
115
102
|
```typescript
|
|
116
|
-
@Database.Model(() => cnst.Order)
|
|
117
103
|
export class OrderModel extends into(Order, cnst.orderCnst) {
|
|
118
104
|
// Query by specific criteria
|
|
119
105
|
async findPendingOrders(userId: string) {
|
|
@@ -206,7 +192,6 @@ Middleware allows you to intercept and modify document operations at various lif
|
|
|
206
192
|
### Example Middleware Implementation:
|
|
207
193
|
|
|
208
194
|
```typescript
|
|
209
|
-
@Database.Middleware(() => cnst.User)
|
|
210
195
|
export class UserMiddleware extends beyond(UserModel, User) {
|
|
211
196
|
onSchema(schema: SchemaOf<UserModel, User>) {
|
|
212
197
|
// Pre-save hook for password hashing
|
|
@@ -258,7 +243,6 @@ Indexes are crucial for query performance. Define them in the Middleware's `onSc
|
|
|
258
243
|
### Example Index Implementation:
|
|
259
244
|
|
|
260
245
|
```typescript
|
|
261
|
-
@Database.Middleware(() => cnst.Product)
|
|
262
246
|
export class ProductMiddleware extends beyond(ProductModel, Product) {
|
|
263
247
|
onSchema(schema: SchemaOf<ProductModel, Product>) {
|
|
264
248
|
// Single field index (1=ascending, -1=descending)
|
|
@@ -346,7 +330,6 @@ DataLoader batches and caches database requests to optimize performance, especia
|
|
|
346
330
|
### Example DataLoader Implementation:
|
|
347
331
|
|
|
348
332
|
```typescript
|
|
349
|
-
@Database.Model(() => cnst.Order)
|
|
350
333
|
export class OrderModel extends into(Order, cnst.orderCnst) {
|
|
351
334
|
// Load orders by user ID
|
|
352
335
|
@Loader.ByField("userId")
|
|
@@ -461,7 +444,6 @@ const resultCount = await productModel.searchCountProduct("wireless headphones")
|
|
|
461
444
|
Use transactions for operations that need to be atomic:
|
|
462
445
|
|
|
463
446
|
```typescript
|
|
464
|
-
@Database.Model(() => cnst.Order)
|
|
465
447
|
export class OrderModel extends into(Order, cnst.orderCnst) {
|
|
466
448
|
@Transaction()
|
|
467
449
|
async createOrderWithItems(orderData, items) {
|
|
@@ -497,10 +479,6 @@ import { dayjs } from "@akanjs/base";
|
|
|
497
479
|
import { beyond, by, Database, into, Loader, SchemaOf } from "@akanjs/document";
|
|
498
480
|
import * as cnst from "../cnst";
|
|
499
481
|
|
|
500
|
-
@Database.Input(() => cnst.ProductInput)
|
|
501
|
-
export class ProductInput extends by(cnst.ProductInput) {}
|
|
502
|
-
|
|
503
|
-
@Database.Document(() => cnst.Product)
|
|
504
482
|
export class Product extends by(cnst.Product) {
|
|
505
483
|
applyDiscount(percentage: number) {
|
|
506
484
|
if (percentage < 0 || percentage > 100) {
|
|
@@ -529,7 +507,6 @@ export class Product extends by(cnst.Product) {
|
|
|
529
507
|
}
|
|
530
508
|
}
|
|
531
509
|
|
|
532
|
-
@Database.Model(() => cnst.Product)
|
|
533
510
|
export class ProductModel extends into(Product, cnst.productCnst) {
|
|
534
511
|
@Loader.ByField("category")
|
|
535
512
|
categoryProductsLoader: Loader<string, Product[]>;
|
|
@@ -579,7 +556,6 @@ export class ProductModel extends into(Product, cnst.productCnst) {
|
|
|
579
556
|
}
|
|
580
557
|
}
|
|
581
558
|
|
|
582
|
-
@Database.Middleware(() => cnst.Product)
|
|
583
559
|
export class ProductMiddleware extends beyond(ProductModel, Product) {
|
|
584
560
|
onSchema(schema: SchemaOf<ProductModel, Product>) {
|
|
585
561
|
// Update stock status on save
|
|
@@ -24,13 +24,6 @@
|
|
|
24
24
|
"path": "libs/shared/lib/user/user.service.ts",
|
|
25
25
|
"sample": 1
|
|
26
26
|
},
|
|
27
|
-
{
|
|
28
|
-
"type": "example",
|
|
29
|
-
"description": "@Service decorator examples",
|
|
30
|
-
"path": "{apps,libs}/*/lib/*/*.service.ts",
|
|
31
|
-
"filterText": "@Service",
|
|
32
|
-
"sample": 2
|
|
33
|
-
},
|
|
34
27
|
{
|
|
35
28
|
"type": "example",
|
|
36
29
|
"description": "@Use decorator examples",
|
|
@@ -89,9 +82,9 @@
|
|
|
89
82
|
},
|
|
90
83
|
{
|
|
91
84
|
"type": "example",
|
|
92
|
-
"description": "
|
|
85
|
+
"description": "serve examples",
|
|
93
86
|
"path": "{apps,libs}/*/lib/*/*.service.ts",
|
|
94
|
-
"filterText": "
|
|
87
|
+
"filterText": "serve",
|
|
95
88
|
"sample": 1
|
|
96
89
|
},
|
|
97
90
|
{
|
|
@@ -113,8 +106,8 @@
|
|
|
113
106
|
"filePath": "./modelService.instruction.md",
|
|
114
107
|
"contents": [
|
|
115
108
|
"Purpose and role of model.service.ts files",
|
|
116
|
-
"Service structure and inheritance (DbService,
|
|
117
|
-
"Core decorators (@
|
|
109
|
+
"Service structure and inheritance (DbService, serve, MixSrvs)",
|
|
110
|
+
"Core decorators (@Srv, @Use, @Db, @Sig)",
|
|
118
111
|
"Database operations (CRUD, queries, search)",
|
|
119
112
|
"Customizing database operations (_preCreate, _postCreate, _preUpdate, _postUpdate, _preRemove, _postRemove)",
|
|
120
113
|
"Working with other services (dependency injection patterns)",
|