@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/index.mjs
CHANGED
|
@@ -1,19 +1,356 @@
|
|
|
1
1
|
import fs, { readFile, mkdir, writeFile } from 'fs/promises';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import traverse5 from '@babel/traverse';
|
|
4
|
+
import * as BabelTypes from '@babel/types';
|
|
2
5
|
import * as path2 from 'path';
|
|
3
6
|
import path2__default, { join, resolve } from 'path';
|
|
4
|
-
import traverse4 from '@babel/traverse';
|
|
5
|
-
import * as BabelTypes from '@babel/types';
|
|
6
7
|
import fastGlob from 'fast-glob';
|
|
7
8
|
import * as parser from '@babel/parser';
|
|
8
9
|
import { parse } from '@babel/parser';
|
|
9
10
|
import { promises, readFileSync, writeFileSync, existsSync, statSync, readdirSync } from 'fs';
|
|
10
|
-
import { createHash } from 'crypto';
|
|
11
11
|
|
|
12
12
|
var __defProp = Object.defineProperty;
|
|
13
13
|
var __export = (target, all) => {
|
|
14
14
|
for (var name in all)
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
|
+
var MAX_HOPS = 8;
|
|
18
|
+
function unwrap(path11) {
|
|
19
|
+
while (path11.isTSAsExpression() || path11.isTSTypeAssertion() || path11.isTSNonNullExpression() || path11.isTSSatisfiesExpression() || path11.isParenthesizedExpression())
|
|
20
|
+
path11 = path11.get("expression");
|
|
21
|
+
return path11;
|
|
22
|
+
}
|
|
23
|
+
function constantValue(path11, depth = 0) {
|
|
24
|
+
if (depth > MAX_HOPS) return void 0;
|
|
25
|
+
path11 = unwrap(path11);
|
|
26
|
+
if (!path11.isIdentifier()) return path11;
|
|
27
|
+
const binding = path11.scope.getBinding(path11.node.name);
|
|
28
|
+
if (!binding?.constant || !binding.path.isVariableDeclarator()) return path11;
|
|
29
|
+
const init = binding.path.get("init");
|
|
30
|
+
const resolved = init.node ? constantValue(init, depth + 1) : void 0;
|
|
31
|
+
if (resolved?.isObjectExpression() && binding.referencePaths.some(
|
|
32
|
+
(reference) => !reference.parentPath?.isJSXSpreadAttribute() && !reference.parentPath?.isSpreadElement()
|
|
33
|
+
))
|
|
34
|
+
return void 0;
|
|
35
|
+
return resolved;
|
|
36
|
+
}
|
|
37
|
+
function propValue(path11, name) {
|
|
38
|
+
const readObject = (path12) => {
|
|
39
|
+
const resolved = constantValue(path12);
|
|
40
|
+
if (!resolved?.isObjectExpression()) return { blocked: true };
|
|
41
|
+
for (const property of [...resolved.get("properties")].reverse()) {
|
|
42
|
+
if (property.isSpreadElement()) {
|
|
43
|
+
const found = readObjectBounded(property.get("argument"));
|
|
44
|
+
if (found.value || found.blocked) return found;
|
|
45
|
+
} else if (property.isObjectProperty() || property.isObjectMethod()) {
|
|
46
|
+
if (property.node.computed) return { blocked: true };
|
|
47
|
+
const key = property.node.key;
|
|
48
|
+
if ((BabelTypes.isIdentifier(key) ? key.name : BabelTypes.isStringLiteral(key) ? key.value : "") === name)
|
|
49
|
+
return property.isObjectProperty() ? { value: property.get("value") } : { value: property };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {};
|
|
53
|
+
};
|
|
54
|
+
let objectBudget = MAX_HOPS;
|
|
55
|
+
const readObjectBounded = (path12) => objectBudget-- > 0 ? readObject(path12) : { blocked: true };
|
|
56
|
+
for (const attr of [...path11.get("attributes")].reverse()) {
|
|
57
|
+
if (attr.isJSXAttribute() && BabelTypes.isJSXIdentifier(attr.node.name, { name })) {
|
|
58
|
+
const value = attr.get("value");
|
|
59
|
+
return value.isJSXExpressionContainer() ? unwrap(value.get("expression")) : value.node ? value : void 0;
|
|
60
|
+
}
|
|
61
|
+
if (attr.isJSXSpreadAttribute()) {
|
|
62
|
+
const found = readObjectBounded(attr.get("argument"));
|
|
63
|
+
if (found.value || found.blocked) return found.value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
function importIdentity(path11, depth = 0) {
|
|
69
|
+
if (depth > MAX_HOPS) return void 0;
|
|
70
|
+
path11 = unwrap(path11);
|
|
71
|
+
if (path11.isIdentifier() || path11.isJSXIdentifier()) {
|
|
72
|
+
const binding = path11.scope.getBinding(path11.node.name);
|
|
73
|
+
if (!binding?.constant) return void 0;
|
|
74
|
+
const declaration = binding.path;
|
|
75
|
+
if (declaration.parentPath?.isImportDeclaration()) {
|
|
76
|
+
const module = declaration.parentPath.node.source.value;
|
|
77
|
+
if (declaration.isImportSpecifier()) {
|
|
78
|
+
const imported = declaration.node.imported;
|
|
79
|
+
return { module, imported: BabelTypes.isIdentifier(imported) ? imported.name : imported.value };
|
|
80
|
+
}
|
|
81
|
+
if (declaration.isImportDefaultSpecifier()) return { module, imported: "default" };
|
|
82
|
+
if (declaration.isImportNamespaceSpecifier()) return { module, imported: "*" };
|
|
83
|
+
}
|
|
84
|
+
if (declaration.isVariableDeclarator() && declaration.get("init").node)
|
|
85
|
+
return importIdentity(declaration.get("init"), depth + 1);
|
|
86
|
+
}
|
|
87
|
+
if (path11.isJSXMemberExpression() || path11.isMemberExpression() && !path11.node.computed) {
|
|
88
|
+
const origin = importIdentity(path11.get("object"), depth + 1);
|
|
89
|
+
const key = path11.node.property;
|
|
90
|
+
if (origin && (BabelTypes.isIdentifier(key) || BabelTypes.isJSXIdentifier(key)) && (origin.imported === "*" || origin.module === "react" && origin.imported === "default"))
|
|
91
|
+
return { module: origin.module, imported: key.name };
|
|
92
|
+
}
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
function handlerFunction(path11, depth = 0) {
|
|
96
|
+
if (depth > MAX_HOPS) return void 0;
|
|
97
|
+
path11 = unwrap(path11);
|
|
98
|
+
if (path11.isFunction()) return path11;
|
|
99
|
+
if (path11.isIdentifier()) {
|
|
100
|
+
const binding = path11.scope.getBinding(path11.node.name);
|
|
101
|
+
if (!binding?.constant) return void 0;
|
|
102
|
+
if (binding.path.isFunctionDeclaration()) return binding.path;
|
|
103
|
+
if (binding.path.isVariableDeclarator() && binding.path.get("init").node)
|
|
104
|
+
return handlerFunction(binding.path.get("init"), depth + 1);
|
|
105
|
+
}
|
|
106
|
+
if (path11.isCallExpression()) {
|
|
107
|
+
const origin = importIdentity(path11.get("callee"));
|
|
108
|
+
if (origin?.module === "react" && origin.imported === "useCallback") {
|
|
109
|
+
const callback = path11.get("arguments")[0];
|
|
110
|
+
if (callback) return handlerFunction(callback, depth + 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (path11.isMemberExpression() && !path11.node.computed && BabelTypes.isThisExpression(path11.node.object)) {
|
|
114
|
+
const key = path11.node.property;
|
|
115
|
+
if (!BabelTypes.isIdentifier(key)) return void 0;
|
|
116
|
+
const owner = path11.findParent((p) => p.isClassDeclaration() || p.isClassExpression());
|
|
117
|
+
if (!owner || !(owner.isClassDeclaration() || owner.isClassExpression())) return void 0;
|
|
118
|
+
for (const member of owner.get("body").get("body")) {
|
|
119
|
+
if (!(member.isClassMethod() || member.isClassProperty()) || member.node.computed || member.node.static)
|
|
120
|
+
continue;
|
|
121
|
+
if (!BabelTypes.isIdentifier(member.node.key, { name: key.name })) continue;
|
|
122
|
+
if (member.isClassMethod() && member.node.kind === "method") return member;
|
|
123
|
+
if (member.isClassProperty() && member.get("value").node)
|
|
124
|
+
return handlerFunction(member.get("value"), depth + 1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/extractors/control-evidence.ts
|
|
131
|
+
function symbol(node) {
|
|
132
|
+
if (BabelTypes.isTSAsExpression(node) || BabelTypes.isTSTypeAssertion(node) || BabelTypes.isTSNonNullExpression(node) || BabelTypes.isTSSatisfiesExpression(node))
|
|
133
|
+
return symbol(node.expression);
|
|
134
|
+
if (BabelTypes.isIdentifier(node) || BabelTypes.isJSXIdentifier(node)) return node.name;
|
|
135
|
+
if (BabelTypes.isThisExpression(node)) return "this";
|
|
136
|
+
if (BabelTypes.isMemberExpression(node) && !node.computed || BabelTypes.isJSXMemberExpression(node)) {
|
|
137
|
+
const object = symbol(node.object), property = symbol(node.property);
|
|
138
|
+
return object && property ? `${object}.${property}` : void 0;
|
|
139
|
+
}
|
|
140
|
+
if (BabelTypes.isUnaryExpression(node) && node.operator === "!") {
|
|
141
|
+
const value = symbol(node.argument);
|
|
142
|
+
return value ? `!${value}` : void 0;
|
|
143
|
+
}
|
|
144
|
+
return void 0;
|
|
145
|
+
}
|
|
146
|
+
function attribute(node, name) {
|
|
147
|
+
return node.attributes.find(
|
|
148
|
+
(a) => BabelTypes.isJSXAttribute(a) && BabelTypes.isJSXIdentifier(a.name, { name })
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
var ICON_LIBRARIES = [
|
|
152
|
+
"lucide-react-native",
|
|
153
|
+
"lucide-react",
|
|
154
|
+
"@tamagui/lucide-icons",
|
|
155
|
+
"@expo/vector-icons",
|
|
156
|
+
"@react-native-vector-icons/",
|
|
157
|
+
"react-native-vector-icons/"
|
|
158
|
+
];
|
|
159
|
+
function iconLibrary(module) {
|
|
160
|
+
return ICON_LIBRARIES.some(
|
|
161
|
+
(name) => name.endsWith("/") ? module.startsWith(name) : module === name || module.startsWith(name + "/")
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function add(values, value, max = 12) {
|
|
165
|
+
if (value && value.length <= 240 && values.length < max && !values.includes(value))
|
|
166
|
+
values.push(value);
|
|
167
|
+
}
|
|
168
|
+
function iconName(path11, explicitIcon = false) {
|
|
169
|
+
const origin = importIdentity(path11);
|
|
170
|
+
if (origin && iconLibrary(origin.module) && origin.imported !== "*")
|
|
171
|
+
return origin.imported === "default" ? origin.module : `${origin.module}:${origin.imported}`;
|
|
172
|
+
const name = symbol(path11.node);
|
|
173
|
+
return name && (explicitIcon || /icon/i.test(name)) ? name : void 0;
|
|
174
|
+
}
|
|
175
|
+
function iconsInElement(path11, explicitIcon = false) {
|
|
176
|
+
const name = iconName(path11.get("name"), explicitIcon);
|
|
177
|
+
if (!name) return void 0;
|
|
178
|
+
const glyph = propValue(path11, "name");
|
|
179
|
+
const value = glyph && constantValue(glyph);
|
|
180
|
+
return value?.isStringLiteral() ? `${name}:${value.node.value}` : name;
|
|
181
|
+
}
|
|
182
|
+
function hasInteraction(path11) {
|
|
183
|
+
return ["onPress", "onLongPress", "onClick"].some(
|
|
184
|
+
(name) => attribute(path11.node, name) || propValue(path11, name)
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
function collectPresentationIcons(path11, icons) {
|
|
188
|
+
const namedIconProps = [
|
|
189
|
+
"icon",
|
|
190
|
+
"prefix",
|
|
191
|
+
"suffix",
|
|
192
|
+
"left",
|
|
193
|
+
"right",
|
|
194
|
+
"leadingIcon",
|
|
195
|
+
"trailingIcon",
|
|
196
|
+
"startIcon",
|
|
197
|
+
"endIcon",
|
|
198
|
+
"renderIcon"
|
|
199
|
+
];
|
|
200
|
+
const props = new Set(namedIconProps);
|
|
201
|
+
for (const attr of path11.node.attributes)
|
|
202
|
+
if (BabelTypes.isJSXAttribute(attr) && BabelTypes.isJSXIdentifier(attr.name) && !/^on[A-Z]/.test(attr.name.name))
|
|
203
|
+
props.add(attr.name.name);
|
|
204
|
+
for (const prop of props) {
|
|
205
|
+
const icon = propValue(path11, prop);
|
|
206
|
+
if (icon) {
|
|
207
|
+
const value = constantValue(icon);
|
|
208
|
+
if (value?.isStringLiteral()) {
|
|
209
|
+
if (prop.toLowerCase().includes("icon")) add(icons, value.node.value, 8);
|
|
210
|
+
} else {
|
|
211
|
+
const explicitIcon = prop.toLowerCase() === "icon";
|
|
212
|
+
if (icon.isJSXElement()) {
|
|
213
|
+
if (hasInteraction(icon.get("openingElement"))) continue;
|
|
214
|
+
add(icons, iconsInElement(icon.get("openingElement"), explicitIcon), 8);
|
|
215
|
+
}
|
|
216
|
+
icon.traverse({
|
|
217
|
+
JSXAttribute(attr) {
|
|
218
|
+
if (BabelTypes.isJSXIdentifier(attr.node.name) && /^on[A-Z]/.test(attr.node.name.name))
|
|
219
|
+
attr.skip();
|
|
220
|
+
},
|
|
221
|
+
JSXElement(child) {
|
|
222
|
+
const opening = child.get("openingElement");
|
|
223
|
+
if (hasInteraction(opening)) {
|
|
224
|
+
child.skip();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
add(icons, iconsInElement(opening, explicitIcon), 8);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
if (!icons.length && (namedIconProps.includes(prop) || explicitIcon))
|
|
231
|
+
add(icons, iconName(icon, explicitIcon), 8);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function collectIcons(path11) {
|
|
237
|
+
const icons = [];
|
|
238
|
+
collectPresentationIcons(path11, icons);
|
|
239
|
+
const jsx = path11.parentPath;
|
|
240
|
+
if (jsx.isJSXElement())
|
|
241
|
+
jsx.traverse({
|
|
242
|
+
// JSX mentioned inside an event callback is not a rendered child icon.
|
|
243
|
+
JSXAttribute(attr) {
|
|
244
|
+
attr.skip();
|
|
245
|
+
},
|
|
246
|
+
JSXElement(child) {
|
|
247
|
+
const opening = child.get("openingElement");
|
|
248
|
+
if (hasInteraction(opening)) {
|
|
249
|
+
child.skip();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
add(icons, iconsInElement(opening), 8);
|
|
253
|
+
collectPresentationIcons(opening, icons);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return icons;
|
|
257
|
+
}
|
|
258
|
+
function conditionsAt(path11) {
|
|
259
|
+
const conditions = [];
|
|
260
|
+
let child = path11;
|
|
261
|
+
for (let parent = child.parentPath; parent && !parent.isFunction(); child = parent, parent = parent.parentPath) {
|
|
262
|
+
let test;
|
|
263
|
+
let negated = false;
|
|
264
|
+
if (parent.isLogicalExpression() && parent.node.right === child.node) {
|
|
265
|
+
if (parent.node.operator === "&&") test = parent.node.left;
|
|
266
|
+
if (parent.node.operator === "||") {
|
|
267
|
+
test = parent.node.left;
|
|
268
|
+
negated = true;
|
|
269
|
+
}
|
|
270
|
+
} else if (parent.isConditionalExpression() && parent.node.test !== child.node) {
|
|
271
|
+
test = parent.node.test;
|
|
272
|
+
negated = parent.node.alternate === child.node;
|
|
273
|
+
} else if (parent.isIfStatement() && parent.node.test !== child.node) {
|
|
274
|
+
test = parent.node.test;
|
|
275
|
+
negated = parent.node.alternate === child.node;
|
|
276
|
+
}
|
|
277
|
+
const name = symbol(test);
|
|
278
|
+
if (name) add(conditions, negated ? name.startsWith("!") ? name.slice(1) : `!${name}` : name);
|
|
279
|
+
}
|
|
280
|
+
return conditions;
|
|
281
|
+
}
|
|
282
|
+
function collectHandlerEvidence(expression, evidence) {
|
|
283
|
+
const seen = /* @__PURE__ */ new Set();
|
|
284
|
+
let budget = 100;
|
|
285
|
+
const visit = (path11, depth) => {
|
|
286
|
+
if (depth > 4 || budget-- <= 0) return;
|
|
287
|
+
const fn = handlerFunction(path11);
|
|
288
|
+
if (!fn || seen.has(fn.node)) return;
|
|
289
|
+
seen.add(fn.node);
|
|
290
|
+
fn.traverse({
|
|
291
|
+
// Ignore uncalled helper definitions; inline callbacks remain source evidence.
|
|
292
|
+
Function(nested) {
|
|
293
|
+
if (!nested.parentPath.isCallExpression() && !nested.parentPath.isObjectProperty())
|
|
294
|
+
nested.skip();
|
|
295
|
+
},
|
|
296
|
+
CallExpression(call) {
|
|
297
|
+
if (budget-- <= 0) {
|
|
298
|
+
call.skip();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
add(evidence.calls, symbol(call.node.callee));
|
|
302
|
+
for (const arg of call.node.arguments) add(evidence.argumentBindings, symbol(arg));
|
|
303
|
+
visit(call.get("callee"), depth + 1);
|
|
304
|
+
if (!BabelTypes.isMemberExpression(call.node.callee) || call.node.callee.computed || !BabelTypes.isIdentifier(call.node.callee.property, { name: "alert" }))
|
|
305
|
+
return;
|
|
306
|
+
const callee = call.get("callee");
|
|
307
|
+
if (!callee.isMemberExpression()) return;
|
|
308
|
+
const origin = importIdentity(callee.get("object"));
|
|
309
|
+
if (origin?.module !== "react-native" || origin.imported !== "Alert") return;
|
|
310
|
+
const options = call.node.arguments[2];
|
|
311
|
+
const destructiveOption = BabelTypes.isArrayExpression(options) && options.elements.some(
|
|
312
|
+
(option) => BabelTypes.isObjectExpression(option) && option.properties.some(
|
|
313
|
+
(p) => BabelTypes.isObjectProperty(p) && !p.computed && symbol(p.key) === "style" && BabelTypes.isStringLiteral(p.value, { value: "destructive" })
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
if (destructiveOption)
|
|
317
|
+
evidence.nativeConfirmation = {
|
|
318
|
+
title: symbol(call.node.arguments[0]),
|
|
319
|
+
destructiveOption
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
visit(unwrap(expression), 0);
|
|
325
|
+
}
|
|
326
|
+
function extractControlEvidence(ast, source, file) {
|
|
327
|
+
const candidates = [];
|
|
328
|
+
const sourceHash = createHash("sha256").update(source).digest("hex");
|
|
329
|
+
traverse5(ast, {
|
|
330
|
+
JSXOpeningElement(path11) {
|
|
331
|
+
const node = path11.node;
|
|
332
|
+
if (attribute(node, "__appilotsControl")) return;
|
|
333
|
+
const value = propValue(path11, "onPress");
|
|
334
|
+
if (!value && !attribute(node, "onPress") || value?.isNullLiteral() || value?.isJSXEmptyExpression())
|
|
335
|
+
return;
|
|
336
|
+
const icons = collectIcons(path11);
|
|
337
|
+
if (!icons.length || node.end == null) return;
|
|
338
|
+
const evidence = {
|
|
339
|
+
version: 1,
|
|
340
|
+
siteId: createHash("sha256").update(`${file}:${sourceHash}:${node.start}`).digest("hex").slice(0, 20),
|
|
341
|
+
component: symbol(node.name) ?? "unknown",
|
|
342
|
+
icons,
|
|
343
|
+
...value && symbol(value.node) ? { handler: symbol(value.node) } : {},
|
|
344
|
+
calls: [],
|
|
345
|
+
argumentBindings: [],
|
|
346
|
+
conditions: conditionsAt(path11)
|
|
347
|
+
};
|
|
348
|
+
if (value) collectHandlerEvidence(value, evidence);
|
|
349
|
+
candidates.push({ evidence, sourceHash, offset: node.end - (node.selfClosing ? 2 : 1) });
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
return candidates;
|
|
353
|
+
}
|
|
17
354
|
function byCodeUnit(a, b) {
|
|
18
355
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
19
356
|
}
|
|
@@ -135,7 +472,7 @@ function classifyJsxComponent(name, element) {
|
|
|
135
472
|
}
|
|
136
473
|
function collectFunctions(ast) {
|
|
137
474
|
const handlers = /* @__PURE__ */ new Map();
|
|
138
|
-
|
|
475
|
+
traverse5(ast, {
|
|
139
476
|
FunctionDeclaration: (nodePath) => {
|
|
140
477
|
if (nodePath.node.id?.name) handlers.set(nodePath.node.id.name, nodePath.node);
|
|
141
478
|
},
|
|
@@ -238,7 +575,7 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
|
|
|
238
575
|
}
|
|
239
576
|
};
|
|
240
577
|
if (fn.body) {
|
|
241
|
-
|
|
578
|
+
traverse5(fn.body, {
|
|
242
579
|
noScope: true,
|
|
243
580
|
enter: (nodePath) => inspectNode(nodePath.node)
|
|
244
581
|
});
|
|
@@ -266,7 +603,7 @@ function setterToStateName(setterName) {
|
|
|
266
603
|
}
|
|
267
604
|
function extractNavigationCalls(ast) {
|
|
268
605
|
const calls = [];
|
|
269
|
-
|
|
606
|
+
traverse5(ast, {
|
|
270
607
|
noScope: !BabelTypes.isFile(ast),
|
|
271
608
|
CallExpression: (nodePath) => {
|
|
272
609
|
const node = nodePath.node;
|
|
@@ -390,6 +727,9 @@ function sanitisePermissionsObject(obj) {
|
|
|
390
727
|
if (typeof obj.isPii === "boolean") {
|
|
391
728
|
out.isPii = obj.isPii;
|
|
392
729
|
}
|
|
730
|
+
if (typeof obj.assessment === "boolean") {
|
|
731
|
+
out.assessment = obj.assessment;
|
|
732
|
+
}
|
|
393
733
|
if (Array.isArray(obj.blockedActions)) {
|
|
394
734
|
const ids = obj.blockedActions.filter((x) => typeof x === "string" && !!x);
|
|
395
735
|
if (ids.length > 0) out.blockedActions = ids;
|
|
@@ -417,6 +757,7 @@ var ScreenAnalyzer = class {
|
|
|
417
757
|
routeTargetFiles;
|
|
418
758
|
/** §D: Count of screens filtered out in strict mode (available after analyze()) */
|
|
419
759
|
screensFilteredOut = 0;
|
|
760
|
+
controlEvidenceFiles = {};
|
|
420
761
|
constructor(config, options) {
|
|
421
762
|
this.config = config;
|
|
422
763
|
this.routeTargetFiles = options?.routeTargetFiles ?? /* @__PURE__ */ new Set();
|
|
@@ -517,6 +858,11 @@ var ScreenAnalyzer = class {
|
|
|
517
858
|
title: registerScreenMeta?.title,
|
|
518
859
|
description: registerScreenMeta?.description,
|
|
519
860
|
components,
|
|
861
|
+
controlCandidates: extractControlEvidence(
|
|
862
|
+
ast,
|
|
863
|
+
source,
|
|
864
|
+
path2__default.relative(this.config.rootDir, filePath)
|
|
865
|
+
),
|
|
520
866
|
forms,
|
|
521
867
|
actions,
|
|
522
868
|
navigationTargets,
|
|
@@ -527,6 +873,8 @@ var ScreenAnalyzer = class {
|
|
|
527
873
|
...permissionsFromJsDoc.isPii ? { isPii: true } : {}
|
|
528
874
|
} : {}
|
|
529
875
|
};
|
|
876
|
+
if (descriptor.controlCandidates?.length)
|
|
877
|
+
this.controlEvidenceFiles[path2__default.relative(this.config.rootDir, filePath).split(path2__default.sep).join("/")] = descriptor.controlCandidates;
|
|
530
878
|
descriptor.__hasRegisterScreen = hasRegisterScreenCall;
|
|
531
879
|
return descriptor;
|
|
532
880
|
}
|
|
@@ -536,7 +884,7 @@ var ScreenAnalyzer = class {
|
|
|
536
884
|
*/
|
|
537
885
|
detectRegisterScreenCall(ast) {
|
|
538
886
|
let found = false;
|
|
539
|
-
|
|
887
|
+
traverse5(ast, {
|
|
540
888
|
CallExpression: (nodePath) => {
|
|
541
889
|
if (found) return;
|
|
542
890
|
const callee = nodePath.node.callee;
|
|
@@ -553,7 +901,7 @@ var ScreenAnalyzer = class {
|
|
|
553
901
|
*/
|
|
554
902
|
extractRegisterScreenMetadata(ast) {
|
|
555
903
|
let metadata = null;
|
|
556
|
-
|
|
904
|
+
traverse5(ast, {
|
|
557
905
|
CallExpression: (nodePath) => {
|
|
558
906
|
const callee = nodePath.node.callee;
|
|
559
907
|
if (BabelTypes.isIdentifier(callee) && callee.name === "registerScreen" || BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.property) && callee.property.name === "registerScreen") {
|
|
@@ -850,7 +1198,7 @@ var ScreenAnalyzer = class {
|
|
|
850
1198
|
*/
|
|
851
1199
|
extractDefaultComponentName(ast) {
|
|
852
1200
|
let componentName = "";
|
|
853
|
-
|
|
1201
|
+
traverse5(ast, {
|
|
854
1202
|
ExportDefaultDeclaration: (nodePath) => {
|
|
855
1203
|
const declaration = nodePath.node.declaration;
|
|
856
1204
|
if (BabelTypes.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
@@ -875,7 +1223,7 @@ var ScreenAnalyzer = class {
|
|
|
875
1223
|
*/
|
|
876
1224
|
extractNavigationTargets(ast) {
|
|
877
1225
|
const targets = /* @__PURE__ */ new Set();
|
|
878
|
-
|
|
1226
|
+
traverse5(ast, {
|
|
879
1227
|
CallExpression: (nodePath) => {
|
|
880
1228
|
const callee = nodePath.node.callee;
|
|
881
1229
|
if (BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.object) && callee.object.name === "navigation" && BabelTypes.isIdentifier(callee.property) && callee.property.name === "navigate") {
|
|
@@ -894,7 +1242,7 @@ var ScreenAnalyzer = class {
|
|
|
894
1242
|
extractForms(ast) {
|
|
895
1243
|
const forms = [];
|
|
896
1244
|
const fields = /* @__PURE__ */ new Map();
|
|
897
|
-
|
|
1245
|
+
traverse5(ast, {
|
|
898
1246
|
JSXOpeningElement: (nodePath) => {
|
|
899
1247
|
const element = nodePath.node;
|
|
900
1248
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -1015,7 +1363,7 @@ var ScreenAnalyzer = class {
|
|
|
1015
1363
|
extractComponents(ast) {
|
|
1016
1364
|
const components = [];
|
|
1017
1365
|
const seen = /* @__PURE__ */ new Set();
|
|
1018
|
-
|
|
1366
|
+
traverse5(ast, {
|
|
1019
1367
|
JSXOpeningElement: (nodePath) => {
|
|
1020
1368
|
const element = nodePath.node;
|
|
1021
1369
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -1079,7 +1427,7 @@ var ScreenAnalyzer = class {
|
|
|
1079
1427
|
const actionLabels = new Map(
|
|
1080
1428
|
actions.filter((a) => a.label).map((a) => [this.normalizeLabel(a.label), a])
|
|
1081
1429
|
);
|
|
1082
|
-
|
|
1430
|
+
traverse5(ast, {
|
|
1083
1431
|
JSXOpeningElement: (nodePath) => {
|
|
1084
1432
|
const element = nodePath.node;
|
|
1085
1433
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -1148,7 +1496,7 @@ var ScreenAnalyzer = class {
|
|
|
1148
1496
|
}
|
|
1149
1497
|
collectButtonHandlersByLabel(ast) {
|
|
1150
1498
|
const out = /* @__PURE__ */ new Map();
|
|
1151
|
-
|
|
1499
|
+
traverse5(ast, {
|
|
1152
1500
|
JSXOpeningElement: (nodePath) => {
|
|
1153
1501
|
const element = nodePath.node;
|
|
1154
1502
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
@@ -1230,7 +1578,7 @@ var ScreenAnalyzer = class {
|
|
|
1230
1578
|
}
|
|
1231
1579
|
};
|
|
1232
1580
|
if (fn.body) {
|
|
1233
|
-
|
|
1581
|
+
traverse5(
|
|
1234
1582
|
fn.body,
|
|
1235
1583
|
{
|
|
1236
1584
|
noScope: true,
|
|
@@ -1427,7 +1775,7 @@ var ScreenAnalyzer = class {
|
|
|
1427
1775
|
extractCollections(ast) {
|
|
1428
1776
|
const renderItemFns = this.collectRenderItemFunctions(ast);
|
|
1429
1777
|
const collections = [];
|
|
1430
|
-
|
|
1778
|
+
traverse5(ast, {
|
|
1431
1779
|
JSXOpeningElement: (nodePath) => {
|
|
1432
1780
|
const element = nodePath.node;
|
|
1433
1781
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
@@ -1464,7 +1812,7 @@ var ScreenAnalyzer = class {
|
|
|
1464
1812
|
}
|
|
1465
1813
|
collectRenderItemFunctions(ast) {
|
|
1466
1814
|
const out = /* @__PURE__ */ new Map();
|
|
1467
|
-
|
|
1815
|
+
traverse5(ast, {
|
|
1468
1816
|
VariableDeclarator: (nodePath) => {
|
|
1469
1817
|
if (!BabelTypes.isIdentifier(nodePath.node.id)) return;
|
|
1470
1818
|
const init = nodePath.node.init;
|
|
@@ -1507,7 +1855,7 @@ var ScreenAnalyzer = class {
|
|
|
1507
1855
|
}
|
|
1508
1856
|
extractRowAction(fn) {
|
|
1509
1857
|
let action;
|
|
1510
|
-
|
|
1858
|
+
traverse5(
|
|
1511
1859
|
fn.body,
|
|
1512
1860
|
{
|
|
1513
1861
|
noScope: true,
|
|
@@ -1560,7 +1908,7 @@ var ScreenAnalyzer = class {
|
|
|
1560
1908
|
} else if (BabelTypes.isIdentifier(firstParam)) {
|
|
1561
1909
|
itemNames.add(firstParam.name);
|
|
1562
1910
|
}
|
|
1563
|
-
|
|
1911
|
+
traverse5(
|
|
1564
1912
|
fn.body,
|
|
1565
1913
|
{
|
|
1566
1914
|
noScope: true,
|
|
@@ -1602,7 +1950,7 @@ var ScreenAnalyzer = class {
|
|
|
1602
1950
|
inferSearchField(ast, dataSource) {
|
|
1603
1951
|
if (!dataSource) return void 0;
|
|
1604
1952
|
let queryBinding;
|
|
1605
|
-
|
|
1953
|
+
traverse5(ast, {
|
|
1606
1954
|
CallExpression: (nodePath) => {
|
|
1607
1955
|
const node = nodePath.node;
|
|
1608
1956
|
if (!BabelTypes.isMemberExpression(node.callee)) return;
|
|
@@ -1613,7 +1961,7 @@ var ScreenAnalyzer = class {
|
|
|
1613
1961
|
const fn = node.arguments[0];
|
|
1614
1962
|
if (!BabelTypes.isArrowFunctionExpression(fn) && !BabelTypes.isFunctionExpression(fn))
|
|
1615
1963
|
return;
|
|
1616
|
-
|
|
1964
|
+
traverse5(
|
|
1617
1965
|
fn.body,
|
|
1618
1966
|
{
|
|
1619
1967
|
noScope: true,
|
|
@@ -1655,7 +2003,7 @@ var ScreenAnalyzer = class {
|
|
|
1655
2003
|
}
|
|
1656
2004
|
};
|
|
1657
2005
|
var EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
1658
|
-
var
|
|
2006
|
+
var MAX_HOPS2 = 8;
|
|
1659
2007
|
var ModuleGraph = class {
|
|
1660
2008
|
asts = /* @__PURE__ */ new Map();
|
|
1661
2009
|
resolved = /* @__PURE__ */ new Map();
|
|
@@ -1774,7 +2122,7 @@ var ModuleGraph = class {
|
|
|
1774
2122
|
* Devolve o arquivo e o nome sob o qual ele é definido lá.
|
|
1775
2123
|
*/
|
|
1776
2124
|
resolveBinding(file, name, hops = 0) {
|
|
1777
|
-
if (hops >
|
|
2125
|
+
if (hops > MAX_HOPS2) return null;
|
|
1778
2126
|
if (this.topLevelInit(file, name) !== null) return { file, name };
|
|
1779
2127
|
const binding = this.imports(file).get(name);
|
|
1780
2128
|
if (binding) {
|
|
@@ -1828,7 +2176,7 @@ var ModuleGraph = class {
|
|
|
1828
2176
|
* com interpolação nem valor calculado, de propósito.
|
|
1829
2177
|
*/
|
|
1830
2178
|
stringConstant(file, node, hops = 0) {
|
|
1831
|
-
if (!node || hops >
|
|
2179
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
1832
2180
|
if (BabelTypes.isStringLiteral(node)) return node.value;
|
|
1833
2181
|
if (BabelTypes.isTemplateLiteral(node)) {
|
|
1834
2182
|
return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? null : null;
|
|
@@ -1857,7 +2205,7 @@ var ModuleGraph = class {
|
|
|
1857
2205
|
* que é como o `pocketpal` monta todas as telas do Drawer).
|
|
1858
2206
|
*/
|
|
1859
2207
|
componentFile(file, node, hops = 0) {
|
|
1860
|
-
if (!node || hops >
|
|
2208
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
1861
2209
|
if (BabelTypes.isCallExpression(node)) {
|
|
1862
2210
|
for (const arg of node.arguments) {
|
|
1863
2211
|
if (BabelTypes.isIdentifier(arg) || BabelTypes.isMemberExpression(arg)) {
|
|
@@ -2053,7 +2401,7 @@ function workspaceGlobs(dir) {
|
|
|
2053
2401
|
}
|
|
2054
2402
|
function expandWorkspaceGlobs(root, globs) {
|
|
2055
2403
|
const out = [];
|
|
2056
|
-
const
|
|
2404
|
+
const add2 = (dir) => {
|
|
2057
2405
|
const pkgPath = path2__default.join(dir, "package.json");
|
|
2058
2406
|
if (!existsSync(pkgPath)) return;
|
|
2059
2407
|
try {
|
|
@@ -2074,12 +2422,12 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
2074
2422
|
for (const entry of entries) {
|
|
2075
2423
|
const dir = path2__default.join(parent, entry);
|
|
2076
2424
|
try {
|
|
2077
|
-
if (statSync(dir).isDirectory())
|
|
2425
|
+
if (statSync(dir).isDirectory()) add2(dir);
|
|
2078
2426
|
} catch {
|
|
2079
2427
|
}
|
|
2080
2428
|
}
|
|
2081
2429
|
} else if (!glob.includes("*")) {
|
|
2082
|
-
|
|
2430
|
+
add2(path2__default.join(root, glob));
|
|
2083
2431
|
}
|
|
2084
2432
|
}
|
|
2085
2433
|
return out.sort((a, b) => b.name.length - a.name.length);
|
|
@@ -2399,7 +2747,7 @@ var NavigationAnalyzer = class {
|
|
|
2399
2747
|
const ast = this.graph.parse(filePath);
|
|
2400
2748
|
if (!ast) return [];
|
|
2401
2749
|
const out = /* @__PURE__ */ new Map();
|
|
2402
|
-
|
|
2750
|
+
traverse5(ast, {
|
|
2403
2751
|
CallExpression: (nodePath) => {
|
|
2404
2752
|
const callee = nodePath.node.callee;
|
|
2405
2753
|
if (!BabelTypes.isIdentifier(callee)) return;
|
|
@@ -2560,7 +2908,7 @@ var NavigationAnalyzer = class {
|
|
|
2560
2908
|
const ast = this.graph.parse(filePath);
|
|
2561
2909
|
if (!ast) return [];
|
|
2562
2910
|
const found = /* @__PURE__ */ new Map();
|
|
2563
|
-
|
|
2911
|
+
traverse5(ast, {
|
|
2564
2912
|
JSXElement: (nodePath) => {
|
|
2565
2913
|
const { node } = nodePath;
|
|
2566
2914
|
const openingElement = node.openingElement;
|
|
@@ -2632,7 +2980,7 @@ var NavigationAnalyzer = class {
|
|
|
2632
2980
|
if (!ast) return [];
|
|
2633
2981
|
const byNavigator = /* @__PURE__ */ new Map();
|
|
2634
2982
|
const resolved = /* @__PURE__ */ new Map();
|
|
2635
|
-
|
|
2983
|
+
traverse5(ast, {
|
|
2636
2984
|
JSXElement: (nodePath) => {
|
|
2637
2985
|
const name = nodePath.node.openingElement.name;
|
|
2638
2986
|
if (!BabelTypes.isJSXMemberExpression(name) || !BabelTypes.isJSXIdentifier(name.object)) return;
|
|
@@ -2865,7 +3213,7 @@ var NavigationAnalyzer = class {
|
|
|
2865
3213
|
...this.config.parserPlugins || []
|
|
2866
3214
|
]
|
|
2867
3215
|
});
|
|
2868
|
-
|
|
3216
|
+
traverse5(ast, {
|
|
2869
3217
|
TSTypeAliasDeclaration: (nodePath) => {
|
|
2870
3218
|
const { node } = nodePath;
|
|
2871
3219
|
const typeName = node.id.name;
|
|
@@ -2938,7 +3286,7 @@ var NavigationAnalyzer = class {
|
|
|
2938
3286
|
if (type.type === "TSUndefinedKeyword") return "undefined";
|
|
2939
3287
|
if (type.type === "TSNullKeyword") return "null";
|
|
2940
3288
|
if (type.type === "TSUnionType") {
|
|
2941
|
-
return type.types.map((
|
|
3289
|
+
return type.types.map((t15) => this.typeToString(t15)).join(" | ");
|
|
2942
3290
|
}
|
|
2943
3291
|
if (type.type === "TSTypeLiteral") {
|
|
2944
3292
|
return "object";
|
|
@@ -2959,7 +3307,7 @@ var NavigationAnalyzer = class {
|
|
|
2959
3307
|
/** Attach parsed type params to navigator screens */
|
|
2960
3308
|
attachParamsToNavigators(navigators, types) {
|
|
2961
3309
|
for (const navigator of navigators) {
|
|
2962
|
-
const matchingType = types.find((
|
|
3310
|
+
const matchingType = types.find((t15) => t15.type === navigator.type);
|
|
2963
3311
|
if (matchingType) {
|
|
2964
3312
|
for (const screen of navigator.screens) {
|
|
2965
3313
|
const screenParams = matchingType.paramEntries.get(screen.name);
|
|
@@ -3048,7 +3396,7 @@ var ComponentAnalyzer = class {
|
|
|
3048
3396
|
plugins: ["jsx", "typescript", ["decorators", { decoratorsBeforeExport: true }]]
|
|
3049
3397
|
});
|
|
3050
3398
|
const components = [];
|
|
3051
|
-
|
|
3399
|
+
traverse5(ast, {
|
|
3052
3400
|
JSXElement: (path11) => {
|
|
3053
3401
|
const component = this.extractComponentFromJSXElement(path11.node);
|
|
3054
3402
|
if (component) {
|
|
@@ -3176,12 +3524,12 @@ var FormAnalyzer = class {
|
|
|
3176
3524
|
this.stateVariables.clear();
|
|
3177
3525
|
this.inputElements = [];
|
|
3178
3526
|
this.submitButtons = [];
|
|
3179
|
-
|
|
3527
|
+
traverse5(ast, {
|
|
3180
3528
|
CallExpression: (path11) => {
|
|
3181
3529
|
this.extractStateVariables(path11.node);
|
|
3182
3530
|
}
|
|
3183
3531
|
});
|
|
3184
|
-
|
|
3532
|
+
traverse5(ast, {
|
|
3185
3533
|
JSXElement: (path11) => {
|
|
3186
3534
|
this.extractFormElements(path11.node);
|
|
3187
3535
|
}
|
|
@@ -3217,23 +3565,23 @@ var FormAnalyzer = class {
|
|
|
3217
3565
|
for (const attr of openingElement.attributes) {
|
|
3218
3566
|
if (BabelTypes.isJSXAttribute(attr) && BabelTypes.isJSXIdentifier(attr.name)) {
|
|
3219
3567
|
const propName = attr.name.name;
|
|
3220
|
-
const
|
|
3568
|
+
const propValue2 = this.extractAttributeValue(attr.value);
|
|
3221
3569
|
switch (propName) {
|
|
3222
3570
|
case "label":
|
|
3223
|
-
info.label =
|
|
3571
|
+
info.label = propValue2;
|
|
3224
3572
|
break;
|
|
3225
3573
|
case "placeholder":
|
|
3226
|
-
info.placeholder =
|
|
3574
|
+
info.placeholder = propValue2;
|
|
3227
3575
|
break;
|
|
3228
3576
|
case "keyboardType":
|
|
3229
|
-
info.keyboardType =
|
|
3577
|
+
info.keyboardType = propValue2;
|
|
3230
3578
|
break;
|
|
3231
3579
|
case "testID":
|
|
3232
|
-
info.testID =
|
|
3580
|
+
info.testID = propValue2;
|
|
3233
3581
|
break;
|
|
3234
3582
|
case "appilotsId":
|
|
3235
|
-
info.appilotsId =
|
|
3236
|
-
if (
|
|
3583
|
+
info.appilotsId = propValue2;
|
|
3584
|
+
if (propValue2) info.varName = propValue2;
|
|
3237
3585
|
break;
|
|
3238
3586
|
case "value":
|
|
3239
3587
|
if (attr.value && BabelTypes.isJSXExpressionContainer(attr.value) && BabelTypes.isIdentifier(attr.value.expression)) {
|
|
@@ -3282,7 +3630,7 @@ var FormAnalyzer = class {
|
|
|
3282
3630
|
}
|
|
3283
3631
|
extractValidationRules(ast) {
|
|
3284
3632
|
const rules = {};
|
|
3285
|
-
|
|
3633
|
+
traverse5(ast, {
|
|
3286
3634
|
IfStatement: (path11) => {
|
|
3287
3635
|
const test = path11.node.test;
|
|
3288
3636
|
const rule = this.extractRuleFromCondition(test);
|
|
@@ -3436,7 +3784,7 @@ async function composeAffordances(options) {
|
|
|
3436
3784
|
const exclusive = children.filter((c) => (fanIn.get(c) ?? 0) <= MAX_FAN_IN);
|
|
3437
3785
|
if (exclusive.length === 0) continue;
|
|
3438
3786
|
const actionIds = new Set(screen.actions.map((a) => a.id));
|
|
3439
|
-
const targetIds = new Set((screen.targets ?? []).map((
|
|
3787
|
+
const targetIds = new Set((screen.targets ?? []).map((t15) => t15.id));
|
|
3440
3788
|
const formIds = new Set(screen.forms.map((f) => f.id));
|
|
3441
3789
|
let gained = false;
|
|
3442
3790
|
for (const file of exclusive) {
|
|
@@ -3578,6 +3926,7 @@ var ReactNativePlatformAnalyzer = class {
|
|
|
3578
3926
|
});
|
|
3579
3927
|
return {
|
|
3580
3928
|
screens: enrichedScreens,
|
|
3929
|
+
controlEvidenceFiles: screenAnalyzer.controlEvidenceFiles,
|
|
3581
3930
|
navigation,
|
|
3582
3931
|
analyzedFiles: screenFiles.length,
|
|
3583
3932
|
...screenAnalyzer.screensFilteredOut > 0 ? { screensFilteredOut: screenAnalyzer.screensFilteredOut } : {},
|
|
@@ -3842,7 +4191,7 @@ function extractWebNavigationCalls(ast) {
|
|
|
3842
4191
|
calls.push({ method: "navigate", targetPath: node.right.value });
|
|
3843
4192
|
}
|
|
3844
4193
|
};
|
|
3845
|
-
|
|
4194
|
+
traverse5(ast, {
|
|
3846
4195
|
noScope: !BabelTypes.isFile(ast),
|
|
3847
4196
|
enter: (nodePath) => inspect(nodePath.node)
|
|
3848
4197
|
});
|
|
@@ -3964,7 +4313,7 @@ var WebScreenAnalyzer = class {
|
|
|
3964
4313
|
// ── registerScreen ────────────────────────────────────────────────
|
|
3965
4314
|
detectRegisterScreenCall(ast) {
|
|
3966
4315
|
let found = false;
|
|
3967
|
-
|
|
4316
|
+
traverse5(ast, {
|
|
3968
4317
|
CallExpression: (nodePath) => {
|
|
3969
4318
|
if (found) return;
|
|
3970
4319
|
if (isRegisterScreenCallee(nodePath.node.callee)) {
|
|
@@ -3977,7 +4326,7 @@ var WebScreenAnalyzer = class {
|
|
|
3977
4326
|
}
|
|
3978
4327
|
extractRegisterScreenMetadata(ast) {
|
|
3979
4328
|
let plain = null;
|
|
3980
|
-
|
|
4329
|
+
traverse5(ast, {
|
|
3981
4330
|
CallExpression: (nodePath) => {
|
|
3982
4331
|
if (!isRegisterScreenCallee(nodePath.node.callee)) return;
|
|
3983
4332
|
const arg = nodePath.node.arguments[0];
|
|
@@ -4025,7 +4374,7 @@ var WebScreenAnalyzer = class {
|
|
|
4025
4374
|
extractComponentName(ast) {
|
|
4026
4375
|
let defaultName = "";
|
|
4027
4376
|
let firstExported = "";
|
|
4028
|
-
|
|
4377
|
+
traverse5(ast, {
|
|
4029
4378
|
ExportDefaultDeclaration: (nodePath) => {
|
|
4030
4379
|
const declaration = nodePath.node.declaration;
|
|
4031
4380
|
if (BabelTypes.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
@@ -4054,7 +4403,7 @@ var WebScreenAnalyzer = class {
|
|
|
4054
4403
|
extractComponents(ast) {
|
|
4055
4404
|
const components = [];
|
|
4056
4405
|
const seen = /* @__PURE__ */ new Set();
|
|
4057
|
-
|
|
4406
|
+
traverse5(ast, {
|
|
4058
4407
|
JSXOpeningElement: (nodePath) => {
|
|
4059
4408
|
const element = nodePath.node;
|
|
4060
4409
|
const name = getJsxElementName(element);
|
|
@@ -4075,7 +4424,7 @@ var WebScreenAnalyzer = class {
|
|
|
4075
4424
|
// ── <label htmlFor> association ───────────────────────────────────
|
|
4076
4425
|
collectHtmlForLabels(ast) {
|
|
4077
4426
|
const labels = /* @__PURE__ */ new Map();
|
|
4078
|
-
|
|
4427
|
+
traverse5(ast, {
|
|
4079
4428
|
JSXElement: (nodePath) => {
|
|
4080
4429
|
const element = nodePath.node;
|
|
4081
4430
|
if (getJsxElementName(element.openingElement) !== "label") return;
|
|
@@ -4118,7 +4467,7 @@ var WebScreenAnalyzer = class {
|
|
|
4118
4467
|
formBuckets.set(formElement, bucket);
|
|
4119
4468
|
return bucket;
|
|
4120
4469
|
};
|
|
4121
|
-
|
|
4470
|
+
traverse5(ast, {
|
|
4122
4471
|
JSXElement: (nodePath) => {
|
|
4123
4472
|
const element = nodePath.node;
|
|
4124
4473
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -4135,7 +4484,7 @@ var WebScreenAnalyzer = class {
|
|
|
4135
4484
|
if (!bucket.fields.has(field.name)) bucket.fields.set(field.name, field);
|
|
4136
4485
|
}
|
|
4137
4486
|
});
|
|
4138
|
-
|
|
4487
|
+
traverse5(ast, {
|
|
4139
4488
|
JSXElement: (nodePath) => {
|
|
4140
4489
|
const element = nodePath.node;
|
|
4141
4490
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -4313,7 +4662,7 @@ var WebScreenAnalyzer = class {
|
|
|
4313
4662
|
const actionLabels = new Map(
|
|
4314
4663
|
actions.filter((a) => a.label).map((a) => [normalizeLabel(a.label), a])
|
|
4315
4664
|
);
|
|
4316
|
-
|
|
4665
|
+
traverse5(ast, {
|
|
4317
4666
|
JSXElement: (nodePath) => {
|
|
4318
4667
|
const element = nodePath.node;
|
|
4319
4668
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -4544,7 +4893,7 @@ var WebScreenAnalyzer = class {
|
|
|
4544
4893
|
for (const call of extractWebNavigationCalls(ast)) {
|
|
4545
4894
|
if (call.targetPath && call.targetPath.startsWith("/")) targets.add(call.targetPath);
|
|
4546
4895
|
}
|
|
4547
|
-
|
|
4896
|
+
traverse5(ast, {
|
|
4548
4897
|
JSXOpeningElement: (nodePath) => {
|
|
4549
4898
|
const element = nodePath.node;
|
|
4550
4899
|
const name = getJsxElementName(element);
|
|
@@ -4564,7 +4913,7 @@ var WebScreenAnalyzer = class {
|
|
|
4564
4913
|
extractCollections(ast) {
|
|
4565
4914
|
const collections = [];
|
|
4566
4915
|
const seen = /* @__PURE__ */ new Set();
|
|
4567
|
-
|
|
4916
|
+
traverse5(ast, {
|
|
4568
4917
|
JSXExpressionContainer: (nodePath) => {
|
|
4569
4918
|
const expr = nodePath.node.expression;
|
|
4570
4919
|
if (!BabelTypes.isCallExpression(expr) || !BabelTypes.isMemberExpression(expr.callee) || !BabelTypes.isIdentifier(expr.callee.object) || !BabelTypes.isIdentifier(expr.callee.property) || expr.callee.property.name !== "map") {
|
|
@@ -4676,7 +5025,7 @@ function firstCalledFunctionName(node) {
|
|
|
4676
5025
|
}
|
|
4677
5026
|
function containsWindowConfirm(body) {
|
|
4678
5027
|
let found = false;
|
|
4679
|
-
|
|
5028
|
+
traverse5(
|
|
4680
5029
|
body,
|
|
4681
5030
|
{
|
|
4682
5031
|
noScope: true,
|
|
@@ -4728,7 +5077,7 @@ function collectionItemNames(callback) {
|
|
|
4728
5077
|
function collectionDisplayFields(callback, itemNames) {
|
|
4729
5078
|
const fields = /* @__PURE__ */ new Set();
|
|
4730
5079
|
if (!callback.body) return [];
|
|
4731
|
-
|
|
5080
|
+
traverse5(
|
|
4732
5081
|
callback.body,
|
|
4733
5082
|
{
|
|
4734
5083
|
noScope: true,
|
|
@@ -4745,7 +5094,7 @@ function collectionDisplayFields(callback, itemNames) {
|
|
|
4745
5094
|
function collectionKeyField(callback, itemNames) {
|
|
4746
5095
|
let keyField;
|
|
4747
5096
|
if (!callback.body) return void 0;
|
|
4748
|
-
|
|
5097
|
+
traverse5(
|
|
4749
5098
|
callback.body,
|
|
4750
5099
|
{
|
|
4751
5100
|
noScope: true,
|
|
@@ -4888,7 +5237,7 @@ var WebNavigationAnalyzer = class {
|
|
|
4888
5237
|
if (BabelTypes.isJSXElement(child)) visitRoute(child, fullPath);
|
|
4889
5238
|
}
|
|
4890
5239
|
};
|
|
4891
|
-
|
|
5240
|
+
traverse5(ast, {
|
|
4892
5241
|
JSXElement: (nodePath) => {
|
|
4893
5242
|
const name = getJsxElementName(nodePath.node.openingElement);
|
|
4894
5243
|
if (name !== "Routes" && name !== "Route") return;
|
|
@@ -4927,7 +5276,7 @@ var WebNavigationAnalyzer = class {
|
|
|
4927
5276
|
"createMemoryRouter",
|
|
4928
5277
|
"useRoutes"
|
|
4929
5278
|
]);
|
|
4930
|
-
|
|
5279
|
+
traverse5(ast, {
|
|
4931
5280
|
CallExpression: (nodePath) => {
|
|
4932
5281
|
const callee = nodePath.node.callee;
|
|
4933
5282
|
if (!BabelTypes.isIdentifier(callee) || !ROUTER_FACTORIES.has(callee.name)) return;
|
|
@@ -5360,8 +5709,8 @@ var ZodParsedType = util.arrayToEnum([
|
|
|
5360
5709
|
"set"
|
|
5361
5710
|
]);
|
|
5362
5711
|
var getParsedType = (data) => {
|
|
5363
|
-
const
|
|
5364
|
-
switch (
|
|
5712
|
+
const t15 = typeof data;
|
|
5713
|
+
switch (t15) {
|
|
5365
5714
|
case "undefined":
|
|
5366
5715
|
return ZodParsedType.undefined;
|
|
5367
5716
|
case "string":
|
|
@@ -9206,7 +9555,25 @@ function isSafeImageUrl(value) {
|
|
|
9206
9555
|
return SAFE_IMAGE_URL_RE.test(trimmed);
|
|
9207
9556
|
}
|
|
9208
9557
|
|
|
9209
|
-
// ../shared/dist/chunk-
|
|
9558
|
+
// ../shared/dist/chunk-KUMPS6RH.mjs
|
|
9559
|
+
var SUPPORTED_APPILOTS_LOCALES = ["pt-BR", "en", "es", "fr"];
|
|
9560
|
+
|
|
9561
|
+
// ../shared/dist/chunk-4RYPNBBY.mjs
|
|
9562
|
+
var symbols = external_exports.array(external_exports.string().max(240)).max(12);
|
|
9563
|
+
var controlEvidenceSchema = external_exports.object({
|
|
9564
|
+
version: external_exports.literal(1),
|
|
9565
|
+
siteId: external_exports.string().regex(/^[a-f0-9]{20}$/),
|
|
9566
|
+
component: external_exports.string().max(240),
|
|
9567
|
+
icons: symbols,
|
|
9568
|
+
handler: external_exports.string().max(240).optional(),
|
|
9569
|
+
calls: symbols,
|
|
9570
|
+
argumentBindings: symbols,
|
|
9571
|
+
conditions: symbols,
|
|
9572
|
+
nativeConfirmation: external_exports.object({
|
|
9573
|
+
title: external_exports.string().max(240).optional(),
|
|
9574
|
+
destructiveOption: external_exports.boolean()
|
|
9575
|
+
}).optional()
|
|
9576
|
+
});
|
|
9210
9577
|
var locatorSourceSchema = external_exports.enum([
|
|
9211
9578
|
"appilotsId",
|
|
9212
9579
|
"testID",
|
|
@@ -9408,7 +9775,9 @@ var screenPermissionDescriptorSchema = external_exports.object({
|
|
|
9408
9775
|
agentAccess: external_exports.enum(["read", "write", "none"]).optional(),
|
|
9409
9776
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
9410
9777
|
blockedActions: external_exports.array(external_exports.string()).optional(),
|
|
9411
|
-
isPii: external_exports.boolean().optional()
|
|
9778
|
+
isPii: external_exports.boolean().optional(),
|
|
9779
|
+
/** Graded assessment — see `screenPermissionSchema.assessment`. */
|
|
9780
|
+
assessment: external_exports.boolean().optional()
|
|
9412
9781
|
}).passthrough();
|
|
9413
9782
|
var screenDescriptorSchema = external_exports.object({
|
|
9414
9783
|
name: external_exports.string(),
|
|
@@ -9633,7 +10002,9 @@ var snapshotInputSchema = external_exports.object({
|
|
|
9633
10002
|
inModal: external_exports.boolean().optional()
|
|
9634
10003
|
}).passthrough();
|
|
9635
10004
|
var snapshotButtonSchema = external_exports.object({
|
|
10005
|
+
controlEvidence: controlEvidenceSchema.optional(),
|
|
9636
10006
|
id: boundedString(160).optional(),
|
|
10007
|
+
dispatchable: external_exports.boolean().optional(),
|
|
9637
10008
|
provenance: identityProvenanceSchema.optional(),
|
|
9638
10009
|
/**
|
|
9639
10010
|
* False when the control is mounted but currently OUTSIDE the window —
|
|
@@ -9694,7 +10065,36 @@ var snapshotListSchema = external_exports.object({
|
|
|
9694
10065
|
*/
|
|
9695
10066
|
source: boundedString(40).optional(),
|
|
9696
10067
|
itemCount: external_exports.number().int().optional(),
|
|
10068
|
+
/**
|
|
10069
|
+
* Size of the whole collection when the app declared it, for a list
|
|
10070
|
+
* that is a WINDOW onto more data than it holds.
|
|
10071
|
+
*
|
|
10072
|
+
* `itemCount` is how many rows the list is rendering from and
|
|
10073
|
+
* `visibleItemCount` how many of those are mounted; neither can
|
|
10074
|
+
* express "there are 36 and you are looking at the first 20",
|
|
10075
|
+
* because a paginated list's `data` is the page. Absent means
|
|
10076
|
+
* unknown — never "same as itemCount".
|
|
10077
|
+
*/
|
|
10078
|
+
totalItemCount: external_exports.number().int().optional(),
|
|
9697
10079
|
visibleItemCount: external_exports.number().int().optional(),
|
|
10080
|
+
viewportItemCount: external_exports.number().int().nonnegative().optional(),
|
|
10081
|
+
exploration: external_exports.object({
|
|
10082
|
+
revision: external_exports.number().int().nonnegative(),
|
|
10083
|
+
observedItemCount: external_exports.number().int().min(0).max(5e3),
|
|
10084
|
+
observedRanges: external_exports.array(
|
|
10085
|
+
external_exports.object({
|
|
10086
|
+
start: external_exports.number().int().nonnegative(),
|
|
10087
|
+
end: external_exports.number().int().nonnegative()
|
|
10088
|
+
})
|
|
10089
|
+
).max(32),
|
|
10090
|
+
rangesTruncated: external_exports.boolean(),
|
|
10091
|
+
coverage: external_exports.enum(["partial", "all-loaded"]),
|
|
10092
|
+
pagination: external_exports.enum(["possible", "not-declared"]),
|
|
10093
|
+
scrollSteps: external_exports.number().int().nonnegative(),
|
|
10094
|
+
remainingScrollSteps: external_exports.number().int().nonnegative(),
|
|
10095
|
+
consecutiveNoProgress: external_exports.number().int().nonnegative(),
|
|
10096
|
+
lastScroll: external_exports.enum(["moved", "no-progress", "boundary", "unverified"]).optional()
|
|
10097
|
+
}).optional(),
|
|
9698
10098
|
refreshing: external_exports.boolean().optional(),
|
|
9699
10099
|
empty: external_exports.boolean().optional(),
|
|
9700
10100
|
label: boundedString(300).optional(),
|
|
@@ -9742,6 +10142,7 @@ var snapshotChoiceGroupSchema = external_exports.object({
|
|
|
9742
10142
|
}).passthrough();
|
|
9743
10143
|
var snapshotElementSchema = external_exports.object({
|
|
9744
10144
|
id: boundedString(160).optional(),
|
|
10145
|
+
dispatchable: external_exports.boolean().optional(),
|
|
9745
10146
|
role: boundedString(40).optional(),
|
|
9746
10147
|
label: boundedString(300).optional(),
|
|
9747
10148
|
texts: external_exports.array(boundedString(500)).max(50).optional(),
|
|
@@ -9804,6 +10205,8 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
9804
10205
|
loadingFinished: external_exports.boolean().optional(),
|
|
9805
10206
|
textsAdded: external_exports.number().int().nonnegative().optional(),
|
|
9806
10207
|
textsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
10208
|
+
buttonsAddedIndices: external_exports.array(external_exports.number().int().nonnegative()).max(12).optional(),
|
|
10209
|
+
buttonsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
9807
10210
|
visibleRowsDelta: external_exports.number().int().optional(),
|
|
9808
10211
|
totalRowsDelta: external_exports.number().int().optional(),
|
|
9809
10212
|
fieldsNewlyFilled: external_exports.array(boundedString(160)).max(12).optional(),
|
|
@@ -9829,6 +10232,16 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
9829
10232
|
}).passthrough().optional()
|
|
9830
10233
|
}).passthrough();
|
|
9831
10234
|
var agentContextSchema = external_exports.object({
|
|
10235
|
+
missionProtocol: external_exports.literal(1).optional(),
|
|
10236
|
+
missionId: boundedString(160).optional(),
|
|
10237
|
+
/** Preferred supported device language, reported by the SDK independently of map/UI labels. */
|
|
10238
|
+
deviceLocale: external_exports.enum(SUPPORTED_APPILOTS_LOCALES).optional(),
|
|
10239
|
+
/**
|
|
10240
|
+
* Per-session plain-language override (short sentences, no jargon,
|
|
10241
|
+
* numbered steps). Absent → the project's `plainLanguage` setting.
|
|
10242
|
+
* Changes how the reply is WORDED only — see `PLAIN_LANGUAGE_PROMPT`.
|
|
10243
|
+
*/
|
|
10244
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
9832
10245
|
/**
|
|
9833
10246
|
* Client platform this observation was captured on. Optional and
|
|
9834
10247
|
* additive (see `clientPlatformSchema`) — absent means
|
|
@@ -10079,6 +10492,12 @@ var actionDiagnoseSchema = external_exports.object({
|
|
|
10079
10492
|
requiresUserInput: external_exports.boolean().optional()
|
|
10080
10493
|
});
|
|
10081
10494
|
var actionResultSchema = external_exports.object({
|
|
10495
|
+
nativeConfirmation: external_exports.object({
|
|
10496
|
+
title: external_exports.string().max(300),
|
|
10497
|
+
message: external_exports.string().max(1e3).optional(),
|
|
10498
|
+
buttonLabel: external_exports.string().max(160).optional(),
|
|
10499
|
+
handlerCompleted: external_exports.boolean()
|
|
10500
|
+
}).optional(),
|
|
10082
10501
|
actionId: external_exports.string(),
|
|
10083
10502
|
type: external_exports.string(),
|
|
10084
10503
|
success: external_exports.boolean(),
|
|
@@ -10145,7 +10564,14 @@ var screenPermissionSchema = external_exports.object({
|
|
|
10145
10564
|
agentAccess: agentAccessLevelSchema.optional(),
|
|
10146
10565
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
10147
10566
|
blockedActions: external_exports.array(external_exports.string().min(1)).optional(),
|
|
10148
|
-
isPii: external_exports.boolean().optional()
|
|
10567
|
+
isPii: external_exports.boolean().optional(),
|
|
10568
|
+
/**
|
|
10569
|
+
* Graded assessment (exam, quiz, graded activity). The agent may
|
|
10570
|
+
* explain the screen and navigate away, never write: every action
|
|
10571
|
+
* except navigate/scroll is blocked server-side, and the model is told
|
|
10572
|
+
* to refuse up front instead of trying. Wins over `agentAccess: 'write'`.
|
|
10573
|
+
*/
|
|
10574
|
+
assessment: external_exports.boolean().optional()
|
|
10149
10575
|
}).strict();
|
|
10150
10576
|
external_exports.object({
|
|
10151
10577
|
agentCanWrite: external_exports.boolean(),
|
|
@@ -10153,6 +10579,10 @@ external_exports.object({
|
|
|
10153
10579
|
confirmDestructive: external_exports.boolean(),
|
|
10154
10580
|
/**
|
|
10155
10581
|
* Map of screenName → override. Keys must be non-empty strings.
|
|
10582
|
+
* A key containing `*` or starting with `/` is a PATTERN, matched
|
|
10583
|
+
* case-insensitively against the screen name and the navigation path
|
|
10584
|
+
* (`*Quiz*`; a path key uses `*` per segment, see docs/permissions).
|
|
10585
|
+
* An exact key beats patterns.
|
|
10156
10586
|
* Limited to 200 entries to keep the JSON column small and editable
|
|
10157
10587
|
* in the dashboard table view.
|
|
10158
10588
|
*/
|
|
@@ -10206,7 +10636,7 @@ external_exports.object({
|
|
|
10206
10636
|
/** null = remove webhook, undefined = leave unchanged. */
|
|
10207
10637
|
budgetWebhookUrl: external_exports.string().url().nullable().optional()
|
|
10208
10638
|
}).strict();
|
|
10209
|
-
var localeSchema = external_exports.enum(
|
|
10639
|
+
var localeSchema = external_exports.enum(SUPPORTED_APPILOTS_LOCALES);
|
|
10210
10640
|
var hexColorSchema = external_exports.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
|
|
10211
10641
|
message: "Must be a hex color like #6366f1"
|
|
10212
10642
|
});
|
|
@@ -10250,6 +10680,13 @@ var imageSourceField = external_exports.string().max(500).refine(isSafeImageUrl,
|
|
|
10250
10680
|
external_exports.object({
|
|
10251
10681
|
/** Tone / persona instructions appended to the system prompt. */
|
|
10252
10682
|
personaPrompt: external_exports.string().max(2048).nullable(),
|
|
10683
|
+
/**
|
|
10684
|
+
* Accessibility: reply in plain language (short sentences, everyday
|
|
10685
|
+
* words, numbered steps, one question at a time). Server-side only,
|
|
10686
|
+
* like `personaPrompt`; a session can override it via
|
|
10687
|
+
* `context.plainLanguage`.
|
|
10688
|
+
*/
|
|
10689
|
+
plainLanguage: external_exports.boolean(),
|
|
10253
10690
|
/** Display name in the chat header (e.g. "Aria"). */
|
|
10254
10691
|
assistantName: external_exports.string().max(60).nullable(),
|
|
10255
10692
|
/** URL or remote asset id for the avatar shown next to assistant turns. */
|
|
@@ -10273,6 +10710,7 @@ external_exports.object({
|
|
|
10273
10710
|
});
|
|
10274
10711
|
external_exports.object({
|
|
10275
10712
|
personaPrompt: external_exports.string().max(2048).nullable().optional(),
|
|
10713
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
10276
10714
|
assistantName: external_exports.string().max(60).nullable().optional(),
|
|
10277
10715
|
assistantAvatar: imageSourceField.nullable().optional(),
|
|
10278
10716
|
emptyStateIcon: imageSourceField.nullable().optional(),
|
|
@@ -10430,6 +10868,8 @@ external_exports.object({
|
|
|
10430
10868
|
* the dev doesn't want their tokens metered.
|
|
10431
10869
|
*/
|
|
10432
10870
|
silent: external_exports.boolean().optional(),
|
|
10871
|
+
/** Run with the plain-language reply style on/off, overriding the project. */
|
|
10872
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
10433
10873
|
/**
|
|
10434
10874
|
* Prior turns of the conversation, in order, that precede `prompt`.
|
|
10435
10875
|
* Lets a driver run the agent loop hop-by-hop headlessly (the multi-
|
|
@@ -11217,9 +11657,22 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
11217
11657
|
const filePath = path2__default.resolve(outputDir, `mcp-document.${this.options.format}`);
|
|
11218
11658
|
await writeFile(filePath, serialized, "utf-8");
|
|
11219
11659
|
console.log(`[MCPGenerator] Document written to: ${filePath}`);
|
|
11660
|
+
const controlFiles = analyzed.controlEvidenceFiles ?? {};
|
|
11661
|
+
await writeFile(
|
|
11662
|
+
path2__default.resolve(outputDir, "control-evidence.json"),
|
|
11663
|
+
JSON.stringify({ version: 1, files: controlFiles }, null, 2),
|
|
11664
|
+
"utf-8"
|
|
11665
|
+
);
|
|
11220
11666
|
const checksumFilePath = path2__default.resolve(outputDir, ".appilots-checksum");
|
|
11221
11667
|
await writeFile(checksumFilePath, checksum, "utf-8");
|
|
11222
11668
|
console.log(`[MCPGenerator] Checksum written to: ${checksumFilePath}`);
|
|
11669
|
+
const evidenceCount = Object.values(controlFiles).reduce(
|
|
11670
|
+
(sum, entries) => sum + entries.length,
|
|
11671
|
+
0
|
|
11672
|
+
);
|
|
11673
|
+
console.log(
|
|
11674
|
+
`[MCPGenerator] Source evidence: ${evidenceCount} icon controls across ${Object.keys(controlFiles).length} files (runtime binding required)`
|
|
11675
|
+
);
|
|
11223
11676
|
console.log("[MCPGenerator] Generation complete!");
|
|
11224
11677
|
return {
|
|
11225
11678
|
document,
|
|
@@ -11285,7 +11738,8 @@ var KNOWN_CONFIG_KEYS = [
|
|
|
11285
11738
|
"navigationExclude",
|
|
11286
11739
|
"platform",
|
|
11287
11740
|
"manifestPath",
|
|
11288
|
-
"eval"
|
|
11741
|
+
"eval",
|
|
11742
|
+
"knowledge"
|
|
11289
11743
|
];
|
|
11290
11744
|
var KEY_ALIASES = {
|
|
11291
11745
|
apiUrl: "serverUrl",
|
|
@@ -11433,7 +11887,8 @@ function saveConfig(dir, config) {
|
|
|
11433
11887
|
navigationExclude: config.navigationExclude || existingConfig?.navigationExclude,
|
|
11434
11888
|
platform: config.platform || existingConfig?.platform,
|
|
11435
11889
|
manifestPath: config.manifestPath || existingConfig?.manifestPath,
|
|
11436
|
-
eval: config.eval || existingConfig?.eval
|
|
11890
|
+
eval: config.eval || existingConfig?.eval,
|
|
11891
|
+
knowledge: config.knowledge || existingConfig?.knowledge
|
|
11437
11892
|
};
|
|
11438
11893
|
try {
|
|
11439
11894
|
writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2), "utf-8");
|
|
@@ -11526,6 +11981,19 @@ function validateConfig(config) {
|
|
|
11526
11981
|
if (config.manifestPath !== void 0 && typeof config.manifestPath !== "string") {
|
|
11527
11982
|
errors.push("manifestPath must be a string");
|
|
11528
11983
|
}
|
|
11984
|
+
if (config.knowledge !== void 0) {
|
|
11985
|
+
if (typeof config.knowledge !== "object" || config.knowledge === null || Array.isArray(config.knowledge)) {
|
|
11986
|
+
errors.push("knowledge must be an object");
|
|
11987
|
+
} else {
|
|
11988
|
+
const kc = config.knowledge;
|
|
11989
|
+
for (const field of ["sources", "exclude"]) {
|
|
11990
|
+
const value = kc[field];
|
|
11991
|
+
if (value !== void 0 && (!Array.isArray(value) || !value.every((item) => typeof item === "string"))) {
|
|
11992
|
+
errors.push(`knowledge.${field} must be an array of strings`);
|
|
11993
|
+
}
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
}
|
|
11529
11997
|
if (config.eval !== void 0) {
|
|
11530
11998
|
if (typeof config.eval !== "object" || config.eval === null || Array.isArray(config.eval)) {
|
|
11531
11999
|
errors.push("eval must be an object");
|
|
@@ -11802,6 +12270,48 @@ var AppilotsAPIClient = class {
|
|
|
11802
12270
|
};
|
|
11803
12271
|
}
|
|
11804
12272
|
}
|
|
12273
|
+
/**
|
|
12274
|
+
* Syncs knowledge documents with the Appilots backend.
|
|
12275
|
+
*
|
|
12276
|
+
* @param documents Array of documents to sync (filename, base64 content, mimeType, checksum)
|
|
12277
|
+
* @returns KnowledgeSyncResult with counts of uploaded, skipped, and errored docs
|
|
12278
|
+
*/
|
|
12279
|
+
async knowledgeSync(documents) {
|
|
12280
|
+
try {
|
|
12281
|
+
const response = await this.request(`${this.baseUrl}/cli/knowledge/sync`, {
|
|
12282
|
+
method: "POST",
|
|
12283
|
+
headers: {
|
|
12284
|
+
"Content-Type": "application/json",
|
|
12285
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
12286
|
+
},
|
|
12287
|
+
body: JSON.stringify({ documents })
|
|
12288
|
+
});
|
|
12289
|
+
if (!response.ok) {
|
|
12290
|
+
const errorData = await response.json().catch(() => ({}));
|
|
12291
|
+
return {
|
|
12292
|
+
success: false,
|
|
12293
|
+
uploaded: 0,
|
|
12294
|
+
skipped: 0,
|
|
12295
|
+
errors: 0,
|
|
12296
|
+
error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`)
|
|
12297
|
+
};
|
|
12298
|
+
}
|
|
12299
|
+
const json = await response.json();
|
|
12300
|
+
const inner = json.data ?? json;
|
|
12301
|
+
return {
|
|
12302
|
+
success: true,
|
|
12303
|
+
...inner
|
|
12304
|
+
};
|
|
12305
|
+
} catch (error) {
|
|
12306
|
+
return {
|
|
12307
|
+
success: false,
|
|
12308
|
+
uploaded: 0,
|
|
12309
|
+
skipped: 0,
|
|
12310
|
+
errors: 0,
|
|
12311
|
+
error: error instanceof Error ? error.message : "Failed to sync knowledge with Appilots API"
|
|
12312
|
+
};
|
|
12313
|
+
}
|
|
12314
|
+
}
|
|
11805
12315
|
/**
|
|
11806
12316
|
* Checks if the Appilots API server is healthy
|
|
11807
12317
|
*
|
|
@@ -11820,7 +12330,7 @@ var AppilotsAPIClient = class {
|
|
|
11820
12330
|
};
|
|
11821
12331
|
|
|
11822
12332
|
// src/version.ts
|
|
11823
|
-
var CLI_VERSION = "0.
|
|
12333
|
+
var CLI_VERSION = "0.14.0";
|
|
11824
12334
|
|
|
11825
12335
|
export { AppilotsAPIClient, CLI_VERSION, ComponentAnalyzer, DEFAULT_MANIFEST_FILENAME, DEFAULT_WEB_SCREEN_PATTERNS, FormAnalyzer, GenericPlatformAnalyzer, MCPGenerator, NavigationAnalyzer, ReactNativePlatformAnalyzer, ReactWebPlatformAnalyzer, ScreenAnalyzer, WebNavigationAnalyzer, WebScreenAnalyzer, formatMetadataWarnings, getConfigPath, getEnvOverrides, lintActionMetadata, loadConfig, loadManifest, mergeManifestNavigation, mergeManifestScreens, resolvePathToScreen, saveConfig, screenNameFromPath, validateConfig };
|
|
11826
12336
|
//# sourceMappingURL=index.mjs.map
|