@akanjs/cli 3.0.0-alpha.8 → 3.0.0-alpha.81

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 (152) hide show
  1. package/.build-stamp +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/{abstractCompactor-e5yq60qz.js → abstractCompactor-pwc6d6yk.js} +3 -5
  5. package/{agent.command-pggr92jj.js → agent.command-180hh0ye.js} +11 -12
  6. package/{application.command-692yap1d.js → application.command-4fq5pmdd.js} +59 -35
  7. package/{applicationBuildRunner-ha3y8ew8.js → applicationBuildRunner-tavpx3fv.js} +22 -20
  8. package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-ktjt0erc.js} +8 -9
  9. package/buildBatch.proc.js +12 -13
  10. package/capacitorApp-kwa67kzs.js +12 -0
  11. package/{cloud.command-wqfsyzv2.js → cloud.command-wm3mtfve.js} +15 -17
  12. package/commandManifest.json +1 -1
  13. package/{context.command-9bxtdn0w.js → context.command-dk2m2sjx.js} +19 -22
  14. package/dependencyScanner-c64m6fxp.js +8 -0
  15. package/getDirname-dr3jz1xg.js +7 -0
  16. package/{guideline.command-nfqahxex.js → guideline.command-w6ddv13r.js} +16 -17
  17. package/guidelines/agentRule/agentRule.generate.json +31 -0
  18. package/guidelines/agentRule/agentRule.instruction.md +525 -0
  19. package/guidelines/conventions/conventions.generate.json +25 -0
  20. package/guidelines/conventions/conventions.instruction.md +847 -0
  21. package/guidelines/cssRule/cssRule.instruction.md +7 -0
  22. package/guidelines/framework/framework.generate.json +3 -2
  23. package/guidelines/framework/framework.instruction.md +1 -9
  24. package/guidelines/mcpRule/mcpRule.generate.json +25 -0
  25. package/guidelines/mcpRule/mcpRule.instruction.md +217 -0
  26. package/guidelines/queryRule/queryRule.generate.json +31 -0
  27. package/guidelines/queryRule/queryRule.instruction.md +143 -0
  28. package/guidelines/runtimeRule/runtimeRule.generate.json +25 -0
  29. package/guidelines/runtimeRule/runtimeRule.instruction.md +176 -0
  30. package/guidelines/ssrRule/ssrRule.instruction.md +49 -5
  31. package/guidelines/transportRule/transportRule.generate.json +31 -0
  32. package/guidelines/transportRule/transportRule.instruction.md +60 -0
  33. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +24 -0
  34. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +263 -0
  35. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
  36. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +402 -0
  37. package/incrementalBuilder.proc.js +21 -22
  38. package/{index-4hek879p.js → index-1kzsn94d.js} +1 -1
  39. package/{index-p3f4q532.js → index-1tgp56ke.js} +31 -30
  40. package/index-3d9y066c.js +76 -0
  41. package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
  42. package/{index-gex7c8kd.js → index-5n0hf8r9.js} +65 -38
  43. package/{index-mssqn2sq.js → index-5y7qba1c.js} +79 -33
  44. package/index-6wwzs1bc.js +7 -0
  45. package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
  46. package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
  47. package/{index-azv0xjb5.js → index-a8h184wr.js} +15 -11
  48. package/{index-67546d0j.js → index-ayy5ecax.js} +1 -1
  49. package/{index-xhgtkekh.js → index-b2nt0w0k.js} +27 -27
  50. package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
  51. package/{index-bz2sfzzt.js → index-cd5x18qh.js} +32 -32
  52. package/{index-bjpxzr6s.js → index-ctphds8v.js} +1 -1
  53. package/{index-w6wbb612.js → index-dq6wq1tv.js} +94 -61
  54. package/{index-6rhabeev.js → index-ft1sfjz0.js} +36 -25
  55. package/{index-7myvn0f0.js → index-h7yw2z9n.js} +7 -7
  56. package/{index-5pmx7bkg.js → index-j9vnq8tr.js} +18 -18
  57. package/{index-vg0sj9nc.js → index-k1n0h7dk.js} +840 -201
  58. package/{index-vtj3zps1.js → index-k86zc21w.js} +57 -57
  59. package/{index-5dw57f0d.js → index-m7e0f1sy.js} +155 -76
  60. package/index-m9c7k0gm.js +159 -0
  61. package/{index-46tjzh6s.js → index-mfm35dhh.js} +29 -29
  62. package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
  63. package/{index-ztpw544k.js → index-qcracvc9.js} +8 -8
  64. package/{index-2c3e2xh8.js → index-saheq2bs.js} +4 -4
  65. package/{index-gmy45vz2.js → index-v0s24t8j.js} +14 -14
  66. package/{index-0cj2zxbm.js → index-wet2ytnr.js} +12 -11
  67. package/{index-j9cxndc0.js → index-x23688ar.js} +12 -7
  68. package/{index-a1e0j63x.js → index-xxe90kqy.js} +58 -25
  69. package/{index-js368gk9.js → index-ypvdw8nr.js} +13 -13
  70. package/{index-qb61h0k7.js → index-z3c9mmw4.js} +1098 -1360
  71. package/index.js +22 -24
  72. package/{library.command-md9715jz.js → library.command-wpedct6c.js} +11 -9
  73. package/{localRegistry.command-exkbamyj.js → localRegistry.command-gk773cte.js} +20 -22
  74. package/{module.command-w5aaddxk.js → module.command-8n4kct3e.js} +13 -15
  75. package/{package.command-yfkrcy0z.js → package.command-fcbd0b1d.js} +8 -9
  76. package/package.json +5 -7
  77. package/{page.command-echbavp4.js → page.command-x4379grq.js} +7 -8
  78. package/{primitive.command-9fbhspw2.js → primitive.command-8etmnqhq.js} +13 -15
  79. package/{quality.command-3pnt9yep.js → quality.command-hfx7z8yr.js} +226 -104
  80. package/recipeScanner-fpagtgc1.js +11 -0
  81. package/{repair.command-carj4vax.js → repair.command-j0fs5qkz.js} +12 -13
  82. package/{routeSourceValidator-e4jmbfcx.js → routeSourceValidator-ze6x894c.js} +0 -2
  83. package/{scalar.command-51ryzypc.js → scalar.command-2emk89eh.js} +11 -13
  84. package/subspace.command-3w3s1zas.js +714 -0
  85. package/templates/app/env/env.client.debug.ts.template +1 -5
  86. package/templates/app/env/env.client.develop.ts.template +1 -5
  87. package/templates/app/env/env.client.local.ts.template +1 -5
  88. package/templates/app/env/env.client.main.ts.template +1 -5
  89. package/templates/app/env/env.client.testing.ts.template +1 -5
  90. package/templates/app/env/env.client.type.ts +2 -6
  91. package/templates/app/env/env.server.debug.ts.template +1 -5
  92. package/templates/app/env/env.server.develop.ts.template +1 -5
  93. package/templates/app/env/env.server.local.ts.template +1 -5
  94. package/templates/app/env/env.server.main.ts.template +1 -5
  95. package/templates/app/main.ts +3 -1
  96. package/templates/appSample/lib/task/task.signal.ts +11 -6
  97. package/templates/appSample/srvkit/AuthGuard.ts +9 -5
  98. package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
  99. package/templates/crudPages/[__model__Id]/edit/page.tsx +6 -6
  100. package/templates/crudPages/[__model__Id]/page.tsx +10 -13
  101. package/templates/crudPages/new/page.tsx +6 -6
  102. package/templates/crudPages/page.tsx +9 -9
  103. package/templates/crudSinglePage/page.tsx +11 -7
  104. package/templates/env/_env.server.type.ts +7 -5
  105. package/templates/lib/db.ts +1 -1
  106. package/templates/lib/dict.ts +1 -1
  107. package/templates/lib/sig.ts +1 -1
  108. package/templates/lib/srv.ts +6 -2
  109. package/templates/module/__Model__.Util.tsx +1 -1
  110. package/templates/module/__Model__.Zone.tsx +3 -5
  111. package/templates/module/__model__.dictionary.ts +4 -1
  112. package/templates/module/__model__.signal.ts +2 -3
  113. package/templates/module/__model__.store.ts +1 -2
  114. package/templates/workspaceRoot/.gitignore.template +0 -1
  115. package/templates/workspaceRoot/AGENTS.md.template +4 -972
  116. package/templates/workspaceRoot/CLAUDE.md.template +19 -0
  117. package/templates/workspaceRoot/biome.json.template +4 -244
  118. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +10 -14
  119. package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
  120. package/templates/workspaceRoot/package.json.template +2 -4
  121. package/typeChecker-3xgrzc2v.js +7 -0
  122. package/typecheck.proc.js +4 -5
  123. package/{workflow.command-91wmvm8k.js → workflow.command-cvpmzwjg.js} +21 -23
  124. package/{workspace.command-wa10mfd9.js → workspace.command-4ztb4fdh.js} +108 -91
  125. package/capacitorApp-p0z5yab2.js +0 -60
  126. package/dependencyScanner-f6grrep3.js +0 -9
  127. package/index-mxvakhsm.js +0 -66
  128. package/index-r24hmh0q.js +0 -4
  129. package/recipeScanner-tghkzrrt.js +0 -12
  130. package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
  131. package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -34
  132. package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
  133. package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
  134. package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -18
  135. package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
  136. package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
  137. package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
  138. package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
  139. package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -19
  140. package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -62
  141. package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
  142. package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
  143. package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
  144. package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -99
  145. package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
  146. package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
  147. package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
  148. package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +0 -59
  149. package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
  150. package/typeChecker-kravn7ns.js +0 -8
  151. /package/{index-0wae5ebk.js → index-meh558jy.js} +0 -0
  152. /package/{index-ss469dec.js → index-nz8prx9g.js} +0 -0
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { AppClientEnv } from "./env.client.type";
4
2
 
5
- export const env: AppClientEnv = {
6
- ...getEnv(),
7
- } as const;
3
+ export const env: AppClientEnv = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { AppClientEnv } from "./env.client.type";
4
2
 
5
- export const env: AppClientEnv = {
6
- ...getEnv(),
7
- } as const;
3
+ export const env: AppClientEnv = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { AppClientEnv } from "./env.client.type";
4
2
 
5
- export const env: AppClientEnv = {
6
- ...getEnv(),
7
- } as const;
3
+ export const env: AppClientEnv = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { AppClientEnv } from "./env.client.type";
4
2
 
5
- export const env: AppClientEnv = {
6
- ...getEnv(),
7
- } as const;
3
+ export const env: AppClientEnv = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { AppClientEnv } from "./env.client.type";
4
2
 
5
- export const env: AppClientEnv = {
6
- ...getEnv(),
7
- } as const;
3
+ export const env: AppClientEnv = {};
@@ -5,12 +5,8 @@ interface Dict {
5
5
  }
6
6
  export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict = {}) {
7
7
  return `
8
- import type { ClientEnv } from "akanjs/base";
9
-
10
- export type AppClientEnv = ClientEnv & {
11
- cloudflare?: {
12
- siteKey: string;
13
- };
8
+ export type AppClientEnv = {
9
+ // apply public envs here
14
10
  };
15
11
  `;
16
12
  }
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { ModulesOptions } from "../lib/option";
4
2
 
5
- export const env: ModulesOptions = {
6
- ...getEnv(),
7
- };
3
+ export const env: ModulesOptions = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { ModulesOptions } from "../lib/option";
4
2
 
5
- export const env: ModulesOptions = {
6
- ...getEnv(),
7
- };
3
+ export const env: ModulesOptions = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { ModulesOptions } from "../lib/option";
4
2
 
5
- export const env: ModulesOptions = {
6
- ...getEnv(),
7
- };
3
+ export const env: ModulesOptions = {};
@@ -1,7 +1,3 @@
1
- import { getEnv } from "akanjs/base";
2
-
3
1
  import type { ModulesOptions } from "../lib/option";
4
2
 
5
- export const env: ModulesOptions = {
6
- ...getEnv(),
7
- };
3
+ export const env: ModulesOptions = {};
@@ -5,7 +5,9 @@ interface Dict {
5
5
  }
6
6
  export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict) {
7
7
  return `
8
- import { AkanApp } from "akanjs/server";
8
+ // Not the \`akanjs/server\` barrel: it re-exports AkanServer, whose graph the gateway never runs. Through the
9
+ // barrel this process evaluates 35MB of SSR renderer and DB driver to spawn children and relay bytes.
10
+ import { AkanApp } from "akanjs/server/akanApp";
9
11
 
10
12
  const run = async () => {
11
13
  await new AkanApp().start();
@@ -1,7 +1,8 @@
1
1
  import type { AppInfo, LibInfo } from "akanjs";
2
2
 
3
3
  export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
- return `import { endpoint, internal, Public, slice } from "akanjs/signal";
4
+ return `import { SignedIn } from "@apps/${dict.appName}/srvkit";
5
+ import { endpoint, internal, None, Public, slice } from "akanjs/signal";
5
6
 
6
7
  import * as cnst from "../cnst";
7
8
  import * as srv from "../srv";
@@ -21,20 +22,24 @@ import * as srv from "../srv";
21
22
 
22
23
  export class TaskInternal extends internal(srv.task, ({ interval }) => ({})) {}
23
24
 
24
- export class TaskSlice extends slice(srv.task, { guards: { root: Public, get: Public, cru: Public } }, (init) => ({
25
+ // Guards, per key: root = the generated admin API (runs any filter with caller-supplied args), get = reads,
26
+ // cru = create/update/remove. root and cru are never Public; None here means "nobody until you name a guard".
27
+ export class TaskSlice extends slice(srv.task, { guards: { root: None, get: Public, cru: SignedIn } }, (init) => ({
25
28
  inPublic: init().exec(function () {
26
29
  return this.taskService.queryAny();
27
30
  }),
28
31
  })) {}
29
32
 
33
+ // Every custom endpoint names its own guards array — the slice's map above only covers generated CRUD.
34
+ // It is also the MCP decision: an endpoint declaring no guard is not published to agents at all.
30
35
  export class TaskEndpoint extends endpoint(srv.task, ({ mutation }) => ({
31
- startTask: mutation(cnst.Task)
36
+ startTask: mutation(cnst.Task, { guards: [SignedIn] })
32
37
  .param("taskId", String)
33
38
  .exec(async function (taskId) {
34
39
  return await this.taskService.startTask(taskId);
35
40
  }),
36
41
 
37
- completeTask: mutation(cnst.Task)
42
+ completeTask: mutation(cnst.Task, { guards: [SignedIn] })
38
43
  .param("taskId", String)
39
44
  .exec(async function (taskId) {
40
45
  return await this.taskService.completeTask(taskId);
@@ -48,7 +53,7 @@ export class TaskEndpoint extends endpoint(srv.task, ({ mutation }) => ({
48
53
  // taskEvent: pubsub(cnst.Task).exec(...),
49
54
  // // search: search parameters (optional, URL query string)
50
55
  // searchTask: query(cnst.LightTask).search("keyword", String).exec(...),
51
- // // guards: authentication/authorization guards
52
- // // export class TaskEndpoint extends endpoint(srv.task, { guards: { root: SignedIn } }, ({ query, mutation }) => ({})) {}
56
+ // // mcp: false keeps a guarded endpoint off the agent shelf without changing its authorization
57
+ // startTask: mutation(cnst.Task, { guards: [SignedIn], mcp: false })...
53
58
  `;
54
59
  }
@@ -3,7 +3,7 @@ import type { AppInfo, LibInfo } from "akanjs";
3
3
  export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
4
  return {
5
5
  filename: "AuthGuard.ts",
6
- content: `import type { Guard, SignalContext } from "akanjs/signal";
6
+ content: `import type { Guard, GuardScope, SignalContext } from "akanjs/signal";
7
7
 
8
8
  // ===== AuthGuard.ts =====
9
9
  // Convention: srvkit/ folder — server-only helpers, cannot import from client code.
@@ -14,10 +14,14 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
14
14
 
15
15
  export class SignedIn implements Guard {
16
16
  static name = "SignedIn";
17
+ // "account" — the verdict reads the caller and nothing about the call, so an MCP catalogue can evaluate it with
18
+ // no arguments and hide what this caller certainly cannot use. Required: "resource" is never evaluated for a
19
+ // listing, so a guard like this one marked that way would filter nothing.
20
+ static scope: GuardScope = "account";
17
21
 
18
22
  canPass(context: SignalContext): boolean {
19
- const user = context.getHttpContext<{ user?: { id: string } }>().req.user;
20
- return !!user;
23
+ // Reads the account the middleware resolved, so the same guard answers over HTTP, a websocket and MCP alike.
24
+ return !!context.get<{ id?: string }>("account");
21
25
  }
22
26
  }
23
27
 
@@ -25,9 +29,9 @@ export class SignedIn implements Guard {
25
29
  // Role-based guard: only allow users with a specific role
26
30
  // export class IsAdmin implements Guard {
27
31
  // static name = "IsAdmin";
32
+ // static scope: GuardScope = "account";
28
33
  // canPass(context: SignalContext): boolean {
29
- // const user = context.getHttpContext<{ user?: { role: string } }>().req.user;
30
- // return user?.role === "admin";
34
+ // return context.get<{ role?: string }>("account")?.role === "admin";
31
35
  // }
32
36
  // }
33
37
  `,
@@ -15,8 +15,9 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
15
15
 
16
16
  export class CurrentUserId implements InternalArg<string | null> {
17
17
  getArg(context: SignalContext): string | null {
18
- const user = context.getHttpContext<{ user?: { id: string } }>().req.user;
19
- return user?.id ?? null;
18
+ // \`context.get\` reads what the account middleware resolved, on every transport. Branching on
19
+ // \`getHttpContext()\` would return null for the same caller arriving over a websocket or MCP.
20
+ return context.get<{ id?: string }>("account")?.id ?? null;
20
21
  }
21
22
  }
22
23
 
@@ -24,7 +25,7 @@ export class CurrentUserId implements InternalArg<string | null> {
24
25
  // Inject current session language/locale
25
26
  // export class CurrentLocale implements InternalArg<string> {
26
27
  // getArg(context: SignalContext): string {
27
- // return context.getHttpContext<{ locale?: string }>().req.locale ?? "en";
28
+ // return context.get<string>("locale") ?? "en";
28
29
  // }
29
30
  // }
30
31
  `,
@@ -9,9 +9,9 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
9
9
  return {
10
10
  filename: "_index.tsx",
11
11
  content: `
12
- import { Load } from "akanjs/ui";
13
- import { fetch, usePage, ${dict.Model} } from "@apps/${dict.appName}/client";
12
+ import { ${dict.Model}, fetch, usePage } from "@apps/${dict.appName}/client";
14
13
  import type { PageConfig } from "akanjs/client";
14
+ import { Load } from "akanjs/ui";
15
15
 
16
16
  interface PageProps {
17
17
  params: { ${dict.model}Id: string };
@@ -20,17 +20,17 @@ interface PageProps {
20
20
  export default async function Page({ params }: PageProps) {
21
21
  const { l } = usePage();
22
22
  const { ${dict.model}Id } = params;
23
- const { ${dict.model}, ${dict.model}Edit } = await fetch.edit${dict.Model}(${dict.model}Id);
23
+ const [{ ${dict.model}Edit }] = await Promise.all([fetch.edit${dict.Model}(${dict.model}Id)]);
24
24
  return (
25
25
  <div className="container">
26
- <div className="flex justify-between m-4 mt-8">
27
- <div className="text-xl text-primary flex gap-2 items-center">
26
+ <div className="m-4 mt-8 flex justify-between">
27
+ <div className="flex items-center gap-2 text-primary text-xl">
28
28
  {l("base.updateModel", { model: l("${dict.model}.modelName") })}
29
29
  </div>
30
30
  </div>
31
31
  <Load.Edit
32
32
  className="flex flex-col items-center"
33
- slice={fetch.slice["${dict.model}InPublic"]}
33
+ slice={fetch.slice.${dict.model}InPublic}
34
34
  edit={${dict.model}Edit}
35
35
  type="form"
36
36
  onCancel="back"
@@ -10,37 +10,34 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
10
10
  filename: "_index.tsx",
11
11
  content: `
12
12
  import { ${dict.Model}, fetch, usePage } from "@apps/${dict.appName}/client";
13
- import { Link } from "akanjs/ui";
14
13
  import type { PageConfig } from "akanjs/client";
14
+ import { buttonRecipe, Link } from "akanjs/ui";
15
15
 
16
16
  interface PageProps {
17
17
  params: { ${dict.model}Id: string };
18
18
  }
19
19
 
20
- export async function generateHead({ params }: PageProps) {
20
+ export function generateHead({ params }: PageProps) {
21
21
  const { ${dict.model}Id } = params;
22
- const { ${dict.model} } = await fetch.view${dict.Model}(${dict.model}Id);
23
22
  return (
24
23
  <>
25
- <title>{${dict.model}.id}</title>
26
- <meta name="description" content={${dict.model}.id} />
27
- <meta property="og:title" content={${dict.model}.id} />
28
- <meta property="og:description" content={${dict.model}.id} />
24
+ <title>{${dict.model}Id}</title>
25
+ <meta name="description" content={${dict.model}Id} />
26
+ <meta property="og:title" content={${dict.model}Id} />
27
+ <meta property="og:description" content={${dict.model}Id} />
29
28
  </>
30
29
  );
31
30
  }
32
31
  export default async function Page({ params }: PageProps) {
33
32
  const { l } = usePage();
34
33
  const { ${dict.model}Id } = params;
35
- const { ${dict.model}, ${dict.model}View } = await fetch.view${dict.Model}(${dict.model}Id);
34
+ const [{ ${dict.model}, ${dict.model}View }] = await Promise.all([fetch.view${dict.Model}(${dict.model}Id)]);
36
35
  return (
37
36
  <div className="container flex flex-col gap-4">
38
- <div className="flex gap-4 font-bold text-lg items-center">
37
+ <div className="flex items-center gap-4 font-bold text-lg">
39
38
  <${dict.Model}.Zone.View view={${dict.model}View} />
40
- <Link href={\`/${dict.model}/\${${dict.model}.id}/edit\`}>
41
- <button className="inline-flex items-center justify-center gap-2 rounded-field bg-primary px-4 py-2 font-medium text-primary-foreground text-sm transition-colors hover:bg-primary/90">
42
- {l("base.updateModel", { model: l("${dict.model}.modelName") })}
43
- </button>
39
+ <Link className={buttonRecipe()} href={\`/${dict.model}/\${${dict.model}.id}/edit\`}>
40
+ {l("base.updateModel", { model: l("${dict.model}.modelName") })}
44
41
  </Link>
45
42
  </div>
46
43
  </div>
@@ -9,23 +9,23 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
9
9
  return {
10
10
  filename: "_index.tsx",
11
11
  content: `
12
- import { Load } from "akanjs/ui";
13
- import { cnst, fetch, usePage, ${dict.Model} } from "@apps/${dict.appName}/client";
12
+ import { type cnst, fetch, usePage, ${dict.Model} } from "@apps/${dict.appName}/client";
14
13
  import type { PageConfig } from "akanjs/client";
14
+ import { Load } from "akanjs/ui";
15
15
 
16
16
  export default function Page() {
17
17
  const { l } = usePage();
18
18
  const ${dict.model}Form: Partial<cnst.${dict.Model}> = {};
19
19
  return (
20
20
  <div className="container">
21
- <div className="flex justify-between m-4 mt-8">
22
- <div className="text-xl text-primary flex gap-2 items-center">
21
+ <div className="m-4 mt-8 flex justify-between">
22
+ <div className="flex items-center gap-2 text-primary text-xl">
23
23
  + {l("base.createModel", { model: l("${dict.model}.modelName") })}
24
24
  </div>
25
25
  </div>
26
26
  <Load.Edit
27
27
  className="flex flex-col items-center"
28
- slice={fetch.slice["${dict.model}InPublic"]}
28
+ slice={fetch.slice.${dict.model}InPublic}
29
29
  edit={${dict.model}Form}
30
30
  type="form"
31
31
  onCancel="back"
@@ -37,6 +37,6 @@ export default function Page() {
37
37
  );
38
38
  }
39
39
  export const pageConfig = { transition: "none" } satisfies PageConfig;
40
- `,
40
+ `,
41
41
  };
42
42
  }
@@ -10,26 +10,26 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
10
10
  filename: "_index.tsx",
11
11
  content: `
12
12
  import { ${dict.Model}, fetch, usePage } from "@apps/${dict.appName}/client";
13
- import { Link } from "akanjs/ui";
14
13
  import type { PageConfig } from "akanjs/client";
14
+ import { buttonRecipe, Link } from "akanjs/ui";
15
15
 
16
16
  export default async function Page() {
17
17
  const { l } = usePage();
18
- const { ${dict.model}InitInPublic } = await fetch.init${dict.Model}InPublic();
18
+ const [{ ${dict.model}InitInPublic }] = await Promise.all([fetch.init${dict.Model}InPublic()]);
19
19
  return (
20
20
  <div className="container flex flex-col gap-4">
21
- <div className="w-full mt-5 px-5 h-full ">
21
+ <div className="mt-5 h-full w-full px-5">
22
22
  <div>{l("${dict.model}.modelName")}</div>
23
- <div className="animate-fadeIn px-4 pt-4 flex gap-4 items-center">
23
+ <div className="flex animate-fadeIn items-center gap-4 px-4 pt-4">
24
24
  <div className="font-bold text-lg md:text-4xl">${dict.Model}s</div>
25
- <Link href={\`/${dict.model}/new\`}>
26
- <button className="inline-flex items-center justify-center gap-2 rounded-field bg-primary px-4 py-2 font-medium text-primary-foreground text-sm transition-colors hover:bg-primary/90">+ {l("base.createModel", { model: l("${dict.model}.modelName") })}</button>
25
+ <Link className={buttonRecipe()} href="/${dict.model}/new">
26
+ + {l("base.createModel", { model: l("${dict.model}.modelName") })}
27
27
  </Link>
28
28
  </div>
29
29
  <div>{l("${dict.model}.modelDesc")}</div>
30
- <div className="flex px-6 mt-3 gap-4">
30
+ <div className="mt-3 flex gap-4 px-6">
31
31
  <${dict.Model}.Zone.Card
32
- className="animate-fadeIn grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 justify-center gap-4 w-full"
32
+ className="grid w-full animate-fadeIn grid-cols-1 justify-center gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"
33
33
  init={${dict.model}InitInPublic}
34
34
  />
35
35
  </div>
@@ -38,6 +38,6 @@ export default async function Page() {
38
38
  );
39
39
  }
40
40
  export const pageConfig = { transition: "none" } satisfies PageConfig;
41
- `,
41
+ `,
42
42
  };
43
43
  }
@@ -9,23 +9,27 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
9
9
  return {
10
10
  filename: "_index.tsx",
11
11
  content: `
12
- import type { PageConfig } from "akanjs/client";
13
- import { Model } from "akanjs/ui";
14
12
  import { ${dict.Model}, fetch, usePage } from "@apps/${dict.appName}/client";
13
+ import type { PageConfig } from "akanjs/client";
14
+ import { buttonRecipe, Model } from "akanjs/ui";
15
15
 
16
16
  export default async function Page() {
17
17
  const { l } = usePage();
18
- const { ${dict.model}InitInPublic } = await fetch.init${dict.Model}InPublic();
18
+ const [{ ${dict.model}InitInPublic }] = await Promise.all([fetch.init${dict.Model}InPublic()]);
19
19
  return (
20
20
  <>
21
- <div className="animate-fadeIn flex items-center gap-4 px-4 pt-4">
22
- <div className="text-lg font-bold md:text-4xl">{l("${dict.model}.modelName")}</div>
23
- <Model.New className="inline-flex items-center gap-2 rounded-field px-3 py-1.5 text-sm transition-colors hover:bg-muted" slice={fetch.slice["${dict.model}InPublic"]} renderTitle="id">
21
+ <div className="flex animate-fadeIn items-center gap-4 px-4 pt-4">
22
+ <div className="font-bold text-lg md:text-4xl">{l("${dict.model}.modelName")}</div>
23
+ <Model.New
24
+ className={buttonRecipe({ size: "sm", variant: "ghost" })}
25
+ slice={fetch.slice.${dict.model}InPublic}
26
+ renderTitle="id"
27
+ >
24
28
  <${dict.Model}.Template.General />
25
29
  </Model.New>
26
30
  </div>
27
31
  <${dict.Model}.Zone.Card
28
- className="animate-fadeIn mt-2 grid w-full grid-cols-1 justify-center gap-4 md:grid-cols-2 xl:grid-cols-3"
32
+ className="mt-2 grid w-full animate-fadeIn grid-cols-1 justify-center gap-4 md:grid-cols-2 xl:grid-cols-3"
29
33
  init={${dict.model}InitInPublic}
30
34
  />
31
35
  </>
@@ -5,12 +5,14 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { [
5
5
  const libs = scanInfo.getLibs();
6
6
  return {
7
7
  filename: "env.server.type.ts",
8
- content: `
9
- ${libs.length ? libs.map((lib) => `import { env as ${lib}Option } from "@libs/${lib}/server";`).join("\n") : 'import { getEnv } from "akanjs/base";'}
8
+ content: libs.length
9
+ ? `
10
+ ${libs.map((lib) => `import { env as ${lib}Option } from "@libs/${lib}/server";`).join("\n")}
10
11
 
11
12
  export const libEnv = {
12
- ${libs.length ? libs.map((lib) => ` ...${lib}Option,`).join("\n") : " ...getEnv(),"}
13
- };
14
- `,
13
+ ${libs.map((lib) => ` ...${lib}Option,`).join("\n")}
14
+ };`
15
+ : `
16
+ export const libEnv = {};`,
15
17
  };
16
18
  }
@@ -13,7 +13,7 @@ ${scalarModules.map((module) => `import { ${capitalize(module)} } from "./__scal
13
13
  ${databaseModules.length ? `import * as cnst from "./cnst";` : ""}
14
14
  ${databaseModules.map((module) => `import * as ${module}Db from "./${module}/${module}.document";`).join("\n")}
15
15
 
16
- ${libs.map((lib) => `export { db as ${lib} } from "@libs/${lib}/server";`).join("\n")}
16
+ ${libs.map((lib) => `export * as ${lib} from "@libs/${lib}/lib/db";`).join("\n")}
17
17
 
18
18
  ${databaseModules.map((module) => `class ${capitalize(module)}Input extends by(cnst.${capitalize(module)}Input) {}`).join("\n")}
19
19
  ${databaseModules.map((module) => `class ${capitalize(module)}Insight extends by(cnst.${capitalize(module)}Insight) {}`).join("\n")}
@@ -9,7 +9,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { [
9
9
  const libs = scanInfo.getLibs();
10
10
  return `
11
11
  import { makeDictionary, makeTrans, registerScalarTrans, registerServiceTrans, registerModelTrans${libs.length === 0 ? `, dictionary as base` : ""} } from "akanjs/dictionary";
12
- ${libs.length ? libs.map((lib) => `import { dictionary as ${lib} } from "@libs/${lib}/server";`).join("\n") : ""}
12
+ ${libs.length ? libs.map((lib) => `import { dictionary as ${lib} } from "@libs/${lib}/lib/dict";`).join("\n") : ""}
13
13
 
14
14
  ${databaseModules.map((module) => `import * as ${module} from "./${module}/${module}.dictionary";`).join("\n")}
15
15
  ${serviceModules.map((module) => `import * as ${module} from "./_${module}/${module}.dictionary";`).join("\n")}
@@ -17,7 +17,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { [
17
17
  return `
18
18
  import { FetchClient, type FetchClientType } from "akanjs/fetch";
19
19
  import { SignalRegistry, serverSignal${libs.length === 0 ? ", fetch as base" : ""} } from "akanjs/signal";
20
- ${libs.map((lib) => `import { fetch as ${lib} } from "@libs/${lib}/server";`).join("\n")}
20
+ ${libs.map((lib) => `import { fetch as ${lib} } from "@libs/${lib}/lib/sig";`).join("\n")}
21
21
 
22
22
  ${[...scanInfo.database.entries()]
23
23
  .filter(([_, files]) => files.has("signal"))
@@ -13,7 +13,11 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { [
13
13
  const libs = scanInfo.getLibs();
14
14
  return `
15
15
  import { ServiceModel } from "akanjs/service";
16
- ${libs.length ? libs.map((lib) => `import { option as ${lib} } from "@libs/${lib}/server";`).join("\n") : 'import type { BackendEnv } from "akanjs/base";'}
16
+ ${
17
+ libs.length
18
+ ? libs.map((lib) => `import * as ${lib} from "@libs/${lib}/lib/option";`).join("\n")
19
+ : 'import type { BackendEnv } from "akanjs/base";'
20
+ }
17
21
 
18
22
  import * as cnst from "./cnst";
19
23
  import * as db from "./db";
@@ -21,7 +25,7 @@ import * as db from "./db";
21
25
  ${databaseModules.map((module) => `import { ${capitalize(module)}Service } from "./${module}/${module}.service";`).join("\n")}
22
26
  ${serviceModules.map((module) => `import { ${capitalize(module)}Service } from "./_${module}/${module}.service";`).join("\n")}
23
27
 
24
- ${libs.map((lib) => `export { srv as ${lib} } from "@libs/${lib}/server";`).join("\n")}
28
+ ${libs.map((lib) => `export * as ${lib} from "@libs/${lib}/lib/srv";`).join("\n")}
25
29
 
26
30
  ${databaseModules.map((module) => `export { ${capitalize(module)}Service } from "./${module}/${module}.service";`).join("\n")}
27
31
  ${serviceModules.map((module) => `export { ${capitalize(module)}Service } from "./_${module}/${module}.service";`).join("\n")}
@@ -21,7 +21,7 @@ interface RemoveProps {
21
21
  export const Remove = ({ ${dict.model}Id }: RemoveProps) => {
22
22
  const { l } = usePage();
23
23
  return (
24
- <Model.Remove modelId={${dict.model}Id} slice={fetch.slice["${dict.model}"]}>
24
+ <Model.Remove modelId={${dict.model}Id} slice={fetch.slice.${dict.model}}>
25
25
  <BiTrash /> {l("base.remove")}
26
26
  </Model.Remove>
27
27
  );
@@ -12,9 +12,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
12
12
  content: `
13
13
  "use client";
14
14
  import { Load } from "akanjs/ui";
15
- // Alias the domain namespace so the Card/View exports below never collide with the model name
16
- // (a model literally named "card" or "view" would otherwise shadow this import).
17
- import { type cnst, ${dict.Model} as ${dict.Model}Domain } from "@${dict.sysType}s/${dict.sysName}/client";
15
+ import { type cnst, ${dict.Model} as ${dict.Model} } from "@${dict.sysType}s/${dict.sysName}/client";
18
16
  import type { ClientInit, ClientView, SliceMeta } from "akanjs/fetch";
19
17
 
20
18
  interface CardProps {
@@ -28,7 +26,7 @@ export const Card = ({ className, init, slice }: CardProps) => {
28
26
  className={className}
29
27
  init={init}
30
28
  renderItem={(${dict.model}) => (
31
- <${dict.Model}Domain.Unit.Card key={${dict.model}.id} href={\`/${dict.model}/\${${dict.model}.id}\`} ${dict.model}={${dict.model}} />
29
+ <${dict.Model}.Unit.Card key={${dict.model}.id} href={\`/${dict.model}/\${${dict.model}.id}\`} ${dict.model}={${dict.model}} />
32
30
  )}
33
31
  />
34
32
  );
@@ -39,7 +37,7 @@ interface ViewProps {
39
37
  view: ClientView<"${dict.model}", cnst.${dict.Model}>;
40
38
  }
41
39
  export const View = ({ view }: ViewProps) => {
42
- return <Load.View view={view} renderView={(${dict.model}) => <${dict.Model}Domain.View.General ${dict.model}={${dict.model}} />} />;
40
+ return <Load.View view={view} renderView={(${dict.model}) => <${dict.Model}.View.General ${dict.model}={${dict.model}} />} />;
43
41
  };
44
42
  `,
45
43
  };
@@ -18,6 +18,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
18
18
  import { modelDictionary } from "akanjs/dictionary";
19
19
 
20
20
  import type { ${dict.Model}, ${dict.Model}Insight } from "./${dict.model}.constant";
21
+ import type { ${dict.Model}Filter } from "./${dict.model}.document";
21
22
  import type { ${dict.Model}Endpoint, ${dict.Model}Slice } from "./${dict.model}.signal";
22
23
 
23
24
  export const dictionary = modelDictionary(["en", "ko"])
@@ -25,9 +26,11 @@ export const dictionary = modelDictionary(["en", "ko"])
25
26
  t(["${modelLabelEn}", "${modelLabelKo}"]).desc(["${modelDescEn}", "${modelDescKo}"])
26
27
  )
27
28
  .model<${dict.Model}>((t) => ({
28
- name: t(["Name", "이름"]),
29
+ name: t(["Name", "이름"]).desc(["Name", "이름"]),
29
30
  }))
30
31
  .insight<${dict.Model}Insight>((t) => ({}))
32
+ .query<${dict.Model}Filter>((fn) => ({}))
33
+ .sort<${dict.Model}Filter>((t) => ({}))
31
34
  .slice<${dict.Model}Slice>((fn) => ({
32
35
  inPublic: fn(["${dict.Model} In Public", "${dict.Model} 공개"]).arg((t) => ({})),
33
36
  }))
@@ -8,14 +8,13 @@ interface Dict {
8
8
  }
9
9
  export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict) {
10
10
  return `
11
- import { endpoint, internal, Public, slice } from "akanjs/signal";
11
+ import { endpoint, internal, None, Public, slice } from "akanjs/signal";
12
12
 
13
- import * as cnst from "../cnst";
14
13
  import * as srv from "../srv";
15
14
 
16
15
  export class ${dict.Model}Internal extends internal(srv.${dict.model}, ({ interval }) => ({})) {}
17
16
 
18
- export class ${dict.Model}Slice extends slice(srv.${dict.model}, { guards: { root: Public, get: Public, cru: Public } }, (init) => ({
17
+ export class ${dict.Model}Slice extends slice(srv.${dict.model}, { guards: { root: None, get: Public, cru: Public } }, (init) => ({
19
18
  inPublic: init()
20
19
  .exec(function () {
21
20
  return this.${dict.model}Service.queryAny();
@@ -9,8 +9,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
9
9
  return `
10
10
  import { store } from "akanjs/store";
11
11
 
12
- import * as cnst from "../cnst";
13
- import { fetch, sig } from "../useClient";
12
+ import { sig } from "../useClient";
14
13
 
15
14
  export class ${dict.Model}Store extends store(sig.${dict.model}, () => ({
16
15
  // state
@@ -124,5 +124,4 @@ libs/*/client.ts
124
124
  libs/*/server.ts
125
125
  libs/*/index.ts
126
126
  **/.akan
127
- **/bun.lock
128
127
  **/tsconfig.tsbuildinfo