@appilots/cli 0.11.3 → 0.14.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/README.md +23 -11
- package/dist/cli/index.js +1049 -392
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +583 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +581 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -28,10 +28,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
));
|
|
29
29
|
|
|
30
30
|
// src/cli/index.ts
|
|
31
|
-
var
|
|
31
|
+
var import_commander9 = require("commander");
|
|
32
32
|
|
|
33
33
|
// src/version.ts
|
|
34
|
-
var CLI_VERSION = "0.
|
|
34
|
+
var CLI_VERSION = "0.14.0";
|
|
35
35
|
|
|
36
36
|
// src/config/index.ts
|
|
37
37
|
var import_fs = require("fs");
|
|
@@ -52,7 +52,8 @@ var KNOWN_CONFIG_KEYS = [
|
|
|
52
52
|
"navigationExclude",
|
|
53
53
|
"platform",
|
|
54
54
|
"manifestPath",
|
|
55
|
-
"eval"
|
|
55
|
+
"eval",
|
|
56
|
+
"knowledge"
|
|
56
57
|
];
|
|
57
58
|
var KEY_ALIASES = {
|
|
58
59
|
apiUrl: "serverUrl",
|
|
@@ -200,7 +201,8 @@ function saveConfig(dir, config) {
|
|
|
200
201
|
navigationExclude: config.navigationExclude || existingConfig?.navigationExclude,
|
|
201
202
|
platform: config.platform || existingConfig?.platform,
|
|
202
203
|
manifestPath: config.manifestPath || existingConfig?.manifestPath,
|
|
203
|
-
eval: config.eval || existingConfig?.eval
|
|
204
|
+
eval: config.eval || existingConfig?.eval,
|
|
205
|
+
knowledge: config.knowledge || existingConfig?.knowledge
|
|
204
206
|
};
|
|
205
207
|
try {
|
|
206
208
|
(0, import_fs.writeFileSync)(configPath, JSON.stringify(mergedConfig, null, 2), "utf-8");
|
|
@@ -293,6 +295,19 @@ function validateConfig(config) {
|
|
|
293
295
|
if (config.manifestPath !== void 0 && typeof config.manifestPath !== "string") {
|
|
294
296
|
errors.push("manifestPath must be a string");
|
|
295
297
|
}
|
|
298
|
+
if (config.knowledge !== void 0) {
|
|
299
|
+
if (typeof config.knowledge !== "object" || config.knowledge === null || Array.isArray(config.knowledge)) {
|
|
300
|
+
errors.push("knowledge must be an object");
|
|
301
|
+
} else {
|
|
302
|
+
const kc = config.knowledge;
|
|
303
|
+
for (const field of ["sources", "exclude"]) {
|
|
304
|
+
const value = kc[field];
|
|
305
|
+
if (value !== void 0 && (!Array.isArray(value) || !value.every((item) => typeof item === "string"))) {
|
|
306
|
+
errors.push(`knowledge.${field} must be an array of strings`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
296
311
|
if (config.eval !== void 0) {
|
|
297
312
|
if (typeof config.eval !== "object" || config.eval === null || Array.isArray(config.eval)) {
|
|
298
313
|
errors.push("eval must be an object");
|
|
@@ -634,6 +649,48 @@ var AppilotsAPIClient = class {
|
|
|
634
649
|
};
|
|
635
650
|
}
|
|
636
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Syncs knowledge documents with the Appilots backend.
|
|
654
|
+
*
|
|
655
|
+
* @param documents Array of documents to sync (filename, base64 content, mimeType, checksum)
|
|
656
|
+
* @returns KnowledgeSyncResult with counts of uploaded, skipped, and errored docs
|
|
657
|
+
*/
|
|
658
|
+
async knowledgeSync(documents) {
|
|
659
|
+
try {
|
|
660
|
+
const response = await this.request(`${this.baseUrl}/cli/knowledge/sync`, {
|
|
661
|
+
method: "POST",
|
|
662
|
+
headers: {
|
|
663
|
+
"Content-Type": "application/json",
|
|
664
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
665
|
+
},
|
|
666
|
+
body: JSON.stringify({ documents })
|
|
667
|
+
});
|
|
668
|
+
if (!response.ok) {
|
|
669
|
+
const errorData = await response.json().catch(() => ({}));
|
|
670
|
+
return {
|
|
671
|
+
success: false,
|
|
672
|
+
uploaded: 0,
|
|
673
|
+
skipped: 0,
|
|
674
|
+
errors: 0,
|
|
675
|
+
error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`)
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
const json = await response.json();
|
|
679
|
+
const inner = json.data ?? json;
|
|
680
|
+
return {
|
|
681
|
+
success: true,
|
|
682
|
+
...inner
|
|
683
|
+
};
|
|
684
|
+
} catch (error2) {
|
|
685
|
+
return {
|
|
686
|
+
success: false,
|
|
687
|
+
uploaded: 0,
|
|
688
|
+
skipped: 0,
|
|
689
|
+
errors: 0,
|
|
690
|
+
error: error2 instanceof Error ? error2.message : "Failed to sync knowledge with Appilots API"
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
637
694
|
/**
|
|
638
695
|
* Checks if the Appilots API server is healthy
|
|
639
696
|
*
|
|
@@ -771,7 +828,7 @@ var import_node_path7 = require("path");
|
|
|
771
828
|
|
|
772
829
|
// src/generators/MCPGenerator.ts
|
|
773
830
|
var import_promises4 = require("fs/promises");
|
|
774
|
-
var
|
|
831
|
+
var import_node_crypto2 = require("crypto");
|
|
775
832
|
var import_node_path5 = __toESM(require("path"));
|
|
776
833
|
|
|
777
834
|
// src/generators/checksum.ts
|
|
@@ -1036,8 +1093,355 @@ async function globSorted(patterns, options) {
|
|
|
1036
1093
|
|
|
1037
1094
|
// src/analyzers/ScreenAnalyzer.ts
|
|
1038
1095
|
var import_promises = __toESM(require("fs/promises"));
|
|
1096
|
+
|
|
1097
|
+
// src/extractors/control-evidence.ts
|
|
1098
|
+
var import_node_crypto = require("crypto");
|
|
1099
|
+
var import_traverse = __toESM(require("@babel/traverse"));
|
|
1100
|
+
var t2 = __toESM(require("@babel/types"));
|
|
1101
|
+
|
|
1102
|
+
// src/extractors/control-evidence-bindings.ts
|
|
1103
|
+
var t = __toESM(require("@babel/types"));
|
|
1104
|
+
var MAX_HOPS = 8;
|
|
1105
|
+
function unwrap(path13) {
|
|
1106
|
+
while (path13.isTSAsExpression() || path13.isTSTypeAssertion() || path13.isTSNonNullExpression() || path13.isTSSatisfiesExpression() || path13.isParenthesizedExpression())
|
|
1107
|
+
path13 = path13.get("expression");
|
|
1108
|
+
return path13;
|
|
1109
|
+
}
|
|
1110
|
+
function constantValue(path13, depth = 0) {
|
|
1111
|
+
if (depth > MAX_HOPS) return void 0;
|
|
1112
|
+
path13 = unwrap(path13);
|
|
1113
|
+
if (!path13.isIdentifier()) return path13;
|
|
1114
|
+
const binding = path13.scope.getBinding(path13.node.name);
|
|
1115
|
+
if (!binding?.constant || !binding.path.isVariableDeclarator()) return path13;
|
|
1116
|
+
const init = binding.path.get("init");
|
|
1117
|
+
const resolved = init.node ? constantValue(init, depth + 1) : void 0;
|
|
1118
|
+
if (resolved?.isObjectExpression() && binding.referencePaths.some(
|
|
1119
|
+
(reference) => !reference.parentPath?.isJSXSpreadAttribute() && !reference.parentPath?.isSpreadElement()
|
|
1120
|
+
))
|
|
1121
|
+
return void 0;
|
|
1122
|
+
return resolved;
|
|
1123
|
+
}
|
|
1124
|
+
function propValue(path13, name) {
|
|
1125
|
+
const readObject = (path14) => {
|
|
1126
|
+
const resolved = constantValue(path14);
|
|
1127
|
+
if (!resolved?.isObjectExpression()) return { blocked: true };
|
|
1128
|
+
for (const property of [...resolved.get("properties")].reverse()) {
|
|
1129
|
+
if (property.isSpreadElement()) {
|
|
1130
|
+
const found = readObjectBounded(property.get("argument"));
|
|
1131
|
+
if (found.value || found.blocked) return found;
|
|
1132
|
+
} else if (property.isObjectProperty() || property.isObjectMethod()) {
|
|
1133
|
+
if (property.node.computed) return { blocked: true };
|
|
1134
|
+
const key = property.node.key;
|
|
1135
|
+
if ((t.isIdentifier(key) ? key.name : t.isStringLiteral(key) ? key.value : "") === name)
|
|
1136
|
+
return property.isObjectProperty() ? { value: property.get("value") } : { value: property };
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return {};
|
|
1140
|
+
};
|
|
1141
|
+
let objectBudget = MAX_HOPS;
|
|
1142
|
+
const readObjectBounded = (path14) => objectBudget-- > 0 ? readObject(path14) : { blocked: true };
|
|
1143
|
+
for (const attr of [...path13.get("attributes")].reverse()) {
|
|
1144
|
+
if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name, { name })) {
|
|
1145
|
+
const value = attr.get("value");
|
|
1146
|
+
return value.isJSXExpressionContainer() ? unwrap(value.get("expression")) : value.node ? value : void 0;
|
|
1147
|
+
}
|
|
1148
|
+
if (attr.isJSXSpreadAttribute()) {
|
|
1149
|
+
const found = readObjectBounded(attr.get("argument"));
|
|
1150
|
+
if (found.value || found.blocked) return found.value;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return void 0;
|
|
1154
|
+
}
|
|
1155
|
+
function importIdentity(path13, depth = 0) {
|
|
1156
|
+
if (depth > MAX_HOPS) return void 0;
|
|
1157
|
+
path13 = unwrap(path13);
|
|
1158
|
+
if (path13.isIdentifier() || path13.isJSXIdentifier()) {
|
|
1159
|
+
const binding = path13.scope.getBinding(path13.node.name);
|
|
1160
|
+
if (!binding?.constant) return void 0;
|
|
1161
|
+
const declaration = binding.path;
|
|
1162
|
+
if (declaration.parentPath?.isImportDeclaration()) {
|
|
1163
|
+
const module2 = declaration.parentPath.node.source.value;
|
|
1164
|
+
if (declaration.isImportSpecifier()) {
|
|
1165
|
+
const imported = declaration.node.imported;
|
|
1166
|
+
return { module: module2, imported: t.isIdentifier(imported) ? imported.name : imported.value };
|
|
1167
|
+
}
|
|
1168
|
+
if (declaration.isImportDefaultSpecifier()) return { module: module2, imported: "default" };
|
|
1169
|
+
if (declaration.isImportNamespaceSpecifier()) return { module: module2, imported: "*" };
|
|
1170
|
+
}
|
|
1171
|
+
if (declaration.isVariableDeclarator() && declaration.get("init").node)
|
|
1172
|
+
return importIdentity(declaration.get("init"), depth + 1);
|
|
1173
|
+
}
|
|
1174
|
+
if (path13.isJSXMemberExpression() || path13.isMemberExpression() && !path13.node.computed) {
|
|
1175
|
+
const origin = importIdentity(path13.get("object"), depth + 1);
|
|
1176
|
+
const key = path13.node.property;
|
|
1177
|
+
if (origin && (t.isIdentifier(key) || t.isJSXIdentifier(key)) && (origin.imported === "*" || origin.module === "react" && origin.imported === "default"))
|
|
1178
|
+
return { module: origin.module, imported: key.name };
|
|
1179
|
+
}
|
|
1180
|
+
return void 0;
|
|
1181
|
+
}
|
|
1182
|
+
function handlerFunction(path13, depth = 0) {
|
|
1183
|
+
if (depth > MAX_HOPS) return void 0;
|
|
1184
|
+
path13 = unwrap(path13);
|
|
1185
|
+
if (path13.isFunction()) return path13;
|
|
1186
|
+
if (path13.isIdentifier()) {
|
|
1187
|
+
const binding = path13.scope.getBinding(path13.node.name);
|
|
1188
|
+
if (!binding?.constant) return void 0;
|
|
1189
|
+
if (binding.path.isFunctionDeclaration()) return binding.path;
|
|
1190
|
+
if (binding.path.isVariableDeclarator() && binding.path.get("init").node)
|
|
1191
|
+
return handlerFunction(binding.path.get("init"), depth + 1);
|
|
1192
|
+
}
|
|
1193
|
+
if (path13.isCallExpression()) {
|
|
1194
|
+
const origin = importIdentity(path13.get("callee"));
|
|
1195
|
+
if (origin?.module === "react" && origin.imported === "useCallback") {
|
|
1196
|
+
const callback = path13.get("arguments")[0];
|
|
1197
|
+
if (callback) return handlerFunction(callback, depth + 1);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (path13.isMemberExpression() && !path13.node.computed && t.isThisExpression(path13.node.object)) {
|
|
1201
|
+
const key = path13.node.property;
|
|
1202
|
+
if (!t.isIdentifier(key)) return void 0;
|
|
1203
|
+
const owner = path13.findParent((p) => p.isClassDeclaration() || p.isClassExpression());
|
|
1204
|
+
if (!owner || !(owner.isClassDeclaration() || owner.isClassExpression())) return void 0;
|
|
1205
|
+
for (const member of owner.get("body").get("body")) {
|
|
1206
|
+
if (!(member.isClassMethod() || member.isClassProperty()) || member.node.computed || member.node.static)
|
|
1207
|
+
continue;
|
|
1208
|
+
if (!t.isIdentifier(member.node.key, { name: key.name })) continue;
|
|
1209
|
+
if (member.isClassMethod() && member.node.kind === "method") return member;
|
|
1210
|
+
if (member.isClassProperty() && member.get("value").node)
|
|
1211
|
+
return handlerFunction(member.get("value"), depth + 1);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return void 0;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/extractors/control-evidence.ts
|
|
1218
|
+
function symbol(node) {
|
|
1219
|
+
if (t2.isTSAsExpression(node) || t2.isTSTypeAssertion(node) || t2.isTSNonNullExpression(node) || t2.isTSSatisfiesExpression(node))
|
|
1220
|
+
return symbol(node.expression);
|
|
1221
|
+
if (t2.isIdentifier(node) || t2.isJSXIdentifier(node)) return node.name;
|
|
1222
|
+
if (t2.isThisExpression(node)) return "this";
|
|
1223
|
+
if (t2.isMemberExpression(node) && !node.computed || t2.isJSXMemberExpression(node)) {
|
|
1224
|
+
const object = symbol(node.object), property = symbol(node.property);
|
|
1225
|
+
return object && property ? `${object}.${property}` : void 0;
|
|
1226
|
+
}
|
|
1227
|
+
if (t2.isUnaryExpression(node) && node.operator === "!") {
|
|
1228
|
+
const value = symbol(node.argument);
|
|
1229
|
+
return value ? `!${value}` : void 0;
|
|
1230
|
+
}
|
|
1231
|
+
return void 0;
|
|
1232
|
+
}
|
|
1233
|
+
function attribute(node, name) {
|
|
1234
|
+
return node.attributes.find(
|
|
1235
|
+
(a) => t2.isJSXAttribute(a) && t2.isJSXIdentifier(a.name, { name })
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
var ICON_LIBRARIES = [
|
|
1239
|
+
"lucide-react-native",
|
|
1240
|
+
"lucide-react",
|
|
1241
|
+
"@tamagui/lucide-icons",
|
|
1242
|
+
"@expo/vector-icons",
|
|
1243
|
+
"@react-native-vector-icons/",
|
|
1244
|
+
"react-native-vector-icons/"
|
|
1245
|
+
];
|
|
1246
|
+
function iconLibrary(module2) {
|
|
1247
|
+
return ICON_LIBRARIES.some(
|
|
1248
|
+
(name) => name.endsWith("/") ? module2.startsWith(name) : module2 === name || module2.startsWith(name + "/")
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
function add(values, value, max = 12) {
|
|
1252
|
+
if (value && value.length <= 240 && values.length < max && !values.includes(value))
|
|
1253
|
+
values.push(value);
|
|
1254
|
+
}
|
|
1255
|
+
function iconName(path13, explicitIcon = false) {
|
|
1256
|
+
const origin = importIdentity(path13);
|
|
1257
|
+
if (origin && iconLibrary(origin.module) && origin.imported !== "*")
|
|
1258
|
+
return origin.imported === "default" ? origin.module : `${origin.module}:${origin.imported}`;
|
|
1259
|
+
const name = symbol(path13.node);
|
|
1260
|
+
return name && (explicitIcon || /icon/i.test(name)) ? name : void 0;
|
|
1261
|
+
}
|
|
1262
|
+
function iconsInElement(path13, explicitIcon = false) {
|
|
1263
|
+
const name = iconName(path13.get("name"), explicitIcon);
|
|
1264
|
+
if (!name) return void 0;
|
|
1265
|
+
const glyph = propValue(path13, "name");
|
|
1266
|
+
const value = glyph && constantValue(glyph);
|
|
1267
|
+
return value?.isStringLiteral() ? `${name}:${value.node.value}` : name;
|
|
1268
|
+
}
|
|
1269
|
+
function hasInteraction(path13) {
|
|
1270
|
+
return ["onPress", "onLongPress", "onClick"].some(
|
|
1271
|
+
(name) => attribute(path13.node, name) || propValue(path13, name)
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
function collectPresentationIcons(path13, icons) {
|
|
1275
|
+
const namedIconProps = [
|
|
1276
|
+
"icon",
|
|
1277
|
+
"prefix",
|
|
1278
|
+
"suffix",
|
|
1279
|
+
"left",
|
|
1280
|
+
"right",
|
|
1281
|
+
"leadingIcon",
|
|
1282
|
+
"trailingIcon",
|
|
1283
|
+
"startIcon",
|
|
1284
|
+
"endIcon",
|
|
1285
|
+
"renderIcon"
|
|
1286
|
+
];
|
|
1287
|
+
const props = new Set(namedIconProps);
|
|
1288
|
+
for (const attr of path13.node.attributes)
|
|
1289
|
+
if (t2.isJSXAttribute(attr) && t2.isJSXIdentifier(attr.name) && !/^on[A-Z]/.test(attr.name.name))
|
|
1290
|
+
props.add(attr.name.name);
|
|
1291
|
+
for (const prop of props) {
|
|
1292
|
+
const icon = propValue(path13, prop);
|
|
1293
|
+
if (icon) {
|
|
1294
|
+
const value = constantValue(icon);
|
|
1295
|
+
if (value?.isStringLiteral()) {
|
|
1296
|
+
if (prop.toLowerCase().includes("icon")) add(icons, value.node.value, 8);
|
|
1297
|
+
} else {
|
|
1298
|
+
const explicitIcon = prop.toLowerCase() === "icon";
|
|
1299
|
+
if (icon.isJSXElement()) {
|
|
1300
|
+
if (hasInteraction(icon.get("openingElement"))) continue;
|
|
1301
|
+
add(icons, iconsInElement(icon.get("openingElement"), explicitIcon), 8);
|
|
1302
|
+
}
|
|
1303
|
+
icon.traverse({
|
|
1304
|
+
JSXAttribute(attr) {
|
|
1305
|
+
if (t2.isJSXIdentifier(attr.node.name) && /^on[A-Z]/.test(attr.node.name.name))
|
|
1306
|
+
attr.skip();
|
|
1307
|
+
},
|
|
1308
|
+
JSXElement(child) {
|
|
1309
|
+
const opening = child.get("openingElement");
|
|
1310
|
+
if (hasInteraction(opening)) {
|
|
1311
|
+
child.skip();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
add(icons, iconsInElement(opening, explicitIcon), 8);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
if (!icons.length && (namedIconProps.includes(prop) || explicitIcon))
|
|
1318
|
+
add(icons, iconName(icon, explicitIcon), 8);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
function collectIcons(path13) {
|
|
1324
|
+
const icons = [];
|
|
1325
|
+
collectPresentationIcons(path13, icons);
|
|
1326
|
+
const jsx = path13.parentPath;
|
|
1327
|
+
if (jsx.isJSXElement())
|
|
1328
|
+
jsx.traverse({
|
|
1329
|
+
// JSX mentioned inside an event callback is not a rendered child icon.
|
|
1330
|
+
JSXAttribute(attr) {
|
|
1331
|
+
attr.skip();
|
|
1332
|
+
},
|
|
1333
|
+
JSXElement(child) {
|
|
1334
|
+
const opening = child.get("openingElement");
|
|
1335
|
+
if (hasInteraction(opening)) {
|
|
1336
|
+
child.skip();
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
add(icons, iconsInElement(opening), 8);
|
|
1340
|
+
collectPresentationIcons(opening, icons);
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
return icons;
|
|
1344
|
+
}
|
|
1345
|
+
function conditionsAt(path13) {
|
|
1346
|
+
const conditions = [];
|
|
1347
|
+
let child = path13;
|
|
1348
|
+
for (let parent = child.parentPath; parent && !parent.isFunction(); child = parent, parent = parent.parentPath) {
|
|
1349
|
+
let test;
|
|
1350
|
+
let negated = false;
|
|
1351
|
+
if (parent.isLogicalExpression() && parent.node.right === child.node) {
|
|
1352
|
+
if (parent.node.operator === "&&") test = parent.node.left;
|
|
1353
|
+
if (parent.node.operator === "||") {
|
|
1354
|
+
test = parent.node.left;
|
|
1355
|
+
negated = true;
|
|
1356
|
+
}
|
|
1357
|
+
} else if (parent.isConditionalExpression() && parent.node.test !== child.node) {
|
|
1358
|
+
test = parent.node.test;
|
|
1359
|
+
negated = parent.node.alternate === child.node;
|
|
1360
|
+
} else if (parent.isIfStatement() && parent.node.test !== child.node) {
|
|
1361
|
+
test = parent.node.test;
|
|
1362
|
+
negated = parent.node.alternate === child.node;
|
|
1363
|
+
}
|
|
1364
|
+
const name = symbol(test);
|
|
1365
|
+
if (name) add(conditions, negated ? name.startsWith("!") ? name.slice(1) : `!${name}` : name);
|
|
1366
|
+
}
|
|
1367
|
+
return conditions;
|
|
1368
|
+
}
|
|
1369
|
+
function collectHandlerEvidence(expression, evidence) {
|
|
1370
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1371
|
+
let budget = 100;
|
|
1372
|
+
const visit = (path13, depth) => {
|
|
1373
|
+
if (depth > 4 || budget-- <= 0) return;
|
|
1374
|
+
const fn = handlerFunction(path13);
|
|
1375
|
+
if (!fn || seen.has(fn.node)) return;
|
|
1376
|
+
seen.add(fn.node);
|
|
1377
|
+
fn.traverse({
|
|
1378
|
+
// Ignore uncalled helper definitions; inline callbacks remain source evidence.
|
|
1379
|
+
Function(nested) {
|
|
1380
|
+
if (!nested.parentPath.isCallExpression() && !nested.parentPath.isObjectProperty())
|
|
1381
|
+
nested.skip();
|
|
1382
|
+
},
|
|
1383
|
+
CallExpression(call) {
|
|
1384
|
+
if (budget-- <= 0) {
|
|
1385
|
+
call.skip();
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
add(evidence.calls, symbol(call.node.callee));
|
|
1389
|
+
for (const arg of call.node.arguments) add(evidence.argumentBindings, symbol(arg));
|
|
1390
|
+
visit(call.get("callee"), depth + 1);
|
|
1391
|
+
if (!t2.isMemberExpression(call.node.callee) || call.node.callee.computed || !t2.isIdentifier(call.node.callee.property, { name: "alert" }))
|
|
1392
|
+
return;
|
|
1393
|
+
const callee = call.get("callee");
|
|
1394
|
+
if (!callee.isMemberExpression()) return;
|
|
1395
|
+
const origin = importIdentity(callee.get("object"));
|
|
1396
|
+
if (origin?.module !== "react-native" || origin.imported !== "Alert") return;
|
|
1397
|
+
const options = call.node.arguments[2];
|
|
1398
|
+
const destructiveOption = t2.isArrayExpression(options) && options.elements.some(
|
|
1399
|
+
(option) => t2.isObjectExpression(option) && option.properties.some(
|
|
1400
|
+
(p) => t2.isObjectProperty(p) && !p.computed && symbol(p.key) === "style" && t2.isStringLiteral(p.value, { value: "destructive" })
|
|
1401
|
+
)
|
|
1402
|
+
);
|
|
1403
|
+
if (destructiveOption)
|
|
1404
|
+
evidence.nativeConfirmation = {
|
|
1405
|
+
title: symbol(call.node.arguments[0]),
|
|
1406
|
+
destructiveOption
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
};
|
|
1411
|
+
visit(unwrap(expression), 0);
|
|
1412
|
+
}
|
|
1413
|
+
function extractControlEvidence(ast, source, file2) {
|
|
1414
|
+
const candidates = [];
|
|
1415
|
+
const sourceHash = (0, import_node_crypto.createHash)("sha256").update(source).digest("hex");
|
|
1416
|
+
(0, import_traverse.default)(ast, {
|
|
1417
|
+
JSXOpeningElement(path13) {
|
|
1418
|
+
const node = path13.node;
|
|
1419
|
+
if (attribute(node, "__appilotsControl")) return;
|
|
1420
|
+
const value = propValue(path13, "onPress");
|
|
1421
|
+
if (!value && !attribute(node, "onPress") || value?.isNullLiteral() || value?.isJSXEmptyExpression())
|
|
1422
|
+
return;
|
|
1423
|
+
const icons = collectIcons(path13);
|
|
1424
|
+
if (!icons.length || node.end == null) return;
|
|
1425
|
+
const evidence = {
|
|
1426
|
+
version: 1,
|
|
1427
|
+
siteId: (0, import_node_crypto.createHash)("sha256").update(`${file2}:${sourceHash}:${node.start}`).digest("hex").slice(0, 20),
|
|
1428
|
+
component: symbol(node.name) ?? "unknown",
|
|
1429
|
+
icons,
|
|
1430
|
+
...value && symbol(value.node) ? { handler: symbol(value.node) } : {},
|
|
1431
|
+
calls: [],
|
|
1432
|
+
argumentBindings: [],
|
|
1433
|
+
conditions: conditionsAt(path13)
|
|
1434
|
+
};
|
|
1435
|
+
if (value) collectHandlerEvidence(value, evidence);
|
|
1436
|
+
candidates.push({ evidence, sourceHash, offset: node.end - (node.selfClosing ? 2 : 1) });
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
return candidates;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// src/analyzers/ScreenAnalyzer.ts
|
|
1039
1443
|
var import_path3 = __toESM(require("path"));
|
|
1040
|
-
var
|
|
1444
|
+
var import_traverse5 = __toESM(require("@babel/traverse"));
|
|
1041
1445
|
var BabelTypes = __toESM(require("@babel/types"));
|
|
1042
1446
|
|
|
1043
1447
|
// src/ast/parse.ts
|
|
@@ -1062,25 +1466,25 @@ function parseSource(source, parserPlugins = []) {
|
|
|
1062
1466
|
}
|
|
1063
1467
|
|
|
1064
1468
|
// src/ast/jsx/attributes.ts
|
|
1065
|
-
var
|
|
1469
|
+
var t3 = __toESM(require("@babel/types"));
|
|
1066
1470
|
function findJsxAttribute(element, name) {
|
|
1067
1471
|
return element.attributes.find(
|
|
1068
|
-
(attr) =>
|
|
1472
|
+
(attr) => t3.isJSXAttribute(attr) && t3.isJSXIdentifier(attr.name) && attr.name.name === name
|
|
1069
1473
|
);
|
|
1070
1474
|
}
|
|
1071
1475
|
function getStringAttr(element, name) {
|
|
1072
1476
|
const attr = findJsxAttribute(element, name);
|
|
1073
1477
|
if (!attr || !attr.value) return void 0;
|
|
1074
|
-
if (
|
|
1075
|
-
if (
|
|
1478
|
+
if (t3.isStringLiteral(attr.value)) return attr.value.value;
|
|
1479
|
+
if (t3.isJSXExpressionContainer(attr.value) && t3.isStringLiteral(attr.value.expression)) {
|
|
1076
1480
|
return attr.value.expression.value;
|
|
1077
1481
|
}
|
|
1078
1482
|
return void 0;
|
|
1079
1483
|
}
|
|
1080
1484
|
function getExpressionIdentifierAttr(element, name) {
|
|
1081
1485
|
const attr = findJsxAttribute(element, name);
|
|
1082
|
-
if (!attr?.value || !
|
|
1083
|
-
return
|
|
1486
|
+
if (!attr?.value || !t3.isJSXExpressionContainer(attr.value)) return void 0;
|
|
1487
|
+
return t3.isIdentifier(attr.value.expression) ? attr.value.expression.name : void 0;
|
|
1084
1488
|
}
|
|
1085
1489
|
function hasJsxAttribute(element, name) {
|
|
1086
1490
|
return Boolean(findJsxAttribute(element, name));
|
|
@@ -1159,18 +1563,18 @@ function classifyJsxComponent(name, element) {
|
|
|
1159
1563
|
}
|
|
1160
1564
|
|
|
1161
1565
|
// src/ast/functions/collect.ts
|
|
1162
|
-
var
|
|
1163
|
-
var
|
|
1566
|
+
var import_traverse2 = __toESM(require("@babel/traverse"));
|
|
1567
|
+
var t4 = __toESM(require("@babel/types"));
|
|
1164
1568
|
function collectFunctions(ast) {
|
|
1165
1569
|
const handlers = /* @__PURE__ */ new Map();
|
|
1166
|
-
(0,
|
|
1570
|
+
(0, import_traverse2.default)(ast, {
|
|
1167
1571
|
FunctionDeclaration: (nodePath) => {
|
|
1168
1572
|
if (nodePath.node.id?.name) handlers.set(nodePath.node.id.name, nodePath.node);
|
|
1169
1573
|
},
|
|
1170
1574
|
VariableDeclarator: (nodePath) => {
|
|
1171
|
-
if (!
|
|
1575
|
+
if (!t4.isIdentifier(nodePath.node.id)) return;
|
|
1172
1576
|
const init = nodePath.node.init;
|
|
1173
|
-
if (
|
|
1577
|
+
if (t4.isArrowFunctionExpression(init) || t4.isFunctionExpression(init)) {
|
|
1174
1578
|
handlers.set(nodePath.node.id.name, init);
|
|
1175
1579
|
}
|
|
1176
1580
|
}
|
|
@@ -1179,8 +1583,8 @@ function collectFunctions(ast) {
|
|
|
1179
1583
|
}
|
|
1180
1584
|
|
|
1181
1585
|
// src/ast/functions/analyze-async.ts
|
|
1182
|
-
var
|
|
1183
|
-
var
|
|
1586
|
+
var import_traverse3 = __toESM(require("@babel/traverse"));
|
|
1587
|
+
var t5 = __toESM(require("@babel/types"));
|
|
1184
1588
|
var DESTRUCTIVE_VERB = /(delete|destroy|remove|discard|wipe|erase|drop|terminate|revoke|deactivate|disable)/i;
|
|
1185
1589
|
function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new Set()) {
|
|
1186
1590
|
if (seen.has(name)) {
|
|
@@ -1202,41 +1606,41 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
|
|
|
1202
1606
|
let destructive = DESTRUCTIVE_VERB.test(name);
|
|
1203
1607
|
const loadingStateBindings = /* @__PURE__ */ new Set();
|
|
1204
1608
|
const inspectCall = (node) => {
|
|
1205
|
-
if (
|
|
1609
|
+
if (t5.isMemberExpression(node.callee) && t5.isIdentifier(node.callee.property)) {
|
|
1206
1610
|
const method = node.callee.property.name;
|
|
1207
1611
|
if (method === "then" || method === "catch" || method === "finally") hasThen = true;
|
|
1208
|
-
if (
|
|
1612
|
+
if (t5.isIdentifier(node.callee.object) && node.callee.object.name === "navigation" && ["navigate", "push", "replace"].includes(method)) {
|
|
1209
1613
|
const firstArg = node.arguments[0];
|
|
1210
|
-
if (
|
|
1614
|
+
if (t5.isStringLiteral(firstArg)) {
|
|
1211
1615
|
targetScreen = firstArg.value;
|
|
1212
1616
|
successSignal = { type: "navigation", target: firstArg.value };
|
|
1213
1617
|
}
|
|
1214
1618
|
}
|
|
1215
|
-
if (
|
|
1619
|
+
if (t5.isIdentifier(node.callee.object) && node.callee.object.name === "navigation" && method === "goBack") {
|
|
1216
1620
|
successSignal = { type: "goBack", description: "Action returns to the previous screen" };
|
|
1217
1621
|
}
|
|
1218
|
-
if (
|
|
1622
|
+
if (t5.isIdentifier(node.callee.object) && node.callee.object.name === "Alert" && method === "alert") {
|
|
1219
1623
|
nativeConfirmationExpected = true;
|
|
1220
1624
|
}
|
|
1221
|
-
if (
|
|
1625
|
+
if (t5.isIdentifier(node.callee.object) && /(toast|notification|snackbar)/i.test(node.callee.object.name)) {
|
|
1222
1626
|
const firstArg = node.arguments[0];
|
|
1223
1627
|
const secondArg = node.arguments[1];
|
|
1224
|
-
const text =
|
|
1628
|
+
const text = t5.isStringLiteral(secondArg) ? secondArg.value : t5.isStringLiteral(firstArg) ? firstArg.value : void 0;
|
|
1225
1629
|
const signal = { type: "toast", description: text };
|
|
1226
|
-
if (/(error|danger|fail)/i.test(method) ||
|
|
1630
|
+
if (/(error|danger|fail)/i.test(method) || t5.isStringLiteral(firstArg) && /error/i.test(firstArg.value)) {
|
|
1227
1631
|
failureSignal = signal;
|
|
1228
1632
|
} else {
|
|
1229
1633
|
successSignal = successSignal ?? signal;
|
|
1230
1634
|
}
|
|
1231
1635
|
}
|
|
1232
1636
|
}
|
|
1233
|
-
if (
|
|
1637
|
+
if (t5.isIdentifier(node.callee)) {
|
|
1234
1638
|
const calleeName = node.callee.name;
|
|
1235
1639
|
if (/^set[A-Z]/.test(calleeName)) {
|
|
1236
1640
|
hasStateSetter = true;
|
|
1237
1641
|
const stateName = setterToStateName(calleeName);
|
|
1238
1642
|
const firstArg = node.arguments[0];
|
|
1239
|
-
if (stateName &&
|
|
1643
|
+
if (stateName && t5.isBooleanLiteral(firstArg)) {
|
|
1240
1644
|
if (/loading|submitting|saving|fetching|refreshing/i.test(stateName)) {
|
|
1241
1645
|
loadingStateBindings.add(stateName);
|
|
1242
1646
|
}
|
|
@@ -1263,14 +1667,14 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
|
|
|
1263
1667
|
}
|
|
1264
1668
|
};
|
|
1265
1669
|
const inspectNode = (node) => {
|
|
1266
|
-
if (
|
|
1267
|
-
if (
|
|
1268
|
-
if (
|
|
1670
|
+
if (t5.isAwaitExpression(node)) hasAwait = true;
|
|
1671
|
+
if (t5.isCallExpression(node)) inspectCall(node);
|
|
1672
|
+
if (t5.isMemberExpression(node) && t5.isIdentifier(node.property) && DESTRUCTIVE_VERB.test(node.property.name)) {
|
|
1269
1673
|
destructive = true;
|
|
1270
1674
|
}
|
|
1271
1675
|
};
|
|
1272
1676
|
if (fn.body) {
|
|
1273
|
-
(0,
|
|
1677
|
+
(0, import_traverse3.default)(fn.body, {
|
|
1274
1678
|
noScope: true,
|
|
1275
1679
|
enter: (nodePath) => inspectNode(nodePath.node)
|
|
1276
1680
|
});
|
|
@@ -1298,21 +1702,21 @@ function setterToStateName(setterName) {
|
|
|
1298
1702
|
}
|
|
1299
1703
|
|
|
1300
1704
|
// src/ast/navigation/calls.ts
|
|
1301
|
-
var
|
|
1302
|
-
var
|
|
1705
|
+
var import_traverse4 = __toESM(require("@babel/traverse"));
|
|
1706
|
+
var t6 = __toESM(require("@babel/types"));
|
|
1303
1707
|
function extractNavigationCalls(ast) {
|
|
1304
1708
|
const calls = [];
|
|
1305
|
-
(0,
|
|
1306
|
-
noScope: !
|
|
1709
|
+
(0, import_traverse4.default)(ast, {
|
|
1710
|
+
noScope: !t6.isFile(ast),
|
|
1307
1711
|
CallExpression: (nodePath) => {
|
|
1308
1712
|
const node = nodePath.node;
|
|
1309
|
-
if (!
|
|
1713
|
+
if (!t6.isMemberExpression(node.callee) || !t6.isIdentifier(node.callee.object) || node.callee.object.name !== "navigation" || !t6.isIdentifier(node.callee.property)) {
|
|
1310
1714
|
return;
|
|
1311
1715
|
}
|
|
1312
1716
|
const method = node.callee.property.name;
|
|
1313
1717
|
if (!["navigate", "push", "replace", "goBack"].includes(method)) return;
|
|
1314
1718
|
const firstArg = node.arguments[0];
|
|
1315
|
-
const targetScreen =
|
|
1719
|
+
const targetScreen = t6.isStringLiteral(firstArg) ? firstArg.value : void 0;
|
|
1316
1720
|
calls.push({
|
|
1317
1721
|
method,
|
|
1318
1722
|
...targetScreen ? { targetScreen } : {},
|
|
@@ -1324,16 +1728,16 @@ function extractNavigationCalls(ast) {
|
|
|
1324
1728
|
}
|
|
1325
1729
|
function extractNavigationParams(arg) {
|
|
1326
1730
|
const params = {};
|
|
1327
|
-
if (!arg || !
|
|
1731
|
+
if (!arg || !t6.isObjectExpression(arg)) return params;
|
|
1328
1732
|
for (const prop of arg.properties) {
|
|
1329
|
-
if (!
|
|
1330
|
-
const key =
|
|
1733
|
+
if (!t6.isObjectProperty(prop)) continue;
|
|
1734
|
+
const key = t6.isIdentifier(prop.key) ? prop.key.name : t6.isStringLiteral(prop.key) ? prop.key.value : void 0;
|
|
1331
1735
|
if (!key) continue;
|
|
1332
|
-
if (
|
|
1736
|
+
if (t6.isMemberExpression(prop.value) && t6.isIdentifier(prop.value.object) && t6.isIdentifier(prop.value.property)) {
|
|
1333
1737
|
params[key] = `${prop.value.object.name}.${prop.value.property.name}`;
|
|
1334
|
-
} else if (
|
|
1738
|
+
} else if (t6.isIdentifier(prop.value)) {
|
|
1335
1739
|
params[key] = prop.value.name;
|
|
1336
|
-
} else if (
|
|
1740
|
+
} else if (t6.isStringLiteral(prop.value)) {
|
|
1337
1741
|
params[key] = prop.value.value;
|
|
1338
1742
|
}
|
|
1339
1743
|
}
|
|
@@ -1426,6 +1830,9 @@ function sanitisePermissionsObject(obj) {
|
|
|
1426
1830
|
if (typeof obj.isPii === "boolean") {
|
|
1427
1831
|
out.isPii = obj.isPii;
|
|
1428
1832
|
}
|
|
1833
|
+
if (typeof obj.assessment === "boolean") {
|
|
1834
|
+
out.assessment = obj.assessment;
|
|
1835
|
+
}
|
|
1429
1836
|
if (Array.isArray(obj.blockedActions)) {
|
|
1430
1837
|
const ids = obj.blockedActions.filter((x) => typeof x === "string" && !!x);
|
|
1431
1838
|
if (ids.length > 0) out.blockedActions = ids;
|
|
@@ -1453,6 +1860,7 @@ var ScreenAnalyzer = class {
|
|
|
1453
1860
|
routeTargetFiles;
|
|
1454
1861
|
/** §D: Count of screens filtered out in strict mode (available after analyze()) */
|
|
1455
1862
|
screensFilteredOut = 0;
|
|
1863
|
+
controlEvidenceFiles = {};
|
|
1456
1864
|
constructor(config, options) {
|
|
1457
1865
|
this.config = config;
|
|
1458
1866
|
this.routeTargetFiles = options?.routeTargetFiles ?? /* @__PURE__ */ new Set();
|
|
@@ -1553,6 +1961,11 @@ var ScreenAnalyzer = class {
|
|
|
1553
1961
|
title: registerScreenMeta?.title,
|
|
1554
1962
|
description: registerScreenMeta?.description,
|
|
1555
1963
|
components,
|
|
1964
|
+
controlCandidates: extractControlEvidence(
|
|
1965
|
+
ast,
|
|
1966
|
+
source,
|
|
1967
|
+
import_path3.default.relative(this.config.rootDir, filePath)
|
|
1968
|
+
),
|
|
1556
1969
|
forms,
|
|
1557
1970
|
actions,
|
|
1558
1971
|
navigationTargets,
|
|
@@ -1563,6 +1976,8 @@ var ScreenAnalyzer = class {
|
|
|
1563
1976
|
...permissionsFromJsDoc.isPii ? { isPii: true } : {}
|
|
1564
1977
|
} : {}
|
|
1565
1978
|
};
|
|
1979
|
+
if (descriptor.controlCandidates?.length)
|
|
1980
|
+
this.controlEvidenceFiles[import_path3.default.relative(this.config.rootDir, filePath).split(import_path3.default.sep).join("/")] = descriptor.controlCandidates;
|
|
1566
1981
|
descriptor.__hasRegisterScreen = hasRegisterScreenCall;
|
|
1567
1982
|
return descriptor;
|
|
1568
1983
|
}
|
|
@@ -1572,7 +1987,7 @@ var ScreenAnalyzer = class {
|
|
|
1572
1987
|
*/
|
|
1573
1988
|
detectRegisterScreenCall(ast) {
|
|
1574
1989
|
let found = false;
|
|
1575
|
-
(0,
|
|
1990
|
+
(0, import_traverse5.default)(ast, {
|
|
1576
1991
|
CallExpression: (nodePath) => {
|
|
1577
1992
|
if (found) return;
|
|
1578
1993
|
const callee = nodePath.node.callee;
|
|
@@ -1589,7 +2004,7 @@ var ScreenAnalyzer = class {
|
|
|
1589
2004
|
*/
|
|
1590
2005
|
extractRegisterScreenMetadata(ast) {
|
|
1591
2006
|
let metadata = null;
|
|
1592
|
-
(0,
|
|
2007
|
+
(0, import_traverse5.default)(ast, {
|
|
1593
2008
|
CallExpression: (nodePath) => {
|
|
1594
2009
|
const callee = nodePath.node.callee;
|
|
1595
2010
|
if (BabelTypes.isIdentifier(callee) && callee.name === "registerScreen" || BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.property) && callee.property.name === "registerScreen") {
|
|
@@ -1886,7 +2301,7 @@ var ScreenAnalyzer = class {
|
|
|
1886
2301
|
*/
|
|
1887
2302
|
extractDefaultComponentName(ast) {
|
|
1888
2303
|
let componentName = "";
|
|
1889
|
-
(0,
|
|
2304
|
+
(0, import_traverse5.default)(ast, {
|
|
1890
2305
|
ExportDefaultDeclaration: (nodePath) => {
|
|
1891
2306
|
const declaration = nodePath.node.declaration;
|
|
1892
2307
|
if (BabelTypes.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
@@ -1911,7 +2326,7 @@ var ScreenAnalyzer = class {
|
|
|
1911
2326
|
*/
|
|
1912
2327
|
extractNavigationTargets(ast) {
|
|
1913
2328
|
const targets = /* @__PURE__ */ new Set();
|
|
1914
|
-
(0,
|
|
2329
|
+
(0, import_traverse5.default)(ast, {
|
|
1915
2330
|
CallExpression: (nodePath) => {
|
|
1916
2331
|
const callee = nodePath.node.callee;
|
|
1917
2332
|
if (BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.object) && callee.object.name === "navigation" && BabelTypes.isIdentifier(callee.property) && callee.property.name === "navigate") {
|
|
@@ -1930,7 +2345,7 @@ var ScreenAnalyzer = class {
|
|
|
1930
2345
|
extractForms(ast) {
|
|
1931
2346
|
const forms = [];
|
|
1932
2347
|
const fields = /* @__PURE__ */ new Map();
|
|
1933
|
-
(0,
|
|
2348
|
+
(0, import_traverse5.default)(ast, {
|
|
1934
2349
|
JSXOpeningElement: (nodePath) => {
|
|
1935
2350
|
const element = nodePath.node;
|
|
1936
2351
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -2051,7 +2466,7 @@ var ScreenAnalyzer = class {
|
|
|
2051
2466
|
extractComponents(ast) {
|
|
2052
2467
|
const components = [];
|
|
2053
2468
|
const seen = /* @__PURE__ */ new Set();
|
|
2054
|
-
(0,
|
|
2469
|
+
(0, import_traverse5.default)(ast, {
|
|
2055
2470
|
JSXOpeningElement: (nodePath) => {
|
|
2056
2471
|
const element = nodePath.node;
|
|
2057
2472
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -2115,7 +2530,7 @@ var ScreenAnalyzer = class {
|
|
|
2115
2530
|
const actionLabels = new Map(
|
|
2116
2531
|
actions.filter((a) => a.label).map((a) => [this.normalizeLabel(a.label), a])
|
|
2117
2532
|
);
|
|
2118
|
-
(0,
|
|
2533
|
+
(0, import_traverse5.default)(ast, {
|
|
2119
2534
|
JSXOpeningElement: (nodePath) => {
|
|
2120
2535
|
const element = nodePath.node;
|
|
2121
2536
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -2184,7 +2599,7 @@ var ScreenAnalyzer = class {
|
|
|
2184
2599
|
}
|
|
2185
2600
|
collectButtonHandlersByLabel(ast) {
|
|
2186
2601
|
const out = /* @__PURE__ */ new Map();
|
|
2187
|
-
(0,
|
|
2602
|
+
(0, import_traverse5.default)(ast, {
|
|
2188
2603
|
JSXOpeningElement: (nodePath) => {
|
|
2189
2604
|
const element = nodePath.node;
|
|
2190
2605
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
@@ -2266,7 +2681,7 @@ var ScreenAnalyzer = class {
|
|
|
2266
2681
|
}
|
|
2267
2682
|
};
|
|
2268
2683
|
if (fn.body) {
|
|
2269
|
-
(0,
|
|
2684
|
+
(0, import_traverse5.default)(
|
|
2270
2685
|
fn.body,
|
|
2271
2686
|
{
|
|
2272
2687
|
noScope: true,
|
|
@@ -2463,7 +2878,7 @@ var ScreenAnalyzer = class {
|
|
|
2463
2878
|
extractCollections(ast) {
|
|
2464
2879
|
const renderItemFns = this.collectRenderItemFunctions(ast);
|
|
2465
2880
|
const collections = [];
|
|
2466
|
-
(0,
|
|
2881
|
+
(0, import_traverse5.default)(ast, {
|
|
2467
2882
|
JSXOpeningElement: (nodePath) => {
|
|
2468
2883
|
const element = nodePath.node;
|
|
2469
2884
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
@@ -2500,7 +2915,7 @@ var ScreenAnalyzer = class {
|
|
|
2500
2915
|
}
|
|
2501
2916
|
collectRenderItemFunctions(ast) {
|
|
2502
2917
|
const out = /* @__PURE__ */ new Map();
|
|
2503
|
-
(0,
|
|
2918
|
+
(0, import_traverse5.default)(ast, {
|
|
2504
2919
|
VariableDeclarator: (nodePath) => {
|
|
2505
2920
|
if (!BabelTypes.isIdentifier(nodePath.node.id)) return;
|
|
2506
2921
|
const init = nodePath.node.init;
|
|
@@ -2543,7 +2958,7 @@ var ScreenAnalyzer = class {
|
|
|
2543
2958
|
}
|
|
2544
2959
|
extractRowAction(fn) {
|
|
2545
2960
|
let action;
|
|
2546
|
-
(0,
|
|
2961
|
+
(0, import_traverse5.default)(
|
|
2547
2962
|
fn.body,
|
|
2548
2963
|
{
|
|
2549
2964
|
noScope: true,
|
|
@@ -2596,7 +3011,7 @@ var ScreenAnalyzer = class {
|
|
|
2596
3011
|
} else if (BabelTypes.isIdentifier(firstParam)) {
|
|
2597
3012
|
itemNames.add(firstParam.name);
|
|
2598
3013
|
}
|
|
2599
|
-
(0,
|
|
3014
|
+
(0, import_traverse5.default)(
|
|
2600
3015
|
fn.body,
|
|
2601
3016
|
{
|
|
2602
3017
|
noScope: true,
|
|
@@ -2638,7 +3053,7 @@ var ScreenAnalyzer = class {
|
|
|
2638
3053
|
inferSearchField(ast, dataSource) {
|
|
2639
3054
|
if (!dataSource) return void 0;
|
|
2640
3055
|
let queryBinding;
|
|
2641
|
-
(0,
|
|
3056
|
+
(0, import_traverse5.default)(ast, {
|
|
2642
3057
|
CallExpression: (nodePath) => {
|
|
2643
3058
|
const node = nodePath.node;
|
|
2644
3059
|
if (!BabelTypes.isMemberExpression(node.callee)) return;
|
|
@@ -2649,7 +3064,7 @@ var ScreenAnalyzer = class {
|
|
|
2649
3064
|
const fn = node.arguments[0];
|
|
2650
3065
|
if (!BabelTypes.isArrowFunctionExpression(fn) && !BabelTypes.isFunctionExpression(fn))
|
|
2651
3066
|
return;
|
|
2652
|
-
(0,
|
|
3067
|
+
(0, import_traverse5.default)(
|
|
2653
3068
|
fn.body,
|
|
2654
3069
|
{
|
|
2655
3070
|
noScope: true,
|
|
@@ -2695,16 +3110,16 @@ var ScreenAnalyzer = class {
|
|
|
2695
3110
|
var import_fs4 = require("fs");
|
|
2696
3111
|
var import_path4 = __toESM(require("path"));
|
|
2697
3112
|
var parser2 = __toESM(require("@babel/parser"));
|
|
2698
|
-
var
|
|
2699
|
-
var
|
|
3113
|
+
var import_traverse6 = __toESM(require("@babel/traverse"));
|
|
3114
|
+
var t8 = __toESM(require("@babel/types"));
|
|
2700
3115
|
|
|
2701
3116
|
// src/ast/navigation/module-graph.ts
|
|
2702
3117
|
var import_node_fs = require("fs");
|
|
2703
3118
|
var import_node_path = __toESM(require("path"));
|
|
2704
3119
|
var parser = __toESM(require("@babel/parser"));
|
|
2705
|
-
var
|
|
3120
|
+
var t7 = __toESM(require("@babel/types"));
|
|
2706
3121
|
var EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
2707
|
-
var
|
|
3122
|
+
var MAX_HOPS2 = 8;
|
|
2708
3123
|
var ModuleGraph = class {
|
|
2709
3124
|
asts = /* @__PURE__ */ new Map();
|
|
2710
3125
|
resolved = /* @__PURE__ */ new Map();
|
|
@@ -2788,15 +3203,15 @@ var ModuleGraph = class {
|
|
|
2788
3203
|
const ast = this.parse(file2);
|
|
2789
3204
|
if (!ast) return out;
|
|
2790
3205
|
for (const stmt of ast.program.body) {
|
|
2791
|
-
if (!
|
|
3206
|
+
if (!t7.isImportDeclaration(stmt)) continue;
|
|
2792
3207
|
const source = stmt.source.value;
|
|
2793
3208
|
for (const spec of stmt.specifiers) {
|
|
2794
|
-
if (
|
|
3209
|
+
if (t7.isImportDefaultSpecifier(spec)) {
|
|
2795
3210
|
out.set(spec.local.name, { source, imported: "default" });
|
|
2796
|
-
} else if (
|
|
3211
|
+
} else if (t7.isImportNamespaceSpecifier(spec)) {
|
|
2797
3212
|
out.set(spec.local.name, { source, imported: "*" });
|
|
2798
|
-
} else if (
|
|
2799
|
-
const imported =
|
|
3213
|
+
} else if (t7.isImportSpecifier(spec)) {
|
|
3214
|
+
const imported = t7.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
2800
3215
|
out.set(spec.local.name, { source, imported });
|
|
2801
3216
|
}
|
|
2802
3217
|
}
|
|
@@ -2808,11 +3223,11 @@ var ModuleGraph = class {
|
|
|
2808
3223
|
const ast = this.parse(file2);
|
|
2809
3224
|
if (!ast) return null;
|
|
2810
3225
|
for (const stmt of ast.program.body) {
|
|
2811
|
-
const decl =
|
|
2812
|
-
if (!
|
|
3226
|
+
const decl = t7.isExportNamedDeclaration(stmt) ? stmt.declaration : stmt;
|
|
3227
|
+
if (!t7.isVariableDeclaration(decl)) continue;
|
|
2813
3228
|
for (const d of decl.declarations) {
|
|
2814
|
-
if (
|
|
2815
|
-
return
|
|
3229
|
+
if (t7.isIdentifier(d.id) && d.id.name === name && d.init) {
|
|
3230
|
+
return t7.isTSAsExpression(d.init) ? d.init.expression : d.init;
|
|
2816
3231
|
}
|
|
2817
3232
|
}
|
|
2818
3233
|
}
|
|
@@ -2823,7 +3238,7 @@ var ModuleGraph = class {
|
|
|
2823
3238
|
* Devolve o arquivo e o nome sob o qual ele é definido lá.
|
|
2824
3239
|
*/
|
|
2825
3240
|
resolveBinding(file2, name, hops = 0) {
|
|
2826
|
-
if (hops >
|
|
3241
|
+
if (hops > MAX_HOPS2) return null;
|
|
2827
3242
|
if (this.topLevelInit(file2, name) !== null) return { file: file2, name };
|
|
2828
3243
|
const binding = this.imports(file2).get(name);
|
|
2829
3244
|
if (binding) {
|
|
@@ -2836,10 +3251,10 @@ var ModuleGraph = class {
|
|
|
2836
3251
|
const ast = this.parse(file2);
|
|
2837
3252
|
if (ast) {
|
|
2838
3253
|
for (const stmt of ast.program.body) {
|
|
2839
|
-
if (!
|
|
3254
|
+
if (!t7.isExportNamedDeclaration(stmt) || !stmt.source) continue;
|
|
2840
3255
|
for (const spec of stmt.specifiers) {
|
|
2841
|
-
if (!
|
|
2842
|
-
const exported =
|
|
3256
|
+
if (!t7.isExportSpecifier(spec)) continue;
|
|
3257
|
+
const exported = t7.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value;
|
|
2843
3258
|
if (exported !== name) continue;
|
|
2844
3259
|
const target = this.resolve(file2, stmt.source.value);
|
|
2845
3260
|
if (!target) return null;
|
|
@@ -2850,7 +3265,7 @@ var ModuleGraph = class {
|
|
|
2850
3265
|
}
|
|
2851
3266
|
if (ast) {
|
|
2852
3267
|
for (const stmt of ast.program.body) {
|
|
2853
|
-
if (!
|
|
3268
|
+
if (!t7.isExportAllDeclaration(stmt)) continue;
|
|
2854
3269
|
const target = this.resolve(file2, stmt.source.value);
|
|
2855
3270
|
if (!target || target === file2) continue;
|
|
2856
3271
|
const deeper = this.resolveBinding(target, name, hops + 1);
|
|
@@ -2859,8 +3274,8 @@ var ModuleGraph = class {
|
|
|
2859
3274
|
}
|
|
2860
3275
|
if (ast) {
|
|
2861
3276
|
for (const stmt of ast.program.body) {
|
|
2862
|
-
if (!
|
|
2863
|
-
if (
|
|
3277
|
+
if (!t7.isExportDefaultDeclaration(stmt)) continue;
|
|
3278
|
+
if (t7.isIdentifier(stmt.declaration)) {
|
|
2864
3279
|
const local = stmt.declaration.name;
|
|
2865
3280
|
if (local === name) return null;
|
|
2866
3281
|
return this.resolveBinding(file2, local, hops + 1) ?? { file: file2, name: local };
|
|
@@ -2877,12 +3292,12 @@ var ModuleGraph = class {
|
|
|
2877
3292
|
* com interpolação nem valor calculado, de propósito.
|
|
2878
3293
|
*/
|
|
2879
3294
|
stringConstant(file2, node, hops = 0) {
|
|
2880
|
-
if (!node || hops >
|
|
2881
|
-
if (
|
|
2882
|
-
if (
|
|
3295
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
3296
|
+
if (t7.isStringLiteral(node)) return node.value;
|
|
3297
|
+
if (t7.isTemplateLiteral(node)) {
|
|
2883
3298
|
return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? null : null;
|
|
2884
3299
|
}
|
|
2885
|
-
if (
|
|
3300
|
+
if (t7.isTSAsExpression(node)) return this.stringConstant(file2, node.expression, hops + 1);
|
|
2886
3301
|
const chain = memberChain(node);
|
|
2887
3302
|
if (!chain) return null;
|
|
2888
3303
|
const origin = this.resolveBinding(file2, chain.root);
|
|
@@ -2890,12 +3305,12 @@ var ModuleGraph = class {
|
|
|
2890
3305
|
let current = this.topLevelInit(origin.file, origin.name);
|
|
2891
3306
|
if (!current) return null;
|
|
2892
3307
|
for (const key of chain.path) {
|
|
2893
|
-
if (!
|
|
3308
|
+
if (!t7.isObjectExpression(current)) return null;
|
|
2894
3309
|
const prop = objectProperty(current, key);
|
|
2895
3310
|
if (!prop) return null;
|
|
2896
|
-
current =
|
|
3311
|
+
current = t7.isTSAsExpression(prop) ? prop.expression : prop;
|
|
2897
3312
|
}
|
|
2898
|
-
return
|
|
3313
|
+
return t7.isStringLiteral(current) ? current.value : null;
|
|
2899
3314
|
}
|
|
2900
3315
|
/**
|
|
2901
3316
|
* O ARQUIVO onde vive o componente de uma rota, ou `null`.
|
|
@@ -2906,24 +3321,24 @@ var ModuleGraph = class {
|
|
|
2906
3321
|
* que é como o `pocketpal` monta todas as telas do Drawer).
|
|
2907
3322
|
*/
|
|
2908
3323
|
componentFile(file2, node, hops = 0) {
|
|
2909
|
-
if (!node || hops >
|
|
2910
|
-
if (
|
|
3324
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
3325
|
+
if (t7.isCallExpression(node)) {
|
|
2911
3326
|
for (const arg of node.arguments) {
|
|
2912
|
-
if (
|
|
3327
|
+
if (t7.isIdentifier(arg) || t7.isMemberExpression(arg)) {
|
|
2913
3328
|
const inner = this.componentFile(file2, arg, hops + 1);
|
|
2914
3329
|
if (inner) return inner;
|
|
2915
3330
|
}
|
|
2916
3331
|
}
|
|
2917
3332
|
return null;
|
|
2918
3333
|
}
|
|
2919
|
-
if (
|
|
3334
|
+
if (t7.isTSAsExpression(node)) return this.componentFile(file2, node.expression, hops + 1);
|
|
2920
3335
|
const chain = memberChain(node);
|
|
2921
3336
|
if (!chain) return null;
|
|
2922
3337
|
const origin = this.resolveBinding(file2, chain.root);
|
|
2923
3338
|
if (!origin) return null;
|
|
2924
3339
|
if (chain.path.length === 0) return origin.file;
|
|
2925
3340
|
const init = this.topLevelInit(origin.file, origin.name);
|
|
2926
|
-
if (init &&
|
|
3341
|
+
if (init && t7.isObjectExpression(init)) {
|
|
2927
3342
|
const prop = objectProperty(init, chain.path[0]);
|
|
2928
3343
|
if (prop) return this.componentFile(origin.file, prop, hops + 1);
|
|
2929
3344
|
}
|
|
@@ -2946,7 +3361,7 @@ var ModuleGraph = class {
|
|
|
2946
3361
|
const names = /* @__PURE__ */ new Set();
|
|
2947
3362
|
const visit = (node) => {
|
|
2948
3363
|
if (!node || typeof node !== "object") return;
|
|
2949
|
-
if (
|
|
3364
|
+
if (t7.isJSXOpeningElement(node) && t7.isJSXIdentifier(node.name)) {
|
|
2950
3365
|
const n = node.name.name;
|
|
2951
3366
|
if (/^[A-Z]/.test(n)) names.add(n);
|
|
2952
3367
|
}
|
|
@@ -2971,24 +3386,24 @@ var ModuleGraph = class {
|
|
|
2971
3386
|
function memberChain(node) {
|
|
2972
3387
|
const chain = [];
|
|
2973
3388
|
let current = node;
|
|
2974
|
-
while (
|
|
3389
|
+
while (t7.isMemberExpression(current)) {
|
|
2975
3390
|
if (current.computed) {
|
|
2976
|
-
if (!
|
|
3391
|
+
if (!t7.isStringLiteral(current.property)) return null;
|
|
2977
3392
|
chain.unshift(current.property.value);
|
|
2978
|
-
} else if (
|
|
3393
|
+
} else if (t7.isIdentifier(current.property)) {
|
|
2979
3394
|
chain.unshift(current.property.name);
|
|
2980
3395
|
} else {
|
|
2981
3396
|
return null;
|
|
2982
3397
|
}
|
|
2983
3398
|
current = current.object;
|
|
2984
3399
|
}
|
|
2985
|
-
return
|
|
3400
|
+
return t7.isIdentifier(current) ? { root: current.name, path: chain } : null;
|
|
2986
3401
|
}
|
|
2987
3402
|
function objectProperty(obj, key) {
|
|
2988
3403
|
for (const prop of obj.properties) {
|
|
2989
|
-
if (!
|
|
2990
|
-
const name =
|
|
2991
|
-
if (name === key &&
|
|
3404
|
+
if (!t7.isObjectProperty(prop)) continue;
|
|
3405
|
+
const name = t7.isIdentifier(prop.key) ? prop.key.name : t7.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3406
|
+
if (name === key && t7.isExpression(prop.value)) return prop.value;
|
|
2992
3407
|
}
|
|
2993
3408
|
return null;
|
|
2994
3409
|
}
|
|
@@ -3102,7 +3517,7 @@ function workspaceGlobs(dir) {
|
|
|
3102
3517
|
}
|
|
3103
3518
|
function expandWorkspaceGlobs(root, globs) {
|
|
3104
3519
|
const out = [];
|
|
3105
|
-
const
|
|
3520
|
+
const add2 = (dir) => {
|
|
3106
3521
|
const pkgPath = import_node_path.default.join(dir, "package.json");
|
|
3107
3522
|
if (!(0, import_node_fs.existsSync)(pkgPath)) return;
|
|
3108
3523
|
try {
|
|
@@ -3111,9 +3526,9 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
3111
3526
|
} catch {
|
|
3112
3527
|
}
|
|
3113
3528
|
};
|
|
3114
|
-
for (const
|
|
3115
|
-
if (
|
|
3116
|
-
const parent = import_node_path.default.join(root,
|
|
3529
|
+
for (const glob2 of globs) {
|
|
3530
|
+
if (glob2.endsWith("/*")) {
|
|
3531
|
+
const parent = import_node_path.default.join(root, glob2.slice(0, -2));
|
|
3117
3532
|
let entries = [];
|
|
3118
3533
|
try {
|
|
3119
3534
|
entries = (0, import_node_fs.readdirSync)(parent);
|
|
@@ -3123,12 +3538,12 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
3123
3538
|
for (const entry of entries) {
|
|
3124
3539
|
const dir = import_node_path.default.join(parent, entry);
|
|
3125
3540
|
try {
|
|
3126
|
-
if ((0, import_node_fs.statSync)(dir).isDirectory())
|
|
3541
|
+
if ((0, import_node_fs.statSync)(dir).isDirectory()) add2(dir);
|
|
3127
3542
|
} catch {
|
|
3128
3543
|
}
|
|
3129
3544
|
}
|
|
3130
|
-
} else if (!
|
|
3131
|
-
|
|
3545
|
+
} else if (!glob2.includes("*")) {
|
|
3546
|
+
add2(import_node_path.default.join(root, glob2));
|
|
3132
3547
|
}
|
|
3133
3548
|
}
|
|
3134
3549
|
return out.sort((a, b) => b.name.length - a.name.length);
|
|
@@ -3249,19 +3664,19 @@ var NAVIGATOR_FACTORIES = {
|
|
|
3249
3664
|
};
|
|
3250
3665
|
var CUSTOM_FACTORY = /^create[A-Za-z0-9]*(Navigator|Stack|Tabs?|Drawer)$/;
|
|
3251
3666
|
function unwrapStaticScreen(value) {
|
|
3252
|
-
if (
|
|
3667
|
+
if (t8.isCallExpression(value)) {
|
|
3253
3668
|
const arg = value.arguments[0];
|
|
3254
3669
|
return arg ? unwrapStaticScreen(arg) : null;
|
|
3255
3670
|
}
|
|
3256
|
-
if (
|
|
3671
|
+
if (t8.isObjectExpression(value)) {
|
|
3257
3672
|
for (const prop of value.properties) {
|
|
3258
|
-
if (!
|
|
3259
|
-
const key =
|
|
3260
|
-
if (key === "screen" &&
|
|
3673
|
+
if (!t8.isObjectProperty(prop)) continue;
|
|
3674
|
+
const key = t8.isIdentifier(prop.key) ? prop.key.name : null;
|
|
3675
|
+
if (key === "screen" && t8.isExpression(prop.value)) return prop.value;
|
|
3261
3676
|
}
|
|
3262
3677
|
return null;
|
|
3263
3678
|
}
|
|
3264
|
-
return
|
|
3679
|
+
return t8.isExpression(value) ? value : null;
|
|
3265
3680
|
}
|
|
3266
3681
|
function inferNavigatorType(factoryName) {
|
|
3267
3682
|
if (/drawer/i.test(factoryName)) return "drawer";
|
|
@@ -3452,24 +3867,24 @@ var NavigationAnalyzer = class {
|
|
|
3452
3867
|
const ast = this.graph.parse(filePath);
|
|
3453
3868
|
if (!ast) return [];
|
|
3454
3869
|
const out = /* @__PURE__ */ new Map();
|
|
3455
|
-
(0,
|
|
3870
|
+
(0, import_traverse6.default)(ast, {
|
|
3456
3871
|
CallExpression: (nodePath) => {
|
|
3457
3872
|
const callee = nodePath.node.callee;
|
|
3458
|
-
if (!
|
|
3873
|
+
if (!t8.isIdentifier(callee)) return;
|
|
3459
3874
|
const known = NAVIGATOR_FACTORIES[callee.name] ?? (CUSTOM_FACTORY.test(callee.name) ? inferNavigatorType(callee.name) : null);
|
|
3460
3875
|
const type = known ?? inferNavigatorType(callee.name);
|
|
3461
3876
|
const confident = known !== null;
|
|
3462
3877
|
let up = nodePath.parentPath;
|
|
3463
3878
|
for (let i = 0; i < 4 && up; i++) {
|
|
3464
|
-
if (
|
|
3465
|
-
if (!
|
|
3879
|
+
if (t8.isVariableDeclarator(up.node)) break;
|
|
3880
|
+
if (!t8.isMemberExpression(up.node) && !t8.isCallExpression(up.node)) {
|
|
3466
3881
|
up = null;
|
|
3467
3882
|
break;
|
|
3468
3883
|
}
|
|
3469
3884
|
up = up.parentPath;
|
|
3470
3885
|
}
|
|
3471
3886
|
const declarator = up?.node;
|
|
3472
|
-
if (!declarator || !
|
|
3887
|
+
if (!declarator || !t8.isVariableDeclarator(declarator) || !t8.isIdentifier(declarator.id)) {
|
|
3473
3888
|
return;
|
|
3474
3889
|
}
|
|
3475
3890
|
const name = declarator.id.name;
|
|
@@ -3499,15 +3914,15 @@ var NavigationAnalyzer = class {
|
|
|
3499
3914
|
* e agrupar rotas não muda o que elas são.
|
|
3500
3915
|
*/
|
|
3501
3916
|
parseStaticScreens(filePath, config, navigatorName, navigatorType, depth = 0) {
|
|
3502
|
-
if (!config || !
|
|
3917
|
+
if (!config || !t8.isObjectExpression(config) || depth > 4) return [];
|
|
3503
3918
|
const out = [];
|
|
3504
3919
|
for (const prop of config.properties) {
|
|
3505
|
-
if (!
|
|
3506
|
-
const key =
|
|
3920
|
+
if (!t8.isObjectProperty(prop)) continue;
|
|
3921
|
+
const key = t8.isIdentifier(prop.key) ? prop.key.name : t8.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3507
3922
|
if (!key) continue;
|
|
3508
|
-
if (key === "groups" &&
|
|
3923
|
+
if (key === "groups" && t8.isObjectExpression(prop.value)) {
|
|
3509
3924
|
for (const group of prop.value.properties) {
|
|
3510
|
-
if (!
|
|
3925
|
+
if (!t8.isObjectProperty(group) || !t8.isExpression(group.value)) continue;
|
|
3511
3926
|
out.push(
|
|
3512
3927
|
...this.parseStaticScreens(
|
|
3513
3928
|
filePath,
|
|
@@ -3520,10 +3935,10 @@ var NavigationAnalyzer = class {
|
|
|
3520
3935
|
}
|
|
3521
3936
|
continue;
|
|
3522
3937
|
}
|
|
3523
|
-
if (key !== "screens" || !
|
|
3938
|
+
if (key !== "screens" || !t8.isObjectExpression(prop.value)) continue;
|
|
3524
3939
|
for (const entry of prop.value.properties) {
|
|
3525
|
-
if (!
|
|
3526
|
-
const routeName =
|
|
3940
|
+
if (!t8.isObjectProperty(entry)) continue;
|
|
3941
|
+
const routeName = t8.isIdentifier(entry.key) ? entry.key.name : t8.isStringLiteral(entry.key) ? entry.key.value : null;
|
|
3527
3942
|
if (!routeName) continue;
|
|
3528
3943
|
const componentExpr = unwrapStaticScreen(entry.value);
|
|
3529
3944
|
const componentFile = componentExpr ? this.graph.componentFile(filePath, componentExpr) : null;
|
|
@@ -3613,15 +4028,15 @@ var NavigationAnalyzer = class {
|
|
|
3613
4028
|
const ast = this.graph.parse(filePath);
|
|
3614
4029
|
if (!ast) return [];
|
|
3615
4030
|
const found = /* @__PURE__ */ new Map();
|
|
3616
|
-
(0,
|
|
4031
|
+
(0, import_traverse6.default)(ast, {
|
|
3617
4032
|
JSXElement: (nodePath) => {
|
|
3618
4033
|
const { node } = nodePath;
|
|
3619
4034
|
const openingElement = node.openingElement;
|
|
3620
|
-
if (!
|
|
4035
|
+
if (!t8.isJSXMemberExpression(openingElement.name) || !t8.isJSXIdentifier(openingElement.name.object)) {
|
|
3621
4036
|
return;
|
|
3622
4037
|
}
|
|
3623
4038
|
const objectName = openingElement.name.object.name;
|
|
3624
|
-
const propertyName =
|
|
4039
|
+
const propertyName = t8.isJSXIdentifier(openingElement.name.property) ? openingElement.name.property.name : null;
|
|
3625
4040
|
if (propertyName !== "Navigator") return;
|
|
3626
4041
|
const decl = this.resolveNavigator(filePath, objectName, declarations);
|
|
3627
4042
|
if (!decl) return;
|
|
@@ -3631,9 +4046,9 @@ var NavigationAnalyzer = class {
|
|
|
3631
4046
|
screens: []
|
|
3632
4047
|
};
|
|
3633
4048
|
const initialRouteAttr = openingElement.attributes.find(
|
|
3634
|
-
(attr) =>
|
|
4049
|
+
(attr) => t8.isJSXAttribute(attr) && t8.isJSXIdentifier(attr.name) && attr.name.name === "initialRouteName"
|
|
3635
4050
|
);
|
|
3636
|
-
if (
|
|
4051
|
+
if (t8.isJSXAttribute(initialRouteAttr)) {
|
|
3637
4052
|
const initial = this.attributeString(filePath, initialRouteAttr);
|
|
3638
4053
|
if (initial) navigator.initialRouteName = initial;
|
|
3639
4054
|
}
|
|
@@ -3685,15 +4100,15 @@ var NavigationAnalyzer = class {
|
|
|
3685
4100
|
if (!ast) return [];
|
|
3686
4101
|
const byNavigator = /* @__PURE__ */ new Map();
|
|
3687
4102
|
const resolved = /* @__PURE__ */ new Map();
|
|
3688
|
-
(0,
|
|
4103
|
+
(0, import_traverse6.default)(ast, {
|
|
3689
4104
|
JSXElement: (nodePath) => {
|
|
3690
4105
|
const name = nodePath.node.openingElement.name;
|
|
3691
|
-
if (!
|
|
3692
|
-
if (!
|
|
4106
|
+
if (!t8.isJSXMemberExpression(name) || !t8.isJSXIdentifier(name.object)) return;
|
|
4107
|
+
if (!t8.isJSXIdentifier(name.property) || name.property.name !== "Screen") return;
|
|
3693
4108
|
const insideNavigator = nodePath.findParent((parent) => {
|
|
3694
4109
|
if (!parent.isJSXElement()) return false;
|
|
3695
4110
|
const parentName = parent.node.openingElement.name;
|
|
3696
|
-
return
|
|
4111
|
+
return t8.isJSXMemberExpression(parentName) && t8.isJSXIdentifier(parentName.property) && parentName.property.name === "Navigator";
|
|
3697
4112
|
});
|
|
3698
4113
|
if (insideNavigator) return;
|
|
3699
4114
|
const local = name.object.name;
|
|
@@ -3732,9 +4147,9 @@ var NavigationAnalyzer = class {
|
|
|
3732
4147
|
const binding = nodePath.scope.getBinding(localName);
|
|
3733
4148
|
if (!binding || binding.kind !== "param") return null;
|
|
3734
4149
|
const param = binding.path.node;
|
|
3735
|
-
if (!
|
|
4150
|
+
if (!t8.isIdentifier(param) || !t8.isTSTypeAnnotation(param.typeAnnotation)) return null;
|
|
3736
4151
|
const annotation = param.typeAnnotation.typeAnnotation;
|
|
3737
|
-
if (!
|
|
4152
|
+
if (!t8.isTSTypeQuery(annotation) || !t8.isIdentifier(annotation.exprName)) return null;
|
|
3738
4153
|
return this.resolveNavigator(filePath, annotation.exprName.name, declarations);
|
|
3739
4154
|
}
|
|
3740
4155
|
/**
|
|
@@ -3755,8 +4170,8 @@ var NavigationAnalyzer = class {
|
|
|
3755
4170
|
/** O valor string de um atributo JSX, resolvendo constante importada. */
|
|
3756
4171
|
attributeString(filePath, attr) {
|
|
3757
4172
|
const value = attr.value;
|
|
3758
|
-
if (
|
|
3759
|
-
if (
|
|
4173
|
+
if (t8.isStringLiteral(value)) return value.value;
|
|
4174
|
+
if (t8.isJSXExpressionContainer(value) && t8.isExpression(value.expression)) {
|
|
3760
4175
|
return this.graph.stringConstant(filePath, value.expression);
|
|
3761
4176
|
}
|
|
3762
4177
|
return null;
|
|
@@ -3770,9 +4185,9 @@ var NavigationAnalyzer = class {
|
|
|
3770
4185
|
// `never`. It is a question about the member name, not about the node
|
|
3771
4186
|
// kind.
|
|
3772
4187
|
isNavigatorMember(node, navigatorVarName, member) {
|
|
3773
|
-
if (!
|
|
4188
|
+
if (!t8.isJSXElement(node)) return false;
|
|
3774
4189
|
const name = node.openingElement.name;
|
|
3775
|
-
return
|
|
4190
|
+
return t8.isJSXMemberExpression(name) && t8.isJSXIdentifier(name.object) && name.object.name === navigatorVarName && t8.isJSXIdentifier(name.property) && name.property.name === member;
|
|
3776
4191
|
}
|
|
3777
4192
|
/**
|
|
3778
4193
|
* Flatten a navigator's JSX children into the `<X.Screen>` elements they
|
|
@@ -3803,37 +4218,37 @@ var NavigationAnalyzer = class {
|
|
|
3803
4218
|
found.push(...this.collectScreenElements(group.children, navigatorVarName, depth + 1));
|
|
3804
4219
|
return;
|
|
3805
4220
|
}
|
|
3806
|
-
if (
|
|
4221
|
+
if (t8.isJSXElement(node)) {
|
|
3807
4222
|
const name = node.openingElement.name;
|
|
3808
|
-
const isForeignNavigator =
|
|
4223
|
+
const isForeignNavigator = t8.isJSXMemberExpression(name) && t8.isJSXIdentifier(name.property) && name.property.name === "Navigator";
|
|
3809
4224
|
if (isForeignNavigator) return;
|
|
3810
4225
|
found.push(...this.collectScreenElements(node.children, navigatorVarName, depth + 1));
|
|
3811
4226
|
return;
|
|
3812
4227
|
}
|
|
3813
|
-
if (
|
|
4228
|
+
if (t8.isJSXFragment(node)) {
|
|
3814
4229
|
found.push(...this.collectScreenElements(node.children, navigatorVarName, depth + 1));
|
|
3815
4230
|
return;
|
|
3816
4231
|
}
|
|
3817
|
-
if (
|
|
4232
|
+
if (t8.isJSXExpressionContainer(node)) {
|
|
3818
4233
|
visit(node.expression);
|
|
3819
4234
|
return;
|
|
3820
4235
|
}
|
|
3821
|
-
if (
|
|
4236
|
+
if (t8.isConditionalExpression(node)) {
|
|
3822
4237
|
visit(node.consequent);
|
|
3823
4238
|
visit(node.alternate);
|
|
3824
4239
|
return;
|
|
3825
4240
|
}
|
|
3826
|
-
if (
|
|
4241
|
+
if (t8.isLogicalExpression(node)) {
|
|
3827
4242
|
visit(node.left);
|
|
3828
4243
|
visit(node.right);
|
|
3829
4244
|
return;
|
|
3830
4245
|
}
|
|
3831
|
-
if (
|
|
4246
|
+
if (t8.isCallExpression(node)) {
|
|
3832
4247
|
for (const arg of node.arguments) {
|
|
3833
|
-
if (
|
|
3834
|
-
if (
|
|
4248
|
+
if (t8.isArrowFunctionExpression(arg) || t8.isFunctionExpression(arg)) {
|
|
4249
|
+
if (t8.isBlockStatement(arg.body)) {
|
|
3835
4250
|
for (const stmt of arg.body.body) {
|
|
3836
|
-
if (
|
|
4251
|
+
if (t8.isReturnStatement(stmt)) visit(stmt.argument);
|
|
3837
4252
|
}
|
|
3838
4253
|
} else {
|
|
3839
4254
|
visit(arg.body);
|
|
@@ -3842,11 +4257,11 @@ var NavigationAnalyzer = class {
|
|
|
3842
4257
|
}
|
|
3843
4258
|
return;
|
|
3844
4259
|
}
|
|
3845
|
-
if (
|
|
4260
|
+
if (t8.isArrayExpression(node)) {
|
|
3846
4261
|
for (const el of node.elements) visit(el);
|
|
3847
4262
|
return;
|
|
3848
4263
|
}
|
|
3849
|
-
if (
|
|
4264
|
+
if (t8.isTSAsExpression(node) || t8.isTSNonNullExpression(node)) {
|
|
3850
4265
|
visit(node.expression);
|
|
3851
4266
|
}
|
|
3852
4267
|
};
|
|
@@ -3876,17 +4291,17 @@ var NavigationAnalyzer = class {
|
|
|
3876
4291
|
parseScreenElement(filePath, element, navigatorName, navigatorType) {
|
|
3877
4292
|
const attrs = element.openingElement.attributes;
|
|
3878
4293
|
const nameAttr = attrs.find(
|
|
3879
|
-
(a) =>
|
|
4294
|
+
(a) => t8.isJSXAttribute(a) && t8.isJSXIdentifier(a.name) && a.name.name === "name"
|
|
3880
4295
|
);
|
|
3881
|
-
const screenName =
|
|
4296
|
+
const screenName = t8.isJSXAttribute(nameAttr) ? this.attributeString(filePath, nameAttr) : null;
|
|
3882
4297
|
if (!screenName) return null;
|
|
3883
4298
|
const componentAttr = attrs.find(
|
|
3884
|
-
(a) =>
|
|
4299
|
+
(a) => t8.isJSXAttribute(a) && t8.isJSXIdentifier(a.name) && a.name.name === "component"
|
|
3885
4300
|
);
|
|
3886
4301
|
let componentFile = null;
|
|
3887
|
-
if (
|
|
4302
|
+
if (t8.isJSXAttribute(componentAttr) && t8.isJSXExpressionContainer(componentAttr.value)) {
|
|
3888
4303
|
const expr = componentAttr.value.expression;
|
|
3889
|
-
if (
|
|
4304
|
+
if (t8.isExpression(expr)) componentFile = this.graph.componentFile(filePath, expr);
|
|
3890
4305
|
}
|
|
3891
4306
|
if (componentFile) this.routeTargets.set(screenName, componentFile);
|
|
3892
4307
|
return {
|
|
@@ -3899,9 +4314,9 @@ var NavigationAnalyzer = class {
|
|
|
3899
4314
|
/** Extract string attribute value from JSX attributes */
|
|
3900
4315
|
extractAttributeValue(attributes, attrName) {
|
|
3901
4316
|
const attr = attributes.find(
|
|
3902
|
-
(a) =>
|
|
4317
|
+
(a) => t8.isJSXAttribute(a) && t8.isJSXIdentifier(a.name) && a.name.name === attrName
|
|
3903
4318
|
);
|
|
3904
|
-
if (
|
|
4319
|
+
if (t8.isJSXAttribute(attr) && t8.isStringLiteral(attr.value)) {
|
|
3905
4320
|
return attr.value.value;
|
|
3906
4321
|
}
|
|
3907
4322
|
return null;
|
|
@@ -3918,7 +4333,7 @@ var NavigationAnalyzer = class {
|
|
|
3918
4333
|
...this.config.parserPlugins || []
|
|
3919
4334
|
]
|
|
3920
4335
|
});
|
|
3921
|
-
(0,
|
|
4336
|
+
(0, import_traverse6.default)(ast, {
|
|
3922
4337
|
TSTypeAliasDeclaration: (nodePath) => {
|
|
3923
4338
|
const { node } = nodePath;
|
|
3924
4339
|
const typeName = node.id.name;
|
|
@@ -3991,7 +4406,7 @@ var NavigationAnalyzer = class {
|
|
|
3991
4406
|
if (type.type === "TSUndefinedKeyword") return "undefined";
|
|
3992
4407
|
if (type.type === "TSNullKeyword") return "null";
|
|
3993
4408
|
if (type.type === "TSUnionType") {
|
|
3994
|
-
return type.types.map((
|
|
4409
|
+
return type.types.map((t18) => this.typeToString(t18)).join(" | ");
|
|
3995
4410
|
}
|
|
3996
4411
|
if (type.type === "TSTypeLiteral") {
|
|
3997
4412
|
return "object";
|
|
@@ -4012,7 +4427,7 @@ var NavigationAnalyzer = class {
|
|
|
4012
4427
|
/** Attach parsed type params to navigator screens */
|
|
4013
4428
|
attachParamsToNavigators(navigators, types) {
|
|
4014
4429
|
for (const navigator of navigators) {
|
|
4015
|
-
const matchingType = types.find((
|
|
4430
|
+
const matchingType = types.find((t18) => t18.type === navigator.type);
|
|
4016
4431
|
if (matchingType) {
|
|
4017
4432
|
for (const screen of navigator.screens) {
|
|
4018
4433
|
const screenParams = matchingType.paramEntries.get(screen.name);
|
|
@@ -4091,8 +4506,8 @@ var NavigationAnalyzer = class {
|
|
|
4091
4506
|
// src/analyzers/ComponentAnalyzer.ts
|
|
4092
4507
|
var import_fs5 = require("fs");
|
|
4093
4508
|
var parser3 = __toESM(require("@babel/parser"));
|
|
4094
|
-
var
|
|
4095
|
-
var
|
|
4509
|
+
var import_traverse7 = __toESM(require("@babel/traverse"));
|
|
4510
|
+
var t9 = __toESM(require("@babel/types"));
|
|
4096
4511
|
var ComponentAnalyzer = class {
|
|
4097
4512
|
config;
|
|
4098
4513
|
constructor(config) {
|
|
@@ -4107,7 +4522,7 @@ var ComponentAnalyzer = class {
|
|
|
4107
4522
|
plugins: ["jsx", "typescript", ["decorators", { decoratorsBeforeExport: true }]]
|
|
4108
4523
|
});
|
|
4109
4524
|
const components = [];
|
|
4110
|
-
(0,
|
|
4525
|
+
(0, import_traverse7.default)(ast, {
|
|
4111
4526
|
JSXElement: (path13) => {
|
|
4112
4527
|
const component = this.extractComponentFromJSXElement(path13.node);
|
|
4113
4528
|
if (component) {
|
|
@@ -4138,19 +4553,19 @@ var ComponentAnalyzer = class {
|
|
|
4138
4553
|
};
|
|
4139
4554
|
}
|
|
4140
4555
|
getElementName(openingElement) {
|
|
4141
|
-
if (
|
|
4556
|
+
if (t9.isJSXIdentifier(openingElement.name)) {
|
|
4142
4557
|
return openingElement.name.name;
|
|
4143
4558
|
}
|
|
4144
|
-
if (
|
|
4559
|
+
if (t9.isJSXMemberExpression(openingElement.name)) {
|
|
4145
4560
|
const parts = [];
|
|
4146
4561
|
let current = openingElement.name;
|
|
4147
|
-
while (
|
|
4148
|
-
if (
|
|
4562
|
+
while (t9.isJSXMemberExpression(current)) {
|
|
4563
|
+
if (t9.isJSXIdentifier(current.property)) {
|
|
4149
4564
|
parts.unshift(current.property.name);
|
|
4150
4565
|
}
|
|
4151
4566
|
current = current.object;
|
|
4152
4567
|
}
|
|
4153
|
-
if (
|
|
4568
|
+
if (t9.isJSXIdentifier(current)) {
|
|
4154
4569
|
parts.unshift(current.name);
|
|
4155
4570
|
}
|
|
4156
4571
|
return parts.join(".");
|
|
@@ -4173,13 +4588,13 @@ var ComponentAnalyzer = class {
|
|
|
4173
4588
|
extractProps(openingElement) {
|
|
4174
4589
|
const props = {};
|
|
4175
4590
|
for (const attr of openingElement.attributes) {
|
|
4176
|
-
if (
|
|
4591
|
+
if (t9.isJSXAttribute(attr) && t9.isJSXIdentifier(attr.name)) {
|
|
4177
4592
|
const propName = attr.name.name;
|
|
4178
4593
|
if (attr.value === null) {
|
|
4179
4594
|
props[propName] = "true";
|
|
4180
|
-
} else if (
|
|
4595
|
+
} else if (t9.isStringLiteral(attr.value)) {
|
|
4181
4596
|
props[propName] = attr.value.value;
|
|
4182
|
-
} else if (
|
|
4597
|
+
} else if (t9.isJSXExpressionContainer(attr.value) && t9.isStringLiteral(attr.value.expression)) {
|
|
4183
4598
|
props[propName] = attr.value.expression.value;
|
|
4184
4599
|
}
|
|
4185
4600
|
}
|
|
@@ -4189,12 +4604,12 @@ var ComponentAnalyzer = class {
|
|
|
4189
4604
|
extractAccessibilityProps(openingElement) {
|
|
4190
4605
|
const result = {};
|
|
4191
4606
|
for (const attr of openingElement.attributes) {
|
|
4192
|
-
if (
|
|
4607
|
+
if (t9.isJSXAttribute(attr) && t9.isJSXIdentifier(attr.name)) {
|
|
4193
4608
|
const propName = attr.name.name;
|
|
4194
4609
|
if ((propName === "testID" || propName === "accessibilityLabel") && attr.value) {
|
|
4195
|
-
if (
|
|
4610
|
+
if (t9.isStringLiteral(attr.value)) {
|
|
4196
4611
|
result[propName] = attr.value.value;
|
|
4197
|
-
} else if (
|
|
4612
|
+
} else if (t9.isJSXExpressionContainer(attr.value) && t9.isStringLiteral(attr.value.expression)) {
|
|
4198
4613
|
result[propName] = attr.value.expression.value;
|
|
4199
4614
|
}
|
|
4200
4615
|
}
|
|
@@ -4206,7 +4621,7 @@ var ComponentAnalyzer = class {
|
|
|
4206
4621
|
if (depth >= maxDepth) return [];
|
|
4207
4622
|
const extracted = [];
|
|
4208
4623
|
for (const child of children) {
|
|
4209
|
-
if (
|
|
4624
|
+
if (t9.isJSXElement(child)) {
|
|
4210
4625
|
const component = this.extractComponentFromJSXElement(child);
|
|
4211
4626
|
if (component) {
|
|
4212
4627
|
extracted.push(component);
|
|
@@ -4220,8 +4635,8 @@ var ComponentAnalyzer = class {
|
|
|
4220
4635
|
// src/analyzers/FormAnalyzer.ts
|
|
4221
4636
|
var import_fs6 = require("fs");
|
|
4222
4637
|
var parser4 = __toESM(require("@babel/parser"));
|
|
4223
|
-
var
|
|
4224
|
-
var
|
|
4638
|
+
var import_traverse8 = __toESM(require("@babel/traverse"));
|
|
4639
|
+
var t10 = __toESM(require("@babel/types"));
|
|
4225
4640
|
var path5 = __toESM(require("path"));
|
|
4226
4641
|
var FormAnalyzer = class {
|
|
4227
4642
|
config;
|
|
@@ -4242,12 +4657,12 @@ var FormAnalyzer = class {
|
|
|
4242
4657
|
this.stateVariables.clear();
|
|
4243
4658
|
this.inputElements = [];
|
|
4244
4659
|
this.submitButtons = [];
|
|
4245
|
-
(0,
|
|
4660
|
+
(0, import_traverse8.default)(ast, {
|
|
4246
4661
|
CallExpression: (path13) => {
|
|
4247
4662
|
this.extractStateVariables(path13.node);
|
|
4248
4663
|
}
|
|
4249
4664
|
});
|
|
4250
|
-
(0,
|
|
4665
|
+
(0, import_traverse8.default)(ast, {
|
|
4251
4666
|
JSXElement: (path13) => {
|
|
4252
4667
|
this.extractFormElements(path13.node);
|
|
4253
4668
|
}
|
|
@@ -4260,7 +4675,7 @@ var FormAnalyzer = class {
|
|
|
4260
4675
|
}
|
|
4261
4676
|
}
|
|
4262
4677
|
extractStateVariables(node) {
|
|
4263
|
-
if (
|
|
4678
|
+
if (t10.isIdentifier(node.callee) && node.callee.name === "useState" && node.arguments.length > 0) {
|
|
4264
4679
|
return;
|
|
4265
4680
|
}
|
|
4266
4681
|
}
|
|
@@ -4281,28 +4696,28 @@ var FormAnalyzer = class {
|
|
|
4281
4696
|
extractInputInfo(openingElement) {
|
|
4282
4697
|
const info2 = { varName: "" };
|
|
4283
4698
|
for (const attr of openingElement.attributes) {
|
|
4284
|
-
if (
|
|
4699
|
+
if (t10.isJSXAttribute(attr) && t10.isJSXIdentifier(attr.name)) {
|
|
4285
4700
|
const propName = attr.name.name;
|
|
4286
|
-
const
|
|
4701
|
+
const propValue2 = this.extractAttributeValue(attr.value);
|
|
4287
4702
|
switch (propName) {
|
|
4288
4703
|
case "label":
|
|
4289
|
-
info2.label =
|
|
4704
|
+
info2.label = propValue2;
|
|
4290
4705
|
break;
|
|
4291
4706
|
case "placeholder":
|
|
4292
|
-
info2.placeholder =
|
|
4707
|
+
info2.placeholder = propValue2;
|
|
4293
4708
|
break;
|
|
4294
4709
|
case "keyboardType":
|
|
4295
|
-
info2.keyboardType =
|
|
4710
|
+
info2.keyboardType = propValue2;
|
|
4296
4711
|
break;
|
|
4297
4712
|
case "testID":
|
|
4298
|
-
info2.testID =
|
|
4713
|
+
info2.testID = propValue2;
|
|
4299
4714
|
break;
|
|
4300
4715
|
case "appilotsId":
|
|
4301
|
-
info2.appilotsId =
|
|
4302
|
-
if (
|
|
4716
|
+
info2.appilotsId = propValue2;
|
|
4717
|
+
if (propValue2) info2.varName = propValue2;
|
|
4303
4718
|
break;
|
|
4304
4719
|
case "value":
|
|
4305
|
-
if (attr.value &&
|
|
4720
|
+
if (attr.value && t10.isJSXExpressionContainer(attr.value) && t10.isIdentifier(attr.value.expression)) {
|
|
4306
4721
|
info2.varName = attr.value.expression.name;
|
|
4307
4722
|
}
|
|
4308
4723
|
break;
|
|
@@ -4314,16 +4729,16 @@ var FormAnalyzer = class {
|
|
|
4314
4729
|
extractButtonInfo(openingElement) {
|
|
4315
4730
|
const info2 = {};
|
|
4316
4731
|
for (const attr of openingElement.attributes) {
|
|
4317
|
-
if (
|
|
4732
|
+
if (t10.isJSXAttribute(attr) && t10.isJSXIdentifier(attr.name)) {
|
|
4318
4733
|
const propName = attr.name.name;
|
|
4319
4734
|
if (propName === "title" || propName === "label") {
|
|
4320
4735
|
info2.label = this.extractAttributeValue(attr.value);
|
|
4321
4736
|
}
|
|
4322
4737
|
if (propName === "onPress") {
|
|
4323
|
-
if (attr.value &&
|
|
4324
|
-
if (
|
|
4738
|
+
if (attr.value && t10.isJSXExpressionContainer(attr.value)) {
|
|
4739
|
+
if (t10.isIdentifier(attr.value.expression)) {
|
|
4325
4740
|
info2.handler = attr.value.expression.name;
|
|
4326
|
-
} else if (
|
|
4741
|
+
} else if (t10.isArrowFunctionExpression(attr.value.expression) || t10.isFunctionExpression(attr.value.expression)) {
|
|
4327
4742
|
info2.handler = "anonymous";
|
|
4328
4743
|
}
|
|
4329
4744
|
}
|
|
@@ -4334,21 +4749,21 @@ var FormAnalyzer = class {
|
|
|
4334
4749
|
}
|
|
4335
4750
|
extractAttributeValue(value) {
|
|
4336
4751
|
if (value === null) return void 0;
|
|
4337
|
-
if (
|
|
4338
|
-
if (
|
|
4752
|
+
if (t10.isStringLiteral(value)) return value.value;
|
|
4753
|
+
if (t10.isJSXExpressionContainer(value) && t10.isStringLiteral(value.expression)) {
|
|
4339
4754
|
return value.expression.value;
|
|
4340
4755
|
}
|
|
4341
4756
|
return void 0;
|
|
4342
4757
|
}
|
|
4343
4758
|
getElementName(openingElement) {
|
|
4344
|
-
if (
|
|
4759
|
+
if (t10.isJSXIdentifier(openingElement.name)) {
|
|
4345
4760
|
return openingElement.name.name;
|
|
4346
4761
|
}
|
|
4347
4762
|
return null;
|
|
4348
4763
|
}
|
|
4349
4764
|
extractValidationRules(ast) {
|
|
4350
4765
|
const rules = {};
|
|
4351
|
-
(0,
|
|
4766
|
+
(0, import_traverse8.default)(ast, {
|
|
4352
4767
|
IfStatement: (path13) => {
|
|
4353
4768
|
const test = path13.node.test;
|
|
4354
4769
|
const rule = this.extractRuleFromCondition(test);
|
|
@@ -4365,36 +4780,36 @@ var FormAnalyzer = class {
|
|
|
4365
4780
|
extractRuleFromCondition(test) {
|
|
4366
4781
|
let fieldName = "";
|
|
4367
4782
|
let description = "";
|
|
4368
|
-
if (
|
|
4369
|
-
if (
|
|
4783
|
+
if (t10.isUnaryExpression(test) && test.operator === "!") {
|
|
4784
|
+
if (t10.isCallExpression(test.argument)) {
|
|
4370
4785
|
const callExpr = test.argument;
|
|
4371
|
-
if (
|
|
4786
|
+
if (t10.isMemberExpression(callExpr.callee)) {
|
|
4372
4787
|
const memberExpr = callExpr.callee;
|
|
4373
|
-
if (
|
|
4788
|
+
if (t10.isIdentifier(memberExpr.object)) {
|
|
4374
4789
|
fieldName = memberExpr.object.name;
|
|
4375
4790
|
description = `${fieldName} is required`;
|
|
4376
4791
|
}
|
|
4377
4792
|
}
|
|
4378
|
-
} else if (
|
|
4793
|
+
} else if (t10.isIdentifier(test.argument)) {
|
|
4379
4794
|
fieldName = test.argument.name;
|
|
4380
4795
|
description = `${fieldName} is required`;
|
|
4381
4796
|
}
|
|
4382
4797
|
}
|
|
4383
|
-
if (
|
|
4384
|
-
if (
|
|
4798
|
+
if (t10.isBinaryExpression(test) && (test.operator === "<" || test.operator === "<=")) {
|
|
4799
|
+
if (t10.isMemberExpression(test.left)) {
|
|
4385
4800
|
const memberExpr = test.left;
|
|
4386
|
-
if (
|
|
4801
|
+
if (t10.isIdentifier(memberExpr.object)) {
|
|
4387
4802
|
fieldName = memberExpr.object.name;
|
|
4388
|
-
if (
|
|
4803
|
+
if (t10.isNumericLiteral(test.right)) {
|
|
4389
4804
|
description = `${fieldName} must be at least ${test.right.value} characters`;
|
|
4390
4805
|
}
|
|
4391
4806
|
}
|
|
4392
4807
|
}
|
|
4393
4808
|
}
|
|
4394
|
-
if (
|
|
4395
|
-
if (
|
|
4396
|
-
const methodName =
|
|
4397
|
-
if (methodName === "includes" &&
|
|
4809
|
+
if (t10.isUnaryExpression(test) && test.operator === "!") {
|
|
4810
|
+
if (t10.isCallExpression(test.argument) && t10.isMemberExpression(test.argument.callee)) {
|
|
4811
|
+
const methodName = t10.isIdentifier(test.argument.callee.property) ? test.argument.callee.property.name : null;
|
|
4812
|
+
if (methodName === "includes" && t10.isIdentifier(test.argument.callee.object)) {
|
|
4398
4813
|
fieldName = test.argument.callee.object.name;
|
|
4399
4814
|
description = `${fieldName} format is invalid`;
|
|
4400
4815
|
}
|
|
@@ -4502,7 +4917,7 @@ async function composeAffordances(options) {
|
|
|
4502
4917
|
const exclusive = children.filter((c) => (fanIn.get(c) ?? 0) <= MAX_FAN_IN);
|
|
4503
4918
|
if (exclusive.length === 0) continue;
|
|
4504
4919
|
const actionIds = new Set(screen.actions.map((a) => a.id));
|
|
4505
|
-
const targetIds = new Set((screen.targets ?? []).map((
|
|
4920
|
+
const targetIds = new Set((screen.targets ?? []).map((t18) => t18.id));
|
|
4506
4921
|
const formIds = new Set(screen.forms.map((f) => f.id));
|
|
4507
4922
|
let gained = false;
|
|
4508
4923
|
for (const file2 of exclusive) {
|
|
@@ -4644,6 +5059,7 @@ var ReactNativePlatformAnalyzer = class {
|
|
|
4644
5059
|
});
|
|
4645
5060
|
return {
|
|
4646
5061
|
screens: enrichedScreens,
|
|
5062
|
+
controlEvidenceFiles: screenAnalyzer.controlEvidenceFiles,
|
|
4647
5063
|
navigation,
|
|
4648
5064
|
analyzedFiles: screenFiles.length,
|
|
4649
5065
|
...screenAnalyzer.screensFilteredOut > 0 ? { screensFilteredOut: screenAnalyzer.screensFilteredOut } : {},
|
|
@@ -4732,8 +5148,8 @@ var GenericPlatformAnalyzer = class {
|
|
|
4732
5148
|
// src/analyzers/web/WebScreenAnalyzer.ts
|
|
4733
5149
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
4734
5150
|
var import_path5 = __toESM(require("path"));
|
|
4735
|
-
var
|
|
4736
|
-
var
|
|
5151
|
+
var import_traverse10 = __toESM(require("@babel/traverse"));
|
|
5152
|
+
var t13 = __toESM(require("@babel/types"));
|
|
4737
5153
|
|
|
4738
5154
|
// src/ast/jsx/web/classify.ts
|
|
4739
5155
|
var VIEW_TAGS = /* @__PURE__ */ new Set([
|
|
@@ -4849,29 +5265,29 @@ function classifyWebJsxComponent(name, element) {
|
|
|
4849
5265
|
}
|
|
4850
5266
|
|
|
4851
5267
|
// src/ast/jsx/names.ts
|
|
4852
|
-
var
|
|
5268
|
+
var t11 = __toESM(require("@babel/types"));
|
|
4853
5269
|
function getJsxElementName(openingElement) {
|
|
4854
5270
|
return getJsxName(openingElement.name);
|
|
4855
5271
|
}
|
|
4856
5272
|
function getJsxName(name) {
|
|
4857
|
-
if (
|
|
4858
|
-
if (
|
|
4859
|
-
if (
|
|
5273
|
+
if (t11.isJSXIdentifier(name)) return name.name;
|
|
5274
|
+
if (t11.isJSXNamespacedName(name)) return `${name.namespace.name}:${name.name.name}`;
|
|
5275
|
+
if (t11.isJSXMemberExpression(name)) {
|
|
4860
5276
|
const objectName = getJsxName(name.object);
|
|
4861
|
-
const propertyName =
|
|
5277
|
+
const propertyName = t11.isJSXIdentifier(name.property) ? name.property.name : null;
|
|
4862
5278
|
return objectName && propertyName ? `${objectName}.${propertyName}` : null;
|
|
4863
5279
|
}
|
|
4864
5280
|
return null;
|
|
4865
5281
|
}
|
|
4866
5282
|
|
|
4867
5283
|
// src/ast/navigation/web-calls.ts
|
|
4868
|
-
var
|
|
4869
|
-
var
|
|
5284
|
+
var import_traverse9 = __toESM(require("@babel/traverse"));
|
|
5285
|
+
var t12 = __toESM(require("@babel/types"));
|
|
4870
5286
|
var ROUTERISH_OBJECTS = /^(router|history|navigation)$/;
|
|
4871
5287
|
function staticRoutePath(node) {
|
|
4872
5288
|
if (!node) return void 0;
|
|
4873
|
-
if (
|
|
4874
|
-
if (
|
|
5289
|
+
if (t12.isStringLiteral(node)) return node.value;
|
|
5290
|
+
if (t12.isTemplateLiteral(node)) {
|
|
4875
5291
|
let path13 = "";
|
|
4876
5292
|
node.quasis.forEach((quasi, index) => {
|
|
4877
5293
|
path13 += quasi.value.cooked ?? quasi.value.raw;
|
|
@@ -4883,15 +5299,15 @@ function staticRoutePath(node) {
|
|
|
4883
5299
|
return void 0;
|
|
4884
5300
|
}
|
|
4885
5301
|
function paramNameOf(expr) {
|
|
4886
|
-
if (
|
|
4887
|
-
if (
|
|
5302
|
+
if (t12.isIdentifier(expr)) return expr.name;
|
|
5303
|
+
if (t12.isMemberExpression(expr) && t12.isIdentifier(expr.property)) return expr.property.name;
|
|
4888
5304
|
return "param";
|
|
4889
5305
|
}
|
|
4890
5306
|
function extractWebNavigationCalls(ast) {
|
|
4891
5307
|
const calls = [];
|
|
4892
5308
|
const inspect = (node) => {
|
|
4893
|
-
if (
|
|
4894
|
-
if (
|
|
5309
|
+
if (t12.isCallExpression(node)) {
|
|
5310
|
+
if (t12.isIdentifier(node.callee) && node.callee.name === "navigate") {
|
|
4895
5311
|
const first = node.arguments[0];
|
|
4896
5312
|
const targetPath = staticRoutePath(first);
|
|
4897
5313
|
if (targetPath !== void 0) {
|
|
@@ -4899,12 +5315,12 @@ function extractWebNavigationCalls(ast) {
|
|
|
4899
5315
|
method: hasReplaceOption(node.arguments[1]) ? "replace" : "navigate",
|
|
4900
5316
|
targetPath
|
|
4901
5317
|
});
|
|
4902
|
-
} else if (
|
|
5318
|
+
} else if (t12.isNumericLiteral(first) || t12.isUnaryExpression(first) && first.operator === "-") {
|
|
4903
5319
|
calls.push({ method: "goBack" });
|
|
4904
5320
|
}
|
|
4905
5321
|
return;
|
|
4906
5322
|
}
|
|
4907
|
-
if (
|
|
5323
|
+
if (t12.isMemberExpression(node.callee) && t12.isIdentifier(node.callee.object) && ROUTERISH_OBJECTS.test(node.callee.object.name) && t12.isIdentifier(node.callee.property)) {
|
|
4908
5324
|
const method = node.callee.property.name;
|
|
4909
5325
|
const targetPath = staticRoutePath(node.arguments[0]);
|
|
4910
5326
|
if ((method === "push" || method === "navigate") && targetPath !== void 0) {
|
|
@@ -4917,25 +5333,25 @@ function extractWebNavigationCalls(ast) {
|
|
|
4917
5333
|
}
|
|
4918
5334
|
return;
|
|
4919
5335
|
}
|
|
4920
|
-
if (
|
|
5336
|
+
if (t12.isAssignmentExpression(node) && t12.isMemberExpression(node.left) && t12.isIdentifier(node.left.property) && node.left.property.name === "href" && isLocationExpression(node.left.object) && t12.isStringLiteral(node.right) && node.right.value.startsWith("/")) {
|
|
4921
5337
|
calls.push({ method: "navigate", targetPath: node.right.value });
|
|
4922
5338
|
}
|
|
4923
5339
|
};
|
|
4924
|
-
(0,
|
|
4925
|
-
noScope: !
|
|
5340
|
+
(0, import_traverse9.default)(ast, {
|
|
5341
|
+
noScope: !t12.isFile(ast),
|
|
4926
5342
|
enter: (nodePath) => inspect(nodePath.node)
|
|
4927
5343
|
});
|
|
4928
5344
|
return calls;
|
|
4929
5345
|
}
|
|
4930
5346
|
function hasReplaceOption(arg) {
|
|
4931
|
-
if (!arg || !
|
|
5347
|
+
if (!arg || !t12.isObjectExpression(arg)) return false;
|
|
4932
5348
|
return arg.properties.some(
|
|
4933
|
-
(prop) =>
|
|
5349
|
+
(prop) => t12.isObjectProperty(prop) && t12.isIdentifier(prop.key) && prop.key.name === "replace" && t12.isBooleanLiteral(prop.value) && prop.value.value === true
|
|
4934
5350
|
);
|
|
4935
5351
|
}
|
|
4936
5352
|
function isLocationExpression(node) {
|
|
4937
|
-
if (
|
|
4938
|
-
return
|
|
5353
|
+
if (t12.isIdentifier(node)) return node.name === "location";
|
|
5354
|
+
return t12.isMemberExpression(node) && t12.isIdentifier(node.object) && node.object.name === "window" && t12.isIdentifier(node.property) && node.property.name === "location";
|
|
4939
5355
|
}
|
|
4940
5356
|
|
|
4941
5357
|
// src/analyzers/web/WebScreenAnalyzer.ts
|
|
@@ -5043,7 +5459,7 @@ var WebScreenAnalyzer = class {
|
|
|
5043
5459
|
// ── registerScreen ────────────────────────────────────────────────
|
|
5044
5460
|
detectRegisterScreenCall(ast) {
|
|
5045
5461
|
let found = false;
|
|
5046
|
-
(0,
|
|
5462
|
+
(0, import_traverse10.default)(ast, {
|
|
5047
5463
|
CallExpression: (nodePath) => {
|
|
5048
5464
|
if (found) return;
|
|
5049
5465
|
if (isRegisterScreenCallee(nodePath.node.callee)) {
|
|
@@ -5056,11 +5472,11 @@ var WebScreenAnalyzer = class {
|
|
|
5056
5472
|
}
|
|
5057
5473
|
extractRegisterScreenMetadata(ast) {
|
|
5058
5474
|
let plain = null;
|
|
5059
|
-
(0,
|
|
5475
|
+
(0, import_traverse10.default)(ast, {
|
|
5060
5476
|
CallExpression: (nodePath) => {
|
|
5061
5477
|
if (!isRegisterScreenCallee(nodePath.node.callee)) return;
|
|
5062
5478
|
const arg = nodePath.node.arguments[0];
|
|
5063
|
-
if (
|
|
5479
|
+
if (t13.isObjectExpression(arg)) {
|
|
5064
5480
|
plain = literalToPlain(arg);
|
|
5065
5481
|
}
|
|
5066
5482
|
}
|
|
@@ -5104,23 +5520,23 @@ var WebScreenAnalyzer = class {
|
|
|
5104
5520
|
extractComponentName(ast) {
|
|
5105
5521
|
let defaultName = "";
|
|
5106
5522
|
let firstExported = "";
|
|
5107
|
-
(0,
|
|
5523
|
+
(0, import_traverse10.default)(ast, {
|
|
5108
5524
|
ExportDefaultDeclaration: (nodePath) => {
|
|
5109
5525
|
const declaration = nodePath.node.declaration;
|
|
5110
|
-
if (
|
|
5526
|
+
if (t13.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
5111
5527
|
defaultName = declaration.id.name;
|
|
5112
|
-
} else if (
|
|
5528
|
+
} else if (t13.isIdentifier(declaration)) {
|
|
5113
5529
|
defaultName = declaration.name;
|
|
5114
5530
|
}
|
|
5115
5531
|
},
|
|
5116
5532
|
ExportNamedDeclaration: (nodePath) => {
|
|
5117
5533
|
if (firstExported) return;
|
|
5118
5534
|
const declaration = nodePath.node.declaration;
|
|
5119
|
-
if (
|
|
5535
|
+
if (t13.isFunctionDeclaration(declaration) && declaration.id && /^[A-Z]/.test(declaration.id.name)) {
|
|
5120
5536
|
firstExported = declaration.id.name;
|
|
5121
|
-
} else if (
|
|
5537
|
+
} else if (t13.isVariableDeclaration(declaration)) {
|
|
5122
5538
|
for (const declarator of declaration.declarations) {
|
|
5123
|
-
if (
|
|
5539
|
+
if (t13.isIdentifier(declarator.id) && /^[A-Z]/.test(declarator.id.name) && (t13.isArrowFunctionExpression(declarator.init) || t13.isFunctionExpression(declarator.init))) {
|
|
5124
5540
|
firstExported = declarator.id.name;
|
|
5125
5541
|
break;
|
|
5126
5542
|
}
|
|
@@ -5133,7 +5549,7 @@ var WebScreenAnalyzer = class {
|
|
|
5133
5549
|
extractComponents(ast) {
|
|
5134
5550
|
const components = [];
|
|
5135
5551
|
const seen = /* @__PURE__ */ new Set();
|
|
5136
|
-
(0,
|
|
5552
|
+
(0, import_traverse10.default)(ast, {
|
|
5137
5553
|
JSXOpeningElement: (nodePath) => {
|
|
5138
5554
|
const element = nodePath.node;
|
|
5139
5555
|
const name = getJsxElementName(element);
|
|
@@ -5154,7 +5570,7 @@ var WebScreenAnalyzer = class {
|
|
|
5154
5570
|
// ── <label htmlFor> association ───────────────────────────────────
|
|
5155
5571
|
collectHtmlForLabels(ast) {
|
|
5156
5572
|
const labels = /* @__PURE__ */ new Map();
|
|
5157
|
-
(0,
|
|
5573
|
+
(0, import_traverse10.default)(ast, {
|
|
5158
5574
|
JSXElement: (nodePath) => {
|
|
5159
5575
|
const element = nodePath.node;
|
|
5160
5576
|
if (getJsxElementName(element.openingElement) !== "label") return;
|
|
@@ -5197,7 +5613,7 @@ var WebScreenAnalyzer = class {
|
|
|
5197
5613
|
formBuckets.set(formElement, bucket);
|
|
5198
5614
|
return bucket;
|
|
5199
5615
|
};
|
|
5200
|
-
(0,
|
|
5616
|
+
(0, import_traverse10.default)(ast, {
|
|
5201
5617
|
JSXElement: (nodePath) => {
|
|
5202
5618
|
const element = nodePath.node;
|
|
5203
5619
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -5214,7 +5630,7 @@ var WebScreenAnalyzer = class {
|
|
|
5214
5630
|
if (!bucket.fields.has(field.name)) bucket.fields.set(field.name, field);
|
|
5215
5631
|
}
|
|
5216
5632
|
});
|
|
5217
|
-
(0,
|
|
5633
|
+
(0, import_traverse10.default)(ast, {
|
|
5218
5634
|
JSXElement: (nodePath) => {
|
|
5219
5635
|
const element = nodePath.node;
|
|
5220
5636
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -5292,15 +5708,15 @@ var WebScreenAnalyzer = class {
|
|
|
5292
5708
|
});
|
|
5293
5709
|
}
|
|
5294
5710
|
for (const attr of opening.attributes) {
|
|
5295
|
-
if (!
|
|
5711
|
+
if (!t13.isJSXAttribute(attr) || !t13.isJSXIdentifier(attr.name)) continue;
|
|
5296
5712
|
const attrName = attr.name.name;
|
|
5297
5713
|
if (attrName === "required") {
|
|
5298
5714
|
if (attr.value === null) field.required = true;
|
|
5299
|
-
else if (
|
|
5715
|
+
else if (t13.isJSXExpressionContainer(attr.value) && t13.isBooleanLiteral(attr.value.expression)) {
|
|
5300
5716
|
field.required = attr.value.expression.value;
|
|
5301
5717
|
}
|
|
5302
5718
|
}
|
|
5303
|
-
if ((attrName === "value" || attrName === "checked") && attr.value &&
|
|
5719
|
+
if ((attrName === "value" || attrName === "checked") && attr.value && t13.isJSXExpressionContainer(attr.value) && t13.isIdentifier(attr.value.expression)) {
|
|
5304
5720
|
field.valueBinding = attr.value.expression.name;
|
|
5305
5721
|
if (!field.name) field.name = attr.value.expression.name;
|
|
5306
5722
|
}
|
|
@@ -5350,7 +5766,7 @@ var WebScreenAnalyzer = class {
|
|
|
5350
5766
|
extractSelectOptions(selectElement) {
|
|
5351
5767
|
const options = [];
|
|
5352
5768
|
for (const child of selectElement.children) {
|
|
5353
|
-
if (!
|
|
5769
|
+
if (!t13.isJSXElement(child)) continue;
|
|
5354
5770
|
if (getJsxElementName(child.openingElement) !== "option") continue;
|
|
5355
5771
|
const value = getStringAttr(child.openingElement, "value");
|
|
5356
5772
|
const label = jsxTextContent(child) || value || "";
|
|
@@ -5392,7 +5808,7 @@ var WebScreenAnalyzer = class {
|
|
|
5392
5808
|
const actionLabels = new Map(
|
|
5393
5809
|
actions.filter((a) => a.label).map((a) => [normalizeLabel(a.label), a])
|
|
5394
5810
|
);
|
|
5395
|
-
(0,
|
|
5811
|
+
(0, import_traverse10.default)(ast, {
|
|
5396
5812
|
JSXElement: (nodePath) => {
|
|
5397
5813
|
const element = nodePath.node;
|
|
5398
5814
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -5461,14 +5877,14 @@ var WebScreenAnalyzer = class {
|
|
|
5461
5877
|
}
|
|
5462
5878
|
let handlerName;
|
|
5463
5879
|
const onClickAttr = opening.attributes.find(
|
|
5464
|
-
(attr) =>
|
|
5880
|
+
(attr) => t13.isJSXAttribute(attr) && t13.isJSXIdentifier(attr.name) && attr.name.name === "onClick"
|
|
5465
5881
|
);
|
|
5466
|
-
if (onClickAttr?.value &&
|
|
5882
|
+
if (onClickAttr?.value && t13.isJSXExpressionContainer(onClickAttr.value)) {
|
|
5467
5883
|
const expr = onClickAttr.value.expression;
|
|
5468
|
-
if (
|
|
5884
|
+
if (t13.isIdentifier(expr)) {
|
|
5469
5885
|
handlerName = expr.name;
|
|
5470
5886
|
action.handler = expr.name;
|
|
5471
|
-
} else if (!
|
|
5887
|
+
} else if (!t13.isJSXEmptyExpression(expr)) {
|
|
5472
5888
|
const inlineNavCalls = extractWebNavigationCalls(expr);
|
|
5473
5889
|
const inlineNav = inlineNavCalls.find((call) => call.targetPath);
|
|
5474
5890
|
if (inlineNav?.targetPath) {
|
|
@@ -5607,12 +6023,12 @@ var WebScreenAnalyzer = class {
|
|
|
5607
6023
|
}
|
|
5608
6024
|
handlerNameFromAttr(opening, attrName) {
|
|
5609
6025
|
const attr = opening.attributes.find(
|
|
5610
|
-
(candidate) =>
|
|
6026
|
+
(candidate) => t13.isJSXAttribute(candidate) && t13.isJSXIdentifier(candidate.name) && candidate.name.name === attrName
|
|
5611
6027
|
);
|
|
5612
|
-
if (!attr?.value || !
|
|
6028
|
+
if (!attr?.value || !t13.isJSXExpressionContainer(attr.value)) return void 0;
|
|
5613
6029
|
const expr = attr.value.expression;
|
|
5614
|
-
if (
|
|
5615
|
-
if (
|
|
6030
|
+
if (t13.isIdentifier(expr)) return expr.name;
|
|
6031
|
+
if (t13.isArrowFunctionExpression(expr) || t13.isFunctionExpression(expr)) {
|
|
5616
6032
|
return firstCalledFunctionName(expr) ?? "anonymous";
|
|
5617
6033
|
}
|
|
5618
6034
|
return void 0;
|
|
@@ -5623,7 +6039,7 @@ var WebScreenAnalyzer = class {
|
|
|
5623
6039
|
for (const call of extractWebNavigationCalls(ast)) {
|
|
5624
6040
|
if (call.targetPath && call.targetPath.startsWith("/")) targets.add(call.targetPath);
|
|
5625
6041
|
}
|
|
5626
|
-
(0,
|
|
6042
|
+
(0, import_traverse10.default)(ast, {
|
|
5627
6043
|
JSXOpeningElement: (nodePath) => {
|
|
5628
6044
|
const element = nodePath.node;
|
|
5629
6045
|
const name = getJsxElementName(element);
|
|
@@ -5643,14 +6059,14 @@ var WebScreenAnalyzer = class {
|
|
|
5643
6059
|
extractCollections(ast) {
|
|
5644
6060
|
const collections = [];
|
|
5645
6061
|
const seen = /* @__PURE__ */ new Set();
|
|
5646
|
-
(0,
|
|
6062
|
+
(0, import_traverse10.default)(ast, {
|
|
5647
6063
|
JSXExpressionContainer: (nodePath) => {
|
|
5648
6064
|
const expr = nodePath.node.expression;
|
|
5649
|
-
if (!
|
|
6065
|
+
if (!t13.isCallExpression(expr) || !t13.isMemberExpression(expr.callee) || !t13.isIdentifier(expr.callee.object) || !t13.isIdentifier(expr.callee.property) || expr.callee.property.name !== "map") {
|
|
5650
6066
|
return;
|
|
5651
6067
|
}
|
|
5652
6068
|
const callback = expr.arguments[0];
|
|
5653
|
-
if (!
|
|
6069
|
+
if (!t13.isArrowFunctionExpression(callback) && !t13.isFunctionExpression(callback)) return;
|
|
5654
6070
|
const enclosing = nodePath.findParent(
|
|
5655
6071
|
(p) => p.isJSXElement()
|
|
5656
6072
|
);
|
|
@@ -5685,22 +6101,22 @@ var WebScreenAnalyzer = class {
|
|
|
5685
6101
|
}
|
|
5686
6102
|
};
|
|
5687
6103
|
function isRegisterScreenCallee(callee) {
|
|
5688
|
-
return
|
|
6104
|
+
return t13.isIdentifier(callee) && callee.name === "registerScreen" || t13.isMemberExpression(callee) && t13.isIdentifier(callee.property) && callee.property.name === "registerScreen";
|
|
5689
6105
|
}
|
|
5690
6106
|
function literalToPlain(node) {
|
|
5691
|
-
if (
|
|
6107
|
+
if (t13.isStringLiteral(node) || t13.isNumericLiteral(node) || t13.isBooleanLiteral(node)) {
|
|
5692
6108
|
return node.value;
|
|
5693
6109
|
}
|
|
5694
|
-
if (
|
|
5695
|
-
if (
|
|
5696
|
-
return node.elements.filter((el) => el !== null &&
|
|
6110
|
+
if (t13.isNullLiteral(node)) return null;
|
|
6111
|
+
if (t13.isArrayExpression(node)) {
|
|
6112
|
+
return node.elements.filter((el) => el !== null && t13.isExpression(el)).map((el) => literalToPlain(el)).filter((value) => value !== void 0);
|
|
5697
6113
|
}
|
|
5698
|
-
if (
|
|
6114
|
+
if (t13.isObjectExpression(node)) {
|
|
5699
6115
|
const out = {};
|
|
5700
6116
|
for (const prop of node.properties) {
|
|
5701
|
-
if (!
|
|
5702
|
-
const key =
|
|
5703
|
-
if (!key || !
|
|
6117
|
+
if (!t13.isObjectProperty(prop)) continue;
|
|
6118
|
+
const key = t13.isIdentifier(prop.key) ? prop.key.name : t13.isStringLiteral(prop.key) ? prop.key.value : void 0;
|
|
6119
|
+
if (!key || !t13.isExpression(prop.value)) continue;
|
|
5704
6120
|
const value = literalToPlain(prop.value);
|
|
5705
6121
|
if (value !== void 0) out[key] = value;
|
|
5706
6122
|
}
|
|
@@ -5712,12 +6128,12 @@ function jsxTextContent(element) {
|
|
|
5712
6128
|
const parts = [];
|
|
5713
6129
|
const walk = (children) => {
|
|
5714
6130
|
for (const child of children) {
|
|
5715
|
-
if (
|
|
6131
|
+
if (t13.isJSXText(child)) {
|
|
5716
6132
|
const trimmed = child.value.replace(/\s+/g, " ").trim();
|
|
5717
6133
|
if (trimmed) parts.push(trimmed);
|
|
5718
|
-
} else if (
|
|
6134
|
+
} else if (t13.isJSXExpressionContainer(child) && t13.isStringLiteral(child.expression)) {
|
|
5719
6135
|
parts.push(child.expression.value);
|
|
5720
|
-
} else if (
|
|
6136
|
+
} else if (t13.isJSXElement(child)) {
|
|
5721
6137
|
walk(child.children);
|
|
5722
6138
|
}
|
|
5723
6139
|
}
|
|
@@ -5727,26 +6143,26 @@ function jsxTextContent(element) {
|
|
|
5727
6143
|
return text.length > 0 ? text : void 0;
|
|
5728
6144
|
}
|
|
5729
6145
|
function firstCalledFunctionName(node) {
|
|
5730
|
-
if (
|
|
5731
|
-
if (
|
|
6146
|
+
if (t13.isIdentifier(node)) return node.name;
|
|
6147
|
+
if (t13.isArrowFunctionExpression(node) || t13.isFunctionExpression(node)) {
|
|
5732
6148
|
return firstCalledFunctionName(node.body);
|
|
5733
6149
|
}
|
|
5734
|
-
if (
|
|
6150
|
+
if (t13.isBlockStatement(node)) {
|
|
5735
6151
|
for (const statement of node.body) {
|
|
5736
6152
|
const handler = firstCalledFunctionName(statement);
|
|
5737
6153
|
if (handler) return handler;
|
|
5738
6154
|
}
|
|
5739
6155
|
return void 0;
|
|
5740
6156
|
}
|
|
5741
|
-
if (
|
|
5742
|
-
if (
|
|
6157
|
+
if (t13.isExpressionStatement(node)) return firstCalledFunctionName(node.expression);
|
|
6158
|
+
if (t13.isReturnStatement(node)) {
|
|
5743
6159
|
return node.argument ? firstCalledFunctionName(node.argument) : void 0;
|
|
5744
6160
|
}
|
|
5745
|
-
if (
|
|
6161
|
+
if (t13.isAwaitExpression(node) || t13.isUnaryExpression(node)) {
|
|
5746
6162
|
return firstCalledFunctionName(node.argument);
|
|
5747
6163
|
}
|
|
5748
|
-
if (
|
|
5749
|
-
if (
|
|
6164
|
+
if (t13.isCallExpression(node)) {
|
|
6165
|
+
if (t13.isIdentifier(node.callee) && !/^(navigate|confirm|alert)$/.test(node.callee.name)) {
|
|
5750
6166
|
return node.callee.name;
|
|
5751
6167
|
}
|
|
5752
6168
|
return void 0;
|
|
@@ -5755,14 +6171,14 @@ function firstCalledFunctionName(node) {
|
|
|
5755
6171
|
}
|
|
5756
6172
|
function containsWindowConfirm(body) {
|
|
5757
6173
|
let found = false;
|
|
5758
|
-
(0,
|
|
6174
|
+
(0, import_traverse10.default)(
|
|
5759
6175
|
body,
|
|
5760
6176
|
{
|
|
5761
6177
|
noScope: true,
|
|
5762
6178
|
CallExpression: (nodePath) => {
|
|
5763
6179
|
const callee = nodePath.node.callee;
|
|
5764
|
-
if (
|
|
5765
|
-
if (
|
|
6180
|
+
if (t13.isIdentifier(callee) && callee.name === "confirm") found = true;
|
|
6181
|
+
if (t13.isMemberExpression(callee) && t13.isIdentifier(callee.object) && callee.object.name === "window" && t13.isIdentifier(callee.property) && callee.property.name === "confirm") {
|
|
5766
6182
|
found = true;
|
|
5767
6183
|
}
|
|
5768
6184
|
}
|
|
@@ -5777,9 +6193,9 @@ function routePathAttr(opening, attrName) {
|
|
|
5777
6193
|
const literal = getStringAttr(opening, attrName);
|
|
5778
6194
|
if (literal) return literal;
|
|
5779
6195
|
const attr = opening.attributes.find(
|
|
5780
|
-
(candidate) =>
|
|
6196
|
+
(candidate) => t13.isJSXAttribute(candidate) && t13.isJSXIdentifier(candidate.name) && candidate.name.name === attrName
|
|
5781
6197
|
);
|
|
5782
|
-
if (!attr?.value || !
|
|
6198
|
+
if (!attr?.value || !t13.isJSXExpressionContainer(attr.value)) return void 0;
|
|
5783
6199
|
return staticRoutePath(attr.value.expression);
|
|
5784
6200
|
}
|
|
5785
6201
|
function slugify(label) {
|
|
@@ -5794,10 +6210,10 @@ function isWeakInferredFieldName(name) {
|
|
|
5794
6210
|
function collectionItemNames(callback) {
|
|
5795
6211
|
const names = /* @__PURE__ */ new Set(["item"]);
|
|
5796
6212
|
const firstParam = callback.params[0];
|
|
5797
|
-
if (
|
|
5798
|
-
if (
|
|
6213
|
+
if (t13.isIdentifier(firstParam)) names.add(firstParam.name);
|
|
6214
|
+
if (t13.isObjectPattern(firstParam)) {
|
|
5799
6215
|
for (const prop of firstParam.properties) {
|
|
5800
|
-
if (
|
|
6216
|
+
if (t13.isObjectProperty(prop) && t13.isIdentifier(prop.key) && t13.isIdentifier(prop.value)) {
|
|
5801
6217
|
names.add(prop.value.name);
|
|
5802
6218
|
}
|
|
5803
6219
|
}
|
|
@@ -5807,13 +6223,13 @@ function collectionItemNames(callback) {
|
|
|
5807
6223
|
function collectionDisplayFields(callback, itemNames) {
|
|
5808
6224
|
const fields = /* @__PURE__ */ new Set();
|
|
5809
6225
|
if (!callback.body) return [];
|
|
5810
|
-
(0,
|
|
6226
|
+
(0, import_traverse10.default)(
|
|
5811
6227
|
callback.body,
|
|
5812
6228
|
{
|
|
5813
6229
|
noScope: true,
|
|
5814
6230
|
MemberExpression: (nodePath) => {
|
|
5815
6231
|
const node = nodePath.node;
|
|
5816
|
-
if (
|
|
6232
|
+
if (t13.isIdentifier(node.object) && itemNames.has(node.object.name) && t13.isIdentifier(node.property)) {
|
|
5817
6233
|
fields.add(node.property.name);
|
|
5818
6234
|
}
|
|
5819
6235
|
}
|
|
@@ -5824,16 +6240,16 @@ function collectionDisplayFields(callback, itemNames) {
|
|
|
5824
6240
|
function collectionKeyField(callback, itemNames) {
|
|
5825
6241
|
let keyField;
|
|
5826
6242
|
if (!callback.body) return void 0;
|
|
5827
|
-
(0,
|
|
6243
|
+
(0, import_traverse10.default)(
|
|
5828
6244
|
callback.body,
|
|
5829
6245
|
{
|
|
5830
6246
|
noScope: true,
|
|
5831
6247
|
JSXAttribute: (nodePath) => {
|
|
5832
6248
|
const attr = nodePath.node;
|
|
5833
|
-
if (!
|
|
5834
|
-
if (!attr.value || !
|
|
6249
|
+
if (!t13.isJSXIdentifier(attr.name) || attr.name.name !== "key") return;
|
|
6250
|
+
if (!attr.value || !t13.isJSXExpressionContainer(attr.value)) return;
|
|
5835
6251
|
const expr = attr.value.expression;
|
|
5836
|
-
if (
|
|
6252
|
+
if (t13.isMemberExpression(expr) && t13.isIdentifier(expr.object) && itemNames.has(expr.object.name) && t13.isIdentifier(expr.property)) {
|
|
5837
6253
|
keyField = keyField ?? expr.property.name;
|
|
5838
6254
|
}
|
|
5839
6255
|
}
|
|
@@ -5856,18 +6272,18 @@ function callbackReturnsTag(callback, tags) {
|
|
|
5856
6272
|
let found = false;
|
|
5857
6273
|
const inspect = (node) => {
|
|
5858
6274
|
if (!node || found) return;
|
|
5859
|
-
if (
|
|
6275
|
+
if (t13.isJSXElement(node)) {
|
|
5860
6276
|
const name = getJsxElementName(node.openingElement);
|
|
5861
6277
|
if (name && tags.has(name)) found = true;
|
|
5862
6278
|
return;
|
|
5863
6279
|
}
|
|
5864
|
-
if (
|
|
6280
|
+
if (t13.isBlockStatement(node)) {
|
|
5865
6281
|
for (const statement of node.body) {
|
|
5866
|
-
if (
|
|
6282
|
+
if (t13.isReturnStatement(statement)) inspect(statement.argument);
|
|
5867
6283
|
}
|
|
5868
6284
|
}
|
|
5869
|
-
if (
|
|
5870
|
-
if (
|
|
6285
|
+
if (t13.isParenthesizedExpression(node)) inspect(node.expression);
|
|
6286
|
+
if (t13.isConditionalExpression(node)) {
|
|
5871
6287
|
inspect(node.consequent);
|
|
5872
6288
|
inspect(node.alternate);
|
|
5873
6289
|
}
|
|
@@ -5894,8 +6310,8 @@ function inferIdentityFields(keyField, displayFields) {
|
|
|
5894
6310
|
// src/analyzers/web/WebNavigationAnalyzer.ts
|
|
5895
6311
|
var import_fs7 = require("fs");
|
|
5896
6312
|
var import_path6 = __toESM(require("path"));
|
|
5897
|
-
var
|
|
5898
|
-
var
|
|
6313
|
+
var import_traverse11 = __toESM(require("@babel/traverse"));
|
|
6314
|
+
var t14 = __toESM(require("@babel/types"));
|
|
5899
6315
|
var WEB_NAVIGATOR_TYPE = "route";
|
|
5900
6316
|
var WebNavigationAnalyzer = class {
|
|
5901
6317
|
config;
|
|
@@ -5955,7 +6371,7 @@ var WebNavigationAnalyzer = class {
|
|
|
5955
6371
|
const name = getJsxElementName(opening);
|
|
5956
6372
|
if (name !== "Route") {
|
|
5957
6373
|
for (const child of element.children) {
|
|
5958
|
-
if (
|
|
6374
|
+
if (t14.isJSXElement(child)) visitRoute(child, parentPath);
|
|
5959
6375
|
}
|
|
5960
6376
|
return;
|
|
5961
6377
|
}
|
|
@@ -5964,16 +6380,16 @@ var WebNavigationAnalyzer = class {
|
|
|
5964
6380
|
const fullPath = this.joinPaths(parentPath, segment, isIndex);
|
|
5965
6381
|
const componentName = this.componentNameFromElementAttr(opening) ?? void 0;
|
|
5966
6382
|
const isLeaf = !element.children.some(
|
|
5967
|
-
(child) =>
|
|
6383
|
+
(child) => t14.isJSXElement(child) && getJsxElementName(child.openingElement) === "Route"
|
|
5968
6384
|
);
|
|
5969
6385
|
if ((segment || isIndex) && (componentName || isLeaf)) {
|
|
5970
6386
|
routes.push(this.buildRoute(fullPath, componentName, isIndex, !isLeaf));
|
|
5971
6387
|
}
|
|
5972
6388
|
for (const child of element.children) {
|
|
5973
|
-
if (
|
|
6389
|
+
if (t14.isJSXElement(child)) visitRoute(child, fullPath);
|
|
5974
6390
|
}
|
|
5975
6391
|
};
|
|
5976
|
-
(0,
|
|
6392
|
+
(0, import_traverse11.default)(ast, {
|
|
5977
6393
|
JSXElement: (nodePath) => {
|
|
5978
6394
|
const name = getJsxElementName(nodePath.node.openingElement);
|
|
5979
6395
|
if (name !== "Routes" && name !== "Route") return;
|
|
@@ -5992,13 +6408,13 @@ var WebNavigationAnalyzer = class {
|
|
|
5992
6408
|
/** `element={<VehicleList/>}` or `Component={VehicleList}`. */
|
|
5993
6409
|
componentNameFromElementAttr(opening) {
|
|
5994
6410
|
for (const attr of opening.attributes) {
|
|
5995
|
-
if (!
|
|
5996
|
-
if (attr.name.name === "element" &&
|
|
6411
|
+
if (!t14.isJSXAttribute(attr) || !t14.isJSXIdentifier(attr.name)) continue;
|
|
6412
|
+
if (attr.name.name === "element" && t14.isJSXExpressionContainer(attr.value)) {
|
|
5997
6413
|
const expr = attr.value.expression;
|
|
5998
|
-
if (
|
|
6414
|
+
if (t14.isJSXElement(expr)) return getJsxElementName(expr.openingElement);
|
|
5999
6415
|
}
|
|
6000
|
-
if (attr.name.name === "Component" &&
|
|
6001
|
-
if (
|
|
6416
|
+
if (attr.name.name === "Component" && t14.isJSXExpressionContainer(attr.value)) {
|
|
6417
|
+
if (t14.isIdentifier(attr.value.expression)) return attr.value.expression.name;
|
|
6002
6418
|
}
|
|
6003
6419
|
}
|
|
6004
6420
|
return null;
|
|
@@ -6012,12 +6428,12 @@ var WebNavigationAnalyzer = class {
|
|
|
6012
6428
|
"createMemoryRouter",
|
|
6013
6429
|
"useRoutes"
|
|
6014
6430
|
]);
|
|
6015
|
-
(0,
|
|
6431
|
+
(0, import_traverse11.default)(ast, {
|
|
6016
6432
|
CallExpression: (nodePath) => {
|
|
6017
6433
|
const callee = nodePath.node.callee;
|
|
6018
|
-
if (!
|
|
6434
|
+
if (!t14.isIdentifier(callee) || !ROUTER_FACTORIES.has(callee.name)) return;
|
|
6019
6435
|
const first = nodePath.node.arguments[0];
|
|
6020
|
-
if (!
|
|
6436
|
+
if (!t14.isArrayExpression(first)) return;
|
|
6021
6437
|
this.visitRouteObjects(first, "", routes);
|
|
6022
6438
|
}
|
|
6023
6439
|
});
|
|
@@ -6025,21 +6441,21 @@ var WebNavigationAnalyzer = class {
|
|
|
6025
6441
|
}
|
|
6026
6442
|
visitRouteObjects(arr, parentPath, out) {
|
|
6027
6443
|
for (const element of arr.elements) {
|
|
6028
|
-
if (!
|
|
6444
|
+
if (!t14.isObjectExpression(element)) continue;
|
|
6029
6445
|
let segment;
|
|
6030
6446
|
let isIndex = false;
|
|
6031
6447
|
let componentName;
|
|
6032
6448
|
let children;
|
|
6033
6449
|
for (const prop of element.properties) {
|
|
6034
|
-
if (!
|
|
6450
|
+
if (!t14.isObjectProperty(prop) || !t14.isIdentifier(prop.key)) continue;
|
|
6035
6451
|
const key = prop.key.name;
|
|
6036
|
-
if (key === "path" &&
|
|
6037
|
-
if (key === "index" &&
|
|
6038
|
-
if (key === "element" &&
|
|
6452
|
+
if (key === "path" && t14.isStringLiteral(prop.value)) segment = prop.value.value;
|
|
6453
|
+
if (key === "index" && t14.isBooleanLiteral(prop.value)) isIndex = prop.value.value;
|
|
6454
|
+
if (key === "element" && t14.isJSXElement(prop.value)) {
|
|
6039
6455
|
componentName = getJsxElementName(prop.value.openingElement) ?? void 0;
|
|
6040
6456
|
}
|
|
6041
|
-
if (key === "Component" &&
|
|
6042
|
-
if (key === "children" &&
|
|
6457
|
+
if (key === "Component" && t14.isIdentifier(prop.value)) componentName = prop.value.name;
|
|
6458
|
+
if (key === "children" && t14.isArrayExpression(prop.value)) children = prop.value;
|
|
6043
6459
|
}
|
|
6044
6460
|
const fullPath = this.joinPaths(parentPath, segment, isIndex);
|
|
6045
6461
|
if ((segment !== void 0 || isIndex) && (componentName || !children)) {
|
|
@@ -6200,23 +6616,23 @@ var ReactWebPlatformAnalyzer = class {
|
|
|
6200
6616
|
}
|
|
6201
6617
|
/** Replace route-path references with screen names where the route table resolves them. */
|
|
6202
6618
|
resolveRoutePaths(screen, routes) {
|
|
6203
|
-
const
|
|
6619
|
+
const resolve3 = (target) => {
|
|
6204
6620
|
if (!target || !target.startsWith("/")) return target;
|
|
6205
6621
|
return resolvePathToScreen(routes, target) ?? target;
|
|
6206
6622
|
};
|
|
6207
6623
|
const navigationTargets = Array.from(
|
|
6208
6624
|
new Set(
|
|
6209
|
-
screen.navigationTargets.map((target) =>
|
|
6625
|
+
screen.navigationTargets.map((target) => resolve3(target)).filter((target) => target !== screen.name)
|
|
6210
6626
|
)
|
|
6211
6627
|
).sort();
|
|
6212
6628
|
const actions = screen.actions.map((action) => {
|
|
6213
|
-
const resolved =
|
|
6629
|
+
const resolved = resolve3(action.targetScreen);
|
|
6214
6630
|
return resolved === action.targetScreen ? action : { ...action, targetScreen: resolved };
|
|
6215
6631
|
});
|
|
6216
6632
|
const collections = screen.collections?.map((collection) => {
|
|
6217
6633
|
const resolveRow = (row) => {
|
|
6218
6634
|
if (!row?.targetScreen) return row;
|
|
6219
|
-
const resolved =
|
|
6635
|
+
const resolved = resolve3(row.targetScreen);
|
|
6220
6636
|
if (resolved === row.targetScreen) return row;
|
|
6221
6637
|
return {
|
|
6222
6638
|
...row,
|
|
@@ -6449,8 +6865,8 @@ var ZodParsedType = util.arrayToEnum([
|
|
|
6449
6865
|
"set"
|
|
6450
6866
|
]);
|
|
6451
6867
|
var getParsedType = (data) => {
|
|
6452
|
-
const
|
|
6453
|
-
switch (
|
|
6868
|
+
const t18 = typeof data;
|
|
6869
|
+
switch (t18) {
|
|
6454
6870
|
case "undefined":
|
|
6455
6871
|
return ZodParsedType.undefined;
|
|
6456
6872
|
case "string":
|
|
@@ -10296,7 +10712,25 @@ function isSafeImageUrl(value) {
|
|
|
10296
10712
|
return SAFE_IMAGE_URL_RE.test(trimmed);
|
|
10297
10713
|
}
|
|
10298
10714
|
|
|
10299
|
-
// ../shared/dist/chunk-
|
|
10715
|
+
// ../shared/dist/chunk-KUMPS6RH.mjs
|
|
10716
|
+
var SUPPORTED_APPILOTS_LOCALES = ["pt-BR", "en", "es", "fr"];
|
|
10717
|
+
|
|
10718
|
+
// ../shared/dist/chunk-4RYPNBBY.mjs
|
|
10719
|
+
var symbols = external_exports.array(external_exports.string().max(240)).max(12);
|
|
10720
|
+
var controlEvidenceSchema = external_exports.object({
|
|
10721
|
+
version: external_exports.literal(1),
|
|
10722
|
+
siteId: external_exports.string().regex(/^[a-f0-9]{20}$/),
|
|
10723
|
+
component: external_exports.string().max(240),
|
|
10724
|
+
icons: symbols,
|
|
10725
|
+
handler: external_exports.string().max(240).optional(),
|
|
10726
|
+
calls: symbols,
|
|
10727
|
+
argumentBindings: symbols,
|
|
10728
|
+
conditions: symbols,
|
|
10729
|
+
nativeConfirmation: external_exports.object({
|
|
10730
|
+
title: external_exports.string().max(240).optional(),
|
|
10731
|
+
destructiveOption: external_exports.boolean()
|
|
10732
|
+
}).optional()
|
|
10733
|
+
});
|
|
10300
10734
|
var locatorSourceSchema = external_exports.enum([
|
|
10301
10735
|
"appilotsId",
|
|
10302
10736
|
"testID",
|
|
@@ -10498,7 +10932,9 @@ var screenPermissionDescriptorSchema = external_exports.object({
|
|
|
10498
10932
|
agentAccess: external_exports.enum(["read", "write", "none"]).optional(),
|
|
10499
10933
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
10500
10934
|
blockedActions: external_exports.array(external_exports.string()).optional(),
|
|
10501
|
-
isPii: external_exports.boolean().optional()
|
|
10935
|
+
isPii: external_exports.boolean().optional(),
|
|
10936
|
+
/** Graded assessment — see `screenPermissionSchema.assessment`. */
|
|
10937
|
+
assessment: external_exports.boolean().optional()
|
|
10502
10938
|
}).passthrough();
|
|
10503
10939
|
var screenDescriptorSchema = external_exports.object({
|
|
10504
10940
|
name: external_exports.string(),
|
|
@@ -10723,7 +11159,9 @@ var snapshotInputSchema = external_exports.object({
|
|
|
10723
11159
|
inModal: external_exports.boolean().optional()
|
|
10724
11160
|
}).passthrough();
|
|
10725
11161
|
var snapshotButtonSchema = external_exports.object({
|
|
11162
|
+
controlEvidence: controlEvidenceSchema.optional(),
|
|
10726
11163
|
id: boundedString(160).optional(),
|
|
11164
|
+
dispatchable: external_exports.boolean().optional(),
|
|
10727
11165
|
provenance: identityProvenanceSchema.optional(),
|
|
10728
11166
|
/**
|
|
10729
11167
|
* False when the control is mounted but currently OUTSIDE the window —
|
|
@@ -10784,7 +11222,36 @@ var snapshotListSchema = external_exports.object({
|
|
|
10784
11222
|
*/
|
|
10785
11223
|
source: boundedString(40).optional(),
|
|
10786
11224
|
itemCount: external_exports.number().int().optional(),
|
|
11225
|
+
/**
|
|
11226
|
+
* Size of the whole collection when the app declared it, for a list
|
|
11227
|
+
* that is a WINDOW onto more data than it holds.
|
|
11228
|
+
*
|
|
11229
|
+
* `itemCount` is how many rows the list is rendering from and
|
|
11230
|
+
* `visibleItemCount` how many of those are mounted; neither can
|
|
11231
|
+
* express "there are 36 and you are looking at the first 20",
|
|
11232
|
+
* because a paginated list's `data` is the page. Absent means
|
|
11233
|
+
* unknown — never "same as itemCount".
|
|
11234
|
+
*/
|
|
11235
|
+
totalItemCount: external_exports.number().int().optional(),
|
|
10787
11236
|
visibleItemCount: external_exports.number().int().optional(),
|
|
11237
|
+
viewportItemCount: external_exports.number().int().nonnegative().optional(),
|
|
11238
|
+
exploration: external_exports.object({
|
|
11239
|
+
revision: external_exports.number().int().nonnegative(),
|
|
11240
|
+
observedItemCount: external_exports.number().int().min(0).max(5e3),
|
|
11241
|
+
observedRanges: external_exports.array(
|
|
11242
|
+
external_exports.object({
|
|
11243
|
+
start: external_exports.number().int().nonnegative(),
|
|
11244
|
+
end: external_exports.number().int().nonnegative()
|
|
11245
|
+
})
|
|
11246
|
+
).max(32),
|
|
11247
|
+
rangesTruncated: external_exports.boolean(),
|
|
11248
|
+
coverage: external_exports.enum(["partial", "all-loaded"]),
|
|
11249
|
+
pagination: external_exports.enum(["possible", "not-declared"]),
|
|
11250
|
+
scrollSteps: external_exports.number().int().nonnegative(),
|
|
11251
|
+
remainingScrollSteps: external_exports.number().int().nonnegative(),
|
|
11252
|
+
consecutiveNoProgress: external_exports.number().int().nonnegative(),
|
|
11253
|
+
lastScroll: external_exports.enum(["moved", "no-progress", "boundary", "unverified"]).optional()
|
|
11254
|
+
}).optional(),
|
|
10788
11255
|
refreshing: external_exports.boolean().optional(),
|
|
10789
11256
|
empty: external_exports.boolean().optional(),
|
|
10790
11257
|
label: boundedString(300).optional(),
|
|
@@ -10832,6 +11299,7 @@ var snapshotChoiceGroupSchema = external_exports.object({
|
|
|
10832
11299
|
}).passthrough();
|
|
10833
11300
|
var snapshotElementSchema = external_exports.object({
|
|
10834
11301
|
id: boundedString(160).optional(),
|
|
11302
|
+
dispatchable: external_exports.boolean().optional(),
|
|
10835
11303
|
role: boundedString(40).optional(),
|
|
10836
11304
|
label: boundedString(300).optional(),
|
|
10837
11305
|
texts: external_exports.array(boundedString(500)).max(50).optional(),
|
|
@@ -10894,6 +11362,8 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
10894
11362
|
loadingFinished: external_exports.boolean().optional(),
|
|
10895
11363
|
textsAdded: external_exports.number().int().nonnegative().optional(),
|
|
10896
11364
|
textsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
11365
|
+
buttonsAddedIndices: external_exports.array(external_exports.number().int().nonnegative()).max(12).optional(),
|
|
11366
|
+
buttonsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
10897
11367
|
visibleRowsDelta: external_exports.number().int().optional(),
|
|
10898
11368
|
totalRowsDelta: external_exports.number().int().optional(),
|
|
10899
11369
|
fieldsNewlyFilled: external_exports.array(boundedString(160)).max(12).optional(),
|
|
@@ -10919,6 +11389,16 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
10919
11389
|
}).passthrough().optional()
|
|
10920
11390
|
}).passthrough();
|
|
10921
11391
|
var agentContextSchema = external_exports.object({
|
|
11392
|
+
missionProtocol: external_exports.literal(1).optional(),
|
|
11393
|
+
missionId: boundedString(160).optional(),
|
|
11394
|
+
/** Preferred supported device language, reported by the SDK independently of map/UI labels. */
|
|
11395
|
+
deviceLocale: external_exports.enum(SUPPORTED_APPILOTS_LOCALES).optional(),
|
|
11396
|
+
/**
|
|
11397
|
+
* Per-session plain-language override (short sentences, no jargon,
|
|
11398
|
+
* numbered steps). Absent → the project's `plainLanguage` setting.
|
|
11399
|
+
* Changes how the reply is WORDED only — see `PLAIN_LANGUAGE_PROMPT`.
|
|
11400
|
+
*/
|
|
11401
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
10922
11402
|
/**
|
|
10923
11403
|
* Client platform this observation was captured on. Optional and
|
|
10924
11404
|
* additive (see `clientPlatformSchema`) — absent means
|
|
@@ -11169,6 +11649,12 @@ var actionDiagnoseSchema = external_exports.object({
|
|
|
11169
11649
|
requiresUserInput: external_exports.boolean().optional()
|
|
11170
11650
|
});
|
|
11171
11651
|
var actionResultSchema = external_exports.object({
|
|
11652
|
+
nativeConfirmation: external_exports.object({
|
|
11653
|
+
title: external_exports.string().max(300),
|
|
11654
|
+
message: external_exports.string().max(1e3).optional(),
|
|
11655
|
+
buttonLabel: external_exports.string().max(160).optional(),
|
|
11656
|
+
handlerCompleted: external_exports.boolean()
|
|
11657
|
+
}).optional(),
|
|
11172
11658
|
actionId: external_exports.string(),
|
|
11173
11659
|
type: external_exports.string(),
|
|
11174
11660
|
success: external_exports.boolean(),
|
|
@@ -11235,7 +11721,14 @@ var screenPermissionSchema = external_exports.object({
|
|
|
11235
11721
|
agentAccess: agentAccessLevelSchema.optional(),
|
|
11236
11722
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
11237
11723
|
blockedActions: external_exports.array(external_exports.string().min(1)).optional(),
|
|
11238
|
-
isPii: external_exports.boolean().optional()
|
|
11724
|
+
isPii: external_exports.boolean().optional(),
|
|
11725
|
+
/**
|
|
11726
|
+
* Graded assessment (exam, quiz, graded activity). The agent may
|
|
11727
|
+
* explain the screen and navigate away, never write: every action
|
|
11728
|
+
* except navigate/scroll is blocked server-side, and the model is told
|
|
11729
|
+
* to refuse up front instead of trying. Wins over `agentAccess: 'write'`.
|
|
11730
|
+
*/
|
|
11731
|
+
assessment: external_exports.boolean().optional()
|
|
11239
11732
|
}).strict();
|
|
11240
11733
|
var projectPermissionsSchema = external_exports.object({
|
|
11241
11734
|
agentCanWrite: external_exports.boolean(),
|
|
@@ -11243,6 +11736,10 @@ var projectPermissionsSchema = external_exports.object({
|
|
|
11243
11736
|
confirmDestructive: external_exports.boolean(),
|
|
11244
11737
|
/**
|
|
11245
11738
|
* Map of screenName → override. Keys must be non-empty strings.
|
|
11739
|
+
* A key containing `*` or starting with `/` is a PATTERN, matched
|
|
11740
|
+
* case-insensitively against the screen name and the navigation path
|
|
11741
|
+
* (`*Quiz*`; a path key uses `*` per segment, see docs/permissions).
|
|
11742
|
+
* An exact key beats patterns.
|
|
11246
11743
|
* Limited to 200 entries to keep the JSON column small and editable
|
|
11247
11744
|
* in the dashboard table view.
|
|
11248
11745
|
*/
|
|
@@ -11296,7 +11793,7 @@ var updateProjectBudgetSchema = external_exports.object({
|
|
|
11296
11793
|
/** null = remove webhook, undefined = leave unchanged. */
|
|
11297
11794
|
budgetWebhookUrl: external_exports.string().url().nullable().optional()
|
|
11298
11795
|
}).strict();
|
|
11299
|
-
var localeSchema = external_exports.enum(
|
|
11796
|
+
var localeSchema = external_exports.enum(SUPPORTED_APPILOTS_LOCALES);
|
|
11300
11797
|
var hexColorSchema = external_exports.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
|
|
11301
11798
|
message: "Must be a hex color like #6366f1"
|
|
11302
11799
|
});
|
|
@@ -11340,6 +11837,13 @@ var imageSourceField = external_exports.string().max(500).refine(isSafeImageUrl,
|
|
|
11340
11837
|
var projectPersonalizationSchema = external_exports.object({
|
|
11341
11838
|
/** Tone / persona instructions appended to the system prompt. */
|
|
11342
11839
|
personaPrompt: external_exports.string().max(2048).nullable(),
|
|
11840
|
+
/**
|
|
11841
|
+
* Accessibility: reply in plain language (short sentences, everyday
|
|
11842
|
+
* words, numbered steps, one question at a time). Server-side only,
|
|
11843
|
+
* like `personaPrompt`; a session can override it via
|
|
11844
|
+
* `context.plainLanguage`.
|
|
11845
|
+
*/
|
|
11846
|
+
plainLanguage: external_exports.boolean(),
|
|
11343
11847
|
/** Display name in the chat header (e.g. "Aria"). */
|
|
11344
11848
|
assistantName: external_exports.string().max(60).nullable(),
|
|
11345
11849
|
/** URL or remote asset id for the avatar shown next to assistant turns. */
|
|
@@ -11363,6 +11867,7 @@ var projectPersonalizationSchema = external_exports.object({
|
|
|
11363
11867
|
});
|
|
11364
11868
|
var updateProjectPersonalizationSchema = external_exports.object({
|
|
11365
11869
|
personaPrompt: external_exports.string().max(2048).nullable().optional(),
|
|
11870
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
11366
11871
|
assistantName: external_exports.string().max(60).nullable().optional(),
|
|
11367
11872
|
assistantAvatar: imageSourceField.nullable().optional(),
|
|
11368
11873
|
emptyStateIcon: imageSourceField.nullable().optional(),
|
|
@@ -11520,6 +12025,8 @@ var sandboxRunRequestSchema = external_exports.object({
|
|
|
11520
12025
|
* the dev doesn't want their tokens metered.
|
|
11521
12026
|
*/
|
|
11522
12027
|
silent: external_exports.boolean().optional(),
|
|
12028
|
+
/** Run with the plain-language reply style on/off, overriding the project. */
|
|
12029
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
11523
12030
|
/**
|
|
11524
12031
|
* Prior turns of the conversation, in order, that precede `prompt`.
|
|
11525
12032
|
* Lets a driver run the agent loop hop-by-hop headlessly (the multi-
|
|
@@ -12039,9 +12546,22 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
12039
12546
|
const filePath = import_node_path5.default.resolve(outputDir, `mcp-document.${this.options.format}`);
|
|
12040
12547
|
await (0, import_promises4.writeFile)(filePath, serialized, "utf-8");
|
|
12041
12548
|
console.log(`[MCPGenerator] Document written to: ${filePath}`);
|
|
12549
|
+
const controlFiles = analyzed.controlEvidenceFiles ?? {};
|
|
12550
|
+
await (0, import_promises4.writeFile)(
|
|
12551
|
+
import_node_path5.default.resolve(outputDir, "control-evidence.json"),
|
|
12552
|
+
JSON.stringify({ version: 1, files: controlFiles }, null, 2),
|
|
12553
|
+
"utf-8"
|
|
12554
|
+
);
|
|
12042
12555
|
const checksumFilePath = import_node_path5.default.resolve(outputDir, ".appilots-checksum");
|
|
12043
12556
|
await (0, import_promises4.writeFile)(checksumFilePath, checksum, "utf-8");
|
|
12044
12557
|
console.log(`[MCPGenerator] Checksum written to: ${checksumFilePath}`);
|
|
12558
|
+
const evidenceCount = Object.values(controlFiles).reduce(
|
|
12559
|
+
(sum, entries) => sum + entries.length,
|
|
12560
|
+
0
|
|
12561
|
+
);
|
|
12562
|
+
console.log(
|
|
12563
|
+
`[MCPGenerator] Source evidence: ${evidenceCount} icon controls across ${Object.keys(controlFiles).length} files (runtime binding required)`
|
|
12564
|
+
);
|
|
12045
12565
|
console.log("[MCPGenerator] Generation complete!");
|
|
12046
12566
|
return {
|
|
12047
12567
|
document,
|
|
@@ -12072,7 +12592,7 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
12072
12592
|
* Calculate SHA-256 checksum of content
|
|
12073
12593
|
*/
|
|
12074
12594
|
calculateChecksum(content) {
|
|
12075
|
-
return (0,
|
|
12595
|
+
return (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex");
|
|
12076
12596
|
}
|
|
12077
12597
|
/**
|
|
12078
12598
|
* Get project name and version from package.json in rootDir
|
|
@@ -12688,15 +13208,15 @@ var import_node_path9 = __toESM(require("path"));
|
|
|
12688
13208
|
var import_commander5 = require("commander");
|
|
12689
13209
|
|
|
12690
13210
|
// src/annotate/sites.ts
|
|
12691
|
-
var
|
|
12692
|
-
var
|
|
13211
|
+
var import_traverse13 = __toESM(require("@babel/traverse"));
|
|
13212
|
+
var t16 = __toESM(require("@babel/types"));
|
|
12693
13213
|
|
|
12694
13214
|
// src/annotate/forwarding.ts
|
|
12695
13215
|
var import_node_path8 = __toESM(require("path"));
|
|
12696
13216
|
var import_node_fs5 = require("fs");
|
|
12697
|
-
var
|
|
12698
|
-
var
|
|
12699
|
-
var
|
|
13217
|
+
var import_traverse12 = __toESM(require("@babel/traverse"));
|
|
13218
|
+
var t15 = __toESM(require("@babel/types"));
|
|
13219
|
+
var traverse12 = import_traverse12.default.default ?? import_traverse12.default;
|
|
12700
13220
|
var RN_TESTID_CARRIERS = /* @__PURE__ */ new Set([
|
|
12701
13221
|
"View",
|
|
12702
13222
|
"Text",
|
|
@@ -12723,14 +13243,14 @@ var RN_TESTID_CARRIERS = /* @__PURE__ */ new Set([
|
|
|
12723
13243
|
]);
|
|
12724
13244
|
function collectImports(ast) {
|
|
12725
13245
|
const out = /* @__PURE__ */ new Map();
|
|
12726
|
-
|
|
13246
|
+
traverse12(ast, {
|
|
12727
13247
|
ImportDeclaration(nodePath) {
|
|
12728
13248
|
const source = nodePath.node.source.value;
|
|
12729
13249
|
for (const spec of nodePath.node.specifiers) {
|
|
12730
|
-
if (
|
|
12731
|
-
const imported =
|
|
13250
|
+
if (t15.isImportSpecifier(spec)) {
|
|
13251
|
+
const imported = t15.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
12732
13252
|
out.set(spec.local.name, { source, local: spec.local.name, imported });
|
|
12733
|
-
} else if (
|
|
13253
|
+
} else if (t15.isImportDefaultSpecifier(spec)) {
|
|
12734
13254
|
out.set(spec.local.name, { source, local: spec.local.name, imported: "default" });
|
|
12735
13255
|
}
|
|
12736
13256
|
}
|
|
@@ -12778,13 +13298,13 @@ function inspectModule(file2, exportName, depth = 0) {
|
|
|
12778
13298
|
let forwards = false;
|
|
12779
13299
|
let derives = false;
|
|
12780
13300
|
let barrelTarget;
|
|
12781
|
-
|
|
13301
|
+
traverse12(ast, {
|
|
12782
13302
|
// `export { Card } from './Card'` — follow one hop.
|
|
12783
13303
|
ExportNamedDeclaration(nodePath) {
|
|
12784
13304
|
const from = nodePath.node.source?.value;
|
|
12785
13305
|
if (!from) return;
|
|
12786
13306
|
const named = nodePath.node.specifiers.some(
|
|
12787
|
-
(spec) =>
|
|
13307
|
+
(spec) => t15.isExportSpecifier(spec) && (t15.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value) === exportName
|
|
12788
13308
|
);
|
|
12789
13309
|
if (named) barrelTarget = from;
|
|
12790
13310
|
},
|
|
@@ -12793,11 +13313,11 @@ function inspectModule(file2, exportName, depth = 0) {
|
|
|
12793
13313
|
},
|
|
12794
13314
|
// `interface CardProps { testID?: string }` / `type Props = { testID… }`
|
|
12795
13315
|
TSPropertySignature(nodePath) {
|
|
12796
|
-
if (
|
|
13316
|
+
if (t15.isIdentifier(nodePath.node.key) && nodePath.node.key.name === "testID") forwards = true;
|
|
12797
13317
|
},
|
|
12798
13318
|
// `function Card({ testID }) {}` — the destructured parameter.
|
|
12799
13319
|
ObjectProperty(nodePath) {
|
|
12800
|
-
if (
|
|
13320
|
+
if (t15.isIdentifier(nodePath.node.key) && nodePath.node.key.name === "testID") forwards = true;
|
|
12801
13321
|
},
|
|
12802
13322
|
// `<View {...props} />` — anything spread onto JSX carries it along.
|
|
12803
13323
|
JSXSpreadAttribute() {
|
|
@@ -12808,17 +13328,17 @@ function inspectModule(file2, exportName, depth = 0) {
|
|
|
12808
13328
|
// that is one hardcoded name shared by every instance, which is the
|
|
12809
13329
|
// duplicate-id problem rather than a solution to it.
|
|
12810
13330
|
JSXAttribute(nodePath) {
|
|
12811
|
-
const name =
|
|
13331
|
+
const name = t15.isJSXIdentifier(nodePath.node.name) ? nodePath.node.name.name : "";
|
|
12812
13332
|
if (name !== "testID" && name !== "accessibilityLabel") return;
|
|
12813
13333
|
const value = nodePath.node.value;
|
|
12814
|
-
if (!
|
|
12815
|
-
if (
|
|
13334
|
+
if (!t15.isJSXExpressionContainer(value)) return;
|
|
13335
|
+
if (t15.isStringLiteral(value.expression)) return;
|
|
12816
13336
|
derives = true;
|
|
12817
13337
|
},
|
|
12818
13338
|
// `useAppilotsTarget(`${groupId}-${option.value}`, …)`
|
|
12819
13339
|
CallExpression(nodePath) {
|
|
12820
13340
|
const callee = nodePath.node.callee;
|
|
12821
|
-
if (
|
|
13341
|
+
if (t15.isIdentifier(callee) && IDENTITY_HOOKS.has(callee.name)) derives = true;
|
|
12822
13342
|
}
|
|
12823
13343
|
});
|
|
12824
13344
|
if (forwards || derives) return { forwards, derives };
|
|
@@ -12852,7 +13372,7 @@ function resetForwardingCache() {
|
|
|
12852
13372
|
}
|
|
12853
13373
|
|
|
12854
13374
|
// src/annotate/sites.ts
|
|
12855
|
-
var
|
|
13375
|
+
var traverse13 = import_traverse13.default.default ?? import_traverse13.default;
|
|
12856
13376
|
var LABEL_PROPS = ["title", "label", "placeholder"];
|
|
12857
13377
|
var IDENTITY_PROPS = ["testID", "appilotsId", "accessibilityLabel"];
|
|
12858
13378
|
function kebab(text) {
|
|
@@ -12903,18 +13423,18 @@ function hasLabelText(element) {
|
|
|
12903
13423
|
function namedHandler(element, prop) {
|
|
12904
13424
|
if (!prop) return void 0;
|
|
12905
13425
|
const attr = findJsxAttribute(element, prop);
|
|
12906
|
-
if (!attr || !
|
|
12907
|
-
return
|
|
13426
|
+
if (!attr || !t16.isJSXExpressionContainer(attr.value)) return void 0;
|
|
13427
|
+
return t16.isIdentifier(attr.value.expression) ? attr.value.expression.name : void 0;
|
|
12908
13428
|
}
|
|
12909
13429
|
var ANNOTATABLE_ROLES = /* @__PURE__ */ new Set(["button", "input", "select", "toggle", "date", "list"]);
|
|
12910
13430
|
function isPerItemRender(nodePath) {
|
|
12911
13431
|
let current = nodePath.parentPath;
|
|
12912
13432
|
while (current) {
|
|
12913
13433
|
const node = current.node;
|
|
12914
|
-
if (
|
|
13434
|
+
if (t16.isCallExpression(node) && t16.isMemberExpression(node.callee) && t16.isIdentifier(node.callee.property) && (node.callee.property.name === "map" || node.callee.property.name === "flatMap")) {
|
|
12915
13435
|
return true;
|
|
12916
13436
|
}
|
|
12917
|
-
if (
|
|
13437
|
+
if (t16.isJSXAttribute(node) && t16.isJSXIdentifier(node.name) && /^render[A-Z]|^ListHeaderComponent$|^ListFooterComponent$/.test(node.name.name) && node.name.name !== "renderScrollComponent") {
|
|
12918
13438
|
return /^renderItem$|^renderSectionHeader$|^renderSectionFooter$/.test(node.name.name);
|
|
12919
13439
|
}
|
|
12920
13440
|
current = current.parentPath;
|
|
@@ -12933,7 +13453,7 @@ function findAnnotationSites(source, file2) {
|
|
|
12933
13453
|
const sites = [];
|
|
12934
13454
|
const perComponent = /* @__PURE__ */ new Map();
|
|
12935
13455
|
const usedIds = /* @__PURE__ */ new Set();
|
|
12936
|
-
|
|
13456
|
+
traverse13(ast, {
|
|
12937
13457
|
JSXOpeningElement(nodePath) {
|
|
12938
13458
|
for (const prop of IDENTITY_PROPS) {
|
|
12939
13459
|
const existing = getStringAttr(nodePath.node, prop);
|
|
@@ -12941,10 +13461,10 @@ function findAnnotationSites(source, file2) {
|
|
|
12941
13461
|
}
|
|
12942
13462
|
}
|
|
12943
13463
|
});
|
|
12944
|
-
|
|
13464
|
+
traverse13(ast, {
|
|
12945
13465
|
JSXOpeningElement(nodePath) {
|
|
12946
13466
|
const element = nodePath.node;
|
|
12947
|
-
if (!
|
|
13467
|
+
if (!t16.isJSXIdentifier(element.name)) return;
|
|
12948
13468
|
const component = element.name.name;
|
|
12949
13469
|
const role = classifyJsxComponent(component, element);
|
|
12950
13470
|
if (!ANNOTATABLE_ROLES.has(role)) return;
|
|
@@ -13028,21 +13548,21 @@ function applyToFile(source, sites) {
|
|
|
13028
13548
|
// src/annotate/forward.ts
|
|
13029
13549
|
var import_node_fs6 = require("fs");
|
|
13030
13550
|
var import_magic_string2 = __toESM(require("magic-string"));
|
|
13031
|
-
var
|
|
13032
|
-
var
|
|
13033
|
-
var
|
|
13551
|
+
var import_traverse14 = __toESM(require("@babel/traverse"));
|
|
13552
|
+
var t17 = __toESM(require("@babel/types"));
|
|
13553
|
+
var traverse14 = import_traverse14.default.default ?? import_traverse14.default;
|
|
13034
13554
|
function findComponent(ast, name) {
|
|
13035
13555
|
let found;
|
|
13036
|
-
|
|
13556
|
+
traverse14(ast, {
|
|
13037
13557
|
FunctionDeclaration(nodePath) {
|
|
13038
13558
|
if (nodePath.node.id?.name === name) {
|
|
13039
13559
|
found ??= { params: nodePath.node.params, body: nodePath.node.body };
|
|
13040
13560
|
}
|
|
13041
13561
|
},
|
|
13042
13562
|
VariableDeclarator(nodePath) {
|
|
13043
|
-
if (!
|
|
13563
|
+
if (!t17.isIdentifier(nodePath.node.id) || nodePath.node.id.name !== name) return;
|
|
13044
13564
|
const init = nodePath.node.init;
|
|
13045
|
-
if (
|
|
13565
|
+
if (t17.isArrowFunctionExpression(init) || t17.isFunctionExpression(init)) {
|
|
13046
13566
|
found ??= { params: init.params, body: init.body };
|
|
13047
13567
|
}
|
|
13048
13568
|
}
|
|
@@ -13054,26 +13574,26 @@ function findReturnedRoots(body) {
|
|
|
13054
13574
|
let unsupported = false;
|
|
13055
13575
|
const record = (node) => {
|
|
13056
13576
|
if (!node) return;
|
|
13057
|
-
if (
|
|
13058
|
-
if (
|
|
13577
|
+
if (t17.isParenthesizedExpression(node)) return record(node.expression);
|
|
13578
|
+
if (t17.isJSXElement(node)) {
|
|
13059
13579
|
roots.push(node.openingElement);
|
|
13060
13580
|
return;
|
|
13061
13581
|
}
|
|
13062
|
-
if (
|
|
13582
|
+
if (t17.isConditionalExpression(node)) {
|
|
13063
13583
|
record(node.consequent);
|
|
13064
13584
|
record(node.alternate);
|
|
13065
13585
|
return;
|
|
13066
13586
|
}
|
|
13067
|
-
if (
|
|
13587
|
+
if (t17.isJSXFragment(node)) {
|
|
13068
13588
|
unsupported = true;
|
|
13069
13589
|
return;
|
|
13070
13590
|
}
|
|
13071
|
-
if (
|
|
13591
|
+
if (t17.isNullLiteral(node)) return;
|
|
13072
13592
|
unsupported = true;
|
|
13073
13593
|
};
|
|
13074
|
-
if (
|
|
13075
|
-
|
|
13076
|
-
|
|
13594
|
+
if (t17.isBlockStatement(body)) {
|
|
13595
|
+
traverse14(
|
|
13596
|
+
t17.file(t17.program([t17.expressionStatement(t17.functionExpression(null, [], body))])),
|
|
13077
13597
|
{
|
|
13078
13598
|
ReturnStatement(nodePath) {
|
|
13079
13599
|
record(nodePath.node.argument);
|
|
@@ -13087,7 +13607,7 @@ function findReturnedRoots(body) {
|
|
|
13087
13607
|
return roots;
|
|
13088
13608
|
}
|
|
13089
13609
|
function isCarrierRoot(element, imports) {
|
|
13090
|
-
if (!
|
|
13610
|
+
if (!t17.isJSXIdentifier(element.name)) return false;
|
|
13091
13611
|
const name = element.name.name;
|
|
13092
13612
|
const origin = imports.get(name);
|
|
13093
13613
|
if (origin?.source !== "react-native") return false;
|
|
@@ -13095,7 +13615,7 @@ function isCarrierRoot(element, imports) {
|
|
|
13095
13615
|
}
|
|
13096
13616
|
function hasProp(element, name) {
|
|
13097
13617
|
return element.attributes.some(
|
|
13098
|
-
(attr) =>
|
|
13618
|
+
(attr) => t17.isJSXAttribute(attr) && t17.isJSXIdentifier(attr.name) && attr.name.name === name
|
|
13099
13619
|
);
|
|
13100
13620
|
}
|
|
13101
13621
|
function indentOf(source, offset) {
|
|
@@ -13121,12 +13641,12 @@ ${indent}${text}` };
|
|
|
13121
13641
|
}
|
|
13122
13642
|
function followBarrel(file2, ast, component) {
|
|
13123
13643
|
let target;
|
|
13124
|
-
|
|
13644
|
+
traverse14(ast, {
|
|
13125
13645
|
ExportNamedDeclaration(nodePath) {
|
|
13126
13646
|
const from = nodePath.node.source?.value;
|
|
13127
13647
|
if (!from) return;
|
|
13128
13648
|
const named = nodePath.node.specifiers.some(
|
|
13129
|
-
(spec) =>
|
|
13649
|
+
(spec) => t17.isExportSpecifier(spec) && (t17.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value) === component
|
|
13130
13650
|
);
|
|
13131
13651
|
if (named) target ??= from;
|
|
13132
13652
|
}
|
|
@@ -13154,7 +13674,7 @@ function planForward(file2, component, depth = 0) {
|
|
|
13154
13674
|
return { ...base, refusal: `no function named ${component} in this file` };
|
|
13155
13675
|
}
|
|
13156
13676
|
const param = found.params[0];
|
|
13157
|
-
if (!
|
|
13677
|
+
if (!t17.isObjectPattern(param)) {
|
|
13158
13678
|
return {
|
|
13159
13679
|
...base,
|
|
13160
13680
|
refusal: `${component} does not destructure its props, so there is no safe place to add one without knowing what the parameter is called downstream`
|
|
@@ -13170,7 +13690,7 @@ function planForward(file2, component, depth = 0) {
|
|
|
13170
13690
|
const imports = collectImports(ast);
|
|
13171
13691
|
const nonCarrier = roots.find((root) => !isCarrierRoot(root, imports));
|
|
13172
13692
|
if (nonCarrier) {
|
|
13173
|
-
const name =
|
|
13693
|
+
const name = t17.isJSXIdentifier(nonCarrier.name) ? nonCarrier.name.name : "its root";
|
|
13174
13694
|
return {
|
|
13175
13695
|
...base,
|
|
13176
13696
|
refusal: `${component} renders <${name}> at its root, which is not a React Native element known to accept testID. Forwarding into it would move the problem down a level, not fix it`
|
|
@@ -13178,30 +13698,30 @@ function planForward(file2, component, depth = 0) {
|
|
|
13178
13698
|
}
|
|
13179
13699
|
const edits = [];
|
|
13180
13700
|
const alreadyDestructured = param.properties.some(
|
|
13181
|
-
(prop) =>
|
|
13701
|
+
(prop) => t17.isObjectProperty(prop) && t17.isIdentifier(prop.key) && prop.key.name === "testID"
|
|
13182
13702
|
);
|
|
13183
13703
|
if (!alreadyDestructured) {
|
|
13184
13704
|
const insert = insertAfterLastMember(source, param.properties, "testID", param.end ?? 0, ",");
|
|
13185
13705
|
edits.push({ ...insert, what: "parameter" });
|
|
13186
13706
|
}
|
|
13187
13707
|
const annotation = param.typeAnnotation;
|
|
13188
|
-
if (
|
|
13708
|
+
if (t17.isTSTypeAnnotation(annotation)) {
|
|
13189
13709
|
const declared = annotation.typeAnnotation;
|
|
13190
13710
|
let body;
|
|
13191
13711
|
let closing = 0;
|
|
13192
|
-
if (
|
|
13712
|
+
if (t17.isTSTypeLiteral(declared)) {
|
|
13193
13713
|
body = declared.members;
|
|
13194
13714
|
closing = declared.end ?? 0;
|
|
13195
|
-
} else if (
|
|
13715
|
+
} else if (t17.isTSTypeReference(declared) && t17.isIdentifier(declared.typeName)) {
|
|
13196
13716
|
const typeName = declared.typeName.name;
|
|
13197
|
-
|
|
13717
|
+
traverse14(ast, {
|
|
13198
13718
|
TSInterfaceDeclaration(nodePath) {
|
|
13199
13719
|
if (nodePath.node.id.name !== typeName) return;
|
|
13200
13720
|
body = nodePath.node.body.body;
|
|
13201
13721
|
closing = nodePath.node.body.end ?? 0;
|
|
13202
13722
|
},
|
|
13203
13723
|
TSTypeAliasDeclaration(nodePath) {
|
|
13204
|
-
if (nodePath.node.id.name !== typeName || !
|
|
13724
|
+
if (nodePath.node.id.name !== typeName || !t17.isTSTypeLiteral(nodePath.node.typeAnnotation))
|
|
13205
13725
|
return;
|
|
13206
13726
|
body = nodePath.node.typeAnnotation.members;
|
|
13207
13727
|
closing = nodePath.node.typeAnnotation.end ?? 0;
|
|
@@ -13216,7 +13736,7 @@ function planForward(file2, component, depth = 0) {
|
|
|
13216
13736
|
}
|
|
13217
13737
|
if (body) {
|
|
13218
13738
|
const declaresTestId = body.some(
|
|
13219
|
-
(member) =>
|
|
13739
|
+
(member) => t17.isTSPropertySignature(member) && t17.isIdentifier(member.key) && member.key.name === "testID"
|
|
13220
13740
|
);
|
|
13221
13741
|
if (!declaresTestId) {
|
|
13222
13742
|
const insert = insertAfterLastMember(source, body, "testID?: string;", closing, ";");
|
|
@@ -14009,9 +14529,145 @@ Pass rate: ${passed}/${total} (${passRatePct}%) \u2014 threshold ${minPassRatePc
|
|
|
14009
14529
|
});
|
|
14010
14530
|
}
|
|
14011
14531
|
|
|
14532
|
+
// src/cli/commands/knowledge.ts
|
|
14533
|
+
var import_commander8 = require("commander");
|
|
14534
|
+
var import_promises7 = require("fs/promises");
|
|
14535
|
+
var import_node_path10 = require("path");
|
|
14536
|
+
var import_node_crypto3 = require("crypto");
|
|
14537
|
+
var import_fast_glob2 = __toESM(require("fast-glob"));
|
|
14538
|
+
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".html", ".htm", ".pdf"]);
|
|
14539
|
+
var MIME_MAP = {
|
|
14540
|
+
".md": "text/markdown",
|
|
14541
|
+
".txt": "text/plain",
|
|
14542
|
+
".html": "text/html",
|
|
14543
|
+
".htm": "text/html",
|
|
14544
|
+
".pdf": "application/pdf"
|
|
14545
|
+
};
|
|
14546
|
+
function knowledgeCommand() {
|
|
14547
|
+
const cmd = new import_commander8.Command("knowledge").description("Manage the project knowledge base (RAG)");
|
|
14548
|
+
cmd.command("sync").description("Sync local documentation files to the knowledge base").option("--verbose", "Enable verbose logging", false).option("--api-key <key>", "Appilots API key (overrides .appilotsrc)").option("--server <url>", "Appilots server URL (overrides .appilotsrc)").option("--json", "Output a machine-readable JSON result (implies --quiet)", false).option("--quiet", "Suppress banner and informational output", false).action(async (options) => {
|
|
14549
|
+
if (options.json) setLogMode("json");
|
|
14550
|
+
else if (options.quiet) setLogMode("quiet");
|
|
14551
|
+
try {
|
|
14552
|
+
banner();
|
|
14553
|
+
const loaded = loadConfig(warn);
|
|
14554
|
+
const apiKey = options.apiKey || loaded?.apiKey;
|
|
14555
|
+
if (!apiKey) {
|
|
14556
|
+
error("No .appilotsrc configuration found");
|
|
14557
|
+
info("Run: appilots init --api-key <your-key>");
|
|
14558
|
+
jsonOutput({ success: false, error: "No configuration found (missing API key)" });
|
|
14559
|
+
process.exit(1);
|
|
14560
|
+
return;
|
|
14561
|
+
}
|
|
14562
|
+
const config = {
|
|
14563
|
+
serverUrl: DEFAULT_SERVER_URL,
|
|
14564
|
+
...loaded,
|
|
14565
|
+
apiKey,
|
|
14566
|
+
...options.server ? { serverUrl: options.server } : {}
|
|
14567
|
+
};
|
|
14568
|
+
const sources = config.knowledge?.sources;
|
|
14569
|
+
if (!sources || sources.length === 0) {
|
|
14570
|
+
error("No knowledge sources configured");
|
|
14571
|
+
info("Add knowledge.sources to .appilotsrc:");
|
|
14572
|
+
info(' { "knowledge": { "sources": ["docs/**/*.md"] } }');
|
|
14573
|
+
jsonOutput({ success: false, error: "No knowledge sources configured" });
|
|
14574
|
+
process.exit(1);
|
|
14575
|
+
return;
|
|
14576
|
+
}
|
|
14577
|
+
const spinner = createSpinner("Scanning for knowledge documents...");
|
|
14578
|
+
spinner.start();
|
|
14579
|
+
const rootDir = process.cwd();
|
|
14580
|
+
const excludePatterns = config.knowledge?.exclude ?? [];
|
|
14581
|
+
const allFiles = [];
|
|
14582
|
+
for (const pattern of sources) {
|
|
14583
|
+
const matches = await (0, import_fast_glob2.default)(pattern, {
|
|
14584
|
+
cwd: rootDir,
|
|
14585
|
+
ignore: ["node_modules/**", ...excludePatterns],
|
|
14586
|
+
onlyFiles: true
|
|
14587
|
+
});
|
|
14588
|
+
for (const match of matches) {
|
|
14589
|
+
const ext = (0, import_node_path10.extname)(match).toLowerCase();
|
|
14590
|
+
if (SUPPORTED_EXTENSIONS.has(ext) && !allFiles.includes(match)) {
|
|
14591
|
+
allFiles.push(match);
|
|
14592
|
+
}
|
|
14593
|
+
}
|
|
14594
|
+
}
|
|
14595
|
+
spinner.stop();
|
|
14596
|
+
if (allFiles.length === 0) {
|
|
14597
|
+
warn("No supported files found matching knowledge.sources patterns");
|
|
14598
|
+
info(`Supported: ${[...SUPPORTED_EXTENSIONS].join(", ")}`);
|
|
14599
|
+
jsonOutput({ success: true, uploaded: 0, skipped: 0, errors: 0, files: 0 });
|
|
14600
|
+
return;
|
|
14601
|
+
}
|
|
14602
|
+
info(`Found ${allFiles.length} document${allFiles.length === 1 ? "" : "s"}`);
|
|
14603
|
+
const documents = [];
|
|
14604
|
+
for (const filePath of allFiles) {
|
|
14605
|
+
const absolutePath = (0, import_node_path10.resolve)(rootDir, filePath);
|
|
14606
|
+
const buffer = await (0, import_promises7.readFile)(absolutePath);
|
|
14607
|
+
const checksum = (0, import_node_crypto3.createHash)("sha256").update(buffer).digest("hex");
|
|
14608
|
+
const ext = (0, import_node_path10.extname)(filePath).toLowerCase();
|
|
14609
|
+
const mimeType = MIME_MAP[ext] ?? "text/plain";
|
|
14610
|
+
documents.push({
|
|
14611
|
+
filename: (0, import_node_path10.relative)(rootDir, absolutePath),
|
|
14612
|
+
content: buffer.toString("base64"),
|
|
14613
|
+
mimeType,
|
|
14614
|
+
checksum
|
|
14615
|
+
});
|
|
14616
|
+
}
|
|
14617
|
+
const uploadSpinner = createSpinner(
|
|
14618
|
+
`Uploading ${documents.length} document${documents.length === 1 ? "" : "s"}...`
|
|
14619
|
+
);
|
|
14620
|
+
uploadSpinner.start();
|
|
14621
|
+
const apiClient = new AppilotsAPIClient({
|
|
14622
|
+
serverUrl: config.serverUrl,
|
|
14623
|
+
apiKey: config.apiKey,
|
|
14624
|
+
timeoutMs: 12e4
|
|
14625
|
+
// Knowledge sync can take longer (embedding)
|
|
14626
|
+
});
|
|
14627
|
+
const result = await apiClient.knowledgeSync(documents);
|
|
14628
|
+
uploadSpinner.stop();
|
|
14629
|
+
if (!result.success) {
|
|
14630
|
+
error(`Knowledge sync failed: ${result.error}`);
|
|
14631
|
+
jsonOutput({ success: false, error: result.error });
|
|
14632
|
+
process.exit(1);
|
|
14633
|
+
return;
|
|
14634
|
+
}
|
|
14635
|
+
success(
|
|
14636
|
+
`Knowledge sync complete: ${result.uploaded} uploaded, ${result.skipped} skipped, ${result.errors} errors`
|
|
14637
|
+
);
|
|
14638
|
+
if (options.verbose && result.details) {
|
|
14639
|
+
for (const detail of result.details) {
|
|
14640
|
+
if (detail.status === "uploaded") {
|
|
14641
|
+
info(` + ${detail.filename}`);
|
|
14642
|
+
} else if (detail.status === "skipped") {
|
|
14643
|
+
info(` = ${detail.filename} (unchanged)`);
|
|
14644
|
+
} else {
|
|
14645
|
+
warn(` ! ${detail.filename}: ${detail.error}`);
|
|
14646
|
+
}
|
|
14647
|
+
}
|
|
14648
|
+
}
|
|
14649
|
+
jsonOutput({
|
|
14650
|
+
success: true,
|
|
14651
|
+
uploaded: result.uploaded,
|
|
14652
|
+
skipped: result.skipped,
|
|
14653
|
+
errors: result.errors,
|
|
14654
|
+
details: result.details
|
|
14655
|
+
});
|
|
14656
|
+
} catch (err) {
|
|
14657
|
+
error(`Knowledge sync failed: ${stringifyUnknown(err)}`);
|
|
14658
|
+
jsonOutput({ success: false, error: stringifyUnknown(err) });
|
|
14659
|
+
if (options.verbose) {
|
|
14660
|
+
console.error(err);
|
|
14661
|
+
}
|
|
14662
|
+
process.exit(1);
|
|
14663
|
+
}
|
|
14664
|
+
});
|
|
14665
|
+
return cmd;
|
|
14666
|
+
}
|
|
14667
|
+
|
|
14012
14668
|
// src/cli/index.ts
|
|
14013
14669
|
async function main() {
|
|
14014
|
-
const program2 = new
|
|
14670
|
+
const program2 = new import_commander9.Command();
|
|
14015
14671
|
program2.name("appilots").description(
|
|
14016
14672
|
"Analyze your React Native app and generate MCP documents for AI-powered navigation.\n\nQuick start:\n $ appilots init --api-key ak_xxx\n $ appilots sync\n $ appilots watch"
|
|
14017
14673
|
).version(CLI_VERSION).option("--verbose", "Enable verbose logging");
|
|
@@ -14022,6 +14678,7 @@ async function main() {
|
|
|
14022
14678
|
program2.addCommand(annotateCommand());
|
|
14023
14679
|
program2.addCommand(statusCommand());
|
|
14024
14680
|
program2.addCommand(evalCommand());
|
|
14681
|
+
program2.addCommand(knowledgeCommand());
|
|
14025
14682
|
const args = process.argv.slice(2);
|
|
14026
14683
|
const firstArg = args[0];
|
|
14027
14684
|
const hasCommand = firstArg !== void 0 && args.length > 0 && !firstArg.startsWith("-");
|