@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.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var fs = require('fs/promises');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
var traverse5 = require('@babel/traverse');
|
|
6
6
|
var BabelTypes = require('@babel/types');
|
|
7
|
+
var path2 = require('path');
|
|
7
8
|
var fastGlob = require('fast-glob');
|
|
8
9
|
var parser = require('@babel/parser');
|
|
9
10
|
var fs$1 = require('fs');
|
|
10
|
-
var crypto = require('crypto');
|
|
11
11
|
|
|
12
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
13
|
|
|
@@ -30,9 +30,9 @@ function _interopNamespace(e) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
33
|
-
var
|
|
34
|
-
var traverse4__default = /*#__PURE__*/_interopDefault(traverse4);
|
|
33
|
+
var traverse5__default = /*#__PURE__*/_interopDefault(traverse5);
|
|
35
34
|
var BabelTypes__namespace = /*#__PURE__*/_interopNamespace(BabelTypes);
|
|
35
|
+
var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
|
|
36
36
|
var fastGlob__default = /*#__PURE__*/_interopDefault(fastGlob);
|
|
37
37
|
var parser__namespace = /*#__PURE__*/_interopNamespace(parser);
|
|
38
38
|
|
|
@@ -41,6 +41,343 @@ var __export = (target, all) => {
|
|
|
41
41
|
for (var name in all)
|
|
42
42
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
43
43
|
};
|
|
44
|
+
var MAX_HOPS = 8;
|
|
45
|
+
function unwrap(path11) {
|
|
46
|
+
while (path11.isTSAsExpression() || path11.isTSTypeAssertion() || path11.isTSNonNullExpression() || path11.isTSSatisfiesExpression() || path11.isParenthesizedExpression())
|
|
47
|
+
path11 = path11.get("expression");
|
|
48
|
+
return path11;
|
|
49
|
+
}
|
|
50
|
+
function constantValue(path11, depth = 0) {
|
|
51
|
+
if (depth > MAX_HOPS) return void 0;
|
|
52
|
+
path11 = unwrap(path11);
|
|
53
|
+
if (!path11.isIdentifier()) return path11;
|
|
54
|
+
const binding = path11.scope.getBinding(path11.node.name);
|
|
55
|
+
if (!binding?.constant || !binding.path.isVariableDeclarator()) return path11;
|
|
56
|
+
const init = binding.path.get("init");
|
|
57
|
+
const resolved = init.node ? constantValue(init, depth + 1) : void 0;
|
|
58
|
+
if (resolved?.isObjectExpression() && binding.referencePaths.some(
|
|
59
|
+
(reference) => !reference.parentPath?.isJSXSpreadAttribute() && !reference.parentPath?.isSpreadElement()
|
|
60
|
+
))
|
|
61
|
+
return void 0;
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
function propValue(path11, name) {
|
|
65
|
+
const readObject = (path12) => {
|
|
66
|
+
const resolved = constantValue(path12);
|
|
67
|
+
if (!resolved?.isObjectExpression()) return { blocked: true };
|
|
68
|
+
for (const property of [...resolved.get("properties")].reverse()) {
|
|
69
|
+
if (property.isSpreadElement()) {
|
|
70
|
+
const found = readObjectBounded(property.get("argument"));
|
|
71
|
+
if (found.value || found.blocked) return found;
|
|
72
|
+
} else if (property.isObjectProperty() || property.isObjectMethod()) {
|
|
73
|
+
if (property.node.computed) return { blocked: true };
|
|
74
|
+
const key = property.node.key;
|
|
75
|
+
if ((BabelTypes__namespace.isIdentifier(key) ? key.name : BabelTypes__namespace.isStringLiteral(key) ? key.value : "") === name)
|
|
76
|
+
return property.isObjectProperty() ? { value: property.get("value") } : { value: property };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {};
|
|
80
|
+
};
|
|
81
|
+
let objectBudget = MAX_HOPS;
|
|
82
|
+
const readObjectBounded = (path12) => objectBudget-- > 0 ? readObject(path12) : { blocked: true };
|
|
83
|
+
for (const attr of [...path11.get("attributes")].reverse()) {
|
|
84
|
+
if (attr.isJSXAttribute() && BabelTypes__namespace.isJSXIdentifier(attr.node.name, { name })) {
|
|
85
|
+
const value = attr.get("value");
|
|
86
|
+
return value.isJSXExpressionContainer() ? unwrap(value.get("expression")) : value.node ? value : void 0;
|
|
87
|
+
}
|
|
88
|
+
if (attr.isJSXSpreadAttribute()) {
|
|
89
|
+
const found = readObjectBounded(attr.get("argument"));
|
|
90
|
+
if (found.value || found.blocked) return found.value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
function importIdentity(path11, depth = 0) {
|
|
96
|
+
if (depth > MAX_HOPS) return void 0;
|
|
97
|
+
path11 = unwrap(path11);
|
|
98
|
+
if (path11.isIdentifier() || path11.isJSXIdentifier()) {
|
|
99
|
+
const binding = path11.scope.getBinding(path11.node.name);
|
|
100
|
+
if (!binding?.constant) return void 0;
|
|
101
|
+
const declaration = binding.path;
|
|
102
|
+
if (declaration.parentPath?.isImportDeclaration()) {
|
|
103
|
+
const module = declaration.parentPath.node.source.value;
|
|
104
|
+
if (declaration.isImportSpecifier()) {
|
|
105
|
+
const imported = declaration.node.imported;
|
|
106
|
+
return { module, imported: BabelTypes__namespace.isIdentifier(imported) ? imported.name : imported.value };
|
|
107
|
+
}
|
|
108
|
+
if (declaration.isImportDefaultSpecifier()) return { module, imported: "default" };
|
|
109
|
+
if (declaration.isImportNamespaceSpecifier()) return { module, imported: "*" };
|
|
110
|
+
}
|
|
111
|
+
if (declaration.isVariableDeclarator() && declaration.get("init").node)
|
|
112
|
+
return importIdentity(declaration.get("init"), depth + 1);
|
|
113
|
+
}
|
|
114
|
+
if (path11.isJSXMemberExpression() || path11.isMemberExpression() && !path11.node.computed) {
|
|
115
|
+
const origin = importIdentity(path11.get("object"), depth + 1);
|
|
116
|
+
const key = path11.node.property;
|
|
117
|
+
if (origin && (BabelTypes__namespace.isIdentifier(key) || BabelTypes__namespace.isJSXIdentifier(key)) && (origin.imported === "*" || origin.module === "react" && origin.imported === "default"))
|
|
118
|
+
return { module: origin.module, imported: key.name };
|
|
119
|
+
}
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
function handlerFunction(path11, depth = 0) {
|
|
123
|
+
if (depth > MAX_HOPS) return void 0;
|
|
124
|
+
path11 = unwrap(path11);
|
|
125
|
+
if (path11.isFunction()) return path11;
|
|
126
|
+
if (path11.isIdentifier()) {
|
|
127
|
+
const binding = path11.scope.getBinding(path11.node.name);
|
|
128
|
+
if (!binding?.constant) return void 0;
|
|
129
|
+
if (binding.path.isFunctionDeclaration()) return binding.path;
|
|
130
|
+
if (binding.path.isVariableDeclarator() && binding.path.get("init").node)
|
|
131
|
+
return handlerFunction(binding.path.get("init"), depth + 1);
|
|
132
|
+
}
|
|
133
|
+
if (path11.isCallExpression()) {
|
|
134
|
+
const origin = importIdentity(path11.get("callee"));
|
|
135
|
+
if (origin?.module === "react" && origin.imported === "useCallback") {
|
|
136
|
+
const callback = path11.get("arguments")[0];
|
|
137
|
+
if (callback) return handlerFunction(callback, depth + 1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (path11.isMemberExpression() && !path11.node.computed && BabelTypes__namespace.isThisExpression(path11.node.object)) {
|
|
141
|
+
const key = path11.node.property;
|
|
142
|
+
if (!BabelTypes__namespace.isIdentifier(key)) return void 0;
|
|
143
|
+
const owner = path11.findParent((p) => p.isClassDeclaration() || p.isClassExpression());
|
|
144
|
+
if (!owner || !(owner.isClassDeclaration() || owner.isClassExpression())) return void 0;
|
|
145
|
+
for (const member of owner.get("body").get("body")) {
|
|
146
|
+
if (!(member.isClassMethod() || member.isClassProperty()) || member.node.computed || member.node.static)
|
|
147
|
+
continue;
|
|
148
|
+
if (!BabelTypes__namespace.isIdentifier(member.node.key, { name: key.name })) continue;
|
|
149
|
+
if (member.isClassMethod() && member.node.kind === "method") return member;
|
|
150
|
+
if (member.isClassProperty() && member.get("value").node)
|
|
151
|
+
return handlerFunction(member.get("value"), depth + 1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/extractors/control-evidence.ts
|
|
158
|
+
function symbol(node) {
|
|
159
|
+
if (BabelTypes__namespace.isTSAsExpression(node) || BabelTypes__namespace.isTSTypeAssertion(node) || BabelTypes__namespace.isTSNonNullExpression(node) || BabelTypes__namespace.isTSSatisfiesExpression(node))
|
|
160
|
+
return symbol(node.expression);
|
|
161
|
+
if (BabelTypes__namespace.isIdentifier(node) || BabelTypes__namespace.isJSXIdentifier(node)) return node.name;
|
|
162
|
+
if (BabelTypes__namespace.isThisExpression(node)) return "this";
|
|
163
|
+
if (BabelTypes__namespace.isMemberExpression(node) && !node.computed || BabelTypes__namespace.isJSXMemberExpression(node)) {
|
|
164
|
+
const object = symbol(node.object), property = symbol(node.property);
|
|
165
|
+
return object && property ? `${object}.${property}` : void 0;
|
|
166
|
+
}
|
|
167
|
+
if (BabelTypes__namespace.isUnaryExpression(node) && node.operator === "!") {
|
|
168
|
+
const value = symbol(node.argument);
|
|
169
|
+
return value ? `!${value}` : void 0;
|
|
170
|
+
}
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
function attribute(node, name) {
|
|
174
|
+
return node.attributes.find(
|
|
175
|
+
(a) => BabelTypes__namespace.isJSXAttribute(a) && BabelTypes__namespace.isJSXIdentifier(a.name, { name })
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
var ICON_LIBRARIES = [
|
|
179
|
+
"lucide-react-native",
|
|
180
|
+
"lucide-react",
|
|
181
|
+
"@tamagui/lucide-icons",
|
|
182
|
+
"@expo/vector-icons",
|
|
183
|
+
"@react-native-vector-icons/",
|
|
184
|
+
"react-native-vector-icons/"
|
|
185
|
+
];
|
|
186
|
+
function iconLibrary(module) {
|
|
187
|
+
return ICON_LIBRARIES.some(
|
|
188
|
+
(name) => name.endsWith("/") ? module.startsWith(name) : module === name || module.startsWith(name + "/")
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
function add(values, value, max = 12) {
|
|
192
|
+
if (value && value.length <= 240 && values.length < max && !values.includes(value))
|
|
193
|
+
values.push(value);
|
|
194
|
+
}
|
|
195
|
+
function iconName(path11, explicitIcon = false) {
|
|
196
|
+
const origin = importIdentity(path11);
|
|
197
|
+
if (origin && iconLibrary(origin.module) && origin.imported !== "*")
|
|
198
|
+
return origin.imported === "default" ? origin.module : `${origin.module}:${origin.imported}`;
|
|
199
|
+
const name = symbol(path11.node);
|
|
200
|
+
return name && (explicitIcon || /icon/i.test(name)) ? name : void 0;
|
|
201
|
+
}
|
|
202
|
+
function iconsInElement(path11, explicitIcon = false) {
|
|
203
|
+
const name = iconName(path11.get("name"), explicitIcon);
|
|
204
|
+
if (!name) return void 0;
|
|
205
|
+
const glyph = propValue(path11, "name");
|
|
206
|
+
const value = glyph && constantValue(glyph);
|
|
207
|
+
return value?.isStringLiteral() ? `${name}:${value.node.value}` : name;
|
|
208
|
+
}
|
|
209
|
+
function hasInteraction(path11) {
|
|
210
|
+
return ["onPress", "onLongPress", "onClick"].some(
|
|
211
|
+
(name) => attribute(path11.node, name) || propValue(path11, name)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
function collectPresentationIcons(path11, icons) {
|
|
215
|
+
const namedIconProps = [
|
|
216
|
+
"icon",
|
|
217
|
+
"prefix",
|
|
218
|
+
"suffix",
|
|
219
|
+
"left",
|
|
220
|
+
"right",
|
|
221
|
+
"leadingIcon",
|
|
222
|
+
"trailingIcon",
|
|
223
|
+
"startIcon",
|
|
224
|
+
"endIcon",
|
|
225
|
+
"renderIcon"
|
|
226
|
+
];
|
|
227
|
+
const props = new Set(namedIconProps);
|
|
228
|
+
for (const attr of path11.node.attributes)
|
|
229
|
+
if (BabelTypes__namespace.isJSXAttribute(attr) && BabelTypes__namespace.isJSXIdentifier(attr.name) && !/^on[A-Z]/.test(attr.name.name))
|
|
230
|
+
props.add(attr.name.name);
|
|
231
|
+
for (const prop of props) {
|
|
232
|
+
const icon = propValue(path11, prop);
|
|
233
|
+
if (icon) {
|
|
234
|
+
const value = constantValue(icon);
|
|
235
|
+
if (value?.isStringLiteral()) {
|
|
236
|
+
if (prop.toLowerCase().includes("icon")) add(icons, value.node.value, 8);
|
|
237
|
+
} else {
|
|
238
|
+
const explicitIcon = prop.toLowerCase() === "icon";
|
|
239
|
+
if (icon.isJSXElement()) {
|
|
240
|
+
if (hasInteraction(icon.get("openingElement"))) continue;
|
|
241
|
+
add(icons, iconsInElement(icon.get("openingElement"), explicitIcon), 8);
|
|
242
|
+
}
|
|
243
|
+
icon.traverse({
|
|
244
|
+
JSXAttribute(attr) {
|
|
245
|
+
if (BabelTypes__namespace.isJSXIdentifier(attr.node.name) && /^on[A-Z]/.test(attr.node.name.name))
|
|
246
|
+
attr.skip();
|
|
247
|
+
},
|
|
248
|
+
JSXElement(child) {
|
|
249
|
+
const opening = child.get("openingElement");
|
|
250
|
+
if (hasInteraction(opening)) {
|
|
251
|
+
child.skip();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
add(icons, iconsInElement(opening, explicitIcon), 8);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
if (!icons.length && (namedIconProps.includes(prop) || explicitIcon))
|
|
258
|
+
add(icons, iconName(icon, explicitIcon), 8);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function collectIcons(path11) {
|
|
264
|
+
const icons = [];
|
|
265
|
+
collectPresentationIcons(path11, icons);
|
|
266
|
+
const jsx = path11.parentPath;
|
|
267
|
+
if (jsx.isJSXElement())
|
|
268
|
+
jsx.traverse({
|
|
269
|
+
// JSX mentioned inside an event callback is not a rendered child icon.
|
|
270
|
+
JSXAttribute(attr) {
|
|
271
|
+
attr.skip();
|
|
272
|
+
},
|
|
273
|
+
JSXElement(child) {
|
|
274
|
+
const opening = child.get("openingElement");
|
|
275
|
+
if (hasInteraction(opening)) {
|
|
276
|
+
child.skip();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
add(icons, iconsInElement(opening), 8);
|
|
280
|
+
collectPresentationIcons(opening, icons);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
return icons;
|
|
284
|
+
}
|
|
285
|
+
function conditionsAt(path11) {
|
|
286
|
+
const conditions = [];
|
|
287
|
+
let child = path11;
|
|
288
|
+
for (let parent = child.parentPath; parent && !parent.isFunction(); child = parent, parent = parent.parentPath) {
|
|
289
|
+
let test;
|
|
290
|
+
let negated = false;
|
|
291
|
+
if (parent.isLogicalExpression() && parent.node.right === child.node) {
|
|
292
|
+
if (parent.node.operator === "&&") test = parent.node.left;
|
|
293
|
+
if (parent.node.operator === "||") {
|
|
294
|
+
test = parent.node.left;
|
|
295
|
+
negated = true;
|
|
296
|
+
}
|
|
297
|
+
} else if (parent.isConditionalExpression() && parent.node.test !== child.node) {
|
|
298
|
+
test = parent.node.test;
|
|
299
|
+
negated = parent.node.alternate === child.node;
|
|
300
|
+
} else if (parent.isIfStatement() && parent.node.test !== child.node) {
|
|
301
|
+
test = parent.node.test;
|
|
302
|
+
negated = parent.node.alternate === child.node;
|
|
303
|
+
}
|
|
304
|
+
const name = symbol(test);
|
|
305
|
+
if (name) add(conditions, negated ? name.startsWith("!") ? name.slice(1) : `!${name}` : name);
|
|
306
|
+
}
|
|
307
|
+
return conditions;
|
|
308
|
+
}
|
|
309
|
+
function collectHandlerEvidence(expression, evidence) {
|
|
310
|
+
const seen = /* @__PURE__ */ new Set();
|
|
311
|
+
let budget = 100;
|
|
312
|
+
const visit = (path11, depth) => {
|
|
313
|
+
if (depth > 4 || budget-- <= 0) return;
|
|
314
|
+
const fn = handlerFunction(path11);
|
|
315
|
+
if (!fn || seen.has(fn.node)) return;
|
|
316
|
+
seen.add(fn.node);
|
|
317
|
+
fn.traverse({
|
|
318
|
+
// Ignore uncalled helper definitions; inline callbacks remain source evidence.
|
|
319
|
+
Function(nested) {
|
|
320
|
+
if (!nested.parentPath.isCallExpression() && !nested.parentPath.isObjectProperty())
|
|
321
|
+
nested.skip();
|
|
322
|
+
},
|
|
323
|
+
CallExpression(call) {
|
|
324
|
+
if (budget-- <= 0) {
|
|
325
|
+
call.skip();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
add(evidence.calls, symbol(call.node.callee));
|
|
329
|
+
for (const arg of call.node.arguments) add(evidence.argumentBindings, symbol(arg));
|
|
330
|
+
visit(call.get("callee"), depth + 1);
|
|
331
|
+
if (!BabelTypes__namespace.isMemberExpression(call.node.callee) || call.node.callee.computed || !BabelTypes__namespace.isIdentifier(call.node.callee.property, { name: "alert" }))
|
|
332
|
+
return;
|
|
333
|
+
const callee = call.get("callee");
|
|
334
|
+
if (!callee.isMemberExpression()) return;
|
|
335
|
+
const origin = importIdentity(callee.get("object"));
|
|
336
|
+
if (origin?.module !== "react-native" || origin.imported !== "Alert") return;
|
|
337
|
+
const options = call.node.arguments[2];
|
|
338
|
+
const destructiveOption = BabelTypes__namespace.isArrayExpression(options) && options.elements.some(
|
|
339
|
+
(option) => BabelTypes__namespace.isObjectExpression(option) && option.properties.some(
|
|
340
|
+
(p) => BabelTypes__namespace.isObjectProperty(p) && !p.computed && symbol(p.key) === "style" && BabelTypes__namespace.isStringLiteral(p.value, { value: "destructive" })
|
|
341
|
+
)
|
|
342
|
+
);
|
|
343
|
+
if (destructiveOption)
|
|
344
|
+
evidence.nativeConfirmation = {
|
|
345
|
+
title: symbol(call.node.arguments[0]),
|
|
346
|
+
destructiveOption
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
visit(unwrap(expression), 0);
|
|
352
|
+
}
|
|
353
|
+
function extractControlEvidence(ast, source, file) {
|
|
354
|
+
const candidates = [];
|
|
355
|
+
const sourceHash = crypto.createHash("sha256").update(source).digest("hex");
|
|
356
|
+
traverse5__default.default(ast, {
|
|
357
|
+
JSXOpeningElement(path11) {
|
|
358
|
+
const node = path11.node;
|
|
359
|
+
if (attribute(node, "__appilotsControl")) return;
|
|
360
|
+
const value = propValue(path11, "onPress");
|
|
361
|
+
if (!value && !attribute(node, "onPress") || value?.isNullLiteral() || value?.isJSXEmptyExpression())
|
|
362
|
+
return;
|
|
363
|
+
const icons = collectIcons(path11);
|
|
364
|
+
if (!icons.length || node.end == null) return;
|
|
365
|
+
const evidence = {
|
|
366
|
+
version: 1,
|
|
367
|
+
siteId: crypto.createHash("sha256").update(`${file}:${sourceHash}:${node.start}`).digest("hex").slice(0, 20),
|
|
368
|
+
component: symbol(node.name) ?? "unknown",
|
|
369
|
+
icons,
|
|
370
|
+
...value && symbol(value.node) ? { handler: symbol(value.node) } : {},
|
|
371
|
+
calls: [],
|
|
372
|
+
argumentBindings: [],
|
|
373
|
+
conditions: conditionsAt(path11)
|
|
374
|
+
};
|
|
375
|
+
if (value) collectHandlerEvidence(value, evidence);
|
|
376
|
+
candidates.push({ evidence, sourceHash, offset: node.end - (node.selfClosing ? 2 : 1) });
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
return candidates;
|
|
380
|
+
}
|
|
44
381
|
function byCodeUnit(a, b) {
|
|
45
382
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
46
383
|
}
|
|
@@ -162,7 +499,7 @@ function classifyJsxComponent(name, element) {
|
|
|
162
499
|
}
|
|
163
500
|
function collectFunctions(ast) {
|
|
164
501
|
const handlers = /* @__PURE__ */ new Map();
|
|
165
|
-
|
|
502
|
+
traverse5__default.default(ast, {
|
|
166
503
|
FunctionDeclaration: (nodePath) => {
|
|
167
504
|
if (nodePath.node.id?.name) handlers.set(nodePath.node.id.name, nodePath.node);
|
|
168
505
|
},
|
|
@@ -265,7 +602,7 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
|
|
|
265
602
|
}
|
|
266
603
|
};
|
|
267
604
|
if (fn.body) {
|
|
268
|
-
|
|
605
|
+
traverse5__default.default(fn.body, {
|
|
269
606
|
noScope: true,
|
|
270
607
|
enter: (nodePath) => inspectNode(nodePath.node)
|
|
271
608
|
});
|
|
@@ -293,7 +630,7 @@ function setterToStateName(setterName) {
|
|
|
293
630
|
}
|
|
294
631
|
function extractNavigationCalls(ast) {
|
|
295
632
|
const calls = [];
|
|
296
|
-
|
|
633
|
+
traverse5__default.default(ast, {
|
|
297
634
|
noScope: !BabelTypes__namespace.isFile(ast),
|
|
298
635
|
CallExpression: (nodePath) => {
|
|
299
636
|
const node = nodePath.node;
|
|
@@ -417,6 +754,9 @@ function sanitisePermissionsObject(obj) {
|
|
|
417
754
|
if (typeof obj.isPii === "boolean") {
|
|
418
755
|
out.isPii = obj.isPii;
|
|
419
756
|
}
|
|
757
|
+
if (typeof obj.assessment === "boolean") {
|
|
758
|
+
out.assessment = obj.assessment;
|
|
759
|
+
}
|
|
420
760
|
if (Array.isArray(obj.blockedActions)) {
|
|
421
761
|
const ids = obj.blockedActions.filter((x) => typeof x === "string" && !!x);
|
|
422
762
|
if (ids.length > 0) out.blockedActions = ids;
|
|
@@ -444,6 +784,7 @@ var ScreenAnalyzer = class {
|
|
|
444
784
|
routeTargetFiles;
|
|
445
785
|
/** §D: Count of screens filtered out in strict mode (available after analyze()) */
|
|
446
786
|
screensFilteredOut = 0;
|
|
787
|
+
controlEvidenceFiles = {};
|
|
447
788
|
constructor(config, options) {
|
|
448
789
|
this.config = config;
|
|
449
790
|
this.routeTargetFiles = options?.routeTargetFiles ?? /* @__PURE__ */ new Set();
|
|
@@ -544,6 +885,11 @@ var ScreenAnalyzer = class {
|
|
|
544
885
|
title: registerScreenMeta?.title,
|
|
545
886
|
description: registerScreenMeta?.description,
|
|
546
887
|
components,
|
|
888
|
+
controlCandidates: extractControlEvidence(
|
|
889
|
+
ast,
|
|
890
|
+
source,
|
|
891
|
+
path2__namespace.default.relative(this.config.rootDir, filePath)
|
|
892
|
+
),
|
|
547
893
|
forms,
|
|
548
894
|
actions,
|
|
549
895
|
navigationTargets,
|
|
@@ -554,6 +900,8 @@ var ScreenAnalyzer = class {
|
|
|
554
900
|
...permissionsFromJsDoc.isPii ? { isPii: true } : {}
|
|
555
901
|
} : {}
|
|
556
902
|
};
|
|
903
|
+
if (descriptor.controlCandidates?.length)
|
|
904
|
+
this.controlEvidenceFiles[path2__namespace.default.relative(this.config.rootDir, filePath).split(path2__namespace.default.sep).join("/")] = descriptor.controlCandidates;
|
|
557
905
|
descriptor.__hasRegisterScreen = hasRegisterScreenCall;
|
|
558
906
|
return descriptor;
|
|
559
907
|
}
|
|
@@ -563,7 +911,7 @@ var ScreenAnalyzer = class {
|
|
|
563
911
|
*/
|
|
564
912
|
detectRegisterScreenCall(ast) {
|
|
565
913
|
let found = false;
|
|
566
|
-
|
|
914
|
+
traverse5__default.default(ast, {
|
|
567
915
|
CallExpression: (nodePath) => {
|
|
568
916
|
if (found) return;
|
|
569
917
|
const callee = nodePath.node.callee;
|
|
@@ -580,7 +928,7 @@ var ScreenAnalyzer = class {
|
|
|
580
928
|
*/
|
|
581
929
|
extractRegisterScreenMetadata(ast) {
|
|
582
930
|
let metadata = null;
|
|
583
|
-
|
|
931
|
+
traverse5__default.default(ast, {
|
|
584
932
|
CallExpression: (nodePath) => {
|
|
585
933
|
const callee = nodePath.node.callee;
|
|
586
934
|
if (BabelTypes__namespace.isIdentifier(callee) && callee.name === "registerScreen" || BabelTypes__namespace.isMemberExpression(callee) && BabelTypes__namespace.isIdentifier(callee.property) && callee.property.name === "registerScreen") {
|
|
@@ -877,7 +1225,7 @@ var ScreenAnalyzer = class {
|
|
|
877
1225
|
*/
|
|
878
1226
|
extractDefaultComponentName(ast) {
|
|
879
1227
|
let componentName = "";
|
|
880
|
-
|
|
1228
|
+
traverse5__default.default(ast, {
|
|
881
1229
|
ExportDefaultDeclaration: (nodePath) => {
|
|
882
1230
|
const declaration = nodePath.node.declaration;
|
|
883
1231
|
if (BabelTypes__namespace.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
@@ -902,7 +1250,7 @@ var ScreenAnalyzer = class {
|
|
|
902
1250
|
*/
|
|
903
1251
|
extractNavigationTargets(ast) {
|
|
904
1252
|
const targets = /* @__PURE__ */ new Set();
|
|
905
|
-
|
|
1253
|
+
traverse5__default.default(ast, {
|
|
906
1254
|
CallExpression: (nodePath) => {
|
|
907
1255
|
const callee = nodePath.node.callee;
|
|
908
1256
|
if (BabelTypes__namespace.isMemberExpression(callee) && BabelTypes__namespace.isIdentifier(callee.object) && callee.object.name === "navigation" && BabelTypes__namespace.isIdentifier(callee.property) && callee.property.name === "navigate") {
|
|
@@ -921,7 +1269,7 @@ var ScreenAnalyzer = class {
|
|
|
921
1269
|
extractForms(ast) {
|
|
922
1270
|
const forms = [];
|
|
923
1271
|
const fields = /* @__PURE__ */ new Map();
|
|
924
|
-
|
|
1272
|
+
traverse5__default.default(ast, {
|
|
925
1273
|
JSXOpeningElement: (nodePath) => {
|
|
926
1274
|
const element = nodePath.node;
|
|
927
1275
|
if (BabelTypes__namespace.isJSXIdentifier(element.name)) {
|
|
@@ -1042,7 +1390,7 @@ var ScreenAnalyzer = class {
|
|
|
1042
1390
|
extractComponents(ast) {
|
|
1043
1391
|
const components = [];
|
|
1044
1392
|
const seen = /* @__PURE__ */ new Set();
|
|
1045
|
-
|
|
1393
|
+
traverse5__default.default(ast, {
|
|
1046
1394
|
JSXOpeningElement: (nodePath) => {
|
|
1047
1395
|
const element = nodePath.node;
|
|
1048
1396
|
if (BabelTypes__namespace.isJSXIdentifier(element.name)) {
|
|
@@ -1106,7 +1454,7 @@ var ScreenAnalyzer = class {
|
|
|
1106
1454
|
const actionLabels = new Map(
|
|
1107
1455
|
actions.filter((a) => a.label).map((a) => [this.normalizeLabel(a.label), a])
|
|
1108
1456
|
);
|
|
1109
|
-
|
|
1457
|
+
traverse5__default.default(ast, {
|
|
1110
1458
|
JSXOpeningElement: (nodePath) => {
|
|
1111
1459
|
const element = nodePath.node;
|
|
1112
1460
|
if (BabelTypes__namespace.isJSXIdentifier(element.name)) {
|
|
@@ -1175,7 +1523,7 @@ var ScreenAnalyzer = class {
|
|
|
1175
1523
|
}
|
|
1176
1524
|
collectButtonHandlersByLabel(ast) {
|
|
1177
1525
|
const out = /* @__PURE__ */ new Map();
|
|
1178
|
-
|
|
1526
|
+
traverse5__default.default(ast, {
|
|
1179
1527
|
JSXOpeningElement: (nodePath) => {
|
|
1180
1528
|
const element = nodePath.node;
|
|
1181
1529
|
if (!BabelTypes__namespace.isJSXIdentifier(element.name)) return;
|
|
@@ -1257,7 +1605,7 @@ var ScreenAnalyzer = class {
|
|
|
1257
1605
|
}
|
|
1258
1606
|
};
|
|
1259
1607
|
if (fn.body) {
|
|
1260
|
-
|
|
1608
|
+
traverse5__default.default(
|
|
1261
1609
|
fn.body,
|
|
1262
1610
|
{
|
|
1263
1611
|
noScope: true,
|
|
@@ -1454,7 +1802,7 @@ var ScreenAnalyzer = class {
|
|
|
1454
1802
|
extractCollections(ast) {
|
|
1455
1803
|
const renderItemFns = this.collectRenderItemFunctions(ast);
|
|
1456
1804
|
const collections = [];
|
|
1457
|
-
|
|
1805
|
+
traverse5__default.default(ast, {
|
|
1458
1806
|
JSXOpeningElement: (nodePath) => {
|
|
1459
1807
|
const element = nodePath.node;
|
|
1460
1808
|
if (!BabelTypes__namespace.isJSXIdentifier(element.name)) return;
|
|
@@ -1491,7 +1839,7 @@ var ScreenAnalyzer = class {
|
|
|
1491
1839
|
}
|
|
1492
1840
|
collectRenderItemFunctions(ast) {
|
|
1493
1841
|
const out = /* @__PURE__ */ new Map();
|
|
1494
|
-
|
|
1842
|
+
traverse5__default.default(ast, {
|
|
1495
1843
|
VariableDeclarator: (nodePath) => {
|
|
1496
1844
|
if (!BabelTypes__namespace.isIdentifier(nodePath.node.id)) return;
|
|
1497
1845
|
const init = nodePath.node.init;
|
|
@@ -1534,7 +1882,7 @@ var ScreenAnalyzer = class {
|
|
|
1534
1882
|
}
|
|
1535
1883
|
extractRowAction(fn) {
|
|
1536
1884
|
let action;
|
|
1537
|
-
|
|
1885
|
+
traverse5__default.default(
|
|
1538
1886
|
fn.body,
|
|
1539
1887
|
{
|
|
1540
1888
|
noScope: true,
|
|
@@ -1587,7 +1935,7 @@ var ScreenAnalyzer = class {
|
|
|
1587
1935
|
} else if (BabelTypes__namespace.isIdentifier(firstParam)) {
|
|
1588
1936
|
itemNames.add(firstParam.name);
|
|
1589
1937
|
}
|
|
1590
|
-
|
|
1938
|
+
traverse5__default.default(
|
|
1591
1939
|
fn.body,
|
|
1592
1940
|
{
|
|
1593
1941
|
noScope: true,
|
|
@@ -1629,7 +1977,7 @@ var ScreenAnalyzer = class {
|
|
|
1629
1977
|
inferSearchField(ast, dataSource) {
|
|
1630
1978
|
if (!dataSource) return void 0;
|
|
1631
1979
|
let queryBinding;
|
|
1632
|
-
|
|
1980
|
+
traverse5__default.default(ast, {
|
|
1633
1981
|
CallExpression: (nodePath) => {
|
|
1634
1982
|
const node = nodePath.node;
|
|
1635
1983
|
if (!BabelTypes__namespace.isMemberExpression(node.callee)) return;
|
|
@@ -1640,7 +1988,7 @@ var ScreenAnalyzer = class {
|
|
|
1640
1988
|
const fn = node.arguments[0];
|
|
1641
1989
|
if (!BabelTypes__namespace.isArrowFunctionExpression(fn) && !BabelTypes__namespace.isFunctionExpression(fn))
|
|
1642
1990
|
return;
|
|
1643
|
-
|
|
1991
|
+
traverse5__default.default(
|
|
1644
1992
|
fn.body,
|
|
1645
1993
|
{
|
|
1646
1994
|
noScope: true,
|
|
@@ -1682,7 +2030,7 @@ var ScreenAnalyzer = class {
|
|
|
1682
2030
|
}
|
|
1683
2031
|
};
|
|
1684
2032
|
var EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
1685
|
-
var
|
|
2033
|
+
var MAX_HOPS2 = 8;
|
|
1686
2034
|
var ModuleGraph = class {
|
|
1687
2035
|
asts = /* @__PURE__ */ new Map();
|
|
1688
2036
|
resolved = /* @__PURE__ */ new Map();
|
|
@@ -1801,7 +2149,7 @@ var ModuleGraph = class {
|
|
|
1801
2149
|
* Devolve o arquivo e o nome sob o qual ele é definido lá.
|
|
1802
2150
|
*/
|
|
1803
2151
|
resolveBinding(file, name, hops = 0) {
|
|
1804
|
-
if (hops >
|
|
2152
|
+
if (hops > MAX_HOPS2) return null;
|
|
1805
2153
|
if (this.topLevelInit(file, name) !== null) return { file, name };
|
|
1806
2154
|
const binding = this.imports(file).get(name);
|
|
1807
2155
|
if (binding) {
|
|
@@ -1855,7 +2203,7 @@ var ModuleGraph = class {
|
|
|
1855
2203
|
* com interpolação nem valor calculado, de propósito.
|
|
1856
2204
|
*/
|
|
1857
2205
|
stringConstant(file, node, hops = 0) {
|
|
1858
|
-
if (!node || hops >
|
|
2206
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
1859
2207
|
if (BabelTypes__namespace.isStringLiteral(node)) return node.value;
|
|
1860
2208
|
if (BabelTypes__namespace.isTemplateLiteral(node)) {
|
|
1861
2209
|
return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? null : null;
|
|
@@ -1884,7 +2232,7 @@ var ModuleGraph = class {
|
|
|
1884
2232
|
* que é como o `pocketpal` monta todas as telas do Drawer).
|
|
1885
2233
|
*/
|
|
1886
2234
|
componentFile(file, node, hops = 0) {
|
|
1887
|
-
if (!node || hops >
|
|
2235
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
1888
2236
|
if (BabelTypes__namespace.isCallExpression(node)) {
|
|
1889
2237
|
for (const arg of node.arguments) {
|
|
1890
2238
|
if (BabelTypes__namespace.isIdentifier(arg) || BabelTypes__namespace.isMemberExpression(arg)) {
|
|
@@ -2080,7 +2428,7 @@ function workspaceGlobs(dir) {
|
|
|
2080
2428
|
}
|
|
2081
2429
|
function expandWorkspaceGlobs(root, globs) {
|
|
2082
2430
|
const out = [];
|
|
2083
|
-
const
|
|
2431
|
+
const add2 = (dir) => {
|
|
2084
2432
|
const pkgPath = path2__namespace.default.join(dir, "package.json");
|
|
2085
2433
|
if (!fs$1.existsSync(pkgPath)) return;
|
|
2086
2434
|
try {
|
|
@@ -2101,12 +2449,12 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
2101
2449
|
for (const entry of entries) {
|
|
2102
2450
|
const dir = path2__namespace.default.join(parent, entry);
|
|
2103
2451
|
try {
|
|
2104
|
-
if (fs$1.statSync(dir).isDirectory())
|
|
2452
|
+
if (fs$1.statSync(dir).isDirectory()) add2(dir);
|
|
2105
2453
|
} catch {
|
|
2106
2454
|
}
|
|
2107
2455
|
}
|
|
2108
2456
|
} else if (!glob.includes("*")) {
|
|
2109
|
-
|
|
2457
|
+
add2(path2__namespace.default.join(root, glob));
|
|
2110
2458
|
}
|
|
2111
2459
|
}
|
|
2112
2460
|
return out.sort((a, b) => b.name.length - a.name.length);
|
|
@@ -2426,7 +2774,7 @@ var NavigationAnalyzer = class {
|
|
|
2426
2774
|
const ast = this.graph.parse(filePath);
|
|
2427
2775
|
if (!ast) return [];
|
|
2428
2776
|
const out = /* @__PURE__ */ new Map();
|
|
2429
|
-
|
|
2777
|
+
traverse5__default.default(ast, {
|
|
2430
2778
|
CallExpression: (nodePath) => {
|
|
2431
2779
|
const callee = nodePath.node.callee;
|
|
2432
2780
|
if (!BabelTypes__namespace.isIdentifier(callee)) return;
|
|
@@ -2587,7 +2935,7 @@ var NavigationAnalyzer = class {
|
|
|
2587
2935
|
const ast = this.graph.parse(filePath);
|
|
2588
2936
|
if (!ast) return [];
|
|
2589
2937
|
const found = /* @__PURE__ */ new Map();
|
|
2590
|
-
|
|
2938
|
+
traverse5__default.default(ast, {
|
|
2591
2939
|
JSXElement: (nodePath) => {
|
|
2592
2940
|
const { node } = nodePath;
|
|
2593
2941
|
const openingElement = node.openingElement;
|
|
@@ -2659,7 +3007,7 @@ var NavigationAnalyzer = class {
|
|
|
2659
3007
|
if (!ast) return [];
|
|
2660
3008
|
const byNavigator = /* @__PURE__ */ new Map();
|
|
2661
3009
|
const resolved = /* @__PURE__ */ new Map();
|
|
2662
|
-
|
|
3010
|
+
traverse5__default.default(ast, {
|
|
2663
3011
|
JSXElement: (nodePath) => {
|
|
2664
3012
|
const name = nodePath.node.openingElement.name;
|
|
2665
3013
|
if (!BabelTypes__namespace.isJSXMemberExpression(name) || !BabelTypes__namespace.isJSXIdentifier(name.object)) return;
|
|
@@ -2892,7 +3240,7 @@ var NavigationAnalyzer = class {
|
|
|
2892
3240
|
...this.config.parserPlugins || []
|
|
2893
3241
|
]
|
|
2894
3242
|
});
|
|
2895
|
-
|
|
3243
|
+
traverse5__default.default(ast, {
|
|
2896
3244
|
TSTypeAliasDeclaration: (nodePath) => {
|
|
2897
3245
|
const { node } = nodePath;
|
|
2898
3246
|
const typeName = node.id.name;
|
|
@@ -2965,7 +3313,7 @@ var NavigationAnalyzer = class {
|
|
|
2965
3313
|
if (type.type === "TSUndefinedKeyword") return "undefined";
|
|
2966
3314
|
if (type.type === "TSNullKeyword") return "null";
|
|
2967
3315
|
if (type.type === "TSUnionType") {
|
|
2968
|
-
return type.types.map((
|
|
3316
|
+
return type.types.map((t15) => this.typeToString(t15)).join(" | ");
|
|
2969
3317
|
}
|
|
2970
3318
|
if (type.type === "TSTypeLiteral") {
|
|
2971
3319
|
return "object";
|
|
@@ -2986,7 +3334,7 @@ var NavigationAnalyzer = class {
|
|
|
2986
3334
|
/** Attach parsed type params to navigator screens */
|
|
2987
3335
|
attachParamsToNavigators(navigators, types) {
|
|
2988
3336
|
for (const navigator of navigators) {
|
|
2989
|
-
const matchingType = types.find((
|
|
3337
|
+
const matchingType = types.find((t15) => t15.type === navigator.type);
|
|
2990
3338
|
if (matchingType) {
|
|
2991
3339
|
for (const screen of navigator.screens) {
|
|
2992
3340
|
const screenParams = matchingType.paramEntries.get(screen.name);
|
|
@@ -3075,7 +3423,7 @@ var ComponentAnalyzer = class {
|
|
|
3075
3423
|
plugins: ["jsx", "typescript", ["decorators", { decoratorsBeforeExport: true }]]
|
|
3076
3424
|
});
|
|
3077
3425
|
const components = [];
|
|
3078
|
-
|
|
3426
|
+
traverse5__default.default(ast, {
|
|
3079
3427
|
JSXElement: (path11) => {
|
|
3080
3428
|
const component = this.extractComponentFromJSXElement(path11.node);
|
|
3081
3429
|
if (component) {
|
|
@@ -3203,12 +3551,12 @@ var FormAnalyzer = class {
|
|
|
3203
3551
|
this.stateVariables.clear();
|
|
3204
3552
|
this.inputElements = [];
|
|
3205
3553
|
this.submitButtons = [];
|
|
3206
|
-
|
|
3554
|
+
traverse5__default.default(ast, {
|
|
3207
3555
|
CallExpression: (path11) => {
|
|
3208
3556
|
this.extractStateVariables(path11.node);
|
|
3209
3557
|
}
|
|
3210
3558
|
});
|
|
3211
|
-
|
|
3559
|
+
traverse5__default.default(ast, {
|
|
3212
3560
|
JSXElement: (path11) => {
|
|
3213
3561
|
this.extractFormElements(path11.node);
|
|
3214
3562
|
}
|
|
@@ -3244,23 +3592,23 @@ var FormAnalyzer = class {
|
|
|
3244
3592
|
for (const attr of openingElement.attributes) {
|
|
3245
3593
|
if (BabelTypes__namespace.isJSXAttribute(attr) && BabelTypes__namespace.isJSXIdentifier(attr.name)) {
|
|
3246
3594
|
const propName = attr.name.name;
|
|
3247
|
-
const
|
|
3595
|
+
const propValue2 = this.extractAttributeValue(attr.value);
|
|
3248
3596
|
switch (propName) {
|
|
3249
3597
|
case "label":
|
|
3250
|
-
info.label =
|
|
3598
|
+
info.label = propValue2;
|
|
3251
3599
|
break;
|
|
3252
3600
|
case "placeholder":
|
|
3253
|
-
info.placeholder =
|
|
3601
|
+
info.placeholder = propValue2;
|
|
3254
3602
|
break;
|
|
3255
3603
|
case "keyboardType":
|
|
3256
|
-
info.keyboardType =
|
|
3604
|
+
info.keyboardType = propValue2;
|
|
3257
3605
|
break;
|
|
3258
3606
|
case "testID":
|
|
3259
|
-
info.testID =
|
|
3607
|
+
info.testID = propValue2;
|
|
3260
3608
|
break;
|
|
3261
3609
|
case "appilotsId":
|
|
3262
|
-
info.appilotsId =
|
|
3263
|
-
if (
|
|
3610
|
+
info.appilotsId = propValue2;
|
|
3611
|
+
if (propValue2) info.varName = propValue2;
|
|
3264
3612
|
break;
|
|
3265
3613
|
case "value":
|
|
3266
3614
|
if (attr.value && BabelTypes__namespace.isJSXExpressionContainer(attr.value) && BabelTypes__namespace.isIdentifier(attr.value.expression)) {
|
|
@@ -3309,7 +3657,7 @@ var FormAnalyzer = class {
|
|
|
3309
3657
|
}
|
|
3310
3658
|
extractValidationRules(ast) {
|
|
3311
3659
|
const rules = {};
|
|
3312
|
-
|
|
3660
|
+
traverse5__default.default(ast, {
|
|
3313
3661
|
IfStatement: (path11) => {
|
|
3314
3662
|
const test = path11.node.test;
|
|
3315
3663
|
const rule = this.extractRuleFromCondition(test);
|
|
@@ -3463,7 +3811,7 @@ async function composeAffordances(options) {
|
|
|
3463
3811
|
const exclusive = children.filter((c) => (fanIn.get(c) ?? 0) <= MAX_FAN_IN);
|
|
3464
3812
|
if (exclusive.length === 0) continue;
|
|
3465
3813
|
const actionIds = new Set(screen.actions.map((a) => a.id));
|
|
3466
|
-
const targetIds = new Set((screen.targets ?? []).map((
|
|
3814
|
+
const targetIds = new Set((screen.targets ?? []).map((t15) => t15.id));
|
|
3467
3815
|
const formIds = new Set(screen.forms.map((f) => f.id));
|
|
3468
3816
|
let gained = false;
|
|
3469
3817
|
for (const file of exclusive) {
|
|
@@ -3605,6 +3953,7 @@ var ReactNativePlatformAnalyzer = class {
|
|
|
3605
3953
|
});
|
|
3606
3954
|
return {
|
|
3607
3955
|
screens: enrichedScreens,
|
|
3956
|
+
controlEvidenceFiles: screenAnalyzer.controlEvidenceFiles,
|
|
3608
3957
|
navigation,
|
|
3609
3958
|
analyzedFiles: screenFiles.length,
|
|
3610
3959
|
...screenAnalyzer.screensFilteredOut > 0 ? { screensFilteredOut: screenAnalyzer.screensFilteredOut } : {},
|
|
@@ -3869,7 +4218,7 @@ function extractWebNavigationCalls(ast) {
|
|
|
3869
4218
|
calls.push({ method: "navigate", targetPath: node.right.value });
|
|
3870
4219
|
}
|
|
3871
4220
|
};
|
|
3872
|
-
|
|
4221
|
+
traverse5__default.default(ast, {
|
|
3873
4222
|
noScope: !BabelTypes__namespace.isFile(ast),
|
|
3874
4223
|
enter: (nodePath) => inspect(nodePath.node)
|
|
3875
4224
|
});
|
|
@@ -3991,7 +4340,7 @@ var WebScreenAnalyzer = class {
|
|
|
3991
4340
|
// ── registerScreen ────────────────────────────────────────────────
|
|
3992
4341
|
detectRegisterScreenCall(ast) {
|
|
3993
4342
|
let found = false;
|
|
3994
|
-
|
|
4343
|
+
traverse5__default.default(ast, {
|
|
3995
4344
|
CallExpression: (nodePath) => {
|
|
3996
4345
|
if (found) return;
|
|
3997
4346
|
if (isRegisterScreenCallee(nodePath.node.callee)) {
|
|
@@ -4004,7 +4353,7 @@ var WebScreenAnalyzer = class {
|
|
|
4004
4353
|
}
|
|
4005
4354
|
extractRegisterScreenMetadata(ast) {
|
|
4006
4355
|
let plain = null;
|
|
4007
|
-
|
|
4356
|
+
traverse5__default.default(ast, {
|
|
4008
4357
|
CallExpression: (nodePath) => {
|
|
4009
4358
|
if (!isRegisterScreenCallee(nodePath.node.callee)) return;
|
|
4010
4359
|
const arg = nodePath.node.arguments[0];
|
|
@@ -4052,7 +4401,7 @@ var WebScreenAnalyzer = class {
|
|
|
4052
4401
|
extractComponentName(ast) {
|
|
4053
4402
|
let defaultName = "";
|
|
4054
4403
|
let firstExported = "";
|
|
4055
|
-
|
|
4404
|
+
traverse5__default.default(ast, {
|
|
4056
4405
|
ExportDefaultDeclaration: (nodePath) => {
|
|
4057
4406
|
const declaration = nodePath.node.declaration;
|
|
4058
4407
|
if (BabelTypes__namespace.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
@@ -4081,7 +4430,7 @@ var WebScreenAnalyzer = class {
|
|
|
4081
4430
|
extractComponents(ast) {
|
|
4082
4431
|
const components = [];
|
|
4083
4432
|
const seen = /* @__PURE__ */ new Set();
|
|
4084
|
-
|
|
4433
|
+
traverse5__default.default(ast, {
|
|
4085
4434
|
JSXOpeningElement: (nodePath) => {
|
|
4086
4435
|
const element = nodePath.node;
|
|
4087
4436
|
const name = getJsxElementName(element);
|
|
@@ -4102,7 +4451,7 @@ var WebScreenAnalyzer = class {
|
|
|
4102
4451
|
// ── <label htmlFor> association ───────────────────────────────────
|
|
4103
4452
|
collectHtmlForLabels(ast) {
|
|
4104
4453
|
const labels = /* @__PURE__ */ new Map();
|
|
4105
|
-
|
|
4454
|
+
traverse5__default.default(ast, {
|
|
4106
4455
|
JSXElement: (nodePath) => {
|
|
4107
4456
|
const element = nodePath.node;
|
|
4108
4457
|
if (getJsxElementName(element.openingElement) !== "label") return;
|
|
@@ -4145,7 +4494,7 @@ var WebScreenAnalyzer = class {
|
|
|
4145
4494
|
formBuckets.set(formElement, bucket);
|
|
4146
4495
|
return bucket;
|
|
4147
4496
|
};
|
|
4148
|
-
|
|
4497
|
+
traverse5__default.default(ast, {
|
|
4149
4498
|
JSXElement: (nodePath) => {
|
|
4150
4499
|
const element = nodePath.node;
|
|
4151
4500
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -4162,7 +4511,7 @@ var WebScreenAnalyzer = class {
|
|
|
4162
4511
|
if (!bucket.fields.has(field.name)) bucket.fields.set(field.name, field);
|
|
4163
4512
|
}
|
|
4164
4513
|
});
|
|
4165
|
-
|
|
4514
|
+
traverse5__default.default(ast, {
|
|
4166
4515
|
JSXElement: (nodePath) => {
|
|
4167
4516
|
const element = nodePath.node;
|
|
4168
4517
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -4340,7 +4689,7 @@ var WebScreenAnalyzer = class {
|
|
|
4340
4689
|
const actionLabels = new Map(
|
|
4341
4690
|
actions.filter((a) => a.label).map((a) => [normalizeLabel(a.label), a])
|
|
4342
4691
|
);
|
|
4343
|
-
|
|
4692
|
+
traverse5__default.default(ast, {
|
|
4344
4693
|
JSXElement: (nodePath) => {
|
|
4345
4694
|
const element = nodePath.node;
|
|
4346
4695
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -4571,7 +4920,7 @@ var WebScreenAnalyzer = class {
|
|
|
4571
4920
|
for (const call of extractWebNavigationCalls(ast)) {
|
|
4572
4921
|
if (call.targetPath && call.targetPath.startsWith("/")) targets.add(call.targetPath);
|
|
4573
4922
|
}
|
|
4574
|
-
|
|
4923
|
+
traverse5__default.default(ast, {
|
|
4575
4924
|
JSXOpeningElement: (nodePath) => {
|
|
4576
4925
|
const element = nodePath.node;
|
|
4577
4926
|
const name = getJsxElementName(element);
|
|
@@ -4591,7 +4940,7 @@ var WebScreenAnalyzer = class {
|
|
|
4591
4940
|
extractCollections(ast) {
|
|
4592
4941
|
const collections = [];
|
|
4593
4942
|
const seen = /* @__PURE__ */ new Set();
|
|
4594
|
-
|
|
4943
|
+
traverse5__default.default(ast, {
|
|
4595
4944
|
JSXExpressionContainer: (nodePath) => {
|
|
4596
4945
|
const expr = nodePath.node.expression;
|
|
4597
4946
|
if (!BabelTypes__namespace.isCallExpression(expr) || !BabelTypes__namespace.isMemberExpression(expr.callee) || !BabelTypes__namespace.isIdentifier(expr.callee.object) || !BabelTypes__namespace.isIdentifier(expr.callee.property) || expr.callee.property.name !== "map") {
|
|
@@ -4703,7 +5052,7 @@ function firstCalledFunctionName(node) {
|
|
|
4703
5052
|
}
|
|
4704
5053
|
function containsWindowConfirm(body) {
|
|
4705
5054
|
let found = false;
|
|
4706
|
-
|
|
5055
|
+
traverse5__default.default(
|
|
4707
5056
|
body,
|
|
4708
5057
|
{
|
|
4709
5058
|
noScope: true,
|
|
@@ -4755,7 +5104,7 @@ function collectionItemNames(callback) {
|
|
|
4755
5104
|
function collectionDisplayFields(callback, itemNames) {
|
|
4756
5105
|
const fields = /* @__PURE__ */ new Set();
|
|
4757
5106
|
if (!callback.body) return [];
|
|
4758
|
-
|
|
5107
|
+
traverse5__default.default(
|
|
4759
5108
|
callback.body,
|
|
4760
5109
|
{
|
|
4761
5110
|
noScope: true,
|
|
@@ -4772,7 +5121,7 @@ function collectionDisplayFields(callback, itemNames) {
|
|
|
4772
5121
|
function collectionKeyField(callback, itemNames) {
|
|
4773
5122
|
let keyField;
|
|
4774
5123
|
if (!callback.body) return void 0;
|
|
4775
|
-
|
|
5124
|
+
traverse5__default.default(
|
|
4776
5125
|
callback.body,
|
|
4777
5126
|
{
|
|
4778
5127
|
noScope: true,
|
|
@@ -4915,7 +5264,7 @@ var WebNavigationAnalyzer = class {
|
|
|
4915
5264
|
if (BabelTypes__namespace.isJSXElement(child)) visitRoute(child, fullPath);
|
|
4916
5265
|
}
|
|
4917
5266
|
};
|
|
4918
|
-
|
|
5267
|
+
traverse5__default.default(ast, {
|
|
4919
5268
|
JSXElement: (nodePath) => {
|
|
4920
5269
|
const name = getJsxElementName(nodePath.node.openingElement);
|
|
4921
5270
|
if (name !== "Routes" && name !== "Route") return;
|
|
@@ -4954,7 +5303,7 @@ var WebNavigationAnalyzer = class {
|
|
|
4954
5303
|
"createMemoryRouter",
|
|
4955
5304
|
"useRoutes"
|
|
4956
5305
|
]);
|
|
4957
|
-
|
|
5306
|
+
traverse5__default.default(ast, {
|
|
4958
5307
|
CallExpression: (nodePath) => {
|
|
4959
5308
|
const callee = nodePath.node.callee;
|
|
4960
5309
|
if (!BabelTypes__namespace.isIdentifier(callee) || !ROUTER_FACTORIES.has(callee.name)) return;
|
|
@@ -5387,8 +5736,8 @@ var ZodParsedType = util.arrayToEnum([
|
|
|
5387
5736
|
"set"
|
|
5388
5737
|
]);
|
|
5389
5738
|
var getParsedType = (data) => {
|
|
5390
|
-
const
|
|
5391
|
-
switch (
|
|
5739
|
+
const t15 = typeof data;
|
|
5740
|
+
switch (t15) {
|
|
5392
5741
|
case "undefined":
|
|
5393
5742
|
return ZodParsedType.undefined;
|
|
5394
5743
|
case "string":
|
|
@@ -9233,7 +9582,25 @@ function isSafeImageUrl(value) {
|
|
|
9233
9582
|
return SAFE_IMAGE_URL_RE.test(trimmed);
|
|
9234
9583
|
}
|
|
9235
9584
|
|
|
9236
|
-
// ../shared/dist/chunk-
|
|
9585
|
+
// ../shared/dist/chunk-KUMPS6RH.mjs
|
|
9586
|
+
var SUPPORTED_APPILOTS_LOCALES = ["pt-BR", "en", "es", "fr"];
|
|
9587
|
+
|
|
9588
|
+
// ../shared/dist/chunk-4RYPNBBY.mjs
|
|
9589
|
+
var symbols = external_exports.array(external_exports.string().max(240)).max(12);
|
|
9590
|
+
var controlEvidenceSchema = external_exports.object({
|
|
9591
|
+
version: external_exports.literal(1),
|
|
9592
|
+
siteId: external_exports.string().regex(/^[a-f0-9]{20}$/),
|
|
9593
|
+
component: external_exports.string().max(240),
|
|
9594
|
+
icons: symbols,
|
|
9595
|
+
handler: external_exports.string().max(240).optional(),
|
|
9596
|
+
calls: symbols,
|
|
9597
|
+
argumentBindings: symbols,
|
|
9598
|
+
conditions: symbols,
|
|
9599
|
+
nativeConfirmation: external_exports.object({
|
|
9600
|
+
title: external_exports.string().max(240).optional(),
|
|
9601
|
+
destructiveOption: external_exports.boolean()
|
|
9602
|
+
}).optional()
|
|
9603
|
+
});
|
|
9237
9604
|
var locatorSourceSchema = external_exports.enum([
|
|
9238
9605
|
"appilotsId",
|
|
9239
9606
|
"testID",
|
|
@@ -9435,7 +9802,9 @@ var screenPermissionDescriptorSchema = external_exports.object({
|
|
|
9435
9802
|
agentAccess: external_exports.enum(["read", "write", "none"]).optional(),
|
|
9436
9803
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
9437
9804
|
blockedActions: external_exports.array(external_exports.string()).optional(),
|
|
9438
|
-
isPii: external_exports.boolean().optional()
|
|
9805
|
+
isPii: external_exports.boolean().optional(),
|
|
9806
|
+
/** Graded assessment — see `screenPermissionSchema.assessment`. */
|
|
9807
|
+
assessment: external_exports.boolean().optional()
|
|
9439
9808
|
}).passthrough();
|
|
9440
9809
|
var screenDescriptorSchema = external_exports.object({
|
|
9441
9810
|
name: external_exports.string(),
|
|
@@ -9660,7 +10029,9 @@ var snapshotInputSchema = external_exports.object({
|
|
|
9660
10029
|
inModal: external_exports.boolean().optional()
|
|
9661
10030
|
}).passthrough();
|
|
9662
10031
|
var snapshotButtonSchema = external_exports.object({
|
|
10032
|
+
controlEvidence: controlEvidenceSchema.optional(),
|
|
9663
10033
|
id: boundedString(160).optional(),
|
|
10034
|
+
dispatchable: external_exports.boolean().optional(),
|
|
9664
10035
|
provenance: identityProvenanceSchema.optional(),
|
|
9665
10036
|
/**
|
|
9666
10037
|
* False when the control is mounted but currently OUTSIDE the window —
|
|
@@ -9721,7 +10092,36 @@ var snapshotListSchema = external_exports.object({
|
|
|
9721
10092
|
*/
|
|
9722
10093
|
source: boundedString(40).optional(),
|
|
9723
10094
|
itemCount: external_exports.number().int().optional(),
|
|
10095
|
+
/**
|
|
10096
|
+
* Size of the whole collection when the app declared it, for a list
|
|
10097
|
+
* that is a WINDOW onto more data than it holds.
|
|
10098
|
+
*
|
|
10099
|
+
* `itemCount` is how many rows the list is rendering from and
|
|
10100
|
+
* `visibleItemCount` how many of those are mounted; neither can
|
|
10101
|
+
* express "there are 36 and you are looking at the first 20",
|
|
10102
|
+
* because a paginated list's `data` is the page. Absent means
|
|
10103
|
+
* unknown — never "same as itemCount".
|
|
10104
|
+
*/
|
|
10105
|
+
totalItemCount: external_exports.number().int().optional(),
|
|
9724
10106
|
visibleItemCount: external_exports.number().int().optional(),
|
|
10107
|
+
viewportItemCount: external_exports.number().int().nonnegative().optional(),
|
|
10108
|
+
exploration: external_exports.object({
|
|
10109
|
+
revision: external_exports.number().int().nonnegative(),
|
|
10110
|
+
observedItemCount: external_exports.number().int().min(0).max(5e3),
|
|
10111
|
+
observedRanges: external_exports.array(
|
|
10112
|
+
external_exports.object({
|
|
10113
|
+
start: external_exports.number().int().nonnegative(),
|
|
10114
|
+
end: external_exports.number().int().nonnegative()
|
|
10115
|
+
})
|
|
10116
|
+
).max(32),
|
|
10117
|
+
rangesTruncated: external_exports.boolean(),
|
|
10118
|
+
coverage: external_exports.enum(["partial", "all-loaded"]),
|
|
10119
|
+
pagination: external_exports.enum(["possible", "not-declared"]),
|
|
10120
|
+
scrollSteps: external_exports.number().int().nonnegative(),
|
|
10121
|
+
remainingScrollSteps: external_exports.number().int().nonnegative(),
|
|
10122
|
+
consecutiveNoProgress: external_exports.number().int().nonnegative(),
|
|
10123
|
+
lastScroll: external_exports.enum(["moved", "no-progress", "boundary", "unverified"]).optional()
|
|
10124
|
+
}).optional(),
|
|
9725
10125
|
refreshing: external_exports.boolean().optional(),
|
|
9726
10126
|
empty: external_exports.boolean().optional(),
|
|
9727
10127
|
label: boundedString(300).optional(),
|
|
@@ -9769,6 +10169,7 @@ var snapshotChoiceGroupSchema = external_exports.object({
|
|
|
9769
10169
|
}).passthrough();
|
|
9770
10170
|
var snapshotElementSchema = external_exports.object({
|
|
9771
10171
|
id: boundedString(160).optional(),
|
|
10172
|
+
dispatchable: external_exports.boolean().optional(),
|
|
9772
10173
|
role: boundedString(40).optional(),
|
|
9773
10174
|
label: boundedString(300).optional(),
|
|
9774
10175
|
texts: external_exports.array(boundedString(500)).max(50).optional(),
|
|
@@ -9831,6 +10232,8 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
9831
10232
|
loadingFinished: external_exports.boolean().optional(),
|
|
9832
10233
|
textsAdded: external_exports.number().int().nonnegative().optional(),
|
|
9833
10234
|
textsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
10235
|
+
buttonsAddedIndices: external_exports.array(external_exports.number().int().nonnegative()).max(12).optional(),
|
|
10236
|
+
buttonsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
9834
10237
|
visibleRowsDelta: external_exports.number().int().optional(),
|
|
9835
10238
|
totalRowsDelta: external_exports.number().int().optional(),
|
|
9836
10239
|
fieldsNewlyFilled: external_exports.array(boundedString(160)).max(12).optional(),
|
|
@@ -9856,6 +10259,16 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
9856
10259
|
}).passthrough().optional()
|
|
9857
10260
|
}).passthrough();
|
|
9858
10261
|
var agentContextSchema = external_exports.object({
|
|
10262
|
+
missionProtocol: external_exports.literal(1).optional(),
|
|
10263
|
+
missionId: boundedString(160).optional(),
|
|
10264
|
+
/** Preferred supported device language, reported by the SDK independently of map/UI labels. */
|
|
10265
|
+
deviceLocale: external_exports.enum(SUPPORTED_APPILOTS_LOCALES).optional(),
|
|
10266
|
+
/**
|
|
10267
|
+
* Per-session plain-language override (short sentences, no jargon,
|
|
10268
|
+
* numbered steps). Absent → the project's `plainLanguage` setting.
|
|
10269
|
+
* Changes how the reply is WORDED only — see `PLAIN_LANGUAGE_PROMPT`.
|
|
10270
|
+
*/
|
|
10271
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
9859
10272
|
/**
|
|
9860
10273
|
* Client platform this observation was captured on. Optional and
|
|
9861
10274
|
* additive (see `clientPlatformSchema`) — absent means
|
|
@@ -10106,6 +10519,12 @@ var actionDiagnoseSchema = external_exports.object({
|
|
|
10106
10519
|
requiresUserInput: external_exports.boolean().optional()
|
|
10107
10520
|
});
|
|
10108
10521
|
var actionResultSchema = external_exports.object({
|
|
10522
|
+
nativeConfirmation: external_exports.object({
|
|
10523
|
+
title: external_exports.string().max(300),
|
|
10524
|
+
message: external_exports.string().max(1e3).optional(),
|
|
10525
|
+
buttonLabel: external_exports.string().max(160).optional(),
|
|
10526
|
+
handlerCompleted: external_exports.boolean()
|
|
10527
|
+
}).optional(),
|
|
10109
10528
|
actionId: external_exports.string(),
|
|
10110
10529
|
type: external_exports.string(),
|
|
10111
10530
|
success: external_exports.boolean(),
|
|
@@ -10172,7 +10591,14 @@ var screenPermissionSchema = external_exports.object({
|
|
|
10172
10591
|
agentAccess: agentAccessLevelSchema.optional(),
|
|
10173
10592
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
10174
10593
|
blockedActions: external_exports.array(external_exports.string().min(1)).optional(),
|
|
10175
|
-
isPii: external_exports.boolean().optional()
|
|
10594
|
+
isPii: external_exports.boolean().optional(),
|
|
10595
|
+
/**
|
|
10596
|
+
* Graded assessment (exam, quiz, graded activity). The agent may
|
|
10597
|
+
* explain the screen and navigate away, never write: every action
|
|
10598
|
+
* except navigate/scroll is blocked server-side, and the model is told
|
|
10599
|
+
* to refuse up front instead of trying. Wins over `agentAccess: 'write'`.
|
|
10600
|
+
*/
|
|
10601
|
+
assessment: external_exports.boolean().optional()
|
|
10176
10602
|
}).strict();
|
|
10177
10603
|
external_exports.object({
|
|
10178
10604
|
agentCanWrite: external_exports.boolean(),
|
|
@@ -10180,6 +10606,10 @@ external_exports.object({
|
|
|
10180
10606
|
confirmDestructive: external_exports.boolean(),
|
|
10181
10607
|
/**
|
|
10182
10608
|
* Map of screenName → override. Keys must be non-empty strings.
|
|
10609
|
+
* A key containing `*` or starting with `/` is a PATTERN, matched
|
|
10610
|
+
* case-insensitively against the screen name and the navigation path
|
|
10611
|
+
* (`*Quiz*`; a path key uses `*` per segment, see docs/permissions).
|
|
10612
|
+
* An exact key beats patterns.
|
|
10183
10613
|
* Limited to 200 entries to keep the JSON column small and editable
|
|
10184
10614
|
* in the dashboard table view.
|
|
10185
10615
|
*/
|
|
@@ -10233,7 +10663,7 @@ external_exports.object({
|
|
|
10233
10663
|
/** null = remove webhook, undefined = leave unchanged. */
|
|
10234
10664
|
budgetWebhookUrl: external_exports.string().url().nullable().optional()
|
|
10235
10665
|
}).strict();
|
|
10236
|
-
var localeSchema = external_exports.enum(
|
|
10666
|
+
var localeSchema = external_exports.enum(SUPPORTED_APPILOTS_LOCALES);
|
|
10237
10667
|
var hexColorSchema = external_exports.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
|
|
10238
10668
|
message: "Must be a hex color like #6366f1"
|
|
10239
10669
|
});
|
|
@@ -10277,6 +10707,13 @@ var imageSourceField = external_exports.string().max(500).refine(isSafeImageUrl,
|
|
|
10277
10707
|
external_exports.object({
|
|
10278
10708
|
/** Tone / persona instructions appended to the system prompt. */
|
|
10279
10709
|
personaPrompt: external_exports.string().max(2048).nullable(),
|
|
10710
|
+
/**
|
|
10711
|
+
* Accessibility: reply in plain language (short sentences, everyday
|
|
10712
|
+
* words, numbered steps, one question at a time). Server-side only,
|
|
10713
|
+
* like `personaPrompt`; a session can override it via
|
|
10714
|
+
* `context.plainLanguage`.
|
|
10715
|
+
*/
|
|
10716
|
+
plainLanguage: external_exports.boolean(),
|
|
10280
10717
|
/** Display name in the chat header (e.g. "Aria"). */
|
|
10281
10718
|
assistantName: external_exports.string().max(60).nullable(),
|
|
10282
10719
|
/** URL or remote asset id for the avatar shown next to assistant turns. */
|
|
@@ -10300,6 +10737,7 @@ external_exports.object({
|
|
|
10300
10737
|
});
|
|
10301
10738
|
external_exports.object({
|
|
10302
10739
|
personaPrompt: external_exports.string().max(2048).nullable().optional(),
|
|
10740
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
10303
10741
|
assistantName: external_exports.string().max(60).nullable().optional(),
|
|
10304
10742
|
assistantAvatar: imageSourceField.nullable().optional(),
|
|
10305
10743
|
emptyStateIcon: imageSourceField.nullable().optional(),
|
|
@@ -10457,6 +10895,8 @@ external_exports.object({
|
|
|
10457
10895
|
* the dev doesn't want their tokens metered.
|
|
10458
10896
|
*/
|
|
10459
10897
|
silent: external_exports.boolean().optional(),
|
|
10898
|
+
/** Run with the plain-language reply style on/off, overriding the project. */
|
|
10899
|
+
plainLanguage: external_exports.boolean().optional(),
|
|
10460
10900
|
/**
|
|
10461
10901
|
* Prior turns of the conversation, in order, that precede `prompt`.
|
|
10462
10902
|
* Lets a driver run the agent loop hop-by-hop headlessly (the multi-
|
|
@@ -11244,9 +11684,22 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
11244
11684
|
const filePath = path2__namespace.default.resolve(outputDir, `mcp-document.${this.options.format}`);
|
|
11245
11685
|
await fs.writeFile(filePath, serialized, "utf-8");
|
|
11246
11686
|
console.log(`[MCPGenerator] Document written to: ${filePath}`);
|
|
11687
|
+
const controlFiles = analyzed.controlEvidenceFiles ?? {};
|
|
11688
|
+
await fs.writeFile(
|
|
11689
|
+
path2__namespace.default.resolve(outputDir, "control-evidence.json"),
|
|
11690
|
+
JSON.stringify({ version: 1, files: controlFiles }, null, 2),
|
|
11691
|
+
"utf-8"
|
|
11692
|
+
);
|
|
11247
11693
|
const checksumFilePath = path2__namespace.default.resolve(outputDir, ".appilots-checksum");
|
|
11248
11694
|
await fs.writeFile(checksumFilePath, checksum, "utf-8");
|
|
11249
11695
|
console.log(`[MCPGenerator] Checksum written to: ${checksumFilePath}`);
|
|
11696
|
+
const evidenceCount = Object.values(controlFiles).reduce(
|
|
11697
|
+
(sum, entries) => sum + entries.length,
|
|
11698
|
+
0
|
|
11699
|
+
);
|
|
11700
|
+
console.log(
|
|
11701
|
+
`[MCPGenerator] Source evidence: ${evidenceCount} icon controls across ${Object.keys(controlFiles).length} files (runtime binding required)`
|
|
11702
|
+
);
|
|
11250
11703
|
console.log("[MCPGenerator] Generation complete!");
|
|
11251
11704
|
return {
|
|
11252
11705
|
document,
|
|
@@ -11312,7 +11765,8 @@ var KNOWN_CONFIG_KEYS = [
|
|
|
11312
11765
|
"navigationExclude",
|
|
11313
11766
|
"platform",
|
|
11314
11767
|
"manifestPath",
|
|
11315
|
-
"eval"
|
|
11768
|
+
"eval",
|
|
11769
|
+
"knowledge"
|
|
11316
11770
|
];
|
|
11317
11771
|
var KEY_ALIASES = {
|
|
11318
11772
|
apiUrl: "serverUrl",
|
|
@@ -11460,7 +11914,8 @@ function saveConfig(dir, config) {
|
|
|
11460
11914
|
navigationExclude: config.navigationExclude || existingConfig?.navigationExclude,
|
|
11461
11915
|
platform: config.platform || existingConfig?.platform,
|
|
11462
11916
|
manifestPath: config.manifestPath || existingConfig?.manifestPath,
|
|
11463
|
-
eval: config.eval || existingConfig?.eval
|
|
11917
|
+
eval: config.eval || existingConfig?.eval,
|
|
11918
|
+
knowledge: config.knowledge || existingConfig?.knowledge
|
|
11464
11919
|
};
|
|
11465
11920
|
try {
|
|
11466
11921
|
fs$1.writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2), "utf-8");
|
|
@@ -11553,6 +12008,19 @@ function validateConfig(config) {
|
|
|
11553
12008
|
if (config.manifestPath !== void 0 && typeof config.manifestPath !== "string") {
|
|
11554
12009
|
errors.push("manifestPath must be a string");
|
|
11555
12010
|
}
|
|
12011
|
+
if (config.knowledge !== void 0) {
|
|
12012
|
+
if (typeof config.knowledge !== "object" || config.knowledge === null || Array.isArray(config.knowledge)) {
|
|
12013
|
+
errors.push("knowledge must be an object");
|
|
12014
|
+
} else {
|
|
12015
|
+
const kc = config.knowledge;
|
|
12016
|
+
for (const field of ["sources", "exclude"]) {
|
|
12017
|
+
const value = kc[field];
|
|
12018
|
+
if (value !== void 0 && (!Array.isArray(value) || !value.every((item) => typeof item === "string"))) {
|
|
12019
|
+
errors.push(`knowledge.${field} must be an array of strings`);
|
|
12020
|
+
}
|
|
12021
|
+
}
|
|
12022
|
+
}
|
|
12023
|
+
}
|
|
11556
12024
|
if (config.eval !== void 0) {
|
|
11557
12025
|
if (typeof config.eval !== "object" || config.eval === null || Array.isArray(config.eval)) {
|
|
11558
12026
|
errors.push("eval must be an object");
|
|
@@ -11829,6 +12297,48 @@ var AppilotsAPIClient = class {
|
|
|
11829
12297
|
};
|
|
11830
12298
|
}
|
|
11831
12299
|
}
|
|
12300
|
+
/**
|
|
12301
|
+
* Syncs knowledge documents with the Appilots backend.
|
|
12302
|
+
*
|
|
12303
|
+
* @param documents Array of documents to sync (filename, base64 content, mimeType, checksum)
|
|
12304
|
+
* @returns KnowledgeSyncResult with counts of uploaded, skipped, and errored docs
|
|
12305
|
+
*/
|
|
12306
|
+
async knowledgeSync(documents) {
|
|
12307
|
+
try {
|
|
12308
|
+
const response = await this.request(`${this.baseUrl}/cli/knowledge/sync`, {
|
|
12309
|
+
method: "POST",
|
|
12310
|
+
headers: {
|
|
12311
|
+
"Content-Type": "application/json",
|
|
12312
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
12313
|
+
},
|
|
12314
|
+
body: JSON.stringify({ documents })
|
|
12315
|
+
});
|
|
12316
|
+
if (!response.ok) {
|
|
12317
|
+
const errorData = await response.json().catch(() => ({}));
|
|
12318
|
+
return {
|
|
12319
|
+
success: false,
|
|
12320
|
+
uploaded: 0,
|
|
12321
|
+
skipped: 0,
|
|
12322
|
+
errors: 0,
|
|
12323
|
+
error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`)
|
|
12324
|
+
};
|
|
12325
|
+
}
|
|
12326
|
+
const json = await response.json();
|
|
12327
|
+
const inner = json.data ?? json;
|
|
12328
|
+
return {
|
|
12329
|
+
success: true,
|
|
12330
|
+
...inner
|
|
12331
|
+
};
|
|
12332
|
+
} catch (error) {
|
|
12333
|
+
return {
|
|
12334
|
+
success: false,
|
|
12335
|
+
uploaded: 0,
|
|
12336
|
+
skipped: 0,
|
|
12337
|
+
errors: 0,
|
|
12338
|
+
error: error instanceof Error ? error.message : "Failed to sync knowledge with Appilots API"
|
|
12339
|
+
};
|
|
12340
|
+
}
|
|
12341
|
+
}
|
|
11832
12342
|
/**
|
|
11833
12343
|
* Checks if the Appilots API server is healthy
|
|
11834
12344
|
*
|
|
@@ -11847,7 +12357,7 @@ var AppilotsAPIClient = class {
|
|
|
11847
12357
|
};
|
|
11848
12358
|
|
|
11849
12359
|
// src/version.ts
|
|
11850
|
-
var CLI_VERSION = "0.
|
|
12360
|
+
var CLI_VERSION = "0.14.0";
|
|
11851
12361
|
|
|
11852
12362
|
exports.AppilotsAPIClient = AppilotsAPIClient;
|
|
11853
12363
|
exports.CLI_VERSION = CLI_VERSION;
|