@appilots/cli 0.11.3 → 0.13.0

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.
package/dist/index.mjs CHANGED
@@ -1,19 +1,356 @@
1
1
  import fs, { readFile, mkdir, writeFile } from 'fs/promises';
2
+ import { createHash } from 'crypto';
3
+ import traverse5 from '@babel/traverse';
4
+ import * as BabelTypes from '@babel/types';
2
5
  import * as path2 from 'path';
3
6
  import path2__default, { join, resolve } from 'path';
4
- import traverse4 from '@babel/traverse';
5
- import * as BabelTypes from '@babel/types';
6
7
  import fastGlob from 'fast-glob';
7
8
  import * as parser from '@babel/parser';
8
9
  import { parse } from '@babel/parser';
9
10
  import { promises, readFileSync, writeFileSync, existsSync, statSync, readdirSync } from 'fs';
10
- import { createHash } from 'crypto';
11
11
 
12
12
  var __defProp = Object.defineProperty;
13
13
  var __export = (target, all) => {
14
14
  for (var name in all)
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
+ var MAX_HOPS = 8;
18
+ function unwrap(path11) {
19
+ while (path11.isTSAsExpression() || path11.isTSTypeAssertion() || path11.isTSNonNullExpression() || path11.isTSSatisfiesExpression() || path11.isParenthesizedExpression())
20
+ path11 = path11.get("expression");
21
+ return path11;
22
+ }
23
+ function constantValue(path11, depth = 0) {
24
+ if (depth > MAX_HOPS) return void 0;
25
+ path11 = unwrap(path11);
26
+ if (!path11.isIdentifier()) return path11;
27
+ const binding = path11.scope.getBinding(path11.node.name);
28
+ if (!binding?.constant || !binding.path.isVariableDeclarator()) return path11;
29
+ const init = binding.path.get("init");
30
+ const resolved = init.node ? constantValue(init, depth + 1) : void 0;
31
+ if (resolved?.isObjectExpression() && binding.referencePaths.some(
32
+ (reference) => !reference.parentPath?.isJSXSpreadAttribute() && !reference.parentPath?.isSpreadElement()
33
+ ))
34
+ return void 0;
35
+ return resolved;
36
+ }
37
+ function propValue(path11, name) {
38
+ const readObject = (path12) => {
39
+ const resolved = constantValue(path12);
40
+ if (!resolved?.isObjectExpression()) return { blocked: true };
41
+ for (const property of [...resolved.get("properties")].reverse()) {
42
+ if (property.isSpreadElement()) {
43
+ const found = readObjectBounded(property.get("argument"));
44
+ if (found.value || found.blocked) return found;
45
+ } else if (property.isObjectProperty() || property.isObjectMethod()) {
46
+ if (property.node.computed) return { blocked: true };
47
+ const key = property.node.key;
48
+ if ((BabelTypes.isIdentifier(key) ? key.name : BabelTypes.isStringLiteral(key) ? key.value : "") === name)
49
+ return property.isObjectProperty() ? { value: property.get("value") } : { value: property };
50
+ }
51
+ }
52
+ return {};
53
+ };
54
+ let objectBudget = MAX_HOPS;
55
+ const readObjectBounded = (path12) => objectBudget-- > 0 ? readObject(path12) : { blocked: true };
56
+ for (const attr of [...path11.get("attributes")].reverse()) {
57
+ if (attr.isJSXAttribute() && BabelTypes.isJSXIdentifier(attr.node.name, { name })) {
58
+ const value = attr.get("value");
59
+ return value.isJSXExpressionContainer() ? unwrap(value.get("expression")) : value.node ? value : void 0;
60
+ }
61
+ if (attr.isJSXSpreadAttribute()) {
62
+ const found = readObjectBounded(attr.get("argument"));
63
+ if (found.value || found.blocked) return found.value;
64
+ }
65
+ }
66
+ return void 0;
67
+ }
68
+ function importIdentity(path11, depth = 0) {
69
+ if (depth > MAX_HOPS) return void 0;
70
+ path11 = unwrap(path11);
71
+ if (path11.isIdentifier() || path11.isJSXIdentifier()) {
72
+ const binding = path11.scope.getBinding(path11.node.name);
73
+ if (!binding?.constant) return void 0;
74
+ const declaration = binding.path;
75
+ if (declaration.parentPath?.isImportDeclaration()) {
76
+ const module = declaration.parentPath.node.source.value;
77
+ if (declaration.isImportSpecifier()) {
78
+ const imported = declaration.node.imported;
79
+ return { module, imported: BabelTypes.isIdentifier(imported) ? imported.name : imported.value };
80
+ }
81
+ if (declaration.isImportDefaultSpecifier()) return { module, imported: "default" };
82
+ if (declaration.isImportNamespaceSpecifier()) return { module, imported: "*" };
83
+ }
84
+ if (declaration.isVariableDeclarator() && declaration.get("init").node)
85
+ return importIdentity(declaration.get("init"), depth + 1);
86
+ }
87
+ if (path11.isJSXMemberExpression() || path11.isMemberExpression() && !path11.node.computed) {
88
+ const origin = importIdentity(path11.get("object"), depth + 1);
89
+ const key = path11.node.property;
90
+ if (origin && (BabelTypes.isIdentifier(key) || BabelTypes.isJSXIdentifier(key)) && (origin.imported === "*" || origin.module === "react" && origin.imported === "default"))
91
+ return { module: origin.module, imported: key.name };
92
+ }
93
+ return void 0;
94
+ }
95
+ function handlerFunction(path11, depth = 0) {
96
+ if (depth > MAX_HOPS) return void 0;
97
+ path11 = unwrap(path11);
98
+ if (path11.isFunction()) return path11;
99
+ if (path11.isIdentifier()) {
100
+ const binding = path11.scope.getBinding(path11.node.name);
101
+ if (!binding?.constant) return void 0;
102
+ if (binding.path.isFunctionDeclaration()) return binding.path;
103
+ if (binding.path.isVariableDeclarator() && binding.path.get("init").node)
104
+ return handlerFunction(binding.path.get("init"), depth + 1);
105
+ }
106
+ if (path11.isCallExpression()) {
107
+ const origin = importIdentity(path11.get("callee"));
108
+ if (origin?.module === "react" && origin.imported === "useCallback") {
109
+ const callback = path11.get("arguments")[0];
110
+ if (callback) return handlerFunction(callback, depth + 1);
111
+ }
112
+ }
113
+ if (path11.isMemberExpression() && !path11.node.computed && BabelTypes.isThisExpression(path11.node.object)) {
114
+ const key = path11.node.property;
115
+ if (!BabelTypes.isIdentifier(key)) return void 0;
116
+ const owner = path11.findParent((p) => p.isClassDeclaration() || p.isClassExpression());
117
+ if (!owner || !(owner.isClassDeclaration() || owner.isClassExpression())) return void 0;
118
+ for (const member of owner.get("body").get("body")) {
119
+ if (!(member.isClassMethod() || member.isClassProperty()) || member.node.computed || member.node.static)
120
+ continue;
121
+ if (!BabelTypes.isIdentifier(member.node.key, { name: key.name })) continue;
122
+ if (member.isClassMethod() && member.node.kind === "method") return member;
123
+ if (member.isClassProperty() && member.get("value").node)
124
+ return handlerFunction(member.get("value"), depth + 1);
125
+ }
126
+ }
127
+ return void 0;
128
+ }
129
+
130
+ // src/extractors/control-evidence.ts
131
+ function symbol(node) {
132
+ if (BabelTypes.isTSAsExpression(node) || BabelTypes.isTSTypeAssertion(node) || BabelTypes.isTSNonNullExpression(node) || BabelTypes.isTSSatisfiesExpression(node))
133
+ return symbol(node.expression);
134
+ if (BabelTypes.isIdentifier(node) || BabelTypes.isJSXIdentifier(node)) return node.name;
135
+ if (BabelTypes.isThisExpression(node)) return "this";
136
+ if (BabelTypes.isMemberExpression(node) && !node.computed || BabelTypes.isJSXMemberExpression(node)) {
137
+ const object = symbol(node.object), property = symbol(node.property);
138
+ return object && property ? `${object}.${property}` : void 0;
139
+ }
140
+ if (BabelTypes.isUnaryExpression(node) && node.operator === "!") {
141
+ const value = symbol(node.argument);
142
+ return value ? `!${value}` : void 0;
143
+ }
144
+ return void 0;
145
+ }
146
+ function attribute(node, name) {
147
+ return node.attributes.find(
148
+ (a) => BabelTypes.isJSXAttribute(a) && BabelTypes.isJSXIdentifier(a.name, { name })
149
+ );
150
+ }
151
+ var ICON_LIBRARIES = [
152
+ "lucide-react-native",
153
+ "lucide-react",
154
+ "@tamagui/lucide-icons",
155
+ "@expo/vector-icons",
156
+ "@react-native-vector-icons/",
157
+ "react-native-vector-icons/"
158
+ ];
159
+ function iconLibrary(module) {
160
+ return ICON_LIBRARIES.some(
161
+ (name) => name.endsWith("/") ? module.startsWith(name) : module === name || module.startsWith(name + "/")
162
+ );
163
+ }
164
+ function add(values, value, max = 12) {
165
+ if (value && value.length <= 240 && values.length < max && !values.includes(value))
166
+ values.push(value);
167
+ }
168
+ function iconName(path11, explicitIcon = false) {
169
+ const origin = importIdentity(path11);
170
+ if (origin && iconLibrary(origin.module) && origin.imported !== "*")
171
+ return origin.imported === "default" ? origin.module : `${origin.module}:${origin.imported}`;
172
+ const name = symbol(path11.node);
173
+ return name && (explicitIcon || /icon/i.test(name)) ? name : void 0;
174
+ }
175
+ function iconsInElement(path11, explicitIcon = false) {
176
+ const name = iconName(path11.get("name"), explicitIcon);
177
+ if (!name) return void 0;
178
+ const glyph = propValue(path11, "name");
179
+ const value = glyph && constantValue(glyph);
180
+ return value?.isStringLiteral() ? `${name}:${value.node.value}` : name;
181
+ }
182
+ function hasInteraction(path11) {
183
+ return ["onPress", "onLongPress", "onClick"].some(
184
+ (name) => attribute(path11.node, name) || propValue(path11, name)
185
+ );
186
+ }
187
+ function collectPresentationIcons(path11, icons) {
188
+ const namedIconProps = [
189
+ "icon",
190
+ "prefix",
191
+ "suffix",
192
+ "left",
193
+ "right",
194
+ "leadingIcon",
195
+ "trailingIcon",
196
+ "startIcon",
197
+ "endIcon",
198
+ "renderIcon"
199
+ ];
200
+ const props = new Set(namedIconProps);
201
+ for (const attr of path11.node.attributes)
202
+ if (BabelTypes.isJSXAttribute(attr) && BabelTypes.isJSXIdentifier(attr.name) && !/^on[A-Z]/.test(attr.name.name))
203
+ props.add(attr.name.name);
204
+ for (const prop of props) {
205
+ const icon = propValue(path11, prop);
206
+ if (icon) {
207
+ const value = constantValue(icon);
208
+ if (value?.isStringLiteral()) {
209
+ if (prop.toLowerCase().includes("icon")) add(icons, value.node.value, 8);
210
+ } else {
211
+ const explicitIcon = prop.toLowerCase() === "icon";
212
+ if (icon.isJSXElement()) {
213
+ if (hasInteraction(icon.get("openingElement"))) continue;
214
+ add(icons, iconsInElement(icon.get("openingElement"), explicitIcon), 8);
215
+ }
216
+ icon.traverse({
217
+ JSXAttribute(attr) {
218
+ if (BabelTypes.isJSXIdentifier(attr.node.name) && /^on[A-Z]/.test(attr.node.name.name))
219
+ attr.skip();
220
+ },
221
+ JSXElement(child) {
222
+ const opening = child.get("openingElement");
223
+ if (hasInteraction(opening)) {
224
+ child.skip();
225
+ return;
226
+ }
227
+ add(icons, iconsInElement(opening, explicitIcon), 8);
228
+ }
229
+ });
230
+ if (!icons.length && (namedIconProps.includes(prop) || explicitIcon))
231
+ add(icons, iconName(icon, explicitIcon), 8);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ function collectIcons(path11) {
237
+ const icons = [];
238
+ collectPresentationIcons(path11, icons);
239
+ const jsx = path11.parentPath;
240
+ if (jsx.isJSXElement())
241
+ jsx.traverse({
242
+ // JSX mentioned inside an event callback is not a rendered child icon.
243
+ JSXAttribute(attr) {
244
+ attr.skip();
245
+ },
246
+ JSXElement(child) {
247
+ const opening = child.get("openingElement");
248
+ if (hasInteraction(opening)) {
249
+ child.skip();
250
+ return;
251
+ }
252
+ add(icons, iconsInElement(opening), 8);
253
+ collectPresentationIcons(opening, icons);
254
+ }
255
+ });
256
+ return icons;
257
+ }
258
+ function conditionsAt(path11) {
259
+ const conditions = [];
260
+ let child = path11;
261
+ for (let parent = child.parentPath; parent && !parent.isFunction(); child = parent, parent = parent.parentPath) {
262
+ let test;
263
+ let negated = false;
264
+ if (parent.isLogicalExpression() && parent.node.right === child.node) {
265
+ if (parent.node.operator === "&&") test = parent.node.left;
266
+ if (parent.node.operator === "||") {
267
+ test = parent.node.left;
268
+ negated = true;
269
+ }
270
+ } else if (parent.isConditionalExpression() && parent.node.test !== child.node) {
271
+ test = parent.node.test;
272
+ negated = parent.node.alternate === child.node;
273
+ } else if (parent.isIfStatement() && parent.node.test !== child.node) {
274
+ test = parent.node.test;
275
+ negated = parent.node.alternate === child.node;
276
+ }
277
+ const name = symbol(test);
278
+ if (name) add(conditions, negated ? name.startsWith("!") ? name.slice(1) : `!${name}` : name);
279
+ }
280
+ return conditions;
281
+ }
282
+ function collectHandlerEvidence(expression, evidence) {
283
+ const seen = /* @__PURE__ */ new Set();
284
+ let budget = 100;
285
+ const visit = (path11, depth) => {
286
+ if (depth > 4 || budget-- <= 0) return;
287
+ const fn = handlerFunction(path11);
288
+ if (!fn || seen.has(fn.node)) return;
289
+ seen.add(fn.node);
290
+ fn.traverse({
291
+ // Ignore uncalled helper definitions; inline callbacks remain source evidence.
292
+ Function(nested) {
293
+ if (!nested.parentPath.isCallExpression() && !nested.parentPath.isObjectProperty())
294
+ nested.skip();
295
+ },
296
+ CallExpression(call) {
297
+ if (budget-- <= 0) {
298
+ call.skip();
299
+ return;
300
+ }
301
+ add(evidence.calls, symbol(call.node.callee));
302
+ for (const arg of call.node.arguments) add(evidence.argumentBindings, symbol(arg));
303
+ visit(call.get("callee"), depth + 1);
304
+ if (!BabelTypes.isMemberExpression(call.node.callee) || call.node.callee.computed || !BabelTypes.isIdentifier(call.node.callee.property, { name: "alert" }))
305
+ return;
306
+ const callee = call.get("callee");
307
+ if (!callee.isMemberExpression()) return;
308
+ const origin = importIdentity(callee.get("object"));
309
+ if (origin?.module !== "react-native" || origin.imported !== "Alert") return;
310
+ const options = call.node.arguments[2];
311
+ const destructiveOption = BabelTypes.isArrayExpression(options) && options.elements.some(
312
+ (option) => BabelTypes.isObjectExpression(option) && option.properties.some(
313
+ (p) => BabelTypes.isObjectProperty(p) && !p.computed && symbol(p.key) === "style" && BabelTypes.isStringLiteral(p.value, { value: "destructive" })
314
+ )
315
+ );
316
+ if (destructiveOption)
317
+ evidence.nativeConfirmation = {
318
+ title: symbol(call.node.arguments[0]),
319
+ destructiveOption
320
+ };
321
+ }
322
+ });
323
+ };
324
+ visit(unwrap(expression), 0);
325
+ }
326
+ function extractControlEvidence(ast, source, file) {
327
+ const candidates = [];
328
+ const sourceHash = createHash("sha256").update(source).digest("hex");
329
+ traverse5(ast, {
330
+ JSXOpeningElement(path11) {
331
+ const node = path11.node;
332
+ if (attribute(node, "__appilotsControl")) return;
333
+ const value = propValue(path11, "onPress");
334
+ if (!value && !attribute(node, "onPress") || value?.isNullLiteral() || value?.isJSXEmptyExpression())
335
+ return;
336
+ const icons = collectIcons(path11);
337
+ if (!icons.length || node.end == null) return;
338
+ const evidence = {
339
+ version: 1,
340
+ siteId: createHash("sha256").update(`${file}:${sourceHash}:${node.start}`).digest("hex").slice(0, 20),
341
+ component: symbol(node.name) ?? "unknown",
342
+ icons,
343
+ ...value && symbol(value.node) ? { handler: symbol(value.node) } : {},
344
+ calls: [],
345
+ argumentBindings: [],
346
+ conditions: conditionsAt(path11)
347
+ };
348
+ if (value) collectHandlerEvidence(value, evidence);
349
+ candidates.push({ evidence, sourceHash, offset: node.end - (node.selfClosing ? 2 : 1) });
350
+ }
351
+ });
352
+ return candidates;
353
+ }
17
354
  function byCodeUnit(a, b) {
18
355
  return a < b ? -1 : a > b ? 1 : 0;
19
356
  }
@@ -135,7 +472,7 @@ function classifyJsxComponent(name, element) {
135
472
  }
136
473
  function collectFunctions(ast) {
137
474
  const handlers = /* @__PURE__ */ new Map();
138
- traverse4(ast, {
475
+ traverse5(ast, {
139
476
  FunctionDeclaration: (nodePath) => {
140
477
  if (nodePath.node.id?.name) handlers.set(nodePath.node.id.name, nodePath.node);
141
478
  },
@@ -238,7 +575,7 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
238
575
  }
239
576
  };
240
577
  if (fn.body) {
241
- traverse4(fn.body, {
578
+ traverse5(fn.body, {
242
579
  noScope: true,
243
580
  enter: (nodePath) => inspectNode(nodePath.node)
244
581
  });
@@ -266,7 +603,7 @@ function setterToStateName(setterName) {
266
603
  }
267
604
  function extractNavigationCalls(ast) {
268
605
  const calls = [];
269
- traverse4(ast, {
606
+ traverse5(ast, {
270
607
  noScope: !BabelTypes.isFile(ast),
271
608
  CallExpression: (nodePath) => {
272
609
  const node = nodePath.node;
@@ -417,6 +754,7 @@ var ScreenAnalyzer = class {
417
754
  routeTargetFiles;
418
755
  /** §D: Count of screens filtered out in strict mode (available after analyze()) */
419
756
  screensFilteredOut = 0;
757
+ controlEvidenceFiles = {};
420
758
  constructor(config, options) {
421
759
  this.config = config;
422
760
  this.routeTargetFiles = options?.routeTargetFiles ?? /* @__PURE__ */ new Set();
@@ -517,6 +855,11 @@ var ScreenAnalyzer = class {
517
855
  title: registerScreenMeta?.title,
518
856
  description: registerScreenMeta?.description,
519
857
  components,
858
+ controlCandidates: extractControlEvidence(
859
+ ast,
860
+ source,
861
+ path2__default.relative(this.config.rootDir, filePath)
862
+ ),
520
863
  forms,
521
864
  actions,
522
865
  navigationTargets,
@@ -527,6 +870,8 @@ var ScreenAnalyzer = class {
527
870
  ...permissionsFromJsDoc.isPii ? { isPii: true } : {}
528
871
  } : {}
529
872
  };
873
+ if (descriptor.controlCandidates?.length)
874
+ this.controlEvidenceFiles[path2__default.relative(this.config.rootDir, filePath).split(path2__default.sep).join("/")] = descriptor.controlCandidates;
530
875
  descriptor.__hasRegisterScreen = hasRegisterScreenCall;
531
876
  return descriptor;
532
877
  }
@@ -536,7 +881,7 @@ var ScreenAnalyzer = class {
536
881
  */
537
882
  detectRegisterScreenCall(ast) {
538
883
  let found = false;
539
- traverse4(ast, {
884
+ traverse5(ast, {
540
885
  CallExpression: (nodePath) => {
541
886
  if (found) return;
542
887
  const callee = nodePath.node.callee;
@@ -553,7 +898,7 @@ var ScreenAnalyzer = class {
553
898
  */
554
899
  extractRegisterScreenMetadata(ast) {
555
900
  let metadata = null;
556
- traverse4(ast, {
901
+ traverse5(ast, {
557
902
  CallExpression: (nodePath) => {
558
903
  const callee = nodePath.node.callee;
559
904
  if (BabelTypes.isIdentifier(callee) && callee.name === "registerScreen" || BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.property) && callee.property.name === "registerScreen") {
@@ -850,7 +1195,7 @@ var ScreenAnalyzer = class {
850
1195
  */
851
1196
  extractDefaultComponentName(ast) {
852
1197
  let componentName = "";
853
- traverse4(ast, {
1198
+ traverse5(ast, {
854
1199
  ExportDefaultDeclaration: (nodePath) => {
855
1200
  const declaration = nodePath.node.declaration;
856
1201
  if (BabelTypes.isFunctionDeclaration(declaration) && declaration.id?.name) {
@@ -875,7 +1220,7 @@ var ScreenAnalyzer = class {
875
1220
  */
876
1221
  extractNavigationTargets(ast) {
877
1222
  const targets = /* @__PURE__ */ new Set();
878
- traverse4(ast, {
1223
+ traverse5(ast, {
879
1224
  CallExpression: (nodePath) => {
880
1225
  const callee = nodePath.node.callee;
881
1226
  if (BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.object) && callee.object.name === "navigation" && BabelTypes.isIdentifier(callee.property) && callee.property.name === "navigate") {
@@ -894,7 +1239,7 @@ var ScreenAnalyzer = class {
894
1239
  extractForms(ast) {
895
1240
  const forms = [];
896
1241
  const fields = /* @__PURE__ */ new Map();
897
- traverse4(ast, {
1242
+ traverse5(ast, {
898
1243
  JSXOpeningElement: (nodePath) => {
899
1244
  const element = nodePath.node;
900
1245
  if (BabelTypes.isJSXIdentifier(element.name)) {
@@ -1015,7 +1360,7 @@ var ScreenAnalyzer = class {
1015
1360
  extractComponents(ast) {
1016
1361
  const components = [];
1017
1362
  const seen = /* @__PURE__ */ new Set();
1018
- traverse4(ast, {
1363
+ traverse5(ast, {
1019
1364
  JSXOpeningElement: (nodePath) => {
1020
1365
  const element = nodePath.node;
1021
1366
  if (BabelTypes.isJSXIdentifier(element.name)) {
@@ -1079,7 +1424,7 @@ var ScreenAnalyzer = class {
1079
1424
  const actionLabels = new Map(
1080
1425
  actions.filter((a) => a.label).map((a) => [this.normalizeLabel(a.label), a])
1081
1426
  );
1082
- traverse4(ast, {
1427
+ traverse5(ast, {
1083
1428
  JSXOpeningElement: (nodePath) => {
1084
1429
  const element = nodePath.node;
1085
1430
  if (BabelTypes.isJSXIdentifier(element.name)) {
@@ -1148,7 +1493,7 @@ var ScreenAnalyzer = class {
1148
1493
  }
1149
1494
  collectButtonHandlersByLabel(ast) {
1150
1495
  const out = /* @__PURE__ */ new Map();
1151
- traverse4(ast, {
1496
+ traverse5(ast, {
1152
1497
  JSXOpeningElement: (nodePath) => {
1153
1498
  const element = nodePath.node;
1154
1499
  if (!BabelTypes.isJSXIdentifier(element.name)) return;
@@ -1230,7 +1575,7 @@ var ScreenAnalyzer = class {
1230
1575
  }
1231
1576
  };
1232
1577
  if (fn.body) {
1233
- traverse4(
1578
+ traverse5(
1234
1579
  fn.body,
1235
1580
  {
1236
1581
  noScope: true,
@@ -1427,7 +1772,7 @@ var ScreenAnalyzer = class {
1427
1772
  extractCollections(ast) {
1428
1773
  const renderItemFns = this.collectRenderItemFunctions(ast);
1429
1774
  const collections = [];
1430
- traverse4(ast, {
1775
+ traverse5(ast, {
1431
1776
  JSXOpeningElement: (nodePath) => {
1432
1777
  const element = nodePath.node;
1433
1778
  if (!BabelTypes.isJSXIdentifier(element.name)) return;
@@ -1464,7 +1809,7 @@ var ScreenAnalyzer = class {
1464
1809
  }
1465
1810
  collectRenderItemFunctions(ast) {
1466
1811
  const out = /* @__PURE__ */ new Map();
1467
- traverse4(ast, {
1812
+ traverse5(ast, {
1468
1813
  VariableDeclarator: (nodePath) => {
1469
1814
  if (!BabelTypes.isIdentifier(nodePath.node.id)) return;
1470
1815
  const init = nodePath.node.init;
@@ -1507,7 +1852,7 @@ var ScreenAnalyzer = class {
1507
1852
  }
1508
1853
  extractRowAction(fn) {
1509
1854
  let action;
1510
- traverse4(
1855
+ traverse5(
1511
1856
  fn.body,
1512
1857
  {
1513
1858
  noScope: true,
@@ -1560,7 +1905,7 @@ var ScreenAnalyzer = class {
1560
1905
  } else if (BabelTypes.isIdentifier(firstParam)) {
1561
1906
  itemNames.add(firstParam.name);
1562
1907
  }
1563
- traverse4(
1908
+ traverse5(
1564
1909
  fn.body,
1565
1910
  {
1566
1911
  noScope: true,
@@ -1602,7 +1947,7 @@ var ScreenAnalyzer = class {
1602
1947
  inferSearchField(ast, dataSource) {
1603
1948
  if (!dataSource) return void 0;
1604
1949
  let queryBinding;
1605
- traverse4(ast, {
1950
+ traverse5(ast, {
1606
1951
  CallExpression: (nodePath) => {
1607
1952
  const node = nodePath.node;
1608
1953
  if (!BabelTypes.isMemberExpression(node.callee)) return;
@@ -1613,7 +1958,7 @@ var ScreenAnalyzer = class {
1613
1958
  const fn = node.arguments[0];
1614
1959
  if (!BabelTypes.isArrowFunctionExpression(fn) && !BabelTypes.isFunctionExpression(fn))
1615
1960
  return;
1616
- traverse4(
1961
+ traverse5(
1617
1962
  fn.body,
1618
1963
  {
1619
1964
  noScope: true,
@@ -1655,7 +2000,7 @@ var ScreenAnalyzer = class {
1655
2000
  }
1656
2001
  };
1657
2002
  var EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
1658
- var MAX_HOPS = 8;
2003
+ var MAX_HOPS2 = 8;
1659
2004
  var ModuleGraph = class {
1660
2005
  asts = /* @__PURE__ */ new Map();
1661
2006
  resolved = /* @__PURE__ */ new Map();
@@ -1774,7 +2119,7 @@ var ModuleGraph = class {
1774
2119
  * Devolve o arquivo e o nome sob o qual ele é definido lá.
1775
2120
  */
1776
2121
  resolveBinding(file, name, hops = 0) {
1777
- if (hops > MAX_HOPS) return null;
2122
+ if (hops > MAX_HOPS2) return null;
1778
2123
  if (this.topLevelInit(file, name) !== null) return { file, name };
1779
2124
  const binding = this.imports(file).get(name);
1780
2125
  if (binding) {
@@ -1828,7 +2173,7 @@ var ModuleGraph = class {
1828
2173
  * com interpolação nem valor calculado, de propósito.
1829
2174
  */
1830
2175
  stringConstant(file, node, hops = 0) {
1831
- if (!node || hops > MAX_HOPS) return null;
2176
+ if (!node || hops > MAX_HOPS2) return null;
1832
2177
  if (BabelTypes.isStringLiteral(node)) return node.value;
1833
2178
  if (BabelTypes.isTemplateLiteral(node)) {
1834
2179
  return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? null : null;
@@ -1857,7 +2202,7 @@ var ModuleGraph = class {
1857
2202
  * que é como o `pocketpal` monta todas as telas do Drawer).
1858
2203
  */
1859
2204
  componentFile(file, node, hops = 0) {
1860
- if (!node || hops > MAX_HOPS) return null;
2205
+ if (!node || hops > MAX_HOPS2) return null;
1861
2206
  if (BabelTypes.isCallExpression(node)) {
1862
2207
  for (const arg of node.arguments) {
1863
2208
  if (BabelTypes.isIdentifier(arg) || BabelTypes.isMemberExpression(arg)) {
@@ -2053,7 +2398,7 @@ function workspaceGlobs(dir) {
2053
2398
  }
2054
2399
  function expandWorkspaceGlobs(root, globs) {
2055
2400
  const out = [];
2056
- const add = (dir) => {
2401
+ const add2 = (dir) => {
2057
2402
  const pkgPath = path2__default.join(dir, "package.json");
2058
2403
  if (!existsSync(pkgPath)) return;
2059
2404
  try {
@@ -2074,12 +2419,12 @@ function expandWorkspaceGlobs(root, globs) {
2074
2419
  for (const entry of entries) {
2075
2420
  const dir = path2__default.join(parent, entry);
2076
2421
  try {
2077
- if (statSync(dir).isDirectory()) add(dir);
2422
+ if (statSync(dir).isDirectory()) add2(dir);
2078
2423
  } catch {
2079
2424
  }
2080
2425
  }
2081
2426
  } else if (!glob.includes("*")) {
2082
- add(path2__default.join(root, glob));
2427
+ add2(path2__default.join(root, glob));
2083
2428
  }
2084
2429
  }
2085
2430
  return out.sort((a, b) => b.name.length - a.name.length);
@@ -2399,7 +2744,7 @@ var NavigationAnalyzer = class {
2399
2744
  const ast = this.graph.parse(filePath);
2400
2745
  if (!ast) return [];
2401
2746
  const out = /* @__PURE__ */ new Map();
2402
- traverse4(ast, {
2747
+ traverse5(ast, {
2403
2748
  CallExpression: (nodePath) => {
2404
2749
  const callee = nodePath.node.callee;
2405
2750
  if (!BabelTypes.isIdentifier(callee)) return;
@@ -2560,7 +2905,7 @@ var NavigationAnalyzer = class {
2560
2905
  const ast = this.graph.parse(filePath);
2561
2906
  if (!ast) return [];
2562
2907
  const found = /* @__PURE__ */ new Map();
2563
- traverse4(ast, {
2908
+ traverse5(ast, {
2564
2909
  JSXElement: (nodePath) => {
2565
2910
  const { node } = nodePath;
2566
2911
  const openingElement = node.openingElement;
@@ -2632,7 +2977,7 @@ var NavigationAnalyzer = class {
2632
2977
  if (!ast) return [];
2633
2978
  const byNavigator = /* @__PURE__ */ new Map();
2634
2979
  const resolved = /* @__PURE__ */ new Map();
2635
- traverse4(ast, {
2980
+ traverse5(ast, {
2636
2981
  JSXElement: (nodePath) => {
2637
2982
  const name = nodePath.node.openingElement.name;
2638
2983
  if (!BabelTypes.isJSXMemberExpression(name) || !BabelTypes.isJSXIdentifier(name.object)) return;
@@ -2865,7 +3210,7 @@ var NavigationAnalyzer = class {
2865
3210
  ...this.config.parserPlugins || []
2866
3211
  ]
2867
3212
  });
2868
- traverse4(ast, {
3213
+ traverse5(ast, {
2869
3214
  TSTypeAliasDeclaration: (nodePath) => {
2870
3215
  const { node } = nodePath;
2871
3216
  const typeName = node.id.name;
@@ -2938,7 +3283,7 @@ var NavigationAnalyzer = class {
2938
3283
  if (type.type === "TSUndefinedKeyword") return "undefined";
2939
3284
  if (type.type === "TSNullKeyword") return "null";
2940
3285
  if (type.type === "TSUnionType") {
2941
- return type.types.map((t13) => this.typeToString(t13)).join(" | ");
3286
+ return type.types.map((t15) => this.typeToString(t15)).join(" | ");
2942
3287
  }
2943
3288
  if (type.type === "TSTypeLiteral") {
2944
3289
  return "object";
@@ -2959,7 +3304,7 @@ var NavigationAnalyzer = class {
2959
3304
  /** Attach parsed type params to navigator screens */
2960
3305
  attachParamsToNavigators(navigators, types) {
2961
3306
  for (const navigator of navigators) {
2962
- const matchingType = types.find((t13) => t13.type === navigator.type);
3307
+ const matchingType = types.find((t15) => t15.type === navigator.type);
2963
3308
  if (matchingType) {
2964
3309
  for (const screen of navigator.screens) {
2965
3310
  const screenParams = matchingType.paramEntries.get(screen.name);
@@ -3048,7 +3393,7 @@ var ComponentAnalyzer = class {
3048
3393
  plugins: ["jsx", "typescript", ["decorators", { decoratorsBeforeExport: true }]]
3049
3394
  });
3050
3395
  const components = [];
3051
- traverse4(ast, {
3396
+ traverse5(ast, {
3052
3397
  JSXElement: (path11) => {
3053
3398
  const component = this.extractComponentFromJSXElement(path11.node);
3054
3399
  if (component) {
@@ -3176,12 +3521,12 @@ var FormAnalyzer = class {
3176
3521
  this.stateVariables.clear();
3177
3522
  this.inputElements = [];
3178
3523
  this.submitButtons = [];
3179
- traverse4(ast, {
3524
+ traverse5(ast, {
3180
3525
  CallExpression: (path11) => {
3181
3526
  this.extractStateVariables(path11.node);
3182
3527
  }
3183
3528
  });
3184
- traverse4(ast, {
3529
+ traverse5(ast, {
3185
3530
  JSXElement: (path11) => {
3186
3531
  this.extractFormElements(path11.node);
3187
3532
  }
@@ -3217,23 +3562,23 @@ var FormAnalyzer = class {
3217
3562
  for (const attr of openingElement.attributes) {
3218
3563
  if (BabelTypes.isJSXAttribute(attr) && BabelTypes.isJSXIdentifier(attr.name)) {
3219
3564
  const propName = attr.name.name;
3220
- const propValue = this.extractAttributeValue(attr.value);
3565
+ const propValue2 = this.extractAttributeValue(attr.value);
3221
3566
  switch (propName) {
3222
3567
  case "label":
3223
- info.label = propValue;
3568
+ info.label = propValue2;
3224
3569
  break;
3225
3570
  case "placeholder":
3226
- info.placeholder = propValue;
3571
+ info.placeholder = propValue2;
3227
3572
  break;
3228
3573
  case "keyboardType":
3229
- info.keyboardType = propValue;
3574
+ info.keyboardType = propValue2;
3230
3575
  break;
3231
3576
  case "testID":
3232
- info.testID = propValue;
3577
+ info.testID = propValue2;
3233
3578
  break;
3234
3579
  case "appilotsId":
3235
- info.appilotsId = propValue;
3236
- if (propValue) info.varName = propValue;
3580
+ info.appilotsId = propValue2;
3581
+ if (propValue2) info.varName = propValue2;
3237
3582
  break;
3238
3583
  case "value":
3239
3584
  if (attr.value && BabelTypes.isJSXExpressionContainer(attr.value) && BabelTypes.isIdentifier(attr.value.expression)) {
@@ -3282,7 +3627,7 @@ var FormAnalyzer = class {
3282
3627
  }
3283
3628
  extractValidationRules(ast) {
3284
3629
  const rules = {};
3285
- traverse4(ast, {
3630
+ traverse5(ast, {
3286
3631
  IfStatement: (path11) => {
3287
3632
  const test = path11.node.test;
3288
3633
  const rule = this.extractRuleFromCondition(test);
@@ -3436,7 +3781,7 @@ async function composeAffordances(options) {
3436
3781
  const exclusive = children.filter((c) => (fanIn.get(c) ?? 0) <= MAX_FAN_IN);
3437
3782
  if (exclusive.length === 0) continue;
3438
3783
  const actionIds = new Set(screen.actions.map((a) => a.id));
3439
- const targetIds = new Set((screen.targets ?? []).map((t13) => t13.id));
3784
+ const targetIds = new Set((screen.targets ?? []).map((t15) => t15.id));
3440
3785
  const formIds = new Set(screen.forms.map((f) => f.id));
3441
3786
  let gained = false;
3442
3787
  for (const file of exclusive) {
@@ -3578,6 +3923,7 @@ var ReactNativePlatformAnalyzer = class {
3578
3923
  });
3579
3924
  return {
3580
3925
  screens: enrichedScreens,
3926
+ controlEvidenceFiles: screenAnalyzer.controlEvidenceFiles,
3581
3927
  navigation,
3582
3928
  analyzedFiles: screenFiles.length,
3583
3929
  ...screenAnalyzer.screensFilteredOut > 0 ? { screensFilteredOut: screenAnalyzer.screensFilteredOut } : {},
@@ -3842,7 +4188,7 @@ function extractWebNavigationCalls(ast) {
3842
4188
  calls.push({ method: "navigate", targetPath: node.right.value });
3843
4189
  }
3844
4190
  };
3845
- traverse4(ast, {
4191
+ traverse5(ast, {
3846
4192
  noScope: !BabelTypes.isFile(ast),
3847
4193
  enter: (nodePath) => inspect(nodePath.node)
3848
4194
  });
@@ -3964,7 +4310,7 @@ var WebScreenAnalyzer = class {
3964
4310
  // ── registerScreen ────────────────────────────────────────────────
3965
4311
  detectRegisterScreenCall(ast) {
3966
4312
  let found = false;
3967
- traverse4(ast, {
4313
+ traverse5(ast, {
3968
4314
  CallExpression: (nodePath) => {
3969
4315
  if (found) return;
3970
4316
  if (isRegisterScreenCallee(nodePath.node.callee)) {
@@ -3977,7 +4323,7 @@ var WebScreenAnalyzer = class {
3977
4323
  }
3978
4324
  extractRegisterScreenMetadata(ast) {
3979
4325
  let plain = null;
3980
- traverse4(ast, {
4326
+ traverse5(ast, {
3981
4327
  CallExpression: (nodePath) => {
3982
4328
  if (!isRegisterScreenCallee(nodePath.node.callee)) return;
3983
4329
  const arg = nodePath.node.arguments[0];
@@ -4025,7 +4371,7 @@ var WebScreenAnalyzer = class {
4025
4371
  extractComponentName(ast) {
4026
4372
  let defaultName = "";
4027
4373
  let firstExported = "";
4028
- traverse4(ast, {
4374
+ traverse5(ast, {
4029
4375
  ExportDefaultDeclaration: (nodePath) => {
4030
4376
  const declaration = nodePath.node.declaration;
4031
4377
  if (BabelTypes.isFunctionDeclaration(declaration) && declaration.id?.name) {
@@ -4054,7 +4400,7 @@ var WebScreenAnalyzer = class {
4054
4400
  extractComponents(ast) {
4055
4401
  const components = [];
4056
4402
  const seen = /* @__PURE__ */ new Set();
4057
- traverse4(ast, {
4403
+ traverse5(ast, {
4058
4404
  JSXOpeningElement: (nodePath) => {
4059
4405
  const element = nodePath.node;
4060
4406
  const name = getJsxElementName(element);
@@ -4075,7 +4421,7 @@ var WebScreenAnalyzer = class {
4075
4421
  // ── <label htmlFor> association ───────────────────────────────────
4076
4422
  collectHtmlForLabels(ast) {
4077
4423
  const labels = /* @__PURE__ */ new Map();
4078
- traverse4(ast, {
4424
+ traverse5(ast, {
4079
4425
  JSXElement: (nodePath) => {
4080
4426
  const element = nodePath.node;
4081
4427
  if (getJsxElementName(element.openingElement) !== "label") return;
@@ -4118,7 +4464,7 @@ var WebScreenAnalyzer = class {
4118
4464
  formBuckets.set(formElement, bucket);
4119
4465
  return bucket;
4120
4466
  };
4121
- traverse4(ast, {
4467
+ traverse5(ast, {
4122
4468
  JSXElement: (nodePath) => {
4123
4469
  const element = nodePath.node;
4124
4470
  const name = getJsxElementName(element.openingElement);
@@ -4135,7 +4481,7 @@ var WebScreenAnalyzer = class {
4135
4481
  if (!bucket.fields.has(field.name)) bucket.fields.set(field.name, field);
4136
4482
  }
4137
4483
  });
4138
- traverse4(ast, {
4484
+ traverse5(ast, {
4139
4485
  JSXElement: (nodePath) => {
4140
4486
  const element = nodePath.node;
4141
4487
  const name = getJsxElementName(element.openingElement);
@@ -4313,7 +4659,7 @@ var WebScreenAnalyzer = class {
4313
4659
  const actionLabels = new Map(
4314
4660
  actions.filter((a) => a.label).map((a) => [normalizeLabel(a.label), a])
4315
4661
  );
4316
- traverse4(ast, {
4662
+ traverse5(ast, {
4317
4663
  JSXElement: (nodePath) => {
4318
4664
  const element = nodePath.node;
4319
4665
  const name = getJsxElementName(element.openingElement);
@@ -4544,7 +4890,7 @@ var WebScreenAnalyzer = class {
4544
4890
  for (const call of extractWebNavigationCalls(ast)) {
4545
4891
  if (call.targetPath && call.targetPath.startsWith("/")) targets.add(call.targetPath);
4546
4892
  }
4547
- traverse4(ast, {
4893
+ traverse5(ast, {
4548
4894
  JSXOpeningElement: (nodePath) => {
4549
4895
  const element = nodePath.node;
4550
4896
  const name = getJsxElementName(element);
@@ -4564,7 +4910,7 @@ var WebScreenAnalyzer = class {
4564
4910
  extractCollections(ast) {
4565
4911
  const collections = [];
4566
4912
  const seen = /* @__PURE__ */ new Set();
4567
- traverse4(ast, {
4913
+ traverse5(ast, {
4568
4914
  JSXExpressionContainer: (nodePath) => {
4569
4915
  const expr = nodePath.node.expression;
4570
4916
  if (!BabelTypes.isCallExpression(expr) || !BabelTypes.isMemberExpression(expr.callee) || !BabelTypes.isIdentifier(expr.callee.object) || !BabelTypes.isIdentifier(expr.callee.property) || expr.callee.property.name !== "map") {
@@ -4676,7 +5022,7 @@ function firstCalledFunctionName(node) {
4676
5022
  }
4677
5023
  function containsWindowConfirm(body) {
4678
5024
  let found = false;
4679
- traverse4(
5025
+ traverse5(
4680
5026
  body,
4681
5027
  {
4682
5028
  noScope: true,
@@ -4728,7 +5074,7 @@ function collectionItemNames(callback) {
4728
5074
  function collectionDisplayFields(callback, itemNames) {
4729
5075
  const fields = /* @__PURE__ */ new Set();
4730
5076
  if (!callback.body) return [];
4731
- traverse4(
5077
+ traverse5(
4732
5078
  callback.body,
4733
5079
  {
4734
5080
  noScope: true,
@@ -4745,7 +5091,7 @@ function collectionDisplayFields(callback, itemNames) {
4745
5091
  function collectionKeyField(callback, itemNames) {
4746
5092
  let keyField;
4747
5093
  if (!callback.body) return void 0;
4748
- traverse4(
5094
+ traverse5(
4749
5095
  callback.body,
4750
5096
  {
4751
5097
  noScope: true,
@@ -4888,7 +5234,7 @@ var WebNavigationAnalyzer = class {
4888
5234
  if (BabelTypes.isJSXElement(child)) visitRoute(child, fullPath);
4889
5235
  }
4890
5236
  };
4891
- traverse4(ast, {
5237
+ traverse5(ast, {
4892
5238
  JSXElement: (nodePath) => {
4893
5239
  const name = getJsxElementName(nodePath.node.openingElement);
4894
5240
  if (name !== "Routes" && name !== "Route") return;
@@ -4927,7 +5273,7 @@ var WebNavigationAnalyzer = class {
4927
5273
  "createMemoryRouter",
4928
5274
  "useRoutes"
4929
5275
  ]);
4930
- traverse4(ast, {
5276
+ traverse5(ast, {
4931
5277
  CallExpression: (nodePath) => {
4932
5278
  const callee = nodePath.node.callee;
4933
5279
  if (!BabelTypes.isIdentifier(callee) || !ROUTER_FACTORIES.has(callee.name)) return;
@@ -5360,8 +5706,8 @@ var ZodParsedType = util.arrayToEnum([
5360
5706
  "set"
5361
5707
  ]);
5362
5708
  var getParsedType = (data) => {
5363
- const t13 = typeof data;
5364
- switch (t13) {
5709
+ const t15 = typeof data;
5710
+ switch (t15) {
5365
5711
  case "undefined":
5366
5712
  return ZodParsedType.undefined;
5367
5713
  case "string":
@@ -9206,7 +9552,25 @@ function isSafeImageUrl(value) {
9206
9552
  return SAFE_IMAGE_URL_RE.test(trimmed);
9207
9553
  }
9208
9554
 
9209
- // ../shared/dist/chunk-NMQNNPSJ.mjs
9555
+ // ../shared/dist/chunk-KUMPS6RH.mjs
9556
+ var SUPPORTED_APPILOTS_LOCALES = ["pt-BR", "en", "es", "fr"];
9557
+
9558
+ // ../shared/dist/chunk-2GTRKNPJ.mjs
9559
+ var symbols = external_exports.array(external_exports.string().max(240)).max(12);
9560
+ var controlEvidenceSchema = external_exports.object({
9561
+ version: external_exports.literal(1),
9562
+ siteId: external_exports.string().regex(/^[a-f0-9]{20}$/),
9563
+ component: external_exports.string().max(240),
9564
+ icons: symbols,
9565
+ handler: external_exports.string().max(240).optional(),
9566
+ calls: symbols,
9567
+ argumentBindings: symbols,
9568
+ conditions: symbols,
9569
+ nativeConfirmation: external_exports.object({
9570
+ title: external_exports.string().max(240).optional(),
9571
+ destructiveOption: external_exports.boolean()
9572
+ }).optional()
9573
+ });
9210
9574
  var locatorSourceSchema = external_exports.enum([
9211
9575
  "appilotsId",
9212
9576
  "testID",
@@ -9633,7 +9997,9 @@ var snapshotInputSchema = external_exports.object({
9633
9997
  inModal: external_exports.boolean().optional()
9634
9998
  }).passthrough();
9635
9999
  var snapshotButtonSchema = external_exports.object({
10000
+ controlEvidence: controlEvidenceSchema.optional(),
9636
10001
  id: boundedString(160).optional(),
10002
+ dispatchable: external_exports.boolean().optional(),
9637
10003
  provenance: identityProvenanceSchema.optional(),
9638
10004
  /**
9639
10005
  * False when the control is mounted but currently OUTSIDE the window —
@@ -9694,7 +10060,36 @@ var snapshotListSchema = external_exports.object({
9694
10060
  */
9695
10061
  source: boundedString(40).optional(),
9696
10062
  itemCount: external_exports.number().int().optional(),
10063
+ /**
10064
+ * Size of the whole collection when the app declared it, for a list
10065
+ * that is a WINDOW onto more data than it holds.
10066
+ *
10067
+ * `itemCount` is how many rows the list is rendering from and
10068
+ * `visibleItemCount` how many of those are mounted; neither can
10069
+ * express "there are 36 and you are looking at the first 20",
10070
+ * because a paginated list's `data` is the page. Absent means
10071
+ * unknown — never "same as itemCount".
10072
+ */
10073
+ totalItemCount: external_exports.number().int().optional(),
9697
10074
  visibleItemCount: external_exports.number().int().optional(),
10075
+ viewportItemCount: external_exports.number().int().nonnegative().optional(),
10076
+ exploration: external_exports.object({
10077
+ revision: external_exports.number().int().nonnegative(),
10078
+ observedItemCount: external_exports.number().int().min(0).max(5e3),
10079
+ observedRanges: external_exports.array(
10080
+ external_exports.object({
10081
+ start: external_exports.number().int().nonnegative(),
10082
+ end: external_exports.number().int().nonnegative()
10083
+ })
10084
+ ).max(32),
10085
+ rangesTruncated: external_exports.boolean(),
10086
+ coverage: external_exports.enum(["partial", "all-loaded"]),
10087
+ pagination: external_exports.enum(["possible", "not-declared"]),
10088
+ scrollSteps: external_exports.number().int().nonnegative(),
10089
+ remainingScrollSteps: external_exports.number().int().nonnegative(),
10090
+ consecutiveNoProgress: external_exports.number().int().nonnegative(),
10091
+ lastScroll: external_exports.enum(["moved", "no-progress", "boundary", "unverified"]).optional()
10092
+ }).optional(),
9698
10093
  refreshing: external_exports.boolean().optional(),
9699
10094
  empty: external_exports.boolean().optional(),
9700
10095
  label: boundedString(300).optional(),
@@ -9742,6 +10137,7 @@ var snapshotChoiceGroupSchema = external_exports.object({
9742
10137
  }).passthrough();
9743
10138
  var snapshotElementSchema = external_exports.object({
9744
10139
  id: boundedString(160).optional(),
10140
+ dispatchable: external_exports.boolean().optional(),
9745
10141
  role: boundedString(40).optional(),
9746
10142
  label: boundedString(300).optional(),
9747
10143
  texts: external_exports.array(boundedString(500)).max(50).optional(),
@@ -9804,6 +10200,8 @@ var agentSnapshotSchema = external_exports.object({
9804
10200
  loadingFinished: external_exports.boolean().optional(),
9805
10201
  textsAdded: external_exports.number().int().nonnegative().optional(),
9806
10202
  textsRemoved: external_exports.number().int().nonnegative().optional(),
10203
+ buttonsAddedIndices: external_exports.array(external_exports.number().int().nonnegative()).max(12).optional(),
10204
+ buttonsRemoved: external_exports.number().int().nonnegative().optional(),
9807
10205
  visibleRowsDelta: external_exports.number().int().optional(),
9808
10206
  totalRowsDelta: external_exports.number().int().optional(),
9809
10207
  fieldsNewlyFilled: external_exports.array(boundedString(160)).max(12).optional(),
@@ -9829,6 +10227,10 @@ var agentSnapshotSchema = external_exports.object({
9829
10227
  }).passthrough().optional()
9830
10228
  }).passthrough();
9831
10229
  var agentContextSchema = external_exports.object({
10230
+ missionProtocol: external_exports.literal(1).optional(),
10231
+ missionId: boundedString(160).optional(),
10232
+ /** Preferred supported device language, reported by the SDK independently of map/UI labels. */
10233
+ deviceLocale: external_exports.enum(SUPPORTED_APPILOTS_LOCALES).optional(),
9832
10234
  /**
9833
10235
  * Client platform this observation was captured on. Optional and
9834
10236
  * additive (see `clientPlatformSchema`) — absent means
@@ -10079,6 +10481,12 @@ var actionDiagnoseSchema = external_exports.object({
10079
10481
  requiresUserInput: external_exports.boolean().optional()
10080
10482
  });
10081
10483
  var actionResultSchema = external_exports.object({
10484
+ nativeConfirmation: external_exports.object({
10485
+ title: external_exports.string().max(300),
10486
+ message: external_exports.string().max(1e3).optional(),
10487
+ buttonLabel: external_exports.string().max(160).optional(),
10488
+ handlerCompleted: external_exports.boolean()
10489
+ }).optional(),
10082
10490
  actionId: external_exports.string(),
10083
10491
  type: external_exports.string(),
10084
10492
  success: external_exports.boolean(),
@@ -10206,7 +10614,7 @@ external_exports.object({
10206
10614
  /** null = remove webhook, undefined = leave unchanged. */
10207
10615
  budgetWebhookUrl: external_exports.string().url().nullable().optional()
10208
10616
  }).strict();
10209
- var localeSchema = external_exports.enum(["pt-BR", "en", "es"]);
10617
+ var localeSchema = external_exports.enum(SUPPORTED_APPILOTS_LOCALES);
10210
10618
  var hexColorSchema = external_exports.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
10211
10619
  message: "Must be a hex color like #6366f1"
10212
10620
  });
@@ -11217,9 +11625,22 @@ var MCPGenerator = class _MCPGenerator {
11217
11625
  const filePath = path2__default.resolve(outputDir, `mcp-document.${this.options.format}`);
11218
11626
  await writeFile(filePath, serialized, "utf-8");
11219
11627
  console.log(`[MCPGenerator] Document written to: ${filePath}`);
11628
+ const controlFiles = analyzed.controlEvidenceFiles ?? {};
11629
+ await writeFile(
11630
+ path2__default.resolve(outputDir, "control-evidence.json"),
11631
+ JSON.stringify({ version: 1, files: controlFiles }, null, 2),
11632
+ "utf-8"
11633
+ );
11220
11634
  const checksumFilePath = path2__default.resolve(outputDir, ".appilots-checksum");
11221
11635
  await writeFile(checksumFilePath, checksum, "utf-8");
11222
11636
  console.log(`[MCPGenerator] Checksum written to: ${checksumFilePath}`);
11637
+ const evidenceCount = Object.values(controlFiles).reduce(
11638
+ (sum, entries) => sum + entries.length,
11639
+ 0
11640
+ );
11641
+ console.log(
11642
+ `[MCPGenerator] Source evidence: ${evidenceCount} icon controls across ${Object.keys(controlFiles).length} files (runtime binding required)`
11643
+ );
11223
11644
  console.log("[MCPGenerator] Generation complete!");
11224
11645
  return {
11225
11646
  document,
@@ -11285,7 +11706,8 @@ var KNOWN_CONFIG_KEYS = [
11285
11706
  "navigationExclude",
11286
11707
  "platform",
11287
11708
  "manifestPath",
11288
- "eval"
11709
+ "eval",
11710
+ "knowledge"
11289
11711
  ];
11290
11712
  var KEY_ALIASES = {
11291
11713
  apiUrl: "serverUrl",
@@ -11433,7 +11855,8 @@ function saveConfig(dir, config) {
11433
11855
  navigationExclude: config.navigationExclude || existingConfig?.navigationExclude,
11434
11856
  platform: config.platform || existingConfig?.platform,
11435
11857
  manifestPath: config.manifestPath || existingConfig?.manifestPath,
11436
- eval: config.eval || existingConfig?.eval
11858
+ eval: config.eval || existingConfig?.eval,
11859
+ knowledge: config.knowledge || existingConfig?.knowledge
11437
11860
  };
11438
11861
  try {
11439
11862
  writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2), "utf-8");
@@ -11526,6 +11949,19 @@ function validateConfig(config) {
11526
11949
  if (config.manifestPath !== void 0 && typeof config.manifestPath !== "string") {
11527
11950
  errors.push("manifestPath must be a string");
11528
11951
  }
11952
+ if (config.knowledge !== void 0) {
11953
+ if (typeof config.knowledge !== "object" || config.knowledge === null || Array.isArray(config.knowledge)) {
11954
+ errors.push("knowledge must be an object");
11955
+ } else {
11956
+ const kc = config.knowledge;
11957
+ for (const field of ["sources", "exclude"]) {
11958
+ const value = kc[field];
11959
+ if (value !== void 0 && (!Array.isArray(value) || !value.every((item) => typeof item === "string"))) {
11960
+ errors.push(`knowledge.${field} must be an array of strings`);
11961
+ }
11962
+ }
11963
+ }
11964
+ }
11529
11965
  if (config.eval !== void 0) {
11530
11966
  if (typeof config.eval !== "object" || config.eval === null || Array.isArray(config.eval)) {
11531
11967
  errors.push("eval must be an object");
@@ -11802,6 +12238,48 @@ var AppilotsAPIClient = class {
11802
12238
  };
11803
12239
  }
11804
12240
  }
12241
+ /**
12242
+ * Syncs knowledge documents with the Appilots backend.
12243
+ *
12244
+ * @param documents Array of documents to sync (filename, base64 content, mimeType, checksum)
12245
+ * @returns KnowledgeSyncResult with counts of uploaded, skipped, and errored docs
12246
+ */
12247
+ async knowledgeSync(documents) {
12248
+ try {
12249
+ const response = await this.request(`${this.baseUrl}/cli/knowledge/sync`, {
12250
+ method: "POST",
12251
+ headers: {
12252
+ "Content-Type": "application/json",
12253
+ Authorization: `Bearer ${this.apiKey}`
12254
+ },
12255
+ body: JSON.stringify({ documents })
12256
+ });
12257
+ if (!response.ok) {
12258
+ const errorData = await response.json().catch(() => ({}));
12259
+ return {
12260
+ success: false,
12261
+ uploaded: 0,
12262
+ skipped: 0,
12263
+ errors: 0,
12264
+ error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`)
12265
+ };
12266
+ }
12267
+ const json = await response.json();
12268
+ const inner = json.data ?? json;
12269
+ return {
12270
+ success: true,
12271
+ ...inner
12272
+ };
12273
+ } catch (error) {
12274
+ return {
12275
+ success: false,
12276
+ uploaded: 0,
12277
+ skipped: 0,
12278
+ errors: 0,
12279
+ error: error instanceof Error ? error.message : "Failed to sync knowledge with Appilots API"
12280
+ };
12281
+ }
12282
+ }
11805
12283
  /**
11806
12284
  * Checks if the Appilots API server is healthy
11807
12285
  *
@@ -11820,7 +12298,7 @@ var AppilotsAPIClient = class {
11820
12298
  };
11821
12299
 
11822
12300
  // src/version.ts
11823
- var CLI_VERSION = "0.11.3";
12301
+ var CLI_VERSION = "0.13.0";
11824
12302
 
11825
12303
  export { AppilotsAPIClient, CLI_VERSION, ComponentAnalyzer, DEFAULT_MANIFEST_FILENAME, DEFAULT_WEB_SCREEN_PATTERNS, FormAnalyzer, GenericPlatformAnalyzer, MCPGenerator, NavigationAnalyzer, ReactNativePlatformAnalyzer, ReactWebPlatformAnalyzer, ScreenAnalyzer, WebNavigationAnalyzer, WebScreenAnalyzer, formatMetadataWarnings, getConfigPath, getEnvOverrides, lintActionMetadata, loadConfig, loadManifest, mergeManifestNavigation, mergeManifestScreens, resolvePathToScreen, saveConfig, screenNameFromPath, validateConfig };
11826
12304
  //# sourceMappingURL=index.mjs.map