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

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 (88) hide show
  1. package/.build-stamp +1 -1
  2. package/{agent.command-yv00p0s0.js → agent.command-a3n15c6t.js} +7 -6
  3. package/{application.command-0qsczf4k.js → application.command-rdm3et0g.js} +5 -4
  4. package/{applicationBuildRunner-ct2pxyvw.js → applicationBuildRunner-6m8n71w4.js} +3 -3
  5. package/buildBatch.proc.js +4 -3
  6. package/{capacitorApp-4r9sm3qg.js → capacitorApp-mr81qdxq.js} +5 -2
  7. package/{cloud.command-dxfv18sa.js → cloud.command-56aeekqn.js} +8 -7
  8. package/{context.command-8d9e3k6b.js → context.command-ysxawe7h.js} +14 -13
  9. package/getDirname-m9s0cmhq.js +8 -0
  10. package/{guideline.command-3jbkxzh6.js → guideline.command-yqf9a372.js} +5 -4
  11. package/guidelines/conventions/conventions.generate.json +25 -0
  12. package/guidelines/conventions/conventions.instruction.md +825 -0
  13. package/guidelines/cssRule/cssRule.instruction.md +7 -0
  14. package/guidelines/framework/framework.instruction.md +1 -0
  15. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +24 -0
  16. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +974 -0
  17. package/incrementalBuilder.proc.js +3 -2
  18. package/index-1577bej2.js +7 -0
  19. package/{index-1pvex4az.js → index-3789fdpg.js} +13 -13
  20. package/{index-ec09pws3.js → index-4mgkgba3.js} +3 -3
  21. package/{index-bbkek1bp.js → index-65aeh789.js} +1 -1
  22. package/{index-7c6k4yvg.js → index-8pd02prk.js} +2 -2
  23. package/{index-t3723f99.js → index-9va8zayb.js} +86 -17
  24. package/{index-nm6wghb6.js → index-a5cs7d8r.js} +5 -5
  25. package/{index-e16gnn26.js → index-bzznxgcx.js} +7 -7
  26. package/{index-pyt2b4nw.js → index-cav1azma.js} +103 -22
  27. package/{index-2q1f5j9k.js → index-cv9cxs5k.js} +3 -3
  28. package/{index-a00jg8x8.js → index-e4b2ae6b.js} +49 -32
  29. package/{index-8hsbnyes.js → index-etwh7sd3.js} +4 -2
  30. package/{index-k95xkh4p.js → index-h5whaj26.js} +2 -2
  31. package/{index-tdbas9r0.js → index-j37qq1f2.js} +1 -1
  32. package/{index-rh16j3c7.js → index-j9cxndc0.js} +3 -2
  33. package/{index-rzwcpcc3.js → index-kcsa1zff.js} +1 -1
  34. package/{index-bekddz9q.js → index-m90fn8xe.js} +4 -4
  35. package/{index-yzmhasdb.js → index-mewacxye.js} +7 -7
  36. package/{index-zasydjxd.js → index-nwqqssnb.js} +8 -10
  37. package/{index-mr5s7mqd.js → index-yzmqvg23.js} +3 -3
  38. package/{index-hgna5rjy.js → index-yzsxc30b.js} +94 -47
  39. package/{index-ej426mcr.js → index-z1s5f4e0.js} +34 -6
  40. package/index.js +19 -18
  41. package/{library.command-ebdj1vnm.js → library.command-x6t29a30.js} +4 -3
  42. package/{localRegistry.command-gb178ne8.js → localRegistry.command-649g1n7b.js} +7 -6
  43. package/{module.command-9c1bnsx4.js → module.command-8w8ys5t3.js} +7 -6
  44. package/{package.command-t93p4jkj.js → package.command-zgy7f3fz.js} +4 -3
  45. package/package.json +2 -2
  46. package/{page.command-k3k0meh2.js → page.command-yjmnkyk2.js} +4 -3
  47. package/{primitive.command-de6nktdt.js → primitive.command-gfqaagsg.js} +8 -7
  48. package/{quality.command-z5kth2wa.js → quality.command-ec8r1dn3.js} +369 -128
  49. package/{repair.command-ehf2g112.js → repair.command-p9ey6mkm.js} +6 -5
  50. package/{routeSourceValidator-cncd92fv.js → routeSourceValidator-e4jmbfcx.js} +2 -38
  51. package/{scalar.command-7a2jtv33.js → scalar.command-9dhmqhtm.js} +6 -5
  52. package/templates/app/page/styles.css.template +3 -3
  53. package/templates/appSample/srvkit/AuthGuard.ts +9 -5
  54. package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
  55. package/templates/crudPages/[__model__Id]/edit/page.tsx +1 -1
  56. package/templates/crudPages/new/page.tsx +1 -1
  57. package/templates/crudSinglePage/page.tsx +1 -1
  58. package/templates/lib/db.ts +3 -1
  59. package/templates/lib/useClient.ts +9 -3
  60. package/templates/module/__Model__.Util.tsx +1 -1
  61. package/templates/module/__Model__.Zone.tsx +3 -5
  62. package/templates/workspaceRoot/AGENTS.md.template +0 -948
  63. package/templates/workspaceRoot/biome.json.template +4 -198
  64. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +4 -1
  65. package/{workflow.command-e0tt0vk2.js → workflow.command-ycwc6zr6.js} +13 -12
  66. package/{workspace.command-fpda0evj.js → workspace.command-xhyhajxa.js} +67 -48
  67. package/templates/libRoot/base/baseLogic.ts +0 -14
  68. package/templates/libRoot/base/index.ts +0 -8
  69. package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
  70. package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -33
  71. package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
  72. package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
  73. package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -18
  74. package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
  75. package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
  76. package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
  77. package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
  78. package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -18
  79. package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -61
  80. package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
  81. package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
  82. package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
  83. package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -58
  84. package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
  85. package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
  86. package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
  87. package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +0 -59
  88. package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
@@ -7,21 +7,176 @@ import {
7
7
  command,
8
8
  runner,
9
9
  script
10
- } from "./index-8hsbnyes.js";
11
- import"./index-hgna5rjy.js";
10
+ } from "./index-etwh7sd3.js";
11
+ import {
12
+ appRootAllowedFiles,
13
+ libFacetRootAllowedFiles
14
+ } from "./index-yzsxc30b.js";
12
15
  import"./index-mxvakhsm.js";
16
+ import"./index-1577bej2.js";
13
17
  import"./index-67546d0j.js";
14
18
  import"./index-r24hmh0q.js";
15
19
 
16
20
  // pkgs/@akanjs/devkit/qualityScanner.ts
17
21
  import { createHash } from "crypto";
18
22
  import { readdir, readFile, stat } from "fs/promises";
19
- import path from "path";
23
+ import path3 from "path";
24
+ import { RESERVED_ROUTE_CONFIG_EXPORTS } from "akanjs/common";
20
25
  import ignore from "ignore";
21
- import ts2 from "typescript";
26
+ import ts4 from "typescript";
22
27
 
23
- // pkgs/@akanjs/devkit/ssrScanner.ts
28
+ // pkgs/@akanjs/devkit/mcpScanner.ts
29
+ import path from "path";
24
30
  import ts from "typescript";
31
+
32
+ class McpScanner {
33
+ scan(sourceFiles) {
34
+ const dictionaries = new Map(sourceFiles.filter((sourceFile) => sourceFile.file.endsWith(".dictionary.ts")).map((sourceFile) => [path.dirname(sourceFile.file), sourceFile]));
35
+ return sourceFiles.filter((sourceFile) => sourceFile.file.endsWith(".signal.ts")).flatMap((sourceFile) => this.#scanSignal(sourceFile, dictionaries.get(path.dirname(sourceFile.file))));
36
+ }
37
+ #scanSignal(signal, dictionary) {
38
+ const exposed = McpScanner.#exposedDeclarations(signal);
39
+ if (!exposed.length)
40
+ return [];
41
+ const refName = path.basename(signal.file, ".signal.ts").replace(/^_+/, "");
42
+ const described = dictionary ? McpScanner.#describedEntries(dictionary) : new Map;
43
+ return [
44
+ ...exposed.filter(({ name, kind }) => !McpScanner.#isDescribed(described, refName, name, kind)).map(({ name, kind, line }) => ({
45
+ rule: "akan.mcp.missing-description",
46
+ scope: "mcp",
47
+ severity: "warning",
48
+ file: signal.file,
49
+ line,
50
+ message: `MCP-exposed ${kind} "${name}" has no dictionary .desc(); an agent sees its name and nothing else.`
51
+ })),
52
+ ...exposed.filter(({ guards }) => guards === "missing").map(({ name, kind, line }) => ({
53
+ rule: "akan.mcp.unguarded-exposure",
54
+ scope: "mcp",
55
+ severity: "warning",
56
+ file: signal.file,
57
+ line,
58
+ message: `MCP-exposed ${kind} "${name}" declares no guards; a slice's guards map never reaches a named slice.`
59
+ }))
60
+ ];
61
+ }
62
+ static #isDescribed(described, refName, name, kind) {
63
+ if (described.get(kind)?.has(name))
64
+ return true;
65
+ return kind === "slice" && !!described.get("endpoint")?.has(`${refName}List${McpScanner.#capitalize(name)}`);
66
+ }
67
+ static #exposedDeclarations(signal) {
68
+ const found = [];
69
+ const visit = (node) => {
70
+ if (McpScanner.#isExposeOption(node)) {
71
+ const declaration = McpScanner.#enclosingDeclaration(node, signal.sourceFile);
72
+ if (declaration)
73
+ found.push(declaration);
74
+ }
75
+ ts.forEachChild(node, visit);
76
+ };
77
+ visit(signal.sourceFile);
78
+ return found;
79
+ }
80
+ static #isExposeOption(node) {
81
+ if (!ts.isPropertyAssignment(node) || McpScanner.#propertyName(node) !== "mcp")
82
+ return false;
83
+ if (!ts.isObjectLiteralExpression(node.initializer))
84
+ return false;
85
+ return node.initializer.properties.some((property) => ts.isPropertyAssignment(property) && McpScanner.#propertyName(property) === "expose" && property.initializer.kind === ts.SyntaxKind.TrueKeyword);
86
+ }
87
+ static #enclosingDeclaration(option, sourceFile) {
88
+ let declaration = null;
89
+ for (let node = option.parent;node; node = node.parent) {
90
+ if (!declaration && ts.isPropertyAssignment(node)) {
91
+ declaration = node;
92
+ continue;
93
+ }
94
+ const kind = McpScanner.#factoryKind(node);
95
+ if (!kind || !declaration)
96
+ continue;
97
+ const name = McpScanner.#propertyName(declaration);
98
+ if (!name)
99
+ return null;
100
+ const line = sourceFile.getLineAndCharacterOfPosition(declaration.getStart(sourceFile)).line + 1;
101
+ return { name, kind, line, guards: McpScanner.#guardState(option) };
102
+ }
103
+ return null;
104
+ }
105
+ static #guardState(option) {
106
+ const options = option.parent;
107
+ if (!ts.isObjectLiteralExpression(options))
108
+ return "unknown";
109
+ if (options.properties.some((property) => ts.isSpreadAssignment(property)))
110
+ return "unknown";
111
+ return options.properties.some((property) => ts.isPropertyAssignment(property) && McpScanner.#propertyName(property) === "guards") ? "declared" : "missing";
112
+ }
113
+ static #factoryKind(node) {
114
+ if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression))
115
+ return null;
116
+ const factory = node.expression.text;
117
+ return factory === "slice" || factory === "endpoint" ? factory : null;
118
+ }
119
+ static #describedEntries(dictionary) {
120
+ const described = new Map;
121
+ const visit = (node) => {
122
+ const stage = McpScanner.#dictionaryStage(node);
123
+ if (stage) {
124
+ for (const [name, chain] of McpScanner.#stageEntries(node)) {
125
+ if (!chain.has("desc"))
126
+ continue;
127
+ const names = described.get(stage) ?? new Set;
128
+ names.add(name);
129
+ described.set(stage, names);
130
+ }
131
+ }
132
+ ts.forEachChild(node, visit);
133
+ };
134
+ visit(dictionary.sourceFile);
135
+ return described;
136
+ }
137
+ static #dictionaryStage(node) {
138
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression))
139
+ return null;
140
+ const stage = node.expression.name.text;
141
+ return stage === "endpoint" || stage === "slice" ? stage : null;
142
+ }
143
+ static #stageEntries(stage) {
144
+ const callback = stage.arguments[0];
145
+ if (!callback || !ts.isArrowFunction(callback))
146
+ return [];
147
+ const body = ts.isParenthesizedExpression(callback.body) ? callback.body.expression : callback.body;
148
+ if (!ts.isObjectLiteralExpression(body))
149
+ return [];
150
+ return body.properties.flatMap((property) => {
151
+ if (!ts.isPropertyAssignment(property))
152
+ return [];
153
+ const name = McpScanner.#propertyName(property);
154
+ return name ? [[name, McpScanner.#chainCalls(property.initializer)]] : [];
155
+ });
156
+ }
157
+ static #chainCalls(expression) {
158
+ const calls = new Set;
159
+ let current = expression;
160
+ while (ts.isCallExpression(current) || ts.isPropertyAccessExpression(current)) {
161
+ if (ts.isPropertyAccessExpression(current))
162
+ calls.add(current.name.text);
163
+ current = current.expression;
164
+ }
165
+ return calls;
166
+ }
167
+ static #propertyName(property) {
168
+ const { name } = property;
169
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name))
170
+ return name.text;
171
+ return null;
172
+ }
173
+ static #capitalize(value) {
174
+ return value.charAt(0).toUpperCase() + value.slice(1);
175
+ }
176
+ }
177
+
178
+ // pkgs/@akanjs/devkit/ssrScanner.ts
179
+ import ts2 from "typescript";
25
180
  var STATIC_COMPONENT_MIN_MASS = 4;
26
181
  var MIXED_COMPONENT_MIN_MASS = 10;
27
182
  var MIXED_COMPONENT_MAX_TOUCHES = 2;
@@ -139,31 +294,31 @@ class SsrScanner {
139
294
  });
140
295
  }
141
296
  }
142
- ts.forEachChild(node, visit);
297
+ ts2.forEachChild(node, visit);
143
298
  };
144
- ts.forEachChild(sourceFile.sourceFile, visit);
299
+ ts2.forEachChild(sourceFile.sourceFile, visit);
145
300
  return warnings;
146
301
  }
147
302
  #isMountEffect(sourceFile, node) {
148
- if (!ts.isCallExpression(node))
303
+ if (!ts2.isCallExpression(node))
149
304
  return false;
150
305
  const callee = node.expression.getText(sourceFile);
151
306
  if (callee !== "useEffect" && callee !== "useLayoutEffect")
152
307
  return false;
153
308
  const deps = node.arguments[1];
154
- return !!deps && ts.isArrayLiteralExpression(deps) && deps.elements.length === 0;
309
+ return !!deps && ts2.isArrayLiteralExpression(deps) && deps.elements.length === 0;
155
310
  }
156
311
  #getLoadCalls(sourceFile, node) {
157
312
  const calls = [];
158
313
  const visit = (child) => {
159
- if (ts.isCallExpression(child)) {
314
+ if (ts2.isCallExpression(child)) {
160
315
  const callee = child.expression.getText(sourceFile);
161
316
  if (/^fetch\.[a-z]/.test(callee) || /^st\.do\.(init|get|view|load|list|count|insight)[A-Z]/.test(callee))
162
317
  calls.push({ callee, node: child });
163
318
  }
164
- ts.forEachChild(child, visit);
319
+ ts2.forEachChild(child, visit);
165
320
  };
166
- ts.forEachChild(node, visit);
321
+ ts2.forEachChild(node, visit);
167
322
  return calls;
168
323
  }
169
324
  #getTemplateStateWarnings(sourceFile) {
@@ -171,7 +326,7 @@ class SsrScanner {
171
326
  return [];
172
327
  const warnings = [];
173
328
  const visit = (node) => {
174
- if (ts.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
329
+ if (ts2.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
175
330
  warnings.push({
176
331
  rule: "akan.ssr.template-client-state",
177
332
  scope: "ssr",
@@ -181,9 +336,9 @@ class SsrScanner {
181
336
  message: "Template holds form state in useState. Templates are store-driven and carry no local state."
182
337
  });
183
338
  }
184
- ts.forEachChild(node, visit);
339
+ ts2.forEachChild(node, visit);
185
340
  };
186
- ts.forEachChild(sourceFile.sourceFile, visit);
341
+ ts2.forEachChild(sourceFile.sourceFile, visit);
187
342
  return warnings;
188
343
  }
189
344
  #measureBalance(sourceFiles) {
@@ -224,14 +379,14 @@ class SsrScanner {
224
379
  return components;
225
380
  }
226
381
  #getComponentNodes(statement) {
227
- if (ts.isFunctionDeclaration(statement) && statement.body)
382
+ if (ts2.isFunctionDeclaration(statement) && statement.body)
228
383
  return [{ name: statement.name?.text ?? "default", node: statement.body }];
229
- if (!ts.isVariableStatement(statement))
384
+ if (!ts2.isVariableStatement(statement))
230
385
  return [];
231
386
  return statement.declarationList.declarations.flatMap((declaration) => {
232
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
387
+ if (!ts2.isIdentifier(declaration.name) || !declaration.initializer)
233
388
  return [];
234
- if (!ts.isArrowFunction(declaration.initializer) && !ts.isFunctionExpression(declaration.initializer))
389
+ if (!ts2.isArrowFunction(declaration.initializer) && !ts2.isFunctionExpression(declaration.initializer))
235
390
  return [];
236
391
  return [{ name: declaration.name.text, node: declaration.initializer }];
237
392
  });
@@ -239,7 +394,7 @@ class SsrScanner {
239
394
  #getTouches(sourceFile, node) {
240
395
  const touches = [];
241
396
  const visit = (child) => {
242
- if (ts.isCallExpression(child)) {
397
+ if (ts2.isCallExpression(child)) {
243
398
  const callee = child.expression.getText(sourceFile);
244
399
  const bareName = callee.split(".").pop() ?? callee;
245
400
  if (callee === "createContext" || callee === "lazy")
@@ -247,60 +402,60 @@ class SsrScanner {
247
402
  else if (/^use[A-Z]/.test(bareName) && !SsrScanner.#serverSafeCalls.has(bareName))
248
403
  touches.push(bareName);
249
404
  }
250
- if (ts.isJsxAttribute(child) && /^on[A-Z]/.test(child.name.getText(sourceFile)))
405
+ if (ts2.isJsxAttribute(child) && /^on[A-Z]/.test(child.name.getText(sourceFile)))
251
406
  touches.push(child.name.getText(sourceFile));
252
- if (ts.isPropertyAccessExpression(child)) {
407
+ if (ts2.isPropertyAccessExpression(child)) {
253
408
  const root = getAccessRoot(child);
254
409
  if (root === "st")
255
410
  touches.push("st");
256
411
  else if (SsrScanner.#clientGlobals.has(root))
257
412
  touches.push(root);
258
413
  }
259
- ts.forEachChild(child, visit);
414
+ ts2.forEachChild(child, visit);
260
415
  };
261
- ts.forEachChild(node, visit);
416
+ ts2.forEachChild(node, visit);
262
417
  return touches;
263
418
  }
264
419
  #getTagNames(sourceFile, node) {
265
420
  const tags = new Set;
266
421
  const visit = (child) => {
267
- if (ts.isJsxOpeningElement(child) || ts.isJsxSelfClosingElement(child))
422
+ if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
268
423
  tags.add(child.tagName.getText(sourceFile).split(".")[0]);
269
- ts.forEachChild(child, visit);
424
+ ts2.forEachChild(child, visit);
270
425
  };
271
- ts.forEachChild(node, visit);
426
+ ts2.forEachChild(node, visit);
272
427
  return tags;
273
428
  }
274
429
  #getMass(node) {
275
430
  let mass = 0;
276
431
  const visit = (child) => {
277
- if (ts.isJsxOpeningElement(child) || ts.isJsxSelfClosingElement(child))
432
+ if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
278
433
  mass += 1;
279
- ts.forEachChild(child, visit);
434
+ ts2.forEachChild(child, visit);
280
435
  };
281
- ts.forEachChild(node, visit);
436
+ ts2.forEachChild(node, visit);
282
437
  return mass;
283
438
  }
284
439
  #hasUseClient(sourceFile) {
285
440
  const first = sourceFile.statements[0];
286
- if (!first || !ts.isExpressionStatement(first) || !ts.isStringLiteral(first.expression))
441
+ if (!first || !ts2.isExpressionStatement(first) || !ts2.isStringLiteral(first.expression))
287
442
  return false;
288
443
  return first.expression.text === "use client";
289
444
  }
290
445
  #hasVendorImport(sourceFile) {
291
- return sourceFile.statements.some((statement) => ts.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
446
+ return sourceFile.statements.some((statement) => ts2.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
292
447
  }
293
448
  #getVendorNames(sourceFile) {
294
449
  const names = new Set;
295
450
  for (const statement of sourceFile.statements) {
296
- if (!ts.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
451
+ if (!ts2.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
297
452
  continue;
298
453
  const clause = statement.importClause;
299
454
  if (clause?.name)
300
455
  names.add(clause.name.text);
301
- if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings))
456
+ if (clause?.namedBindings && ts2.isNamespaceImport(clause.namedBindings))
302
457
  names.add(clause.namedBindings.name.text);
303
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings))
458
+ if (clause?.namedBindings && ts2.isNamedImports(clause.namedBindings))
304
459
  for (const element of clause.namedBindings.elements)
305
460
  names.add(element.name.text);
306
461
  }
@@ -308,10 +463,10 @@ class SsrScanner {
308
463
  }
309
464
  #importsClientRuntime(sourceFile) {
310
465
  for (const statement of sourceFile.statements) {
311
- if (!ts.isImportDeclaration(statement))
466
+ if (!ts2.isImportDeclaration(statement))
312
467
  continue;
313
468
  const bindings = statement.importClause?.namedBindings;
314
- if (!bindings || !ts.isNamedImports(bindings))
469
+ if (!bindings || !ts2.isNamedImports(bindings))
315
470
  continue;
316
471
  if (bindings.elements.some((element) => SsrScanner.#clientRuntimeImports.has(element.name.text)))
317
472
  return true;
@@ -362,7 +517,7 @@ function getShare(serverMass, clientMass) {
362
517
  return total === 0 ? 1 : serverMass / total;
363
518
  }
364
519
  function getSpecifier(statement) {
365
- return ts.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
520
+ return ts2.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
366
521
  }
367
522
  function isVendorSpecifier(specifier) {
368
523
  if (specifier === "" || specifier.startsWith(".") || specifier.startsWith("/"))
@@ -375,9 +530,125 @@ function isVendorSpecifier(specifier) {
375
530
  }
376
531
  function getAccessRoot(node) {
377
532
  let current = node;
378
- while (ts.isPropertyAccessExpression(current))
533
+ while (ts2.isPropertyAccessExpression(current))
379
534
  current = current.expression;
380
- return ts.isIdentifier(current) ? current.text : "";
535
+ return ts2.isIdentifier(current) ? current.text : "";
536
+ }
537
+
538
+ // pkgs/@akanjs/devkit/storeScanner.ts
539
+ import path2 from "path";
540
+ import ts3 from "typescript";
541
+
542
+ class StoreScanner {
543
+ scan(sourceFiles) {
544
+ const dictionaries = new Map(sourceFiles.filter((sourceFile) => sourceFile.file.endsWith(".dictionary.ts")).map((sourceFile) => [path2.dirname(sourceFile.file), sourceFile]));
545
+ return sourceFiles.filter((sourceFile) => sourceFile.file.endsWith(".store.ts")).flatMap((sourceFile) => this.#scanStore(sourceFile, dictionaries.get(path2.dirname(sourceFile.file))));
546
+ }
547
+ #scanStore(store, dictionary) {
548
+ const actions = StoreScanner.#customActions(store);
549
+ if (!actions.length)
550
+ return [];
551
+ const described = dictionary ? StoreScanner.#describedEntries(dictionary) : new Map;
552
+ return actions.filter(({ name, fetched }) => fetched.length && !fetched.includes(name)).filter(({ name }) => !described.get("store")?.has(name) && !described.get("endpoint")?.has(name)).map(({ name, line, fetched }) => ({
553
+ rule: "akan.agent.missing-store-description",
554
+ scope: "agent",
555
+ severity: "warning",
556
+ file: store.file,
557
+ line,
558
+ message: `Store action "${name}" calls ${fetched.map((key) => `${key}()`).join(", ")} under a different name and has no dictionary .store() entry, so an agent reading it has the name and nothing else.`
559
+ }));
560
+ }
561
+ static #customActions(store) {
562
+ const actions = [];
563
+ const visit = (node) => {
564
+ if (ts3.isClassDeclaration(node) && StoreScanner.#extendsStore(node)) {
565
+ for (const member of node.members) {
566
+ if (!ts3.isMethodDeclaration(member))
567
+ continue;
568
+ if (member.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.StaticKeyword))
569
+ continue;
570
+ const name = StoreScanner.#memberName(member);
571
+ if (!name)
572
+ continue;
573
+ const line = store.sourceFile.getLineAndCharacterOfPosition(member.getStart(store.sourceFile)).line + 1;
574
+ actions.push({ name, line, fetched: StoreScanner.#fetchedEndpoints(member) });
575
+ }
576
+ }
577
+ ts3.forEachChild(node, visit);
578
+ };
579
+ visit(store.sourceFile);
580
+ return actions;
581
+ }
582
+ static #extendsStore(node) {
583
+ return !!node.heritageClauses?.some((clause) => clause.types.some((type) => ts3.isCallExpression(type.expression) && StoreScanner.#isStoreCall(type.expression)));
584
+ }
585
+ static #isStoreCall(expression) {
586
+ return ts3.isIdentifier(expression.expression) && expression.expression.text === "store";
587
+ }
588
+ static #fetchedEndpoints(member) {
589
+ const fetched = new Set;
590
+ const visit = (node) => {
591
+ if (ts3.isCallExpression(node) && ts3.isPropertyAccessExpression(node.expression) && ts3.isIdentifier(node.expression.expression) && node.expression.expression.text === "fetch")
592
+ fetched.add(node.expression.name.text);
593
+ ts3.forEachChild(node, visit);
594
+ };
595
+ visit(member);
596
+ return [...fetched];
597
+ }
598
+ static #describedEntries(dictionary) {
599
+ const described = new Map;
600
+ const visit = (node) => {
601
+ const stage = StoreScanner.#dictionaryStage(node);
602
+ if (stage) {
603
+ for (const [name, chain] of StoreScanner.#stageEntries(node)) {
604
+ if (!chain.has("desc"))
605
+ continue;
606
+ const names = described.get(stage) ?? new Set;
607
+ names.add(name);
608
+ described.set(stage, names);
609
+ }
610
+ }
611
+ ts3.forEachChild(node, visit);
612
+ };
613
+ visit(dictionary.sourceFile);
614
+ return described;
615
+ }
616
+ static #dictionaryStage(node) {
617
+ if (!ts3.isCallExpression(node) || !ts3.isPropertyAccessExpression(node.expression))
618
+ return null;
619
+ const stage = node.expression.name.text;
620
+ return stage === "store" || stage === "endpoint" ? stage : null;
621
+ }
622
+ static #stageEntries(stage) {
623
+ const callback = stage.arguments[0];
624
+ if (!callback || !ts3.isArrowFunction(callback))
625
+ return [];
626
+ const body = ts3.isParenthesizedExpression(callback.body) ? callback.body.expression : callback.body;
627
+ if (!ts3.isObjectLiteralExpression(body))
628
+ return [];
629
+ return body.properties.flatMap((property) => {
630
+ if (!ts3.isPropertyAssignment(property))
631
+ return [];
632
+ const name = StoreScanner.#memberName(property);
633
+ return name ? [[name, StoreScanner.#chainCalls(property.initializer)]] : [];
634
+ });
635
+ }
636
+ static #chainCalls(expression) {
637
+ const calls = new Set;
638
+ let current = expression;
639
+ while (ts3.isCallExpression(current) || ts3.isPropertyAccessExpression(current)) {
640
+ if (ts3.isPropertyAccessExpression(current))
641
+ calls.add(current.name.text);
642
+ current = current.expression;
643
+ }
644
+ return calls;
645
+ }
646
+ static #memberName(member) {
647
+ const { name } = member;
648
+ if (!name || !ts3.isIdentifier(name) && !ts3.isStringLiteral(name))
649
+ return null;
650
+ return name.text;
651
+ }
381
652
  }
382
653
 
383
654
  // pkgs/@akanjs/devkit/qualityScanner.ts
@@ -404,27 +675,6 @@ var SUGGESTED_RULES = [
404
675
  "Move shared app utilities to apps/*/common instead of creating apps/*/base.",
405
676
  "Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
406
677
  ];
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
678
  var CONVENTION_SUFFIXES = [
429
679
  ".constant.ts",
430
680
  ".dictionary.ts",
@@ -433,20 +683,6 @@ var CONVENTION_SUFFIXES = [
433
683
  ".signal.ts",
434
684
  ".store.ts"
435
685
  ];
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
686
  var RULE_FIXES = {
451
687
  "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
688
  "akan.global.duplicate-exported-function-body": "Extract the shared implementation into a single exported helper and import it, instead of copying the body.",
@@ -469,7 +705,10 @@ var RULE_FIXES = {
469
705
  "akan.ssr.client-static-markup": "Keep the interactive element in the client component and hoist the static subtree into a server component, then accept it as `children` or render it through a Unit/View reference.",
470
706
  "akan.ssr.client-mount-load": "Load the data in the route with `fetch.initX(...)` / `fetch.viewX(...)` and pass the init/view object down as a prop; the client store hydrates from it and the effect goes away.",
471
707
  "akan.ssr.module-missing-server-view": "Add a <Model>.Unit.tsx for list/card rendering and a <Model>.View.tsx for the detail surface, then have the Zone delegate to them.",
472
- "akan.ssr.template-client-state": "Bind the field to the store instead: `value={xForm.field}` with `onChange={st.do.setFieldOnX}`."
708
+ "akan.ssr.template-client-state": "Bind the field to the store instead: `value={xForm.field}` with `onChange={st.do.setFieldOnX}`.",
709
+ "akan.mcp.missing-description": "Add `.desc([en, ko])` to this entry in the module's dictionary \u2014 for a slice, either on the slice entry or on the `<model>List<Slice>` endpoint it generates. Describe when to reach for it, not what it is named.",
710
+ "akan.agent.missing-store-description": "Add the action to the module dictionary's `.store()` stage with a `.desc([en, ko])` saying what it does for the user \u2014 not what the endpoint it calls does. That stage is optional everywhere else: an action named after its endpoint already reads as that endpoint's description.",
711
+ "akan.mcp.unguarded-exposure": "Name the guards in the same option object as `mcp`: `init({ guards: [SignedIn], mcp: { expose: true } })`. Write `guards: [Public]` if anonymous reads are the intent \u2014 the access is the same, but only one of the two is a decision. The `slice()` call's guards map reaches the root slice and base CRUD, never a named slice."
473
712
  };
474
713
  function getRuleFix(rule) {
475
714
  if (rule.startsWith("akan.convention"))
@@ -490,7 +729,9 @@ class AkanQualityScanner {
490
729
  ...sourceFiles.flatMap((sourceFile) => this.#scanConventionQuality(sourceFile)),
491
730
  ...sourceFiles.flatMap((sourceFile) => this.#scanLayoutQuality(sourceFile)),
492
731
  ...abstractFiles.flatMap((abstractFile) => this.#scanAbstractQuality(abstractFile)),
493
- ...ssr.warnings
732
+ ...ssr.warnings,
733
+ ...new McpScanner().scan(sourceFiles),
734
+ ...new StoreScanner().scan(sourceFiles)
494
735
  ];
495
736
  return {
496
737
  workspaceRoot,
@@ -504,7 +745,7 @@ class AkanQualityScanner {
504
745
  const ignoreFilter = ignore().add(await this.#readGitIgnore(workspaceRoot));
505
746
  const files = [];
506
747
  for (const targetRoot of ["apps", "libs"]) {
507
- const absoluteTargetRoot = path.join(workspaceRoot, targetRoot);
748
+ const absoluteTargetRoot = path3.join(workspaceRoot, targetRoot);
508
749
  if (!await isDirectory(absoluteTargetRoot))
509
750
  continue;
510
751
  await this.#walkTargetFiles(workspaceRoot, absoluteTargetRoot, ignoreFilter, files);
@@ -512,7 +753,7 @@ class AkanQualityScanner {
512
753
  return files.sort();
513
754
  }
514
755
  async#readGitIgnore(workspaceRoot) {
515
- const gitIgnorePath = path.join(workspaceRoot, ".gitignore");
756
+ const gitIgnorePath = path3.join(workspaceRoot, ".gitignore");
516
757
  if (!await Bun.file(gitIgnorePath).exists())
517
758
  return [];
518
759
  return (await readFile(gitIgnorePath, "utf8")).split(/\r?\n/);
@@ -520,8 +761,8 @@ class AkanQualityScanner {
520
761
  async#walkTargetFiles(workspaceRoot, currentPath, ignoreFilter, files) {
521
762
  const entries = await readdir(currentPath, { withFileTypes: true });
522
763
  for (const entry of entries) {
523
- const absolutePath = path.join(currentPath, entry.name);
524
- const relativePath = toPosix(path.relative(workspaceRoot, absolutePath));
764
+ const absolutePath = path3.join(currentPath, entry.name);
765
+ const relativePath = toPosix(path3.relative(workspaceRoot, absolutePath));
525
766
  if (shouldSkipPath(relativePath, entry.isDirectory(), ignoreFilter))
526
767
  continue;
527
768
  if (entry.isDirectory()) {
@@ -536,17 +777,17 @@ class AkanQualityScanner {
536
777
  }
537
778
  }
538
779
  async#readSourceFile(workspaceRoot, file) {
539
- const absolutePath = path.join(workspaceRoot, file);
780
+ const absolutePath = path3.join(workspaceRoot, file);
540
781
  const content = await readFile(absolutePath, "utf8");
541
782
  return {
542
783
  file,
543
784
  absolutePath,
544
785
  content,
545
- sourceFile: ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true, getScriptKind(file))
786
+ sourceFile: ts4.createSourceFile(file, content, ts4.ScriptTarget.Latest, true, getScriptKind(file))
546
787
  };
547
788
  }
548
789
  async#readTextFile(workspaceRoot, file) {
549
- return { file, content: await readFile(path.join(workspaceRoot, file), "utf8") };
790
+ return { file, content: await readFile(path3.join(workspaceRoot, file), "utf8") };
550
791
  }
551
792
  #scanGlobalQuality(sourceFiles) {
552
793
  const exportedFunctionLikes = sourceFiles.flatMap((sourceFile) => getExportedFunctionLikes(sourceFile));
@@ -670,7 +911,7 @@ class AkanQualityScanner {
670
911
  const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile.file.endsWith(candidate));
671
912
  if (!suffix)
672
913
  return [];
673
- const modelName = toPascalCase(path.basename(sourceFile.file, suffix));
914
+ const modelName = toPascalCase(path3.basename(sourceFile.file, suffix));
674
915
  const warnings = [];
675
916
  for (const declaration of getTopLevelDeclarations(sourceFile)) {
676
917
  if (isAllowedConventionDeclaration(suffix, modelName, declaration))
@@ -681,7 +922,7 @@ class AkanQualityScanner {
681
922
  severity: "warning",
682
923
  file: sourceFile.file,
683
924
  line: declaration.line,
684
- message: `${path.basename(sourceFile.file)} should not declare top-level ${declaration.kind} "${declaration.name}". Allowed declarations: ${getConventionDescription(suffix, modelName)}.`
925
+ message: `${path3.basename(sourceFile.file)} should not declare top-level ${declaration.kind} "${declaration.name}". Allowed declarations: ${getConventionDescription(suffix, modelName)}.`
685
926
  });
686
927
  }
687
928
  return warnings;
@@ -703,7 +944,7 @@ class AkanQualityScanner {
703
944
  #scanLayoutQuality(sourceFile) {
704
945
  const segments = sourceFile.file.split("/");
705
946
  const warnings = [];
706
- if (segments[0] === "apps" && segments.length === 3 && !APP_ROOT_FILES.has(segments[2])) {
947
+ if (segments[0] === "apps" && segments.length === 3 && !appRootAllowedFiles.has(segments[2])) {
707
948
  warnings.push({
708
949
  rule: "akan.layout.app-root-file",
709
950
  scope: "layout",
@@ -713,7 +954,7 @@ class AkanQualityScanner {
713
954
  });
714
955
  }
715
956
  const libRootFile = getLibRootFile(sourceFile.file);
716
- if (libRootFile && !LIB_ROOT_FILES.has(libRootFile)) {
957
+ if (libRootFile && !libFacetRootAllowedFiles.has(libRootFile)) {
717
958
  warnings.push({
718
959
  rule: "akan.layout.lib-root-file",
719
960
  scope: "layout",
@@ -789,7 +1030,7 @@ function getExportedFunctionLikes(sourceFile) {
789
1030
  const declarations = [];
790
1031
  const nameExempt = isPageRouteFile(sourceFile.file) || isUiComponentFile(sourceFile.file);
791
1032
  for (const statement of sourceFile.sourceFile.statements) {
792
- if (ts2.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
1033
+ if (ts4.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
793
1034
  declarations.push({
794
1035
  name: statement.name.text,
795
1036
  kind: "function",
@@ -799,7 +1040,7 @@ function getExportedFunctionLikes(sourceFile) {
799
1040
  duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, false)
800
1041
  });
801
1042
  }
802
- if (ts2.isClassDeclaration(statement) && statement.name && isExported(statement)) {
1043
+ if (ts4.isClassDeclaration(statement) && statement.name && isExported(statement)) {
803
1044
  declarations.push({
804
1045
  name: statement.name.text,
805
1046
  kind: "class",
@@ -809,9 +1050,9 @@ function getExportedFunctionLikes(sourceFile) {
809
1050
  duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, isEnumClassStatement(sourceFile.sourceFile, statement))
810
1051
  });
811
1052
  }
812
- if (ts2.isVariableStatement(statement) && isExported(statement)) {
1053
+ if (ts4.isVariableStatement(statement) && isExported(statement)) {
813
1054
  for (const declaration of statement.declarationList.declarations) {
814
- if (!ts2.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
1055
+ if (!ts4.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
815
1056
  continue;
816
1057
  declarations.push({
817
1058
  name: declaration.name.text,
@@ -850,14 +1091,14 @@ function isInLibModule(file) {
850
1091
  return (segments[0] === "apps" || segments[0] === "libs") && segments.includes("lib");
851
1092
  }
852
1093
  function isEnumClassStatement(sourceFile, statement) {
853
- if (!ts2.isClassDeclaration(statement))
1094
+ if (!ts4.isClassDeclaration(statement))
854
1095
  return false;
855
- const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword);
1096
+ const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts4.SyntaxKind.ExtendsKeyword);
856
1097
  const expression = heritageClause?.types[0]?.expression;
857
1098
  return !!expression && expression.getText(sourceFile).startsWith("enumOf(");
858
1099
  }
859
1100
  function getExportedClassNames(sourceFile) {
860
- return sourceFile.statements.filter((statement) => ts2.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
1101
+ return sourceFile.statements.filter((statement) => ts4.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
861
1102
  }
862
1103
  function isComponentDeclarationFile(file) {
863
1104
  if (!file.endsWith(".tsx"))
@@ -873,7 +1114,7 @@ function isComponentDeclarationFile(file) {
873
1114
  function getComponentFileDeclarations(sourceFile) {
874
1115
  const reExportedNames = new Set;
875
1116
  for (const statement of sourceFile.statements) {
876
- if (ts2.isExportDeclaration(statement) && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
1117
+ if (ts4.isExportDeclaration(statement) && statement.exportClause && ts4.isNamedExports(statement.exportClause)) {
877
1118
  for (const element of statement.exportClause.elements)
878
1119
  reExportedNames.add((element.propertyName ?? element.name).text);
879
1120
  }
@@ -883,19 +1124,19 @@ function getComponentFileDeclarations(sourceFile) {
883
1124
  const isDefaultExport = isDefaultExportStatement(statement);
884
1125
  const inlineExported = isExported(statement);
885
1126
  const add = (name, kind, line) => declarations.push({ name, kind, line, exported: inlineExported || reExportedNames.has(name), isDefaultExport });
886
- if (ts2.isInterfaceDeclaration(statement))
1127
+ if (ts4.isInterfaceDeclaration(statement))
887
1128
  add(statement.name.text, "interface", getLine(sourceFile, statement));
888
- else if (ts2.isTypeAliasDeclaration(statement))
1129
+ else if (ts4.isTypeAliasDeclaration(statement))
889
1130
  add(statement.name.text, "type", getLine(sourceFile, statement));
890
- else if (ts2.isEnumDeclaration(statement))
1131
+ else if (ts4.isEnumDeclaration(statement))
891
1132
  add(statement.name.text, "enum", getLine(sourceFile, statement));
892
- else if (ts2.isFunctionDeclaration(statement) && statement.name)
1133
+ else if (ts4.isFunctionDeclaration(statement) && statement.name)
893
1134
  add(statement.name.text, "function", getLine(sourceFile, statement));
894
- else if (ts2.isClassDeclaration(statement) && statement.name)
1135
+ else if (ts4.isClassDeclaration(statement) && statement.name)
895
1136
  add(statement.name.text, "class", getLine(sourceFile, statement));
896
- else if (ts2.isVariableStatement(statement)) {
1137
+ else if (ts4.isVariableStatement(statement)) {
897
1138
  for (const declaration of statement.declarationList.declarations) {
898
- if (!ts2.isIdentifier(declaration.name))
1139
+ if (!ts4.isIdentifier(declaration.name))
899
1140
  continue;
900
1141
  const kind = isFunctionLikeInitializer(declaration.initializer) ? "function" : "variable";
901
1142
  add(declaration.name.text, kind, getLine(sourceFile, declaration));
@@ -907,15 +1148,15 @@ function getComponentFileDeclarations(sourceFile) {
907
1148
  function getCompoundComponentNames(sourceFile) {
908
1149
  const names = new Set;
909
1150
  for (const statement of sourceFile.statements) {
910
- if (!ts2.isExpressionStatement(statement))
1151
+ if (!ts4.isExpressionStatement(statement))
911
1152
  continue;
912
1153
  const { expression } = statement;
913
- if (!ts2.isBinaryExpression(expression) || expression.operatorToken.kind !== ts2.SyntaxKind.EqualsToken)
1154
+ if (!ts4.isBinaryExpression(expression) || expression.operatorToken.kind !== ts4.SyntaxKind.EqualsToken)
914
1155
  continue;
915
- if (!ts2.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
1156
+ if (!ts4.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
916
1157
  continue;
917
1158
  names.add(expression.left.name.text);
918
- if (ts2.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
1159
+ if (ts4.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
919
1160
  names.add(expression.right.text);
920
1161
  }
921
1162
  return names;
@@ -929,15 +1170,15 @@ function isRestrictedInternalKind(kind) {
929
1170
  function isAllowedComponentExport(declaration, isPage) {
930
1171
  if (isComponentValueKind(declaration.kind) && isPascalCaseName(declaration.name))
931
1172
  return true;
932
- return isPage && PAGE_RESERVED_EXPORTS.has(declaration.name);
1173
+ return isPage && RESERVED_ROUTE_CONFIG_EXPORTS.has(declaration.name);
933
1174
  }
934
1175
  function isPascalCaseName(name) {
935
1176
  return /^[A-Z]/.test(name) && !/^[A-Z0-9_]+$/.test(name);
936
1177
  }
937
1178
  function isDefaultExportStatement(statement) {
938
- if (ts2.isExportAssignment(statement))
1179
+ if (ts4.isExportAssignment(statement))
939
1180
  return !statement.isExportEquals;
940
- return !!(ts2.getCombinedModifierFlags(statement) & ts2.ModifierFlags.Default);
1181
+ return !!(ts4.getCombinedModifierFlags(statement) & ts4.ModifierFlags.Default);
941
1182
  }
942
1183
  function getPlaceholderExportWarnings(sourceFile) {
943
1184
  if (!sourceFile.file.endsWith("/index.ts") && !sourceFile.file.endsWith("/index.tsx"))
@@ -967,7 +1208,7 @@ function getDictionaryTextWarnings(sourceFile) {
967
1208
  function getGlobalMutationWarnings(sourceFile) {
968
1209
  const warnings = [];
969
1210
  for (const statement of sourceFile.sourceFile.statements) {
970
- if (ts2.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
1211
+ if (ts4.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
971
1212
  warnings.push({
972
1213
  rule: "akan.file.global-declaration",
973
1214
  scope: "file",
@@ -977,7 +1218,7 @@ function getGlobalMutationWarnings(sourceFile) {
977
1218
  message: "Global declarations require an explicit low-level integration allowlist."
978
1219
  });
979
1220
  }
980
- if (ts2.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
1221
+ if (ts4.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
981
1222
  warnings.push({
982
1223
  rule: "akan.file.window-augmentation",
983
1224
  scope: "file",
@@ -987,7 +1228,7 @@ function getGlobalMutationWarnings(sourceFile) {
987
1228
  message: "Window augmentation should be isolated to approved browser integration files."
988
1229
  });
989
1230
  }
990
- if (ts2.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
1231
+ if (ts4.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
991
1232
  warnings.push({
992
1233
  rule: "akan.file.prototype-mutation",
993
1234
  scope: "file",
@@ -1005,23 +1246,23 @@ function getTopLevelDeclarations(sourceFile) {
1005
1246
  }
1006
1247
  function getTopLevelDeclaration(sourceFile, statement) {
1007
1248
  const line = getLine(sourceFile, statement);
1008
- if (ts2.isClassDeclaration(statement) && statement.name) {
1249
+ if (ts4.isClassDeclaration(statement) && statement.name) {
1009
1250
  return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
1010
1251
  }
1011
- if (ts2.isFunctionDeclaration(statement) && statement.name) {
1252
+ if (ts4.isFunctionDeclaration(statement) && statement.name) {
1012
1253
  return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
1013
1254
  }
1014
- if (ts2.isInterfaceDeclaration(statement)) {
1255
+ if (ts4.isInterfaceDeclaration(statement)) {
1015
1256
  return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
1016
1257
  }
1017
- if (ts2.isTypeAliasDeclaration(statement)) {
1258
+ if (ts4.isTypeAliasDeclaration(statement)) {
1018
1259
  return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
1019
1260
  }
1020
- if (ts2.isEnumDeclaration(statement)) {
1261
+ if (ts4.isEnumDeclaration(statement)) {
1021
1262
  return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
1022
1263
  }
1023
- if (ts2.isVariableStatement(statement)) {
1024
- return statement.declarationList.declarations.filter((declaration) => ts2.isIdentifier(declaration.name)).map((declaration) => ({
1264
+ if (ts4.isVariableStatement(statement)) {
1265
+ return statement.declarationList.declarations.filter((declaration) => ts4.isIdentifier(declaration.name)).map((declaration) => ({
1025
1266
  name: declaration.name.text,
1026
1267
  kind: "variable",
1027
1268
  line: getLine(sourceFile, declaration),
@@ -1029,7 +1270,7 @@ function getTopLevelDeclaration(sourceFile, statement) {
1029
1270
  node: statement
1030
1271
  }));
1031
1272
  }
1032
- if (ts2.isExportDeclaration(statement)) {
1273
+ if (ts4.isExportDeclaration(statement)) {
1033
1274
  return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
1034
1275
  }
1035
1276
  return [];
@@ -1054,9 +1295,9 @@ function isAllowedConstantDeclaration(modelName, declaration) {
1054
1295
  return false;
1055
1296
  if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
1056
1297
  return true;
1057
- if (!ts2.isClassDeclaration(declaration.node))
1298
+ if (!ts4.isClassDeclaration(declaration.node))
1058
1299
  return false;
1059
- const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword);
1300
+ const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts4.SyntaxKind.ExtendsKeyword);
1060
1301
  const expression = heritageClause?.types[0]?.expression;
1061
1302
  return !!expression && expression.getText().startsWith("enumOf(");
1062
1303
  }
@@ -1122,13 +1363,13 @@ function getModuleInfo(file) {
1122
1363
  return { moduleName, fileName, kind: "database" };
1123
1364
  }
1124
1365
  function isExportedConst(declaration) {
1125
- return declaration.exported && ts2.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts2.NodeFlags.Const) !== 0;
1366
+ return declaration.exported && ts4.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts4.NodeFlags.Const) !== 0;
1126
1367
  }
1127
1368
  function isExported(node) {
1128
- return !!ts2.getCombinedModifierFlags(node) && !!(ts2.getCombinedModifierFlags(node) & ts2.ModifierFlags.Export);
1369
+ return !!ts4.getCombinedModifierFlags(node) && !!(ts4.getCombinedModifierFlags(node) & ts4.ModifierFlags.Export);
1129
1370
  }
1130
1371
  function isFunctionLikeInitializer(node) {
1131
- return !!node && (ts2.isArrowFunction(node) || ts2.isFunctionExpression(node));
1372
+ return !!node && (ts4.isArrowFunction(node) || ts4.isFunctionExpression(node));
1132
1373
  }
1133
1374
  function getBodyFingerprint(sourceFile, node) {
1134
1375
  if (!node)
@@ -1150,7 +1391,7 @@ async function isDirectory(absolutePath) {
1150
1391
  }
1151
1392
  }
1152
1393
  function getScriptKind(file) {
1153
- return file.endsWith(".tsx") ? ts2.ScriptKind.TSX : ts2.ScriptKind.TS;
1394
+ return file.endsWith(".tsx") ? ts4.ScriptKind.TSX : ts4.ScriptKind.TS;
1154
1395
  }
1155
1396
  function getLine(sourceFile, node) {
1156
1397
  return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
@@ -1171,7 +1412,7 @@ function toPascalCase(value) {
1171
1412
  return value.replace(/(^|[-_./])([a-zA-Z0-9])/g, (_, __, char) => char.toUpperCase()).replace(/[-_./]/g, "");
1172
1413
  }
1173
1414
  function toPosix(value) {
1174
- return value.split(path.sep).join("/");
1415
+ return value.split(path3.sep).join("/");
1175
1416
  }
1176
1417
  function groupBy(items, getKey) {
1177
1418
  const grouped = new Map;