@appilots/sdk 0.7.0 → 0.10.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 +46 -4
- package/dist/.build-meta.json +5 -0
- package/dist/{chunk-KYFAXT6V.js → chunk-2GCWCPSD.js} +19 -16
- package/dist/{chunk-R4D34FEW.mjs → chunk-AHHDOUVX.mjs} +2151 -333
- package/dist/{chunk-DR75QTYK.mjs → chunk-IHPLS5FE.mjs} +16 -13
- package/dist/{chunk-HFRIB4YN.js → chunk-VX7AL4SA.js} +1049 -1162
- package/dist/{chunk-KUFWRJC4.mjs → chunk-XHR65V2W.mjs} +831 -949
- package/dist/{chunk-DZ7QRFHD.js → chunk-YB77RYCC.js} +2178 -332
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +11 -11
- package/dist/hooks/index.mjs +2 -2
- package/dist/{index-nI-s3Exg.d.mts → index-BPZ0j71S.d.mts} +315 -15
- package/dist/{index-nI-s3Exg.d.ts → index-BPZ0j71S.d.ts} +315 -15
- package/dist/index.d.mts +325 -22
- package/dist/index.d.ts +325 -22
- package/dist/index.js +340 -227
- package/dist/index.mjs +274 -173
- package/dist/navigation/index.d.mts +171 -3
- package/dist/navigation/index.d.ts +171 -3
- package/dist/navigation/index.js +13 -13
- package/dist/navigation/index.mjs +2 -2
- package/metro.d.ts +16 -1
- package/metro.js +104 -10
- package/package.json +16 -6
- package/dist/registerScreen-D1oGm28T.d.mts +0 -159
- package/dist/registerScreen-D1oGm28T.d.ts +0 -159
|
@@ -14,6 +14,153 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
14
14
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
+
// ../shared/dist/chunk-UEMUBXFH.mjs
|
|
18
|
+
var DEFAULT_API_BASE_URL = "https://api.appilots.com";
|
|
19
|
+
var APPILOTS_API_PATH_PREFIX = "/api/v1";
|
|
20
|
+
function normalizeApiBaseUrl(configured) {
|
|
21
|
+
const raw = (configured ?? DEFAULT_API_BASE_URL).trim().replace(/\/+$/, "");
|
|
22
|
+
if (!raw) return `${DEFAULT_API_BASE_URL}${APPILOTS_API_PATH_PREFIX}`;
|
|
23
|
+
if (new RegExp(`${APPILOTS_API_PATH_PREFIX}(/|$)`).test(raw)) return raw;
|
|
24
|
+
return `${raw}${APPILOTS_API_PATH_PREFIX}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ../shared/dist/screen-text/index.mjs
|
|
28
|
+
function build(table, extra = []) {
|
|
29
|
+
return [...Object.values(table).flat(), ...extra].join("|");
|
|
30
|
+
}
|
|
31
|
+
var VALIDATION = {
|
|
32
|
+
pt: [
|
|
33
|
+
"inv[a\xE1]lid",
|
|
34
|
+
"obrigat[o\xF3]ri",
|
|
35
|
+
"j[a\xE1]\\s+existe",
|
|
36
|
+
"duplicad",
|
|
37
|
+
"muito\\s+curto",
|
|
38
|
+
"n[a\xE3]o\\s+(?:foi|pode)",
|
|
39
|
+
"deve\\s+ser",
|
|
40
|
+
"selecione",
|
|
41
|
+
"escolha",
|
|
42
|
+
"preencha",
|
|
43
|
+
"corrija"
|
|
44
|
+
],
|
|
45
|
+
en: [
|
|
46
|
+
"invalid",
|
|
47
|
+
"required",
|
|
48
|
+
"already\\s+exists",
|
|
49
|
+
"too\\s+short",
|
|
50
|
+
"must\\s+be",
|
|
51
|
+
"select\\s+(?:the|a|an)",
|
|
52
|
+
"please\\s+(?:enter|choose|select|fill)",
|
|
53
|
+
"can'?t\\s+be\\s+(?:empty|blank)",
|
|
54
|
+
"cannot\\s+be\\s+(?:empty|blank)"
|
|
55
|
+
],
|
|
56
|
+
es: [
|
|
57
|
+
"obligatori",
|
|
58
|
+
"ya\\s+existe",
|
|
59
|
+
"demasiado\\s+corto",
|
|
60
|
+
"debe\\s+ser",
|
|
61
|
+
"seleccione",
|
|
62
|
+
"elija",
|
|
63
|
+
"complet[ae]\\s+(?:el|los|este)",
|
|
64
|
+
"no\\s+puede\\s+(?:estar|quedar)\\s+vac"
|
|
65
|
+
],
|
|
66
|
+
fr: [
|
|
67
|
+
"invalide",
|
|
68
|
+
"obligatoire",
|
|
69
|
+
"existe\\s+d[e\xE9]j[a\xE0]",
|
|
70
|
+
"trop\\s+court",
|
|
71
|
+
"doit\\s+[\xEAe]tre",
|
|
72
|
+
"s[e\xE9]lectionnez",
|
|
73
|
+
"choisissez",
|
|
74
|
+
"veuillez\\s+(?:saisir|choisir|remplir|s[e\xE9]lectionner)",
|
|
75
|
+
"ne\\s+peut\\s+pas\\s+[\xEAe]tre\\s+vide"
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
var VALIDATION_SHARED = ["format[oa]?\\b", "\\bformat\\b"];
|
|
79
|
+
var WRITE_SUCCESS = {
|
|
80
|
+
pt: [
|
|
81
|
+
"cadastrad[oa]s?\\s+com\\s+sucesso",
|
|
82
|
+
"registrad[oa]s?\\s+com\\s+sucesso",
|
|
83
|
+
"salv[oa]s?\\s+com\\s+sucesso",
|
|
84
|
+
"criad[oa]s?\\s+com\\s+sucesso",
|
|
85
|
+
"atualizad[oa]s?\\s+com\\s+sucesso",
|
|
86
|
+
"foi\\s+cadastrad[oa]",
|
|
87
|
+
"foi\\s+registrad[oa]",
|
|
88
|
+
"foi\\s+salv[oa]",
|
|
89
|
+
"foi\\s+criad[oa]"
|
|
90
|
+
],
|
|
91
|
+
en: [
|
|
92
|
+
"successfully\\s+(?:registered|saved|created|updated|added)",
|
|
93
|
+
"(?:has\\s+been|was)\\s+(?:registered|saved|created|updated|added)"
|
|
94
|
+
],
|
|
95
|
+
es: [
|
|
96
|
+
"registrad[oa]s?\\s+(?:exitosamente|correctamente|con\\s+[e\xE9]xito)",
|
|
97
|
+
"guardad[oa]s?\\s+(?:correctamente|con\\s+[e\xE9]xito)",
|
|
98
|
+
"cread[oa]s?\\s+(?:correctamente|con\\s+[e\xE9]xito)",
|
|
99
|
+
"se\\s+ha\\s+(?:guardado|creado|registrado|actualizado)"
|
|
100
|
+
],
|
|
101
|
+
fr: [
|
|
102
|
+
"(?:enregistr|cr[e\xE9]|ajout|mis\\s+[a\xE0]\\s+jour)[e\xE9]?e?s?\\s+avec\\s+succ[e\xE8]s",
|
|
103
|
+
"a\\s+[e\xE9]t[e\xE9]\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]e?"
|
|
104
|
+
]
|
|
105
|
+
};
|
|
106
|
+
var COMPLETION_CLAIM = {
|
|
107
|
+
pt: [
|
|
108
|
+
"j[a\xE1]\\s+(?:cadastrei|registrei|salvei|criei)",
|
|
109
|
+
"conclu[i\xED]\\s+o\\s+cadastro",
|
|
110
|
+
"cadastro\\s+(?:foi\\s+)?(?:conclu[i\xED]do|realizado|feito)"
|
|
111
|
+
],
|
|
112
|
+
en: [
|
|
113
|
+
"i(?:'|\u2019)?ve\\s+(?:registered|saved|created|added)",
|
|
114
|
+
"i\\s+have\\s+(?:registered|saved|created|added)"
|
|
115
|
+
],
|
|
116
|
+
es: ["ya\\s+(?:lo\\s+|la\\s+)?(?:registr[e\xE9]|guard[e\xE9]|cre[e\xE9])"],
|
|
117
|
+
fr: ["j(?:e\\s+l(?:'|\u2019)ai|(?:'|\u2019)ai)\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]"]
|
|
118
|
+
};
|
|
119
|
+
var SUBMIT_CONTROL = {
|
|
120
|
+
pt: ["cadastrar", "registrar", "salvar", "enviar", "confirmar"],
|
|
121
|
+
en: ["submit", "register", "save", "confirm"],
|
|
122
|
+
es: ["guardar", "registrar", "enviar", "confirmar", "aceptar"],
|
|
123
|
+
fr: ["enregistrer", "soumettre", "envoyer", "confirmer", "valider"]
|
|
124
|
+
};
|
|
125
|
+
var SUBMIT_LABEL_EXTRA = {
|
|
126
|
+
pt: ["criar", "adicionar", "concluir", "finalizar"],
|
|
127
|
+
en: ["create", "add", "done", "finish", "continue"],
|
|
128
|
+
es: ["crear", "a[\xF1n]adir", "agregar", "continuar", "finalizar"],
|
|
129
|
+
fr: ["cr[e\xE9]er", "ajouter", "terminer", "continuer"]
|
|
130
|
+
};
|
|
131
|
+
var CREATE_EDIT_ROUTE = {
|
|
132
|
+
pt: ["cadastr", "criar", "novo", "nova", "registr", "edit"],
|
|
133
|
+
en: ["create", "edit", "form", "new", "add"],
|
|
134
|
+
es: ["crear", "nuevo", "nueva", "editar", "alta"],
|
|
135
|
+
fr: ["cr[e\xE9]er", "nouveau", "nouvelle", "modifier", "ajout"]
|
|
136
|
+
};
|
|
137
|
+
var WRITE_INTENT = {
|
|
138
|
+
pt: ["cadastr\\w*", "registr\\w*", "cria\\w*", "adiciona\\w*", "salv\\w*"],
|
|
139
|
+
en: [
|
|
140
|
+
"creat(?:e|es|ed|ing|ion)",
|
|
141
|
+
"save[sd]?",
|
|
142
|
+
"saving",
|
|
143
|
+
"add(?:s|ed|ing)?",
|
|
144
|
+
"submit\\w*",
|
|
145
|
+
"register\\w*"
|
|
146
|
+
],
|
|
147
|
+
es: ["crea\\w*", "guarda\\w*", "a[n\xF1]ad\\w*", "agrega\\w*", "registra\\w*"],
|
|
148
|
+
fr: ["cr[e\xE9][e\xE9]\\w*", "enregistr\\w*", "ajout\\w*", "sauvegard\\w*", "soumet\\w*"]
|
|
149
|
+
};
|
|
150
|
+
var FORM_VALIDATION_TEXT_RE = new RegExp(`(${build(VALIDATION, VALIDATION_SHARED)})`, "i");
|
|
151
|
+
var FORM_WRITE_SUCCESS_TEXT_RE = new RegExp(`\\b(${build(WRITE_SUCCESS)})`, "i");
|
|
152
|
+
new RegExp(`\\b(${build(WRITE_INTENT)})\\b`, "i");
|
|
153
|
+
new RegExp(
|
|
154
|
+
`\\b(${build(WRITE_SUCCESS)}|${build(COMPLETION_CLAIM)})`,
|
|
155
|
+
"i"
|
|
156
|
+
);
|
|
157
|
+
var SUBMIT_CONTROL_RE = new RegExp(`(${build(SUBMIT_CONTROL)})`, "i");
|
|
158
|
+
new RegExp(
|
|
159
|
+
`(${build(SUBMIT_CONTROL)}|${build(SUBMIT_LABEL_EXTRA)})`,
|
|
160
|
+
"i"
|
|
161
|
+
);
|
|
162
|
+
var CREATE_EDIT_ROUTE_RE = new RegExp(`(${build(CREATE_EDIT_ROUTE)})`, "i");
|
|
163
|
+
|
|
17
164
|
// ../shared/dist/chunk-43YQ52Q7.mjs
|
|
18
165
|
var SENSITIVE_FIELD_NAME_RE = /(\b|_|-)(password|senha|contrase[ñn]a|mot[\s_-]*de[\s_-]*passe|passwd|pwd|secret|token|api[\s_-]*key|access[\s_-]*key|private[\s_-]*key|cvv|cvc|cvn|cvc2|cid|pin(?:[\s_-]*code)?|otp|one[\s_-]*time[\s_-]*code|2fa|mfa|security[\s_-]*code|c[óo]digo[\s_-]*seguran[çc]a|c[óo]digo[\s_-]*verifica[çc][ãa]o|verification[\s_-]*code)(\b|_|-)/i;
|
|
19
166
|
function isSensitiveFieldName(id, label, placeholder, type) {
|
|
@@ -28,6 +175,39 @@ function isSensitiveFieldName(id, label, placeholder, type) {
|
|
|
28
175
|
|
|
29
176
|
// ../client-core/dist/index.mjs
|
|
30
177
|
var SDK_VERSION = "0.1.0";
|
|
178
|
+
var _enabled;
|
|
179
|
+
var _resolve;
|
|
180
|
+
function setAppilotsDebugEnabled(enabled) {
|
|
181
|
+
_enabled = enabled;
|
|
182
|
+
}
|
|
183
|
+
function setAppilotsDebugResolver(resolve) {
|
|
184
|
+
_resolve = resolve;
|
|
185
|
+
}
|
|
186
|
+
function isAppilotsDebugEnabled() {
|
|
187
|
+
if (_enabled !== void 0) return _enabled;
|
|
188
|
+
try {
|
|
189
|
+
return _resolve?.() === true;
|
|
190
|
+
} catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function appilotsDebugLog(...args) {
|
|
195
|
+
if (!isAppilotsDebugEnabled()) return;
|
|
196
|
+
console.log("[Appilots]", ...args);
|
|
197
|
+
}
|
|
198
|
+
function appilotsDebugWarn(...args) {
|
|
199
|
+
if (!isAppilotsDebugEnabled()) return;
|
|
200
|
+
console.log("[Appilots]", ...args);
|
|
201
|
+
}
|
|
202
|
+
function describeValue(value) {
|
|
203
|
+
if (value === void 0) return "undefined";
|
|
204
|
+
if (value === null) return "null";
|
|
205
|
+
if (typeof value === "string") return `string(len=${value.length})`;
|
|
206
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
207
|
+
if (Array.isArray(value)) return `array(len=${value.length})`;
|
|
208
|
+
if (typeof value === "object") return `object(keys=${Object.keys(value).length})`;
|
|
209
|
+
return typeof value;
|
|
210
|
+
}
|
|
31
211
|
var StreamTransportError = class extends Error {
|
|
32
212
|
constructor(message) {
|
|
33
213
|
super(message);
|
|
@@ -58,6 +238,13 @@ function parseRetryAfter(...candidates) {
|
|
|
58
238
|
return 60;
|
|
59
239
|
}
|
|
60
240
|
var CONTINUATION_TIMEOUT_MS = 15e4;
|
|
241
|
+
function safeJson(text) {
|
|
242
|
+
try {
|
|
243
|
+
return JSON.parse(text || "{}");
|
|
244
|
+
} catch {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
61
248
|
var AppilotsClient = class {
|
|
62
249
|
baseUrl;
|
|
63
250
|
projectId;
|
|
@@ -72,7 +259,7 @@ var AppilotsClient = class {
|
|
|
72
259
|
identified = false;
|
|
73
260
|
constructor(options) {
|
|
74
261
|
this.projectId = options.projectId;
|
|
75
|
-
this.baseUrl = (options.apiBaseUrl
|
|
262
|
+
this.baseUrl = normalizeApiBaseUrl(options.apiBaseUrl);
|
|
76
263
|
this.timeout = options.timeout ?? 6e4;
|
|
77
264
|
this.debug = options.debug ?? false;
|
|
78
265
|
this.mcpVersion = options.mcpVersion;
|
|
@@ -82,15 +269,34 @@ var AppilotsClient = class {
|
|
|
82
269
|
"Content-Type": "application/json",
|
|
83
270
|
// Version telemetry: lets the server correlate behavior per SDK
|
|
84
271
|
// release and pick the MCP doc matching this app build (B.4).
|
|
85
|
-
|
|
272
|
+
// The platform SDK supplies its own published version; the
|
|
273
|
+
// client-core constant is only the fallback (see `sdkVersion`).
|
|
274
|
+
"X-Appilots-Sdk-Version": options.sdkVersion ?? SDK_VERSION,
|
|
86
275
|
...options.appVersion ? { "X-App-Version": options.appVersion } : {},
|
|
87
276
|
...options.mcpVersion ? { "X-Appilots-Mcp-Version": options.mcpVersion } : {},
|
|
88
277
|
...options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {},
|
|
89
278
|
...options.headers
|
|
90
279
|
};
|
|
91
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* A 404 on an `/agent/*` path is almost never a missing record — those
|
|
283
|
+
* routes are static. It means the request reached SOMETHING that is not
|
|
284
|
+
* this API, or reached it at the wrong mount point, and the server's own
|
|
285
|
+
* message ("Route not found") tells the integrator nothing about which.
|
|
286
|
+
*
|
|
287
|
+
* The symptom is unmistakable once you have seen it: every message
|
|
288
|
+
* fails, including a plain "hello" that needs no tool at all, because
|
|
289
|
+
* nothing ever reaches the agent. Left as-is it reads like the agent is
|
|
290
|
+
* broken.
|
|
291
|
+
*/
|
|
292
|
+
explainIfMisroutedBaseUrl(status, json, fallback) {
|
|
293
|
+
if (status !== 404) return fallback;
|
|
294
|
+
const code = json?.error?.code;
|
|
295
|
+
if (code !== "NOT_FOUND") return fallback;
|
|
296
|
+
return `${fallback} \u2014 the Appilots API answered 404 for every path under ${this.baseUrl}. That usually means apiBaseUrl points somewhere this API is not mounted. The hosted API is https://api.appilots.com${APPILOTS_API_PATH_PREFIX}; a local stack is http://localhost:4000${APPILOTS_API_PATH_PREFIX}.`;
|
|
297
|
+
}
|
|
92
298
|
log(...args) {
|
|
93
|
-
if (this.debug)
|
|
299
|
+
if (this.debug) appilotsDebugLog("[AppilotsClient]", ...args);
|
|
94
300
|
}
|
|
95
301
|
describeNonJsonResponse(url, response, body) {
|
|
96
302
|
const contentType = response.headers?.get?.("content-type") ?? "unknown content-type";
|
|
@@ -124,13 +330,10 @@ var AppilotsClient = class {
|
|
|
124
330
|
if (response.status === 429) {
|
|
125
331
|
throw new RateLimitedError(
|
|
126
332
|
errorMsg,
|
|
127
|
-
parseRetryAfter(
|
|
128
|
-
json?.error?.retryAfterSeconds,
|
|
129
|
-
response.headers?.get?.("retry-after")
|
|
130
|
-
)
|
|
333
|
+
parseRetryAfter(json?.error?.retryAfterSeconds, response.headers?.get?.("retry-after"))
|
|
131
334
|
);
|
|
132
335
|
}
|
|
133
|
-
throw new Error(errorMsg);
|
|
336
|
+
throw new Error(this.explainIfMisroutedBaseUrl(response.status, json, errorMsg));
|
|
134
337
|
}
|
|
135
338
|
return json.data ?? json;
|
|
136
339
|
} finally {
|
|
@@ -197,7 +400,8 @@ var AppilotsClient = class {
|
|
|
197
400
|
introspectionFragment() {
|
|
198
401
|
try {
|
|
199
402
|
const report = this.introspectionReporter?.();
|
|
200
|
-
if (!report
|
|
403
|
+
if (!report) return {};
|
|
404
|
+
if (report.captured !== false && !report.failureReason) return {};
|
|
201
405
|
return { introspection: report };
|
|
202
406
|
} catch {
|
|
203
407
|
return {};
|
|
@@ -244,7 +448,11 @@ var AppilotsClient = class {
|
|
|
244
448
|
content: data.message.summary,
|
|
245
449
|
timestamp: Date.now()
|
|
246
450
|
} : void 0;
|
|
247
|
-
this.log("Response received:", {
|
|
451
|
+
this.log("Response received:", {
|
|
452
|
+
messageId: message.id,
|
|
453
|
+
actions: actions?.length ?? 0,
|
|
454
|
+
hasSummary: !!summaryMessage
|
|
455
|
+
});
|
|
248
456
|
return {
|
|
249
457
|
sessionId: this.sessionId,
|
|
250
458
|
message,
|
|
@@ -385,17 +593,17 @@ var AppilotsClient = class {
|
|
|
385
593
|
fail(
|
|
386
594
|
new RateLimitedError(
|
|
387
595
|
message2,
|
|
388
|
-
parseRetryAfter(
|
|
389
|
-
retryAfterSeconds,
|
|
390
|
-
xhr.getResponseHeader?.("retry-after")
|
|
391
|
-
)
|
|
596
|
+
parseRetryAfter(retryAfterSeconds, xhr.getResponseHeader?.("retry-after"))
|
|
392
597
|
)
|
|
393
598
|
);
|
|
394
599
|
return;
|
|
395
600
|
}
|
|
396
|
-
|
|
397
|
-
|
|
601
|
+
const explained = this.explainIfMisroutedBaseUrl(
|
|
602
|
+
xhr.status,
|
|
603
|
+
safeJson(xhr.responseText),
|
|
604
|
+
message2
|
|
398
605
|
);
|
|
606
|
+
fail(sawAnyEvent ? new Error(explained) : new StreamTransportError(explained));
|
|
399
607
|
return;
|
|
400
608
|
}
|
|
401
609
|
if (!doneEvent) {
|
|
@@ -577,7 +785,9 @@ var AppilotsClient = class {
|
|
|
577
785
|
};
|
|
578
786
|
case "navigate":
|
|
579
787
|
return {
|
|
580
|
-
|
|
788
|
+
// No `?? ''` here: an empty string would make a goBack look
|
|
789
|
+
// like a navigate to a nameless screen further down (#398).
|
|
790
|
+
screenName: typeof payload.screenName === "string" ? payload.screenName : void 0,
|
|
581
791
|
params: payload.params,
|
|
582
792
|
navigationAction: payload.navigationAction ?? "navigate",
|
|
583
793
|
path: Array.isArray(payload.path) ? payload.path : void 0
|
|
@@ -1110,8 +1320,15 @@ function subscribeAppilotsDebugTraces(listener) {
|
|
|
1110
1320
|
};
|
|
1111
1321
|
}
|
|
1112
1322
|
var INTERNAL_MARKER_RE = /^\[(?:Permission policy notice|Recovery exhausted|User rejected the proposed action)[^\]]*\]:\s*/i;
|
|
1113
|
-
var
|
|
1114
|
-
var
|
|
1323
|
+
var INTERNAL_ID_SRC = 'el:[^\\s".]+|list-\\d+-item-\\d+';
|
|
1324
|
+
var TECHNICAL_FIELD_SRC = "\\b(?:targetId|componentId|screenName|actionId|fieldId|elementId|testID|test-id|accessibilityId|accessibility-id)\\s*[:=_-]\\s*\\S+";
|
|
1325
|
+
var TECHNICAL_SELECTOR_RE = new RegExp(`${INTERNAL_ID_SRC}|${TECHNICAL_FIELD_SRC}`, "i");
|
|
1326
|
+
var TOOL_NAME_SRC = "(?:form_fill|ui_interaction|scroll_list)\\b";
|
|
1327
|
+
var TOOL_VERB_WITH_ID_SRC = `(?:press|click|tap|navigate)\\w*\\b[^\\n]{0,40}?(?:${INTERNAL_ID_SRC})`;
|
|
1328
|
+
var TECHNICAL_TRACE_RE = new RegExp(
|
|
1329
|
+
`(^|\\n)\\s*(?:Results:|[-*]\\s*[\u2713\u2717]\\s+|${TOOL_NAME_SRC}|${TOOL_VERB_WITH_ID_SRC}|(?:Cliquei|Clicked)\\s+em\\s+(?:${INTERNAL_ID_SRC}|[A-Za-z][\\w-]*:))`,
|
|
1330
|
+
"i"
|
|
1331
|
+
);
|
|
1115
1332
|
function sanitizeVisibleAssistantContent(content) {
|
|
1116
1333
|
return content.split("\n").map((line) => line.replace(INTERNAL_MARKER_RE, "").trimEnd()).filter((line) => !INTERNAL_MARKER_RE.test(line)).join("\n").trim();
|
|
1117
1334
|
}
|
|
@@ -1119,7 +1336,7 @@ function humanizeAssistantFallback(content) {
|
|
|
1119
1336
|
let text = sanitizeVisibleAssistantContent(content).replace(/\blist-\d+-item-\d+\b/gi, "o item da lista").replace(/\bel:[^\s".]+/gi, "o elemento").replace(
|
|
1120
1337
|
/"\s*(?:press|click|tap|navigate|form_fill|ui_interaction|scroll_list)\s+on\s+[^"]+"/gi,
|
|
1121
1338
|
'"a a\xE7\xE3o solicitada"'
|
|
1122
|
-
).
|
|
1339
|
+
).trim();
|
|
1123
1340
|
if (text.length < 16) return null;
|
|
1124
1341
|
const probe = {
|
|
1125
1342
|
role: "assistant",
|
|
@@ -1134,9 +1351,8 @@ function isInternalTraceMessage(message) {
|
|
|
1134
1351
|
if (!content) return true;
|
|
1135
1352
|
return TECHNICAL_TRACE_RE.test(content) || TECHNICAL_SELECTOR_RE.test(content);
|
|
1136
1353
|
}
|
|
1137
|
-
var CREATE_FORM_ROUTE_RE =
|
|
1138
|
-
var SUBMIT_BUTTON_RE =
|
|
1139
|
-
var FORM_VALIDATION_TEXT_RE = /(inv[aá]lid|obrigat[oó]ri|required|invalid|j[aá]\s+existe|already\s+exists|duplicad|formato|format|too\s+short|muito\s+curto|n[aã]o\s+(?:foi|pode)|must\s+be|deve\s+ser|selecione|escolha|select\s+(?:the|a|o|an))/i;
|
|
1354
|
+
var CREATE_FORM_ROUTE_RE = CREATE_EDIT_ROUTE_RE;
|
|
1355
|
+
var SUBMIT_BUTTON_RE = SUBMIT_CONTROL_RE;
|
|
1140
1356
|
function normalize2(value) {
|
|
1141
1357
|
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
1142
1358
|
}
|
|
@@ -1154,7 +1370,9 @@ function snapshotHasVisibleSubmitButton(snap) {
|
|
|
1154
1370
|
);
|
|
1155
1371
|
}
|
|
1156
1372
|
function snapshotHasVisibleValidation(snap) {
|
|
1157
|
-
return (snap.texts ?? []).some(
|
|
1373
|
+
return (snap.texts ?? []).some(
|
|
1374
|
+
(text) => typeof text === "string" && FORM_VALIDATION_TEXT_RE.test(text)
|
|
1375
|
+
);
|
|
1158
1376
|
}
|
|
1159
1377
|
function snapshotShowsOpenCreateForm(snap, activePath = []) {
|
|
1160
1378
|
if (!snap) return false;
|
|
@@ -1571,9 +1789,10 @@ var ChatSessionMachine = class {
|
|
|
1571
1789
|
};
|
|
1572
1790
|
}
|
|
1573
1791
|
const hasToolWithoutEffect = toolWithoutEffectIds.has(actionId);
|
|
1792
|
+
const observedNoEffect = hasToolWithoutEffect || r.effect === "none";
|
|
1574
1793
|
let recoveryAttempt = 0;
|
|
1575
1794
|
let recoveryExhausted = false;
|
|
1576
|
-
if (!r.success ||
|
|
1795
|
+
if (!r.skipped && (!r.success || observedNoEffect)) {
|
|
1577
1796
|
const previous = this.recoveryCountByFingerprint.get(r.fingerprint) ?? 0;
|
|
1578
1797
|
const next = previous + 1;
|
|
1579
1798
|
this.recoveryCountByFingerprint.set(r.fingerprint, next);
|
|
@@ -1590,16 +1809,18 @@ var ChatSessionMachine = class {
|
|
|
1590
1809
|
recoveryAttempt,
|
|
1591
1810
|
recoveryExhausted,
|
|
1592
1811
|
toolWithoutEffect: hasToolWithoutEffect,
|
|
1593
|
-
effect:
|
|
1812
|
+
effect: observedNoEffect ? "none" : r.effect,
|
|
1594
1813
|
userVisibleStatusKey: r.userVisibleStatusKey
|
|
1595
1814
|
};
|
|
1596
1815
|
});
|
|
1597
1816
|
const exhausted = results.filter((r) => r.recoveryExhausted);
|
|
1598
1817
|
this.setState({
|
|
1599
|
-
loadingStatusKey: results.some(
|
|
1818
|
+
loadingStatusKey: results.some(
|
|
1819
|
+
(r) => !r.success || r.toolWithoutEffect || r.effect === "none"
|
|
1820
|
+
) ? "statusAdjusting" : "statusAnalyzing"
|
|
1600
1821
|
});
|
|
1601
|
-
|
|
1602
|
-
`
|
|
1822
|
+
appilotsDebugLog(
|
|
1823
|
+
`ChatSessionMachine: running continuation hop=${turn.hop} (${results.filter((r) => r.success).length}/${results.length} succeeded` + (exhausted.length > 0 ? `, ${exhausted.length} recovery-exhausted` : "") + ")"
|
|
1603
1824
|
);
|
|
1604
1825
|
const response = await this.deps.client.continueAgent(results, context, turn.hop);
|
|
1605
1826
|
this.addAssistantMessage(response.message);
|
|
@@ -1672,6 +1893,7 @@ var ChatSessionMachine = class {
|
|
|
1672
1893
|
const diagnose = event.data?.diagnose;
|
|
1673
1894
|
const effect = event.data?.effect;
|
|
1674
1895
|
const userVisibleStatusKey = event.data?.userVisibleStatusKey;
|
|
1896
|
+
const skipped = event.data?.skipped === true;
|
|
1675
1897
|
const existing = turn.results.get(actionId);
|
|
1676
1898
|
if (existing) {
|
|
1677
1899
|
existing.success = success;
|
|
@@ -1679,6 +1901,7 @@ var ChatSessionMachine = class {
|
|
|
1679
1901
|
existing.diagnose = diagnose;
|
|
1680
1902
|
existing.effect = effect;
|
|
1681
1903
|
existing.userVisibleStatusKey = userVisibleStatusKey;
|
|
1904
|
+
existing.skipped = skipped;
|
|
1682
1905
|
}
|
|
1683
1906
|
turn.reportedIds.add(actionId);
|
|
1684
1907
|
if (turn.reportedIds.size >= turn.actionIds.size) {
|
|
@@ -1715,8 +1938,8 @@ var ChatSessionMachine = class {
|
|
|
1715
1938
|
this.abortController = abortController;
|
|
1716
1939
|
try {
|
|
1717
1940
|
const context = this.deps.adapter.buildContext();
|
|
1718
|
-
|
|
1719
|
-
`
|
|
1941
|
+
appilotsDebugLog(
|
|
1942
|
+
`ChatSessionMachine sendMessage: context \u2014 currentScreen="${context.currentScreen ?? "unknown"}", snapshot={texts:${context.snapshot?.texts?.length ?? 0}, inputs:${context.snapshot?.inputs?.length ?? 0}, buttons:${context.snapshot?.buttons?.length ?? 0}, elements:${context.snapshot?.elements?.length ?? 0}, lists:${context.snapshot?.lists?.length ?? 0}, listItems:${(context.snapshot?.lists ?? []).reduce((acc, l) => acc + (l.items?.length ?? 0), 0)}, choiceGroups:${context.snapshot?.choiceGroups?.length ?? 0}}`
|
|
1720
1943
|
);
|
|
1721
1944
|
let response;
|
|
1722
1945
|
if (streamingEnabled) {
|
|
@@ -1744,9 +1967,7 @@ var ChatSessionMachine = class {
|
|
|
1744
1967
|
});
|
|
1745
1968
|
} catch (err) {
|
|
1746
1969
|
if (err instanceof StreamTransportError) {
|
|
1747
|
-
|
|
1748
|
-
`[Appilots] streaming unavailable, falling back to JSON: ${err.message}`
|
|
1749
|
-
);
|
|
1970
|
+
appilotsDebugWarn(`streaming unavailable, falling back to JSON: ${err.message}`);
|
|
1750
1971
|
response = await this.deps.client.sendMessage(content.trim(), context);
|
|
1751
1972
|
} else {
|
|
1752
1973
|
throw err;
|
|
@@ -1755,8 +1976,8 @@ var ChatSessionMachine = class {
|
|
|
1755
1976
|
} else {
|
|
1756
1977
|
response = await this.deps.client.sendMessage(content.trim(), context);
|
|
1757
1978
|
}
|
|
1758
|
-
|
|
1759
|
-
`
|
|
1979
|
+
appilotsDebugLog(
|
|
1980
|
+
`ChatSessionMachine sendMessage: response received \u2014 actions=${response.actions?.length ?? 0}, hasSummary=${!!response.summaryMessage}`
|
|
1760
1981
|
);
|
|
1761
1982
|
removeStreamingPlaceholder();
|
|
1762
1983
|
this.addAssistantMessage(response.message);
|
|
@@ -1846,7 +2067,11 @@ function isStructuredRisky(value) {
|
|
|
1846
2067
|
}
|
|
1847
2068
|
function actionMetadataRequiresConfirmation(meta) {
|
|
1848
2069
|
if (meta.requiresConfirmation === true || meta.destructive === true || meta.effect === "destructive" || meta.riskLevel === "high" || meta.risk === "destructive") {
|
|
1849
|
-
return {
|
|
2070
|
+
return {
|
|
2071
|
+
required: true,
|
|
2072
|
+
label: typeof meta.label === "string" ? meta.label : void 0,
|
|
2073
|
+
source: "screen-metadata"
|
|
2074
|
+
};
|
|
1850
2075
|
}
|
|
1851
2076
|
return { required: false };
|
|
1852
2077
|
}
|
|
@@ -1887,13 +2112,9 @@ function toDestructiveConfirmPayload(confirm, policy, fallbackLabel) {
|
|
|
1887
2112
|
const labelText = policy.label ?? existingDescription ?? fallbackLabel ?? "esta a\xE7\xE3o";
|
|
1888
2113
|
return {
|
|
1889
2114
|
...payload,
|
|
1890
|
-
title: "A\xE7\xE3o destrutiva",
|
|
1891
|
-
message: `Tem certeza que deseja "${labelText}"?`,
|
|
1892
2115
|
actionDescription: labelText,
|
|
1893
2116
|
severity: "danger",
|
|
1894
|
-
destructive: true
|
|
1895
|
-
confirmLabel: typeof payload.confirmLabel === "string" && payload.confirmLabel ? payload.confirmLabel : "Sim, executar",
|
|
1896
|
-
cancelLabel: typeof payload.cancelLabel === "string" && payload.cancelLabel ? payload.cancelLabel : "Cancelar"
|
|
2117
|
+
destructive: true
|
|
1897
2118
|
};
|
|
1898
2119
|
}
|
|
1899
2120
|
function confirmNeedsDestructiveUpgrade(confirm) {
|
|
@@ -1959,8 +2180,8 @@ var ActionQueueMachine = class {
|
|
|
1959
2180
|
if (event.type !== "agent:action:start") return;
|
|
1960
2181
|
const action = event.data?.action;
|
|
1961
2182
|
if (!action?.id) return;
|
|
1962
|
-
|
|
1963
|
-
`
|
|
2183
|
+
appilotsDebugLog(
|
|
2184
|
+
`ActionQueueMachine: received agent:action:start event, action id="${action.id}", type="${action.type}"`
|
|
1964
2185
|
);
|
|
1965
2186
|
if (this.state.actions.some((a) => a.id === action.id)) return;
|
|
1966
2187
|
this.setActions([...this.state.actions, action]);
|
|
@@ -2061,8 +2282,8 @@ var ActionQueueMachine = class {
|
|
|
2061
2282
|
if (!policy.required) return false;
|
|
2062
2283
|
const confirmId = `confirm-local-${target.id}`;
|
|
2063
2284
|
if (this.state.actions.some((a) => a.id === confirmId)) return true;
|
|
2064
|
-
|
|
2065
|
-
`
|
|
2285
|
+
appilotsDebugLog(
|
|
2286
|
+
`ActionQueueMachine: action "${componentId || target.id}" requires confirmation (${policy.source ?? "local-policy"}) \u2014 synthesising local confirm (id="${confirmId}")`
|
|
2066
2287
|
);
|
|
2067
2288
|
const synth = {
|
|
2068
2289
|
id: confirmId,
|
|
@@ -2108,13 +2329,13 @@ var ActionQueueMachine = class {
|
|
|
2108
2329
|
const pending = this.state.actions.filter(
|
|
2109
2330
|
(a) => a.status === "pending" && !this.processed.has(a.id)
|
|
2110
2331
|
);
|
|
2111
|
-
|
|
2112
|
-
`
|
|
2332
|
+
appilotsDebugLog(
|
|
2333
|
+
`ActionQueueMachine: autoExecute check \u2014 ${pending.length} pending action(s) to process`
|
|
2113
2334
|
);
|
|
2114
2335
|
let lastWasNavigate = false;
|
|
2115
2336
|
let preNavScreen = null;
|
|
2116
2337
|
let preNavSignature = null;
|
|
2117
|
-
for (const action of pending) {
|
|
2338
|
+
for (const [index, action] of pending.entries()) {
|
|
2118
2339
|
if (action.type === "confirm") {
|
|
2119
2340
|
continue;
|
|
2120
2341
|
}
|
|
@@ -2124,14 +2345,14 @@ var ActionQueueMachine = class {
|
|
|
2124
2345
|
const gate = findGatingConfirm(action, this.state.actions);
|
|
2125
2346
|
if (gate) {
|
|
2126
2347
|
if (gate.status === "pending" || gate.status === "executing") {
|
|
2127
|
-
|
|
2128
|
-
`
|
|
2348
|
+
appilotsDebugLog(
|
|
2349
|
+
`ActionQueueMachine: action id="${action.id}" is gated by confirm id="${gate.id}" (status=${gate.status}); waiting for user decision`
|
|
2129
2350
|
);
|
|
2130
2351
|
continue;
|
|
2131
2352
|
}
|
|
2132
2353
|
if (gate.status === "failed") {
|
|
2133
|
-
|
|
2134
|
-
`
|
|
2354
|
+
appilotsDebugLog(
|
|
2355
|
+
`ActionQueueMachine: confirm id="${gate.id}" was rejected; skipping gated action id="${action.id}"`
|
|
2135
2356
|
);
|
|
2136
2357
|
this.processed.add(action.id);
|
|
2137
2358
|
this.updateActions(
|
|
@@ -2163,12 +2384,12 @@ var ActionQueueMachine = class {
|
|
|
2163
2384
|
fromSignature: preNavSignature,
|
|
2164
2385
|
maxMs: 3500
|
|
2165
2386
|
});
|
|
2166
|
-
|
|
2167
|
-
`
|
|
2387
|
+
appilotsDebugLog(
|
|
2388
|
+
`ActionQueueMachine: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
|
|
2168
2389
|
);
|
|
2169
2390
|
}
|
|
2170
|
-
|
|
2171
|
-
`
|
|
2391
|
+
appilotsDebugLog(
|
|
2392
|
+
`ActionQueueMachine: auto-executing action id="${action.id}" type="${action.type}"`
|
|
2172
2393
|
);
|
|
2173
2394
|
this.processed.add(action.id);
|
|
2174
2395
|
if (action.type === "navigate") {
|
|
@@ -2177,20 +2398,67 @@ var ActionQueueMachine = class {
|
|
|
2177
2398
|
}
|
|
2178
2399
|
const succeeded = await this.approveAction(action.id);
|
|
2179
2400
|
if (!succeeded) {
|
|
2401
|
+
const failed = this.state.actions.find((candidate) => candidate.id === action.id);
|
|
2180
2402
|
this.warn(
|
|
2181
2403
|
`ActionQueueMachine: stopping current action batch after failed action id="${action.id}"`
|
|
2182
2404
|
);
|
|
2405
|
+
await this.skipRemainingBatchActions(pending.slice(index + 1), action, failed?.error);
|
|
2183
2406
|
break;
|
|
2184
2407
|
}
|
|
2185
2408
|
lastWasNavigate = action.type === "navigate";
|
|
2186
2409
|
}
|
|
2187
2410
|
}
|
|
2411
|
+
/**
|
|
2412
|
+
* A turn is an all-terminal contract: ChatSessionMachine cannot ask the
|
|
2413
|
+
* server for the next step until every action in the current batch has
|
|
2414
|
+
* reported a completion or an error. When an early action fails we must
|
|
2415
|
+
* not execute dependent actions, but leaving them `pending` deadlocks the
|
|
2416
|
+
* whole turn. Mark the untouched suffix as failed/skipped and emit the
|
|
2417
|
+
* same terminal event a real executor would have emitted.
|
|
2418
|
+
*/
|
|
2419
|
+
async skipRemainingBatchActions(remaining, failedAction, failureReason) {
|
|
2420
|
+
const skipped = remaining.filter((candidate) => {
|
|
2421
|
+
const current = this.state.actions.find((action) => action.id === candidate.id);
|
|
2422
|
+
if (current?.status !== "pending" || this.processed.has(candidate.id)) return false;
|
|
2423
|
+
if (candidate.type === "confirm") return false;
|
|
2424
|
+
const gate = findGatingConfirm(candidate, this.state.actions);
|
|
2425
|
+
if (gate && (gate.status === "pending" || gate.status === "executing")) return false;
|
|
2426
|
+
return true;
|
|
2427
|
+
});
|
|
2428
|
+
if (skipped.length === 0) return;
|
|
2429
|
+
const reason = `Skipped because previous action "${failedAction.id}" failed` + (failureReason ? `: ${failureReason}` : "");
|
|
2430
|
+
const skippedIds = new Set(skipped.map((action) => action.id));
|
|
2431
|
+
for (const action of skipped) this.processed.add(action.id);
|
|
2432
|
+
this.updateActions(
|
|
2433
|
+
(prev) => prev.map(
|
|
2434
|
+
(action) => skippedIds.has(action.id) ? { ...action, status: "failed", error: reason } : action
|
|
2435
|
+
)
|
|
2436
|
+
);
|
|
2437
|
+
for (const action of skipped) {
|
|
2438
|
+
try {
|
|
2439
|
+
await this.deps.client.completeAction(action.id, false, reason);
|
|
2440
|
+
} catch {
|
|
2441
|
+
}
|
|
2442
|
+
this.deps.emit({
|
|
2443
|
+
type: "agent:action:error",
|
|
2444
|
+
timestamp: Date.now(),
|
|
2445
|
+
data: {
|
|
2446
|
+
actionId: action.id,
|
|
2447
|
+
actionType: action.type,
|
|
2448
|
+
success: false,
|
|
2449
|
+
error: reason,
|
|
2450
|
+
skipped: true,
|
|
2451
|
+
blockedByActionId: failedAction.id
|
|
2452
|
+
}
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2188
2456
|
// ── Approve & execute an action ─────────────────────────────
|
|
2189
2457
|
async approveAction(actionId) {
|
|
2190
2458
|
const action = this.state.actions.find((a) => a.id === actionId);
|
|
2191
2459
|
if (!action) return false;
|
|
2192
|
-
|
|
2193
|
-
`
|
|
2460
|
+
appilotsDebugLog(
|
|
2461
|
+
`ActionQueueMachine: action status transition: pending \u2192 executing (id="${actionId}", type="${action.type}")`
|
|
2194
2462
|
);
|
|
2195
2463
|
this.updateActions(
|
|
2196
2464
|
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "executing" } : a)
|
|
@@ -2202,8 +2470,8 @@ var ActionQueueMachine = class {
|
|
|
2202
2470
|
{ confirmedDestructive }
|
|
2203
2471
|
);
|
|
2204
2472
|
if (result.success) {
|
|
2205
|
-
|
|
2206
|
-
`
|
|
2473
|
+
appilotsDebugLog(
|
|
2474
|
+
`ActionQueueMachine: action status transition: executing \u2192 completed (id="${actionId}", type="${action.type}")`
|
|
2207
2475
|
);
|
|
2208
2476
|
this.updateActions(
|
|
2209
2477
|
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "completed" } : a)
|
|
@@ -2282,9 +2550,7 @@ var ActionQueueMachine = class {
|
|
|
2282
2550
|
return this.state.actions.filter((a) => a.status === "executing");
|
|
2283
2551
|
}
|
|
2284
2552
|
get completedActions() {
|
|
2285
|
-
return this.state.actions.filter(
|
|
2286
|
-
(a) => a.status === "completed" || a.status === "failed"
|
|
2287
|
-
);
|
|
2553
|
+
return this.state.actions.filter((a) => a.status === "completed" || a.status === "failed");
|
|
2288
2554
|
}
|
|
2289
2555
|
};
|
|
2290
2556
|
function normalize3(s) {
|
|
@@ -2313,13 +2579,11 @@ function normalizeId(value) {
|
|
|
2313
2579
|
function isOptionalCosmeticTargetId(targetId) {
|
|
2314
2580
|
if (typeof targetId !== "string" || !targetId.trim()) return false;
|
|
2315
2581
|
const id = normalizeId(targetId);
|
|
2316
|
-
return /
|
|
2582
|
+
return /sheetaction|sheet-action|photo-sheet|actionsheet|action-sheet/.test(id);
|
|
2317
2583
|
}
|
|
2318
2584
|
function routeLooksLikeFormContext(route) {
|
|
2319
2585
|
const haystack = normalizeId(route ?? "");
|
|
2320
|
-
return /create|edit|form|cadastr|register|novo|
|
|
2321
|
-
haystack
|
|
2322
|
-
);
|
|
2586
|
+
return /create|edit|form|cadastr|criar|register|registr|novo|nova/.test(haystack);
|
|
2323
2587
|
}
|
|
2324
2588
|
function screenPathParts(screen) {
|
|
2325
2589
|
if (!screen) return [];
|
|
@@ -2694,6 +2958,7 @@ function listItemElement(route, list, item) {
|
|
|
2694
2958
|
list.id ?? list.label ?? `list-${list.index}`,
|
|
2695
2959
|
key
|
|
2696
2960
|
]);
|
|
2961
|
+
const provenance = item.itemKey || item.reactKey ? "declared" : label ? "derived" : "positional";
|
|
2697
2962
|
const inModal = (item.buttons ?? []).some((button) => button.inModal) || (item.inputs ?? []).some((input) => input.inModal) || (item.toggles ?? []).some((toggle) => toggle.inModal);
|
|
2698
2963
|
return {
|
|
2699
2964
|
id,
|
|
@@ -2703,6 +2968,7 @@ function listItemElement(route, list, item) {
|
|
|
2703
2968
|
actions: ["press"],
|
|
2704
2969
|
disabled: false,
|
|
2705
2970
|
source: "list",
|
|
2971
|
+
provenance,
|
|
2706
2972
|
targetId: item.syntheticId,
|
|
2707
2973
|
...inModal ? { inModal: true } : {},
|
|
2708
2974
|
listContext: {
|
|
@@ -2732,17 +2998,14 @@ function buttonElement(route, button, index) {
|
|
|
2732
2998
|
disabled: button.disabled,
|
|
2733
2999
|
selected: button.selected,
|
|
2734
3000
|
source: "button",
|
|
3001
|
+
...button.provenance ? { provenance: button.provenance } : {},
|
|
2735
3002
|
targetId: button.id ?? button.label,
|
|
2736
3003
|
...button.inModal ? { inModal: true } : {}
|
|
2737
3004
|
};
|
|
2738
3005
|
}
|
|
2739
3006
|
function inputElement(route, input, index) {
|
|
2740
3007
|
const label = input.label ?? input.placeholder ?? input.id;
|
|
2741
|
-
const id = stableElementId([
|
|
2742
|
-
"input",
|
|
2743
|
-
route ?? "unknown",
|
|
2744
|
-
input.id ?? label ?? `input-${index}`
|
|
2745
|
-
]);
|
|
3008
|
+
const id = stableElementId(["input", route ?? "unknown", input.id ?? label ?? `input-${index}`]);
|
|
2746
3009
|
return {
|
|
2747
3010
|
id,
|
|
2748
3011
|
role: "input",
|
|
@@ -2751,6 +3014,7 @@ function inputElement(route, input, index) {
|
|
|
2751
3014
|
actions: ["focus", "setValue"],
|
|
2752
3015
|
disabled: input.editable === false,
|
|
2753
3016
|
source: "input",
|
|
3017
|
+
...input.provenance ? { provenance: input.provenance } : {},
|
|
2754
3018
|
targetId: input.id ?? input.label ?? input.placeholder,
|
|
2755
3019
|
...input.inModal ? { inModal: true } : {}
|
|
2756
3020
|
};
|
|
@@ -2770,10 +3034,45 @@ function toggleElement(route, toggle, index) {
|
|
|
2770
3034
|
actions: ["toggle", "press"],
|
|
2771
3035
|
selected: toggle.value,
|
|
2772
3036
|
source: "toggle",
|
|
3037
|
+
...toggle.provenance ? { provenance: toggle.provenance } : {},
|
|
2773
3038
|
targetId: toggle.id ?? toggle.label,
|
|
2774
3039
|
...toggle.inModal ? { inModal: true } : {}
|
|
2775
3040
|
};
|
|
2776
3041
|
}
|
|
3042
|
+
function listItemToggleElement(route, list, item, toggle, toggleIndex) {
|
|
3043
|
+
const rowLabel = labelForListItem(item);
|
|
3044
|
+
const label = toggle.label ?? rowLabel ?? toggle.id;
|
|
3045
|
+
const key = item.itemKey ?? item.reactKey ?? rowLabel ?? item.syntheticId;
|
|
3046
|
+
const id = stableElementId([
|
|
3047
|
+
"toggle",
|
|
3048
|
+
route ?? "unknown",
|
|
3049
|
+
list.id ?? list.label ?? `list-${list.index}`,
|
|
3050
|
+
key,
|
|
3051
|
+
toggle.id ?? `toggle-${toggleIndex}`
|
|
3052
|
+
]);
|
|
3053
|
+
return {
|
|
3054
|
+
id,
|
|
3055
|
+
role: "toggle",
|
|
3056
|
+
label,
|
|
3057
|
+
// The row's texts are what the user actually says out loud.
|
|
3058
|
+
texts: uniqueTexts([label, ...item.texts ?? []]),
|
|
3059
|
+
actions: ["toggle", "press"],
|
|
3060
|
+
selected: toggle.value,
|
|
3061
|
+
source: "list",
|
|
3062
|
+
...toggle.provenance ? { provenance: toggle.provenance } : {},
|
|
3063
|
+
targetId: toggle.id,
|
|
3064
|
+
listContext: {
|
|
3065
|
+
listIndex: list.index,
|
|
3066
|
+
listId: list.id,
|
|
3067
|
+
listLabel: list.label,
|
|
3068
|
+
itemIndex: item.index,
|
|
3069
|
+
itemKey: item.itemKey,
|
|
3070
|
+
reactKey: item.reactKey,
|
|
3071
|
+
syntheticId: item.syntheticId
|
|
3072
|
+
},
|
|
3073
|
+
...toggle.inModal ? { inModal: true } : {}
|
|
3074
|
+
};
|
|
3075
|
+
}
|
|
2777
3076
|
function sliderElement(route, slider, index) {
|
|
2778
3077
|
const label = slider.label ?? slider.id;
|
|
2779
3078
|
const id = stableElementId([
|
|
@@ -2789,37 +3088,48 @@ function sliderElement(route, slider, index) {
|
|
|
2789
3088
|
actions: ["setValue"],
|
|
2790
3089
|
disabled: slider.disabled,
|
|
2791
3090
|
source: "slider",
|
|
3091
|
+
...slider.provenance ? { provenance: slider.provenance } : {},
|
|
2792
3092
|
targetId: slider.id ?? slider.label,
|
|
2793
3093
|
...slider.inModal ? { inModal: true } : {}
|
|
2794
3094
|
};
|
|
2795
3095
|
}
|
|
2796
|
-
function deriveInteractionElements(snapshot) {
|
|
3096
|
+
function deriveInteractionElements(snapshot, options) {
|
|
2797
3097
|
const elements = [];
|
|
2798
3098
|
const seen = /* @__PURE__ */ new Set();
|
|
2799
|
-
const
|
|
3099
|
+
const rectOf = options?.rectOf;
|
|
3100
|
+
const push = (element, node) => {
|
|
2800
3101
|
if (!element.id || seen.has(element.id)) return;
|
|
2801
3102
|
seen.add(element.id);
|
|
2802
|
-
|
|
3103
|
+
const rect = rectOf && node !== void 0 ? rectOf(node) : void 0;
|
|
3104
|
+
elements.push(rect ? { ...element, rect } : element);
|
|
2803
3105
|
};
|
|
2804
3106
|
for (const list of snapshot.lists ?? []) {
|
|
2805
3107
|
for (const item of list.items ?? []) {
|
|
2806
|
-
push(listItemElement(snapshot.route, list, item));
|
|
3108
|
+
push(listItemElement(snapshot.route, list, item), item);
|
|
3109
|
+
for (const [toggleIndex, toggle] of (item.toggles ?? []).entries()) {
|
|
3110
|
+
push(listItemToggleElement(snapshot.route, list, item, toggle, toggleIndex), toggle);
|
|
3111
|
+
}
|
|
2807
3112
|
}
|
|
2808
3113
|
}
|
|
2809
3114
|
for (const [index, button] of (snapshot.buttons ?? []).entries()) {
|
|
2810
|
-
push(buttonElement(snapshot.route, button, index));
|
|
3115
|
+
push(buttonElement(snapshot.route, button, index), button);
|
|
2811
3116
|
}
|
|
2812
3117
|
for (const [index, input] of (snapshot.inputs ?? []).entries()) {
|
|
2813
|
-
push(inputElement(snapshot.route, input, index));
|
|
3118
|
+
push(inputElement(snapshot.route, input, index), input);
|
|
2814
3119
|
}
|
|
2815
3120
|
for (const [index, toggle] of (snapshot.toggles ?? []).entries()) {
|
|
2816
|
-
push(toggleElement(snapshot.route, toggle, index));
|
|
3121
|
+
push(toggleElement(snapshot.route, toggle, index), toggle);
|
|
2817
3122
|
}
|
|
2818
3123
|
for (const [index, slider] of (snapshot.sliders ?? []).entries()) {
|
|
2819
|
-
push(sliderElement(snapshot.route, slider, index));
|
|
3124
|
+
push(sliderElement(snapshot.route, slider, index), slider);
|
|
2820
3125
|
}
|
|
2821
3126
|
return elements;
|
|
2822
3127
|
}
|
|
3128
|
+
function toWireElement(entry) {
|
|
3129
|
+
if (entry.rect === void 0) return entry;
|
|
3130
|
+
const { rect: _rect, ...wire } = entry;
|
|
3131
|
+
return wire;
|
|
3132
|
+
}
|
|
2823
3133
|
function findElementForChoice(elements, option) {
|
|
2824
3134
|
if (option.targetId) {
|
|
2825
3135
|
const byTarget = elements.find((element) => element.targetId === option.targetId);
|
|
@@ -2848,6 +3158,118 @@ function attachElementIdsToChoiceGroups(choiceGroups, elements) {
|
|
|
2848
3158
|
})
|
|
2849
3159
|
}));
|
|
2850
3160
|
}
|
|
3161
|
+
var WIRE_LIMITS = {
|
|
3162
|
+
route: 200,
|
|
3163
|
+
texts: 500,
|
|
3164
|
+
textLength: 2e3,
|
|
3165
|
+
inputs: 300,
|
|
3166
|
+
buttons: 300,
|
|
3167
|
+
toggles: 300,
|
|
3168
|
+
sliders: 100,
|
|
3169
|
+
lists: 100,
|
|
3170
|
+
listItems: 500,
|
|
3171
|
+
listDataPreview: 500,
|
|
3172
|
+
listDataPreviewTextLength: 500,
|
|
3173
|
+
scrollables: 20,
|
|
3174
|
+
choiceGroups: 100,
|
|
3175
|
+
choiceOptions: 200,
|
|
3176
|
+
elements: 1e3,
|
|
3177
|
+
elementTexts: 50,
|
|
3178
|
+
elementTextLength: 500,
|
|
3179
|
+
elementActions: 20,
|
|
3180
|
+
idLength: 160,
|
|
3181
|
+
labelLength: 300,
|
|
3182
|
+
valueLength: 4096,
|
|
3183
|
+
containerTypeLength: 60};
|
|
3184
|
+
var Cut = class {
|
|
3185
|
+
dropped = false;
|
|
3186
|
+
/** Cap an array's length, remembering whether entries were lost. */
|
|
3187
|
+
array(values, max) {
|
|
3188
|
+
if (!values || values.length <= max) return values;
|
|
3189
|
+
this.dropped = true;
|
|
3190
|
+
return values.slice(0, max);
|
|
3191
|
+
}
|
|
3192
|
+
string(value, max) {
|
|
3193
|
+
if (value === void 0 || value.length <= max) return value;
|
|
3194
|
+
this.dropped = true;
|
|
3195
|
+
return value.slice(0, max);
|
|
3196
|
+
}
|
|
3197
|
+
};
|
|
3198
|
+
function clampInput(cut, input) {
|
|
3199
|
+
input.id = cut.string(input.id, WIRE_LIMITS.idLength);
|
|
3200
|
+
input.label = cut.string(input.label, WIRE_LIMITS.labelLength);
|
|
3201
|
+
input.placeholder = cut.string(input.placeholder, WIRE_LIMITS.labelLength);
|
|
3202
|
+
input.value = cut.string(input.value, WIRE_LIMITS.valueLength);
|
|
3203
|
+
return input;
|
|
3204
|
+
}
|
|
3205
|
+
function clampList(cut, list) {
|
|
3206
|
+
list.id = cut.string(list.id, WIRE_LIMITS.idLength);
|
|
3207
|
+
list.containerType = cut.string(list.containerType, WIRE_LIMITS.containerTypeLength);
|
|
3208
|
+
list.label = cut.string(list.label, WIRE_LIMITS.labelLength);
|
|
3209
|
+
list.items = cut.array(list.items, WIRE_LIMITS.listItems) ?? [];
|
|
3210
|
+
if (list.dataPreview) {
|
|
3211
|
+
list.dataPreview = cut.array(list.dataPreview, WIRE_LIMITS.listDataPreview);
|
|
3212
|
+
for (const entry of list.dataPreview ?? []) {
|
|
3213
|
+
entry.key = cut.string(entry.key, WIRE_LIMITS.idLength);
|
|
3214
|
+
entry.text = cut.string(entry.text, WIRE_LIMITS.listDataPreviewTextLength);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
return list;
|
|
3218
|
+
}
|
|
3219
|
+
function clampChoiceGroup(cut, group) {
|
|
3220
|
+
group.id = cut.string(group.id, WIRE_LIMITS.idLength);
|
|
3221
|
+
group.label = cut.string(group.label, WIRE_LIMITS.labelLength);
|
|
3222
|
+
group.options = cut.array(group.options, WIRE_LIMITS.choiceOptions) ?? [];
|
|
3223
|
+
return group;
|
|
3224
|
+
}
|
|
3225
|
+
function clampSnapshotToWireLimits(snapshot) {
|
|
3226
|
+
const cut = new Cut();
|
|
3227
|
+
if (typeof snapshot.route === "string") {
|
|
3228
|
+
snapshot.route = cut.string(snapshot.route, WIRE_LIMITS.route);
|
|
3229
|
+
}
|
|
3230
|
+
snapshot.texts = (cut.array(snapshot.texts, WIRE_LIMITS.texts) ?? []).map(
|
|
3231
|
+
(text) => cut.string(text, WIRE_LIMITS.textLength)
|
|
3232
|
+
);
|
|
3233
|
+
snapshot.inputs = (cut.array(snapshot.inputs, WIRE_LIMITS.inputs) ?? []).map(
|
|
3234
|
+
(input) => clampInput(cut, input)
|
|
3235
|
+
);
|
|
3236
|
+
snapshot.buttons = (cut.array(snapshot.buttons, WIRE_LIMITS.buttons) ?? []).map((button) => {
|
|
3237
|
+
button.id = cut.string(button.id, WIRE_LIMITS.idLength);
|
|
3238
|
+
button.label = cut.string(button.label, WIRE_LIMITS.labelLength);
|
|
3239
|
+
return button;
|
|
3240
|
+
});
|
|
3241
|
+
snapshot.toggles = (cut.array(snapshot.toggles, WIRE_LIMITS.toggles) ?? []).map((toggle) => {
|
|
3242
|
+
toggle.id = cut.string(toggle.id, WIRE_LIMITS.idLength);
|
|
3243
|
+
toggle.label = cut.string(toggle.label, WIRE_LIMITS.labelLength);
|
|
3244
|
+
return toggle;
|
|
3245
|
+
});
|
|
3246
|
+
snapshot.sliders = (cut.array(snapshot.sliders, WIRE_LIMITS.sliders) ?? []).map((slider) => {
|
|
3247
|
+
slider.id = cut.string(slider.id, WIRE_LIMITS.idLength);
|
|
3248
|
+
slider.label = cut.string(slider.label, WIRE_LIMITS.labelLength);
|
|
3249
|
+
return slider;
|
|
3250
|
+
});
|
|
3251
|
+
snapshot.lists = (cut.array(snapshot.lists, WIRE_LIMITS.lists) ?? []).map(
|
|
3252
|
+
(list) => clampList(cut, list)
|
|
3253
|
+
);
|
|
3254
|
+
if (snapshot.scrollables) {
|
|
3255
|
+
snapshot.scrollables = cut.array(snapshot.scrollables, WIRE_LIMITS.scrollables);
|
|
3256
|
+
}
|
|
3257
|
+
snapshot.choiceGroups = (cut.array(snapshot.choiceGroups, WIRE_LIMITS.choiceGroups) ?? []).map(
|
|
3258
|
+
(group) => clampChoiceGroup(cut, group)
|
|
3259
|
+
);
|
|
3260
|
+
snapshot.elements = (cut.array(snapshot.elements, WIRE_LIMITS.elements) ?? []).map((element) => {
|
|
3261
|
+
element.id = cut.string(element.id, WIRE_LIMITS.idLength);
|
|
3262
|
+
element.label = cut.string(element.label, WIRE_LIMITS.labelLength);
|
|
3263
|
+
element.targetId = cut.string(element.targetId, WIRE_LIMITS.idLength);
|
|
3264
|
+
element.texts = (cut.array(element.texts, WIRE_LIMITS.elementTexts) ?? []).map(
|
|
3265
|
+
(text) => cut.string(text, WIRE_LIMITS.elementTextLength)
|
|
3266
|
+
);
|
|
3267
|
+
element.actions = cut.array(element.actions, WIRE_LIMITS.elementActions) ?? [];
|
|
3268
|
+
return element;
|
|
3269
|
+
});
|
|
3270
|
+
if (cut.dropped) snapshot.truncated = true;
|
|
3271
|
+
return snapshot;
|
|
3272
|
+
}
|
|
2851
3273
|
|
|
2852
3274
|
// src/registry/ComponentRegistry.ts
|
|
2853
3275
|
var ComponentRegistryImpl = class {
|
|
@@ -2859,7 +3281,9 @@ var ComponentRegistryImpl = class {
|
|
|
2859
3281
|
*/
|
|
2860
3282
|
register(id, entry) {
|
|
2861
3283
|
this.components.set(id, entry);
|
|
2862
|
-
|
|
3284
|
+
appilotsDebugLog(
|
|
3285
|
+
`ComponentRegistry.register: id="${id}" kind="${entry.kind}" \u2014 total count: ${this.components.size}`
|
|
3286
|
+
);
|
|
2863
3287
|
this.notify(id, entry);
|
|
2864
3288
|
}
|
|
2865
3289
|
/**
|
|
@@ -2867,7 +3291,9 @@ var ComponentRegistryImpl = class {
|
|
|
2867
3291
|
*/
|
|
2868
3292
|
unregister(id) {
|
|
2869
3293
|
this.components.delete(id);
|
|
2870
|
-
|
|
3294
|
+
appilotsDebugLog(
|
|
3295
|
+
`ComponentRegistry.unregister: id="${id}" \u2014 total count: ${this.components.size}`
|
|
3296
|
+
);
|
|
2871
3297
|
this.notify(id, null);
|
|
2872
3298
|
}
|
|
2873
3299
|
/**
|
|
@@ -2971,14 +3397,14 @@ var ListRegistryImpl = class {
|
|
|
2971
3397
|
listeners = /* @__PURE__ */ new Set();
|
|
2972
3398
|
register(id, entry) {
|
|
2973
3399
|
this.lists.set(id, entry);
|
|
2974
|
-
|
|
2975
|
-
`
|
|
3400
|
+
appilotsDebugLog(
|
|
3401
|
+
`ListRegistry.register: id="${id}" component="${entry.component}" items=${entry.itemCount ?? "unknown"} \u2014 total count: ${this.lists.size}`
|
|
2976
3402
|
);
|
|
2977
3403
|
this.notify(id, entry);
|
|
2978
3404
|
}
|
|
2979
3405
|
unregister(id) {
|
|
2980
3406
|
this.lists.delete(id);
|
|
2981
|
-
|
|
3407
|
+
appilotsDebugLog(`ListRegistry.unregister: id="${id}" \u2014 total count: ${this.lists.size}`);
|
|
2982
3408
|
this.notify(id, null);
|
|
2983
3409
|
}
|
|
2984
3410
|
get(id) {
|
|
@@ -3024,7 +3450,7 @@ var ElementRegistryImpl = class {
|
|
|
3024
3450
|
next.set(element.id, element);
|
|
3025
3451
|
}
|
|
3026
3452
|
this.elements = next;
|
|
3027
|
-
|
|
3453
|
+
appilotsDebugLog(`ElementRegistry.replaceAll: elements=${this.elements.size}`);
|
|
3028
3454
|
}
|
|
3029
3455
|
register(id, entry) {
|
|
3030
3456
|
this.elements.set(id, entry);
|
|
@@ -3125,26 +3551,60 @@ function sectionListFlatIndex(sections, section, localIndex) {
|
|
|
3125
3551
|
return base + localIndex;
|
|
3126
3552
|
}
|
|
3127
3553
|
|
|
3554
|
+
// src/auto/interceptGuard.ts
|
|
3555
|
+
var _intercept = null;
|
|
3556
|
+
var _failures = 0;
|
|
3557
|
+
var INTERCEPT_FAILURE_LIMIT = 25;
|
|
3558
|
+
function setInterceptor(fn) {
|
|
3559
|
+
_intercept = fn;
|
|
3560
|
+
_failures = 0;
|
|
3561
|
+
}
|
|
3562
|
+
function isInterceptionActive() {
|
|
3563
|
+
return _intercept !== null;
|
|
3564
|
+
}
|
|
3565
|
+
function safeIntercept(type, props) {
|
|
3566
|
+
const intercept = _intercept;
|
|
3567
|
+
if (!intercept) return null;
|
|
3568
|
+
try {
|
|
3569
|
+
return intercept(type, props);
|
|
3570
|
+
} catch (err) {
|
|
3571
|
+
_failures += 1;
|
|
3572
|
+
if (_failures === 1) {
|
|
3573
|
+
console.error(
|
|
3574
|
+
"[Appilots] Auto-tracking failed to inspect an element and passed it through unchanged. Your app is unaffected; the agent may not see this component. This is an Appilots bug \u2014 please report it: https://github.com/Axtern-Labs/Appilots/issues",
|
|
3575
|
+
err
|
|
3576
|
+
);
|
|
3577
|
+
}
|
|
3578
|
+
if (_failures >= INTERCEPT_FAILURE_LIMIT) {
|
|
3579
|
+
_intercept = null;
|
|
3580
|
+
console.error(
|
|
3581
|
+
`[Appilots] Auto-tracking disabled after ${INTERCEPT_FAILURE_LIMIT} failures. The agent will only see components registered explicitly via registerScreen(). Your app is unaffected.`
|
|
3582
|
+
);
|
|
3583
|
+
}
|
|
3584
|
+
return null;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3128
3588
|
// src/auto/enableAutoTracking.tsx
|
|
3129
3589
|
function extractId(props) {
|
|
3130
3590
|
const raw = props?.testID ?? props?.nativeID ?? props?.id ?? props?.accessibilityLabel ?? props?.name ?? props?.placeholder ?? (typeof props?.autoComplete === "string" && props.autoComplete !== "off" ? props.autoComplete : void 0);
|
|
3131
3591
|
if (raw && typeof raw === "string") {
|
|
3132
3592
|
const normalized = normalizeId2(raw);
|
|
3133
3593
|
if (normalized && normalized.length > 0) {
|
|
3134
|
-
|
|
3594
|
+
appilotsDebugLog(`extractId: raw="${raw}" \u2192 normalized="${normalized}"`);
|
|
3135
3595
|
return normalized;
|
|
3136
3596
|
}
|
|
3137
|
-
|
|
3597
|
+
appilotsDebugLog(`extractId: raw="${raw}" normalized to empty string, falling back`);
|
|
3138
3598
|
}
|
|
3139
3599
|
const hint = deriveSemanticInputId(props);
|
|
3140
3600
|
if (hint) {
|
|
3141
|
-
|
|
3142
|
-
`
|
|
3601
|
+
appilotsDebugWarn(
|
|
3602
|
+
`extractId: no testID/nativeID/accessibilityLabel/name/placeholder/autoComplete on input \u2014 falling back to semantic hint "${hint}". Add testID to make this field reliably addressable.`
|
|
3143
3603
|
);
|
|
3144
3604
|
return hint;
|
|
3145
3605
|
}
|
|
3146
|
-
|
|
3147
|
-
"
|
|
3606
|
+
appilotsDebugLog(
|
|
3607
|
+
"extractId: no identifiable prop found (testID/nativeID/accessibilityLabel/name/placeholder/autoComplete missing); skipping registration"
|
|
3148
3608
|
);
|
|
3149
3609
|
return null;
|
|
3150
3610
|
}
|
|
@@ -3162,15 +3622,17 @@ var TrackedTextInput = React2__default.default.forwardRef(function TrackedTextIn
|
|
|
3162
3622
|
onChangeRef.current = props.onChangeText;
|
|
3163
3623
|
React2__default.default.useEffect(() => {
|
|
3164
3624
|
if (!id) {
|
|
3165
|
-
|
|
3625
|
+
appilotsDebugLog("TrackedTextInput useEffect: no id extracted, skipping registration");
|
|
3166
3626
|
return;
|
|
3167
3627
|
}
|
|
3168
|
-
|
|
3628
|
+
appilotsDebugLog(
|
|
3629
|
+
`TrackedTextInput useEffect: registering field id="${id}" label="${extractLabel(props)}"`
|
|
3630
|
+
);
|
|
3169
3631
|
const entry = {
|
|
3170
3632
|
kind: "field",
|
|
3171
3633
|
getValue: () => valueRef.current,
|
|
3172
3634
|
setValue: (v) => {
|
|
3173
|
-
|
|
3635
|
+
appilotsDebugLog(`TrackedTextInput setValue: id="${id}" value=${describeValue(v)}`);
|
|
3174
3636
|
onChangeRef.current?.(v);
|
|
3175
3637
|
inputRef.current?.setNativeProps?.({ text: v });
|
|
3176
3638
|
},
|
|
@@ -3180,15 +3642,18 @@ var TrackedTextInput = React2__default.default.forwardRef(function TrackedTextIn
|
|
|
3180
3642
|
};
|
|
3181
3643
|
registry.register(id, entry);
|
|
3182
3644
|
return () => {
|
|
3183
|
-
|
|
3645
|
+
appilotsDebugLog(`TrackedTextInput useEffect cleanup: unregistering field id="${id}"`);
|
|
3184
3646
|
registry.unregister(id);
|
|
3185
3647
|
};
|
|
3186
3648
|
}, [id]);
|
|
3187
|
-
const mergedRef = React2__default.default.useCallback(
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3649
|
+
const mergedRef = React2__default.default.useCallback(
|
|
3650
|
+
(instance) => {
|
|
3651
|
+
inputRef.current = instance;
|
|
3652
|
+
if (typeof forwardedRef === "function") forwardedRef(instance);
|
|
3653
|
+
else if (forwardedRef && typeof forwardedRef === "object") forwardedRef.current = instance;
|
|
3654
|
+
},
|
|
3655
|
+
[forwardedRef]
|
|
3656
|
+
);
|
|
3192
3657
|
return _originalCreateElement(reactNative.TextInput, { ...props, ref: mergedRef });
|
|
3193
3658
|
});
|
|
3194
3659
|
var TrackedSwitch = React2__default.default.forwardRef(function TrackedSwitch2(props, forwardedRef) {
|
|
@@ -3200,22 +3665,24 @@ var TrackedSwitch = React2__default.default.forwardRef(function TrackedSwitch2(p
|
|
|
3200
3665
|
onChangeRef.current = props.onValueChange;
|
|
3201
3666
|
React2__default.default.useEffect(() => {
|
|
3202
3667
|
if (!id) {
|
|
3203
|
-
|
|
3668
|
+
appilotsDebugLog("TrackedSwitch useEffect: no id extracted, skipping registration");
|
|
3204
3669
|
return;
|
|
3205
3670
|
}
|
|
3206
|
-
|
|
3671
|
+
appilotsDebugLog(
|
|
3672
|
+
`TrackedSwitch useEffect: registering toggle id="${id}" label="${extractLabel(props)}"`
|
|
3673
|
+
);
|
|
3207
3674
|
const entry = {
|
|
3208
3675
|
kind: "toggle",
|
|
3209
3676
|
getValue: () => valueRef.current,
|
|
3210
3677
|
setValue: (v) => {
|
|
3211
|
-
|
|
3678
|
+
appilotsDebugLog(`TrackedSwitch setValue called: id="${id}" value=${v}`);
|
|
3212
3679
|
onChangeRef.current?.(v);
|
|
3213
3680
|
},
|
|
3214
3681
|
label: extractLabel(props)
|
|
3215
3682
|
};
|
|
3216
3683
|
registry.register(id, entry);
|
|
3217
3684
|
return () => {
|
|
3218
|
-
|
|
3685
|
+
appilotsDebugLog(`TrackedSwitch useEffect cleanup: unregistering toggle id="${id}"`);
|
|
3219
3686
|
registry.unregister(id);
|
|
3220
3687
|
};
|
|
3221
3688
|
}, [id]);
|
|
@@ -3235,21 +3702,25 @@ function getTrackedPressable(OriginalComponent) {
|
|
|
3235
3702
|
return;
|
|
3236
3703
|
}
|
|
3237
3704
|
if (!id || !props.onPress) {
|
|
3238
|
-
|
|
3705
|
+
appilotsDebugLog(
|
|
3706
|
+
`TrackedPressable useEffect: skipping registration (id=${id}, hasOnPress=${!!props.onPress})`
|
|
3707
|
+
);
|
|
3239
3708
|
return;
|
|
3240
3709
|
}
|
|
3241
|
-
|
|
3710
|
+
appilotsDebugLog(
|
|
3711
|
+
`TrackedPressable useEffect: registering target id="${id}" label="${extractLabel(props)}"`
|
|
3712
|
+
);
|
|
3242
3713
|
const entry = {
|
|
3243
3714
|
kind: "target",
|
|
3244
3715
|
press: () => {
|
|
3245
|
-
|
|
3716
|
+
appilotsDebugLog(`TrackedPressable press called: id="${id}"`);
|
|
3246
3717
|
onPressRef.current?.();
|
|
3247
3718
|
},
|
|
3248
3719
|
label: extractLabel(props)
|
|
3249
3720
|
};
|
|
3250
3721
|
registry.register(id, entry);
|
|
3251
3722
|
return () => {
|
|
3252
|
-
|
|
3723
|
+
appilotsDebugLog(`TrackedPressable useEffect cleanup: unregistering target id="${id}"`);
|
|
3253
3724
|
registry.unregister(id);
|
|
3254
3725
|
};
|
|
3255
3726
|
}, [id, !!props.onPress]);
|
|
@@ -3258,12 +3729,7 @@ function getTrackedPressable(OriginalComponent) {
|
|
|
3258
3729
|
_pressableWrapperCache.set(OriginalComponent, cached);
|
|
3259
3730
|
return cached;
|
|
3260
3731
|
}
|
|
3261
|
-
var LIST_COMPONENT_NAMES = /* @__PURE__ */ new Set([
|
|
3262
|
-
"FlatList",
|
|
3263
|
-
"SectionList",
|
|
3264
|
-
"VirtualizedList",
|
|
3265
|
-
"FlashList"
|
|
3266
|
-
]);
|
|
3732
|
+
var LIST_COMPONENT_NAMES = /* @__PURE__ */ new Set(["FlatList", "SectionList", "VirtualizedList", "FlashList"]);
|
|
3267
3733
|
var _listWrapperCache = /* @__PURE__ */ new Map();
|
|
3268
3734
|
var _generatedListIdCounter = 0;
|
|
3269
3735
|
function componentDisplayName(type) {
|
|
@@ -3317,12 +3783,10 @@ var PREVIEW_TEXT_KEYS = [
|
|
|
3317
3783
|
"label",
|
|
3318
3784
|
"description",
|
|
3319
3785
|
"descricao",
|
|
3320
|
-
"plate",
|
|
3321
|
-
"placa",
|
|
3322
|
-
"model",
|
|
3323
|
-
"modelo",
|
|
3324
3786
|
"email",
|
|
3325
|
-
"username"
|
|
3787
|
+
"username",
|
|
3788
|
+
"code",
|
|
3789
|
+
"codigo"
|
|
3326
3790
|
];
|
|
3327
3791
|
function projectItemText(item) {
|
|
3328
3792
|
if (item == null) return "";
|
|
@@ -3418,10 +3882,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3418
3882
|
const itemCount = resolveItemCount(props);
|
|
3419
3883
|
const refreshing = props.refreshing === true;
|
|
3420
3884
|
const label = listLabel(props);
|
|
3421
|
-
const dataPreview = React2__default.default.useMemo(
|
|
3422
|
-
() => buildDataPreview(props),
|
|
3423
|
-
[props.data, props.sections]
|
|
3424
|
-
);
|
|
3885
|
+
const dataPreview = React2__default.default.useMemo(() => buildDataPreview(props), [props.data, props.sections]);
|
|
3425
3886
|
const mergedRef = React2__default.default.useCallback(
|
|
3426
3887
|
(instance) => {
|
|
3427
3888
|
instanceRef.current = instance;
|
|
@@ -3456,7 +3917,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3456
3917
|
}
|
|
3457
3918
|
return false;
|
|
3458
3919
|
} catch (err) {
|
|
3459
|
-
|
|
3920
|
+
appilotsDebugWarn(`TrackedList scrollToIndex failed for "${listId}":`, err);
|
|
3460
3921
|
return false;
|
|
3461
3922
|
}
|
|
3462
3923
|
},
|
|
@@ -3475,7 +3936,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3475
3936
|
}
|
|
3476
3937
|
return false;
|
|
3477
3938
|
} catch (err) {
|
|
3478
|
-
|
|
3939
|
+
appilotsDebugWarn(`TrackedList scrollToOffset failed for "${listId}":`, err);
|
|
3479
3940
|
return false;
|
|
3480
3941
|
}
|
|
3481
3942
|
},
|
|
@@ -3519,26 +3980,148 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3519
3980
|
_listWrapperCache.set(OriginalComponent, cached);
|
|
3520
3981
|
return cached;
|
|
3521
3982
|
}
|
|
3522
|
-
var
|
|
3983
|
+
var SCROLLABLE_COMPONENT_NAMES = /* @__PURE__ */ new Set(["ScrollView", "KeyboardAwareScrollView"]);
|
|
3984
|
+
var _scrollWrapperCache = /* @__PURE__ */ new Map();
|
|
3985
|
+
var _generatedScrollIdCounter = 0;
|
|
3986
|
+
function extractScrollId(props, componentName) {
|
|
3987
|
+
const raw = props?.appilotsScrollId ?? props?.testID ?? props?.accessibilityLabel;
|
|
3988
|
+
if (typeof raw === "string" && raw.trim().length > 0) return raw.trim();
|
|
3989
|
+
_generatedScrollIdCounter += 1;
|
|
3990
|
+
return `${componentName.toLowerCase()}-${_generatedScrollIdCounter}`;
|
|
3991
|
+
}
|
|
3992
|
+
function chain(appHandler, ours) {
|
|
3993
|
+
return ((...args) => {
|
|
3994
|
+
if (typeof appHandler === "function") {
|
|
3995
|
+
try {
|
|
3996
|
+
appHandler(...args);
|
|
3997
|
+
} catch (err) {
|
|
3998
|
+
appilotsDebugWarn("TrackedScrollView: app scroll handler threw:", err);
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
ours(...args);
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
function getTrackedScrollView(OriginalComponent, componentName) {
|
|
4005
|
+
let cached = _scrollWrapperCache.get(OriginalComponent);
|
|
4006
|
+
if (cached) return cached;
|
|
4007
|
+
cached = React2__default.default.forwardRef(function TrackedScrollView(props, forwardedRef) {
|
|
4008
|
+
const idRef = React2__default.default.useRef(null);
|
|
4009
|
+
if (!idRef.current) idRef.current = extractScrollId(props, componentName);
|
|
4010
|
+
const scrollId = idRef.current;
|
|
4011
|
+
const instanceRef = React2__default.default.useRef(null);
|
|
4012
|
+
const metricsRef = React2__default.default.useRef({
|
|
4013
|
+
offset: 0,
|
|
4014
|
+
visibleLength: 0,
|
|
4015
|
+
contentLength: 0
|
|
4016
|
+
});
|
|
4017
|
+
const horizontal = props.horizontal === true;
|
|
4018
|
+
const mergedRef = React2__default.default.useCallback(
|
|
4019
|
+
(instance) => {
|
|
4020
|
+
instanceRef.current = instance;
|
|
4021
|
+
if (typeof forwardedRef === "function") forwardedRef(instance);
|
|
4022
|
+
else if (forwardedRef && typeof forwardedRef === "object") forwardedRef.current = instance;
|
|
4023
|
+
},
|
|
4024
|
+
[forwardedRef]
|
|
4025
|
+
);
|
|
4026
|
+
const label = typeof props.appilotsScrollLabel === "string" ? props.appilotsScrollLabel : typeof props.accessibilityLabel === "string" ? props.accessibilityLabel : void 0;
|
|
4027
|
+
React2__default.default.useEffect(() => {
|
|
4028
|
+
listRegistry.register(scrollId, {
|
|
4029
|
+
kind: "scroll",
|
|
4030
|
+
id: scrollId,
|
|
4031
|
+
component: componentName,
|
|
4032
|
+
label,
|
|
4033
|
+
scrollToOffset: (offset) => {
|
|
4034
|
+
const instance = instanceRef.current;
|
|
4035
|
+
if (!instance) return false;
|
|
4036
|
+
try {
|
|
4037
|
+
const scrollTo = typeof instance.scrollTo === "function" ? instance.scrollTo.bind(instance) : typeof instance.getScrollResponder === "function" ? instance.getScrollResponder()?.scrollTo?.bind(instance.getScrollResponder()) : void 0;
|
|
4038
|
+
if (typeof scrollTo !== "function") return false;
|
|
4039
|
+
scrollTo(horizontal ? { x: offset, animated: true } : { y: offset, animated: true });
|
|
4040
|
+
metricsRef.current = { ...metricsRef.current, offset };
|
|
4041
|
+
return true;
|
|
4042
|
+
} catch (err) {
|
|
4043
|
+
appilotsDebugWarn(`TrackedScrollView scrollTo failed for "${scrollId}":`, err);
|
|
4044
|
+
return false;
|
|
4045
|
+
}
|
|
4046
|
+
},
|
|
4047
|
+
getScrollMetrics: () => {
|
|
4048
|
+
const m = metricsRef.current;
|
|
4049
|
+
return m.contentLength > 0 ? { ...m } : void 0;
|
|
4050
|
+
}
|
|
4051
|
+
});
|
|
4052
|
+
return () => {
|
|
4053
|
+
listRegistry.unregister(scrollId);
|
|
4054
|
+
};
|
|
4055
|
+
}, [scrollId, label, horizontal]);
|
|
4056
|
+
const onScroll = React2__default.default.useMemo(
|
|
4057
|
+
() => chain(props.onScroll, (event) => {
|
|
4058
|
+
const n = event?.nativeEvent;
|
|
4059
|
+
if (!n) return;
|
|
4060
|
+
const offset = horizontal ? n.contentOffset?.x : n.contentOffset?.y;
|
|
4061
|
+
const visible = horizontal ? n.layoutMeasurement?.width : n.layoutMeasurement?.height;
|
|
4062
|
+
const content = horizontal ? n.contentSize?.width : n.contentSize?.height;
|
|
4063
|
+
metricsRef.current = {
|
|
4064
|
+
offset: typeof offset === "number" ? offset : metricsRef.current.offset,
|
|
4065
|
+
visibleLength: typeof visible === "number" ? visible : metricsRef.current.visibleLength,
|
|
4066
|
+
contentLength: typeof content === "number" ? content : metricsRef.current.contentLength
|
|
4067
|
+
};
|
|
4068
|
+
}),
|
|
4069
|
+
[props.onScroll, horizontal]
|
|
4070
|
+
);
|
|
4071
|
+
const onLayout = React2__default.default.useMemo(
|
|
4072
|
+
() => chain(props.onLayout, (event) => {
|
|
4073
|
+
const size = horizontal ? event?.nativeEvent?.layout?.width : event?.nativeEvent?.layout?.height;
|
|
4074
|
+
if (typeof size === "number") {
|
|
4075
|
+
metricsRef.current = { ...metricsRef.current, visibleLength: size };
|
|
4076
|
+
}
|
|
4077
|
+
}),
|
|
4078
|
+
[props.onLayout, horizontal]
|
|
4079
|
+
);
|
|
4080
|
+
const onContentSizeChange = React2__default.default.useMemo(
|
|
4081
|
+
() => chain(props.onContentSizeChange, (w, h) => {
|
|
4082
|
+
const size = horizontal ? w : h;
|
|
4083
|
+
if (typeof size === "number") {
|
|
4084
|
+
metricsRef.current = { ...metricsRef.current, contentLength: size };
|
|
4085
|
+
}
|
|
4086
|
+
}),
|
|
4087
|
+
[props.onContentSizeChange, horizontal]
|
|
4088
|
+
);
|
|
4089
|
+
return _originalCreateElement(OriginalComponent, {
|
|
4090
|
+
...props,
|
|
4091
|
+
ref: mergedRef,
|
|
4092
|
+
// Without a throttle iOS fires `onScroll` once per gesture, which
|
|
4093
|
+
// is enough to stay roughly in sync but leaves the offset stale
|
|
4094
|
+
// mid-fling. 16ms is RN's own recommendation for live metrics.
|
|
4095
|
+
scrollEventThrottle: props.scrollEventThrottle ?? 16,
|
|
4096
|
+
onScroll,
|
|
4097
|
+
onLayout,
|
|
4098
|
+
onContentSizeChange,
|
|
4099
|
+
__appilotsScrollId: scrollId
|
|
4100
|
+
});
|
|
4101
|
+
});
|
|
4102
|
+
_scrollWrapperCache.set(OriginalComponent, cached);
|
|
4103
|
+
return cached;
|
|
4104
|
+
}
|
|
4105
|
+
var _enabled2 = false;
|
|
3523
4106
|
function enableAppilotsAutoTracking(options) {
|
|
3524
|
-
if (
|
|
3525
|
-
|
|
4107
|
+
if (_enabled2) return;
|
|
4108
|
+
_enabled2 = true;
|
|
3526
4109
|
const { trackPressable = true, debug = false } = options ?? {};
|
|
3527
4110
|
if (debug) {
|
|
3528
4111
|
componentRegistry.subscribe((id, entry) => {
|
|
3529
4112
|
if (entry) {
|
|
3530
|
-
|
|
4113
|
+
appilotsDebugLog(`Registered ${entry.kind}: "${id}" (${entry.label ?? "no label"})`);
|
|
3531
4114
|
} else {
|
|
3532
|
-
|
|
4115
|
+
appilotsDebugLog(`Unregistered: "${id}"`);
|
|
3533
4116
|
}
|
|
3534
4117
|
});
|
|
3535
4118
|
listRegistry.subscribe((id, entry) => {
|
|
3536
4119
|
if (entry) {
|
|
3537
|
-
|
|
3538
|
-
`
|
|
4120
|
+
appilotsDebugLog(
|
|
4121
|
+
`Registered list: "${id}" (${entry.component}, items=${entry.itemCount ?? "unknown"})`
|
|
3539
4122
|
);
|
|
3540
4123
|
} else {
|
|
3541
|
-
|
|
4124
|
+
appilotsDebugLog(`Unregistered list: "${id}"`);
|
|
3542
4125
|
}
|
|
3543
4126
|
});
|
|
3544
4127
|
}
|
|
@@ -3546,12 +4129,14 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3546
4129
|
_originalCreateElement = originalCreateElement;
|
|
3547
4130
|
function isComponent(type, target, name) {
|
|
3548
4131
|
if (type === target) {
|
|
3549
|
-
|
|
4132
|
+
appilotsDebugLog(`isComponent: matched "${name}" by reference equality`);
|
|
3550
4133
|
return true;
|
|
3551
4134
|
}
|
|
3552
4135
|
const dn = type?.displayName || type?.name || "";
|
|
3553
4136
|
if (dn === name) {
|
|
3554
|
-
|
|
4137
|
+
appilotsDebugLog(
|
|
4138
|
+
`isComponent: matched "${name}" by displayName/name (reference equality failed \u2014 possible monorepo issue)`
|
|
4139
|
+
);
|
|
3555
4140
|
return true;
|
|
3556
4141
|
}
|
|
3557
4142
|
return false;
|
|
@@ -3560,45 +4145,60 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3560
4145
|
if (!props || !type || props.__appilotsInternal) return null;
|
|
3561
4146
|
const displayName = componentDisplayName(type);
|
|
3562
4147
|
if (LIST_COMPONENT_NAMES.has(displayName)) {
|
|
3563
|
-
|
|
4148
|
+
appilotsDebugLog(`intercepted list component "${displayName}"`);
|
|
3564
4149
|
return { type: getTrackedList(type, displayName), props };
|
|
3565
4150
|
}
|
|
4151
|
+
if (SCROLLABLE_COMPONENT_NAMES.has(displayName)) {
|
|
4152
|
+
appilotsDebugLog(`intercepted scrollable "${displayName}"`);
|
|
4153
|
+
return { type: getTrackedScrollView(type, displayName), props };
|
|
4154
|
+
}
|
|
3566
4155
|
if (isComponent(type, reactNative.TextInput, "TextInput")) {
|
|
3567
4156
|
if (props.testID || props.accessibilityLabel || props.placeholder) {
|
|
3568
|
-
|
|
4157
|
+
appilotsDebugLog(
|
|
4158
|
+
// Ids, not the user-facing copy: a placeholder is the app's
|
|
4159
|
+
// own text and often the most identifying string on screen.
|
|
4160
|
+
`intercepted TextInput (testID="${props.testID ?? "?"}" hasLabel=${!!props.accessibilityLabel} hasPlaceholder=${!!props.placeholder})`
|
|
4161
|
+
);
|
|
3569
4162
|
return { type: TrackedTextInput, props };
|
|
3570
4163
|
}
|
|
3571
4164
|
}
|
|
3572
4165
|
if (isComponent(type, reactNative.Switch, "Switch")) {
|
|
3573
4166
|
if (props.testID || props.accessibilityLabel) {
|
|
3574
|
-
|
|
4167
|
+
appilotsDebugLog(
|
|
4168
|
+
`intercepted Switch (testID="${props.testID}", label="${props.accessibilityLabel}")`
|
|
4169
|
+
);
|
|
3575
4170
|
return { type: TrackedSwitch, props };
|
|
3576
4171
|
}
|
|
3577
4172
|
}
|
|
3578
4173
|
if (isComponent(type, reactNative.TouchableOpacity, "TouchableOpacity") || trackPressable && isComponent(type, reactNative.Pressable, "Pressable")) {
|
|
3579
4174
|
if ((props.testID || props.accessibilityLabel) && props.onPress) {
|
|
3580
|
-
|
|
4175
|
+
appilotsDebugLog(
|
|
4176
|
+
`intercepted Pressable/TouchableOpacity (testID="${props.testID}", label="${props.accessibilityLabel}")`
|
|
4177
|
+
);
|
|
3581
4178
|
return { type: getTrackedPressable(type), props };
|
|
3582
4179
|
}
|
|
3583
4180
|
}
|
|
3584
4181
|
return null;
|
|
3585
4182
|
}
|
|
4183
|
+
setInterceptor(interceptType);
|
|
3586
4184
|
React2__default.default.createElement = function(type, props, ...children) {
|
|
3587
|
-
const replacement =
|
|
4185
|
+
const replacement = safeIntercept(type, props);
|
|
3588
4186
|
if (replacement) {
|
|
3589
4187
|
return originalCreateElement(replacement.type, replacement.props, ...children);
|
|
3590
4188
|
}
|
|
3591
4189
|
return originalCreateElement(type, props, ...children);
|
|
3592
4190
|
};
|
|
3593
|
-
|
|
3594
|
-
|
|
4191
|
+
_createElementPatched = true;
|
|
4192
|
+
appilotsDebugLog("Auto-tracking: React.createElement patched");
|
|
3595
4193
|
try {
|
|
3596
4194
|
const jsxRuntime = __require("react/jsx-runtime");
|
|
3597
4195
|
if (jsxRuntime) {
|
|
3598
4196
|
_patchJsxRuntimeModule(jsxRuntime);
|
|
3599
4197
|
}
|
|
3600
4198
|
} catch (e) {
|
|
3601
|
-
|
|
4199
|
+
appilotsDebugLog(
|
|
4200
|
+
"Auto-tracking: react/jsx-runtime not available internally (will be patched by metro shim)"
|
|
4201
|
+
);
|
|
3602
4202
|
}
|
|
3603
4203
|
try {
|
|
3604
4204
|
const jsxDevRuntime = __require("react/jsx-dev-runtime");
|
|
@@ -3606,104 +4206,121 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3606
4206
|
_patchJsxDevRuntimeModule(jsxDevRuntime);
|
|
3607
4207
|
}
|
|
3608
4208
|
} catch (e) {
|
|
3609
|
-
|
|
4209
|
+
appilotsDebugLog(
|
|
4210
|
+
"Auto-tracking: react/jsx-dev-runtime not available internally (will be patched by metro shim)"
|
|
4211
|
+
);
|
|
3610
4212
|
}
|
|
3611
4213
|
}
|
|
3612
|
-
var _interceptType = null;
|
|
3613
4214
|
var _jsxRuntimePatched = false;
|
|
3614
4215
|
var _jsxDevRuntimePatched = false;
|
|
4216
|
+
var _createElementPatched = false;
|
|
3615
4217
|
function _patchJsxRuntimeModule(jsxRuntime) {
|
|
3616
|
-
if (_jsxRuntimePatched || !
|
|
4218
|
+
if (_jsxRuntimePatched || !isInterceptionActive() || !jsxRuntime) return;
|
|
3617
4219
|
_jsxRuntimePatched = true;
|
|
3618
|
-
const interceptType = _interceptType;
|
|
3619
4220
|
const origJsx = jsxRuntime.jsx;
|
|
3620
4221
|
const origJsxs = jsxRuntime.jsxs;
|
|
3621
4222
|
if (origJsx) {
|
|
3622
4223
|
jsxRuntime.jsx = function(type, props, key) {
|
|
3623
|
-
const replacement =
|
|
4224
|
+
const replacement = safeIntercept(type, props);
|
|
3624
4225
|
if (replacement) {
|
|
3625
4226
|
return origJsx(replacement.type, replacement.props, key);
|
|
3626
4227
|
}
|
|
3627
4228
|
return origJsx(type, props, key);
|
|
3628
4229
|
};
|
|
3629
|
-
|
|
4230
|
+
appilotsDebugLog("Auto-tracking: react/jsx-runtime jsx() patched");
|
|
3630
4231
|
}
|
|
3631
4232
|
if (origJsxs) {
|
|
3632
4233
|
jsxRuntime.jsxs = function(type, props, key) {
|
|
3633
|
-
const replacement =
|
|
4234
|
+
const replacement = safeIntercept(type, props);
|
|
3634
4235
|
if (replacement) {
|
|
3635
4236
|
return origJsxs(replacement.type, replacement.props, key);
|
|
3636
4237
|
}
|
|
3637
4238
|
return origJsxs(type, props, key);
|
|
3638
4239
|
};
|
|
3639
|
-
|
|
4240
|
+
appilotsDebugLog("Auto-tracking: react/jsx-runtime jsxs() patched");
|
|
3640
4241
|
}
|
|
3641
4242
|
}
|
|
3642
4243
|
function _patchJsxDevRuntimeModule(jsxDevRuntime) {
|
|
3643
|
-
if (_jsxDevRuntimePatched || !
|
|
4244
|
+
if (_jsxDevRuntimePatched || !isInterceptionActive() || !jsxDevRuntime) return;
|
|
3644
4245
|
_jsxDevRuntimePatched = true;
|
|
3645
|
-
const interceptType = _interceptType;
|
|
3646
4246
|
if (jsxDevRuntime.jsxDEV) {
|
|
3647
4247
|
const origJsxDEV = jsxDevRuntime.jsxDEV;
|
|
3648
4248
|
jsxDevRuntime.jsxDEV = function(type, props, key, isStaticChildren, source, self) {
|
|
3649
|
-
const replacement =
|
|
4249
|
+
const replacement = safeIntercept(type, props);
|
|
3650
4250
|
if (replacement) {
|
|
3651
4251
|
return origJsxDEV(replacement.type, replacement.props, key, isStaticChildren, source, self);
|
|
3652
4252
|
}
|
|
3653
4253
|
return origJsxDEV(type, props, key, isStaticChildren, source, self);
|
|
3654
4254
|
};
|
|
3655
|
-
|
|
4255
|
+
appilotsDebugLog("Auto-tracking: react/jsx-dev-runtime jsxDEV() patched");
|
|
3656
4256
|
}
|
|
3657
4257
|
}
|
|
3658
4258
|
function _patchJsxRuntimes(jsxRuntime, jsxDevRuntime) {
|
|
3659
|
-
if (!
|
|
3660
|
-
|
|
4259
|
+
if (!isInterceptionActive()) {
|
|
4260
|
+
appilotsDebugWarn("_patchJsxRuntimes called but auto-tracking is not enabled");
|
|
3661
4261
|
return;
|
|
3662
4262
|
}
|
|
3663
|
-
|
|
4263
|
+
appilotsDebugLog("_patchJsxRuntimes: patching from app context (metro shim)");
|
|
3664
4264
|
if (jsxRuntime) _patchJsxRuntimeModule(jsxRuntime);
|
|
3665
4265
|
if (jsxDevRuntime) _patchJsxDevRuntimeModule(jsxDevRuntime);
|
|
3666
4266
|
}
|
|
3667
4267
|
function isAutoTrackingEnabled() {
|
|
3668
|
-
return
|
|
4268
|
+
return _enabled2;
|
|
4269
|
+
}
|
|
4270
|
+
function getAutoTrackingState() {
|
|
4271
|
+
return {
|
|
4272
|
+
enabled: _enabled2,
|
|
4273
|
+
createElementPatched: _createElementPatched,
|
|
4274
|
+
jsxRuntimePatched: _jsxRuntimePatched,
|
|
4275
|
+
jsxDevRuntimePatched: _jsxDevRuntimePatched,
|
|
4276
|
+
interceptionActive: isInterceptionActive()
|
|
4277
|
+
};
|
|
3669
4278
|
}
|
|
3670
4279
|
|
|
3671
4280
|
// src/core/initAppilots.ts
|
|
3672
4281
|
var _globalConfig = null;
|
|
3673
4282
|
var _autoConfigAttempted = false;
|
|
3674
4283
|
function initAppilots(config) {
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
4284
|
+
appilotsDebugLog(
|
|
4285
|
+
`initAppilots called with config: projectId="${config.projectId}", apiBaseUrl="${config.apiBaseUrl ?? config.apiUrl ?? "default"}", debug=${config.debug ?? false}, autoTracking=${JSON.stringify(config.autoTracking ?? {})}`
|
|
4286
|
+
);
|
|
4287
|
+
if (!config.apiBaseUrl) {
|
|
4288
|
+
config.apiBaseUrl = config.apiUrl ?? config.serverUrl;
|
|
3678
4289
|
}
|
|
3679
4290
|
_globalConfig = config;
|
|
3680
4291
|
const autoTrackingEnabled = config.autoTracking?.enabled !== false;
|
|
3681
4292
|
if (autoTrackingEnabled) {
|
|
3682
|
-
|
|
4293
|
+
appilotsDebugLog(
|
|
4294
|
+
"initAppilots: auto-tracking is enabled, calling enableAppilotsAutoTracking()"
|
|
4295
|
+
);
|
|
3683
4296
|
enableAppilotsAutoTracking({
|
|
3684
4297
|
trackPressable: config.autoTracking?.trackPressable ?? true,
|
|
3685
4298
|
debug: config.debug ?? false
|
|
3686
4299
|
});
|
|
3687
4300
|
} else {
|
|
3688
|
-
|
|
4301
|
+
appilotsDebugLog("initAppilots: auto-tracking is explicitly disabled");
|
|
3689
4302
|
}
|
|
3690
4303
|
}
|
|
3691
4304
|
function tryAutoConfig() {
|
|
3692
4305
|
if (_globalConfig || _autoConfigAttempted) {
|
|
3693
|
-
|
|
4306
|
+
appilotsDebugLog(
|
|
4307
|
+
`tryAutoConfig: skipping (alreadyConfigured=${!!_globalConfig}, alreadyAttempted=${_autoConfigAttempted})`
|
|
4308
|
+
);
|
|
3694
4309
|
return;
|
|
3695
4310
|
}
|
|
3696
4311
|
_autoConfigAttempted = true;
|
|
3697
4312
|
try {
|
|
3698
4313
|
const rc = globalThis.__APPILOTS_RC__;
|
|
3699
4314
|
if (rc && typeof rc === "object" && rc.projectId) {
|
|
3700
|
-
|
|
4315
|
+
appilotsDebugLog(
|
|
4316
|
+
`tryAutoConfig: found globalThis.__APPILOTS_RC__ with projectId="${rc.projectId}"`
|
|
4317
|
+
);
|
|
3701
4318
|
initAppilots(rc);
|
|
3702
4319
|
} else {
|
|
3703
|
-
|
|
4320
|
+
appilotsDebugLog("tryAutoConfig: globalThis.__APPILOTS_RC__ not found or missing projectId");
|
|
3704
4321
|
}
|
|
3705
4322
|
} catch {
|
|
3706
|
-
|
|
4323
|
+
appilotsDebugWarn("tryAutoConfig: error reading globalThis.__APPILOTS_RC__");
|
|
3707
4324
|
}
|
|
3708
4325
|
}
|
|
3709
4326
|
function getGlobalConfig() {
|
|
@@ -3720,13 +4337,20 @@ var _diagnostics = {
|
|
|
3720
4337
|
};
|
|
3721
4338
|
function setFiberRoot(fiber) {
|
|
3722
4339
|
if (_fiberRoot !== fiber) {
|
|
3723
|
-
|
|
4340
|
+
appilotsDebugLog(`Introspection: fiber root captured (type=${typeName(fiber)})`);
|
|
3724
4341
|
_fiberRoot = fiber;
|
|
3725
4342
|
}
|
|
3726
4343
|
_diagnostics.captured = true;
|
|
3727
4344
|
}
|
|
3728
4345
|
function getFiberRoot() {
|
|
3729
|
-
return _fiberRoot;
|
|
4346
|
+
return resolveCurrentRoot(_fiberRoot);
|
|
4347
|
+
}
|
|
4348
|
+
function resolveCurrentRoot(fiber) {
|
|
4349
|
+
const current = fiber?.stateNode?.current;
|
|
4350
|
+
if (current && (current === fiber || current.alternate === fiber)) {
|
|
4351
|
+
return current;
|
|
4352
|
+
}
|
|
4353
|
+
return fiber;
|
|
3730
4354
|
}
|
|
3731
4355
|
function recordIntrospectionFailure(reason, reactVersion) {
|
|
3732
4356
|
_diagnostics.failureCount += 1;
|
|
@@ -3735,6 +4359,13 @@ function recordIntrospectionFailure(reason, reactVersion) {
|
|
|
3735
4359
|
_diagnostics.reactVersion = reactVersion;
|
|
3736
4360
|
}
|
|
3737
4361
|
}
|
|
4362
|
+
function recordRenderFailure(reactVersion) {
|
|
4363
|
+
_fiberRoot = null;
|
|
4364
|
+
_diagnostics.captured = false;
|
|
4365
|
+
_diagnostics.failureCount += 1;
|
|
4366
|
+
_diagnostics.failureReason = "render-crashed";
|
|
4367
|
+
_diagnostics.reactVersion = reactVersion;
|
|
4368
|
+
}
|
|
3738
4369
|
function getIntrospectionDiagnostics() {
|
|
3739
4370
|
return { ..._diagnostics };
|
|
3740
4371
|
}
|
|
@@ -3743,182 +4374,976 @@ function typeName(fiber) {
|
|
|
3743
4374
|
if (!t) return "unknown";
|
|
3744
4375
|
if (typeof t === "string") return t;
|
|
3745
4376
|
if (typeof t === "function") return t.displayName ?? t.name ?? "anonymous";
|
|
3746
|
-
if (typeof t === "object")
|
|
4377
|
+
if (typeof t === "object")
|
|
4378
|
+
return t.displayName ?? t.render?.displayName ?? t.render?.name ?? "forwardRef/memo";
|
|
3747
4379
|
return String(t);
|
|
3748
4380
|
}
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
4381
|
+
|
|
4382
|
+
// src/introspection/textPolicy.ts
|
|
4383
|
+
function isReferenceable(text) {
|
|
4384
|
+
return typeof text === "string" && /[\p{L}\p{N}]/u.test(text);
|
|
4385
|
+
}
|
|
4386
|
+
function foldLatinDiacritics(text) {
|
|
4387
|
+
return text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC");
|
|
4388
|
+
}
|
|
4389
|
+
function foldForCompare(text) {
|
|
4390
|
+
if (!text) return "";
|
|
4391
|
+
return foldLatinDiacritics(text.toLowerCase()).replace(/[^\p{L}\p{N}]/gu, "");
|
|
4392
|
+
}
|
|
4393
|
+
function labelToId(label) {
|
|
4394
|
+
if (!label) return "";
|
|
4395
|
+
return foldLatinDiacritics(label.toLowerCase()).replace(/[^\p{L}\p{N}\s]/gu, "").trim().replace(/\s+(.)/gu, (_, c) => c.toUpperCase()).replace(/\s+/gu, "");
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
// src/introspection/geometry.ts
|
|
4399
|
+
var _host = null;
|
|
4400
|
+
var _asked = 0;
|
|
4401
|
+
var _answered = 0;
|
|
4402
|
+
function setGeometryHost(host) {
|
|
4403
|
+
_host = host;
|
|
4404
|
+
_asked = 0;
|
|
4405
|
+
_answered = 0;
|
|
4406
|
+
}
|
|
4407
|
+
function geometryAvailability() {
|
|
4408
|
+
if (!_host) return "unavailable";
|
|
4409
|
+
if (_asked > 0 && _answered === 0) return "silent";
|
|
4410
|
+
return "available";
|
|
4411
|
+
}
|
|
4412
|
+
function geometryStats() {
|
|
4413
|
+
return { asked: _asked, answered: _answered };
|
|
4414
|
+
}
|
|
4415
|
+
function isUsableRect(rect) {
|
|
4416
|
+
return !!rect && Number.isFinite(rect.x) && Number.isFinite(rect.y) && Number.isFinite(rect.width) && Number.isFinite(rect.height);
|
|
4417
|
+
}
|
|
4418
|
+
var HOST_SEARCH_DEPTH = 6;
|
|
4419
|
+
function rectOfFiber(fiber) {
|
|
4420
|
+
const host = _host;
|
|
4421
|
+
if (!host || !fiber) return null;
|
|
4422
|
+
const stack = [{ node: fiber, depth: 0 }];
|
|
4423
|
+
let consulted = false;
|
|
4424
|
+
while (stack.length > 0) {
|
|
4425
|
+
const { node, depth } = stack.pop();
|
|
4426
|
+
const current = node;
|
|
4427
|
+
if (!current) continue;
|
|
4428
|
+
if (current.stateNode) {
|
|
4429
|
+
consulted = true;
|
|
4430
|
+
let rect = null;
|
|
4431
|
+
try {
|
|
4432
|
+
rect = host.rectOf(current.stateNode);
|
|
4433
|
+
} catch {
|
|
4434
|
+
rect = null;
|
|
4435
|
+
}
|
|
4436
|
+
if (isUsableRect(rect)) {
|
|
4437
|
+
_asked += 1;
|
|
4438
|
+
_answered += 1;
|
|
4439
|
+
return rect;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
if (depth < HOST_SEARCH_DEPTH && current.child) {
|
|
4443
|
+
stack.push({ node: current.child, depth: depth + 1 });
|
|
4444
|
+
const sibling = current.child.sibling;
|
|
4445
|
+
if (sibling) stack.push({ node: sibling, depth: depth + 1 });
|
|
3760
4446
|
}
|
|
3761
|
-
let top = fiber;
|
|
3762
|
-
while (top.return) top = top.return;
|
|
3763
|
-
setFiberRoot(top);
|
|
3764
|
-
}
|
|
3765
|
-
render() {
|
|
3766
|
-
return this.props.children ?? null;
|
|
3767
|
-
}
|
|
3768
|
-
};
|
|
3769
|
-
function AppilotsFiberRoot({
|
|
3770
|
-
children,
|
|
3771
|
-
onUnavailable
|
|
3772
|
-
}) {
|
|
3773
|
-
const Internals = React2__default.default.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
|
|
3774
|
-
const ownerFiber = Internals?.ReactCurrentOwner?.current;
|
|
3775
|
-
if (ownerFiber && !getFiberRoot()) {
|
|
3776
|
-
setFiberRoot(ownerFiber);
|
|
3777
4447
|
}
|
|
3778
|
-
|
|
4448
|
+
if (consulted) _asked += 1;
|
|
4449
|
+
return null;
|
|
3779
4450
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
4451
|
+
function isRectOnScreen(rect, window) {
|
|
4452
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
4453
|
+
if (rect.x >= window.width || rect.y >= window.height) return false;
|
|
4454
|
+
if (rect.x + rect.width <= 0 || rect.y + rect.height <= 0) return false;
|
|
4455
|
+
return true;
|
|
3785
4456
|
}
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
4457
|
+
var _rectByNode = /* @__PURE__ */ new WeakMap();
|
|
4458
|
+
function readGeometry(fiber) {
|
|
4459
|
+
const host = _host;
|
|
4460
|
+
if (!host) return { rect: null, fields: {} };
|
|
4461
|
+
const rect = rectOfFiber(fiber);
|
|
4462
|
+
if (!rect) return { rect: null, fields: {} };
|
|
4463
|
+
return { rect, fields: { onScreen: isRectOnScreen(rect, host.window) } };
|
|
3789
4464
|
}
|
|
3790
|
-
function
|
|
3791
|
-
if (
|
|
3792
|
-
|
|
4465
|
+
function withRect(node, reading) {
|
|
4466
|
+
if (reading.rect) _rectByNode.set(node, reading.rect);
|
|
4467
|
+
return node;
|
|
3793
4468
|
}
|
|
3794
|
-
function
|
|
3795
|
-
if (!
|
|
3796
|
-
|
|
4469
|
+
function rectForNode(node) {
|
|
4470
|
+
if (!node || typeof node !== "object") return void 0;
|
|
4471
|
+
return _rectByNode.get(node);
|
|
3797
4472
|
}
|
|
3798
4473
|
|
|
3799
|
-
// src/
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
console.log(`[Appilots] AppilotsProvider: configProp=${!!configProp}, globalConfig=${!!globalConfig}`);
|
|
3806
|
-
const config = configProp ?? (globalConfig ? {
|
|
3807
|
-
projectId: globalConfig.projectId,
|
|
3808
|
-
apiBaseUrl: globalConfig.apiBaseUrl,
|
|
3809
|
-
apiKey: globalConfig.apiKey,
|
|
3810
|
-
permissions: globalConfig.permissions,
|
|
3811
|
-
debug: globalConfig.debug,
|
|
3812
|
-
appVersion: globalConfig.appVersion,
|
|
3813
|
-
mcpVersion: globalConfig.mcpVersion,
|
|
3814
|
-
fetchPersonalization: globalConfig.fetchPersonalization
|
|
3815
|
-
} : null);
|
|
3816
|
-
if (!config?.projectId) {
|
|
3817
|
-
throw new Error(
|
|
3818
|
-
"AppilotsProvider: No config found. Add withAppilots() to your metro.config.js and create a .appilotsrc file, or pass a config prop directly."
|
|
3819
|
-
);
|
|
4474
|
+
// src/introspection/walkFiber.ts
|
|
4475
|
+
function getTypeName(type) {
|
|
4476
|
+
if (!type) return null;
|
|
4477
|
+
if (typeof type === "string") return type;
|
|
4478
|
+
if (typeof type === "function") {
|
|
4479
|
+
return type.displayName ?? type.name ?? null;
|
|
3820
4480
|
}
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
);
|
|
3845
|
-
const
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
4481
|
+
if (typeof type === "object") {
|
|
4482
|
+
if (type.displayName) return type.displayName;
|
|
4483
|
+
if (type.render) {
|
|
4484
|
+
return type.render.displayName ?? type.render.name ?? null;
|
|
4485
|
+
}
|
|
4486
|
+
if (type.type) return getTypeName(type.type);
|
|
4487
|
+
}
|
|
4488
|
+
return null;
|
|
4489
|
+
}
|
|
4490
|
+
function matchesName(fiber, name) {
|
|
4491
|
+
return getTypeName(fiber.type) === name || getTypeName(fiber.elementType) === name;
|
|
4492
|
+
}
|
|
4493
|
+
function extractTextFromChildren(children) {
|
|
4494
|
+
if (children == null) return "";
|
|
4495
|
+
if (typeof children === "string") return children;
|
|
4496
|
+
if (typeof children === "number") return String(children);
|
|
4497
|
+
if (Array.isArray(children)) {
|
|
4498
|
+
return children.map((c) => extractTextFromChildren(c)).filter(Boolean).join("");
|
|
4499
|
+
}
|
|
4500
|
+
return "";
|
|
4501
|
+
}
|
|
4502
|
+
function findChildText(fiber) {
|
|
4503
|
+
const stack = [];
|
|
4504
|
+
if (fiber.child) stack.push(fiber.child);
|
|
4505
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4506
|
+
const texts = [];
|
|
4507
|
+
const glyphs = [];
|
|
4508
|
+
while (stack.length > 0) {
|
|
4509
|
+
const node = stack.pop();
|
|
4510
|
+
if (!node || visited.has(node)) continue;
|
|
4511
|
+
visited.add(node);
|
|
4512
|
+
if (matchesName(node, "Text")) {
|
|
4513
|
+
const text = extractTextFromChildren(node.memoizedProps?.children);
|
|
4514
|
+
const trimmed = text.trim();
|
|
4515
|
+
if (isReferenceable(trimmed)) {
|
|
4516
|
+
texts.push(trimmed);
|
|
4517
|
+
if (texts.length >= 3) break;
|
|
4518
|
+
} else if (trimmed && glyphs.length < 3) {
|
|
4519
|
+
glyphs.push(trimmed);
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4523
|
+
if (node.child) stack.push(node.child);
|
|
4524
|
+
}
|
|
4525
|
+
if (texts.length > 0) return texts.join(" \xB7 ");
|
|
4526
|
+
return glyphs.length > 0 ? glyphs.join(" \xB7 ") : void 0;
|
|
4527
|
+
}
|
|
4528
|
+
var SELF_REFERENTIAL_COMPONENTS = /* @__PURE__ */ new Set([
|
|
4529
|
+
"AppilotsChat",
|
|
4530
|
+
"AppilotsChatInner",
|
|
4531
|
+
"ActionBreadcrumb",
|
|
4532
|
+
"ConfirmDialog"
|
|
4533
|
+
]);
|
|
4534
|
+
function isAppilotsSkipMarked(props) {
|
|
4535
|
+
if (!props) return false;
|
|
4536
|
+
return props.__appilotsSkip === true || props["data-appilots-skip"] === true || props.appilotsSkip === true;
|
|
4537
|
+
}
|
|
4538
|
+
function isAppilotsSensitiveMarked(props) {
|
|
4539
|
+
if (!props) return false;
|
|
4540
|
+
return props.__appilotsSensitive === true || props["data-appilots-sensitive"] === true || props.appilotsSensitive === true;
|
|
4541
|
+
}
|
|
4542
|
+
function stringProp(props, name) {
|
|
4543
|
+
const value = props?.[name];
|
|
4544
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4545
|
+
}
|
|
4546
|
+
function numberProp(props, name) {
|
|
4547
|
+
const value = props?.[name];
|
|
4548
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4549
|
+
}
|
|
4550
|
+
function shouldSkipSubtree(fiber) {
|
|
4551
|
+
const props = fiber.memoizedProps;
|
|
4552
|
+
if (!props) return false;
|
|
4553
|
+
const typeName2 = getTypeName(fiber.type) ?? getTypeName(fiber.elementType);
|
|
4554
|
+
if (typeName2 && SELF_REFERENTIAL_COMPONENTS.has(typeName2)) return true;
|
|
4555
|
+
if (isAppilotsSkipMarked(props)) return true;
|
|
4556
|
+
if (isModalFiber(fiber) && props.visible === false) return true;
|
|
4557
|
+
if (matchesName(fiber, "Screen") && props.activityState === 0) return true;
|
|
4558
|
+
const style = props.style;
|
|
4559
|
+
if (style) {
|
|
4560
|
+
const flat = Array.isArray(style) ? Object.assign({}, ...style.filter(Boolean)) : style;
|
|
4561
|
+
if (flat?.display === "none") return true;
|
|
4562
|
+
if (flat?.opacity === 0 && props.pointerEvents === "none") return true;
|
|
4563
|
+
}
|
|
4564
|
+
return false;
|
|
4565
|
+
}
|
|
4566
|
+
function inferInputType(props) {
|
|
4567
|
+
if (props.secureTextEntry || isAppilotsSensitiveMarked(props)) return "password";
|
|
4568
|
+
switch (props.keyboardType) {
|
|
4569
|
+
case "email-address":
|
|
4570
|
+
return "email";
|
|
4571
|
+
case "numeric":
|
|
4572
|
+
case "number-pad":
|
|
4573
|
+
case "decimal-pad":
|
|
4574
|
+
return "number";
|
|
4575
|
+
case "phone-pad":
|
|
4576
|
+
return "phone";
|
|
4577
|
+
default:
|
|
4578
|
+
return "text";
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
function inferLabel(props) {
|
|
4582
|
+
return props.accessibilityLabel ?? props.label ?? props.placeholder ?? props.testID;
|
|
4583
|
+
}
|
|
4584
|
+
function resolveIdentity(props, label) {
|
|
4585
|
+
const declared = stringProp(props, "testID") ?? stringProp(props, "accessibilityLabel");
|
|
4586
|
+
if (declared) return { id: declared, provenance: "declared" };
|
|
4587
|
+
const derived = labelToId(label);
|
|
4588
|
+
if (derived) return { id: derived, provenance: "derived" };
|
|
4589
|
+
return { provenance: "positional" };
|
|
4590
|
+
}
|
|
4591
|
+
function isSwitchLike(fiber, props) {
|
|
4592
|
+
if (props?.accessibilityRole === "switch" || props?.role === "switch") return true;
|
|
4593
|
+
return matchesName(fiber, "Switch");
|
|
4594
|
+
}
|
|
4595
|
+
function switchValue(props) {
|
|
4596
|
+
if (typeof props?.value === "boolean") return props.value;
|
|
4597
|
+
const checked = props?.accessibilityState?.checked;
|
|
4598
|
+
return checked === true;
|
|
4599
|
+
}
|
|
4600
|
+
function nearbyLabelForSwitch(fiber) {
|
|
4601
|
+
let parent = fiber?.return;
|
|
4602
|
+
for (let depth = 0; parent && depth < 3; depth++, parent = parent.return) {
|
|
4603
|
+
const text = findChildText(parent);
|
|
4604
|
+
if (text && isReferenceable(text)) return text;
|
|
4605
|
+
}
|
|
4606
|
+
return void 0;
|
|
4607
|
+
}
|
|
4608
|
+
var ALWAYS_LIST_NAMES = /* @__PURE__ */ new Set([
|
|
4609
|
+
"FlatList",
|
|
4610
|
+
"SectionList",
|
|
4611
|
+
"VirtualizedList",
|
|
4612
|
+
// Shopify's high-perf FlatList replacement — same usage shape, same
|
|
4613
|
+
// virtualization, same per-item key.
|
|
4614
|
+
"FlashList"
|
|
4615
|
+
]);
|
|
4616
|
+
var HEURISTIC_LIST_NAMES = /* @__PURE__ */ new Set(["ScrollView"]);
|
|
4617
|
+
var LIST_CELL_NAMES = /* @__PURE__ */ new Set([
|
|
4618
|
+
"CellRenderer",
|
|
4619
|
+
"CellRendererComponent",
|
|
4620
|
+
"VirtualizedListCellContextProvider"
|
|
4621
|
+
]);
|
|
4622
|
+
function looksLikeListContainerByProps(props) {
|
|
4623
|
+
if (!props || typeof props !== "object") return false;
|
|
4624
|
+
if (typeof props.renderItem !== "function") return false;
|
|
4625
|
+
if (Array.isArray(props.sections)) return true;
|
|
4626
|
+
if (Array.isArray(props.data)) return true;
|
|
4627
|
+
return typeof props.getItemCount === "function" && typeof props.getItem === "function";
|
|
4628
|
+
}
|
|
4629
|
+
function isAlwaysListContainer(fiber) {
|
|
4630
|
+
for (const name of ALWAYS_LIST_NAMES) {
|
|
4631
|
+
if (matchesName(fiber, name)) return true;
|
|
4632
|
+
}
|
|
4633
|
+
return looksLikeListContainerByProps(fiber?.memoizedProps);
|
|
4634
|
+
}
|
|
4635
|
+
function isHeuristicListContainer(fiber) {
|
|
4636
|
+
for (const name of HEURISTIC_LIST_NAMES) {
|
|
4637
|
+
if (matchesName(fiber, name)) return true;
|
|
4638
|
+
}
|
|
4639
|
+
return false;
|
|
4640
|
+
}
|
|
4641
|
+
function isKnownListCell(fiber) {
|
|
4642
|
+
const typeName2 = getTypeName(fiber.type) ?? getTypeName(fiber.elementType);
|
|
4643
|
+
return !!typeName2 && LIST_CELL_NAMES.has(typeName2);
|
|
4644
|
+
}
|
|
4645
|
+
var MODAL_HOST_NAMES = /* @__PURE__ */ new Set(["RCTModalHostView", "ModalHostView"]);
|
|
4646
|
+
function isModalHostFiber(fiber) {
|
|
4647
|
+
const type = fiber?.type;
|
|
4648
|
+
return typeof type === "string" && MODAL_HOST_NAMES.has(type);
|
|
4649
|
+
}
|
|
4650
|
+
function isModalFiber(fiber) {
|
|
4651
|
+
return matchesName(fiber, "Modal") || isModalHostFiber(fiber);
|
|
4652
|
+
}
|
|
4653
|
+
function isVisibleModalFiber(fiber) {
|
|
4654
|
+
return isModalFiber(fiber) && fiber?.memoizedProps?.visible !== false;
|
|
4655
|
+
}
|
|
4656
|
+
function collectVisibleModalFibers(container, excludeRoots = []) {
|
|
4657
|
+
const excluded = /* @__PURE__ */ new WeakSet();
|
|
4658
|
+
for (const rootFiber of excludeRoots) {
|
|
4659
|
+
if (rootFiber) excluded.add(rootFiber);
|
|
4660
|
+
}
|
|
4661
|
+
const found = [];
|
|
4662
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4663
|
+
const stack = [];
|
|
4664
|
+
if (container?.child) stack.push(container.child);
|
|
4665
|
+
while (stack.length > 0) {
|
|
4666
|
+
const node = stack.pop();
|
|
4667
|
+
if (!node || visited.has(node)) continue;
|
|
4668
|
+
visited.add(node);
|
|
4669
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4670
|
+
if (excluded.has(node) || shouldSkipSubtree(node)) continue;
|
|
4671
|
+
if (isVisibleModalFiber(node)) {
|
|
4672
|
+
found.push(node);
|
|
4673
|
+
continue;
|
|
4674
|
+
}
|
|
4675
|
+
if (node.child) stack.push(node.child);
|
|
4676
|
+
}
|
|
4677
|
+
return found;
|
|
4678
|
+
}
|
|
4679
|
+
function isMarkedListItem(fiber, listId) {
|
|
4680
|
+
const props = fiber?.memoizedProps;
|
|
4681
|
+
if (!props || props.__appilotsListItem !== true) return false;
|
|
4682
|
+
if (!listId) return true;
|
|
4683
|
+
return props.__appilotsListId === listId;
|
|
4684
|
+
}
|
|
4685
|
+
function listIdFromProps(props) {
|
|
4686
|
+
return stringProp(props, "__appilotsListId") ?? stringProp(props, "appilotsListId") ?? // A ScrollView that turned out to hold keyed rows is BOTH: the
|
|
4687
|
+
// walker sees a list, auto-tracking registered a scrollable. Sharing
|
|
4688
|
+
// the id lets the two merge instead of describing one surface twice.
|
|
4689
|
+
stringProp(props, "__appilotsScrollId");
|
|
4690
|
+
}
|
|
4691
|
+
function explicitListIdFromProps(props) {
|
|
4692
|
+
return stringProp(props, "__appilotsListId") ?? stringProp(props, "appilotsListId");
|
|
4693
|
+
}
|
|
4694
|
+
function listMetadataFromProps(props) {
|
|
4695
|
+
const id = listIdFromProps(props);
|
|
4696
|
+
const itemCount = numberProp(props, "__appilotsItemCount");
|
|
4697
|
+
const label = stringProp(props, "__appilotsListLabel") ?? stringProp(props, "accessibilityLabel") ?? stringProp(props, "testID");
|
|
4698
|
+
const refreshing = props?.refreshing === true || props?.__appilotsRefreshing === true;
|
|
4699
|
+
const empty = props?.__appilotsEmpty === true || typeof itemCount === "number" && itemCount === 0;
|
|
4700
|
+
return {
|
|
4701
|
+
id,
|
|
4702
|
+
itemCount,
|
|
4703
|
+
refreshing,
|
|
4704
|
+
empty,
|
|
4705
|
+
label,
|
|
4706
|
+
source: id ? "auto-tracked" : void 0
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
function hasRenderableContent(fiber) {
|
|
4710
|
+
const stack = [fiber];
|
|
4711
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4712
|
+
while (stack.length > 0) {
|
|
4713
|
+
const node = stack.pop();
|
|
4714
|
+
if (!node || visited.has(node)) continue;
|
|
4715
|
+
visited.add(node);
|
|
4716
|
+
if (shouldSkipSubtree(node) || isModalFiber(node)) continue;
|
|
4717
|
+
const props = node.memoizedProps ?? {};
|
|
4718
|
+
if (matchesName(node, "Text")) {
|
|
4719
|
+
const text = extractTextFromChildren(props.children);
|
|
4720
|
+
if (text && text.trim().length > 0) return true;
|
|
4721
|
+
}
|
|
4722
|
+
if (matchesName(node, "TextInput") || matchesName(node, "Switch")) {
|
|
4723
|
+
return true;
|
|
4724
|
+
}
|
|
4725
|
+
if (TOUCHABLE_NAMES.has(getTypeName(node.type) ?? "") || TOUCHABLE_NAMES.has(getTypeName(node.elementType) ?? "")) {
|
|
4726
|
+
return true;
|
|
4727
|
+
}
|
|
4728
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4729
|
+
if (node.child) stack.push(node.child);
|
|
4730
|
+
}
|
|
4731
|
+
return false;
|
|
4732
|
+
}
|
|
4733
|
+
function findListCellsByType(container, minItems) {
|
|
4734
|
+
const cells = [];
|
|
4735
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4736
|
+
const stack = [];
|
|
4737
|
+
if (container?.child) stack.push(container.child);
|
|
4738
|
+
while (stack.length > 0) {
|
|
4739
|
+
const node = stack.pop();
|
|
4740
|
+
if (!node || visited.has(node)) continue;
|
|
4741
|
+
visited.add(node);
|
|
4742
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4743
|
+
if (shouldSkipSubtree(node) || isModalFiber(node)) continue;
|
|
4744
|
+
if (isAlwaysListContainer(node)) continue;
|
|
4745
|
+
if (isKnownListCell(node) && hasRenderableContent(node)) {
|
|
4746
|
+
cells.push(node);
|
|
4747
|
+
continue;
|
|
4748
|
+
}
|
|
4749
|
+
if (node.child) stack.push(node.child);
|
|
4750
|
+
}
|
|
4751
|
+
return cells.length >= minItems ? cells : null;
|
|
4752
|
+
}
|
|
4753
|
+
function findMarkedListItems(container, minItems, listId) {
|
|
4754
|
+
const items = [];
|
|
4755
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4756
|
+
const stack = [];
|
|
4757
|
+
if (container?.child) stack.push(container.child);
|
|
4758
|
+
while (stack.length > 0) {
|
|
4759
|
+
const node = stack.pop();
|
|
4760
|
+
if (!node || visited.has(node)) continue;
|
|
4761
|
+
visited.add(node);
|
|
4762
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4763
|
+
if (shouldSkipSubtree(node) || isModalFiber(node)) continue;
|
|
4764
|
+
if (isMarkedListItem(node, listId)) {
|
|
4765
|
+
items.push(node);
|
|
4766
|
+
continue;
|
|
4767
|
+
}
|
|
4768
|
+
if (node.child) stack.push(node.child);
|
|
4769
|
+
}
|
|
4770
|
+
if (items.length < minItems) return null;
|
|
4771
|
+
items.sort((a, b) => {
|
|
4772
|
+
const ai = numberProp(a.memoizedProps, "__appilotsItemIndex");
|
|
4773
|
+
const bi = numberProp(b.memoizedProps, "__appilotsItemIndex");
|
|
4774
|
+
if (ai === void 0 && bi === void 0) return 0;
|
|
4775
|
+
if (ai === void 0) return 1;
|
|
4776
|
+
if (bi === void 0) return -1;
|
|
4777
|
+
return ai - bi;
|
|
4778
|
+
});
|
|
4779
|
+
return items;
|
|
4780
|
+
}
|
|
4781
|
+
function hasAuthoredReactKey(fiber) {
|
|
4782
|
+
const key = fiber?.key;
|
|
4783
|
+
if (key === null || key === void 0 || key === "") return false;
|
|
4784
|
+
const s = String(key);
|
|
4785
|
+
if (s.startsWith(".") && !s.startsWith(".$")) return false;
|
|
4786
|
+
return true;
|
|
4787
|
+
}
|
|
4788
|
+
function findKeyedSiblingItems(container, minItems) {
|
|
4789
|
+
if (!container) return null;
|
|
4790
|
+
let current = container;
|
|
4791
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4792
|
+
while (current && !visited.has(current)) {
|
|
4793
|
+
visited.add(current);
|
|
4794
|
+
const siblings = [];
|
|
4795
|
+
let cur = current.child;
|
|
4796
|
+
while (cur) {
|
|
4797
|
+
if (!isModalFiber(cur)) siblings.push(cur);
|
|
4798
|
+
cur = cur.sibling;
|
|
4799
|
+
}
|
|
4800
|
+
if (siblings.length === 0) return null;
|
|
4801
|
+
const keyed = siblings.filter(hasAuthoredReactKey);
|
|
4802
|
+
if (keyed.length >= minItems) {
|
|
4803
|
+
return keyed;
|
|
4804
|
+
}
|
|
4805
|
+
if (siblings.length > 1) {
|
|
4806
|
+
return null;
|
|
4807
|
+
}
|
|
4808
|
+
current = siblings[0];
|
|
4809
|
+
}
|
|
4810
|
+
return null;
|
|
4811
|
+
}
|
|
4812
|
+
function findListItems(container, minItems = 2, preferCells = false) {
|
|
4813
|
+
if (!container) return null;
|
|
4814
|
+
const listId = listIdFromProps(container.memoizedProps);
|
|
4815
|
+
const markedItems = findMarkedListItems(container, minItems, listId);
|
|
4816
|
+
if (markedItems) return markedItems;
|
|
4817
|
+
if (preferCells) {
|
|
4818
|
+
const cells = findListCellsByType(container, minItems);
|
|
4819
|
+
if (cells) return cells;
|
|
4820
|
+
}
|
|
4821
|
+
if (minItems <= 1) {
|
|
4822
|
+
const multiItemLevel = findKeyedSiblingItems(container, 2);
|
|
4823
|
+
if (multiItemLevel) return multiItemLevel;
|
|
4824
|
+
}
|
|
4825
|
+
return findKeyedSiblingItems(container, minItems) ?? findListCellsByType(container, minItems);
|
|
4826
|
+
}
|
|
4827
|
+
var TOUCHABLE_NAMES = /* @__PURE__ */ new Set([
|
|
4828
|
+
"TouchableOpacity",
|
|
4829
|
+
"TouchableHighlight",
|
|
4830
|
+
"TouchableWithoutFeedback",
|
|
4831
|
+
"TouchableNativeFeedback",
|
|
4832
|
+
"Pressable"
|
|
4833
|
+
]);
|
|
4834
|
+
function hasPressHandler(fiber) {
|
|
4835
|
+
const props = fiber?.memoizedProps ?? {};
|
|
4836
|
+
return typeof props.onPress === "function" || typeof props.onPressIn === "function";
|
|
4837
|
+
}
|
|
4838
|
+
function isPressableTarget(fiber) {
|
|
4839
|
+
return TOUCHABLE_NAMES.has(getTypeName(fiber.type) ?? "") || TOUCHABLE_NAMES.has(getTypeName(fiber.elementType) ?? "") || hasPressHandler(fiber);
|
|
4840
|
+
}
|
|
4841
|
+
function emptyDetectorCounts() {
|
|
4842
|
+
return {
|
|
4843
|
+
Text: 0,
|
|
4844
|
+
TextInput: 0,
|
|
4845
|
+
Switch: 0,
|
|
4846
|
+
ActivityIndicator: 0,
|
|
4847
|
+
Modal: 0,
|
|
4848
|
+
adjustable: 0,
|
|
4849
|
+
pressable: 0,
|
|
4850
|
+
listContainers: 0
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4853
|
+
function totalDetectorHits(counts) {
|
|
4854
|
+
return counts.Text + counts.TextInput + counts.Switch + counts.ActivityIndicator + counts.Modal + counts.adjustable + counts.pressable + counts.listContainers;
|
|
4855
|
+
}
|
|
4856
|
+
function addDetectorCounts(into, from) {
|
|
4857
|
+
into.Text += from.Text;
|
|
4858
|
+
into.TextInput += from.TextInput;
|
|
4859
|
+
into.Switch += from.Switch;
|
|
4860
|
+
into.ActivityIndicator += from.ActivityIndicator;
|
|
4861
|
+
into.Modal += from.Modal;
|
|
4862
|
+
into.adjustable += from.adjustable;
|
|
4863
|
+
into.pressable += from.pressable;
|
|
4864
|
+
into.listContainers += from.listContainers;
|
|
4865
|
+
}
|
|
4866
|
+
var _lastWalk = null;
|
|
4867
|
+
function getLastWalkDiagnostics() {
|
|
4868
|
+
return _lastWalk ? { ..._lastWalk, detectors: { ..._lastWalk.detectors } } : null;
|
|
4869
|
+
}
|
|
4870
|
+
function walkSubtree(root, detectLists, startInModal = false) {
|
|
4871
|
+
const result = {
|
|
4872
|
+
texts: [],
|
|
4873
|
+
inputs: [],
|
|
4874
|
+
buttons: [],
|
|
4875
|
+
toggles: [],
|
|
4876
|
+
sliders: [],
|
|
4877
|
+
visitedFibers: 0,
|
|
4878
|
+
skippedHidden: 0,
|
|
4879
|
+
loading: false,
|
|
4880
|
+
modalOpen: false,
|
|
4881
|
+
lists: [],
|
|
4882
|
+
detectors: emptyDetectorCounts()
|
|
4883
|
+
};
|
|
4884
|
+
if (!root) return result;
|
|
4885
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4886
|
+
const stack = [];
|
|
4887
|
+
if (root.child) stack.push({ fiber: root.child, inModal: startInModal });
|
|
4888
|
+
while (stack.length > 0) {
|
|
4889
|
+
const { fiber, inModal, ancestorPress } = stack.pop();
|
|
4890
|
+
if (!fiber || visited.has(fiber)) continue;
|
|
4891
|
+
visited.add(fiber);
|
|
4892
|
+
result.visitedFibers++;
|
|
4893
|
+
if (fiber.sibling) stack.push({ fiber: fiber.sibling, inModal });
|
|
4894
|
+
if (shouldSkipSubtree(fiber)) {
|
|
4895
|
+
result.skippedHidden++;
|
|
4896
|
+
continue;
|
|
4897
|
+
}
|
|
4898
|
+
if (detectLists) {
|
|
4899
|
+
const isAlways = isAlwaysListContainer(fiber);
|
|
4900
|
+
const isHeuristic = !isAlways && isHeuristicListContainer(fiber);
|
|
4901
|
+
if (isAlways || isHeuristic) {
|
|
4902
|
+
result.detectors.listContainers++;
|
|
4903
|
+
const minItems = isAlways ? 1 : 2;
|
|
4904
|
+
const items = findListItems(fiber, minItems, isAlways);
|
|
4905
|
+
if (items && items.length >= minItems) {
|
|
4906
|
+
const containerType = getTypeName(fiber.type) ?? getTypeName(fiber.elementType) ?? "List";
|
|
4907
|
+
const listIndex = result.lists.length;
|
|
4908
|
+
const itemSnapshots = items.map((itemFiber, i) => {
|
|
4909
|
+
const itemResult = walkSubtree(itemFiber, false, inModal);
|
|
4910
|
+
result.visitedFibers += itemResult.visitedFibers;
|
|
4911
|
+
result.skippedHidden += itemResult.skippedHidden;
|
|
4912
|
+
addDetectorCounts(result.detectors, itemResult.detectors);
|
|
4913
|
+
if (itemResult.loading) result.loading = true;
|
|
4914
|
+
if (itemResult.modalOpen) result.modalOpen = true;
|
|
4915
|
+
const reactKey = typeof itemFiber.key === "string" || typeof itemFiber.key === "number" ? String(itemFiber.key) : typeof itemFiber.memoizedProps?.__appilotsItemKey === "string" || typeof itemFiber.memoizedProps?.__appilotsItemKey === "number" ? String(itemFiber.memoizedProps.__appilotsItemKey) : void 0;
|
|
4916
|
+
const itemKey = typeof itemFiber.memoizedProps?.__appilotsItemKey === "string" || typeof itemFiber.memoizedProps?.__appilotsItemKey === "number" ? String(itemFiber.memoizedProps.__appilotsItemKey) : void 0;
|
|
4917
|
+
const dataIndex = numberProp(itemFiber.memoizedProps, "__appilotsItemIndex");
|
|
4918
|
+
const syntheticId = `list-${listIndex}-item-${i + 1}`;
|
|
4919
|
+
return {
|
|
4920
|
+
index: i + 1,
|
|
4921
|
+
dataIndex,
|
|
4922
|
+
reactKey,
|
|
4923
|
+
itemKey,
|
|
4924
|
+
syntheticId,
|
|
4925
|
+
texts: dedupeStringsInternal(itemResult.texts),
|
|
4926
|
+
// Scope the row's ordinals by its own synthetic id, so an
|
|
4927
|
+
// unnamed control in row 3 cannot collide with one in row 1
|
|
4928
|
+
// or with a screen-level one.
|
|
4929
|
+
buttons: dedupeButtonsInternal(itemResult.buttons, `${syntheticId}-`),
|
|
4930
|
+
inputs: dedupeInputsInternal(itemResult.inputs, `${syntheticId}-`),
|
|
4931
|
+
toggles: dedupeTogglesInternal(itemResult.toggles, `${syntheticId}-`)
|
|
4932
|
+
};
|
|
4933
|
+
});
|
|
4934
|
+
const meta2 = listMetadataFromProps(fiber.memoizedProps);
|
|
4935
|
+
result.lists.push({
|
|
4936
|
+
index: listIndex,
|
|
4937
|
+
id: meta2.id,
|
|
4938
|
+
containerType,
|
|
4939
|
+
source: meta2.source ?? "fiber",
|
|
4940
|
+
itemCount: meta2.itemCount,
|
|
4941
|
+
visibleItemCount: itemSnapshots.length,
|
|
4942
|
+
refreshing: meta2.refreshing,
|
|
4943
|
+
empty: meta2.empty,
|
|
4944
|
+
label: meta2.label,
|
|
4945
|
+
items: itemSnapshots
|
|
4946
|
+
});
|
|
4947
|
+
for (const modalFiber of collectVisibleModalFibers(fiber, items)) {
|
|
4948
|
+
result.modalOpen = true;
|
|
4949
|
+
if (modalFiber.child) stack.push({ fiber: modalFiber.child, inModal: true });
|
|
4950
|
+
}
|
|
4951
|
+
continue;
|
|
4952
|
+
}
|
|
4953
|
+
const meta = listMetadataFromProps(fiber.memoizedProps);
|
|
4954
|
+
const declaresList = meta.itemCount !== void 0 || meta.refreshing || meta.empty || !!explicitListIdFromProps(fiber.memoizedProps);
|
|
4955
|
+
if (declaresList) {
|
|
4956
|
+
const containerType = getTypeName(fiber.type) ?? getTypeName(fiber.elementType) ?? "List";
|
|
4957
|
+
result.lists.push({
|
|
4958
|
+
index: result.lists.length,
|
|
4959
|
+
id: meta.id,
|
|
4960
|
+
containerType,
|
|
4961
|
+
source: meta.source ?? "auto-tracked",
|
|
4962
|
+
itemCount: meta.itemCount,
|
|
4963
|
+
visibleItemCount: 0,
|
|
4964
|
+
refreshing: meta.refreshing,
|
|
4965
|
+
empty: meta.empty,
|
|
4966
|
+
label: meta.label,
|
|
4967
|
+
items: []
|
|
4968
|
+
});
|
|
4969
|
+
for (const modalFiber of collectVisibleModalFibers(fiber)) {
|
|
4970
|
+
result.modalOpen = true;
|
|
4971
|
+
if (modalFiber.child) stack.push({ fiber: modalFiber.child, inModal: true });
|
|
4972
|
+
}
|
|
4973
|
+
continue;
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
const props = fiber.memoizedProps ?? {};
|
|
4978
|
+
if (matchesName(fiber, "Text")) {
|
|
4979
|
+
result.detectors.Text++;
|
|
4980
|
+
const text = extractTextFromChildren(props.children);
|
|
4981
|
+
if (text && text.trim().length > 0) {
|
|
4982
|
+
result.texts.push(text.trim());
|
|
4983
|
+
}
|
|
4984
|
+
} else if (matchesName(fiber, "TextInput")) {
|
|
4985
|
+
result.detectors.TextInput++;
|
|
4986
|
+
if (props.__appilotsInternal !== true) {
|
|
4987
|
+
const isSecure = !!props.secureTextEntry || isAppilotsSensitiveMarked(props);
|
|
4988
|
+
const rawValue = typeof props.value === "string" ? props.value : void 0;
|
|
4989
|
+
const safeValue = isSecure ? rawValue && rawValue.length > 0 ? "<hidden>" : void 0 : rawValue;
|
|
4990
|
+
const identity = resolveIdentity(props, inferLabel(props));
|
|
4991
|
+
const geom = readGeometry(fiber);
|
|
4992
|
+
result.inputs.push(
|
|
4993
|
+
withRect(
|
|
4994
|
+
{
|
|
4995
|
+
id: identity.id,
|
|
4996
|
+
provenance: identity.provenance,
|
|
4997
|
+
label: inferLabel(props),
|
|
4998
|
+
value: safeValue,
|
|
4999
|
+
placeholder: props.placeholder,
|
|
5000
|
+
editable: props.editable !== false,
|
|
5001
|
+
secure: isSecure,
|
|
5002
|
+
type: inferInputType(props),
|
|
5003
|
+
// A field whose return key submits (#398). On a search box or
|
|
5004
|
+
// a one-field login this IS the submit, and without the flag
|
|
5005
|
+
// the screen looks like a form with no way to send it.
|
|
5006
|
+
...typeof props.onSubmitEditing === "function" ? { submitsOnReturn: true } : {},
|
|
5007
|
+
...inModal ? { inModal: true } : {},
|
|
5008
|
+
...geom.fields
|
|
5009
|
+
},
|
|
5010
|
+
geom
|
|
5011
|
+
)
|
|
5012
|
+
);
|
|
5013
|
+
}
|
|
5014
|
+
} else if (props.accessibilityRole === "adjustable") {
|
|
5015
|
+
result.detectors.adjustable++;
|
|
5016
|
+
if (props.__appilotsInternal !== true) {
|
|
5017
|
+
const av = props.accessibilityValue ?? {};
|
|
5018
|
+
const sliderLabel = props.accessibilityLabel ?? props.testID;
|
|
5019
|
+
const sliderIdentity = resolveIdentity(props, sliderLabel);
|
|
5020
|
+
const geom = readGeometry(fiber);
|
|
5021
|
+
result.sliders.push(
|
|
5022
|
+
withRect(
|
|
5023
|
+
{
|
|
5024
|
+
id: sliderIdentity.id,
|
|
5025
|
+
provenance: sliderIdentity.provenance,
|
|
5026
|
+
label: sliderLabel,
|
|
5027
|
+
value: typeof av.now === "number" ? av.now : void 0,
|
|
5028
|
+
min: typeof av.min === "number" ? av.min : void 0,
|
|
5029
|
+
max: typeof av.max === "number" ? av.max : void 0,
|
|
5030
|
+
...inModal ? { inModal: true } : {},
|
|
5031
|
+
...geom.fields
|
|
5032
|
+
},
|
|
5033
|
+
geom
|
|
5034
|
+
)
|
|
5035
|
+
);
|
|
5036
|
+
}
|
|
5037
|
+
} else if (isSwitchLike(fiber, props)) {
|
|
5038
|
+
result.detectors.Switch++;
|
|
5039
|
+
if (props.__appilotsInternal !== true) {
|
|
5040
|
+
const label = props.accessibilityLabel ?? props.testID ?? nearbyLabelForSwitch(fiber);
|
|
5041
|
+
const toggleIdentity = resolveIdentity(props, label);
|
|
5042
|
+
const geom = readGeometry(fiber);
|
|
5043
|
+
result.toggles.push(
|
|
5044
|
+
withRect(
|
|
5045
|
+
{
|
|
5046
|
+
// Same fallback the branch below has always had for buttons.
|
|
5047
|
+
// Without it every unnamed switch arrived as `id: undefined`
|
|
5048
|
+
// and dedupeToggles merged them all into one (#385).
|
|
5049
|
+
id: toggleIdentity.id,
|
|
5050
|
+
provenance: toggleIdentity.provenance,
|
|
5051
|
+
label,
|
|
5052
|
+
value: switchValue(props),
|
|
5053
|
+
...inModal ? { inModal: true } : {},
|
|
5054
|
+
...geom.fields
|
|
5055
|
+
},
|
|
5056
|
+
geom
|
|
5057
|
+
)
|
|
5058
|
+
);
|
|
5059
|
+
}
|
|
5060
|
+
continue;
|
|
5061
|
+
} else if (isPressableTarget(fiber)) {
|
|
5062
|
+
result.detectors.pressable++;
|
|
5063
|
+
const isRepeatOfAncestor = typeof props.onPress === "function" && props.onPress === ancestorPress;
|
|
5064
|
+
if (props.__appilotsInternal !== true && !isRepeatOfAncestor) {
|
|
5065
|
+
const label = findChildText(fiber);
|
|
5066
|
+
const state = props.accessibilityState ?? {};
|
|
5067
|
+
const on = state.checked === true || state.selected === true;
|
|
5068
|
+
const buttonIdentity = resolveIdentity(props, label);
|
|
5069
|
+
const geom = readGeometry(fiber);
|
|
5070
|
+
result.buttons.push(
|
|
5071
|
+
withRect(
|
|
5072
|
+
{
|
|
5073
|
+
id: buttonIdentity.id,
|
|
5074
|
+
provenance: buttonIdentity.provenance,
|
|
5075
|
+
label,
|
|
5076
|
+
disabled: !!props.disabled,
|
|
5077
|
+
...on ? { selected: true } : {},
|
|
5078
|
+
...inModal ? { inModal: true } : {},
|
|
5079
|
+
...geom.fields
|
|
5080
|
+
},
|
|
5081
|
+
geom
|
|
5082
|
+
)
|
|
5083
|
+
);
|
|
5084
|
+
}
|
|
5085
|
+
if (fiber.child) {
|
|
5086
|
+
stack.push({
|
|
5087
|
+
fiber: fiber.child,
|
|
5088
|
+
inModal,
|
|
5089
|
+
// Children of this pressable inherit its handler, so a nested
|
|
5090
|
+
// fiber forwarding the SAME `onPress` is recognized as a repeat.
|
|
5091
|
+
ancestorPress: typeof props.onPress === "function" ? props.onPress : ancestorPress
|
|
5092
|
+
});
|
|
5093
|
+
}
|
|
5094
|
+
continue;
|
|
5095
|
+
} else if (matchesName(fiber, "ActivityIndicator")) {
|
|
5096
|
+
result.detectors.ActivityIndicator++;
|
|
5097
|
+
result.loading = true;
|
|
5098
|
+
} else if (isModalFiber(fiber)) {
|
|
5099
|
+
result.detectors.Modal++;
|
|
5100
|
+
result.modalOpen = true;
|
|
5101
|
+
if (fiber.child) stack.push({ fiber: fiber.child, inModal: true });
|
|
5102
|
+
continue;
|
|
5103
|
+
}
|
|
5104
|
+
if (fiber.child) stack.push({ fiber: fiber.child, inModal });
|
|
5105
|
+
}
|
|
5106
|
+
return result;
|
|
5107
|
+
}
|
|
5108
|
+
function walkFiber(root) {
|
|
5109
|
+
const snapshot = {
|
|
5110
|
+
route: getCurrentScreen(),
|
|
5111
|
+
texts: [],
|
|
5112
|
+
inputs: [],
|
|
5113
|
+
buttons: [],
|
|
5114
|
+
toggles: [],
|
|
5115
|
+
// Sliders come from the ComponentRegistry (useAppilotsSlider) —
|
|
5116
|
+
// captureSnapshot merges them so observation only ever advertises
|
|
5117
|
+
// sliders the executor can actually set (min/max/step + setValue).
|
|
5118
|
+
sliders: [],
|
|
5119
|
+
loading: false,
|
|
5120
|
+
modalOpen: false,
|
|
5121
|
+
lists: [],
|
|
5122
|
+
choiceGroups: [],
|
|
5123
|
+
elements: [],
|
|
5124
|
+
stats: { visitedFibers: 0, skippedHidden: 0 }
|
|
5125
|
+
};
|
|
5126
|
+
if (!root) return snapshot;
|
|
5127
|
+
const sub = walkSubtree(root, true);
|
|
5128
|
+
snapshot.texts = dedupeStringsInternal(sub.texts);
|
|
5129
|
+
snapshot.inputs = dedupeInputsInternal(sub.inputs);
|
|
5130
|
+
snapshot.buttons = dedupeButtonsInternal(sub.buttons);
|
|
5131
|
+
snapshot.toggles = dedupeTogglesInternal(sub.toggles);
|
|
5132
|
+
snapshot.sliders = dedupeSliders(sub.sliders);
|
|
5133
|
+
snapshot.loading = sub.loading;
|
|
5134
|
+
snapshot.modalOpen = sub.modalOpen;
|
|
5135
|
+
snapshot.lists = sub.lists;
|
|
5136
|
+
snapshot.choiceGroups = deriveChoiceGroups(snapshot);
|
|
5137
|
+
snapshot.stats.visitedFibers = sub.visitedFibers;
|
|
5138
|
+
snapshot.stats.skippedHidden = sub.skippedHidden;
|
|
5139
|
+
snapshot.stats.identity = tallyIdentity(snapshot);
|
|
5140
|
+
_lastWalk = {
|
|
5141
|
+
visitedFibers: sub.visitedFibers,
|
|
5142
|
+
skippedHidden: sub.skippedHidden,
|
|
5143
|
+
detectors: sub.detectors
|
|
5144
|
+
};
|
|
5145
|
+
return snapshot;
|
|
5146
|
+
}
|
|
5147
|
+
function tallyIdentity(snapshot) {
|
|
5148
|
+
const tally = { declared: 0, derived: 0, positional: 0 };
|
|
5149
|
+
const count = (entries2) => {
|
|
5150
|
+
for (const entry of entries2) {
|
|
5151
|
+
if (entry.provenance) tally[entry.provenance] += 1;
|
|
5152
|
+
}
|
|
5153
|
+
};
|
|
5154
|
+
count(snapshot.inputs);
|
|
5155
|
+
count(snapshot.buttons);
|
|
5156
|
+
count(snapshot.toggles);
|
|
5157
|
+
count(snapshot.sliders);
|
|
5158
|
+
for (const list of snapshot.lists) {
|
|
5159
|
+
for (const item of list.items) {
|
|
5160
|
+
count(item.buttons);
|
|
5161
|
+
count(item.inputs);
|
|
5162
|
+
count(item.toggles);
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
return tally;
|
|
5166
|
+
}
|
|
5167
|
+
function dedupeButtonsInternal(buttons, scope = "") {
|
|
5168
|
+
return dedupeButtons(buttons, scope);
|
|
5169
|
+
}
|
|
5170
|
+
function dedupeInputsInternal(inputs, scope = "") {
|
|
5171
|
+
return dedupeInputs(inputs, scope);
|
|
5172
|
+
}
|
|
5173
|
+
function dedupeTogglesInternal(toggles, scope = "") {
|
|
5174
|
+
return dedupeToggles(toggles, scope);
|
|
5175
|
+
}
|
|
5176
|
+
function dedupeStringsInternal(strings) {
|
|
5177
|
+
return dedupeStrings(strings);
|
|
5178
|
+
}
|
|
5179
|
+
function isCommandLikeLabel(label) {
|
|
5180
|
+
if (!label) return true;
|
|
5181
|
+
const normalized = labelToId(label);
|
|
5182
|
+
if (!normalized) return true;
|
|
5183
|
+
return /^(proximo|prox|next|continuar|continue|voltar|back|confirmar|confirm|cancelar|cancel|salvar|save|enviar|submit|ok|limpar|clear|fechar|close|adicionar|add|novo|nova|new)$/i.test(
|
|
5184
|
+
normalized
|
|
3858
5185
|
);
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
5186
|
+
}
|
|
5187
|
+
function deriveChoiceGroups(snapshot) {
|
|
5188
|
+
const groups = [];
|
|
5189
|
+
for (const list of snapshot.lists) {
|
|
5190
|
+
const options = list.items.filter((item) => item.texts.length > 0 || item.buttons.length > 0).map((item, i) => {
|
|
5191
|
+
const texts = item.texts.length > 0 ? item.texts : item.buttons.map((b) => b.label ?? b.id ?? "").filter((text) => text.length > 0);
|
|
5192
|
+
return {
|
|
5193
|
+
index: i + 1,
|
|
5194
|
+
syntheticId: `${list.id ?? `list-${list.index}`}-choice-${i + 1}`,
|
|
5195
|
+
targetId: item.syntheticId,
|
|
5196
|
+
label: texts.slice(0, 2).join(" \xB7 "),
|
|
5197
|
+
texts,
|
|
5198
|
+
disabled: false
|
|
5199
|
+
};
|
|
3867
5200
|
});
|
|
3868
|
-
|
|
3869
|
-
|
|
5201
|
+
if (options.length > 0) {
|
|
5202
|
+
groups.push({
|
|
5203
|
+
index: groups.length,
|
|
5204
|
+
id: list.id,
|
|
5205
|
+
label: list.label,
|
|
5206
|
+
source: "list",
|
|
5207
|
+
options
|
|
5208
|
+
});
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
const buttonOptions = snapshot.buttons.filter((button) => !button.disabled).filter((button) => isReferenceable(button.label ?? button.id)).filter((button) => !isCommandLikeLabel(button.label ?? button.id)).map((button, i) => {
|
|
5212
|
+
const label = button.label ?? button.id ?? "";
|
|
5213
|
+
return {
|
|
5214
|
+
index: i + 1,
|
|
5215
|
+
syntheticId: `choice-buttons-option-${i + 1}`,
|
|
5216
|
+
targetId: button.id ?? button.label,
|
|
5217
|
+
label,
|
|
5218
|
+
texts: [label],
|
|
5219
|
+
selected: !!button.selected,
|
|
5220
|
+
disabled: !!button.disabled
|
|
3870
5221
|
};
|
|
3871
|
-
}
|
|
3872
|
-
|
|
3873
|
-
(
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
5222
|
+
});
|
|
5223
|
+
if (buttonOptions.length >= 2) {
|
|
5224
|
+
groups.push({
|
|
5225
|
+
index: groups.length,
|
|
5226
|
+
id: "visible-options",
|
|
5227
|
+
label: "Visible options",
|
|
5228
|
+
source: "buttons",
|
|
5229
|
+
options: buttonOptions
|
|
5230
|
+
});
|
|
5231
|
+
}
|
|
5232
|
+
return groups;
|
|
3877
5233
|
}
|
|
3878
|
-
function
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
5234
|
+
function positionalId(scope, kind, ordinal) {
|
|
5235
|
+
return `${scope}${kind}-${ordinal}`;
|
|
5236
|
+
}
|
|
5237
|
+
function dedupeButtons(buttons, scope = "") {
|
|
5238
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5239
|
+
const out = [];
|
|
5240
|
+
let unnamed = 0;
|
|
5241
|
+
for (const b of buttons) {
|
|
5242
|
+
const idTrim = b.id?.trim();
|
|
5243
|
+
const labelTrim = b.label?.trim();
|
|
5244
|
+
if (!idTrim && !isReferenceable(labelTrim)) {
|
|
5245
|
+
unnamed += 1;
|
|
5246
|
+
out.push({ ...b, id: positionalId(scope, "btn", unnamed), provenance: "positional" });
|
|
5247
|
+
continue;
|
|
5248
|
+
}
|
|
5249
|
+
const key = `${idTrim ?? ""}|${labelTrim ?? ""}|${b.disabled ? 1 : 0}|${b.inModal ? 1 : 0}|${b.selected ? 1 : 0}`;
|
|
5250
|
+
if (seen.has(key)) continue;
|
|
5251
|
+
seen.add(key);
|
|
5252
|
+
out.push(b);
|
|
3882
5253
|
}
|
|
3883
|
-
return
|
|
5254
|
+
return out;
|
|
5255
|
+
}
|
|
5256
|
+
function dedupeInputs(inputs, scope = "") {
|
|
5257
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5258
|
+
const out = [];
|
|
5259
|
+
let unnamed = 0;
|
|
5260
|
+
for (const i of inputs) {
|
|
5261
|
+
if (!i.id?.trim() && !i.label?.trim() && !i.placeholder?.trim()) {
|
|
5262
|
+
unnamed += 1;
|
|
5263
|
+
out.push({ ...i, id: positionalId(scope, "input", unnamed), provenance: "positional" });
|
|
5264
|
+
continue;
|
|
5265
|
+
}
|
|
5266
|
+
const key = `${i.id ?? ""}|${i.label ?? ""}|${i.placeholder ?? ""}|${i.inModal ? 1 : 0}`;
|
|
5267
|
+
if (seen.has(key)) continue;
|
|
5268
|
+
seen.add(key);
|
|
5269
|
+
out.push(i);
|
|
5270
|
+
}
|
|
5271
|
+
return out;
|
|
5272
|
+
}
|
|
5273
|
+
function dedupeSliders(sliders, scope = "") {
|
|
5274
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5275
|
+
const out = [];
|
|
5276
|
+
let unnamed = 0;
|
|
5277
|
+
for (const s of sliders) {
|
|
5278
|
+
if (!s.id?.trim() && !s.label?.trim()) {
|
|
5279
|
+
unnamed += 1;
|
|
5280
|
+
out.push({ ...s, id: positionalId(scope, "slider", unnamed), provenance: "positional" });
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
const key = `${s.id ?? ""}|${s.label ?? ""}|${s.inModal ? 1 : 0}`;
|
|
5284
|
+
if (seen.has(key)) continue;
|
|
5285
|
+
seen.add(key);
|
|
5286
|
+
out.push(s);
|
|
5287
|
+
}
|
|
5288
|
+
return out;
|
|
5289
|
+
}
|
|
5290
|
+
function dedupeToggles(toggles, scope = "") {
|
|
5291
|
+
let unnamed = 0;
|
|
5292
|
+
return toggles.map((t) => {
|
|
5293
|
+
if (t.id?.trim()) return t;
|
|
5294
|
+
unnamed += 1;
|
|
5295
|
+
return { ...t, id: positionalId(scope, "toggle", unnamed), provenance: "positional" };
|
|
5296
|
+
});
|
|
5297
|
+
}
|
|
5298
|
+
function dedupeStrings(strings) {
|
|
5299
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5300
|
+
const out = [];
|
|
5301
|
+
for (const s of strings) {
|
|
5302
|
+
const trimmed = s.trim();
|
|
5303
|
+
if (!trimmed) continue;
|
|
5304
|
+
if (seen.has(trimmed)) continue;
|
|
5305
|
+
seen.add(trimmed);
|
|
5306
|
+
out.push(trimmed);
|
|
5307
|
+
}
|
|
5308
|
+
return out;
|
|
3884
5309
|
}
|
|
3885
5310
|
|
|
3886
5311
|
// src/introspection/probeLoadingState.ts
|
|
3887
|
-
function
|
|
5312
|
+
function getTypeName2(type) {
|
|
3888
5313
|
if (!type) return null;
|
|
3889
5314
|
if (typeof type === "string") return type;
|
|
3890
5315
|
if (typeof type === "function") return type.displayName ?? type.name ?? null;
|
|
3891
5316
|
if (typeof type === "object") {
|
|
3892
5317
|
if (type.displayName) return type.displayName;
|
|
3893
5318
|
if (type.render) return type.render.displayName ?? type.render.name ?? null;
|
|
3894
|
-
if (type.type) return
|
|
5319
|
+
if (type.type) return getTypeName2(type.type);
|
|
3895
5320
|
}
|
|
3896
5321
|
return null;
|
|
3897
5322
|
}
|
|
3898
|
-
function
|
|
3899
|
-
return
|
|
5323
|
+
function matchesName2(fiber, name) {
|
|
5324
|
+
return getTypeName2(fiber.type) === name || getTypeName2(fiber.elementType) === name;
|
|
3900
5325
|
}
|
|
3901
5326
|
var SKELETON_NAME_RE = /skeleton|shimmer|contentloader/i;
|
|
3902
5327
|
function looksLikeSkeleton(fiber) {
|
|
3903
|
-
const name =
|
|
5328
|
+
const name = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
|
|
3904
5329
|
return !!name && SKELETON_NAME_RE.test(name);
|
|
3905
5330
|
}
|
|
3906
|
-
var
|
|
5331
|
+
var SELF_REFERENTIAL_COMPONENTS2 = /* @__PURE__ */ new Set([
|
|
3907
5332
|
"AppilotsChat",
|
|
3908
5333
|
"AppilotsChatInner",
|
|
3909
5334
|
"ActionBreadcrumb",
|
|
3910
5335
|
"ConfirmDialog"
|
|
3911
5336
|
]);
|
|
3912
|
-
function
|
|
5337
|
+
function shouldSkipSubtree2(fiber) {
|
|
3913
5338
|
const props = fiber.memoizedProps;
|
|
3914
5339
|
if (!props) return false;
|
|
3915
|
-
const typeName2 =
|
|
3916
|
-
if (typeName2 &&
|
|
5340
|
+
const typeName2 = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
|
|
5341
|
+
if (typeName2 && SELF_REFERENTIAL_COMPONENTS2.has(typeName2)) return true;
|
|
3917
5342
|
if (props.__appilotsSkip === true || props["data-appilots-skip"] === true || props.appilotsSkip === true) {
|
|
3918
5343
|
return true;
|
|
3919
5344
|
}
|
|
3920
|
-
if (
|
|
3921
|
-
if (
|
|
5345
|
+
if (isModalFiber(fiber) && props.visible === false) return true;
|
|
5346
|
+
if (matchesName2(fiber, "Screen") && props.activityState === 0) return true;
|
|
3922
5347
|
const style = props.style;
|
|
3923
5348
|
if (style) {
|
|
3924
5349
|
const flat = Array.isArray(style) ? Object.assign({}, ...style.filter(Boolean)) : style;
|
|
@@ -3927,7 +5352,7 @@ function shouldSkipSubtree(fiber) {
|
|
|
3927
5352
|
}
|
|
3928
5353
|
return false;
|
|
3929
5354
|
}
|
|
3930
|
-
var
|
|
5355
|
+
var TOUCHABLE_NAMES2 = /* @__PURE__ */ new Set([
|
|
3931
5356
|
"TouchableOpacity",
|
|
3932
5357
|
"TouchableHighlight",
|
|
3933
5358
|
"TouchableWithoutFeedback",
|
|
@@ -3935,7 +5360,7 @@ var TOUCHABLE_NAMES = /* @__PURE__ */ new Set([
|
|
|
3935
5360
|
"Pressable"
|
|
3936
5361
|
]);
|
|
3937
5362
|
function isTouchable(fiber) {
|
|
3938
|
-
return
|
|
5363
|
+
return TOUCHABLE_NAMES2.has(getTypeName2(fiber.type) ?? "") || TOUCHABLE_NAMES2.has(getTypeName2(fiber.elementType) ?? "");
|
|
3939
5364
|
}
|
|
3940
5365
|
function pickId(props) {
|
|
3941
5366
|
return props?.testID ?? props?.accessibilityLabel ?? void 0;
|
|
@@ -3971,31 +5396,34 @@ function probeLoadingState(pressedComponentId, route) {
|
|
|
3971
5396
|
visited.add(fiber);
|
|
3972
5397
|
result.visitedFibers++;
|
|
3973
5398
|
if (fiber.sibling) stack.push(fiber.sibling);
|
|
3974
|
-
if (
|
|
5399
|
+
if (shouldSkipSubtree2(fiber)) continue;
|
|
3975
5400
|
const props = fiber.memoizedProps ?? {};
|
|
3976
|
-
if (
|
|
5401
|
+
if (matchesName2(fiber, "ActivityIndicator") || looksLikeSkeleton(fiber)) {
|
|
3977
5402
|
result.loading = true;
|
|
3978
|
-
} else if (
|
|
5403
|
+
} else if (isModalFiber(fiber)) {
|
|
3979
5404
|
result.modalOpen = true;
|
|
5405
|
+
} else if (isSwitchLike(fiber, props)) {
|
|
5406
|
+
const id = pickId(props);
|
|
5407
|
+
toggleSigs.push(`${id ?? "?"}:${switchValue(props) ? 1 : 0}`);
|
|
3980
5408
|
} else if (isTouchable(fiber)) {
|
|
3981
5409
|
if (props.__appilotsInternal !== true) {
|
|
3982
5410
|
const id = pickId(props);
|
|
3983
5411
|
const disabled = !!props.disabled;
|
|
3984
|
-
|
|
5412
|
+
const state = props.accessibilityState ?? {};
|
|
5413
|
+
const on = state.checked === true || state.selected === true;
|
|
5414
|
+
buttonSigs.push(`${id ?? "?"}:${disabled ? 1 : 0}:${on ? 1 : 0}`);
|
|
3985
5415
|
if (pressedComponentId && idMatches(id, pressedComponentId)) {
|
|
3986
5416
|
result.pressedFound = true;
|
|
3987
5417
|
if (disabled) result.pressedDisabled = true;
|
|
3988
5418
|
}
|
|
3989
5419
|
}
|
|
3990
|
-
} else if (
|
|
5420
|
+
} else if (matchesName2(fiber, "TextInput")) {
|
|
3991
5421
|
if (props.__appilotsInternal !== true) {
|
|
3992
5422
|
const id = pickId(props);
|
|
3993
5423
|
const editable = props.editable !== false;
|
|
3994
|
-
|
|
5424
|
+
const filled = typeof props.value === "string" && props.value.length > 0;
|
|
5425
|
+
inputSigs.push(`${id ?? "?"}:${editable ? 1 : 0}:${filled ? 1 : 0}`);
|
|
3995
5426
|
}
|
|
3996
|
-
} else if (matchesName(fiber, "Switch")) {
|
|
3997
|
-
const id = pickId(props);
|
|
3998
|
-
toggleSigs.push(`${id ?? "?"}:${props.value ? 1 : 0}`);
|
|
3999
5427
|
}
|
|
4000
5428
|
if (fiber.child) stack.push(fiber.child);
|
|
4001
5429
|
}
|
|
@@ -4006,11 +5434,48 @@ function probeLoadingState(pressedComponentId, route) {
|
|
|
4006
5434
|
return result;
|
|
4007
5435
|
}
|
|
4008
5436
|
|
|
5437
|
+
// ../shared/dist/routes/index.mjs
|
|
5438
|
+
var CONTAINER_SUFFIXES = [
|
|
5439
|
+
"screen",
|
|
5440
|
+
"route",
|
|
5441
|
+
"page",
|
|
5442
|
+
"view",
|
|
5443
|
+
"tab",
|
|
5444
|
+
"stack",
|
|
5445
|
+
"drawer",
|
|
5446
|
+
"navigator"
|
|
5447
|
+
];
|
|
5448
|
+
var CONTENT_SUFFIXES = ["list", "details", "detail", "create", "edit", "new", "form", "index"];
|
|
5449
|
+
var MIN_STEM_LENGTH = 3;
|
|
5450
|
+
function fold(name) {
|
|
5451
|
+
return name.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
5452
|
+
}
|
|
5453
|
+
function stripSuffixes(folded, suffixes) {
|
|
5454
|
+
for (const suffix of suffixes) {
|
|
5455
|
+
if (!folded.endsWith(suffix)) continue;
|
|
5456
|
+
const stem = folded.slice(0, -suffix.length);
|
|
5457
|
+
if (stem.length >= MIN_STEM_LENGTH) return stem;
|
|
5458
|
+
}
|
|
5459
|
+
return folded;
|
|
5460
|
+
}
|
|
5461
|
+
function normalizeRouteFamily(name) {
|
|
5462
|
+
const all = [...CONTAINER_SUFFIXES, ...CONTENT_SUFFIXES];
|
|
5463
|
+
let stem = stripSuffixes(stripSuffixes(fold(name), all), all);
|
|
5464
|
+
if (stem.endsWith("s") && stem.length - 1 >= MIN_STEM_LENGTH) stem = stem.slice(0, -1);
|
|
5465
|
+
return stem;
|
|
5466
|
+
}
|
|
5467
|
+
function routesBelongToSameFeature(a, b) {
|
|
5468
|
+
const na = normalizeRouteFamily(a);
|
|
5469
|
+
const nb = normalizeRouteFamily(b);
|
|
5470
|
+
if (!na || !nb) return false;
|
|
5471
|
+
return na === nb;
|
|
5472
|
+
}
|
|
5473
|
+
|
|
4009
5474
|
// src/navigation/navigationState.ts
|
|
4010
5475
|
var _currentScreen = null;
|
|
4011
5476
|
var _navigationRef = null;
|
|
4012
5477
|
function setCurrentScreen(screen) {
|
|
4013
|
-
|
|
5478
|
+
appilotsDebugLog(`setCurrentScreen: "${_currentScreen}" \u2192 "${screen}"`);
|
|
4014
5479
|
_currentScreen = screen;
|
|
4015
5480
|
}
|
|
4016
5481
|
function getCurrentScreen() {
|
|
@@ -4019,8 +5484,8 @@ function getCurrentScreen() {
|
|
|
4019
5484
|
const deepest = activePath[activePath.length - 1] ?? null;
|
|
4020
5485
|
if (deepest && deepest !== _currentScreen) {
|
|
4021
5486
|
if (_currentScreen) {
|
|
4022
|
-
|
|
4023
|
-
`
|
|
5487
|
+
appilotsDebugLog(
|
|
5488
|
+
`getCurrentScreen: active path differs from cached \u2014 deepest="${deepest}", cached="${_currentScreen}". Updating cache.`
|
|
4024
5489
|
);
|
|
4025
5490
|
}
|
|
4026
5491
|
_currentScreen = deepest;
|
|
@@ -4031,7 +5496,9 @@ function getCurrentScreen() {
|
|
|
4031
5496
|
const liveScreen = getActiveRouteFromRef(_navigationRef);
|
|
4032
5497
|
if (liveScreen) {
|
|
4033
5498
|
if (liveScreen !== _currentScreen) {
|
|
4034
|
-
|
|
5499
|
+
appilotsDebugLog(
|
|
5500
|
+
`getCurrentScreen: live reading differs from cached \u2014 live="${liveScreen}", cached="${_currentScreen}". Updating cache.`
|
|
5501
|
+
);
|
|
4035
5502
|
_currentScreen = liveScreen;
|
|
4036
5503
|
}
|
|
4037
5504
|
return liveScreen;
|
|
@@ -4075,7 +5542,9 @@ function getActiveRouteNames() {
|
|
|
4075
5542
|
function routeNamesAtState(state) {
|
|
4076
5543
|
if (!state) return [];
|
|
4077
5544
|
if (Array.isArray(state.routeNames)) {
|
|
4078
|
-
return state.routeNames.filter(
|
|
5545
|
+
return state.routeNames.filter(
|
|
5546
|
+
(name) => typeof name === "string" && name.length > 0
|
|
5547
|
+
);
|
|
4079
5548
|
}
|
|
4080
5549
|
if (Array.isArray(state.routes)) {
|
|
4081
5550
|
return state.routes.map((route) => route?.name).filter((name) => typeof name === "string" && name.length > 0);
|
|
@@ -4192,16 +5661,6 @@ function getActiveRouteFromRef(navRef) {
|
|
|
4192
5661
|
}
|
|
4193
5662
|
}
|
|
4194
5663
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4195
|
-
function normalizeRouteFamily(name) {
|
|
4196
|
-
return name.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/(tab|list|screen|details?|navigator)$/i, "");
|
|
4197
|
-
}
|
|
4198
|
-
function routesBelongToSameFeature(a, b) {
|
|
4199
|
-
const na = normalizeRouteFamily(a);
|
|
4200
|
-
const nb = normalizeRouteFamily(b);
|
|
4201
|
-
if (!na || !nb) return false;
|
|
4202
|
-
if (na === nb) return true;
|
|
4203
|
-
return na.startsWith(nb) || nb.startsWith(na);
|
|
4204
|
-
}
|
|
4205
5664
|
function navigationTargetReached(target, fromScreen) {
|
|
4206
5665
|
const cur = getCurrentScreen();
|
|
4207
5666
|
const path = getActiveRouteNames();
|
|
@@ -4377,6 +5836,365 @@ async function waitForLoadingSettle(opts = {}) {
|
|
|
4377
5836
|
};
|
|
4378
5837
|
}
|
|
4379
5838
|
|
|
5839
|
+
// src/version.ts
|
|
5840
|
+
var SDK_VERSION2 = "0.10.0";
|
|
5841
|
+
var FiberSentinel = class extends React2__default.default.Component {
|
|
5842
|
+
componentDidMount() {
|
|
5843
|
+
const fiber = this._reactInternals ?? this._reactInternalFiber ?? null;
|
|
5844
|
+
if (!fiber) {
|
|
5845
|
+
const reactVersion = React2__default.default.version ?? null;
|
|
5846
|
+
appilotsDebugWarn(
|
|
5847
|
+
`FiberSentinel: this._reactInternals is undefined; snapshot capture will fall back to render-time owner. React version may be incompatible (react=${reactVersion}).`
|
|
5848
|
+
);
|
|
5849
|
+
recordIntrospectionFailure("sentinel-missing-internals", reactVersion);
|
|
5850
|
+
this.props.onUnavailable?.({ reactVersion });
|
|
5851
|
+
return;
|
|
5852
|
+
}
|
|
5853
|
+
let top = fiber;
|
|
5854
|
+
while (top.return) top = top.return;
|
|
5855
|
+
setFiberRoot(top);
|
|
5856
|
+
}
|
|
5857
|
+
render() {
|
|
5858
|
+
return this.props.children ?? null;
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
function AppilotsFiberRoot({
|
|
5862
|
+
children,
|
|
5863
|
+
onUnavailable
|
|
5864
|
+
}) {
|
|
5865
|
+
const Internals = React2__default.default.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
|
|
5866
|
+
const ownerFiber = Internals?.ReactCurrentOwner?.current;
|
|
5867
|
+
if (ownerFiber && !getFiberRoot()) {
|
|
5868
|
+
setFiberRoot(ownerFiber);
|
|
5869
|
+
}
|
|
5870
|
+
return /* @__PURE__ */ React2__default.default.createElement(FiberSentinel, { onUnavailable }, children);
|
|
5871
|
+
}
|
|
5872
|
+
|
|
5873
|
+
// src/debug/logger.ts
|
|
5874
|
+
setAppilotsDebugResolver(() => getGlobalConfig()?.debug === true);
|
|
5875
|
+
function setAppilotsDebugEnabled2(enabled) {
|
|
5876
|
+
setAppilotsDebugEnabled(enabled);
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
// src/platform/nativeDialogTracking.ts
|
|
5880
|
+
var DEFAULT_BUTTON_LABEL = "OK";
|
|
5881
|
+
var ANSWERED_DIALOG_MEMORY_MS = 15e3;
|
|
5882
|
+
var _open = null;
|
|
5883
|
+
var _lastAnswered = null;
|
|
5884
|
+
var _counter = 0;
|
|
5885
|
+
var _installed = null;
|
|
5886
|
+
function getOpenNativeDialog() {
|
|
5887
|
+
return _open;
|
|
5888
|
+
}
|
|
5889
|
+
function getLastAnsweredNativeDialog() {
|
|
5890
|
+
if (!_lastAnswered) return null;
|
|
5891
|
+
if (Date.now() - _lastAnswered.at > ANSWERED_DIALOG_MEMORY_MS) return null;
|
|
5892
|
+
return _lastAnswered;
|
|
5893
|
+
}
|
|
5894
|
+
function labelOf(button, index) {
|
|
5895
|
+
const text = typeof button?.text === "string" ? button.text.trim() : "";
|
|
5896
|
+
if (text.length > 0) return text;
|
|
5897
|
+
return index === 0 ? DEFAULT_BUTTON_LABEL : `Button ${index + 1}`;
|
|
5898
|
+
}
|
|
5899
|
+
function installNativeDialogTracking(host) {
|
|
5900
|
+
if (_installed) return;
|
|
5901
|
+
const original = host.alert;
|
|
5902
|
+
const patched = (title, message, buttons, options) => {
|
|
5903
|
+
const id = `native-dialog-${_counter += 1}`;
|
|
5904
|
+
const source = Array.isArray(buttons) && buttons.length > 0 ? buttons : [{ text: DEFAULT_BUTTON_LABEL }];
|
|
5905
|
+
const close = (dialogId, answeredWith) => {
|
|
5906
|
+
if (_open?.id !== dialogId) return;
|
|
5907
|
+
_lastAnswered = {
|
|
5908
|
+
id: dialogId,
|
|
5909
|
+
...tracked.title ? { title: tracked.title } : {},
|
|
5910
|
+
buttons: tracked.buttons.map((button) => button.label),
|
|
5911
|
+
...answeredWith ? { answeredWith } : {},
|
|
5912
|
+
at: Date.now()
|
|
5913
|
+
};
|
|
5914
|
+
_open = null;
|
|
5915
|
+
};
|
|
5916
|
+
const tracked = {
|
|
5917
|
+
id,
|
|
5918
|
+
title: typeof title === "string" && title.trim().length > 0 ? title.trim() : void 0,
|
|
5919
|
+
message: typeof message === "string" && message.trim().length > 0 ? message.trim() : void 0,
|
|
5920
|
+
buttons: []
|
|
5921
|
+
};
|
|
5922
|
+
const wrapped = source.map((button, index) => ({
|
|
5923
|
+
...button,
|
|
5924
|
+
onPress: (value) => {
|
|
5925
|
+
close(id, labelOf(button, index));
|
|
5926
|
+
button.onPress?.(value);
|
|
5927
|
+
}
|
|
5928
|
+
}));
|
|
5929
|
+
tracked.buttons = source.map((button, index) => ({
|
|
5930
|
+
label: labelOf(button, index),
|
|
5931
|
+
...button.style ? { style: button.style } : {},
|
|
5932
|
+
press: () => {
|
|
5933
|
+
wrapped[index]?.onPress?.();
|
|
5934
|
+
}
|
|
5935
|
+
}));
|
|
5936
|
+
_open = tracked;
|
|
5937
|
+
const opts = options;
|
|
5938
|
+
const patchedOptions = opts && typeof opts === "object" ? {
|
|
5939
|
+
...opts,
|
|
5940
|
+
onDismiss: () => {
|
|
5941
|
+
close(id);
|
|
5942
|
+
opts.onDismiss?.();
|
|
5943
|
+
}
|
|
5944
|
+
} : options;
|
|
5945
|
+
return original(title, message, wrapped, patchedOptions);
|
|
5946
|
+
};
|
|
5947
|
+
host.alert = patched;
|
|
5948
|
+
_installed = { host, original, patched };
|
|
5949
|
+
}
|
|
5950
|
+
function uninstallNativeDialogTracking() {
|
|
5951
|
+
if (!_installed) return;
|
|
5952
|
+
if (_installed.host.alert === _installed.patched) {
|
|
5953
|
+
_installed.host.alert = _installed.original;
|
|
5954
|
+
}
|
|
5955
|
+
_installed = null;
|
|
5956
|
+
_open = null;
|
|
5957
|
+
_lastAnswered = null;
|
|
5958
|
+
}
|
|
5959
|
+
|
|
5960
|
+
// src/introspection/nameMangling.ts
|
|
5961
|
+
var _lastProbe = null;
|
|
5962
|
+
function getNameManglingProbe() {
|
|
5963
|
+
return _lastProbe;
|
|
5964
|
+
}
|
|
5965
|
+
function probeNameMangling(components) {
|
|
5966
|
+
const observed = {};
|
|
5967
|
+
let mangled = false;
|
|
5968
|
+
for (const [expected, component] of Object.entries(components)) {
|
|
5969
|
+
if (component === void 0) continue;
|
|
5970
|
+
const actual = getTypeName(component) ?? null;
|
|
5971
|
+
observed[expected] = actual;
|
|
5972
|
+
if (actual !== expected) mangled = true;
|
|
5973
|
+
}
|
|
5974
|
+
_lastProbe = { mangled, observed };
|
|
5975
|
+
if (mangled) {
|
|
5976
|
+
recordIntrospectionFailure("names-mangled", null);
|
|
5977
|
+
console.warn(
|
|
5978
|
+
"[Appilots] This bundle renamed React Native components: " + Object.entries(observed).filter(([expectedName, actual]) => actual !== expectedName).map(([expectedName, actual]) => `${expectedName} \u2192 ${actual ?? "unknown"}`).join(", ") + ". Detection falls back to accessibility roles and component shape, which covers switches, modals and lists but is less precise. To restore names, keep `keep_fnames`/`keep_classnames` in your Metro minifierConfig \u2014 `withAppilots()` sets them for you."
|
|
5979
|
+
);
|
|
5980
|
+
}
|
|
5981
|
+
return _lastProbe;
|
|
5982
|
+
}
|
|
5983
|
+
|
|
5984
|
+
// src/platform/fabricGeometry.ts
|
|
5985
|
+
function readRect(instance) {
|
|
5986
|
+
const candidate = instance;
|
|
5987
|
+
const read = candidate?.getBoundingClientRect ?? candidate?.unstable_getBoundingClientRect;
|
|
5988
|
+
if (typeof read !== "function") return null;
|
|
5989
|
+
const rect = read.call(candidate);
|
|
5990
|
+
if (!rect) return null;
|
|
5991
|
+
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
5992
|
+
}
|
|
5993
|
+
function createFabricGeometryHost(dimensions) {
|
|
5994
|
+
const fabric = globalThis.nativeFabricUIManager;
|
|
5995
|
+
if (!fabric) return null;
|
|
5996
|
+
return {
|
|
5997
|
+
get window() {
|
|
5998
|
+
return dimensions.get("window");
|
|
5999
|
+
},
|
|
6000
|
+
rectOf: readRect
|
|
6001
|
+
};
|
|
6002
|
+
}
|
|
6003
|
+
var AppilotsErrorBoundary = class extends React2__default.default.Component {
|
|
6004
|
+
state = { degraded: false, escalate: null };
|
|
6005
|
+
/**
|
|
6006
|
+
* Instance field rather than state: `componentDidCatch` needs to know
|
|
6007
|
+
* whether THIS is the first failure, and by the time it runs
|
|
6008
|
+
* `getDerivedStateFromError` has already flipped `state.degraded`.
|
|
6009
|
+
*/
|
|
6010
|
+
hasDegradedOnce = false;
|
|
6011
|
+
static getDerivedStateFromError() {
|
|
6012
|
+
return { degraded: true };
|
|
6013
|
+
}
|
|
6014
|
+
componentDidCatch(error, info) {
|
|
6015
|
+
if (this.hasDegradedOnce) {
|
|
6016
|
+
this.setState({ escalate: error ?? new Error("Appilots: re-thrown host render error") });
|
|
6017
|
+
return;
|
|
6018
|
+
}
|
|
6019
|
+
this.hasDegradedOnce = true;
|
|
6020
|
+
console.error(
|
|
6021
|
+
`[Appilots] The Appilots ${this.props.surface} threw while rendering and has been disabled for the rest of this session. Your app keeps running \u2014 this is an Appilots bug, not a bug in your code. Please report it with the stack below: https://github.com/Axtern-Labs/Appilots/issues`,
|
|
6022
|
+
error
|
|
6023
|
+
);
|
|
6024
|
+
try {
|
|
6025
|
+
this.props.onError?.(error, info?.componentStack ?? null);
|
|
6026
|
+
} catch {
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
render() {
|
|
6030
|
+
if (this.state.escalate !== null) throw this.state.escalate;
|
|
6031
|
+
if (this.state.degraded) return this.props.fallback;
|
|
6032
|
+
return this.props.children;
|
|
6033
|
+
}
|
|
6034
|
+
};
|
|
6035
|
+
|
|
6036
|
+
// src/context/AppilotsProvider.tsx
|
|
6037
|
+
var AppilotsContext = React2.createContext(null);
|
|
6038
|
+
function createInertClient() {
|
|
6039
|
+
const unavailable = () => Promise.reject(new Error("Appilots is unavailable: the SDK degraded after a render error."));
|
|
6040
|
+
return new Proxy({}, {
|
|
6041
|
+
get: (_target, prop) => {
|
|
6042
|
+
if (prop === "then" || typeof prop === "symbol") return void 0;
|
|
6043
|
+
return unavailable;
|
|
6044
|
+
}
|
|
6045
|
+
});
|
|
6046
|
+
}
|
|
6047
|
+
function createDegradedContext(config) {
|
|
6048
|
+
return {
|
|
6049
|
+
config: config ?? { projectId: "" },
|
|
6050
|
+
client: createInertClient(),
|
|
6051
|
+
subscribe: () => () => {
|
|
6052
|
+
},
|
|
6053
|
+
emit: () => {
|
|
6054
|
+
},
|
|
6055
|
+
remotePersonalization: null,
|
|
6056
|
+
degraded: true
|
|
6057
|
+
};
|
|
6058
|
+
}
|
|
6059
|
+
function AppilotsProvider({ config, children, client }) {
|
|
6060
|
+
return /* @__PURE__ */ React2__default.default.createElement(
|
|
6061
|
+
AppilotsErrorBoundary,
|
|
6062
|
+
{
|
|
6063
|
+
surface: "provider",
|
|
6064
|
+
onError: reportProviderRenderFailure,
|
|
6065
|
+
fallback: /* @__PURE__ */ React2__default.default.createElement(AppilotsContext.Provider, { value: createDegradedContext(config ?? null) }, children)
|
|
6066
|
+
},
|
|
6067
|
+
/* @__PURE__ */ React2__default.default.createElement(AppilotsProviderInner, { config, client }, children)
|
|
6068
|
+
);
|
|
6069
|
+
}
|
|
6070
|
+
function reportProviderRenderFailure() {
|
|
6071
|
+
recordRenderFailure(readReactVersion());
|
|
6072
|
+
}
|
|
6073
|
+
function readReactVersion() {
|
|
6074
|
+
const version = React2__default.default.version;
|
|
6075
|
+
return typeof version === "string" ? version : null;
|
|
6076
|
+
}
|
|
6077
|
+
function AppilotsProviderInner({
|
|
6078
|
+
config: configProp,
|
|
6079
|
+
children,
|
|
6080
|
+
client: externalClient
|
|
6081
|
+
}) {
|
|
6082
|
+
appilotsDebugLog(
|
|
6083
|
+
`AppilotsProvider initializing \u2014 config source: ${configProp ? "prop" : "auto (globalThis.__APPILOTS_RC__)"}`
|
|
6084
|
+
);
|
|
6085
|
+
tryAutoConfig();
|
|
6086
|
+
const globalConfig = getGlobalConfig();
|
|
6087
|
+
appilotsDebugLog(`AppilotsProvider: configProp=${!!configProp}, globalConfig=${!!globalConfig}`);
|
|
6088
|
+
const config = configProp ?? (globalConfig ? {
|
|
6089
|
+
projectId: globalConfig.projectId,
|
|
6090
|
+
apiBaseUrl: globalConfig.apiBaseUrl,
|
|
6091
|
+
apiKey: globalConfig.apiKey,
|
|
6092
|
+
permissions: globalConfig.permissions,
|
|
6093
|
+
debug: globalConfig.debug,
|
|
6094
|
+
appVersion: globalConfig.appVersion,
|
|
6095
|
+
mcpVersion: globalConfig.mcpVersion,
|
|
6096
|
+
fetchPersonalization: globalConfig.fetchPersonalization,
|
|
6097
|
+
suppressNativeConfirm: globalConfig.suppressNativeConfirm
|
|
6098
|
+
} : null);
|
|
6099
|
+
if (!config?.projectId) {
|
|
6100
|
+
throw new Error(
|
|
6101
|
+
"AppilotsProvider: No config found. Add withAppilots() to your metro.config.js and create a .appilotsrc file, or pass a config prop directly."
|
|
6102
|
+
);
|
|
6103
|
+
}
|
|
6104
|
+
React2.useEffect(() => {
|
|
6105
|
+
setAppilotsDebugEnabled2(config.debug === true);
|
|
6106
|
+
}, [config.debug]);
|
|
6107
|
+
React2.useEffect(() => {
|
|
6108
|
+
probeNameMangling({ Switch: reactNative.Switch, Modal: reactNative.Modal, Text: reactNative.Text });
|
|
6109
|
+
}, []);
|
|
6110
|
+
React2.useEffect(() => {
|
|
6111
|
+
setGeometryHost(createFabricGeometryHost(reactNative.Dimensions));
|
|
6112
|
+
return () => setGeometryHost(null);
|
|
6113
|
+
}, []);
|
|
6114
|
+
const trackNativeDialogs = config.trackNativeDialogs !== false;
|
|
6115
|
+
React2.useEffect(() => {
|
|
6116
|
+
if (!trackNativeDialogs) return;
|
|
6117
|
+
installNativeDialogTracking(reactNative.Alert);
|
|
6118
|
+
return () => {
|
|
6119
|
+
uninstallNativeDialogTracking();
|
|
6120
|
+
};
|
|
6121
|
+
}, [trackNativeDialogs]);
|
|
6122
|
+
const listenersRef = React2.useRef(/* @__PURE__ */ new Set());
|
|
6123
|
+
const subscribe = React2.useCallback((handler) => {
|
|
6124
|
+
listenersRef.current.add(handler);
|
|
6125
|
+
return () => {
|
|
6126
|
+
listenersRef.current.delete(handler);
|
|
6127
|
+
};
|
|
6128
|
+
}, []);
|
|
6129
|
+
const emit = React2.useCallback((event) => {
|
|
6130
|
+
recordAppilotsEventTrace(event);
|
|
6131
|
+
listenersRef.current.forEach((handler) => handler(event));
|
|
6132
|
+
}, []);
|
|
6133
|
+
const handleIntrospectionUnavailable = React2.useCallback(
|
|
6134
|
+
({ reactVersion }) => {
|
|
6135
|
+
emit({
|
|
6136
|
+
type: "sdk:introspection:unavailable",
|
|
6137
|
+
timestamp: Date.now(),
|
|
6138
|
+
data: { reason: "sentinel-missing-internals", reactVersion }
|
|
6139
|
+
});
|
|
6140
|
+
},
|
|
6141
|
+
[emit]
|
|
6142
|
+
);
|
|
6143
|
+
const client = React2.useMemo(
|
|
6144
|
+
() => externalClient ?? new AppilotsClient({
|
|
6145
|
+
projectId: config.projectId,
|
|
6146
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
6147
|
+
apiKey: config.apiKey,
|
|
6148
|
+
debug: config.debug,
|
|
6149
|
+
appVersion: config.appVersion,
|
|
6150
|
+
mcpVersion: config.mcpVersion,
|
|
6151
|
+
// This package's published version, so the server knows which
|
|
6152
|
+
// SDK is actually in the field (issue #312).
|
|
6153
|
+
sdkVersion: SDK_VERSION2,
|
|
6154
|
+
user: config.user,
|
|
6155
|
+
// Only reports a FAILED reading; the happy path sends nothing.
|
|
6156
|
+
introspectionReporter: getIntrospectionDiagnostics
|
|
6157
|
+
}),
|
|
6158
|
+
[
|
|
6159
|
+
config.projectId,
|
|
6160
|
+
config.apiBaseUrl,
|
|
6161
|
+
config.apiKey,
|
|
6162
|
+
config.debug,
|
|
6163
|
+
config.appVersion,
|
|
6164
|
+
config.mcpVersion,
|
|
6165
|
+
config.user,
|
|
6166
|
+
externalClient
|
|
6167
|
+
]
|
|
6168
|
+
);
|
|
6169
|
+
const fetchPersonalizationEnabled = config.fetchPersonalization !== false;
|
|
6170
|
+
const [remotePersonalization, setRemotePersonalization] = React2.useState(
|
|
6171
|
+
null
|
|
6172
|
+
);
|
|
6173
|
+
React2.useEffect(() => {
|
|
6174
|
+
if (!fetchPersonalizationEnabled) return;
|
|
6175
|
+
let cancelled = false;
|
|
6176
|
+
client.getPersonalization().then((data) => {
|
|
6177
|
+
if (!cancelled) setRemotePersonalization(data);
|
|
6178
|
+
}).catch(() => {
|
|
6179
|
+
});
|
|
6180
|
+
return () => {
|
|
6181
|
+
cancelled = true;
|
|
6182
|
+
};
|
|
6183
|
+
}, [client, fetchPersonalizationEnabled]);
|
|
6184
|
+
const value = React2.useMemo(
|
|
6185
|
+
() => ({ config, client, subscribe, emit, remotePersonalization }),
|
|
6186
|
+
[config, client, subscribe, emit, remotePersonalization]
|
|
6187
|
+
);
|
|
6188
|
+
return /* @__PURE__ */ React2__default.default.createElement(AppilotsContext.Provider, { value }, /* @__PURE__ */ React2__default.default.createElement(AppilotsFiberRoot, { onUnavailable: handleIntrospectionUnavailable }, children));
|
|
6189
|
+
}
|
|
6190
|
+
function useAppilotsContext() {
|
|
6191
|
+
const context = React2.useContext(AppilotsContext);
|
|
6192
|
+
if (!context) {
|
|
6193
|
+
throw new Error("useAppilotsContext must be used within an <AppilotsProvider>");
|
|
6194
|
+
}
|
|
6195
|
+
return context;
|
|
6196
|
+
}
|
|
6197
|
+
|
|
4380
6198
|
// src/navigation/registerScreen.ts
|
|
4381
6199
|
var screenRegistry = /* @__PURE__ */ new Map();
|
|
4382
6200
|
function registerScreen(metadata) {
|
|
@@ -4394,17 +6212,22 @@ function clearScreenRegistry() {
|
|
|
4394
6212
|
|
|
4395
6213
|
exports.ActionQueueMachine = ActionQueueMachine;
|
|
4396
6214
|
exports.AppilotsClient = AppilotsClient;
|
|
6215
|
+
exports.AppilotsErrorBoundary = AppilotsErrorBoundary;
|
|
4397
6216
|
exports.AppilotsProvider = AppilotsProvider;
|
|
4398
6217
|
exports.AppilotsRegistryProvider = AppilotsRegistryProvider;
|
|
4399
6218
|
exports.ChatSessionMachine = ChatSessionMachine;
|
|
6219
|
+
exports.FORM_VALIDATION_TEXT_RE = FORM_VALIDATION_TEXT_RE;
|
|
6220
|
+
exports.FORM_WRITE_SUCCESS_TEXT_RE = FORM_WRITE_SUCCESS_TEXT_RE;
|
|
4400
6221
|
exports.OPTIONAL_STEP_AUTOMATION_HINT = OPTIONAL_STEP_AUTOMATION_HINT;
|
|
4401
6222
|
exports.RateLimitedError = RateLimitedError;
|
|
4402
|
-
exports.SDK_VERSION =
|
|
6223
|
+
exports.SDK_VERSION = SDK_VERSION2;
|
|
6224
|
+
exports.TOUCHABLE_NAMES = TOUCHABLE_NAMES;
|
|
4403
6225
|
exports._patchJsxRuntimes = _patchJsxRuntimes;
|
|
4404
6226
|
exports.actionPressTargetId = actionPressTargetId;
|
|
4405
6227
|
exports.appilotsDebugLog = appilotsDebugLog;
|
|
4406
6228
|
exports.appilotsDebugWarn = appilotsDebugWarn;
|
|
4407
6229
|
exports.attachElementIdsToChoiceGroups = attachElementIdsToChoiceGroups;
|
|
6230
|
+
exports.clampSnapshotToWireLimits = clampSnapshotToWireLimits;
|
|
4408
6231
|
exports.clearAppilotsDebugTraces = clearAppilotsDebugTraces;
|
|
4409
6232
|
exports.clearScreenRegistry = clearScreenRegistry;
|
|
4410
6233
|
exports.componentRegistry = componentRegistry;
|
|
@@ -4415,27 +6238,44 @@ exports.defaultDarkTheme = defaultDarkTheme;
|
|
|
4415
6238
|
exports.defaultLightTheme = defaultLightTheme;
|
|
4416
6239
|
exports.deriveInteractionElements = deriveInteractionElements;
|
|
4417
6240
|
exports.describeAction = describeAction;
|
|
6241
|
+
exports.describeValue = describeValue;
|
|
4418
6242
|
exports.elementRegistry = elementRegistry;
|
|
6243
|
+
exports.emptyDetectorCounts = emptyDetectorCounts;
|
|
4419
6244
|
exports.enableAppilotsAutoTracking = enableAppilotsAutoTracking;
|
|
6245
|
+
exports.findListItems = findListItems;
|
|
6246
|
+
exports.foldForCompare = foldForCompare;
|
|
6247
|
+
exports.geometryAvailability = geometryAvailability;
|
|
6248
|
+
exports.geometryStats = geometryStats;
|
|
4420
6249
|
exports.getActiveRouteNames = getActiveRouteNames;
|
|
4421
6250
|
exports.getAllScreens = getAllScreens;
|
|
4422
6251
|
exports.getAppilotsDebugTraces = getAppilotsDebugTraces;
|
|
6252
|
+
exports.getAutoTrackingState = getAutoTrackingState;
|
|
4423
6253
|
exports.getCurrentScreen = getCurrentScreen;
|
|
4424
6254
|
exports.getCurrentScreenSignature = getCurrentScreenSignature;
|
|
4425
6255
|
exports.getDefaultRegistry = getDefaultRegistry;
|
|
4426
6256
|
exports.getFiberRoot = getFiberRoot;
|
|
4427
6257
|
exports.getGlobalConfig = getGlobalConfig;
|
|
4428
6258
|
exports.getIntrospectionDiagnostics = getIntrospectionDiagnostics;
|
|
6259
|
+
exports.getLastAnsweredNativeDialog = getLastAnsweredNativeDialog;
|
|
6260
|
+
exports.getLastWalkDiagnostics = getLastWalkDiagnostics;
|
|
6261
|
+
exports.getNameManglingProbe = getNameManglingProbe;
|
|
4429
6262
|
exports.getNavigationRef = getNavigationRef;
|
|
4430
6263
|
exports.getNavigationStateSnapshot = getNavigationStateSnapshot;
|
|
6264
|
+
exports.getOpenNativeDialog = getOpenNativeDialog;
|
|
4431
6265
|
exports.getScreenMetadata = getScreenMetadata;
|
|
6266
|
+
exports.getTypeName = getTypeName;
|
|
4432
6267
|
exports.humanizeError = humanizeError;
|
|
4433
6268
|
exports.idLooselyMatches = idLooselyMatches;
|
|
4434
6269
|
exports.initAppilots = initAppilots;
|
|
6270
|
+
exports.isAlwaysListContainer = isAlwaysListContainer;
|
|
4435
6271
|
exports.isAutoTrackingEnabled = isAutoTrackingEnabled;
|
|
4436
6272
|
exports.isGenericSelectValue = isGenericSelectValue;
|
|
6273
|
+
exports.isHeuristicListContainer = isHeuristicListContainer;
|
|
4437
6274
|
exports.isListItemPressTarget = isListItemPressTarget;
|
|
6275
|
+
exports.isModalFiber = isModalFiber;
|
|
4438
6276
|
exports.isOptionalStepAutomationFailure = isOptionalStepAutomationFailure;
|
|
6277
|
+
exports.isReferenceable = isReferenceable;
|
|
6278
|
+
exports.isSwitchLike = isSwitchLike;
|
|
4439
6279
|
exports.listRegistry = listRegistry;
|
|
4440
6280
|
exports.looksDestructiveActionLabel = looksDestructiveActionLabel;
|
|
4441
6281
|
exports.matchScore = matchScore;
|
|
@@ -4445,15 +6285,21 @@ exports.parseStableElementId = parseStableElementId;
|
|
|
4445
6285
|
exports.probeLoadingState = probeLoadingState;
|
|
4446
6286
|
exports.recordAppilotsDebugTrace = recordAppilotsDebugTrace;
|
|
4447
6287
|
exports.recordIntrospectionFailure = recordIntrospectionFailure;
|
|
6288
|
+
exports.rectForNode = rectForNode;
|
|
4448
6289
|
exports.registerScreen = registerScreen;
|
|
4449
6290
|
exports.resolveBaseTheme = resolveBaseTheme;
|
|
4450
6291
|
exports.routesBelongToSameFeature = routesBelongToSameFeature;
|
|
4451
6292
|
exports.screenDepartedBaseline = screenDepartedBaseline;
|
|
4452
6293
|
exports.setCurrentScreen = setCurrentScreen;
|
|
4453
6294
|
exports.setNavigationRef = setNavigationRef;
|
|
6295
|
+
exports.shouldSkipSubtree = shouldSkipSubtree;
|
|
4454
6296
|
exports.snapshotShowsOpenCreateForm = snapshotShowsOpenCreateForm;
|
|
4455
6297
|
exports.subscribeAppilotsDebugTraces = subscribeAppilotsDebugTraces;
|
|
6298
|
+
exports.switchValue = switchValue;
|
|
6299
|
+
exports.toWireElement = toWireElement;
|
|
6300
|
+
exports.totalDetectorHits = totalDetectorHits;
|
|
4456
6301
|
exports.useAppilotsContext = useAppilotsContext;
|
|
4457
6302
|
exports.useResolvedRegistry = useResolvedRegistry;
|
|
4458
6303
|
exports.waitForLoadingSettle = waitForLoadingSettle;
|
|
4459
6304
|
exports.waitForScreenSettle = waitForScreenSettle;
|
|
6305
|
+
exports.walkFiber = walkFiber;
|