@akanjs/cli 3.0.0-alpha.10 → 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 (49) hide show
  1. package/.build-stamp +1 -1
  2. package/{agent.command-f0gqkfgz.js → agent.command-a3n15c6t.js} +5 -5
  3. package/{application.command-x903m2qq.js → application.command-rdm3et0g.js} +4 -4
  4. package/{applicationBuildRunner-ha3y8ew8.js → applicationBuildRunner-6m8n71w4.js} +1 -1
  5. package/buildBatch.proc.js +2 -2
  6. package/{capacitorApp-9xswcgx7.js → capacitorApp-mr81qdxq.js} +2 -2
  7. package/{cloud.command-zs6kyf83.js → cloud.command-56aeekqn.js} +7 -7
  8. package/{context.command-fmepgjzg.js → context.command-ysxawe7h.js} +12 -12
  9. package/{guideline.command-n6sw0a0c.js → guideline.command-yqf9a372.js} +2 -2
  10. package/guidelines/conventions/conventions.instruction.md +157 -0
  11. package/guidelines/cssRule/cssRule.instruction.md +7 -0
  12. package/guidelines/framework/framework.instruction.md +1 -0
  13. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +3 -1
  14. package/incrementalBuilder.proc.js +1 -1
  15. package/{index-5sn5w2mt.js → index-3789fdpg.js} +10 -10
  16. package/{index-y4vkyhvv.js → index-4mgkgba3.js} +2 -2
  17. package/{index-xmmzf150.js → index-65aeh789.js} +1 -1
  18. package/{index-d43t8af2.js → index-8pd02prk.js} +2 -2
  19. package/{index-779ske1y.js → index-9va8zayb.js} +1 -1
  20. package/{index-82jrh4hj.js → index-a5cs7d8r.js} +5 -5
  21. package/{index-62955kn5.js → index-bzznxgcx.js} +5 -5
  22. package/{index-qb61h0k7.js → index-cav1azma.js} +51 -5
  23. package/{index-qzdjd4ct.js → index-cv9cxs5k.js} +3 -3
  24. package/{index-774gcx29.js → index-e4b2ae6b.js} +2 -2
  25. package/{index-kdh76j26.js → index-etwh7sd3.js} +1 -1
  26. package/{index-gcv8ncfh.js → index-h5whaj26.js} +2 -2
  27. package/{index-rxkpk7n4.js → index-kcsa1zff.js} +1 -1
  28. package/{index-3vta1tqa.js → index-m90fn8xe.js} +4 -4
  29. package/{index-jhpa13zj.js → index-mewacxye.js} +4 -4
  30. package/{index-4m0bphac.js → index-nwqqssnb.js} +8 -10
  31. package/{index-jf2ya7r2.js → index-yzmqvg23.js} +3 -3
  32. package/{index-z8ptfkzv.js → index-yzsxc30b.js} +15 -12
  33. package/{index-eyyf2149.js → index-z1s5f4e0.js} +3 -3
  34. package/index.js +18 -18
  35. package/{library.command-cv9gdbmg.js → library.command-x6t29a30.js} +3 -3
  36. package/{localRegistry.command-d8fg36zc.js → localRegistry.command-649g1n7b.js} +6 -6
  37. package/{module.command-qyr1s9cn.js → module.command-8w8ys5t3.js} +5 -5
  38. package/{package.command-wsjx0k47.js → package.command-zgy7f3fz.js} +3 -3
  39. package/package.json +2 -2
  40. package/{page.command-rdzg65rm.js → page.command-yjmnkyk2.js} +3 -3
  41. package/{primitive.command-89g38b53.js → primitive.command-gfqaagsg.js} +6 -6
  42. package/{quality.command-0cs2z92d.js → quality.command-ec8r1dn3.js} +361 -90
  43. package/{repair.command-6fphxfwj.js → repair.command-p9ey6mkm.js} +5 -5
  44. package/{scalar.command-xfer69s6.js → scalar.command-9dhmqhtm.js} +4 -4
  45. package/templates/appSample/srvkit/AuthGuard.ts +9 -5
  46. package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
  47. package/templates/workspaceRoot/biome.json.template +1 -0
  48. package/{workflow.command-2ygfqt4f.js → workflow.command-ycwc6zr6.js} +9 -9
  49. package/{workspace.command-vbgpjkf9.js → workspace.command-xhyhajxa.js} +50 -33
@@ -7,11 +7,11 @@ import {
7
7
  command,
8
8
  runner,
9
9
  script
10
- } from "./index-kdh76j26.js";
10
+ } from "./index-etwh7sd3.js";
11
11
  import {
12
12
  appRootAllowedFiles,
13
13
  libFacetRootAllowedFiles
14
- } from "./index-z8ptfkzv.js";
14
+ } from "./index-yzsxc30b.js";
15
15
  import"./index-mxvakhsm.js";
16
16
  import"./index-1577bej2.js";
17
17
  import"./index-67546d0j.js";
@@ -20,13 +20,163 @@ import"./index-r24hmh0q.js";
20
20
  // pkgs/@akanjs/devkit/qualityScanner.ts
21
21
  import { createHash } from "crypto";
22
22
  import { readdir, readFile, stat } from "fs/promises";
23
- import path from "path";
23
+ import path3 from "path";
24
24
  import { RESERVED_ROUTE_CONFIG_EXPORTS } from "akanjs/common";
25
25
  import ignore from "ignore";
26
- import ts2 from "typescript";
26
+ import ts4 from "typescript";
27
27
 
28
- // pkgs/@akanjs/devkit/ssrScanner.ts
28
+ // pkgs/@akanjs/devkit/mcpScanner.ts
29
+ import path from "path";
29
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";
30
180
  var STATIC_COMPONENT_MIN_MASS = 4;
31
181
  var MIXED_COMPONENT_MIN_MASS = 10;
32
182
  var MIXED_COMPONENT_MAX_TOUCHES = 2;
@@ -144,31 +294,31 @@ class SsrScanner {
144
294
  });
145
295
  }
146
296
  }
147
- ts.forEachChild(node, visit);
297
+ ts2.forEachChild(node, visit);
148
298
  };
149
- ts.forEachChild(sourceFile.sourceFile, visit);
299
+ ts2.forEachChild(sourceFile.sourceFile, visit);
150
300
  return warnings;
151
301
  }
152
302
  #isMountEffect(sourceFile, node) {
153
- if (!ts.isCallExpression(node))
303
+ if (!ts2.isCallExpression(node))
154
304
  return false;
155
305
  const callee = node.expression.getText(sourceFile);
156
306
  if (callee !== "useEffect" && callee !== "useLayoutEffect")
157
307
  return false;
158
308
  const deps = node.arguments[1];
159
- return !!deps && ts.isArrayLiteralExpression(deps) && deps.elements.length === 0;
309
+ return !!deps && ts2.isArrayLiteralExpression(deps) && deps.elements.length === 0;
160
310
  }
161
311
  #getLoadCalls(sourceFile, node) {
162
312
  const calls = [];
163
313
  const visit = (child) => {
164
- if (ts.isCallExpression(child)) {
314
+ if (ts2.isCallExpression(child)) {
165
315
  const callee = child.expression.getText(sourceFile);
166
316
  if (/^fetch\.[a-z]/.test(callee) || /^st\.do\.(init|get|view|load|list|count|insight)[A-Z]/.test(callee))
167
317
  calls.push({ callee, node: child });
168
318
  }
169
- ts.forEachChild(child, visit);
319
+ ts2.forEachChild(child, visit);
170
320
  };
171
- ts.forEachChild(node, visit);
321
+ ts2.forEachChild(node, visit);
172
322
  return calls;
173
323
  }
174
324
  #getTemplateStateWarnings(sourceFile) {
@@ -176,7 +326,7 @@ class SsrScanner {
176
326
  return [];
177
327
  const warnings = [];
178
328
  const visit = (node) => {
179
- if (ts.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
329
+ if (ts2.isCallExpression(node) && node.expression.getText(sourceFile.sourceFile) === "useState") {
180
330
  warnings.push({
181
331
  rule: "akan.ssr.template-client-state",
182
332
  scope: "ssr",
@@ -186,9 +336,9 @@ class SsrScanner {
186
336
  message: "Template holds form state in useState. Templates are store-driven and carry no local state."
187
337
  });
188
338
  }
189
- ts.forEachChild(node, visit);
339
+ ts2.forEachChild(node, visit);
190
340
  };
191
- ts.forEachChild(sourceFile.sourceFile, visit);
341
+ ts2.forEachChild(sourceFile.sourceFile, visit);
192
342
  return warnings;
193
343
  }
194
344
  #measureBalance(sourceFiles) {
@@ -229,14 +379,14 @@ class SsrScanner {
229
379
  return components;
230
380
  }
231
381
  #getComponentNodes(statement) {
232
- if (ts.isFunctionDeclaration(statement) && statement.body)
382
+ if (ts2.isFunctionDeclaration(statement) && statement.body)
233
383
  return [{ name: statement.name?.text ?? "default", node: statement.body }];
234
- if (!ts.isVariableStatement(statement))
384
+ if (!ts2.isVariableStatement(statement))
235
385
  return [];
236
386
  return statement.declarationList.declarations.flatMap((declaration) => {
237
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
387
+ if (!ts2.isIdentifier(declaration.name) || !declaration.initializer)
238
388
  return [];
239
- if (!ts.isArrowFunction(declaration.initializer) && !ts.isFunctionExpression(declaration.initializer))
389
+ if (!ts2.isArrowFunction(declaration.initializer) && !ts2.isFunctionExpression(declaration.initializer))
240
390
  return [];
241
391
  return [{ name: declaration.name.text, node: declaration.initializer }];
242
392
  });
@@ -244,7 +394,7 @@ class SsrScanner {
244
394
  #getTouches(sourceFile, node) {
245
395
  const touches = [];
246
396
  const visit = (child) => {
247
- if (ts.isCallExpression(child)) {
397
+ if (ts2.isCallExpression(child)) {
248
398
  const callee = child.expression.getText(sourceFile);
249
399
  const bareName = callee.split(".").pop() ?? callee;
250
400
  if (callee === "createContext" || callee === "lazy")
@@ -252,60 +402,60 @@ class SsrScanner {
252
402
  else if (/^use[A-Z]/.test(bareName) && !SsrScanner.#serverSafeCalls.has(bareName))
253
403
  touches.push(bareName);
254
404
  }
255
- 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)))
256
406
  touches.push(child.name.getText(sourceFile));
257
- if (ts.isPropertyAccessExpression(child)) {
407
+ if (ts2.isPropertyAccessExpression(child)) {
258
408
  const root = getAccessRoot(child);
259
409
  if (root === "st")
260
410
  touches.push("st");
261
411
  else if (SsrScanner.#clientGlobals.has(root))
262
412
  touches.push(root);
263
413
  }
264
- ts.forEachChild(child, visit);
414
+ ts2.forEachChild(child, visit);
265
415
  };
266
- ts.forEachChild(node, visit);
416
+ ts2.forEachChild(node, visit);
267
417
  return touches;
268
418
  }
269
419
  #getTagNames(sourceFile, node) {
270
420
  const tags = new Set;
271
421
  const visit = (child) => {
272
- if (ts.isJsxOpeningElement(child) || ts.isJsxSelfClosingElement(child))
422
+ if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
273
423
  tags.add(child.tagName.getText(sourceFile).split(".")[0]);
274
- ts.forEachChild(child, visit);
424
+ ts2.forEachChild(child, visit);
275
425
  };
276
- ts.forEachChild(node, visit);
426
+ ts2.forEachChild(node, visit);
277
427
  return tags;
278
428
  }
279
429
  #getMass(node) {
280
430
  let mass = 0;
281
431
  const visit = (child) => {
282
- if (ts.isJsxOpeningElement(child) || ts.isJsxSelfClosingElement(child))
432
+ if (ts2.isJsxOpeningElement(child) || ts2.isJsxSelfClosingElement(child))
283
433
  mass += 1;
284
- ts.forEachChild(child, visit);
434
+ ts2.forEachChild(child, visit);
285
435
  };
286
- ts.forEachChild(node, visit);
436
+ ts2.forEachChild(node, visit);
287
437
  return mass;
288
438
  }
289
439
  #hasUseClient(sourceFile) {
290
440
  const first = sourceFile.statements[0];
291
- if (!first || !ts.isExpressionStatement(first) || !ts.isStringLiteral(first.expression))
441
+ if (!first || !ts2.isExpressionStatement(first) || !ts2.isStringLiteral(first.expression))
292
442
  return false;
293
443
  return first.expression.text === "use client";
294
444
  }
295
445
  #hasVendorImport(sourceFile) {
296
- return sourceFile.statements.some((statement) => ts.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
446
+ return sourceFile.statements.some((statement) => ts2.isImportDeclaration(statement) && isVendorSpecifier(getSpecifier(statement)));
297
447
  }
298
448
  #getVendorNames(sourceFile) {
299
449
  const names = new Set;
300
450
  for (const statement of sourceFile.statements) {
301
- if (!ts.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
451
+ if (!ts2.isImportDeclaration(statement) || !isVendorSpecifier(getSpecifier(statement)))
302
452
  continue;
303
453
  const clause = statement.importClause;
304
454
  if (clause?.name)
305
455
  names.add(clause.name.text);
306
- if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings))
456
+ if (clause?.namedBindings && ts2.isNamespaceImport(clause.namedBindings))
307
457
  names.add(clause.namedBindings.name.text);
308
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings))
458
+ if (clause?.namedBindings && ts2.isNamedImports(clause.namedBindings))
309
459
  for (const element of clause.namedBindings.elements)
310
460
  names.add(element.name.text);
311
461
  }
@@ -313,10 +463,10 @@ class SsrScanner {
313
463
  }
314
464
  #importsClientRuntime(sourceFile) {
315
465
  for (const statement of sourceFile.statements) {
316
- if (!ts.isImportDeclaration(statement))
466
+ if (!ts2.isImportDeclaration(statement))
317
467
  continue;
318
468
  const bindings = statement.importClause?.namedBindings;
319
- if (!bindings || !ts.isNamedImports(bindings))
469
+ if (!bindings || !ts2.isNamedImports(bindings))
320
470
  continue;
321
471
  if (bindings.elements.some((element) => SsrScanner.#clientRuntimeImports.has(element.name.text)))
322
472
  return true;
@@ -367,7 +517,7 @@ function getShare(serverMass, clientMass) {
367
517
  return total === 0 ? 1 : serverMass / total;
368
518
  }
369
519
  function getSpecifier(statement) {
370
- return ts.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
520
+ return ts2.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
371
521
  }
372
522
  function isVendorSpecifier(specifier) {
373
523
  if (specifier === "" || specifier.startsWith(".") || specifier.startsWith("/"))
@@ -380,9 +530,125 @@ function isVendorSpecifier(specifier) {
380
530
  }
381
531
  function getAccessRoot(node) {
382
532
  let current = node;
383
- while (ts.isPropertyAccessExpression(current))
533
+ while (ts2.isPropertyAccessExpression(current))
384
534
  current = current.expression;
385
- 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
+ }
386
652
  }
387
653
 
388
654
  // pkgs/@akanjs/devkit/qualityScanner.ts
@@ -439,7 +705,10 @@ var RULE_FIXES = {
439
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.",
440
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.",
441
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.",
442
- "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."
443
712
  };
444
713
  function getRuleFix(rule) {
445
714
  if (rule.startsWith("akan.convention"))
@@ -460,7 +729,9 @@ class AkanQualityScanner {
460
729
  ...sourceFiles.flatMap((sourceFile) => this.#scanConventionQuality(sourceFile)),
461
730
  ...sourceFiles.flatMap((sourceFile) => this.#scanLayoutQuality(sourceFile)),
462
731
  ...abstractFiles.flatMap((abstractFile) => this.#scanAbstractQuality(abstractFile)),
463
- ...ssr.warnings
732
+ ...ssr.warnings,
733
+ ...new McpScanner().scan(sourceFiles),
734
+ ...new StoreScanner().scan(sourceFiles)
464
735
  ];
465
736
  return {
466
737
  workspaceRoot,
@@ -474,7 +745,7 @@ class AkanQualityScanner {
474
745
  const ignoreFilter = ignore().add(await this.#readGitIgnore(workspaceRoot));
475
746
  const files = [];
476
747
  for (const targetRoot of ["apps", "libs"]) {
477
- const absoluteTargetRoot = path.join(workspaceRoot, targetRoot);
748
+ const absoluteTargetRoot = path3.join(workspaceRoot, targetRoot);
478
749
  if (!await isDirectory(absoluteTargetRoot))
479
750
  continue;
480
751
  await this.#walkTargetFiles(workspaceRoot, absoluteTargetRoot, ignoreFilter, files);
@@ -482,7 +753,7 @@ class AkanQualityScanner {
482
753
  return files.sort();
483
754
  }
484
755
  async#readGitIgnore(workspaceRoot) {
485
- const gitIgnorePath = path.join(workspaceRoot, ".gitignore");
756
+ const gitIgnorePath = path3.join(workspaceRoot, ".gitignore");
486
757
  if (!await Bun.file(gitIgnorePath).exists())
487
758
  return [];
488
759
  return (await readFile(gitIgnorePath, "utf8")).split(/\r?\n/);
@@ -490,8 +761,8 @@ class AkanQualityScanner {
490
761
  async#walkTargetFiles(workspaceRoot, currentPath, ignoreFilter, files) {
491
762
  const entries = await readdir(currentPath, { withFileTypes: true });
492
763
  for (const entry of entries) {
493
- const absolutePath = path.join(currentPath, entry.name);
494
- const relativePath = toPosix(path.relative(workspaceRoot, absolutePath));
764
+ const absolutePath = path3.join(currentPath, entry.name);
765
+ const relativePath = toPosix(path3.relative(workspaceRoot, absolutePath));
495
766
  if (shouldSkipPath(relativePath, entry.isDirectory(), ignoreFilter))
496
767
  continue;
497
768
  if (entry.isDirectory()) {
@@ -506,17 +777,17 @@ class AkanQualityScanner {
506
777
  }
507
778
  }
508
779
  async#readSourceFile(workspaceRoot, file) {
509
- const absolutePath = path.join(workspaceRoot, file);
780
+ const absolutePath = path3.join(workspaceRoot, file);
510
781
  const content = await readFile(absolutePath, "utf8");
511
782
  return {
512
783
  file,
513
784
  absolutePath,
514
785
  content,
515
- sourceFile: ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true, getScriptKind(file))
786
+ sourceFile: ts4.createSourceFile(file, content, ts4.ScriptTarget.Latest, true, getScriptKind(file))
516
787
  };
517
788
  }
518
789
  async#readTextFile(workspaceRoot, file) {
519
- return { file, content: await readFile(path.join(workspaceRoot, file), "utf8") };
790
+ return { file, content: await readFile(path3.join(workspaceRoot, file), "utf8") };
520
791
  }
521
792
  #scanGlobalQuality(sourceFiles) {
522
793
  const exportedFunctionLikes = sourceFiles.flatMap((sourceFile) => getExportedFunctionLikes(sourceFile));
@@ -640,7 +911,7 @@ class AkanQualityScanner {
640
911
  const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile.file.endsWith(candidate));
641
912
  if (!suffix)
642
913
  return [];
643
- const modelName = toPascalCase(path.basename(sourceFile.file, suffix));
914
+ const modelName = toPascalCase(path3.basename(sourceFile.file, suffix));
644
915
  const warnings = [];
645
916
  for (const declaration of getTopLevelDeclarations(sourceFile)) {
646
917
  if (isAllowedConventionDeclaration(suffix, modelName, declaration))
@@ -651,7 +922,7 @@ class AkanQualityScanner {
651
922
  severity: "warning",
652
923
  file: sourceFile.file,
653
924
  line: declaration.line,
654
- 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)}.`
655
926
  });
656
927
  }
657
928
  return warnings;
@@ -759,7 +1030,7 @@ function getExportedFunctionLikes(sourceFile) {
759
1030
  const declarations = [];
760
1031
  const nameExempt = isPageRouteFile(sourceFile.file) || isUiComponentFile(sourceFile.file);
761
1032
  for (const statement of sourceFile.sourceFile.statements) {
762
- if (ts2.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
1033
+ if (ts4.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
763
1034
  declarations.push({
764
1035
  name: statement.name.text,
765
1036
  kind: "function",
@@ -769,7 +1040,7 @@ function getExportedFunctionLikes(sourceFile) {
769
1040
  duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, false)
770
1041
  });
771
1042
  }
772
- if (ts2.isClassDeclaration(statement) && statement.name && isExported(statement)) {
1043
+ if (ts4.isClassDeclaration(statement) && statement.name && isExported(statement)) {
773
1044
  declarations.push({
774
1045
  name: statement.name.text,
775
1046
  kind: "class",
@@ -779,9 +1050,9 @@ function getExportedFunctionLikes(sourceFile) {
779
1050
  duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile.file, isEnumClassStatement(sourceFile.sourceFile, statement))
780
1051
  });
781
1052
  }
782
- if (ts2.isVariableStatement(statement) && isExported(statement)) {
1053
+ if (ts4.isVariableStatement(statement) && isExported(statement)) {
783
1054
  for (const declaration of statement.declarationList.declarations) {
784
- if (!ts2.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
1055
+ if (!ts4.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
785
1056
  continue;
786
1057
  declarations.push({
787
1058
  name: declaration.name.text,
@@ -820,14 +1091,14 @@ function isInLibModule(file) {
820
1091
  return (segments[0] === "apps" || segments[0] === "libs") && segments.includes("lib");
821
1092
  }
822
1093
  function isEnumClassStatement(sourceFile, statement) {
823
- if (!ts2.isClassDeclaration(statement))
1094
+ if (!ts4.isClassDeclaration(statement))
824
1095
  return false;
825
- const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword);
1096
+ const heritageClause = statement.heritageClauses?.find((clause) => clause.token === ts4.SyntaxKind.ExtendsKeyword);
826
1097
  const expression = heritageClause?.types[0]?.expression;
827
1098
  return !!expression && expression.getText(sourceFile).startsWith("enumOf(");
828
1099
  }
829
1100
  function getExportedClassNames(sourceFile) {
830
- 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);
831
1102
  }
832
1103
  function isComponentDeclarationFile(file) {
833
1104
  if (!file.endsWith(".tsx"))
@@ -843,7 +1114,7 @@ function isComponentDeclarationFile(file) {
843
1114
  function getComponentFileDeclarations(sourceFile) {
844
1115
  const reExportedNames = new Set;
845
1116
  for (const statement of sourceFile.statements) {
846
- if (ts2.isExportDeclaration(statement) && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
1117
+ if (ts4.isExportDeclaration(statement) && statement.exportClause && ts4.isNamedExports(statement.exportClause)) {
847
1118
  for (const element of statement.exportClause.elements)
848
1119
  reExportedNames.add((element.propertyName ?? element.name).text);
849
1120
  }
@@ -853,19 +1124,19 @@ function getComponentFileDeclarations(sourceFile) {
853
1124
  const isDefaultExport = isDefaultExportStatement(statement);
854
1125
  const inlineExported = isExported(statement);
855
1126
  const add = (name, kind, line) => declarations.push({ name, kind, line, exported: inlineExported || reExportedNames.has(name), isDefaultExport });
856
- if (ts2.isInterfaceDeclaration(statement))
1127
+ if (ts4.isInterfaceDeclaration(statement))
857
1128
  add(statement.name.text, "interface", getLine(sourceFile, statement));
858
- else if (ts2.isTypeAliasDeclaration(statement))
1129
+ else if (ts4.isTypeAliasDeclaration(statement))
859
1130
  add(statement.name.text, "type", getLine(sourceFile, statement));
860
- else if (ts2.isEnumDeclaration(statement))
1131
+ else if (ts4.isEnumDeclaration(statement))
861
1132
  add(statement.name.text, "enum", getLine(sourceFile, statement));
862
- else if (ts2.isFunctionDeclaration(statement) && statement.name)
1133
+ else if (ts4.isFunctionDeclaration(statement) && statement.name)
863
1134
  add(statement.name.text, "function", getLine(sourceFile, statement));
864
- else if (ts2.isClassDeclaration(statement) && statement.name)
1135
+ else if (ts4.isClassDeclaration(statement) && statement.name)
865
1136
  add(statement.name.text, "class", getLine(sourceFile, statement));
866
- else if (ts2.isVariableStatement(statement)) {
1137
+ else if (ts4.isVariableStatement(statement)) {
867
1138
  for (const declaration of statement.declarationList.declarations) {
868
- if (!ts2.isIdentifier(declaration.name))
1139
+ if (!ts4.isIdentifier(declaration.name))
869
1140
  continue;
870
1141
  const kind = isFunctionLikeInitializer(declaration.initializer) ? "function" : "variable";
871
1142
  add(declaration.name.text, kind, getLine(sourceFile, declaration));
@@ -877,15 +1148,15 @@ function getComponentFileDeclarations(sourceFile) {
877
1148
  function getCompoundComponentNames(sourceFile) {
878
1149
  const names = new Set;
879
1150
  for (const statement of sourceFile.statements) {
880
- if (!ts2.isExpressionStatement(statement))
1151
+ if (!ts4.isExpressionStatement(statement))
881
1152
  continue;
882
1153
  const { expression } = statement;
883
- if (!ts2.isBinaryExpression(expression) || expression.operatorToken.kind !== ts2.SyntaxKind.EqualsToken)
1154
+ if (!ts4.isBinaryExpression(expression) || expression.operatorToken.kind !== ts4.SyntaxKind.EqualsToken)
884
1155
  continue;
885
- if (!ts2.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
1156
+ if (!ts4.isPropertyAccessExpression(expression.left) || !isPascalCaseName(expression.left.name.text))
886
1157
  continue;
887
1158
  names.add(expression.left.name.text);
888
- if (ts2.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
1159
+ if (ts4.isIdentifier(expression.right) && isPascalCaseName(expression.right.text))
889
1160
  names.add(expression.right.text);
890
1161
  }
891
1162
  return names;
@@ -905,9 +1176,9 @@ function isPascalCaseName(name) {
905
1176
  return /^[A-Z]/.test(name) && !/^[A-Z0-9_]+$/.test(name);
906
1177
  }
907
1178
  function isDefaultExportStatement(statement) {
908
- if (ts2.isExportAssignment(statement))
1179
+ if (ts4.isExportAssignment(statement))
909
1180
  return !statement.isExportEquals;
910
- return !!(ts2.getCombinedModifierFlags(statement) & ts2.ModifierFlags.Default);
1181
+ return !!(ts4.getCombinedModifierFlags(statement) & ts4.ModifierFlags.Default);
911
1182
  }
912
1183
  function getPlaceholderExportWarnings(sourceFile) {
913
1184
  if (!sourceFile.file.endsWith("/index.ts") && !sourceFile.file.endsWith("/index.tsx"))
@@ -937,7 +1208,7 @@ function getDictionaryTextWarnings(sourceFile) {
937
1208
  function getGlobalMutationWarnings(sourceFile) {
938
1209
  const warnings = [];
939
1210
  for (const statement of sourceFile.sourceFile.statements) {
940
- if (ts2.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
1211
+ if (ts4.isModuleDeclaration(statement) && statement.name.getText(sourceFile.sourceFile) === "global") {
941
1212
  warnings.push({
942
1213
  rule: "akan.file.global-declaration",
943
1214
  scope: "file",
@@ -947,7 +1218,7 @@ function getGlobalMutationWarnings(sourceFile) {
947
1218
  message: "Global declarations require an explicit low-level integration allowlist."
948
1219
  });
949
1220
  }
950
- if (ts2.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
1221
+ if (ts4.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
951
1222
  warnings.push({
952
1223
  rule: "akan.file.window-augmentation",
953
1224
  scope: "file",
@@ -957,7 +1228,7 @@ function getGlobalMutationWarnings(sourceFile) {
957
1228
  message: "Window augmentation should be isolated to approved browser integration files."
958
1229
  });
959
1230
  }
960
- if (ts2.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
1231
+ if (ts4.isExpressionStatement(statement) && statement.expression.getText(sourceFile.sourceFile).includes(".prototype.")) {
961
1232
  warnings.push({
962
1233
  rule: "akan.file.prototype-mutation",
963
1234
  scope: "file",
@@ -975,23 +1246,23 @@ function getTopLevelDeclarations(sourceFile) {
975
1246
  }
976
1247
  function getTopLevelDeclaration(sourceFile, statement) {
977
1248
  const line = getLine(sourceFile, statement);
978
- if (ts2.isClassDeclaration(statement) && statement.name) {
1249
+ if (ts4.isClassDeclaration(statement) && statement.name) {
979
1250
  return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
980
1251
  }
981
- if (ts2.isFunctionDeclaration(statement) && statement.name) {
1252
+ if (ts4.isFunctionDeclaration(statement) && statement.name) {
982
1253
  return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
983
1254
  }
984
- if (ts2.isInterfaceDeclaration(statement)) {
1255
+ if (ts4.isInterfaceDeclaration(statement)) {
985
1256
  return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
986
1257
  }
987
- if (ts2.isTypeAliasDeclaration(statement)) {
1258
+ if (ts4.isTypeAliasDeclaration(statement)) {
988
1259
  return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
989
1260
  }
990
- if (ts2.isEnumDeclaration(statement)) {
1261
+ if (ts4.isEnumDeclaration(statement)) {
991
1262
  return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
992
1263
  }
993
- if (ts2.isVariableStatement(statement)) {
994
- 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) => ({
995
1266
  name: declaration.name.text,
996
1267
  kind: "variable",
997
1268
  line: getLine(sourceFile, declaration),
@@ -999,7 +1270,7 @@ function getTopLevelDeclaration(sourceFile, statement) {
999
1270
  node: statement
1000
1271
  }));
1001
1272
  }
1002
- if (ts2.isExportDeclaration(statement)) {
1273
+ if (ts4.isExportDeclaration(statement)) {
1003
1274
  return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
1004
1275
  }
1005
1276
  return [];
@@ -1024,9 +1295,9 @@ function isAllowedConstantDeclaration(modelName, declaration) {
1024
1295
  return false;
1025
1296
  if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
1026
1297
  return true;
1027
- if (!ts2.isClassDeclaration(declaration.node))
1298
+ if (!ts4.isClassDeclaration(declaration.node))
1028
1299
  return false;
1029
- 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);
1030
1301
  const expression = heritageClause?.types[0]?.expression;
1031
1302
  return !!expression && expression.getText().startsWith("enumOf(");
1032
1303
  }
@@ -1092,13 +1363,13 @@ function getModuleInfo(file) {
1092
1363
  return { moduleName, fileName, kind: "database" };
1093
1364
  }
1094
1365
  function isExportedConst(declaration) {
1095
- 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;
1096
1367
  }
1097
1368
  function isExported(node) {
1098
- return !!ts2.getCombinedModifierFlags(node) && !!(ts2.getCombinedModifierFlags(node) & ts2.ModifierFlags.Export);
1369
+ return !!ts4.getCombinedModifierFlags(node) && !!(ts4.getCombinedModifierFlags(node) & ts4.ModifierFlags.Export);
1099
1370
  }
1100
1371
  function isFunctionLikeInitializer(node) {
1101
- return !!node && (ts2.isArrowFunction(node) || ts2.isFunctionExpression(node));
1372
+ return !!node && (ts4.isArrowFunction(node) || ts4.isFunctionExpression(node));
1102
1373
  }
1103
1374
  function getBodyFingerprint(sourceFile, node) {
1104
1375
  if (!node)
@@ -1120,7 +1391,7 @@ async function isDirectory(absolutePath) {
1120
1391
  }
1121
1392
  }
1122
1393
  function getScriptKind(file) {
1123
- return file.endsWith(".tsx") ? ts2.ScriptKind.TSX : ts2.ScriptKind.TS;
1394
+ return file.endsWith(".tsx") ? ts4.ScriptKind.TSX : ts4.ScriptKind.TS;
1124
1395
  }
1125
1396
  function getLine(sourceFile, node) {
1126
1397
  return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
@@ -1141,7 +1412,7 @@ function toPascalCase(value) {
1141
1412
  return value.replace(/(^|[-_./])([a-zA-Z0-9])/g, (_, __, char) => char.toUpperCase()).replace(/[-_./]/g, "");
1142
1413
  }
1143
1414
  function toPosix(value) {
1144
- return value.split(path.sep).join("/");
1415
+ return value.split(path3.sep).join("/");
1145
1416
  }
1146
1417
  function groupBy(items, getKey) {
1147
1418
  const grouped = new Map;