@akanjs/cli 0.9.47 → 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 +4 -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 +4 -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
|
@@ -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)",
|
|
@@ -23,7 +23,6 @@ Akan.js services typically extend base service classes provided by the framework
|
|
|
23
23
|
Most services extend `DbService` to work with MongoDB models:
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
@Service("ProductService")
|
|
27
26
|
export class ProductService extends DbService(db.productDb) {
|
|
28
27
|
// Service methods here
|
|
29
28
|
}
|
|
@@ -31,13 +30,12 @@ export class ProductService extends DbService(db.productDb) {
|
|
|
31
30
|
|
|
32
31
|
The `DbService` function automatically injects the database model and provides numerous convenience methods for CRUD operations.
|
|
33
32
|
|
|
34
|
-
###
|
|
33
|
+
### serve
|
|
35
34
|
|
|
36
35
|
For utility services that don't directly work with a database model:
|
|
37
36
|
|
|
38
37
|
```typescript
|
|
39
|
-
|
|
40
|
-
export class SecurityService extends LogService("SecurityService") {
|
|
38
|
+
export class SecurityService extends serve("security" as const) {
|
|
41
39
|
// Utility methods here
|
|
42
40
|
}
|
|
43
41
|
```
|
|
@@ -51,7 +49,7 @@ For specific domain services with shared functionality:
|
|
|
51
49
|
export const LibService = MixSrvs(SharedService, PlatformService, SocialService);
|
|
52
50
|
|
|
53
51
|
// In another service
|
|
54
|
-
|
|
52
|
+
|
|
55
53
|
export class CustomService extends LibService {
|
|
56
54
|
// Custom methods here
|
|
57
55
|
}
|
|
@@ -59,20 +57,6 @@ export class CustomService extends LibService {
|
|
|
59
57
|
|
|
60
58
|
## Core Decorators
|
|
61
59
|
|
|
62
|
-
### @Service Decorator
|
|
63
|
-
|
|
64
|
-
Declares a class as an injectable NestJS service with additional configuration options:
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
@Service("UserService", {
|
|
68
|
-
enabled: true, // Enable/disable service dynamically
|
|
69
|
-
serverMode: "federation", // "federation", "batch", or not specified
|
|
70
|
-
})
|
|
71
|
-
export class UserService extends DbService(db.userDb) {
|
|
72
|
-
// Service implementation
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
60
|
Options:
|
|
77
61
|
|
|
78
62
|
- `enabled`: Boolean flag to conditionally disable a service (default: true)
|
|
@@ -263,7 +247,6 @@ async _postRemove(doc: Product): Promise<Product> {
|
|
|
263
247
|
Services often work together to implement complex operations:
|
|
264
248
|
|
|
265
249
|
```typescript
|
|
266
|
-
@Service("OrderService")
|
|
267
250
|
export class OrderService extends DbService(db.orderDb) {
|
|
268
251
|
@Srv() productService: ProductService;
|
|
269
252
|
@Srv() userService: UserService;
|
|
@@ -322,7 +305,6 @@ export class OrderService extends DbService(db.orderDb) {
|
|
|
322
305
|
For real-time features:
|
|
323
306
|
|
|
324
307
|
```typescript
|
|
325
|
-
@Service("ChatService")
|
|
326
308
|
export class ChatService extends DbService(db.chatDb) {
|
|
327
309
|
@Websocket()
|
|
328
310
|
websocket: Websocket<ChatSignal>;
|
|
@@ -354,8 +336,7 @@ export class ChatService extends DbService(db.chatDb) {
|
|
|
354
336
|
For background processing:
|
|
355
337
|
|
|
356
338
|
```typescript
|
|
357
|
-
|
|
358
|
-
export class EmailService extends LogService("EmailService") {
|
|
339
|
+
export class EmailService extends serve("email" as const) {
|
|
359
340
|
@Queue()
|
|
360
341
|
emailQueue: Queue<EmailSignal>;
|
|
361
342
|
@Use()
|
|
@@ -441,7 +422,7 @@ Services should not maintain internal state between requests:
|
|
|
441
422
|
|
|
442
423
|
```typescript
|
|
443
424
|
// BAD - maintains state in the service
|
|
444
|
-
|
|
425
|
+
|
|
445
426
|
class CounterService {
|
|
446
427
|
private count = 0; // This state is lost on server restart
|
|
447
428
|
|
|
@@ -454,7 +435,7 @@ class CounterService {
|
|
|
454
435
|
}
|
|
455
436
|
|
|
456
437
|
// GOOD - uses database for state
|
|
457
|
-
|
|
438
|
+
|
|
458
439
|
class CounterService extends DbService(db.counterDb) {
|
|
459
440
|
async increment(counterId: string) {
|
|
460
441
|
const counter = await this.getCounter(counterId);
|
|
@@ -474,7 +455,7 @@ Each service should focus on a specific domain:
|
|
|
474
455
|
|
|
475
456
|
```typescript
|
|
476
457
|
// BAD - mixing concerns
|
|
477
|
-
|
|
458
|
+
|
|
478
459
|
class UserService extends DbService(db.userDb) {
|
|
479
460
|
async createUser(data) {
|
|
480
461
|
// User creation logic
|
|
@@ -490,7 +471,7 @@ class UserService extends DbService(db.userDb) {
|
|
|
490
471
|
}
|
|
491
472
|
|
|
492
473
|
// GOOD - separate services
|
|
493
|
-
|
|
474
|
+
|
|
494
475
|
class UserService extends DbService(db.userDb) {
|
|
495
476
|
@Srv() emailService: EmailService;
|
|
496
477
|
@Srv() paymentService: PaymentService;
|
|
@@ -500,14 +481,12 @@ class UserService extends DbService(db.userDb) {
|
|
|
500
481
|
}
|
|
501
482
|
}
|
|
502
483
|
|
|
503
|
-
|
|
504
|
-
class EmailService extends LogService("EmailService") {
|
|
484
|
+
class EmailService extends serve("email" as const) {
|
|
505
485
|
async sendEmail(userId, subject, content) {
|
|
506
486
|
// Email sending logic
|
|
507
487
|
}
|
|
508
488
|
}
|
|
509
489
|
|
|
510
|
-
@Service("PaymentService")
|
|
511
490
|
class PaymentService extends DbService(db.paymentDb) {
|
|
512
491
|
async processPayment(userId, amount) {
|
|
513
492
|
// Payment processing logic
|
|
@@ -546,13 +525,12 @@ Inject other services using decorators, not direct imports:
|
|
|
546
525
|
// BAD - direct import
|
|
547
526
|
import { UserService } from "../user/user.service";
|
|
548
527
|
|
|
549
|
-
@Service("OrderService")
|
|
550
528
|
class OrderService extends DbService(db.orderDb) {
|
|
551
529
|
private userService = new UserService(); // Hard-coded dependency
|
|
552
530
|
}
|
|
553
531
|
|
|
554
532
|
// GOOD - dependency injection
|
|
555
|
-
|
|
533
|
+
|
|
556
534
|
class OrderService extends DbService(db.orderDb) {
|
|
557
535
|
@Srv() userService: UserService; // Injected dependency
|
|
558
536
|
}
|
|
@@ -563,7 +541,6 @@ class OrderService extends DbService(db.orderDb) {
|
|
|
563
541
|
Use the built-in logger for consistent logging:
|
|
564
542
|
|
|
565
543
|
```typescript
|
|
566
|
-
@Service("PaymentService")
|
|
567
544
|
class PaymentService extends DbService(db.paymentDb) {
|
|
568
545
|
async processPayment(orderId: string, amount: number) {
|
|
569
546
|
this.logger.log(`Processing payment of ${amount} for order ${orderId}`);
|
|
@@ -581,20 +558,6 @@ class PaymentService extends DbService(db.paymentDb) {
|
|
|
581
558
|
}
|
|
582
559
|
```
|
|
583
560
|
|
|
584
|
-
## Integration with NestJS
|
|
585
|
-
|
|
586
|
-
Akan.js services are built on top of NestJS's dependency injection system. The `@Service` decorator builds on top of NestJS's `@Injectable()`:
|
|
587
|
-
|
|
588
|
-
```typescript
|
|
589
|
-
// Behind the scenes, @Service does this:
|
|
590
|
-
@Injectable()
|
|
591
|
-
export class UserService {
|
|
592
|
-
// ...
|
|
593
|
-
}
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
This allows Akan.js services to be used within NestJS modules, controllers, and resolvers.
|
|
597
|
-
|
|
598
561
|
## Complete Example
|
|
599
562
|
|
|
600
563
|
Here's a complete example of a service implementation:
|
|
@@ -602,7 +565,7 @@ Here's a complete example of a service implementation:
|
|
|
602
565
|
```typescript
|
|
603
566
|
import { Dayjs } from "@akanjs/base";
|
|
604
567
|
import { Cron } from "@akanjs/nest";
|
|
605
|
-
import { DbService,
|
|
568
|
+
import { DbService, Srv, Use } from "@akanjs/service";
|
|
606
569
|
import type { EmailApi } from "@util/nest";
|
|
607
570
|
|
|
608
571
|
import * as cnst from "../cnst";
|
|
@@ -610,7 +573,6 @@ import * as db from "../db";
|
|
|
610
573
|
import { Revert } from "../dict";
|
|
611
574
|
import type * as srv from "../srv";
|
|
612
575
|
|
|
613
|
-
@Service("OrderService")
|
|
614
576
|
export class OrderService extends DbService(db.orderDb) {
|
|
615
577
|
@Srv() userService: srv.UserService;
|
|
616
578
|
@Srv() productService: srv.ProductService;
|
|
@@ -704,31 +666,6 @@ export class OrderService extends DbService(db.orderDb) {
|
|
|
704
666
|
|
|
705
667
|
## Troubleshooting
|
|
706
668
|
|
|
707
|
-
### Service Not Being Injected
|
|
708
|
-
|
|
709
|
-
If a service isn't being injected correctly:
|
|
710
|
-
|
|
711
|
-
1. Ensure the service name matches:
|
|
712
|
-
|
|
713
|
-
```typescript
|
|
714
|
-
@Service("UserService") // This name must match
|
|
715
|
-
export class UserService extends DbService(db.userDb) {}
|
|
716
|
-
|
|
717
|
-
@Srv("UserService") // Or this must match if using explicit name
|
|
718
|
-
userService: UserService;
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
2. Check if the service is enabled in the current environment:
|
|
722
|
-
|
|
723
|
-
```typescript
|
|
724
|
-
@Service("UserService", { enabled: process.env.ENABLE_USER_SERVICE === 'true' })
|
|
725
|
-
```
|
|
726
|
-
|
|
727
|
-
3. Verify server mode restrictions:
|
|
728
|
-
```typescript
|
|
729
|
-
@Service("CleanupService", { serverMode: "batch" }) // Only runs in batch mode
|
|
730
|
-
```
|
|
731
|
-
|
|
732
669
|
### Database Operations Failing
|
|
733
670
|
|
|
734
671
|
1. Check model definition and schema:
|
|
@@ -763,7 +700,7 @@ Model services in Akan.js are powerful components that encapsulate business logi
|
|
|
763
700
|
Key takeaways:
|
|
764
701
|
|
|
765
702
|
- Services should be stateless, with persistent state managed in the database
|
|
766
|
-
- Use the appropriate decorator (@
|
|
703
|
+
- Use the appropriate decorator (@Srv, @Use, etc.) for dependency injection
|
|
767
704
|
- Implement lifecycle hooks for proper resource management
|
|
768
705
|
- Use scheduled tasks for recurring operations
|
|
769
706
|
- Follow single responsibility principle
|
|
@@ -176,7 +176,6 @@
|
|
|
176
176
|
"contents": [
|
|
177
177
|
"Purpose and role of model.signal.ts files",
|
|
178
178
|
"Signal class structure and core components",
|
|
179
|
-
"@Signal decorator and Signal class definition",
|
|
180
179
|
"DbSignal and other higher-order functions",
|
|
181
180
|
"Endpoint decorators (@Query, @Mutation, @Message, @Process, @Pubsub)",
|
|
182
181
|
"Parameter decorators (@Arg.Query, @Arg.Param, @Arg.Body, @Self, etc.)",
|
|
@@ -26,7 +26,6 @@ import { Arg, DbSignal, Mutation, Query, resolve, Self, Signal } from "@akanjs/s
|
|
|
26
26
|
import * as cnst from "../cnst";
|
|
27
27
|
import type * as db from "../db";
|
|
28
28
|
|
|
29
|
-
@Signal(() => cnst.ModelName)
|
|
30
29
|
export class ModelNameSignal extends DbSignal(cnst.modelNameCnst, cnst.Srvs, {
|
|
31
30
|
guards: { get: Query.Public, cru: Mutation.User },
|
|
32
31
|
}) {
|
|
@@ -36,36 +35,7 @@ export class ModelNameSignal extends DbSignal(cnst.modelNameCnst, cnst.Srvs, {
|
|
|
36
35
|
|
|
37
36
|
Key components:
|
|
38
37
|
|
|
39
|
-
-
|
|
40
|
-
- `DbSignal()` higher-order function provides standard CRUD operations
|
|
41
|
-
- `guards` option configures default access control for CRUD operations
|
|
42
|
-
- Service references are injected from `Srvs` imported from cnst.ts
|
|
43
|
-
|
|
44
|
-
## Decorator Reference
|
|
45
|
-
|
|
46
|
-
### 1. @Query Decorator
|
|
47
|
-
|
|
48
|
-
Used for read-only operations with access control levels:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
@Query.Public(() => [cnst.Product]) // Public access, returns array of Products
|
|
52
|
-
async productListInFeatured(
|
|
53
|
-
@Arg.Query("skip", () => Int, { nullable: true }) skip: number | null,
|
|
54
|
-
@Arg.Query("limit", () => Int, { nullable: true }) limit: number | null,
|
|
55
|
-
@Arg.Query("sort", () => String, { nullable: true }) sort: SortOf<cnst.ProductFilter> | null
|
|
56
|
-
) {
|
|
57
|
-
const products = await this.productService.listFeatured({ skip, limit, sort });
|
|
58
|
-
return resolve<cnst.Product[]>(products); // Type-safe resolution
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Access control levels:
|
|
63
|
-
|
|
64
|
-
- `Query.Public` - Available to unauthenticated users
|
|
65
|
-
- `Query.User` - Requires user authentication
|
|
66
|
-
- `Query.Admin` - Requires admin access
|
|
67
|
-
- `Query.SuperAdmin` - Requires super admin access
|
|
68
|
-
- `Query.Every` - Available to all authenticated users (any role)
|
|
38
|
+
- `
|
|
69
39
|
|
|
70
40
|
### 2. @Mutation Decorator
|
|
71
41
|
|
|
@@ -169,7 +139,7 @@ The slice pattern standardizes data access with consistent naming and pagination
|
|
|
169
139
|
async chatRoomListInSelf( // listIn<SliceName> pattern
|
|
170
140
|
@Arg.Query("skip", () => Int, { nullable: true }) skip: number | null,
|
|
171
141
|
@Arg.Query("limit", () => Int, { nullable: true }) limit: number | null,
|
|
172
|
-
@Arg.Query("sort", () => String, { nullable: true }) sort: SortOf<
|
|
142
|
+
@Arg.Query("sort", () => String, { nullable: true }) sort: SortOf<db.ChatRoomFilter> | null,
|
|
173
143
|
@Self() self: Self
|
|
174
144
|
) {
|
|
175
145
|
const chatRooms = await this.chatRoomService.listInUser(self.id, { skip, limit, sort });
|
|
@@ -288,7 +258,6 @@ function ChatRoom({ roomId }) {
|
|
|
288
258
|
DbSignal creates standard operations that you don't need to implement:
|
|
289
259
|
|
|
290
260
|
```typescript
|
|
291
|
-
@Signal(() => cnst.Board)
|
|
292
261
|
export class BoardSignal extends DbSignal(cnst.boardCnst, cnst.Srvs, {
|
|
293
262
|
guards: { get: Query.Public, cru: Mutation.Admin },
|
|
294
263
|
}) {
|
|
@@ -408,7 +377,6 @@ async getProjectDetails(
|
|
|
408
377
|
### Basic CRUD Signal with DbSignal
|
|
409
378
|
|
|
410
379
|
```typescript
|
|
411
|
-
@Signal(() => cnst.Product)
|
|
412
380
|
export class ProductSignal extends DbSignal(cnst.productCnst, cnst.Srvs, {
|
|
413
381
|
guards: { get: Query.Public, cru: Mutation.Admin },
|
|
414
382
|
}) {
|
|
@@ -436,7 +404,6 @@ export class ProductSignal extends DbSignal(cnst.productCnst, cnst.Srvs, {
|
|
|
436
404
|
### Real-Time Chat Signal
|
|
437
405
|
|
|
438
406
|
```typescript
|
|
439
|
-
@Signal(() => cnst.Chat)
|
|
440
407
|
export class ChatSignal {
|
|
441
408
|
@Mutation.User(() => cnst.Chat)
|
|
442
409
|
async sendChat(
|
|
@@ -464,7 +431,6 @@ export class ChatSignal {
|
|
|
464
431
|
### Background Processing Signal
|
|
465
432
|
|
|
466
433
|
```typescript
|
|
467
|
-
@Signal(() => cnst.Report)
|
|
468
434
|
export class ReportSignal {
|
|
469
435
|
@Mutation.Admin(() => cnst.Report)
|
|
470
436
|
async generateReport(
|
|
@@ -543,7 +509,6 @@ export function SelfSlice<T>(returnType: () => T) {
|
|
|
543
509
|
### Signal Integration with External APIs
|
|
544
510
|
|
|
545
511
|
```typescript
|
|
546
|
-
@Signal(() => cnst.ExternalData)
|
|
547
512
|
export class ExternalApiSignal {
|
|
548
513
|
@Query.Public(() => cnst.WeatherData)
|
|
549
514
|
async getWeatherData(@Arg.Query("location", () => String) location: string) {
|
|
@@ -564,7 +529,6 @@ export class ExternalApiSignal {
|
|
|
564
529
|
Signals can implement caching for performance:
|
|
565
530
|
|
|
566
531
|
```typescript
|
|
567
|
-
@Signal(() => cnst.Product)
|
|
568
532
|
export class ProductSignal extends DbSignal(cnst.productCnst, Srvs) {
|
|
569
533
|
@Query.Public(() => cnst.Product)
|
|
570
534
|
async getProduct(@Arg.Param("productId", () => ID) productId: string) {
|
|
@@ -37,7 +37,6 @@ import { stateOf, Store } from "@akanjs/store";
|
|
|
37
37
|
import * as cnst from "../cnst";
|
|
38
38
|
import { fetch } from "../sig";
|
|
39
39
|
|
|
40
|
-
@Store(() => cnst.Product)
|
|
41
40
|
export class ProductStore extends stateOf(fetch.productGql, {
|
|
42
41
|
// Custom state properties
|
|
43
42
|
featuredProducts: [] as cnst.LightProduct[],
|
|
@@ -359,7 +358,7 @@ For complex applications, compose multiple stores:
|
|
|
359
358
|
// In store.ts
|
|
360
359
|
export class RootStore extends MixStore(ProductStore, CategoryStore, VendorStore, CartStore) {}
|
|
361
360
|
|
|
362
|
-
export const
|
|
361
|
+
export const storeRoot = rootStoreOf(RootStore);
|
|
363
362
|
```
|
|
364
363
|
|
|
365
364
|
### Cross-Store Operations
|
|
@@ -489,7 +488,6 @@ import * as cnst from "../cnst";
|
|
|
489
488
|
import { fetch } from "../sig";
|
|
490
489
|
import { msg } from "../msg";
|
|
491
490
|
|
|
492
|
-
@Store(() => cnst.Product)
|
|
493
491
|
export class ProductStore extends stateOf(fetch.productGql, {
|
|
494
492
|
// UI State
|
|
495
493
|
productViewMode: "grid" as "grid" | "list",
|