@akanjs/cli 3.0.0-alpha.1 → 3.0.0-alpha.100

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 (162) 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-byytxs22.js} +3 -5
  5. package/{agent.command-yv00p0s0.js → agent.command-v1a4746v.js} +11 -12
  6. package/{application.command-0qsczf4k.js → application.command-fzdcnzhg.js} +65 -37
  7. package/{applicationBuildRunner-ct2pxyvw.js → applicationBuildRunner-s4gsk728.js} +28 -24
  8. package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-28v9vbzk.js} +8 -9
  9. package/buildBatch.proc.js +12 -13
  10. package/capacitorApp-8d6d4zkm.js +12 -0
  11. package/{cloud.command-dxfv18sa.js → cloud.command-wygy2z05.js} +15 -17
  12. package/commandManifest.json +1 -1
  13. package/{context.command-8d9e3k6b.js → context.command-4vdt1aqq.js} +19 -22
  14. package/dependencyScanner-shs82ynp.js +8 -0
  15. package/devTui-qnf1txdz.js +553 -0
  16. package/devTuiView-yzct2cq6.js +9 -0
  17. package/getDirname-dr3jz1xg.js +7 -0
  18. package/{guideline.command-3jbkxzh6.js → guideline.command-p92dq6ts.js} +16 -17
  19. package/guidelines/agentRule/agentRule.generate.json +31 -0
  20. package/guidelines/agentRule/agentRule.instruction.md +533 -0
  21. package/guidelines/conventions/conventions.generate.json +25 -0
  22. package/guidelines/conventions/conventions.instruction.md +908 -0
  23. package/guidelines/cssRule/cssRule.instruction.md +7 -0
  24. package/guidelines/framework/framework.generate.json +3 -2
  25. package/guidelines/framework/framework.instruction.md +1 -9
  26. package/guidelines/mcpRule/mcpRule.generate.json +25 -0
  27. package/guidelines/mcpRule/mcpRule.instruction.md +217 -0
  28. package/guidelines/modelStore/modelStore.instruction.md +12 -0
  29. package/guidelines/modelTemplate/modelTemplate.instruction.md +5 -0
  30. package/guidelines/queryRule/queryRule.generate.json +31 -0
  31. package/guidelines/queryRule/queryRule.instruction.md +175 -0
  32. package/guidelines/runtimeRule/runtimeRule.generate.json +25 -0
  33. package/guidelines/runtimeRule/runtimeRule.instruction.md +220 -0
  34. package/guidelines/ssrRule/ssrRule.instruction.md +49 -5
  35. package/guidelines/transportRule/transportRule.generate.json +31 -0
  36. package/guidelines/transportRule/transportRule.instruction.md +60 -0
  37. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +24 -0
  38. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +263 -0
  39. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
  40. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +402 -0
  41. package/incrementalBuilder.proc.js +26 -24
  42. package/{index-mr5s7mqd.js → index-0amdxycf.js} +8 -8
  43. package/{index-t3723f99.js → index-1yxtbmbj.js} +111 -42
  44. package/{index-ej426mcr.js → index-29sxqfaw.js} +65 -38
  45. package/index-3d9y066c.js +76 -0
  46. package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
  47. package/index-5svjba73.js +159 -0
  48. package/index-6wwzs1bc.js +7 -0
  49. package/{index-k95xkh4p.js → index-7d6vny25.js} +16 -12
  50. package/{index-ec09pws3.js → index-8p8p0cf8.js} +14 -14
  51. package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
  52. package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
  53. package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
  54. package/{index-0cj2zxbm.js → index-c69j78kk.js} +12 -11
  55. package/{index-rzwcpcc3.js → index-fc1bh0a7.js} +4 -4
  56. package/{index-7c6k4yvg.js → index-gq44bqjt.js} +58 -25
  57. package/{index-a00jg8x8.js → index-gtmz74tj.js} +79 -55
  58. package/{index-tdbas9r0.js → index-h7yw2z9n.js} +7 -7
  59. package/{index-bjpxzr6s.js → index-hwkygn9s.js} +51 -3
  60. package/{index-bbkek1bp.js → index-hyywke0f.js} +1 -1
  61. package/{index-46tjzh6s.js → index-kcxmv48c.js} +29 -29
  62. package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
  63. package/{index-pyt2b4nw.js → index-p5neav9b.js} +1129 -1356
  64. package/{index-67546d0j.js → index-q0y2jq9j.js} +14 -2
  65. package/{index-bekddz9q.js → index-qkk27bk6.js} +32 -32
  66. package/{index-hgna5rjy.js → index-qsq3q8fy.js} +1034 -257
  67. package/{index-2q1f5j9k.js → index-srdssqj7.js} +18 -18
  68. package/{index-rh16j3c7.js → index-w8kmx1b6.js} +23 -13
  69. package/{index-nm6wghb6.js → index-yks023mb.js} +13 -13
  70. package/{index-1pvex4az.js → index-yza7zkpt.js} +94 -61
  71. package/{index-yzmhasdb.js → index-zmjppp25.js} +57 -57
  72. package/{index-8hsbnyes.js → index-znynzt5w.js} +100 -30
  73. package/{index-zasydjxd.js → index-zxgr3s8s.js} +31 -30
  74. package/{index-e16gnn26.js → index-zz5jyd94.js} +1002 -128
  75. package/index.js +22 -24
  76. package/{library.command-ebdj1vnm.js → library.command-s4fszngs.js} +11 -9
  77. package/{localRegistry.command-gb178ne8.js → localRegistry.command-gyw4da9z.js} +20 -22
  78. package/{module.command-9c1bnsx4.js → module.command-6n7dwvav.js} +13 -15
  79. package/{package.command-t93p4jkj.js → package.command-gr82ypnr.js} +8 -9
  80. package/package.json +5 -7
  81. package/{page.command-k3k0meh2.js → page.command-7gvgb3jb.js} +7 -8
  82. package/{primitive.command-de6nktdt.js → primitive.command-tk5ckw38.js} +13 -15
  83. package/{quality.command-z5kth2wa.js → quality.command-38v81baf.js} +229 -138
  84. package/recipeScanner-fpagtgc1.js +11 -0
  85. package/{repair.command-ehf2g112.js → repair.command-5xbwvsxp.js} +12 -13
  86. package/{routeSourceValidator-cncd92fv.js → routeSourceValidator-ze6x894c.js} +2 -40
  87. package/{scalar.command-7a2jtv33.js → scalar.command-0epwcr5k.js} +11 -13
  88. package/subspace.command-e2t36xb2.js +755 -0
  89. package/templates/app/env/env.client.debug.ts.template +1 -5
  90. package/templates/app/env/env.client.develop.ts.template +1 -5
  91. package/templates/app/env/env.client.local.ts.template +1 -5
  92. package/templates/app/env/env.client.main.ts.template +1 -5
  93. package/templates/app/env/env.client.testing.ts.template +1 -5
  94. package/templates/app/env/env.client.type.ts +2 -6
  95. package/templates/app/env/env.server.debug.ts.template +1 -5
  96. package/templates/app/env/env.server.develop.ts.template +1 -5
  97. package/templates/app/env/env.server.local.ts.template +1 -5
  98. package/templates/app/env/env.server.main.ts.template +1 -5
  99. package/templates/app/main.ts +3 -1
  100. package/templates/app/page/styles.css.template +3 -3
  101. package/templates/appSample/lib/task/task.signal.ts +11 -6
  102. package/templates/appSample/srvkit/AuthGuard.ts +9 -5
  103. package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
  104. package/templates/crudPages/[__model__Id]/edit/page.tsx +6 -6
  105. package/templates/crudPages/[__model__Id]/page.tsx +10 -13
  106. package/templates/crudPages/new/page.tsx +6 -6
  107. package/templates/crudPages/page.tsx +9 -9
  108. package/templates/crudSinglePage/page.tsx +11 -7
  109. package/templates/env/_env.server.type.ts +7 -5
  110. package/templates/lib/db.ts +4 -2
  111. package/templates/lib/dict.ts +1 -1
  112. package/templates/lib/sig.ts +5 -1
  113. package/templates/lib/srv.ts +6 -2
  114. package/templates/lib/useClient.ts +9 -3
  115. package/templates/module/__Model__.Util.tsx +1 -1
  116. package/templates/module/__Model__.Zone.tsx +3 -5
  117. package/templates/module/__model__.dictionary.ts +4 -1
  118. package/templates/module/__model__.signal.ts +2 -3
  119. package/templates/module/__model__.store.ts +1 -2
  120. package/templates/workspaceRoot/.gitignore.template +0 -1
  121. package/templates/workspaceRoot/.husky/pre-commit.template +16 -0
  122. package/templates/workspaceRoot/.vscode/settings.json.template +1 -1
  123. package/templates/workspaceRoot/AGENTS.md.template +4 -948
  124. package/templates/workspaceRoot/CLAUDE.md.template +19 -0
  125. package/templates/workspaceRoot/biome.json.template +4 -198
  126. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +15 -16
  127. package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
  128. package/templates/workspaceRoot/package.json.template +2 -4
  129. package/typeChecker-3xgrzc2v.js +7 -0
  130. package/typecheck.proc.js +4 -5
  131. package/{workflow.command-e0tt0vk2.js → workflow.command-kphbr7eh.js} +21 -23
  132. package/{workspace.command-fpda0evj.js → workspace.command-j1gatz3d.js} +108 -91
  133. package/capacitorApp-4r9sm3qg.js +0 -58
  134. package/dependencyScanner-f6grrep3.js +0 -9
  135. package/index-mxvakhsm.js +0 -66
  136. package/index-r24hmh0q.js +0 -4
  137. package/recipeScanner-tghkzrrt.js +0 -12
  138. package/templates/libRoot/base/baseLogic.ts +0 -14
  139. package/templates/libRoot/base/index.ts +0 -8
  140. package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
  141. package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -33
  142. package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
  143. package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
  144. package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -18
  145. package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
  146. package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
  147. package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
  148. package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
  149. package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -18
  150. package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -61
  151. package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
  152. package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
  153. package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
  154. package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -58
  155. package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
  156. package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
  157. package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
  158. package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +0 -59
  159. package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
  160. package/typeChecker-kravn7ns.js +0 -8
  161. /package/{index-0wae5ebk.js → index-meh558jy.js} +0 -0
  162. /package/{index-ss469dec.js → index-nz8prx9g.js} +0 -0
@@ -1,27 +1,100 @@
1
1
  // @bun
2
- import {
3
- AbstractDoc
4
- } from "./index-0wae5ebk.js";
5
2
  import {
6
3
  Workspace,
7
- command,
8
4
  runner,
9
- script
10
- } from "./index-8hsbnyes.js";
11
- import"./index-hgna5rjy.js";
12
- import"./index-mxvakhsm.js";
13
- import"./index-67546d0j.js";
14
- import"./index-r24hmh0q.js";
5
+ script,
6
+ command
7
+ } from "./index-znynzt5w.js";
8
+ import"./index-q0y2jq9j.js";
9
+ import {
10
+ rootAllowedFiles,
11
+ rootAllowedDirs,
12
+ isAllowedLibFacetRootFile
13
+ } from "./index-qsq3q8fy.js";
14
+ import"./index-6wwzs1bc.js";
15
+ import {
16
+ AbstractDoc
17
+ } from "./index-meh558jy.js";
15
18
 
16
19
  // pkgs/@akanjs/devkit/qualityScanner.ts
17
20
  import { createHash } from "crypto";
18
21
  import { readdir, readFile, stat } from "fs/promises";
19
22
  import path from "path";
23
+ import { RESERVED_ROUTE_CONFIG_EXPORTS } from "akanjs/common";
20
24
  import ignore from "ignore";
21
- import ts2 from "typescript";
25
+ import ts3 from "typescript";
22
26
 
23
- // pkgs/@akanjs/devkit/ssrScanner.ts
27
+ // pkgs/@akanjs/devkit/formSetterScanner.ts
24
28
  import ts from "typescript";
29
+
30
+ class FormSetterScanner {
31
+ static #fieldSetter = /^set[A-Za-z0-9_$]*On[A-Za-z0-9_$]*$/;
32
+ scan(sourceFiles) {
33
+ return sourceFiles.filter((sourceFile) => sourceFile.file.endsWith(".tsx")).flatMap((f) => this.#scanFile(f));
34
+ }
35
+ #scanFile({ file, sourceFile }) {
36
+ const wrapped = [];
37
+ const visit = (node) => {
38
+ if (ts.isJsxAttribute(node)) {
39
+ const setter = FormSetterScanner.#wrappedSetterOf(node);
40
+ if (setter)
41
+ wrapped.push({ setter, line: FormSetterScanner.#lineOf(sourceFile, node) });
42
+ }
43
+ ts.forEachChild(node, visit);
44
+ };
45
+ visit(sourceFile);
46
+ if (!wrapped.length)
47
+ return [];
48
+ const setters = [...new Set(wrapped.map((entry) => entry.setter))].sort();
49
+ return [
50
+ {
51
+ rule: "akan.agent.unpublished-form-setter",
52
+ scope: "agent",
53
+ severity: "warning",
54
+ message: setters.length === 1 ? `${setters[0]} is reached through a wrapper, so that field publishes no agent tool and carries no data-akan-action.` : `${setters.length} field setters are reached through a wrapper, so those fields publish no agent tool and carry no data-akan-action: ${setters.join(", ")}.`,
55
+ file,
56
+ line: wrapped[0]?.line,
57
+ locations: wrapped.map(({ line }) => ({ file, line }))
58
+ }
59
+ ];
60
+ }
61
+ static #wrappedSetterOf(attribute) {
62
+ if (!ts.isIdentifier(attribute.name) || !/^on[A-Z]/.test(attribute.name.text))
63
+ return null;
64
+ const initializer = attribute.initializer;
65
+ if (!initializer || !ts.isJsxExpression(initializer) || !initializer.expression)
66
+ return null;
67
+ const handler = initializer.expression;
68
+ if (!ts.isArrowFunction(handler) && !ts.isFunctionExpression(handler))
69
+ return null;
70
+ if (!handler.parameters.length)
71
+ return null;
72
+ return FormSetterScanner.#setterCallIn(handler.body);
73
+ }
74
+ static #setterCallIn(node) {
75
+ let found = null;
76
+ const visit = (current) => {
77
+ if (found)
78
+ return;
79
+ if (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
80
+ const { expression, name } = current.expression;
81
+ if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "st" && expression.name.text === "do" && FormSetterScanner.#fieldSetter.test(name.text)) {
82
+ found = name.text;
83
+ return;
84
+ }
85
+ }
86
+ ts.forEachChild(current, visit);
87
+ };
88
+ visit(node);
89
+ return found;
90
+ }
91
+ static #lineOf(sourceFile, node) {
92
+ return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
93
+ }
94
+ }
95
+
96
+ // pkgs/@akanjs/devkit/ssrScanner.ts
97
+ import ts2 from "typescript";
25
98
  var STATIC_COMPONENT_MIN_MASS = 4;
26
99
  var MIXED_COMPONENT_MIN_MASS = 10;
27
100
  var MIXED_COMPONENT_MAX_TOUCHES = 2;
@@ -139,31 +212,31 @@ class SsrScanner {
139
212
  });
140
213
  }
141
214
  }
142
- ts.forEachChild(node, visit);
215
+ ts2.forEachChild(node, visit);
143
216
  };
144
- ts.forEachChild(sourceFile.sourceFile, visit);
217
+ ts2.forEachChild(sourceFile.sourceFile, visit);
145
218
  return warnings;
146
219
  }
147
220
  #isMountEffect(sourceFile, node) {
148
- if (!ts.isCallExpression(node))
221
+ if (!ts2.isCallExpression(node))
149
222
  return false;
150
223
  const callee = node.expression.getText(sourceFile);
151
224
  if (callee !== "useEffect" && callee !== "useLayoutEffect")
152
225
  return false;
153
226
  const deps = node.arguments[1];
154
- return !!deps && ts.isArrayLiteralExpression(deps) && deps.elements.length === 0;
227
+ return !!deps && ts2.isArrayLiteralExpression(deps) && deps.elements.length === 0;
155
228
  }
156
229
  #getLoadCalls(sourceFile, node) {
157
230
  const calls = [];
158
231
  const visit = (child) => {
159
- if (ts.isCallExpression(child)) {
232
+ if (ts2.isCallExpression(child)) {
160
233
  const callee = child.expression.getText(sourceFile);
161
234
  if (/^fetch\.[a-z]/.test(callee) || /^st\.do\.(init|get|view|load|list|count|insight)[A-Z]/.test(callee))
162
235
  calls.push({ callee, node: child });
163
236
  }
164
- ts.forEachChild(child, visit);
237
+ ts2.forEachChild(child, visit);
165
238
  };
166
- ts.forEachChild(node, visit);
239
+ ts2.forEachChild(node, visit);
167
240
  return calls;
168
241
  }
169
242
  #getTemplateStateWarnings(sourceFile) {
@@ -171,7 +244,7 @@ class SsrScanner {
171
244
  return [];
172
245
  const warnings = [];
173
246
  const visit = (node) => {
174
- if (ts.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
247
+ if (ts2.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
175
248
  warnings.push({
176
249
  rule: "akan.ssr.template-client-state",
177
250
  scope: "ssr",
@@ -181,9 +254,9 @@ class SsrScanner {
181
254
  message: "Template holds form state in useState. Templates are store-driven and carry no local state."
182
255
  });
183
256
  }
184
- ts.forEachChild(node, visit);
257
+ ts2.forEachChild(node, visit);
185
258
  };
186
- ts.forEachChild(sourceFile.sourceFile, visit);
259
+ ts2.forEachChild(sourceFile.sourceFile, visit);
187
260
  return warnings;
188
261
  }
189
262
  #measureBalance(sourceFiles) {
@@ -224,14 +297,14 @@ class SsrScanner {
224
297
  return components;
225
298
  }
226
299
  #getComponentNodes(statement) {
227
- if (ts.isFunctionDeclaration(statement) && statement.body)
300
+ if (ts2.isFunctionDeclaration(statement) && statement.body)
228
301
  return [{ name: statement.name?.text ?? "default", node: statement.body }];
229
- if (!ts.isVariableStatement(statement))
302
+ if (!ts2.isVariableStatement(statement))
230
303
  return [];
231
304
  return statement.declarationList.declarations.flatMap((declaration) => {
232
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
305
+ if (!ts2.isIdentifier(declaration.name) || !declaration.initializer)
233
306
  return [];
234
- if (!ts.isArrowFunction(declaration.initializer) && !ts.isFunctionExpression(declaration.initializer))
307
+ if (!ts2.isArrowFunction(declaration.initializer) && !ts2.isFunctionExpression(declaration.initializer))
235
308
  return [];
236
309
  return [{ name: declaration.name.text, node: declaration.initializer }];
237
310
  });
@@ -239,7 +312,7 @@ class SsrScanner {
239
312
  #getTouches(sourceFile, node) {
240
313
  const touches = [];
241
314
  const visit = (child) => {
242
- if (ts.isCallExpression(child)) {
315
+ if (ts2.isCallExpression(child)) {
243
316
  const callee = child.expression.getText(sourceFile);
244
317
  const bareName = callee.split(".").pop() ?? callee;
245
318
  if (callee === "createContext" || callee === "lazy")
@@ -247,60 +320,60 @@ class SsrScanner {
247
320
  else if (/^use[A-Z]/.test(bareName) && !SsrScanner.#serverSafeCalls.has(bareName))
248
321
  touches.push(bareName);
249
322
  }
250
- if (ts.isJsxAttribute(child) && /^on[A-Z]/.test(child.name.getText(sourceFile)))
323
+ if (ts2.isJsxAttribute(child) && /^on[A-Z]/.test(child.name.getText(sourceFile)))
251
324
  touches.push(child.name.getText(sourceFile));
252
- if (ts.isPropertyAccessExpression(child)) {
325
+ if (ts2.isPropertyAccessExpression(child)) {
253
326
  const root = getAccessRoot(child);
254
327
  if (root === "st")
255
328
  touches.push("st");
256
329
  else if (SsrScanner.#clientGlobals.has(root))
257
330
  touches.push(root);
258
331
  }
259
- ts.forEachChild(child, visit);
332
+ ts2.forEachChild(child, visit);
260
333
  };
261
- ts.forEachChild(node, visit);
334
+ ts2.forEachChild(node, visit);
262
335
  return touches;
263
336
  }
264
337
  #getTagNames(sourceFile, node) {
265
338
  const tags = new Set;
266
339
  const visit = (child) => {
267
- if (ts.isJsxOpeningElement(child) || ts.isJsxSelfClosingElement(child))
340
+ if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
268
341
  tags.add(child.tagName.getText(sourceFile).split(".")[0]);
269
- ts.forEachChild(child, visit);
342
+ ts2.forEachChild(child, visit);
270
343
  };
271
- ts.forEachChild(node, visit);
344
+ ts2.forEachChild(node, visit);
272
345
  return tags;
273
346
  }
274
347
  #getMass(node) {
275
348
  let mass = 0;
276
349
  const visit = (child) => {
277
- if (ts.isJsxOpeningElement(child) || ts.isJsxSelfClosingElement(child))
350
+ if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
278
351
  mass += 1;
279
- ts.forEachChild(child, visit);
352
+ ts2.forEachChild(child, visit);
280
353
  };
281
- ts.forEachChild(node, visit);
354
+ ts2.forEachChild(node, visit);
282
355
  return mass;
283
356
  }
284
357
  #hasUseClient(sourceFile) {
285
358
  const first = sourceFile.statements[0];
286
- if (!first || !ts.isExpressionStatement(first) || !ts.isStringLiteral(first.expression))
359
+ if (!first || !ts2.isExpressionStatement(first) || !ts2.isStringLiteral(first.expression))
287
360
  return false;
288
361
  return first.expression.text === "use client";
289
362
  }
290
363
  #hasVendorImport(sourceFile) {
291
- return sourceFile.statements.some((statement) => ts.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
364
+ return sourceFile.statements.some((statement) => ts2.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
292
365
  }
293
366
  #getVendorNames(sourceFile) {
294
367
  const names = new Set;
295
368
  for (const statement of sourceFile.statements) {
296
- if (!ts.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
369
+ if (!ts2.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
297
370
  continue;
298
371
  const clause = statement.importClause;
299
372
  if (clause?.name)
300
373
  names.add(clause.name.text);
301
- if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings))
374
+ if (clause?.namedBindings && ts2.isNamespaceImport(clause.namedBindings))
302
375
  names.add(clause.namedBindings.name.text);
303
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings))
376
+ if (clause?.namedBindings && ts2.isNamedImports(clause.namedBindings))
304
377
  for (const element of clause.namedBindings.elements)
305
378
  names.add(element.name.text);
306
379
  }
@@ -308,10 +381,10 @@ class SsrScanner {
308
381
  }
309
382
  #importsClientRuntime(sourceFile) {
310
383
  for (const statement of sourceFile.statements) {
311
- if (!ts.isImportDeclaration(statement))
384
+ if (!ts2.isImportDeclaration(statement))
312
385
  continue;
313
386
  const bindings = statement.importClause?.namedBindings;
314
- if (!bindings || !ts.isNamedImports(bindings))
387
+ if (!bindings || !ts2.isNamedImports(bindings))
315
388
  continue;
316
389
  if (bindings.elements.some((element) => SsrScanner.#clientRuntimeImports.has(element.name.text)))
317
390
  return true;
@@ -362,7 +435,7 @@ function getShare(serverMass, clientMass) {
362
435
  return total === 0 ? 1 : serverMass / total;
363
436
  }
364
437
  function getSpecifier(statement) {
365
- return ts.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
438
+ return ts2.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
366
439
  }
367
440
  function isVendorSpecifier(specifier) {
368
441
  if (specifier === "" || specifier.startsWith(".") || specifier.startsWith("/"))
@@ -375,9 +448,9 @@ function isVendorSpecifier(specifier) {
375
448
  }
376
449
  function getAccessRoot(node) {
377
450
  let current = node;
378
- while (ts.isPropertyAccessExpression(current))
451
+ while (ts2.isPropertyAccessExpression(current))
379
452
  current = current.expression;
380
- return ts.isIdentifier(current) ? current.text : "";
453
+ return ts2.isIdentifier(current) ? current.text : "";
381
454
  }
382
455
 
383
456
  // pkgs/@akanjs/devkit/qualityScanner.ts
@@ -401,30 +474,10 @@ var SUGGESTED_RULES = [
401
474
  "Keep apps/*/lib and libs/*/lib root files limited to generated support facets such as cnst.ts, db.ts, dict.ts, sig.ts, srv.ts, st.ts, useClient.ts, and useServer.ts.",
402
475
  "Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
403
476
  "Keep module UI filenames predictable: database modules use <Model>.Template/Unit/Util/View/Zone.tsx, service modules use <Service>.Util/Zone.tsx, and scalar modules use <Scalar>.Template/Unit.tsx.",
404
- "Move shared app utilities to apps/*/common instead of creating apps/*/base.",
477
+ "Hand a form control its store setter by reference so the field publishes an agent tool; put normalization in the control's transform prop and a multi-write in a _postSet<Field> hook on the store.",
478
+ "Move shared app and lib utilities to common/ instead of creating apps/*/base or libs/*/base.",
405
479
  "Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
406
480
  ];
407
- var APP_ROOT_FILES = new Set([
408
- "akan.app.json",
409
- "akan.config.ts",
410
- "capacitor.config.ts",
411
- "client.ts",
412
- "main.ts",
413
- "package.json",
414
- "server.ts",
415
- "tsconfig.json"
416
- ]);
417
- var LIB_ROOT_FILES = new Set([
418
- "cnst.ts",
419
- "db.ts",
420
- "dict.ts",
421
- "option.ts",
422
- "sig.ts",
423
- "srv.ts",
424
- "st.ts",
425
- "useClient.ts",
426
- "useServer.ts"
427
- ]);
428
481
  var CONVENTION_SUFFIXES = [
429
482
  ".constant.ts",
430
483
  ".dictionary.ts",
@@ -433,20 +486,6 @@ var CONVENTION_SUFFIXES = [
433
486
  ".signal.ts",
434
487
  ".store.ts"
435
488
  ];
436
- var PAGE_RESERVED_EXPORTS = new Set([
437
- "pageConfig",
438
- "head",
439
- "metadata",
440
- "generateHead",
441
- "generateMetadata",
442
- "fonts",
443
- "manifest",
444
- "theme",
445
- "reconnect",
446
- "wsConnect",
447
- "layoutStyle",
448
- "gaTrackingId"
449
- ]);
450
489
  var RULE_FIXES = {
451
490
  "akan.global.duplicate-exported-function-name": "Rename one of the exports, or if they are the same thing, extract it into one shared module and import it in both places.",
452
491
  "akan.global.duplicate-exported-function-body": "Extract the shared implementation into a single exported helper and import it, instead of copying the body.",
@@ -454,15 +493,20 @@ var RULE_FIXES = {
454
493
  "akan.file.max-lines": "Break the file into smaller focused modules; keep one primary responsibility per file.",
455
494
  "akan.file.abstract-max-lines": "Run `akan compact <app-or-lib>` to rewrite the abstract with the AI editor, keeping only the invariants and workflows the source files cannot show.",
456
495
  "akan.file.placeholder-export": "Remove the placeholder export; generated indexes should only re-export real modules.",
496
+ "akan.file.bang-comment-in-client": "Rewrite the marker as `// FIXME:` or `// TODO:`. Keep the bang form for server, srvkit, and CLI files.",
457
497
  "akan.file.dictionary-stale-text": "Replace the scaffold text with real localized copy for this dictionary entry.",
458
498
  "akan.file.global-declaration": "Move the global declaration into an approved low-level integration file (e.g. webkit) and keep it isolated.",
459
499
  "akan.file.window-augmentation": "Move the Window augmentation into an approved browser integration file and keep it isolated.",
460
500
  "akan.file.prototype-mutation": "Avoid prototype mutation, or isolate it in an approved low-level integration file.",
461
501
  "akan.file.class-export-global-declaration": "Move the helper to a sibling file and import it into the class module.",
502
+ "akan.agent.unpublished-form-setter": "Pass the setter by reference where the wrapper only forwards (onChange={st.do.setTitleOnTask}); move normalization into the control's own transform prop; move a multi-write into a _postSet<Field> hook on the store, keeping the generated setter on the control.",
462
503
  "akan.file.component-internal-declaration": "Move the type or helper to a type/util file in ui/, webkit/, or common/ by purpose. If it is the component's props, declare it as `interface <Component>Props`.",
463
504
  "akan.file.component-export": "Move the value or type to a util/constant/type file in ui/, webkit/, or common/ and import it. Adding `export` is not a valid fix \u2014 only PascalCase components and their `<Component>Props` interface belong here.",
464
505
  "akan.layout.app-root-file": "Move the file into a conventional app folder (common, env, lib, page, private, public, script, srvkit, ui, or webkit).",
465
- "akan.layout.lib-root-file": "Move the file into a domain module folder under lib/; keep lib root limited to generated support facets.",
506
+ "akan.layout.app-root-folder": "Move the folder's contents into a conventional app folder (common, env, lib, page, private, public, script, srvkit, ui, or webkit) and delete it.",
507
+ "akan.layout.lib-root-file": "Move the file into a conventional lib folder (common, env, lib, page, private, public, srvkit, ui, or webkit).",
508
+ "akan.layout.lib-root-folder": "Move the folder's contents into a conventional lib folder (common, env, lib, page, private, public, srvkit, ui, or webkit) and delete it.",
509
+ "akan.layout.lib-facet-file": "Move the file into a domain module folder under lib/; keep the lib facet root limited to generated support facets.",
466
510
  "akan.layout.module-ui-file": "Rename the file to an allowed module UI name, or move it to ui/ if it is not a module component.",
467
511
  "akan.ssr.unnecessary-use-client": 'Delete the "use client" directive so the file renders on the server. If it exists only to wrap one client child, drop the wrapper and use the child directly.',
468
512
  "akan.ssr.client-static-component": "Move the component to a server file \u2014 a <Model>.Unit.tsx / <Model>.View.tsx for a module, or a ui/ file with no directive \u2014 and reference it from the client file.",
@@ -490,6 +534,7 @@ class AkanQualityScanner {
490
534
  ...sourceFiles.flatMap((sourceFile) => this.#scanConventionQuality(sourceFile)),
491
535
  ...sourceFiles.flatMap((sourceFile) => this.#scanLayoutQuality(sourceFile)),
492
536
  ...abstractFiles.flatMap((abstractFile) => this.#scanAbstractQuality(abstractFile)),
537
+ ...new FormSetterScanner().scan(sourceFiles),
493
538
  ...ssr.warnings
494
539
  ];
495
540
  return {
@@ -542,7 +587,7 @@ class AkanQualityScanner {
542
587
  file,
543
588
  absolutePath,
544
589
  content,
545
- sourceFile: ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true, getScriptKind(file))
590
+ sourceFile: ts3.createSourceFile(file, content, ts3.ScriptTarget.Latest, true, getScriptKind(file))
546
591
  };
547
592
  }
548
593
  async#readTextFile(workspaceRoot, file) {
@@ -600,6 +645,7 @@ class AkanQualityScanner {
600
645
  message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
601
646
  });
602
647
  }
648
+ warnings.push(...getBangCommentWarnings(sourceFile));
603
649
  warnings.push(...getPlaceholderExportWarnings(sourceFile));
604
650
  warnings.push(...getDictionaryTextWarnings(sourceFile));
605
651
  warnings.push(...getGlobalMutationWarnings(sourceFile));
@@ -701,25 +747,30 @@ class AkanQualityScanner {
701
747
  ];
702
748
  }
703
749
  #scanLayoutQuality(sourceFile) {
704
- const segments = sourceFile.file.split("/");
705
750
  const warnings = [];
706
- if (segments[0] === "apps" && segments.length === 3 && !APP_ROOT_FILES.has(segments[2])) {
707
- warnings.push({
708
- rule: "akan.layout.app-root-file",
709
- scope: "layout",
710
- severity: "warning",
711
- file: sourceFile.file,
712
- message: `Unexpected app root file "${segments[2]}". Keep application code in conventional app folders.`
713
- });
751
+ const rootEntry = getSysRootEntry(sourceFile.file);
752
+ if (rootEntry) {
753
+ const { type, name, isDir } = rootEntry;
754
+ const allowed = isDir ? rootAllowedDirs[type].has(name) : rootAllowedFiles[type].has(name);
755
+ const kind = isDir ? "folder" : "file";
756
+ if (!allowed) {
757
+ warnings.push({
758
+ rule: `akan.layout.${type}-root-${kind}`,
759
+ scope: "layout",
760
+ severity: "warning",
761
+ file: sourceFile.file,
762
+ message: `Unexpected ${type} root ${kind} "${name}". Keep ${type} code in conventional ${type} folders.`
763
+ });
764
+ }
714
765
  }
715
- const libRootFile = getLibRootFile(sourceFile.file);
716
- if (libRootFile && !LIB_ROOT_FILES.has(libRootFile)) {
766
+ const libFacetFile = getLibFacetRootFile(sourceFile.file);
767
+ if (libFacetFile && !isAllowedLibFacetRootFile(libFacetFile)) {
717
768
  warnings.push({
718
- rule: "akan.layout.lib-root-file",
769
+ rule: "akan.layout.lib-facet-file",
719
770
  scope: "layout",
720
771
  severity: "warning",
721
772
  file: sourceFile.file,
722
- message: `Unexpected lib root file "${libRootFile}". Keep direct lib root files limited to generated support facets.`
773
+ message: `Unexpected lib facet root file "${libFacetFile}". Keep direct lib/ root files limited to generated support facets.`
723
774
  });
724
775
  }
725
776
  const moduleUiWarning = getModuleUiWarning(sourceFile.file);
@@ -789,7 +840,7 @@ function getExportedFunctionLikes(sourceFile) {
789
840
  const declarations = [];
790
841
  const nameExempt = isPageRouteFile(sourceFile.file) || isUiComponentFile(sourceFile.file);
791
842
  for (const statement of sourceFile.sourceFile.statements) {
792
- if (ts2.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
843
+ if (ts3.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
793
844
  declarations.push({
794
845
  name: statement.name.text,
795
846
  kind: "function",
@@ -799,7 +850,7 @@ function getExportedFunctionLikes(sourceFile) {
799
850
  duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, false)
800
851
  });
801
852
  }
802
- if (ts2.isClassDeclaration(statement) && statement.name && isExported(statement)) {
853
+ if (ts3.isClassDeclaration(statement) && statement.name && isExported(statement)) {
803
854
  declarations.push({
804
855
  name: statement.name.text,
805
856
  kind: "class",
@@ -809,9 +860,9 @@ function getExportedFunctionLikes(sourceFile) {
809
860
  duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, isEnumClassStatement(sourceFile.sourceFile, statement))
810
861
  });
811
862
  }
812
- if (ts2.isVariableStatement(statement) && isExported(statement)) {
863
+ if (ts3.isVariableStatement(statement) && isExported(statement)) {
813
864
  for (const declaration of statement.declarationList.declarations) {
814
- if (!ts2.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
865
+ if (!ts3.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
815
866
  continue;
816
867
  declarations.push({
817
868
  name: declaration.name.text,
@@ -850,14 +901,14 @@ function isInLibModule(file) {
850
901
  return (segments[0] === "apps" || segments[0] === "libs") && segments.includes("lib");
851
902
  }
852
903
  function isEnumClassStatement(sourceFile, statement) {
853
- if (!ts2.isClassDeclaration(statement))
904
+ if (!ts3.isClassDeclaration(statement))
854
905
  return false;
855
- const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword);
906
+ const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
856
907
  const expression = heritageClause?.types[0]?.expression;
857
908
  return !!expression && expression.getText(sourceFile).startsWith("enumOf(");
858
909
  }
859
910
  function getExportedClassNames(sourceFile) {
860
- return sourceFile.statements.filter((statement) => ts2.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
911
+ return sourceFile.statements.filter((statement) => ts3.isClassDeclaration(statement)).filter((statement) => isExported(statement)).map((statement) => statement.name?.text).filter((name) => !!name);
861
912
  }
862
913
  function isComponentDeclarationFile(file) {
863
914
  if (!file.endsWith(".tsx"))
@@ -873,7 +924,7 @@ function isComponentDeclarationFile(file) {
873
924
  function getComponentFileDeclarations(sourceFile) {
874
925
  const reExportedNames = new Set;
875
926
  for (const statement of sourceFile.statements) {
876
- if (ts2.isExportDeclaration(statement) && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
927
+ if (ts3.isExportDeclaration(statement) && statement.exportClause && ts3.isNamedExports(statement.exportClause)) {
877
928
  for (const element of statement.exportClause.elements)
878
929
  reExportedNames.add((element.propertyName ?? element.name).text);
879
930
  }
@@ -883,19 +934,19 @@ function getComponentFileDeclarations(sourceFile) {
883
934
  const isDefaultExport = isDefaultExportStatement(statement);
884
935
  const inlineExported = isExported(statement);
885
936
  const add = (name, kind, line) => declarations.push({ name, kind, line, exported: inlineExported || reExportedNames.has(name), isDefaultExport });
886
- if (ts2.isInterfaceDeclaration(statement))
937
+ if (ts3.isInterfaceDeclaration(statement))
887
938
  add(statement.name.text, "interface", getLine(sourceFile, statement));
888
- else if (ts2.isTypeAliasDeclaration(statement))
939
+ else if (ts3.isTypeAliasDeclaration(statement))
889
940
  add(statement.name.text, "type", getLine(sourceFile, statement));
890
- else if (ts2.isEnumDeclaration(statement))
941
+ else if (ts3.isEnumDeclaration(statement))
891
942
  add(statement.name.text, "enum", getLine(sourceFile, statement));
892
- else if (ts2.isFunctionDeclaration(statement) && statement.name)
943
+ else if (ts3.isFunctionDeclaration(statement) && statement.name)
893
944
  add(statement.name.text, "function", getLine(sourceFile, statement));
894
- else if (ts2.isClassDeclaration(statement) && statement.name)
945
+ else if (ts3.isClassDeclaration(statement) && statement.name)
895
946
  add(statement.name.text, "class", getLine(sourceFile, statement));
896
- else if (ts2.isVariableStatement(statement)) {
947
+ else if (ts3.isVariableStatement(statement)) {
897
948
  for (const declaration of statement.declarationList.declarations) {
898
- if (!ts2.isIdentifier(declaration.name))
949
+ if (!ts3.isIdentifier(declaration.name))
899
950
  continue;
900
951
  const kind = isFunctionLikeInitializer(declaration.initializer) ? "function" : "variable";
901
952
  add(declaration.name.text, kind, getLine(sourceFile, declaration));
@@ -907,15 +958,15 @@ function getComponentFileDeclarations(sourceFile) {
907
958
  function getCompoundComponentNames(sourceFile) {
908
959
  const names = new Set;
909
960
  for (const statement of sourceFile.statements) {
910
- if (!ts2.isExpressionStatement(statement))
961
+ if (!ts3.isExpressionStatement(statement))
911
962
  continue;
912
963
  const { expression } = statement;
913
- if (!ts2.isBinaryExpression(expression) || expression.operatorToken.kind !== ts2.SyntaxKind.EqualsToken)
964
+ if (!ts3.isBinaryExpression(expression) || expression.operatorToken.kind !== ts3.SyntaxKind.EqualsToken)
914
965
  continue;
915
- if (!ts2.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
966
+ if (!ts3.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
916
967
  continue;
917
968
  names.add(expression.left.name.text);
918
- if (ts2.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
969
+ if (ts3.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
919
970
  names.add(expression.right.text);
920
971
  }
921
972
  return names;
@@ -929,15 +980,43 @@ function isRestrictedInternalKind(kind) {
929
980
  function isAllowedComponentExport(declaration, isPage) {
930
981
  if (isComponentValueKind(declaration.kind) && isPascalCaseName(declaration.name))
931
982
  return true;
932
- return isPage && PAGE_RESERVED_EXPORTS.has(declaration.name);
983
+ return isPage && RESERVED_ROUTE_CONFIG_EXPORTS.has(declaration.name);
933
984
  }
934
985
  function isPascalCaseName(name) {
935
986
  return /^[A-Z]/.test(name) && !/^[A-Z0-9_]+$/.test(name);
936
987
  }
937
988
  function isDefaultExportStatement(statement) {
938
- if (ts2.isExportAssignment(statement))
989
+ if (ts3.isExportAssignment(statement))
939
990
  return !statement.isExportEquals;
940
- return !!(ts2.getCombinedModifierFlags(statement) & ts2.ModifierFlags.Default);
991
+ if (!ts3.canHaveModifiers(statement))
992
+ return false;
993
+ return !!ts3.getModifiers(statement)?.some((modifier) => modifier.kind === ts3.SyntaxKind.DefaultKeyword);
994
+ }
995
+ function getBangCommentWarnings(sourceFile) {
996
+ if (!isClientReachableFile(sourceFile.file))
997
+ return [];
998
+ return findPatternLines(sourceFile.content, /(?:^|[^\s/][ \t]+)\/[*/]!/).map((line) => ({
999
+ rule: "akan.file.bang-comment-in-client",
1000
+ scope: "file",
1001
+ severity: "warning",
1002
+ file: sourceFile.file,
1003
+ line,
1004
+ message: "A `//!` or `/*!` marker survives minification and ships to the browser."
1005
+ }));
1006
+ }
1007
+ function isClientReachableFile(file) {
1008
+ if (/\.(test|spec)\.tsx?$/.test(file))
1009
+ return false;
1010
+ if (/\.(constant|store)\.ts$/.test(file))
1011
+ return true;
1012
+ if (/\.(Template|Unit|Util|View|Zone)\.tsx$/.test(file))
1013
+ return true;
1014
+ const segments = file.split("/");
1015
+ if (segments.includes("ui") || segments.includes("webkit"))
1016
+ return /\.tsx?$/.test(file);
1017
+ if (segments.includes("common"))
1018
+ return file.endsWith(".ts");
1019
+ return segments.includes("page") && file.endsWith(".tsx");
941
1020
  }
942
1021
  function getPlaceholderExportWarnings(sourceFile) {
943
1022
  if (!sourceFile.file.endsWith("/index.ts") && !sourceFile.file.endsWith("/index.tsx"))
@@ -967,7 +1046,7 @@ function getDictionaryTextWarnings(sourceFile) {
967
1046
  function getGlobalMutationWarnings(sourceFile) {
968
1047
  const warnings = [];
969
1048
  for (const statement of sourceFile.sourceFile.statements) {
970
- if (ts2.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
1049
+ if (ts3.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
971
1050
  warnings.push({
972
1051
  rule: "akan.file.global-declaration",
973
1052
  scope: "file",
@@ -977,7 +1056,7 @@ function getGlobalMutationWarnings(sourceFile) {
977
1056
  message: "Global declarations require an explicit low-level integration allowlist."
978
1057
  });
979
1058
  }
980
- if (ts2.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
1059
+ if (ts3.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
981
1060
  warnings.push({
982
1061
  rule: "akan.file.window-augmentation",
983
1062
  scope: "file",
@@ -987,7 +1066,7 @@ function getGlobalMutationWarnings(sourceFile) {
987
1066
  message: "Window augmentation should be isolated to approved browser integration files."
988
1067
  });
989
1068
  }
990
- if (ts2.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
1069
+ if (ts3.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
991
1070
  warnings.push({
992
1071
  rule: "akan.file.prototype-mutation",
993
1072
  scope: "file",
@@ -1005,23 +1084,23 @@ function getTopLevelDeclarations(sourceFile) {
1005
1084
  }
1006
1085
  function getTopLevelDeclaration(sourceFile, statement) {
1007
1086
  const line = getLine(sourceFile, statement);
1008
- if (ts2.isClassDeclaration(statement) && statement.name) {
1087
+ if (ts3.isClassDeclaration(statement) && statement.name) {
1009
1088
  return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
1010
1089
  }
1011
- if (ts2.isFunctionDeclaration(statement) && statement.name) {
1090
+ if (ts3.isFunctionDeclaration(statement) && statement.name) {
1012
1091
  return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
1013
1092
  }
1014
- if (ts2.isInterfaceDeclaration(statement)) {
1093
+ if (ts3.isInterfaceDeclaration(statement)) {
1015
1094
  return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
1016
1095
  }
1017
- if (ts2.isTypeAliasDeclaration(statement)) {
1096
+ if (ts3.isTypeAliasDeclaration(statement)) {
1018
1097
  return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
1019
1098
  }
1020
- if (ts2.isEnumDeclaration(statement)) {
1099
+ if (ts3.isEnumDeclaration(statement)) {
1021
1100
  return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
1022
1101
  }
1023
- if (ts2.isVariableStatement(statement)) {
1024
- return statement.declarationList.declarations.filter((declaration) => ts2.isIdentifier(declaration.name)).map((declaration) => ({
1102
+ if (ts3.isVariableStatement(statement)) {
1103
+ return statement.declarationList.declarations.filter((declaration) => ts3.isIdentifier(declaration.name)).map((declaration) => ({
1025
1104
  name: declaration.name.text,
1026
1105
  kind: "variable",
1027
1106
  line: getLine(sourceFile, declaration),
@@ -1029,7 +1108,7 @@ function getTopLevelDeclaration(sourceFile, statement) {
1029
1108
  node: statement
1030
1109
  }));
1031
1110
  }
1032
- if (ts2.isExportDeclaration(statement)) {
1111
+ if (ts3.isExportDeclaration(statement)) {
1033
1112
  return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
1034
1113
  }
1035
1114
  return [];
@@ -1054,9 +1133,9 @@ function isAllowedConstantDeclaration(modelName, declaration) {
1054
1133
  return false;
1055
1134
  if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
1056
1135
  return true;
1057
- if (!ts2.isClassDeclaration(declaration.node))
1136
+ if (!ts3.isClassDeclaration(declaration.node))
1058
1137
  return false;
1059
- const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword);
1138
+ const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts3.SyntaxKind.ExtendsKeyword);
1060
1139
  const expression = heritageClause?.types[0]?.expression;
1061
1140
  return !!expression && expression.getText().startsWith("enumOf(");
1062
1141
  }
@@ -1073,7 +1152,19 @@ function getConventionDescription(suffix, modelName) {
1073
1152
  return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
1074
1153
  return `${modelName}Store`;
1075
1154
  }
1076
- function getLibRootFile(file) {
1155
+ var sysRootTypes = { apps: "app", libs: "lib" };
1156
+ function getSysRootEntry(file) {
1157
+ const segments = file.split("/");
1158
+ const root = segments[0];
1159
+ const name = segments[2];
1160
+ if (!root || !name || segments.length < 3)
1161
+ return null;
1162
+ const type = sysRootTypes[root];
1163
+ if (!type)
1164
+ return null;
1165
+ return { type, name, isDir: segments.length > 3 };
1166
+ }
1167
+ function getLibFacetRootFile(file) {
1077
1168
  const segments = file.split("/");
1078
1169
  if ((segments[0] === "libs" || segments[0] === "apps") && segments.length === 4 && segments[2] === "lib")
1079
1170
  return segments[3];
@@ -1122,13 +1213,13 @@ function getModuleInfo(file) {
1122
1213
  return { moduleName, fileName, kind: "database" };
1123
1214
  }
1124
1215
  function isExportedConst(declaration) {
1125
- return declaration.exported && ts2.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts2.NodeFlags.Const) !== 0;
1216
+ return declaration.exported && ts3.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts3.NodeFlags.Const) !== 0;
1126
1217
  }
1127
1218
  function isExported(node) {
1128
- return !!ts2.getCombinedModifierFlags(node) && !!(ts2.getCombinedModifierFlags(node) & ts2.ModifierFlags.Export);
1219
+ return !!ts3.getCombinedModifierFlags(node) && !!(ts3.getCombinedModifierFlags(node) & ts3.ModifierFlags.Export);
1129
1220
  }
1130
1221
  function isFunctionLikeInitializer(node) {
1131
- return !!node && (ts2.isArrowFunction(node) || ts2.isFunctionExpression(node));
1222
+ return !!node && (ts3.isArrowFunction(node) || ts3.isFunctionExpression(node));
1132
1223
  }
1133
1224
  function getBodyFingerprint(sourceFile, node) {
1134
1225
  if (!node)
@@ -1150,7 +1241,7 @@ async function isDirectory(absolutePath) {
1150
1241
  }
1151
1242
  }
1152
1243
  function getScriptKind(file) {
1153
- return file.endsWith(".tsx") ? ts2.ScriptKind.TSX : ts2.ScriptKind.TS;
1244
+ return file.endsWith(".tsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS;
1154
1245
  }
1155
1246
  function getLine(sourceFile, node) {
1156
1247
  return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;