@akanjs/cli 0.9.42 → 0.9.43

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.
Files changed (59) hide show
  1. package/cjs/index.js +16 -6
  2. package/cjs/src/guidelines/___library/sharedUiStructureDescription.en.md +0 -1
  3. package/cjs/src/guidelines/modelService/modelService.generate.json +0 -30
  4. package/cjs/src/guidelines/modelService/modelService.instruction.md +0 -164
  5. package/cjs/src/templates/app/lib/___appName__/__appName__.dictionary.js +6 -1
  6. package/cjs/src/templates/app/lib/___appName__/__appName__.service.js +1 -8
  7. package/cjs/src/templates/app/lib/___appName__/__appName__.signal.js +34 -0
  8. package/cjs/src/templates/app/lib/summary/Summary.Zone.js +1 -21
  9. package/cjs/src/templates/app/lib/user/User.Util.js +1 -26
  10. package/cjs/src/templates/app/lib/user/User.Zone.js +2 -30
  11. package/cjs/src/templates/app/lib/user/user.document.js +1 -7
  12. package/cjs/src/templates/app/lib/user/user.service.js +2 -8
  13. package/cjs/src/templates/lib/__lib/lib.service.js +1 -1
  14. package/cjs/src/templates/lib/cnst_.js +5 -2
  15. package/cjs/src/templates/lib/fetch.js +1 -1
  16. package/cjs/src/templates/lib/store.js +0 -1
  17. package/cjs/src/templates/libRoot/lib/summary/Summary.Zone.js +1 -21
  18. package/cjs/src/templates/libRoot/lib/user/User.Util.js +1 -26
  19. package/cjs/src/templates/libRoot/lib/user/User.Zone.js +2 -29
  20. package/cjs/src/templates/libRoot/lib/user/user.document.js +1 -7
  21. package/cjs/src/templates/libRoot/lib/user/user.service.js +2 -8
  22. package/cjs/src/templates/module/__Model__.Util.js +6 -36
  23. package/cjs/src/templates/module/__Model__.Zone.js +2 -26
  24. package/cjs/src/templates/module/__model__.document.js +1 -7
  25. package/cjs/src/templates/module/__model__.service.js +1 -7
  26. package/cjs/src/templates/workspaceRoot/.vscode/settings.json.template +0 -1
  27. package/esm/index.js +16 -6
  28. package/esm/src/guidelines/___library/sharedUiStructureDescription.en.md +0 -1
  29. package/esm/src/guidelines/modelService/modelService.generate.json +0 -30
  30. package/esm/src/guidelines/modelService/modelService.instruction.md +0 -164
  31. package/esm/src/templates/app/lib/___appName__/__appName__.dictionary.js +6 -1
  32. package/esm/src/templates/app/lib/___appName__/__appName__.service.js +1 -8
  33. package/esm/src/templates/app/lib/___appName__/__appName__.signal.js +14 -0
  34. package/esm/src/templates/app/lib/summary/Summary.Zone.js +1 -21
  35. package/esm/src/templates/app/lib/user/User.Util.js +1 -26
  36. package/esm/src/templates/app/lib/user/User.Zone.js +2 -30
  37. package/esm/src/templates/app/lib/user/user.document.js +1 -7
  38. package/esm/src/templates/app/lib/user/user.service.js +2 -8
  39. package/esm/src/templates/lib/__lib/lib.service.js +1 -1
  40. package/esm/src/templates/lib/cnst_.js +5 -2
  41. package/esm/src/templates/lib/fetch.js +1 -1
  42. package/esm/src/templates/lib/store.js +0 -1
  43. package/esm/src/templates/libRoot/lib/summary/Summary.Zone.js +1 -21
  44. package/esm/src/templates/libRoot/lib/user/User.Util.js +1 -26
  45. package/esm/src/templates/libRoot/lib/user/User.Zone.js +2 -29
  46. package/esm/src/templates/libRoot/lib/user/user.document.js +1 -7
  47. package/esm/src/templates/libRoot/lib/user/user.service.js +2 -8
  48. package/esm/src/templates/module/__Model__.Util.js +6 -36
  49. package/esm/src/templates/module/__Model__.Zone.js +2 -26
  50. package/esm/src/templates/module/__model__.document.js +1 -7
  51. package/esm/src/templates/module/__model__.service.js +1 -7
  52. package/esm/src/templates/workspaceRoot/.vscode/settings.json.template +0 -1
  53. package/package.json +1 -1
  54. package/src/guidelines/___library/sharedUiStructureDescription.en.md +0 -1
  55. package/src/guidelines/modelService/modelService.instruction.md +0 -164
  56. package/src/templates/app/{app/[lang]/admin/page.d.ts → lib/___appName__/__appName__.signal.d.ts} +2 -4
  57. package/ui/MultiScrollList.d.ts +4 -1
  58. package/cjs/src/templates/app/app/[lang]/admin/page.js +0 -63
  59. package/esm/src/templates/app/app/[lang]/admin/page.js +0 -43
@@ -27,36 +27,9 @@ function getContent(scanInfo, dict) {
27
27
  filename: "User.Zone.tsx",
28
28
  content: `
29
29
  "use client";
30
- import { ModelsProps } from "@akanjs/client";
31
- import { DefaultOf } from "@akanjs/signal";
32
- import { cnst, st, User } from "@${dict.libName}/client";
33
- import { Data, Model } from "@akanjs/ui";
30
+ import { st, User } from "@${dict.libName}/client";
31
+ import { Model } from "@akanjs/ui";
34
32
 
35
- export const Admin = ({ sliceName = "user", init, query }: ModelsProps<cnst.User>) => {
36
- return (
37
- <Data.ListContainer
38
- init={init}
39
- query={query}
40
- sliceName={sliceName}
41
- renderItem={User.Unit.Card}
42
- renderDashboard={User.Util.Stat}
43
- renderInsight={User.Util.Insight}
44
- renderTemplate={User.Template.General}
45
- renderTitle={(user: DefaultOf<cnst.User>) => \`\${user.nickname}\`}
46
- renderView={(user: cnst.User) => <User.View.General user={user} />}
47
- type="list"
48
- columns={["nickname", "status", "lastLoginAt", "createdAt"]}
49
- actions={(user: cnst.LightUser, idx) => [
50
- "remove",
51
- "edit",
52
- "view",
53
- user.status === "active"
54
- ? { type: "restrict", render: () => <User.Util.Restrict id={user.id} /> }
55
- : { type: "release", render: () => <User.Util.Release id={user.id} /> },
56
- ]}
57
- />
58
- );
59
- };
60
33
 
61
34
  interface SelfProps {
62
35
  className?: string;
@@ -36,13 +36,7 @@ export class UserInput extends by(rootUser.Input, cnst.UserInput) {}
36
36
  export class User extends by(rootUser.Doc, cnst.User) {}
37
37
 
38
38
  @Database.Model(() => cnst.User)
39
- export class UserModel extends into(User, cnst.userCnst) {
40
- async getSummary(): Promise<cnst.UserSummary> {
41
- return {
42
- ...(await this.getDefaultSummary()),
43
- };
44
- }
45
- }
39
+ export class UserModel extends into(User, cnst.userCnst) {}
46
40
 
47
41
  @Database.Middleware(() => cnst.User)
48
42
  export class UserMiddleware extends beyond(UserModel, User) {
@@ -31,12 +31,6 @@ import { cnst } from "../cnst";
31
31
  import * as db from "../db";
32
32
 
33
33
  @Service("UserService")
34
- export class UserService extends ExtendedUserService(db.userDb, LibUserService) {
35
- async summarize${dict.LibName}(): Promise<cnst.${dict.LibName}UserSummary> {
36
- return {
37
- ...(await this.userModel.getSummary()),
38
- };
39
- }
40
- }
41
- `;
34
+ export class UserService extends ExtendedUserService(db.userDb, LibUserService) {}
35
+ `;
42
36
  }
@@ -27,43 +27,13 @@ function getContent(scanInfo, dict) {
27
27
  filename: `${dict.Model}.Util.tsx`,
28
28
  content: `
29
29
  "use client";
30
- import { ModelDashboardProps, ModelInsightProps } from "@akanjs/client";
31
- import { getQueryMap } from "@akanjs/constant";
32
- import { cnst } from "@${dict.sysName}/client";
33
- import { Data } from "@akanjs/ui";
30
+ import { Model } from "@akanjs/ui";
34
31
 
35
- export const Stat = ({
36
- className,
37
- summary,
38
- sliceName = "${dict.model}",
39
- queryMap = getQueryMap(cnst.${dict.Model}Summary),
40
- hidePresents,
41
- }: ModelDashboardProps<cnst.Summary>) => {
42
- return (
43
- <Data.Dashboard
44
- className={className}
45
- summary={summary}
46
- sliceName={sliceName}
47
- queryMap={queryMap}
48
- columns={["total${dict.Model}"]}
49
- hidePresents={hidePresents}
50
- />
51
- );
52
- };
53
-
54
- export const Insight = ({
55
- className,
56
- insight,
57
- sliceName = "${dict.model}",
58
- }: ModelInsightProps<cnst.${dict.Model}Insight>) => {
59
- return (
60
- <Data.Insight
61
- className={className}
62
- insight={insight}
63
- sliceName={sliceName}
64
- columns={["count"]}
65
- />
66
- );
32
+ interface RemoveProps {
33
+ ${dict.model}Id: string;
34
+ }
35
+ export const Remove = ({ ${dict.model}Id }: RemoveProps) => {
36
+ return <Model.Remove modelId={${dict.model}Id} sliceName="${dict.model}" />;
67
37
  };
68
38
  `
69
39
  };
@@ -27,33 +27,9 @@ function getContent(scanInfo, dict) {
27
27
  filename: `${dict.Model}.Zone.tsx`,
28
28
  content: `
29
29
  "use client";
30
- import { Data, Load } from "@akanjs/ui";
31
- import { ModelsProps } from "@akanjs/client";
30
+ import { Load } from "@akanjs/ui";
32
31
  import { cnst, ${dict.Model} } from "@${dict.sysName}/client";
33
- import { ClientInit, ClientView, DefaultOf } from "@akanjs/signal";
34
-
35
- export const Admin = ({ sliceName = "${dict.model}", init, query }: ModelsProps<cnst.${dict.Model}>) => {
36
- return (
37
- <Data.ListContainer
38
- init={init}
39
- query={query}
40
- sliceName={sliceName}
41
- renderItem={${dict.Model}.Unit.Card}
42
- renderDashboard={${dict.Model}.Util.Stat}
43
- renderInsight={${dict.Model}.Util.Insight}
44
- renderTemplate={${dict.Model}.Template.General}
45
- renderTitle={(${dict.model}: DefaultOf<cnst.${dict.Model}>) => \`${dict.Model} - \${${dict.model}.id ? ${dict.model}.id : "New"}\`}
46
- renderView={(${dict.model}: cnst.${dict.Model}) => <${dict.Model}.View.General ${dict.model}={${dict.model}} />}
47
- columns={[
48
- "id",
49
- "status",
50
- "createdAt",
51
- "updatedAt",
52
- ]}
53
- actions={(${dict.model}: cnst.Light${dict.Model}, idx) => ["remove", "edit", "view"]}
54
- />
55
- );
56
- };
32
+ import { ClientInit, ClientView } from "@akanjs/signal";
57
33
 
58
34
  interface CardProps {
59
35
  className?: string;
@@ -35,13 +35,7 @@ export class ${dict.Model}Input extends by(cnst.${dict.Model}Input) {}
35
35
  export class ${dict.Model} extends by(cnst.${dict.Model}) {}
36
36
 
37
37
  @Database.Model(() => cnst.${dict.Model})
38
- export class ${dict.Model}Model extends into(${dict.Model}, cnst.${dict.model}Cnst) {
39
- async getSummary(): Promise<cnst.${dict.Model}Summary> {
40
- return {
41
- ...(await this.getDefaultSummary()),
42
- };
43
- }
44
- }
38
+ export class ${dict.Model}Model extends into(${dict.Model}, cnst.${dict.model}Cnst) {}
45
39
 
46
40
  @Database.Middleware(() => cnst.${dict.Model})
47
41
  export class ${dict.Model}Middleware extends beyond(${dict.Model}Model, ${dict.Model}) {
@@ -30,12 +30,6 @@ import { cnst } from "../cnst";
30
30
  import * as db from "../db";
31
31
 
32
32
  @Service("${dict.Model}Service")
33
- export class ${dict.Model}Service extends DbService(db.${dict.model}Db) {
34
- async summarize(): Promise<cnst.${dict.Model}Summary> {
35
- return {
36
- ...(await this.${dict.model}Model.getSummary()),
37
- };
38
- }
39
- }
33
+ export class ${dict.Model}Service extends DbService(db.${dict.model}Db) {}
40
34
  `;
41
35
  }
@@ -2,7 +2,6 @@
2
2
  "testing.saveBeforeTest": false,
3
3
  "jest.autoRun": "false",
4
4
  "jestTestExplorer.flattenExplorer": false,
5
- "eslint.validate": ["json"],
6
5
  "prettier.configPath": ".prettierrc.json",
7
6
  "tailwindCSS.classAttributes": ["class", "className", ".*Class", ".*ClassName"],
8
7
  "colorize.languages": ["typescript", "javascript", "css", "scss"],
package/esm/index.js CHANGED
@@ -3941,6 +3941,7 @@ var MultiScrollList = ({ logList, maxLength = 100 }) => {
3941
3941
  const [boxHeight, setBoxHeight] = useState2(
3942
3942
  height - HEADER_HEIGHT - OUTER_BORDER_HEIGHT - FOOTER_HEIGHT - BORDER_HEIGHT
3943
3943
  );
3944
+ const [boxWidth, setBoxWidth] = useState2(width - 28);
3944
3945
  const getLimitedLogs = (logs) => {
3945
3946
  return logs.length > maxLength ? logs.slice(logs.length - maxLength) : logs;
3946
3947
  };
@@ -3992,6 +3993,15 @@ var MultiScrollList = ({ logList, maxLength = 100 }) => {
3992
3993
  useEffect2(() => {
3993
3994
  const getLogsToRender = (logs, index) => {
3994
3995
  const limitedLogs = getLimitedLogs(logs);
3996
+ const logWidth = limitedLogs.reduce((acc, log) => acc + log.content.length, 0);
3997
+ if (logWidth > boxWidth) {
3998
+ return limitedLogs.map((log) => {
3999
+ return {
4000
+ type: log.type,
4001
+ content: log.content.slice(0, boxWidth)
4002
+ };
4003
+ });
4004
+ }
3995
4005
  if (scrollPos > 0 && tabIndex === index) {
3996
4006
  return limitedLogs.slice(limitedLogs.length - boxHeight - scrollPos, limitedLogs.length - scrollPos);
3997
4007
  } else if (limitedLogs.length > boxHeight) {
@@ -4032,9 +4042,9 @@ var MultiScrollList = ({ logList, maxLength = 100 }) => {
4032
4042
  height: "100%"
4033
4043
  },
4034
4044
  scrollPos > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, focusLog.slice(0, focusLog.length - 1).map((log, index) => {
4035
- return /* @__PURE__ */ React.createElement(Text, { key: index }, log);
4045
+ return /* @__PURE__ */ React.createElement(Text, { color: log.type === "error" ? "red" : "white", key: index }, log.content);
4036
4046
  }), /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "Scrolling... +", scrollPos)) : focusLog.map((log, index) => {
4037
- return /* @__PURE__ */ React.createElement(Text, { key: index }, log);
4047
+ return /* @__PURE__ */ React.createElement(Text, { color: log.type === "error" ? "red" : "white", key: index }, log.content);
4038
4048
  })
4039
4049
  ))), /* @__PURE__ */ React.createElement(Box, { width: "100%", height: FOOTER_HEIGHT }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, null, "You can use the following shortcuts:"), /* @__PURE__ */ React.createElement(Newline, null), "* ", /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "tab"), " to switch tab.", /* @__PURE__ */ React.createElement(Newline, null), "* ", /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "up"), " and ", /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "down"), " to scroll.", " ", /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "shift"), " to scroll faster. (x10)", /* @__PURE__ */ React.createElement(Newline, null), "* ", /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "space"), " to scroll.", /* @__PURE__ */ React.createElement(Newline, null), "* ", /* @__PURE__ */ React.createElement(Text, { backgroundColor: "green" }, "escape"), " to stop scrolling.")));
4040
4050
  };
@@ -4157,17 +4167,17 @@ var Start = ({ appName, bcp, fcp, csr, maxLength = 100 }) => {
4157
4167
  logList: [
4158
4168
  {
4159
4169
  title: `${appName} frontend`,
4160
- logs: frontendLogs.map((log) => log.content),
4170
+ logs: frontendLogs,
4161
4171
  color: "#ff493b"
4162
4172
  },
4163
4173
  {
4164
4174
  title: `${appName} backend`,
4165
- logs: backendLogs.map((log) => log.content),
4175
+ logs: backendLogs,
4166
4176
  color: "#e535ab"
4167
4177
  },
4168
4178
  {
4169
4179
  title: `${appName} react`,
4170
- logs: csrLogs.map((log) => log.content),
4180
+ logs: csrLogs,
4171
4181
  color: "#7cc5d9"
4172
4182
  }
4173
4183
  ],
@@ -6281,7 +6291,7 @@ var WorkspaceRunner = class {
6281
6291
  ...Object.fromEntries(devDependencies.map((dependency) => [dependency, latestPublishedVersionOfBase]))
6282
6292
  }
6283
6293
  };
6284
- workspace.writeFile("package.json", packageJson);
6294
+ workspace.setPackageJson(packageJson);
6285
6295
  const installSpinner = workspace.spinning("Installing dependencies with pnpm...");
6286
6296
  await workspace.spawn("pnpm", ["install", "--reporter=silent"]);
6287
6297
  installSpinner.succeed("Dependencies installed with pnpm");
@@ -733,7 +733,6 @@ SlateContent
733
733
  Purpose: Metadata-driven property editing
734
734
 
735
735
  - prop: string - Property name
736
- - slice: StoreOf<any, any> - Store slice
737
736
  - renderTemplate?: (form: any) => ReactNode - Custom edit renderer
738
737
  - renderView?: (model: any) => ReactNode - Custom view renderer
739
738
  - modelPath?: string - Model path in store
@@ -52,34 +52,6 @@
52
52
  "filterText": "@Sig",
53
53
  "sample": 1
54
54
  },
55
- {
56
- "type": "example",
57
- "description": "onModuleInit method examples",
58
- "path": "{apps,libs}/*/lib/*/*.service.ts",
59
- "filterText": "onModuleInit",
60
- "sample": 2
61
- },
62
- {
63
- "type": "example",
64
- "description": "onModuleDestroy method examples",
65
- "path": "{apps,libs}/*/lib/*/*.service.ts",
66
- "filterText": "onModuleDestroy",
67
- "sample": 1
68
- },
69
- {
70
- "type": "example",
71
- "description": "@Cron decorator examples",
72
- "path": "{apps,libs}/*/lib/*/*.service.ts",
73
- "filterText": "@Cron",
74
- "sample": 2
75
- },
76
- {
77
- "type": "example",
78
- "description": "@Interval decorator examples",
79
- "path": "{apps,libs}/*/lib/*/*.service.ts",
80
- "filterText": "@Interval",
81
- "sample": 1
82
- },
83
55
  {
84
56
  "type": "example",
85
57
  "description": "DbService _preCreate examples",
@@ -143,8 +115,6 @@
143
115
  "Purpose and role of model.service.ts files",
144
116
  "Service structure and inheritance (DbService, LogService, MixSrvs)",
145
117
  "Core decorators (@Service, @Srv, @Use, @Db, @Sig)",
146
- "Lifecycle methods (onModuleInit, onModuleDestroy)",
147
- "Scheduled tasks (@Cron, @Interval)",
148
118
  "Database operations (CRUD, queries, search)",
149
119
  "Customizing database operations (_preCreate, _postCreate, _preUpdate, _postUpdate, _preRemove, _postRemove)",
150
120
  "Working with other services (dependency injection patterns)",
@@ -136,97 +136,6 @@ Injects a websocket server instance for real-time communication:
136
136
  protected readonly websocket: Websocket<ChatSignal>;
137
137
  ```
138
138
 
139
- ## Lifecycle Methods
140
-
141
- Akan.js services can implement lifecycle hooks that are called by the NestJS framework.
142
-
143
- ### onModuleInit()
144
-
145
- Called once the module has been initialized by NestJS. Use this for setup operations:
146
-
147
- ```typescript
148
- async onModuleInit() {
149
- // Initialize service resources
150
- await this.loadInitialData();
151
- this.setupEventListeners();
152
- this.logger.log('Service initialized successfully');
153
- }
154
- ```
155
-
156
- Common use cases:
157
-
158
- - Initializing in-memory cache
159
- - Setting up event listeners
160
- - Loading configuration data
161
- - Establishing connections to external services
162
-
163
- ### onModuleDestroy()
164
-
165
- Called just before the application shuts down. Use this for cleanup operations:
166
-
167
- ```typescript
168
- async onModuleDestroy() {
169
- // Clean up resources
170
- await Promise.all(this.connections.map(conn => conn.close()));
171
- this.clearTimers();
172
- this.logger.log('Service resources released');
173
- }
174
- ```
175
-
176
- Common use cases:
177
-
178
- - Closing database connections
179
- - Releasing external resources
180
- - Clearing timers and intervals
181
- - Unregistering event listeners
182
-
183
- ## Scheduled Tasks
184
-
185
- Akan.js provides decorators for scheduling recurring tasks.
186
-
187
- ### @Cron Decorator
188
-
189
- Executes methods on a cron schedule:
190
-
191
- ```typescript
192
- @Cron("0 0 * * *") // Run at midnight every day
193
- async dailyCleanup() {
194
- await this.cleanupExpiredRecords();
195
- this.logger.log('Daily cleanup completed');
196
- }
197
-
198
- @Cron("*/5 * * * *", { serverMode: "batch" }) // Run every 5 minutes, only in batch mode
199
- async periodicSync() {
200
- await this.syncExternalData();
201
- }
202
- ```
203
-
204
- The first parameter uses standard cron syntax:
205
-
206
- - `* * * * *` = minute hour day-of-month month day-of-week
207
- - Special patterns like `@daily`, `@hourly` are also supported
208
-
209
- Options include:
210
-
211
- - `serverMode`: Restrict to a specific server mode (e.g., "batch")
212
- - `name`: Assign a name to the scheduled job
213
- - `timeZone`: Specify the timezone for the schedule
214
-
215
- ### @Interval Decorator
216
-
217
- Executes methods at fixed time intervals in milliseconds:
218
-
219
- ```typescript
220
- @Interval(60000) // Run every minute (60,000 ms)
221
- async checkStatus() {
222
- await this.monitorSystemHealth();
223
- }
224
- ```
225
-
226
- Options:
227
-
228
- - `name`: Assign a name to the interval
229
-
230
139
  ## Database Operations
231
140
 
232
141
  When extending `DbService`, your service automatically inherits numerous methods for working with the database model.
@@ -606,34 +515,6 @@ class PaymentService extends DbService(db.paymentDb) {
606
515
  }
607
516
  ```
608
517
 
609
- ### 3. Proper Resource Management
610
-
611
- Clean up resources in lifecycle hooks:
612
-
613
- ```typescript
614
- @Service("ExternalApiService")
615
- class ExternalApiService extends LogService("ExternalApiService") {
616
- private client: ApiClient;
617
- private timers: NodeJS.Timeout[] = [];
618
-
619
- async onModuleInit() {
620
- this.client = new ApiClient();
621
- await this.client.connect();
622
-
623
- this.timers.push(setInterval(() => this.refreshToken(), 3600000));
624
- }
625
-
626
- async onModuleDestroy() {
627
- // Clean up resources
628
- await this.client.disconnect();
629
-
630
- for (const timer of this.timers) {
631
- clearInterval(timer);
632
- }
633
- }
634
- }
635
- ```
636
-
637
518
  ### 4. Transaction Safety
638
519
 
639
520
  Use hooks for validating operations:
@@ -818,25 +699,6 @@ export class OrderService extends DbService(db.orderDb) {
818
699
 
819
700
  return data;
820
701
  }
821
-
822
- // Scheduled task
823
- @Cron("0 0 * * *", { serverMode: "batch" })
824
- async cleanupAbandonedOrders() {
825
- const cutoffDate = dayjs().subtract(7, "days");
826
- const orders = await this.listPendingOrders(cutoffDate);
827
-
828
- for (const order of orders) {
829
- await this.cancelOrder(order.id);
830
- this.logger.log(`Cancelled abandoned order: ${order.id}`);
831
- }
832
- }
833
-
834
- // Summary method
835
- async summarize(): Promise<cnst.OrderSummary> {
836
- return {
837
- ...(await this.orderModel.getSummary()),
838
- };
839
- }
840
702
  }
841
703
  ```
842
704
 
@@ -894,32 +756,6 @@ If a service isn't being injected correctly:
894
756
  }
895
757
  ```
896
758
 
897
- ### Scheduled Tasks Not Running
898
-
899
- 1. Verify cron syntax:
900
-
901
- ```typescript
902
- // Correct syntax
903
- @Cron("0 * * * *") // Every hour at minute 0
904
-
905
- // Incorrect syntax
906
- @Cron("0 * * *") // Missing day-of-week field
907
- ```
908
-
909
- 2. Check server mode restrictions:
910
-
911
- ```typescript
912
- // Only runs in batch mode
913
- @Cron("0 * * * *", { serverMode: "batch" })
914
- ```
915
-
916
- 3. Ensure the service is properly initialized:
917
- ```typescript
918
- onModuleInit() {
919
- this.logger.log('Service initialized'); // Is this being logged?
920
- }
921
- ```
922
-
923
759
  ## Summary
924
760
 
925
761
  Model services in Akan.js are powerful components that encapsulate business logic and data operations. By following the patterns and best practices outlined in this guide, you can create maintainable, scalable services that effectively implement your application's domain logic while maintaining clean separation of concerns.
@@ -1,9 +1,14 @@
1
1
  // pkgs/@akanjs/cli/src/templates/app/lib/___appName__/__appName__.dictionary.ts
2
2
  function getContent(scanInfo, dict) {
3
3
  return `
4
+ import type { SignalDictionary } from "@akanjs/dictionary";
5
+ import type { ${dict.AppName}Signal } from "./${dict.appName}.signal";
6
+
4
7
  const dictionary = {} as const;
5
8
 
6
- export const ${dict.appName}Dictionary = dictionary;
9
+ const signalDictionary = {} satisfies SignalDictionary<${dict.AppName}Signal>;
10
+
11
+ export const ${dict.appName}Dictionary = { ...dictionary, ...signalDictionary };
7
12
  `;
8
13
  }
9
14
  export {
@@ -7,14 +7,7 @@ import { LogService, Service, Srv } from "@akanjs/service";
7
7
  import type * as srv from "../srv";
8
8
 
9
9
  @Service("${dict.appName}Service", { serverMode: "batch" })
10
- export class ${dict.AppName}Service extends LogService("${dict.appName}Service") {
11
- @Srv() protected readonly summaryService: srv.SummaryService;
12
-
13
- @Cron("0 * * * *")
14
- async takePeriodicSnapshot() {
15
- await this.summaryService.makeSummary();
16
- }
17
- }
10
+ export class ${dict.AppName}Service extends LogService("${dict.appName}Service") {}
18
11
  `;
19
12
  }
20
13
  export {
@@ -0,0 +1,14 @@
1
+ // pkgs/@akanjs/cli/src/templates/app/lib/___appName__/__appName__.signal.ts
2
+ function getContent(scanInfo, dict) {
3
+ return `
4
+ import { LogSignal, Signal } from "@akanjs/signal";
5
+
6
+ import { Srvs } from "../cnst";
7
+
8
+ @Signal({ name: "${dict.AppName}" })
9
+ export class ${dict.AppName}Signal extends LogSignal(Srvs) {}
10
+ `;
11
+ }
12
+ export {
13
+ getContent as default
14
+ };
@@ -4,29 +4,9 @@ function getContent(scanInfo, dict) {
4
4
  filename: "Summary.Zone.tsx",
5
5
  content: `
6
6
  "use client";
7
- import { ModelsProps } from "@akanjs/client";
8
- import { DefaultOf } from "@akanjs/signal";
9
- import { cnst, st, Summary } from "@${dict.appName}/client";
10
- import { Data } from "@akanjs/ui";
7
+ import { st, Summary } from "@${dict.appName}/client";
11
8
  import { Loading } from "@akanjs/ui";
12
9
 
13
- export const Admin = ({ sliceName = "summary", init, query }: ModelsProps<cnst.Summary>) => {
14
- return (
15
- <Data.ListContainer
16
- init={init}
17
- query={query}
18
- sliceName={sliceName}
19
- renderItem={Summary.Unit.Card}
20
- renderTemplate={Summary.Template.General}
21
- renderTitle={(summary: DefaultOf<cnst.Summary>) => \`\${summary.at}\`}
22
- renderView={(summary: cnst.Summary) => <Summary.View.General summary={summary} />}
23
- type="list"
24
- columns={["type", "status", "createdAt"]}
25
- actions={(summary: cnst.LightSummary, idx) => ["remove", "edit"]}
26
- />
27
- );
28
- };
29
-
30
10
  export const Dashboard = () => {
31
11
  const summary = st.use.summary();
32
12
  if (!summary) return <Loading.Skeleton active />;
@@ -4,34 +4,9 @@ function getContent(scanInfo, dict) {
4
4
  filename: "User.Util.tsx",
5
5
  content: `
6
6
  "use client";
7
- import { clsx, ModelDashboardProps, ModelInsightProps } from "@akanjs/client";
8
- import { getQueryMap } from "@akanjs/constant";
9
- import { cnst, st } from "@${dict.appName}/client";
10
- import { Data } from "@akanjs/ui";
7
+ import { clsx, st } from "@${dict.appName}/client";
11
8
  import { AiOutlineEdit, AiOutlineLock, AiOutlineUnlock } from "react-icons/ai";
12
9
 
13
- export const Stat = ({
14
- className,
15
- summary,
16
- sliceName = "user",
17
- queryMap = getQueryMap(cnst.UserSummary),
18
- hidePresents,
19
- }: ModelDashboardProps<cnst.Summary>) => {
20
- return (
21
- <Data.Dashboard
22
- className={clsx("", className)}
23
- summary={summary}
24
- sliceName={sliceName}
25
- queryMap={queryMap}
26
- columns={["totalUser", "dau"]}
27
- hidePresents={hidePresents}
28
- />
29
- );
30
- };
31
- export const Insight = ({ className, insight, sliceName = "user" }: ModelInsightProps<cnst.UserInsight>) => {
32
- return <Data.Insight className={clsx("", className)} insight={insight} sliceName={sliceName} columns={["count"]} />;
33
- };
34
-
35
10
  interface EditSelfProps {
36
11
  className?: string;
37
12
  }
@@ -4,36 +4,8 @@ function getContent(scanInfo, dict) {
4
4
  filename: "User.Zone.tsx",
5
5
  content: `
6
6
  "use client";
7
- import { ModelsProps } from "@akanjs/client";
8
- import { DefaultOf } from "@akanjs/signal";
9
- import { cnst, st, User } from "@${dict.appName}/client";
10
- import { Data, Model } from "@akanjs/ui";
11
-
12
- export const Admin = ({ sliceName = "user", init, query }: ModelsProps<cnst.User>) => {
13
- return (
14
- <Data.ListContainer
15
- init={init}
16
- query={query}
17
- sliceName={sliceName}
18
- renderItem={User.Unit.Card}
19
- renderDashboard={User.Util.Stat}
20
- renderInsight={User.Util.Insight}
21
- renderTemplate={User.Template.General}
22
- renderTitle={(user: DefaultOf<cnst.User>) => \`\${user.nickname}\`}
23
- renderView={(user: cnst.User) => <User.View.General user={user} />}
24
- type="list"
25
- columns={["nickname", "status", "lastLoginAt", "createdAt"]}
26
- actions={(user: cnst.LightUser, idx) => [
27
- "remove",
28
- "edit",
29
- "view",
30
- user.status === "active"
31
- ? { type: "restrict", render: () => <User.Util.Restrict id={user.id} /> }
32
- : { type: "release", render: () => <User.Util.Release id={user.id} /> },
33
- ]}
34
- />
35
- );
36
- };
7
+ import { st, User } from "@${dict.appName}/client";
8
+ import { Model } from "@akanjs/ui";
37
9
 
38
10
  interface SelfProps {
39
11
  className?: string;