@appilots/sdk 0.8.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 +5 -4
- package/dist/.build-meta.json +5 -0
- package/dist/{chunk-H7BMQTXH.js → chunk-2GCWCPSD.js} +19 -16
- package/dist/{chunk-C2KVVJ5K.mjs → chunk-AHHDOUVX.mjs} +1968 -386
- package/dist/{chunk-ZSEACJKU.mjs → chunk-IHPLS5FE.mjs} +16 -13
- package/dist/{chunk-N7EAOLBP.js → chunk-VX7AL4SA.js} +956 -1134
- package/dist/{chunk-MHWVRZYF.mjs → chunk-XHR65V2W.mjs} +736 -919
- package/dist/{chunk-AYWMBMSN.js → chunk-YB77RYCC.js} +1992 -384
- 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-BdYY-dvG.d.mts → index-BPZ0j71S.d.mts} +254 -6
- package/dist/{index-BdYY-dvG.d.ts → index-BPZ0j71S.d.ts} +254 -6
- package/dist/index.d.mts +306 -21
- package/dist/index.d.ts +306 -21
- package/dist/index.js +337 -229
- package/dist/index.mjs +269 -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 +4 -2
- package/dist/registerScreen-D1oGm28T.d.mts +0 -159
- package/dist/registerScreen-D1oGm28T.d.ts +0 -159
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React2, { createContext, useEffect, useRef, useCallback, useMemo, useState, useContext } from 'react';
|
|
2
|
-
import { TextInput, Switch, TouchableOpacity, Pressable } from 'react-native';
|
|
2
|
+
import { TextInput, Switch, Text, Modal, Alert, Dimensions, TouchableOpacity, Pressable } from 'react-native';
|
|
3
3
|
|
|
4
4
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
5
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -8,6 +8,153 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
+
// ../shared/dist/chunk-UEMUBXFH.mjs
|
|
12
|
+
var DEFAULT_API_BASE_URL = "https://api.appilots.com";
|
|
13
|
+
var APPILOTS_API_PATH_PREFIX = "/api/v1";
|
|
14
|
+
function normalizeApiBaseUrl(configured) {
|
|
15
|
+
const raw = (configured ?? DEFAULT_API_BASE_URL).trim().replace(/\/+$/, "");
|
|
16
|
+
if (!raw) return `${DEFAULT_API_BASE_URL}${APPILOTS_API_PATH_PREFIX}`;
|
|
17
|
+
if (new RegExp(`${APPILOTS_API_PATH_PREFIX}(/|$)`).test(raw)) return raw;
|
|
18
|
+
return `${raw}${APPILOTS_API_PATH_PREFIX}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ../shared/dist/screen-text/index.mjs
|
|
22
|
+
function build(table, extra = []) {
|
|
23
|
+
return [...Object.values(table).flat(), ...extra].join("|");
|
|
24
|
+
}
|
|
25
|
+
var VALIDATION = {
|
|
26
|
+
pt: [
|
|
27
|
+
"inv[a\xE1]lid",
|
|
28
|
+
"obrigat[o\xF3]ri",
|
|
29
|
+
"j[a\xE1]\\s+existe",
|
|
30
|
+
"duplicad",
|
|
31
|
+
"muito\\s+curto",
|
|
32
|
+
"n[a\xE3]o\\s+(?:foi|pode)",
|
|
33
|
+
"deve\\s+ser",
|
|
34
|
+
"selecione",
|
|
35
|
+
"escolha",
|
|
36
|
+
"preencha",
|
|
37
|
+
"corrija"
|
|
38
|
+
],
|
|
39
|
+
en: [
|
|
40
|
+
"invalid",
|
|
41
|
+
"required",
|
|
42
|
+
"already\\s+exists",
|
|
43
|
+
"too\\s+short",
|
|
44
|
+
"must\\s+be",
|
|
45
|
+
"select\\s+(?:the|a|an)",
|
|
46
|
+
"please\\s+(?:enter|choose|select|fill)",
|
|
47
|
+
"can'?t\\s+be\\s+(?:empty|blank)",
|
|
48
|
+
"cannot\\s+be\\s+(?:empty|blank)"
|
|
49
|
+
],
|
|
50
|
+
es: [
|
|
51
|
+
"obligatori",
|
|
52
|
+
"ya\\s+existe",
|
|
53
|
+
"demasiado\\s+corto",
|
|
54
|
+
"debe\\s+ser",
|
|
55
|
+
"seleccione",
|
|
56
|
+
"elija",
|
|
57
|
+
"complet[ae]\\s+(?:el|los|este)",
|
|
58
|
+
"no\\s+puede\\s+(?:estar|quedar)\\s+vac"
|
|
59
|
+
],
|
|
60
|
+
fr: [
|
|
61
|
+
"invalide",
|
|
62
|
+
"obligatoire",
|
|
63
|
+
"existe\\s+d[e\xE9]j[a\xE0]",
|
|
64
|
+
"trop\\s+court",
|
|
65
|
+
"doit\\s+[\xEAe]tre",
|
|
66
|
+
"s[e\xE9]lectionnez",
|
|
67
|
+
"choisissez",
|
|
68
|
+
"veuillez\\s+(?:saisir|choisir|remplir|s[e\xE9]lectionner)",
|
|
69
|
+
"ne\\s+peut\\s+pas\\s+[\xEAe]tre\\s+vide"
|
|
70
|
+
]
|
|
71
|
+
};
|
|
72
|
+
var VALIDATION_SHARED = ["format[oa]?\\b", "\\bformat\\b"];
|
|
73
|
+
var WRITE_SUCCESS = {
|
|
74
|
+
pt: [
|
|
75
|
+
"cadastrad[oa]s?\\s+com\\s+sucesso",
|
|
76
|
+
"registrad[oa]s?\\s+com\\s+sucesso",
|
|
77
|
+
"salv[oa]s?\\s+com\\s+sucesso",
|
|
78
|
+
"criad[oa]s?\\s+com\\s+sucesso",
|
|
79
|
+
"atualizad[oa]s?\\s+com\\s+sucesso",
|
|
80
|
+
"foi\\s+cadastrad[oa]",
|
|
81
|
+
"foi\\s+registrad[oa]",
|
|
82
|
+
"foi\\s+salv[oa]",
|
|
83
|
+
"foi\\s+criad[oa]"
|
|
84
|
+
],
|
|
85
|
+
en: [
|
|
86
|
+
"successfully\\s+(?:registered|saved|created|updated|added)",
|
|
87
|
+
"(?:has\\s+been|was)\\s+(?:registered|saved|created|updated|added)"
|
|
88
|
+
],
|
|
89
|
+
es: [
|
|
90
|
+
"registrad[oa]s?\\s+(?:exitosamente|correctamente|con\\s+[e\xE9]xito)",
|
|
91
|
+
"guardad[oa]s?\\s+(?:correctamente|con\\s+[e\xE9]xito)",
|
|
92
|
+
"cread[oa]s?\\s+(?:correctamente|con\\s+[e\xE9]xito)",
|
|
93
|
+
"se\\s+ha\\s+(?:guardado|creado|registrado|actualizado)"
|
|
94
|
+
],
|
|
95
|
+
fr: [
|
|
96
|
+
"(?:enregistr|cr[e\xE9]|ajout|mis\\s+[a\xE0]\\s+jour)[e\xE9]?e?s?\\s+avec\\s+succ[e\xE8]s",
|
|
97
|
+
"a\\s+[e\xE9]t[e\xE9]\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]e?"
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
var COMPLETION_CLAIM = {
|
|
101
|
+
pt: [
|
|
102
|
+
"j[a\xE1]\\s+(?:cadastrei|registrei|salvei|criei)",
|
|
103
|
+
"conclu[i\xED]\\s+o\\s+cadastro",
|
|
104
|
+
"cadastro\\s+(?:foi\\s+)?(?:conclu[i\xED]do|realizado|feito)"
|
|
105
|
+
],
|
|
106
|
+
en: [
|
|
107
|
+
"i(?:'|\u2019)?ve\\s+(?:registered|saved|created|added)",
|
|
108
|
+
"i\\s+have\\s+(?:registered|saved|created|added)"
|
|
109
|
+
],
|
|
110
|
+
es: ["ya\\s+(?:lo\\s+|la\\s+)?(?:registr[e\xE9]|guard[e\xE9]|cre[e\xE9])"],
|
|
111
|
+
fr: ["j(?:e\\s+l(?:'|\u2019)ai|(?:'|\u2019)ai)\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]"]
|
|
112
|
+
};
|
|
113
|
+
var SUBMIT_CONTROL = {
|
|
114
|
+
pt: ["cadastrar", "registrar", "salvar", "enviar", "confirmar"],
|
|
115
|
+
en: ["submit", "register", "save", "confirm"],
|
|
116
|
+
es: ["guardar", "registrar", "enviar", "confirmar", "aceptar"],
|
|
117
|
+
fr: ["enregistrer", "soumettre", "envoyer", "confirmer", "valider"]
|
|
118
|
+
};
|
|
119
|
+
var SUBMIT_LABEL_EXTRA = {
|
|
120
|
+
pt: ["criar", "adicionar", "concluir", "finalizar"],
|
|
121
|
+
en: ["create", "add", "done", "finish", "continue"],
|
|
122
|
+
es: ["crear", "a[\xF1n]adir", "agregar", "continuar", "finalizar"],
|
|
123
|
+
fr: ["cr[e\xE9]er", "ajouter", "terminer", "continuer"]
|
|
124
|
+
};
|
|
125
|
+
var CREATE_EDIT_ROUTE = {
|
|
126
|
+
pt: ["cadastr", "criar", "novo", "nova", "registr", "edit"],
|
|
127
|
+
en: ["create", "edit", "form", "new", "add"],
|
|
128
|
+
es: ["crear", "nuevo", "nueva", "editar", "alta"],
|
|
129
|
+
fr: ["cr[e\xE9]er", "nouveau", "nouvelle", "modifier", "ajout"]
|
|
130
|
+
};
|
|
131
|
+
var WRITE_INTENT = {
|
|
132
|
+
pt: ["cadastr\\w*", "registr\\w*", "cria\\w*", "adiciona\\w*", "salv\\w*"],
|
|
133
|
+
en: [
|
|
134
|
+
"creat(?:e|es|ed|ing|ion)",
|
|
135
|
+
"save[sd]?",
|
|
136
|
+
"saving",
|
|
137
|
+
"add(?:s|ed|ing)?",
|
|
138
|
+
"submit\\w*",
|
|
139
|
+
"register\\w*"
|
|
140
|
+
],
|
|
141
|
+
es: ["crea\\w*", "guarda\\w*", "a[n\xF1]ad\\w*", "agrega\\w*", "registra\\w*"],
|
|
142
|
+
fr: ["cr[e\xE9][e\xE9]\\w*", "enregistr\\w*", "ajout\\w*", "sauvegard\\w*", "soumet\\w*"]
|
|
143
|
+
};
|
|
144
|
+
var FORM_VALIDATION_TEXT_RE = new RegExp(`(${build(VALIDATION, VALIDATION_SHARED)})`, "i");
|
|
145
|
+
var FORM_WRITE_SUCCESS_TEXT_RE = new RegExp(`\\b(${build(WRITE_SUCCESS)})`, "i");
|
|
146
|
+
new RegExp(`\\b(${build(WRITE_INTENT)})\\b`, "i");
|
|
147
|
+
new RegExp(
|
|
148
|
+
`\\b(${build(WRITE_SUCCESS)}|${build(COMPLETION_CLAIM)})`,
|
|
149
|
+
"i"
|
|
150
|
+
);
|
|
151
|
+
var SUBMIT_CONTROL_RE = new RegExp(`(${build(SUBMIT_CONTROL)})`, "i");
|
|
152
|
+
new RegExp(
|
|
153
|
+
`(${build(SUBMIT_CONTROL)}|${build(SUBMIT_LABEL_EXTRA)})`,
|
|
154
|
+
"i"
|
|
155
|
+
);
|
|
156
|
+
var CREATE_EDIT_ROUTE_RE = new RegExp(`(${build(CREATE_EDIT_ROUTE)})`, "i");
|
|
157
|
+
|
|
11
158
|
// ../shared/dist/chunk-43YQ52Q7.mjs
|
|
12
159
|
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;
|
|
13
160
|
function isSensitiveFieldName(id, label, placeholder, type) {
|
|
@@ -22,6 +169,39 @@ function isSensitiveFieldName(id, label, placeholder, type) {
|
|
|
22
169
|
|
|
23
170
|
// ../client-core/dist/index.mjs
|
|
24
171
|
var SDK_VERSION = "0.1.0";
|
|
172
|
+
var _enabled;
|
|
173
|
+
var _resolve;
|
|
174
|
+
function setAppilotsDebugEnabled(enabled) {
|
|
175
|
+
_enabled = enabled;
|
|
176
|
+
}
|
|
177
|
+
function setAppilotsDebugResolver(resolve) {
|
|
178
|
+
_resolve = resolve;
|
|
179
|
+
}
|
|
180
|
+
function isAppilotsDebugEnabled() {
|
|
181
|
+
if (_enabled !== void 0) return _enabled;
|
|
182
|
+
try {
|
|
183
|
+
return _resolve?.() === true;
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function appilotsDebugLog(...args) {
|
|
189
|
+
if (!isAppilotsDebugEnabled()) return;
|
|
190
|
+
console.log("[Appilots]", ...args);
|
|
191
|
+
}
|
|
192
|
+
function appilotsDebugWarn(...args) {
|
|
193
|
+
if (!isAppilotsDebugEnabled()) return;
|
|
194
|
+
console.log("[Appilots]", ...args);
|
|
195
|
+
}
|
|
196
|
+
function describeValue(value) {
|
|
197
|
+
if (value === void 0) return "undefined";
|
|
198
|
+
if (value === null) return "null";
|
|
199
|
+
if (typeof value === "string") return `string(len=${value.length})`;
|
|
200
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
201
|
+
if (Array.isArray(value)) return `array(len=${value.length})`;
|
|
202
|
+
if (typeof value === "object") return `object(keys=${Object.keys(value).length})`;
|
|
203
|
+
return typeof value;
|
|
204
|
+
}
|
|
25
205
|
var StreamTransportError = class extends Error {
|
|
26
206
|
constructor(message) {
|
|
27
207
|
super(message);
|
|
@@ -52,6 +232,13 @@ function parseRetryAfter(...candidates) {
|
|
|
52
232
|
return 60;
|
|
53
233
|
}
|
|
54
234
|
var CONTINUATION_TIMEOUT_MS = 15e4;
|
|
235
|
+
function safeJson(text) {
|
|
236
|
+
try {
|
|
237
|
+
return JSON.parse(text || "{}");
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
55
242
|
var AppilotsClient = class {
|
|
56
243
|
baseUrl;
|
|
57
244
|
projectId;
|
|
@@ -66,7 +253,7 @@ var AppilotsClient = class {
|
|
|
66
253
|
identified = false;
|
|
67
254
|
constructor(options) {
|
|
68
255
|
this.projectId = options.projectId;
|
|
69
|
-
this.baseUrl = (options.apiBaseUrl
|
|
256
|
+
this.baseUrl = normalizeApiBaseUrl(options.apiBaseUrl);
|
|
70
257
|
this.timeout = options.timeout ?? 6e4;
|
|
71
258
|
this.debug = options.debug ?? false;
|
|
72
259
|
this.mcpVersion = options.mcpVersion;
|
|
@@ -85,8 +272,25 @@ var AppilotsClient = class {
|
|
|
85
272
|
...options.headers
|
|
86
273
|
};
|
|
87
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* A 404 on an `/agent/*` path is almost never a missing record — those
|
|
277
|
+
* routes are static. It means the request reached SOMETHING that is not
|
|
278
|
+
* this API, or reached it at the wrong mount point, and the server's own
|
|
279
|
+
* message ("Route not found") tells the integrator nothing about which.
|
|
280
|
+
*
|
|
281
|
+
* The symptom is unmistakable once you have seen it: every message
|
|
282
|
+
* fails, including a plain "hello" that needs no tool at all, because
|
|
283
|
+
* nothing ever reaches the agent. Left as-is it reads like the agent is
|
|
284
|
+
* broken.
|
|
285
|
+
*/
|
|
286
|
+
explainIfMisroutedBaseUrl(status, json, fallback) {
|
|
287
|
+
if (status !== 404) return fallback;
|
|
288
|
+
const code = json?.error?.code;
|
|
289
|
+
if (code !== "NOT_FOUND") return fallback;
|
|
290
|
+
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}.`;
|
|
291
|
+
}
|
|
88
292
|
log(...args) {
|
|
89
|
-
if (this.debug)
|
|
293
|
+
if (this.debug) appilotsDebugLog("[AppilotsClient]", ...args);
|
|
90
294
|
}
|
|
91
295
|
describeNonJsonResponse(url, response, body) {
|
|
92
296
|
const contentType = response.headers?.get?.("content-type") ?? "unknown content-type";
|
|
@@ -120,13 +324,10 @@ var AppilotsClient = class {
|
|
|
120
324
|
if (response.status === 429) {
|
|
121
325
|
throw new RateLimitedError(
|
|
122
326
|
errorMsg,
|
|
123
|
-
parseRetryAfter(
|
|
124
|
-
json?.error?.retryAfterSeconds,
|
|
125
|
-
response.headers?.get?.("retry-after")
|
|
126
|
-
)
|
|
327
|
+
parseRetryAfter(json?.error?.retryAfterSeconds, response.headers?.get?.("retry-after"))
|
|
127
328
|
);
|
|
128
329
|
}
|
|
129
|
-
throw new Error(errorMsg);
|
|
330
|
+
throw new Error(this.explainIfMisroutedBaseUrl(response.status, json, errorMsg));
|
|
130
331
|
}
|
|
131
332
|
return json.data ?? json;
|
|
132
333
|
} finally {
|
|
@@ -241,7 +442,11 @@ var AppilotsClient = class {
|
|
|
241
442
|
content: data.message.summary,
|
|
242
443
|
timestamp: Date.now()
|
|
243
444
|
} : void 0;
|
|
244
|
-
this.log("Response received:", {
|
|
445
|
+
this.log("Response received:", {
|
|
446
|
+
messageId: message.id,
|
|
447
|
+
actions: actions?.length ?? 0,
|
|
448
|
+
hasSummary: !!summaryMessage
|
|
449
|
+
});
|
|
245
450
|
return {
|
|
246
451
|
sessionId: this.sessionId,
|
|
247
452
|
message,
|
|
@@ -382,17 +587,17 @@ var AppilotsClient = class {
|
|
|
382
587
|
fail(
|
|
383
588
|
new RateLimitedError(
|
|
384
589
|
message2,
|
|
385
|
-
parseRetryAfter(
|
|
386
|
-
retryAfterSeconds,
|
|
387
|
-
xhr.getResponseHeader?.("retry-after")
|
|
388
|
-
)
|
|
590
|
+
parseRetryAfter(retryAfterSeconds, xhr.getResponseHeader?.("retry-after"))
|
|
389
591
|
)
|
|
390
592
|
);
|
|
391
593
|
return;
|
|
392
594
|
}
|
|
393
|
-
|
|
394
|
-
|
|
595
|
+
const explained = this.explainIfMisroutedBaseUrl(
|
|
596
|
+
xhr.status,
|
|
597
|
+
safeJson(xhr.responseText),
|
|
598
|
+
message2
|
|
395
599
|
);
|
|
600
|
+
fail(sawAnyEvent ? new Error(explained) : new StreamTransportError(explained));
|
|
396
601
|
return;
|
|
397
602
|
}
|
|
398
603
|
if (!doneEvent) {
|
|
@@ -574,7 +779,9 @@ var AppilotsClient = class {
|
|
|
574
779
|
};
|
|
575
780
|
case "navigate":
|
|
576
781
|
return {
|
|
577
|
-
|
|
782
|
+
// No `?? ''` here: an empty string would make a goBack look
|
|
783
|
+
// like a navigate to a nameless screen further down (#398).
|
|
784
|
+
screenName: typeof payload.screenName === "string" ? payload.screenName : void 0,
|
|
578
785
|
params: payload.params,
|
|
579
786
|
navigationAction: payload.navigationAction ?? "navigate",
|
|
580
787
|
path: Array.isArray(payload.path) ? payload.path : void 0
|
|
@@ -1107,8 +1314,15 @@ function subscribeAppilotsDebugTraces(listener) {
|
|
|
1107
1314
|
};
|
|
1108
1315
|
}
|
|
1109
1316
|
var INTERNAL_MARKER_RE = /^\[(?:Permission policy notice|Recovery exhausted|User rejected the proposed action)[^\]]*\]:\s*/i;
|
|
1110
|
-
var
|
|
1111
|
-
var
|
|
1317
|
+
var INTERNAL_ID_SRC = 'el:[^\\s".]+|list-\\d+-item-\\d+';
|
|
1318
|
+
var TECHNICAL_FIELD_SRC = "\\b(?:targetId|componentId|screenName|actionId|fieldId|elementId|testID|test-id|accessibilityId|accessibility-id)\\s*[:=_-]\\s*\\S+";
|
|
1319
|
+
var TECHNICAL_SELECTOR_RE = new RegExp(`${INTERNAL_ID_SRC}|${TECHNICAL_FIELD_SRC}`, "i");
|
|
1320
|
+
var TOOL_NAME_SRC = "(?:form_fill|ui_interaction|scroll_list)\\b";
|
|
1321
|
+
var TOOL_VERB_WITH_ID_SRC = `(?:press|click|tap|navigate)\\w*\\b[^\\n]{0,40}?(?:${INTERNAL_ID_SRC})`;
|
|
1322
|
+
var TECHNICAL_TRACE_RE = new RegExp(
|
|
1323
|
+
`(^|\\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-]*:))`,
|
|
1324
|
+
"i"
|
|
1325
|
+
);
|
|
1112
1326
|
function sanitizeVisibleAssistantContent(content) {
|
|
1113
1327
|
return content.split("\n").map((line) => line.replace(INTERNAL_MARKER_RE, "").trimEnd()).filter((line) => !INTERNAL_MARKER_RE.test(line)).join("\n").trim();
|
|
1114
1328
|
}
|
|
@@ -1116,7 +1330,7 @@ function humanizeAssistantFallback(content) {
|
|
|
1116
1330
|
let text = sanitizeVisibleAssistantContent(content).replace(/\blist-\d+-item-\d+\b/gi, "o item da lista").replace(/\bel:[^\s".]+/gi, "o elemento").replace(
|
|
1117
1331
|
/"\s*(?:press|click|tap|navigate|form_fill|ui_interaction|scroll_list)\s+on\s+[^"]+"/gi,
|
|
1118
1332
|
'"a a\xE7\xE3o solicitada"'
|
|
1119
|
-
).
|
|
1333
|
+
).trim();
|
|
1120
1334
|
if (text.length < 16) return null;
|
|
1121
1335
|
const probe = {
|
|
1122
1336
|
role: "assistant",
|
|
@@ -1131,9 +1345,8 @@ function isInternalTraceMessage(message) {
|
|
|
1131
1345
|
if (!content) return true;
|
|
1132
1346
|
return TECHNICAL_TRACE_RE.test(content) || TECHNICAL_SELECTOR_RE.test(content);
|
|
1133
1347
|
}
|
|
1134
|
-
var CREATE_FORM_ROUTE_RE =
|
|
1135
|
-
var SUBMIT_BUTTON_RE =
|
|
1136
|
-
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;
|
|
1348
|
+
var CREATE_FORM_ROUTE_RE = CREATE_EDIT_ROUTE_RE;
|
|
1349
|
+
var SUBMIT_BUTTON_RE = SUBMIT_CONTROL_RE;
|
|
1137
1350
|
function normalize2(value) {
|
|
1138
1351
|
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
1139
1352
|
}
|
|
@@ -1151,7 +1364,9 @@ function snapshotHasVisibleSubmitButton(snap) {
|
|
|
1151
1364
|
);
|
|
1152
1365
|
}
|
|
1153
1366
|
function snapshotHasVisibleValidation(snap) {
|
|
1154
|
-
return (snap.texts ?? []).some(
|
|
1367
|
+
return (snap.texts ?? []).some(
|
|
1368
|
+
(text) => typeof text === "string" && FORM_VALIDATION_TEXT_RE.test(text)
|
|
1369
|
+
);
|
|
1155
1370
|
}
|
|
1156
1371
|
function snapshotShowsOpenCreateForm(snap, activePath = []) {
|
|
1157
1372
|
if (!snap) return false;
|
|
@@ -1568,9 +1783,10 @@ var ChatSessionMachine = class {
|
|
|
1568
1783
|
};
|
|
1569
1784
|
}
|
|
1570
1785
|
const hasToolWithoutEffect = toolWithoutEffectIds.has(actionId);
|
|
1786
|
+
const observedNoEffect = hasToolWithoutEffect || r.effect === "none";
|
|
1571
1787
|
let recoveryAttempt = 0;
|
|
1572
1788
|
let recoveryExhausted = false;
|
|
1573
|
-
if (!r.success ||
|
|
1789
|
+
if (!r.skipped && (!r.success || observedNoEffect)) {
|
|
1574
1790
|
const previous = this.recoveryCountByFingerprint.get(r.fingerprint) ?? 0;
|
|
1575
1791
|
const next = previous + 1;
|
|
1576
1792
|
this.recoveryCountByFingerprint.set(r.fingerprint, next);
|
|
@@ -1587,16 +1803,18 @@ var ChatSessionMachine = class {
|
|
|
1587
1803
|
recoveryAttempt,
|
|
1588
1804
|
recoveryExhausted,
|
|
1589
1805
|
toolWithoutEffect: hasToolWithoutEffect,
|
|
1590
|
-
effect:
|
|
1806
|
+
effect: observedNoEffect ? "none" : r.effect,
|
|
1591
1807
|
userVisibleStatusKey: r.userVisibleStatusKey
|
|
1592
1808
|
};
|
|
1593
1809
|
});
|
|
1594
1810
|
const exhausted = results.filter((r) => r.recoveryExhausted);
|
|
1595
1811
|
this.setState({
|
|
1596
|
-
loadingStatusKey: results.some(
|
|
1812
|
+
loadingStatusKey: results.some(
|
|
1813
|
+
(r) => !r.success || r.toolWithoutEffect || r.effect === "none"
|
|
1814
|
+
) ? "statusAdjusting" : "statusAnalyzing"
|
|
1597
1815
|
});
|
|
1598
|
-
|
|
1599
|
-
`
|
|
1816
|
+
appilotsDebugLog(
|
|
1817
|
+
`ChatSessionMachine: running continuation hop=${turn.hop} (${results.filter((r) => r.success).length}/${results.length} succeeded` + (exhausted.length > 0 ? `, ${exhausted.length} recovery-exhausted` : "") + ")"
|
|
1600
1818
|
);
|
|
1601
1819
|
const response = await this.deps.client.continueAgent(results, context, turn.hop);
|
|
1602
1820
|
this.addAssistantMessage(response.message);
|
|
@@ -1669,6 +1887,7 @@ var ChatSessionMachine = class {
|
|
|
1669
1887
|
const diagnose = event.data?.diagnose;
|
|
1670
1888
|
const effect = event.data?.effect;
|
|
1671
1889
|
const userVisibleStatusKey = event.data?.userVisibleStatusKey;
|
|
1890
|
+
const skipped = event.data?.skipped === true;
|
|
1672
1891
|
const existing = turn.results.get(actionId);
|
|
1673
1892
|
if (existing) {
|
|
1674
1893
|
existing.success = success;
|
|
@@ -1676,6 +1895,7 @@ var ChatSessionMachine = class {
|
|
|
1676
1895
|
existing.diagnose = diagnose;
|
|
1677
1896
|
existing.effect = effect;
|
|
1678
1897
|
existing.userVisibleStatusKey = userVisibleStatusKey;
|
|
1898
|
+
existing.skipped = skipped;
|
|
1679
1899
|
}
|
|
1680
1900
|
turn.reportedIds.add(actionId);
|
|
1681
1901
|
if (turn.reportedIds.size >= turn.actionIds.size) {
|
|
@@ -1712,8 +1932,8 @@ var ChatSessionMachine = class {
|
|
|
1712
1932
|
this.abortController = abortController;
|
|
1713
1933
|
try {
|
|
1714
1934
|
const context = this.deps.adapter.buildContext();
|
|
1715
|
-
|
|
1716
|
-
`
|
|
1935
|
+
appilotsDebugLog(
|
|
1936
|
+
`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}}`
|
|
1717
1937
|
);
|
|
1718
1938
|
let response;
|
|
1719
1939
|
if (streamingEnabled) {
|
|
@@ -1741,9 +1961,7 @@ var ChatSessionMachine = class {
|
|
|
1741
1961
|
});
|
|
1742
1962
|
} catch (err) {
|
|
1743
1963
|
if (err instanceof StreamTransportError) {
|
|
1744
|
-
|
|
1745
|
-
`[Appilots] streaming unavailable, falling back to JSON: ${err.message}`
|
|
1746
|
-
);
|
|
1964
|
+
appilotsDebugWarn(`streaming unavailable, falling back to JSON: ${err.message}`);
|
|
1747
1965
|
response = await this.deps.client.sendMessage(content.trim(), context);
|
|
1748
1966
|
} else {
|
|
1749
1967
|
throw err;
|
|
@@ -1752,8 +1970,8 @@ var ChatSessionMachine = class {
|
|
|
1752
1970
|
} else {
|
|
1753
1971
|
response = await this.deps.client.sendMessage(content.trim(), context);
|
|
1754
1972
|
}
|
|
1755
|
-
|
|
1756
|
-
`
|
|
1973
|
+
appilotsDebugLog(
|
|
1974
|
+
`ChatSessionMachine sendMessage: response received \u2014 actions=${response.actions?.length ?? 0}, hasSummary=${!!response.summaryMessage}`
|
|
1757
1975
|
);
|
|
1758
1976
|
removeStreamingPlaceholder();
|
|
1759
1977
|
this.addAssistantMessage(response.message);
|
|
@@ -1843,7 +2061,11 @@ function isStructuredRisky(value) {
|
|
|
1843
2061
|
}
|
|
1844
2062
|
function actionMetadataRequiresConfirmation(meta) {
|
|
1845
2063
|
if (meta.requiresConfirmation === true || meta.destructive === true || meta.effect === "destructive" || meta.riskLevel === "high" || meta.risk === "destructive") {
|
|
1846
|
-
return {
|
|
2064
|
+
return {
|
|
2065
|
+
required: true,
|
|
2066
|
+
label: typeof meta.label === "string" ? meta.label : void 0,
|
|
2067
|
+
source: "screen-metadata"
|
|
2068
|
+
};
|
|
1847
2069
|
}
|
|
1848
2070
|
return { required: false };
|
|
1849
2071
|
}
|
|
@@ -1884,13 +2106,9 @@ function toDestructiveConfirmPayload(confirm, policy, fallbackLabel) {
|
|
|
1884
2106
|
const labelText = policy.label ?? existingDescription ?? fallbackLabel ?? "esta a\xE7\xE3o";
|
|
1885
2107
|
return {
|
|
1886
2108
|
...payload,
|
|
1887
|
-
title: "A\xE7\xE3o destrutiva",
|
|
1888
|
-
message: `Tem certeza que deseja "${labelText}"?`,
|
|
1889
2109
|
actionDescription: labelText,
|
|
1890
2110
|
severity: "danger",
|
|
1891
|
-
destructive: true
|
|
1892
|
-
confirmLabel: typeof payload.confirmLabel === "string" && payload.confirmLabel ? payload.confirmLabel : "Sim, executar",
|
|
1893
|
-
cancelLabel: typeof payload.cancelLabel === "string" && payload.cancelLabel ? payload.cancelLabel : "Cancelar"
|
|
2111
|
+
destructive: true
|
|
1894
2112
|
};
|
|
1895
2113
|
}
|
|
1896
2114
|
function confirmNeedsDestructiveUpgrade(confirm) {
|
|
@@ -1956,8 +2174,8 @@ var ActionQueueMachine = class {
|
|
|
1956
2174
|
if (event.type !== "agent:action:start") return;
|
|
1957
2175
|
const action = event.data?.action;
|
|
1958
2176
|
if (!action?.id) return;
|
|
1959
|
-
|
|
1960
|
-
`
|
|
2177
|
+
appilotsDebugLog(
|
|
2178
|
+
`ActionQueueMachine: received agent:action:start event, action id="${action.id}", type="${action.type}"`
|
|
1961
2179
|
);
|
|
1962
2180
|
if (this.state.actions.some((a) => a.id === action.id)) return;
|
|
1963
2181
|
this.setActions([...this.state.actions, action]);
|
|
@@ -2058,8 +2276,8 @@ var ActionQueueMachine = class {
|
|
|
2058
2276
|
if (!policy.required) return false;
|
|
2059
2277
|
const confirmId = `confirm-local-${target.id}`;
|
|
2060
2278
|
if (this.state.actions.some((a) => a.id === confirmId)) return true;
|
|
2061
|
-
|
|
2062
|
-
`
|
|
2279
|
+
appilotsDebugLog(
|
|
2280
|
+
`ActionQueueMachine: action "${componentId || target.id}" requires confirmation (${policy.source ?? "local-policy"}) \u2014 synthesising local confirm (id="${confirmId}")`
|
|
2063
2281
|
);
|
|
2064
2282
|
const synth = {
|
|
2065
2283
|
id: confirmId,
|
|
@@ -2105,13 +2323,13 @@ var ActionQueueMachine = class {
|
|
|
2105
2323
|
const pending = this.state.actions.filter(
|
|
2106
2324
|
(a) => a.status === "pending" && !this.processed.has(a.id)
|
|
2107
2325
|
);
|
|
2108
|
-
|
|
2109
|
-
`
|
|
2326
|
+
appilotsDebugLog(
|
|
2327
|
+
`ActionQueueMachine: autoExecute check \u2014 ${pending.length} pending action(s) to process`
|
|
2110
2328
|
);
|
|
2111
2329
|
let lastWasNavigate = false;
|
|
2112
2330
|
let preNavScreen = null;
|
|
2113
2331
|
let preNavSignature = null;
|
|
2114
|
-
for (const action of pending) {
|
|
2332
|
+
for (const [index, action] of pending.entries()) {
|
|
2115
2333
|
if (action.type === "confirm") {
|
|
2116
2334
|
continue;
|
|
2117
2335
|
}
|
|
@@ -2121,14 +2339,14 @@ var ActionQueueMachine = class {
|
|
|
2121
2339
|
const gate = findGatingConfirm(action, this.state.actions);
|
|
2122
2340
|
if (gate) {
|
|
2123
2341
|
if (gate.status === "pending" || gate.status === "executing") {
|
|
2124
|
-
|
|
2125
|
-
`
|
|
2342
|
+
appilotsDebugLog(
|
|
2343
|
+
`ActionQueueMachine: action id="${action.id}" is gated by confirm id="${gate.id}" (status=${gate.status}); waiting for user decision`
|
|
2126
2344
|
);
|
|
2127
2345
|
continue;
|
|
2128
2346
|
}
|
|
2129
2347
|
if (gate.status === "failed") {
|
|
2130
|
-
|
|
2131
|
-
`
|
|
2348
|
+
appilotsDebugLog(
|
|
2349
|
+
`ActionQueueMachine: confirm id="${gate.id}" was rejected; skipping gated action id="${action.id}"`
|
|
2132
2350
|
);
|
|
2133
2351
|
this.processed.add(action.id);
|
|
2134
2352
|
this.updateActions(
|
|
@@ -2160,12 +2378,12 @@ var ActionQueueMachine = class {
|
|
|
2160
2378
|
fromSignature: preNavSignature,
|
|
2161
2379
|
maxMs: 3500
|
|
2162
2380
|
});
|
|
2163
|
-
|
|
2164
|
-
`
|
|
2381
|
+
appilotsDebugLog(
|
|
2382
|
+
`ActionQueueMachine: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
|
|
2165
2383
|
);
|
|
2166
2384
|
}
|
|
2167
|
-
|
|
2168
|
-
`
|
|
2385
|
+
appilotsDebugLog(
|
|
2386
|
+
`ActionQueueMachine: auto-executing action id="${action.id}" type="${action.type}"`
|
|
2169
2387
|
);
|
|
2170
2388
|
this.processed.add(action.id);
|
|
2171
2389
|
if (action.type === "navigate") {
|
|
@@ -2174,20 +2392,67 @@ var ActionQueueMachine = class {
|
|
|
2174
2392
|
}
|
|
2175
2393
|
const succeeded = await this.approveAction(action.id);
|
|
2176
2394
|
if (!succeeded) {
|
|
2395
|
+
const failed = this.state.actions.find((candidate) => candidate.id === action.id);
|
|
2177
2396
|
this.warn(
|
|
2178
2397
|
`ActionQueueMachine: stopping current action batch after failed action id="${action.id}"`
|
|
2179
2398
|
);
|
|
2399
|
+
await this.skipRemainingBatchActions(pending.slice(index + 1), action, failed?.error);
|
|
2180
2400
|
break;
|
|
2181
2401
|
}
|
|
2182
2402
|
lastWasNavigate = action.type === "navigate";
|
|
2183
2403
|
}
|
|
2184
2404
|
}
|
|
2405
|
+
/**
|
|
2406
|
+
* A turn is an all-terminal contract: ChatSessionMachine cannot ask the
|
|
2407
|
+
* server for the next step until every action in the current batch has
|
|
2408
|
+
* reported a completion or an error. When an early action fails we must
|
|
2409
|
+
* not execute dependent actions, but leaving them `pending` deadlocks the
|
|
2410
|
+
* whole turn. Mark the untouched suffix as failed/skipped and emit the
|
|
2411
|
+
* same terminal event a real executor would have emitted.
|
|
2412
|
+
*/
|
|
2413
|
+
async skipRemainingBatchActions(remaining, failedAction, failureReason) {
|
|
2414
|
+
const skipped = remaining.filter((candidate) => {
|
|
2415
|
+
const current = this.state.actions.find((action) => action.id === candidate.id);
|
|
2416
|
+
if (current?.status !== "pending" || this.processed.has(candidate.id)) return false;
|
|
2417
|
+
if (candidate.type === "confirm") return false;
|
|
2418
|
+
const gate = findGatingConfirm(candidate, this.state.actions);
|
|
2419
|
+
if (gate && (gate.status === "pending" || gate.status === "executing")) return false;
|
|
2420
|
+
return true;
|
|
2421
|
+
});
|
|
2422
|
+
if (skipped.length === 0) return;
|
|
2423
|
+
const reason = `Skipped because previous action "${failedAction.id}" failed` + (failureReason ? `: ${failureReason}` : "");
|
|
2424
|
+
const skippedIds = new Set(skipped.map((action) => action.id));
|
|
2425
|
+
for (const action of skipped) this.processed.add(action.id);
|
|
2426
|
+
this.updateActions(
|
|
2427
|
+
(prev) => prev.map(
|
|
2428
|
+
(action) => skippedIds.has(action.id) ? { ...action, status: "failed", error: reason } : action
|
|
2429
|
+
)
|
|
2430
|
+
);
|
|
2431
|
+
for (const action of skipped) {
|
|
2432
|
+
try {
|
|
2433
|
+
await this.deps.client.completeAction(action.id, false, reason);
|
|
2434
|
+
} catch {
|
|
2435
|
+
}
|
|
2436
|
+
this.deps.emit({
|
|
2437
|
+
type: "agent:action:error",
|
|
2438
|
+
timestamp: Date.now(),
|
|
2439
|
+
data: {
|
|
2440
|
+
actionId: action.id,
|
|
2441
|
+
actionType: action.type,
|
|
2442
|
+
success: false,
|
|
2443
|
+
error: reason,
|
|
2444
|
+
skipped: true,
|
|
2445
|
+
blockedByActionId: failedAction.id
|
|
2446
|
+
}
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2185
2450
|
// ── Approve & execute an action ─────────────────────────────
|
|
2186
2451
|
async approveAction(actionId) {
|
|
2187
2452
|
const action = this.state.actions.find((a) => a.id === actionId);
|
|
2188
2453
|
if (!action) return false;
|
|
2189
|
-
|
|
2190
|
-
`
|
|
2454
|
+
appilotsDebugLog(
|
|
2455
|
+
`ActionQueueMachine: action status transition: pending \u2192 executing (id="${actionId}", type="${action.type}")`
|
|
2191
2456
|
);
|
|
2192
2457
|
this.updateActions(
|
|
2193
2458
|
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "executing" } : a)
|
|
@@ -2199,8 +2464,8 @@ var ActionQueueMachine = class {
|
|
|
2199
2464
|
{ confirmedDestructive }
|
|
2200
2465
|
);
|
|
2201
2466
|
if (result.success) {
|
|
2202
|
-
|
|
2203
|
-
`
|
|
2467
|
+
appilotsDebugLog(
|
|
2468
|
+
`ActionQueueMachine: action status transition: executing \u2192 completed (id="${actionId}", type="${action.type}")`
|
|
2204
2469
|
);
|
|
2205
2470
|
this.updateActions(
|
|
2206
2471
|
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "completed" } : a)
|
|
@@ -2279,9 +2544,7 @@ var ActionQueueMachine = class {
|
|
|
2279
2544
|
return this.state.actions.filter((a) => a.status === "executing");
|
|
2280
2545
|
}
|
|
2281
2546
|
get completedActions() {
|
|
2282
|
-
return this.state.actions.filter(
|
|
2283
|
-
(a) => a.status === "completed" || a.status === "failed"
|
|
2284
|
-
);
|
|
2547
|
+
return this.state.actions.filter((a) => a.status === "completed" || a.status === "failed");
|
|
2285
2548
|
}
|
|
2286
2549
|
};
|
|
2287
2550
|
function normalize3(s) {
|
|
@@ -2310,13 +2573,11 @@ function normalizeId(value) {
|
|
|
2310
2573
|
function isOptionalCosmeticTargetId(targetId) {
|
|
2311
2574
|
if (typeof targetId !== "string" || !targetId.trim()) return false;
|
|
2312
2575
|
const id = normalizeId(targetId);
|
|
2313
|
-
return /
|
|
2576
|
+
return /sheetaction|sheet-action|photo-sheet|actionsheet|action-sheet/.test(id);
|
|
2314
2577
|
}
|
|
2315
2578
|
function routeLooksLikeFormContext(route) {
|
|
2316
2579
|
const haystack = normalizeId(route ?? "");
|
|
2317
|
-
return /create|edit|form|cadastr|register|novo|
|
|
2318
|
-
haystack
|
|
2319
|
-
);
|
|
2580
|
+
return /create|edit|form|cadastr|criar|register|registr|novo|nova/.test(haystack);
|
|
2320
2581
|
}
|
|
2321
2582
|
function screenPathParts(screen) {
|
|
2322
2583
|
if (!screen) return [];
|
|
@@ -2691,6 +2952,7 @@ function listItemElement(route, list, item) {
|
|
|
2691
2952
|
list.id ?? list.label ?? `list-${list.index}`,
|
|
2692
2953
|
key
|
|
2693
2954
|
]);
|
|
2955
|
+
const provenance = item.itemKey || item.reactKey ? "declared" : label ? "derived" : "positional";
|
|
2694
2956
|
const inModal = (item.buttons ?? []).some((button) => button.inModal) || (item.inputs ?? []).some((input) => input.inModal) || (item.toggles ?? []).some((toggle) => toggle.inModal);
|
|
2695
2957
|
return {
|
|
2696
2958
|
id,
|
|
@@ -2700,6 +2962,7 @@ function listItemElement(route, list, item) {
|
|
|
2700
2962
|
actions: ["press"],
|
|
2701
2963
|
disabled: false,
|
|
2702
2964
|
source: "list",
|
|
2965
|
+
provenance,
|
|
2703
2966
|
targetId: item.syntheticId,
|
|
2704
2967
|
...inModal ? { inModal: true } : {},
|
|
2705
2968
|
listContext: {
|
|
@@ -2729,17 +2992,14 @@ function buttonElement(route, button, index) {
|
|
|
2729
2992
|
disabled: button.disabled,
|
|
2730
2993
|
selected: button.selected,
|
|
2731
2994
|
source: "button",
|
|
2995
|
+
...button.provenance ? { provenance: button.provenance } : {},
|
|
2732
2996
|
targetId: button.id ?? button.label,
|
|
2733
2997
|
...button.inModal ? { inModal: true } : {}
|
|
2734
2998
|
};
|
|
2735
2999
|
}
|
|
2736
3000
|
function inputElement(route, input, index) {
|
|
2737
3001
|
const label = input.label ?? input.placeholder ?? input.id;
|
|
2738
|
-
const id = stableElementId([
|
|
2739
|
-
"input",
|
|
2740
|
-
route ?? "unknown",
|
|
2741
|
-
input.id ?? label ?? `input-${index}`
|
|
2742
|
-
]);
|
|
3002
|
+
const id = stableElementId(["input", route ?? "unknown", input.id ?? label ?? `input-${index}`]);
|
|
2743
3003
|
return {
|
|
2744
3004
|
id,
|
|
2745
3005
|
role: "input",
|
|
@@ -2748,6 +3008,7 @@ function inputElement(route, input, index) {
|
|
|
2748
3008
|
actions: ["focus", "setValue"],
|
|
2749
3009
|
disabled: input.editable === false,
|
|
2750
3010
|
source: "input",
|
|
3011
|
+
...input.provenance ? { provenance: input.provenance } : {},
|
|
2751
3012
|
targetId: input.id ?? input.label ?? input.placeholder,
|
|
2752
3013
|
...input.inModal ? { inModal: true } : {}
|
|
2753
3014
|
};
|
|
@@ -2767,10 +3028,45 @@ function toggleElement(route, toggle, index) {
|
|
|
2767
3028
|
actions: ["toggle", "press"],
|
|
2768
3029
|
selected: toggle.value,
|
|
2769
3030
|
source: "toggle",
|
|
3031
|
+
...toggle.provenance ? { provenance: toggle.provenance } : {},
|
|
2770
3032
|
targetId: toggle.id ?? toggle.label,
|
|
2771
3033
|
...toggle.inModal ? { inModal: true } : {}
|
|
2772
3034
|
};
|
|
2773
3035
|
}
|
|
3036
|
+
function listItemToggleElement(route, list, item, toggle, toggleIndex) {
|
|
3037
|
+
const rowLabel = labelForListItem(item);
|
|
3038
|
+
const label = toggle.label ?? rowLabel ?? toggle.id;
|
|
3039
|
+
const key = item.itemKey ?? item.reactKey ?? rowLabel ?? item.syntheticId;
|
|
3040
|
+
const id = stableElementId([
|
|
3041
|
+
"toggle",
|
|
3042
|
+
route ?? "unknown",
|
|
3043
|
+
list.id ?? list.label ?? `list-${list.index}`,
|
|
3044
|
+
key,
|
|
3045
|
+
toggle.id ?? `toggle-${toggleIndex}`
|
|
3046
|
+
]);
|
|
3047
|
+
return {
|
|
3048
|
+
id,
|
|
3049
|
+
role: "toggle",
|
|
3050
|
+
label,
|
|
3051
|
+
// The row's texts are what the user actually says out loud.
|
|
3052
|
+
texts: uniqueTexts([label, ...item.texts ?? []]),
|
|
3053
|
+
actions: ["toggle", "press"],
|
|
3054
|
+
selected: toggle.value,
|
|
3055
|
+
source: "list",
|
|
3056
|
+
...toggle.provenance ? { provenance: toggle.provenance } : {},
|
|
3057
|
+
targetId: toggle.id,
|
|
3058
|
+
listContext: {
|
|
3059
|
+
listIndex: list.index,
|
|
3060
|
+
listId: list.id,
|
|
3061
|
+
listLabel: list.label,
|
|
3062
|
+
itemIndex: item.index,
|
|
3063
|
+
itemKey: item.itemKey,
|
|
3064
|
+
reactKey: item.reactKey,
|
|
3065
|
+
syntheticId: item.syntheticId
|
|
3066
|
+
},
|
|
3067
|
+
...toggle.inModal ? { inModal: true } : {}
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
2774
3070
|
function sliderElement(route, slider, index) {
|
|
2775
3071
|
const label = slider.label ?? slider.id;
|
|
2776
3072
|
const id = stableElementId([
|
|
@@ -2786,37 +3082,48 @@ function sliderElement(route, slider, index) {
|
|
|
2786
3082
|
actions: ["setValue"],
|
|
2787
3083
|
disabled: slider.disabled,
|
|
2788
3084
|
source: "slider",
|
|
3085
|
+
...slider.provenance ? { provenance: slider.provenance } : {},
|
|
2789
3086
|
targetId: slider.id ?? slider.label,
|
|
2790
3087
|
...slider.inModal ? { inModal: true } : {}
|
|
2791
3088
|
};
|
|
2792
3089
|
}
|
|
2793
|
-
function deriveInteractionElements(snapshot) {
|
|
3090
|
+
function deriveInteractionElements(snapshot, options) {
|
|
2794
3091
|
const elements = [];
|
|
2795
3092
|
const seen = /* @__PURE__ */ new Set();
|
|
2796
|
-
const
|
|
3093
|
+
const rectOf = options?.rectOf;
|
|
3094
|
+
const push = (element, node) => {
|
|
2797
3095
|
if (!element.id || seen.has(element.id)) return;
|
|
2798
3096
|
seen.add(element.id);
|
|
2799
|
-
|
|
3097
|
+
const rect = rectOf && node !== void 0 ? rectOf(node) : void 0;
|
|
3098
|
+
elements.push(rect ? { ...element, rect } : element);
|
|
2800
3099
|
};
|
|
2801
3100
|
for (const list of snapshot.lists ?? []) {
|
|
2802
3101
|
for (const item of list.items ?? []) {
|
|
2803
|
-
push(listItemElement(snapshot.route, list, item));
|
|
3102
|
+
push(listItemElement(snapshot.route, list, item), item);
|
|
3103
|
+
for (const [toggleIndex, toggle] of (item.toggles ?? []).entries()) {
|
|
3104
|
+
push(listItemToggleElement(snapshot.route, list, item, toggle, toggleIndex), toggle);
|
|
3105
|
+
}
|
|
2804
3106
|
}
|
|
2805
3107
|
}
|
|
2806
3108
|
for (const [index, button] of (snapshot.buttons ?? []).entries()) {
|
|
2807
|
-
push(buttonElement(snapshot.route, button, index));
|
|
3109
|
+
push(buttonElement(snapshot.route, button, index), button);
|
|
2808
3110
|
}
|
|
2809
3111
|
for (const [index, input] of (snapshot.inputs ?? []).entries()) {
|
|
2810
|
-
push(inputElement(snapshot.route, input, index));
|
|
3112
|
+
push(inputElement(snapshot.route, input, index), input);
|
|
2811
3113
|
}
|
|
2812
3114
|
for (const [index, toggle] of (snapshot.toggles ?? []).entries()) {
|
|
2813
|
-
push(toggleElement(snapshot.route, toggle, index));
|
|
3115
|
+
push(toggleElement(snapshot.route, toggle, index), toggle);
|
|
2814
3116
|
}
|
|
2815
3117
|
for (const [index, slider] of (snapshot.sliders ?? []).entries()) {
|
|
2816
|
-
push(sliderElement(snapshot.route, slider, index));
|
|
3118
|
+
push(sliderElement(snapshot.route, slider, index), slider);
|
|
2817
3119
|
}
|
|
2818
3120
|
return elements;
|
|
2819
3121
|
}
|
|
3122
|
+
function toWireElement(entry) {
|
|
3123
|
+
if (entry.rect === void 0) return entry;
|
|
3124
|
+
const { rect: _rect, ...wire } = entry;
|
|
3125
|
+
return wire;
|
|
3126
|
+
}
|
|
2820
3127
|
function findElementForChoice(elements, option) {
|
|
2821
3128
|
if (option.targetId) {
|
|
2822
3129
|
const byTarget = elements.find((element) => element.targetId === option.targetId);
|
|
@@ -2857,6 +3164,7 @@ var WIRE_LIMITS = {
|
|
|
2857
3164
|
listItems: 500,
|
|
2858
3165
|
listDataPreview: 500,
|
|
2859
3166
|
listDataPreviewTextLength: 500,
|
|
3167
|
+
scrollables: 20,
|
|
2860
3168
|
choiceGroups: 100,
|
|
2861
3169
|
choiceOptions: 200,
|
|
2862
3170
|
elements: 1e3,
|
|
@@ -2937,6 +3245,9 @@ function clampSnapshotToWireLimits(snapshot) {
|
|
|
2937
3245
|
snapshot.lists = (cut.array(snapshot.lists, WIRE_LIMITS.lists) ?? []).map(
|
|
2938
3246
|
(list) => clampList(cut, list)
|
|
2939
3247
|
);
|
|
3248
|
+
if (snapshot.scrollables) {
|
|
3249
|
+
snapshot.scrollables = cut.array(snapshot.scrollables, WIRE_LIMITS.scrollables);
|
|
3250
|
+
}
|
|
2940
3251
|
snapshot.choiceGroups = (cut.array(snapshot.choiceGroups, WIRE_LIMITS.choiceGroups) ?? []).map(
|
|
2941
3252
|
(group) => clampChoiceGroup(cut, group)
|
|
2942
3253
|
);
|
|
@@ -2964,7 +3275,9 @@ var ComponentRegistryImpl = class {
|
|
|
2964
3275
|
*/
|
|
2965
3276
|
register(id, entry) {
|
|
2966
3277
|
this.components.set(id, entry);
|
|
2967
|
-
|
|
3278
|
+
appilotsDebugLog(
|
|
3279
|
+
`ComponentRegistry.register: id="${id}" kind="${entry.kind}" \u2014 total count: ${this.components.size}`
|
|
3280
|
+
);
|
|
2968
3281
|
this.notify(id, entry);
|
|
2969
3282
|
}
|
|
2970
3283
|
/**
|
|
@@ -2972,7 +3285,9 @@ var ComponentRegistryImpl = class {
|
|
|
2972
3285
|
*/
|
|
2973
3286
|
unregister(id) {
|
|
2974
3287
|
this.components.delete(id);
|
|
2975
|
-
|
|
3288
|
+
appilotsDebugLog(
|
|
3289
|
+
`ComponentRegistry.unregister: id="${id}" \u2014 total count: ${this.components.size}`
|
|
3290
|
+
);
|
|
2976
3291
|
this.notify(id, null);
|
|
2977
3292
|
}
|
|
2978
3293
|
/**
|
|
@@ -3076,14 +3391,14 @@ var ListRegistryImpl = class {
|
|
|
3076
3391
|
listeners = /* @__PURE__ */ new Set();
|
|
3077
3392
|
register(id, entry) {
|
|
3078
3393
|
this.lists.set(id, entry);
|
|
3079
|
-
|
|
3080
|
-
`
|
|
3394
|
+
appilotsDebugLog(
|
|
3395
|
+
`ListRegistry.register: id="${id}" component="${entry.component}" items=${entry.itemCount ?? "unknown"} \u2014 total count: ${this.lists.size}`
|
|
3081
3396
|
);
|
|
3082
3397
|
this.notify(id, entry);
|
|
3083
3398
|
}
|
|
3084
3399
|
unregister(id) {
|
|
3085
3400
|
this.lists.delete(id);
|
|
3086
|
-
|
|
3401
|
+
appilotsDebugLog(`ListRegistry.unregister: id="${id}" \u2014 total count: ${this.lists.size}`);
|
|
3087
3402
|
this.notify(id, null);
|
|
3088
3403
|
}
|
|
3089
3404
|
get(id) {
|
|
@@ -3129,7 +3444,7 @@ var ElementRegistryImpl = class {
|
|
|
3129
3444
|
next.set(element.id, element);
|
|
3130
3445
|
}
|
|
3131
3446
|
this.elements = next;
|
|
3132
|
-
|
|
3447
|
+
appilotsDebugLog(`ElementRegistry.replaceAll: elements=${this.elements.size}`);
|
|
3133
3448
|
}
|
|
3134
3449
|
register(id, entry) {
|
|
3135
3450
|
this.elements.set(id, entry);
|
|
@@ -3270,20 +3585,20 @@ function extractId(props) {
|
|
|
3270
3585
|
if (raw && typeof raw === "string") {
|
|
3271
3586
|
const normalized = normalizeId2(raw);
|
|
3272
3587
|
if (normalized && normalized.length > 0) {
|
|
3273
|
-
|
|
3588
|
+
appilotsDebugLog(`extractId: raw="${raw}" \u2192 normalized="${normalized}"`);
|
|
3274
3589
|
return normalized;
|
|
3275
3590
|
}
|
|
3276
|
-
|
|
3591
|
+
appilotsDebugLog(`extractId: raw="${raw}" normalized to empty string, falling back`);
|
|
3277
3592
|
}
|
|
3278
3593
|
const hint = deriveSemanticInputId(props);
|
|
3279
3594
|
if (hint) {
|
|
3280
|
-
|
|
3281
|
-
`
|
|
3595
|
+
appilotsDebugWarn(
|
|
3596
|
+
`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.`
|
|
3282
3597
|
);
|
|
3283
3598
|
return hint;
|
|
3284
3599
|
}
|
|
3285
|
-
|
|
3286
|
-
"
|
|
3600
|
+
appilotsDebugLog(
|
|
3601
|
+
"extractId: no identifiable prop found (testID/nativeID/accessibilityLabel/name/placeholder/autoComplete missing); skipping registration"
|
|
3287
3602
|
);
|
|
3288
3603
|
return null;
|
|
3289
3604
|
}
|
|
@@ -3301,15 +3616,17 @@ var TrackedTextInput = React2.forwardRef(function TrackedTextInput2(props, forwa
|
|
|
3301
3616
|
onChangeRef.current = props.onChangeText;
|
|
3302
3617
|
React2.useEffect(() => {
|
|
3303
3618
|
if (!id) {
|
|
3304
|
-
|
|
3619
|
+
appilotsDebugLog("TrackedTextInput useEffect: no id extracted, skipping registration");
|
|
3305
3620
|
return;
|
|
3306
3621
|
}
|
|
3307
|
-
|
|
3622
|
+
appilotsDebugLog(
|
|
3623
|
+
`TrackedTextInput useEffect: registering field id="${id}" label="${extractLabel(props)}"`
|
|
3624
|
+
);
|
|
3308
3625
|
const entry = {
|
|
3309
3626
|
kind: "field",
|
|
3310
3627
|
getValue: () => valueRef.current,
|
|
3311
3628
|
setValue: (v) => {
|
|
3312
|
-
|
|
3629
|
+
appilotsDebugLog(`TrackedTextInput setValue: id="${id}" value=${describeValue(v)}`);
|
|
3313
3630
|
onChangeRef.current?.(v);
|
|
3314
3631
|
inputRef.current?.setNativeProps?.({ text: v });
|
|
3315
3632
|
},
|
|
@@ -3319,15 +3636,18 @@ var TrackedTextInput = React2.forwardRef(function TrackedTextInput2(props, forwa
|
|
|
3319
3636
|
};
|
|
3320
3637
|
registry.register(id, entry);
|
|
3321
3638
|
return () => {
|
|
3322
|
-
|
|
3639
|
+
appilotsDebugLog(`TrackedTextInput useEffect cleanup: unregistering field id="${id}"`);
|
|
3323
3640
|
registry.unregister(id);
|
|
3324
3641
|
};
|
|
3325
3642
|
}, [id]);
|
|
3326
|
-
const mergedRef = React2.useCallback(
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3643
|
+
const mergedRef = React2.useCallback(
|
|
3644
|
+
(instance) => {
|
|
3645
|
+
inputRef.current = instance;
|
|
3646
|
+
if (typeof forwardedRef === "function") forwardedRef(instance);
|
|
3647
|
+
else if (forwardedRef && typeof forwardedRef === "object") forwardedRef.current = instance;
|
|
3648
|
+
},
|
|
3649
|
+
[forwardedRef]
|
|
3650
|
+
);
|
|
3331
3651
|
return _originalCreateElement(TextInput, { ...props, ref: mergedRef });
|
|
3332
3652
|
});
|
|
3333
3653
|
var TrackedSwitch = React2.forwardRef(function TrackedSwitch2(props, forwardedRef) {
|
|
@@ -3339,22 +3659,24 @@ var TrackedSwitch = React2.forwardRef(function TrackedSwitch2(props, forwardedRe
|
|
|
3339
3659
|
onChangeRef.current = props.onValueChange;
|
|
3340
3660
|
React2.useEffect(() => {
|
|
3341
3661
|
if (!id) {
|
|
3342
|
-
|
|
3662
|
+
appilotsDebugLog("TrackedSwitch useEffect: no id extracted, skipping registration");
|
|
3343
3663
|
return;
|
|
3344
3664
|
}
|
|
3345
|
-
|
|
3665
|
+
appilotsDebugLog(
|
|
3666
|
+
`TrackedSwitch useEffect: registering toggle id="${id}" label="${extractLabel(props)}"`
|
|
3667
|
+
);
|
|
3346
3668
|
const entry = {
|
|
3347
3669
|
kind: "toggle",
|
|
3348
3670
|
getValue: () => valueRef.current,
|
|
3349
3671
|
setValue: (v) => {
|
|
3350
|
-
|
|
3672
|
+
appilotsDebugLog(`TrackedSwitch setValue called: id="${id}" value=${v}`);
|
|
3351
3673
|
onChangeRef.current?.(v);
|
|
3352
3674
|
},
|
|
3353
3675
|
label: extractLabel(props)
|
|
3354
3676
|
};
|
|
3355
3677
|
registry.register(id, entry);
|
|
3356
3678
|
return () => {
|
|
3357
|
-
|
|
3679
|
+
appilotsDebugLog(`TrackedSwitch useEffect cleanup: unregistering toggle id="${id}"`);
|
|
3358
3680
|
registry.unregister(id);
|
|
3359
3681
|
};
|
|
3360
3682
|
}, [id]);
|
|
@@ -3374,21 +3696,25 @@ function getTrackedPressable(OriginalComponent) {
|
|
|
3374
3696
|
return;
|
|
3375
3697
|
}
|
|
3376
3698
|
if (!id || !props.onPress) {
|
|
3377
|
-
|
|
3699
|
+
appilotsDebugLog(
|
|
3700
|
+
`TrackedPressable useEffect: skipping registration (id=${id}, hasOnPress=${!!props.onPress})`
|
|
3701
|
+
);
|
|
3378
3702
|
return;
|
|
3379
3703
|
}
|
|
3380
|
-
|
|
3704
|
+
appilotsDebugLog(
|
|
3705
|
+
`TrackedPressable useEffect: registering target id="${id}" label="${extractLabel(props)}"`
|
|
3706
|
+
);
|
|
3381
3707
|
const entry = {
|
|
3382
3708
|
kind: "target",
|
|
3383
3709
|
press: () => {
|
|
3384
|
-
|
|
3710
|
+
appilotsDebugLog(`TrackedPressable press called: id="${id}"`);
|
|
3385
3711
|
onPressRef.current?.();
|
|
3386
3712
|
},
|
|
3387
3713
|
label: extractLabel(props)
|
|
3388
3714
|
};
|
|
3389
3715
|
registry.register(id, entry);
|
|
3390
3716
|
return () => {
|
|
3391
|
-
|
|
3717
|
+
appilotsDebugLog(`TrackedPressable useEffect cleanup: unregistering target id="${id}"`);
|
|
3392
3718
|
registry.unregister(id);
|
|
3393
3719
|
};
|
|
3394
3720
|
}, [id, !!props.onPress]);
|
|
@@ -3397,12 +3723,7 @@ function getTrackedPressable(OriginalComponent) {
|
|
|
3397
3723
|
_pressableWrapperCache.set(OriginalComponent, cached);
|
|
3398
3724
|
return cached;
|
|
3399
3725
|
}
|
|
3400
|
-
var LIST_COMPONENT_NAMES = /* @__PURE__ */ new Set([
|
|
3401
|
-
"FlatList",
|
|
3402
|
-
"SectionList",
|
|
3403
|
-
"VirtualizedList",
|
|
3404
|
-
"FlashList"
|
|
3405
|
-
]);
|
|
3726
|
+
var LIST_COMPONENT_NAMES = /* @__PURE__ */ new Set(["FlatList", "SectionList", "VirtualizedList", "FlashList"]);
|
|
3406
3727
|
var _listWrapperCache = /* @__PURE__ */ new Map();
|
|
3407
3728
|
var _generatedListIdCounter = 0;
|
|
3408
3729
|
function componentDisplayName(type) {
|
|
@@ -3456,12 +3777,10 @@ var PREVIEW_TEXT_KEYS = [
|
|
|
3456
3777
|
"label",
|
|
3457
3778
|
"description",
|
|
3458
3779
|
"descricao",
|
|
3459
|
-
"plate",
|
|
3460
|
-
"placa",
|
|
3461
|
-
"model",
|
|
3462
|
-
"modelo",
|
|
3463
3780
|
"email",
|
|
3464
|
-
"username"
|
|
3781
|
+
"username",
|
|
3782
|
+
"code",
|
|
3783
|
+
"codigo"
|
|
3465
3784
|
];
|
|
3466
3785
|
function projectItemText(item) {
|
|
3467
3786
|
if (item == null) return "";
|
|
@@ -3557,10 +3876,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3557
3876
|
const itemCount = resolveItemCount(props);
|
|
3558
3877
|
const refreshing = props.refreshing === true;
|
|
3559
3878
|
const label = listLabel(props);
|
|
3560
|
-
const dataPreview = React2.useMemo(
|
|
3561
|
-
() => buildDataPreview(props),
|
|
3562
|
-
[props.data, props.sections]
|
|
3563
|
-
);
|
|
3879
|
+
const dataPreview = React2.useMemo(() => buildDataPreview(props), [props.data, props.sections]);
|
|
3564
3880
|
const mergedRef = React2.useCallback(
|
|
3565
3881
|
(instance) => {
|
|
3566
3882
|
instanceRef.current = instance;
|
|
@@ -3595,7 +3911,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3595
3911
|
}
|
|
3596
3912
|
return false;
|
|
3597
3913
|
} catch (err) {
|
|
3598
|
-
|
|
3914
|
+
appilotsDebugWarn(`TrackedList scrollToIndex failed for "${listId}":`, err);
|
|
3599
3915
|
return false;
|
|
3600
3916
|
}
|
|
3601
3917
|
},
|
|
@@ -3614,7 +3930,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3614
3930
|
}
|
|
3615
3931
|
return false;
|
|
3616
3932
|
} catch (err) {
|
|
3617
|
-
|
|
3933
|
+
appilotsDebugWarn(`TrackedList scrollToOffset failed for "${listId}":`, err);
|
|
3618
3934
|
return false;
|
|
3619
3935
|
}
|
|
3620
3936
|
},
|
|
@@ -3658,26 +3974,148 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3658
3974
|
_listWrapperCache.set(OriginalComponent, cached);
|
|
3659
3975
|
return cached;
|
|
3660
3976
|
}
|
|
3661
|
-
var
|
|
3977
|
+
var SCROLLABLE_COMPONENT_NAMES = /* @__PURE__ */ new Set(["ScrollView", "KeyboardAwareScrollView"]);
|
|
3978
|
+
var _scrollWrapperCache = /* @__PURE__ */ new Map();
|
|
3979
|
+
var _generatedScrollIdCounter = 0;
|
|
3980
|
+
function extractScrollId(props, componentName) {
|
|
3981
|
+
const raw = props?.appilotsScrollId ?? props?.testID ?? props?.accessibilityLabel;
|
|
3982
|
+
if (typeof raw === "string" && raw.trim().length > 0) return raw.trim();
|
|
3983
|
+
_generatedScrollIdCounter += 1;
|
|
3984
|
+
return `${componentName.toLowerCase()}-${_generatedScrollIdCounter}`;
|
|
3985
|
+
}
|
|
3986
|
+
function chain(appHandler, ours) {
|
|
3987
|
+
return ((...args) => {
|
|
3988
|
+
if (typeof appHandler === "function") {
|
|
3989
|
+
try {
|
|
3990
|
+
appHandler(...args);
|
|
3991
|
+
} catch (err) {
|
|
3992
|
+
appilotsDebugWarn("TrackedScrollView: app scroll handler threw:", err);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
ours(...args);
|
|
3996
|
+
});
|
|
3997
|
+
}
|
|
3998
|
+
function getTrackedScrollView(OriginalComponent, componentName) {
|
|
3999
|
+
let cached = _scrollWrapperCache.get(OriginalComponent);
|
|
4000
|
+
if (cached) return cached;
|
|
4001
|
+
cached = React2.forwardRef(function TrackedScrollView(props, forwardedRef) {
|
|
4002
|
+
const idRef = React2.useRef(null);
|
|
4003
|
+
if (!idRef.current) idRef.current = extractScrollId(props, componentName);
|
|
4004
|
+
const scrollId = idRef.current;
|
|
4005
|
+
const instanceRef = React2.useRef(null);
|
|
4006
|
+
const metricsRef = React2.useRef({
|
|
4007
|
+
offset: 0,
|
|
4008
|
+
visibleLength: 0,
|
|
4009
|
+
contentLength: 0
|
|
4010
|
+
});
|
|
4011
|
+
const horizontal = props.horizontal === true;
|
|
4012
|
+
const mergedRef = React2.useCallback(
|
|
4013
|
+
(instance) => {
|
|
4014
|
+
instanceRef.current = instance;
|
|
4015
|
+
if (typeof forwardedRef === "function") forwardedRef(instance);
|
|
4016
|
+
else if (forwardedRef && typeof forwardedRef === "object") forwardedRef.current = instance;
|
|
4017
|
+
},
|
|
4018
|
+
[forwardedRef]
|
|
4019
|
+
);
|
|
4020
|
+
const label = typeof props.appilotsScrollLabel === "string" ? props.appilotsScrollLabel : typeof props.accessibilityLabel === "string" ? props.accessibilityLabel : void 0;
|
|
4021
|
+
React2.useEffect(() => {
|
|
4022
|
+
listRegistry.register(scrollId, {
|
|
4023
|
+
kind: "scroll",
|
|
4024
|
+
id: scrollId,
|
|
4025
|
+
component: componentName,
|
|
4026
|
+
label,
|
|
4027
|
+
scrollToOffset: (offset) => {
|
|
4028
|
+
const instance = instanceRef.current;
|
|
4029
|
+
if (!instance) return false;
|
|
4030
|
+
try {
|
|
4031
|
+
const scrollTo = typeof instance.scrollTo === "function" ? instance.scrollTo.bind(instance) : typeof instance.getScrollResponder === "function" ? instance.getScrollResponder()?.scrollTo?.bind(instance.getScrollResponder()) : void 0;
|
|
4032
|
+
if (typeof scrollTo !== "function") return false;
|
|
4033
|
+
scrollTo(horizontal ? { x: offset, animated: true } : { y: offset, animated: true });
|
|
4034
|
+
metricsRef.current = { ...metricsRef.current, offset };
|
|
4035
|
+
return true;
|
|
4036
|
+
} catch (err) {
|
|
4037
|
+
appilotsDebugWarn(`TrackedScrollView scrollTo failed for "${scrollId}":`, err);
|
|
4038
|
+
return false;
|
|
4039
|
+
}
|
|
4040
|
+
},
|
|
4041
|
+
getScrollMetrics: () => {
|
|
4042
|
+
const m = metricsRef.current;
|
|
4043
|
+
return m.contentLength > 0 ? { ...m } : void 0;
|
|
4044
|
+
}
|
|
4045
|
+
});
|
|
4046
|
+
return () => {
|
|
4047
|
+
listRegistry.unregister(scrollId);
|
|
4048
|
+
};
|
|
4049
|
+
}, [scrollId, label, horizontal]);
|
|
4050
|
+
const onScroll = React2.useMemo(
|
|
4051
|
+
() => chain(props.onScroll, (event) => {
|
|
4052
|
+
const n = event?.nativeEvent;
|
|
4053
|
+
if (!n) return;
|
|
4054
|
+
const offset = horizontal ? n.contentOffset?.x : n.contentOffset?.y;
|
|
4055
|
+
const visible = horizontal ? n.layoutMeasurement?.width : n.layoutMeasurement?.height;
|
|
4056
|
+
const content = horizontal ? n.contentSize?.width : n.contentSize?.height;
|
|
4057
|
+
metricsRef.current = {
|
|
4058
|
+
offset: typeof offset === "number" ? offset : metricsRef.current.offset,
|
|
4059
|
+
visibleLength: typeof visible === "number" ? visible : metricsRef.current.visibleLength,
|
|
4060
|
+
contentLength: typeof content === "number" ? content : metricsRef.current.contentLength
|
|
4061
|
+
};
|
|
4062
|
+
}),
|
|
4063
|
+
[props.onScroll, horizontal]
|
|
4064
|
+
);
|
|
4065
|
+
const onLayout = React2.useMemo(
|
|
4066
|
+
() => chain(props.onLayout, (event) => {
|
|
4067
|
+
const size = horizontal ? event?.nativeEvent?.layout?.width : event?.nativeEvent?.layout?.height;
|
|
4068
|
+
if (typeof size === "number") {
|
|
4069
|
+
metricsRef.current = { ...metricsRef.current, visibleLength: size };
|
|
4070
|
+
}
|
|
4071
|
+
}),
|
|
4072
|
+
[props.onLayout, horizontal]
|
|
4073
|
+
);
|
|
4074
|
+
const onContentSizeChange = React2.useMemo(
|
|
4075
|
+
() => chain(props.onContentSizeChange, (w, h) => {
|
|
4076
|
+
const size = horizontal ? w : h;
|
|
4077
|
+
if (typeof size === "number") {
|
|
4078
|
+
metricsRef.current = { ...metricsRef.current, contentLength: size };
|
|
4079
|
+
}
|
|
4080
|
+
}),
|
|
4081
|
+
[props.onContentSizeChange, horizontal]
|
|
4082
|
+
);
|
|
4083
|
+
return _originalCreateElement(OriginalComponent, {
|
|
4084
|
+
...props,
|
|
4085
|
+
ref: mergedRef,
|
|
4086
|
+
// Without a throttle iOS fires `onScroll` once per gesture, which
|
|
4087
|
+
// is enough to stay roughly in sync but leaves the offset stale
|
|
4088
|
+
// mid-fling. 16ms is RN's own recommendation for live metrics.
|
|
4089
|
+
scrollEventThrottle: props.scrollEventThrottle ?? 16,
|
|
4090
|
+
onScroll,
|
|
4091
|
+
onLayout,
|
|
4092
|
+
onContentSizeChange,
|
|
4093
|
+
__appilotsScrollId: scrollId
|
|
4094
|
+
});
|
|
4095
|
+
});
|
|
4096
|
+
_scrollWrapperCache.set(OriginalComponent, cached);
|
|
4097
|
+
return cached;
|
|
4098
|
+
}
|
|
4099
|
+
var _enabled2 = false;
|
|
3662
4100
|
function enableAppilotsAutoTracking(options) {
|
|
3663
|
-
if (
|
|
3664
|
-
|
|
4101
|
+
if (_enabled2) return;
|
|
4102
|
+
_enabled2 = true;
|
|
3665
4103
|
const { trackPressable = true, debug = false } = options ?? {};
|
|
3666
4104
|
if (debug) {
|
|
3667
4105
|
componentRegistry.subscribe((id, entry) => {
|
|
3668
4106
|
if (entry) {
|
|
3669
|
-
|
|
4107
|
+
appilotsDebugLog(`Registered ${entry.kind}: "${id}" (${entry.label ?? "no label"})`);
|
|
3670
4108
|
} else {
|
|
3671
|
-
|
|
4109
|
+
appilotsDebugLog(`Unregistered: "${id}"`);
|
|
3672
4110
|
}
|
|
3673
4111
|
});
|
|
3674
4112
|
listRegistry.subscribe((id, entry) => {
|
|
3675
4113
|
if (entry) {
|
|
3676
|
-
|
|
3677
|
-
`
|
|
4114
|
+
appilotsDebugLog(
|
|
4115
|
+
`Registered list: "${id}" (${entry.component}, items=${entry.itemCount ?? "unknown"})`
|
|
3678
4116
|
);
|
|
3679
4117
|
} else {
|
|
3680
|
-
|
|
4118
|
+
appilotsDebugLog(`Unregistered list: "${id}"`);
|
|
3681
4119
|
}
|
|
3682
4120
|
});
|
|
3683
4121
|
}
|
|
@@ -3685,12 +4123,14 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3685
4123
|
_originalCreateElement = originalCreateElement;
|
|
3686
4124
|
function isComponent(type, target, name) {
|
|
3687
4125
|
if (type === target) {
|
|
3688
|
-
|
|
4126
|
+
appilotsDebugLog(`isComponent: matched "${name}" by reference equality`);
|
|
3689
4127
|
return true;
|
|
3690
4128
|
}
|
|
3691
4129
|
const dn = type?.displayName || type?.name || "";
|
|
3692
4130
|
if (dn === name) {
|
|
3693
|
-
|
|
4131
|
+
appilotsDebugLog(
|
|
4132
|
+
`isComponent: matched "${name}" by displayName/name (reference equality failed \u2014 possible monorepo issue)`
|
|
4133
|
+
);
|
|
3694
4134
|
return true;
|
|
3695
4135
|
}
|
|
3696
4136
|
return false;
|
|
@@ -3699,24 +4139,36 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3699
4139
|
if (!props || !type || props.__appilotsInternal) return null;
|
|
3700
4140
|
const displayName = componentDisplayName(type);
|
|
3701
4141
|
if (LIST_COMPONENT_NAMES.has(displayName)) {
|
|
3702
|
-
|
|
4142
|
+
appilotsDebugLog(`intercepted list component "${displayName}"`);
|
|
3703
4143
|
return { type: getTrackedList(type, displayName), props };
|
|
3704
4144
|
}
|
|
4145
|
+
if (SCROLLABLE_COMPONENT_NAMES.has(displayName)) {
|
|
4146
|
+
appilotsDebugLog(`intercepted scrollable "${displayName}"`);
|
|
4147
|
+
return { type: getTrackedScrollView(type, displayName), props };
|
|
4148
|
+
}
|
|
3705
4149
|
if (isComponent(type, TextInput, "TextInput")) {
|
|
3706
4150
|
if (props.testID || props.accessibilityLabel || props.placeholder) {
|
|
3707
|
-
|
|
4151
|
+
appilotsDebugLog(
|
|
4152
|
+
// Ids, not the user-facing copy: a placeholder is the app's
|
|
4153
|
+
// own text and often the most identifying string on screen.
|
|
4154
|
+
`intercepted TextInput (testID="${props.testID ?? "?"}" hasLabel=${!!props.accessibilityLabel} hasPlaceholder=${!!props.placeholder})`
|
|
4155
|
+
);
|
|
3708
4156
|
return { type: TrackedTextInput, props };
|
|
3709
4157
|
}
|
|
3710
4158
|
}
|
|
3711
4159
|
if (isComponent(type, Switch, "Switch")) {
|
|
3712
4160
|
if (props.testID || props.accessibilityLabel) {
|
|
3713
|
-
|
|
4161
|
+
appilotsDebugLog(
|
|
4162
|
+
`intercepted Switch (testID="${props.testID}", label="${props.accessibilityLabel}")`
|
|
4163
|
+
);
|
|
3714
4164
|
return { type: TrackedSwitch, props };
|
|
3715
4165
|
}
|
|
3716
4166
|
}
|
|
3717
4167
|
if (isComponent(type, TouchableOpacity, "TouchableOpacity") || trackPressable && isComponent(type, Pressable, "Pressable")) {
|
|
3718
4168
|
if ((props.testID || props.accessibilityLabel) && props.onPress) {
|
|
3719
|
-
|
|
4169
|
+
appilotsDebugLog(
|
|
4170
|
+
`intercepted Pressable/TouchableOpacity (testID="${props.testID}", label="${props.accessibilityLabel}")`
|
|
4171
|
+
);
|
|
3720
4172
|
return { type: getTrackedPressable(type), props };
|
|
3721
4173
|
}
|
|
3722
4174
|
}
|
|
@@ -3730,14 +4182,17 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3730
4182
|
}
|
|
3731
4183
|
return originalCreateElement(type, props, ...children);
|
|
3732
4184
|
};
|
|
3733
|
-
|
|
4185
|
+
_createElementPatched = true;
|
|
4186
|
+
appilotsDebugLog("Auto-tracking: React.createElement patched");
|
|
3734
4187
|
try {
|
|
3735
4188
|
const jsxRuntime = __require("react/jsx-runtime");
|
|
3736
4189
|
if (jsxRuntime) {
|
|
3737
4190
|
_patchJsxRuntimeModule(jsxRuntime);
|
|
3738
4191
|
}
|
|
3739
4192
|
} catch (e) {
|
|
3740
|
-
|
|
4193
|
+
appilotsDebugLog(
|
|
4194
|
+
"Auto-tracking: react/jsx-runtime not available internally (will be patched by metro shim)"
|
|
4195
|
+
);
|
|
3741
4196
|
}
|
|
3742
4197
|
try {
|
|
3743
4198
|
const jsxDevRuntime = __require("react/jsx-dev-runtime");
|
|
@@ -3745,11 +4200,14 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3745
4200
|
_patchJsxDevRuntimeModule(jsxDevRuntime);
|
|
3746
4201
|
}
|
|
3747
4202
|
} catch (e) {
|
|
3748
|
-
|
|
4203
|
+
appilotsDebugLog(
|
|
4204
|
+
"Auto-tracking: react/jsx-dev-runtime not available internally (will be patched by metro shim)"
|
|
4205
|
+
);
|
|
3749
4206
|
}
|
|
3750
4207
|
}
|
|
3751
4208
|
var _jsxRuntimePatched = false;
|
|
3752
4209
|
var _jsxDevRuntimePatched = false;
|
|
4210
|
+
var _createElementPatched = false;
|
|
3753
4211
|
function _patchJsxRuntimeModule(jsxRuntime) {
|
|
3754
4212
|
if (_jsxRuntimePatched || !isInterceptionActive() || !jsxRuntime) return;
|
|
3755
4213
|
_jsxRuntimePatched = true;
|
|
@@ -3763,7 +4221,7 @@ function _patchJsxRuntimeModule(jsxRuntime) {
|
|
|
3763
4221
|
}
|
|
3764
4222
|
return origJsx(type, props, key);
|
|
3765
4223
|
};
|
|
3766
|
-
|
|
4224
|
+
appilotsDebugLog("Auto-tracking: react/jsx-runtime jsx() patched");
|
|
3767
4225
|
}
|
|
3768
4226
|
if (origJsxs) {
|
|
3769
4227
|
jsxRuntime.jsxs = function(type, props, key) {
|
|
@@ -3773,7 +4231,7 @@ function _patchJsxRuntimeModule(jsxRuntime) {
|
|
|
3773
4231
|
}
|
|
3774
4232
|
return origJsxs(type, props, key);
|
|
3775
4233
|
};
|
|
3776
|
-
|
|
4234
|
+
appilotsDebugLog("Auto-tracking: react/jsx-runtime jsxs() patched");
|
|
3777
4235
|
}
|
|
3778
4236
|
}
|
|
3779
4237
|
function _patchJsxDevRuntimeModule(jsxDevRuntime) {
|
|
@@ -3788,58 +4246,75 @@ function _patchJsxDevRuntimeModule(jsxDevRuntime) {
|
|
|
3788
4246
|
}
|
|
3789
4247
|
return origJsxDEV(type, props, key, isStaticChildren, source, self);
|
|
3790
4248
|
};
|
|
3791
|
-
|
|
4249
|
+
appilotsDebugLog("Auto-tracking: react/jsx-dev-runtime jsxDEV() patched");
|
|
3792
4250
|
}
|
|
3793
4251
|
}
|
|
3794
4252
|
function _patchJsxRuntimes(jsxRuntime, jsxDevRuntime) {
|
|
3795
4253
|
if (!isInterceptionActive()) {
|
|
3796
|
-
|
|
4254
|
+
appilotsDebugWarn("_patchJsxRuntimes called but auto-tracking is not enabled");
|
|
3797
4255
|
return;
|
|
3798
4256
|
}
|
|
3799
|
-
|
|
4257
|
+
appilotsDebugLog("_patchJsxRuntimes: patching from app context (metro shim)");
|
|
3800
4258
|
if (jsxRuntime) _patchJsxRuntimeModule(jsxRuntime);
|
|
3801
4259
|
if (jsxDevRuntime) _patchJsxDevRuntimeModule(jsxDevRuntime);
|
|
3802
4260
|
}
|
|
3803
4261
|
function isAutoTrackingEnabled() {
|
|
3804
|
-
return
|
|
4262
|
+
return _enabled2;
|
|
4263
|
+
}
|
|
4264
|
+
function getAutoTrackingState() {
|
|
4265
|
+
return {
|
|
4266
|
+
enabled: _enabled2,
|
|
4267
|
+
createElementPatched: _createElementPatched,
|
|
4268
|
+
jsxRuntimePatched: _jsxRuntimePatched,
|
|
4269
|
+
jsxDevRuntimePatched: _jsxDevRuntimePatched,
|
|
4270
|
+
interceptionActive: isInterceptionActive()
|
|
4271
|
+
};
|
|
3805
4272
|
}
|
|
3806
4273
|
|
|
3807
4274
|
// src/core/initAppilots.ts
|
|
3808
4275
|
var _globalConfig = null;
|
|
3809
4276
|
var _autoConfigAttempted = false;
|
|
3810
4277
|
function initAppilots(config) {
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
4278
|
+
appilotsDebugLog(
|
|
4279
|
+
`initAppilots called with config: projectId="${config.projectId}", apiBaseUrl="${config.apiBaseUrl ?? config.apiUrl ?? "default"}", debug=${config.debug ?? false}, autoTracking=${JSON.stringify(config.autoTracking ?? {})}`
|
|
4280
|
+
);
|
|
4281
|
+
if (!config.apiBaseUrl) {
|
|
4282
|
+
config.apiBaseUrl = config.apiUrl ?? config.serverUrl;
|
|
3814
4283
|
}
|
|
3815
4284
|
_globalConfig = config;
|
|
3816
4285
|
const autoTrackingEnabled = config.autoTracking?.enabled !== false;
|
|
3817
4286
|
if (autoTrackingEnabled) {
|
|
3818
|
-
|
|
4287
|
+
appilotsDebugLog(
|
|
4288
|
+
"initAppilots: auto-tracking is enabled, calling enableAppilotsAutoTracking()"
|
|
4289
|
+
);
|
|
3819
4290
|
enableAppilotsAutoTracking({
|
|
3820
4291
|
trackPressable: config.autoTracking?.trackPressable ?? true,
|
|
3821
4292
|
debug: config.debug ?? false
|
|
3822
4293
|
});
|
|
3823
4294
|
} else {
|
|
3824
|
-
|
|
4295
|
+
appilotsDebugLog("initAppilots: auto-tracking is explicitly disabled");
|
|
3825
4296
|
}
|
|
3826
4297
|
}
|
|
3827
4298
|
function tryAutoConfig() {
|
|
3828
4299
|
if (_globalConfig || _autoConfigAttempted) {
|
|
3829
|
-
|
|
4300
|
+
appilotsDebugLog(
|
|
4301
|
+
`tryAutoConfig: skipping (alreadyConfigured=${!!_globalConfig}, alreadyAttempted=${_autoConfigAttempted})`
|
|
4302
|
+
);
|
|
3830
4303
|
return;
|
|
3831
4304
|
}
|
|
3832
4305
|
_autoConfigAttempted = true;
|
|
3833
4306
|
try {
|
|
3834
4307
|
const rc = globalThis.__APPILOTS_RC__;
|
|
3835
4308
|
if (rc && typeof rc === "object" && rc.projectId) {
|
|
3836
|
-
|
|
4309
|
+
appilotsDebugLog(
|
|
4310
|
+
`tryAutoConfig: found globalThis.__APPILOTS_RC__ with projectId="${rc.projectId}"`
|
|
4311
|
+
);
|
|
3837
4312
|
initAppilots(rc);
|
|
3838
4313
|
} else {
|
|
3839
|
-
|
|
4314
|
+
appilotsDebugLog("tryAutoConfig: globalThis.__APPILOTS_RC__ not found or missing projectId");
|
|
3840
4315
|
}
|
|
3841
4316
|
} catch {
|
|
3842
|
-
|
|
4317
|
+
appilotsDebugWarn("tryAutoConfig: error reading globalThis.__APPILOTS_RC__");
|
|
3843
4318
|
}
|
|
3844
4319
|
}
|
|
3845
4320
|
function getGlobalConfig() {
|
|
@@ -3856,7 +4331,7 @@ var _diagnostics = {
|
|
|
3856
4331
|
};
|
|
3857
4332
|
function setFiberRoot(fiber) {
|
|
3858
4333
|
if (_fiberRoot !== fiber) {
|
|
3859
|
-
|
|
4334
|
+
appilotsDebugLog(`Introspection: fiber root captured (type=${typeName(fiber)})`);
|
|
3860
4335
|
_fiberRoot = fiber;
|
|
3861
4336
|
}
|
|
3862
4337
|
_diagnostics.captured = true;
|
|
@@ -3893,262 +4368,976 @@ function typeName(fiber) {
|
|
|
3893
4368
|
if (!t) return "unknown";
|
|
3894
4369
|
if (typeof t === "string") return t;
|
|
3895
4370
|
if (typeof t === "function") return t.displayName ?? t.name ?? "anonymous";
|
|
3896
|
-
if (typeof t === "object")
|
|
4371
|
+
if (typeof t === "object")
|
|
4372
|
+
return t.displayName ?? t.render?.displayName ?? t.render?.name ?? "forwardRef/memo";
|
|
3897
4373
|
return String(t);
|
|
3898
4374
|
}
|
|
3899
4375
|
|
|
3900
|
-
// src/
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
4376
|
+
// src/introspection/textPolicy.ts
|
|
4377
|
+
function isReferenceable(text) {
|
|
4378
|
+
return typeof text === "string" && /[\p{L}\p{N}]/u.test(text);
|
|
4379
|
+
}
|
|
4380
|
+
function foldLatinDiacritics(text) {
|
|
4381
|
+
return text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC");
|
|
4382
|
+
}
|
|
4383
|
+
function foldForCompare(text) {
|
|
4384
|
+
if (!text) return "";
|
|
4385
|
+
return foldLatinDiacritics(text.toLowerCase()).replace(/[^\p{L}\p{N}]/gu, "");
|
|
4386
|
+
}
|
|
4387
|
+
function labelToId(label) {
|
|
4388
|
+
if (!label) return "";
|
|
4389
|
+
return foldLatinDiacritics(label.toLowerCase()).replace(/[^\p{L}\p{N}\s]/gu, "").trim().replace(/\s+(.)/gu, (_, c) => c.toUpperCase()).replace(/\s+/gu, "");
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
// src/introspection/geometry.ts
|
|
4393
|
+
var _host = null;
|
|
4394
|
+
var _asked = 0;
|
|
4395
|
+
var _answered = 0;
|
|
4396
|
+
function setGeometryHost(host) {
|
|
4397
|
+
_host = host;
|
|
4398
|
+
_asked = 0;
|
|
4399
|
+
_answered = 0;
|
|
4400
|
+
}
|
|
4401
|
+
function geometryAvailability() {
|
|
4402
|
+
if (!_host) return "unavailable";
|
|
4403
|
+
if (_asked > 0 && _answered === 0) return "silent";
|
|
4404
|
+
return "available";
|
|
4405
|
+
}
|
|
4406
|
+
function geometryStats() {
|
|
4407
|
+
return { asked: _asked, answered: _answered };
|
|
4408
|
+
}
|
|
4409
|
+
function isUsableRect(rect) {
|
|
4410
|
+
return !!rect && Number.isFinite(rect.x) && Number.isFinite(rect.y) && Number.isFinite(rect.width) && Number.isFinite(rect.height);
|
|
4411
|
+
}
|
|
4412
|
+
var HOST_SEARCH_DEPTH = 6;
|
|
4413
|
+
function rectOfFiber(fiber) {
|
|
4414
|
+
const host = _host;
|
|
4415
|
+
if (!host || !fiber) return null;
|
|
4416
|
+
const stack = [{ node: fiber, depth: 0 }];
|
|
4417
|
+
let consulted = false;
|
|
4418
|
+
while (stack.length > 0) {
|
|
4419
|
+
const { node, depth } = stack.pop();
|
|
4420
|
+
const current = node;
|
|
4421
|
+
if (!current) continue;
|
|
4422
|
+
if (current.stateNode) {
|
|
4423
|
+
consulted = true;
|
|
4424
|
+
let rect = null;
|
|
4425
|
+
try {
|
|
4426
|
+
rect = host.rectOf(current.stateNode);
|
|
4427
|
+
} catch {
|
|
4428
|
+
rect = null;
|
|
4429
|
+
}
|
|
4430
|
+
if (isUsableRect(rect)) {
|
|
4431
|
+
_asked += 1;
|
|
4432
|
+
_answered += 1;
|
|
4433
|
+
return rect;
|
|
4434
|
+
}
|
|
4435
|
+
}
|
|
4436
|
+
if (depth < HOST_SEARCH_DEPTH && current.child) {
|
|
4437
|
+
stack.push({ node: current.child, depth: depth + 1 });
|
|
4438
|
+
const sibling = current.child.sibling;
|
|
4439
|
+
if (sibling) stack.push({ node: sibling, depth: depth + 1 });
|
|
3913
4440
|
}
|
|
3914
|
-
let top = fiber;
|
|
3915
|
-
while (top.return) top = top.return;
|
|
3916
|
-
setFiberRoot(top);
|
|
3917
|
-
}
|
|
3918
|
-
render() {
|
|
3919
|
-
return this.props.children ?? null;
|
|
3920
|
-
}
|
|
3921
|
-
};
|
|
3922
|
-
function AppilotsFiberRoot({
|
|
3923
|
-
children,
|
|
3924
|
-
onUnavailable
|
|
3925
|
-
}) {
|
|
3926
|
-
const Internals = React2.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
|
|
3927
|
-
const ownerFiber = Internals?.ReactCurrentOwner?.current;
|
|
3928
|
-
if (ownerFiber && !getFiberRoot()) {
|
|
3929
|
-
setFiberRoot(ownerFiber);
|
|
3930
4441
|
}
|
|
3931
|
-
|
|
4442
|
+
if (consulted) _asked += 1;
|
|
4443
|
+
return null;
|
|
3932
4444
|
}
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
4445
|
+
function isRectOnScreen(rect, window) {
|
|
4446
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
4447
|
+
if (rect.x >= window.width || rect.y >= window.height) return false;
|
|
4448
|
+
if (rect.x + rect.width <= 0 || rect.y + rect.height <= 0) return false;
|
|
4449
|
+
return true;
|
|
3938
4450
|
}
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
4451
|
+
var _rectByNode = /* @__PURE__ */ new WeakMap();
|
|
4452
|
+
function readGeometry(fiber) {
|
|
4453
|
+
const host = _host;
|
|
4454
|
+
if (!host) return { rect: null, fields: {} };
|
|
4455
|
+
const rect = rectOfFiber(fiber);
|
|
4456
|
+
if (!rect) return { rect: null, fields: {} };
|
|
4457
|
+
return { rect, fields: { onScreen: isRectOnScreen(rect, host.window) } };
|
|
3942
4458
|
}
|
|
3943
|
-
function
|
|
3944
|
-
if (
|
|
3945
|
-
|
|
4459
|
+
function withRect(node, reading) {
|
|
4460
|
+
if (reading.rect) _rectByNode.set(node, reading.rect);
|
|
4461
|
+
return node;
|
|
3946
4462
|
}
|
|
3947
|
-
function
|
|
3948
|
-
if (!
|
|
3949
|
-
|
|
4463
|
+
function rectForNode(node) {
|
|
4464
|
+
if (!node || typeof node !== "object") return void 0;
|
|
4465
|
+
return _rectByNode.get(node);
|
|
3950
4466
|
}
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
hasDegradedOnce = false;
|
|
3959
|
-
static getDerivedStateFromError() {
|
|
3960
|
-
return { degraded: true };
|
|
4467
|
+
|
|
4468
|
+
// src/introspection/walkFiber.ts
|
|
4469
|
+
function getTypeName(type) {
|
|
4470
|
+
if (!type) return null;
|
|
4471
|
+
if (typeof type === "string") return type;
|
|
4472
|
+
if (typeof type === "function") {
|
|
4473
|
+
return type.displayName ?? type.name ?? null;
|
|
3961
4474
|
}
|
|
3962
|
-
|
|
3963
|
-
if (
|
|
3964
|
-
|
|
3965
|
-
return;
|
|
3966
|
-
}
|
|
3967
|
-
this.hasDegradedOnce = true;
|
|
3968
|
-
console.error(
|
|
3969
|
-
`[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`,
|
|
3970
|
-
error
|
|
3971
|
-
);
|
|
3972
|
-
try {
|
|
3973
|
-
this.props.onError?.(error, info?.componentStack ?? null);
|
|
3974
|
-
} catch {
|
|
4475
|
+
if (typeof type === "object") {
|
|
4476
|
+
if (type.displayName) return type.displayName;
|
|
4477
|
+
if (type.render) {
|
|
4478
|
+
return type.render.displayName ?? type.render.name ?? null;
|
|
3975
4479
|
}
|
|
4480
|
+
if (type.type) return getTypeName(type.type);
|
|
3976
4481
|
}
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
4482
|
+
return null;
|
|
4483
|
+
}
|
|
4484
|
+
function matchesName(fiber, name) {
|
|
4485
|
+
return getTypeName(fiber.type) === name || getTypeName(fiber.elementType) === name;
|
|
4486
|
+
}
|
|
4487
|
+
function extractTextFromChildren(children) {
|
|
4488
|
+
if (children == null) return "";
|
|
4489
|
+
if (typeof children === "string") return children;
|
|
4490
|
+
if (typeof children === "number") return String(children);
|
|
4491
|
+
if (Array.isArray(children)) {
|
|
4492
|
+
return children.map((c) => extractTextFromChildren(c)).filter(Boolean).join("");
|
|
3981
4493
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
const
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4494
|
+
return "";
|
|
4495
|
+
}
|
|
4496
|
+
function findChildText(fiber) {
|
|
4497
|
+
const stack = [];
|
|
4498
|
+
if (fiber.child) stack.push(fiber.child);
|
|
4499
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4500
|
+
const texts = [];
|
|
4501
|
+
const glyphs = [];
|
|
4502
|
+
while (stack.length > 0) {
|
|
4503
|
+
const node = stack.pop();
|
|
4504
|
+
if (!node || visited.has(node)) continue;
|
|
4505
|
+
visited.add(node);
|
|
4506
|
+
if (matchesName(node, "Text")) {
|
|
4507
|
+
const text = extractTextFromChildren(node.memoizedProps?.children);
|
|
4508
|
+
const trimmed = text.trim();
|
|
4509
|
+
if (isReferenceable(trimmed)) {
|
|
4510
|
+
texts.push(trimmed);
|
|
4511
|
+
if (texts.length >= 3) break;
|
|
4512
|
+
} else if (trimmed && glyphs.length < 3) {
|
|
4513
|
+
glyphs.push(trimmed);
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4517
|
+
if (node.child) stack.push(node.child);
|
|
4518
|
+
}
|
|
4519
|
+
if (texts.length > 0) return texts.join(" \xB7 ");
|
|
4520
|
+
return glyphs.length > 0 ? glyphs.join(" \xB7 ") : void 0;
|
|
4521
|
+
}
|
|
4522
|
+
var SELF_REFERENTIAL_COMPONENTS = /* @__PURE__ */ new Set([
|
|
4523
|
+
"AppilotsChat",
|
|
4524
|
+
"AppilotsChatInner",
|
|
4525
|
+
"ActionBreadcrumb",
|
|
4526
|
+
"ConfirmDialog"
|
|
4527
|
+
]);
|
|
4528
|
+
function isAppilotsSkipMarked(props) {
|
|
4529
|
+
if (!props) return false;
|
|
4530
|
+
return props.__appilotsSkip === true || props["data-appilots-skip"] === true || props.appilotsSkip === true;
|
|
4531
|
+
}
|
|
4532
|
+
function isAppilotsSensitiveMarked(props) {
|
|
4533
|
+
if (!props) return false;
|
|
4534
|
+
return props.__appilotsSensitive === true || props["data-appilots-sensitive"] === true || props.appilotsSensitive === true;
|
|
4535
|
+
}
|
|
4536
|
+
function stringProp(props, name) {
|
|
4537
|
+
const value = props?.[name];
|
|
4538
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4539
|
+
}
|
|
4540
|
+
function numberProp(props, name) {
|
|
4541
|
+
const value = props?.[name];
|
|
4542
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4543
|
+
}
|
|
4544
|
+
function shouldSkipSubtree(fiber) {
|
|
4545
|
+
const props = fiber.memoizedProps;
|
|
4546
|
+
if (!props) return false;
|
|
4547
|
+
const typeName2 = getTypeName(fiber.type) ?? getTypeName(fiber.elementType);
|
|
4548
|
+
if (typeName2 && SELF_REFERENTIAL_COMPONENTS.has(typeName2)) return true;
|
|
4549
|
+
if (isAppilotsSkipMarked(props)) return true;
|
|
4550
|
+
if (isModalFiber(fiber) && props.visible === false) return true;
|
|
4551
|
+
if (matchesName(fiber, "Screen") && props.activityState === 0) return true;
|
|
4552
|
+
const style = props.style;
|
|
4553
|
+
if (style) {
|
|
4554
|
+
const flat = Array.isArray(style) ? Object.assign({}, ...style.filter(Boolean)) : style;
|
|
4555
|
+
if (flat?.display === "none") return true;
|
|
4556
|
+
if (flat?.opacity === 0 && props.pointerEvents === "none") return true;
|
|
4557
|
+
}
|
|
4558
|
+
return false;
|
|
4559
|
+
}
|
|
4560
|
+
function inferInputType(props) {
|
|
4561
|
+
if (props.secureTextEntry || isAppilotsSensitiveMarked(props)) return "password";
|
|
4562
|
+
switch (props.keyboardType) {
|
|
4563
|
+
case "email-address":
|
|
4564
|
+
return "email";
|
|
4565
|
+
case "numeric":
|
|
4566
|
+
case "number-pad":
|
|
4567
|
+
case "decimal-pad":
|
|
4568
|
+
return "number";
|
|
4569
|
+
case "phone-pad":
|
|
4570
|
+
return "phone";
|
|
4571
|
+
default:
|
|
4572
|
+
return "text";
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
function inferLabel(props) {
|
|
4576
|
+
return props.accessibilityLabel ?? props.label ?? props.placeholder ?? props.testID;
|
|
4577
|
+
}
|
|
4578
|
+
function resolveIdentity(props, label) {
|
|
4579
|
+
const declared = stringProp(props, "testID") ?? stringProp(props, "accessibilityLabel");
|
|
4580
|
+
if (declared) return { id: declared, provenance: "declared" };
|
|
4581
|
+
const derived = labelToId(label);
|
|
4582
|
+
if (derived) return { id: derived, provenance: "derived" };
|
|
4583
|
+
return { provenance: "positional" };
|
|
4584
|
+
}
|
|
4585
|
+
function isSwitchLike(fiber, props) {
|
|
4586
|
+
if (props?.accessibilityRole === "switch" || props?.role === "switch") return true;
|
|
4587
|
+
return matchesName(fiber, "Switch");
|
|
4588
|
+
}
|
|
4589
|
+
function switchValue(props) {
|
|
4590
|
+
if (typeof props?.value === "boolean") return props.value;
|
|
4591
|
+
const checked = props?.accessibilityState?.checked;
|
|
4592
|
+
return checked === true;
|
|
4593
|
+
}
|
|
4594
|
+
function nearbyLabelForSwitch(fiber) {
|
|
4595
|
+
let parent = fiber?.return;
|
|
4596
|
+
for (let depth = 0; parent && depth < 3; depth++, parent = parent.return) {
|
|
4597
|
+
const text = findChildText(parent);
|
|
4598
|
+
if (text && isReferenceable(text)) return text;
|
|
4599
|
+
}
|
|
4600
|
+
return void 0;
|
|
4601
|
+
}
|
|
4602
|
+
var ALWAYS_LIST_NAMES = /* @__PURE__ */ new Set([
|
|
4603
|
+
"FlatList",
|
|
4604
|
+
"SectionList",
|
|
4605
|
+
"VirtualizedList",
|
|
4606
|
+
// Shopify's high-perf FlatList replacement — same usage shape, same
|
|
4607
|
+
// virtualization, same per-item key.
|
|
4608
|
+
"FlashList"
|
|
4609
|
+
]);
|
|
4610
|
+
var HEURISTIC_LIST_NAMES = /* @__PURE__ */ new Set(["ScrollView"]);
|
|
4611
|
+
var LIST_CELL_NAMES = /* @__PURE__ */ new Set([
|
|
4612
|
+
"CellRenderer",
|
|
4613
|
+
"CellRendererComponent",
|
|
4614
|
+
"VirtualizedListCellContextProvider"
|
|
4615
|
+
]);
|
|
4616
|
+
function looksLikeListContainerByProps(props) {
|
|
4617
|
+
if (!props || typeof props !== "object") return false;
|
|
4618
|
+
if (typeof props.renderItem !== "function") return false;
|
|
4619
|
+
if (Array.isArray(props.sections)) return true;
|
|
4620
|
+
if (Array.isArray(props.data)) return true;
|
|
4621
|
+
return typeof props.getItemCount === "function" && typeof props.getItem === "function";
|
|
4622
|
+
}
|
|
4623
|
+
function isAlwaysListContainer(fiber) {
|
|
4624
|
+
for (const name of ALWAYS_LIST_NAMES) {
|
|
4625
|
+
if (matchesName(fiber, name)) return true;
|
|
4626
|
+
}
|
|
4627
|
+
return looksLikeListContainerByProps(fiber?.memoizedProps);
|
|
4628
|
+
}
|
|
4629
|
+
function isHeuristicListContainer(fiber) {
|
|
4630
|
+
for (const name of HEURISTIC_LIST_NAMES) {
|
|
4631
|
+
if (matchesName(fiber, name)) return true;
|
|
4632
|
+
}
|
|
4633
|
+
return false;
|
|
4634
|
+
}
|
|
4635
|
+
function isKnownListCell(fiber) {
|
|
4636
|
+
const typeName2 = getTypeName(fiber.type) ?? getTypeName(fiber.elementType);
|
|
4637
|
+
return !!typeName2 && LIST_CELL_NAMES.has(typeName2);
|
|
4638
|
+
}
|
|
4639
|
+
var MODAL_HOST_NAMES = /* @__PURE__ */ new Set(["RCTModalHostView", "ModalHostView"]);
|
|
4640
|
+
function isModalHostFiber(fiber) {
|
|
4641
|
+
const type = fiber?.type;
|
|
4642
|
+
return typeof type === "string" && MODAL_HOST_NAMES.has(type);
|
|
4643
|
+
}
|
|
4644
|
+
function isModalFiber(fiber) {
|
|
4645
|
+
return matchesName(fiber, "Modal") || isModalHostFiber(fiber);
|
|
4646
|
+
}
|
|
4647
|
+
function isVisibleModalFiber(fiber) {
|
|
4648
|
+
return isModalFiber(fiber) && fiber?.memoizedProps?.visible !== false;
|
|
4649
|
+
}
|
|
4650
|
+
function collectVisibleModalFibers(container, excludeRoots = []) {
|
|
4651
|
+
const excluded = /* @__PURE__ */ new WeakSet();
|
|
4652
|
+
for (const rootFiber of excludeRoots) {
|
|
4653
|
+
if (rootFiber) excluded.add(rootFiber);
|
|
4654
|
+
}
|
|
4655
|
+
const found = [];
|
|
4656
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4657
|
+
const stack = [];
|
|
4658
|
+
if (container?.child) stack.push(container.child);
|
|
4659
|
+
while (stack.length > 0) {
|
|
4660
|
+
const node = stack.pop();
|
|
4661
|
+
if (!node || visited.has(node)) continue;
|
|
4662
|
+
visited.add(node);
|
|
4663
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4664
|
+
if (excluded.has(node) || shouldSkipSubtree(node)) continue;
|
|
4665
|
+
if (isVisibleModalFiber(node)) {
|
|
4666
|
+
found.push(node);
|
|
4667
|
+
continue;
|
|
4668
|
+
}
|
|
4669
|
+
if (node.child) stack.push(node.child);
|
|
4670
|
+
}
|
|
4671
|
+
return found;
|
|
4672
|
+
}
|
|
4673
|
+
function isMarkedListItem(fiber, listId) {
|
|
4674
|
+
const props = fiber?.memoizedProps;
|
|
4675
|
+
if (!props || props.__appilotsListItem !== true) return false;
|
|
4676
|
+
if (!listId) return true;
|
|
4677
|
+
return props.__appilotsListId === listId;
|
|
4678
|
+
}
|
|
4679
|
+
function listIdFromProps(props) {
|
|
4680
|
+
return stringProp(props, "__appilotsListId") ?? stringProp(props, "appilotsListId") ?? // A ScrollView that turned out to hold keyed rows is BOTH: the
|
|
4681
|
+
// walker sees a list, auto-tracking registered a scrollable. Sharing
|
|
4682
|
+
// the id lets the two merge instead of describing one surface twice.
|
|
4683
|
+
stringProp(props, "__appilotsScrollId");
|
|
4684
|
+
}
|
|
4685
|
+
function explicitListIdFromProps(props) {
|
|
4686
|
+
return stringProp(props, "__appilotsListId") ?? stringProp(props, "appilotsListId");
|
|
4687
|
+
}
|
|
4688
|
+
function listMetadataFromProps(props) {
|
|
4689
|
+
const id = listIdFromProps(props);
|
|
4690
|
+
const itemCount = numberProp(props, "__appilotsItemCount");
|
|
4691
|
+
const label = stringProp(props, "__appilotsListLabel") ?? stringProp(props, "accessibilityLabel") ?? stringProp(props, "testID");
|
|
4692
|
+
const refreshing = props?.refreshing === true || props?.__appilotsRefreshing === true;
|
|
4693
|
+
const empty = props?.__appilotsEmpty === true || typeof itemCount === "number" && itemCount === 0;
|
|
4694
|
+
return {
|
|
4695
|
+
id,
|
|
4696
|
+
itemCount,
|
|
4697
|
+
refreshing,
|
|
4698
|
+
empty,
|
|
4699
|
+
label,
|
|
4700
|
+
source: id ? "auto-tracked" : void 0
|
|
4701
|
+
};
|
|
4702
|
+
}
|
|
4703
|
+
function hasRenderableContent(fiber) {
|
|
4704
|
+
const stack = [fiber];
|
|
4705
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4706
|
+
while (stack.length > 0) {
|
|
4707
|
+
const node = stack.pop();
|
|
4708
|
+
if (!node || visited.has(node)) continue;
|
|
4709
|
+
visited.add(node);
|
|
4710
|
+
if (shouldSkipSubtree(node) || isModalFiber(node)) continue;
|
|
4711
|
+
const props = node.memoizedProps ?? {};
|
|
4712
|
+
if (matchesName(node, "Text")) {
|
|
4713
|
+
const text = extractTextFromChildren(props.children);
|
|
4714
|
+
if (text && text.trim().length > 0) return true;
|
|
4715
|
+
}
|
|
4716
|
+
if (matchesName(node, "TextInput") || matchesName(node, "Switch")) {
|
|
4717
|
+
return true;
|
|
3994
4718
|
}
|
|
4719
|
+
if (TOUCHABLE_NAMES.has(getTypeName(node.type) ?? "") || TOUCHABLE_NAMES.has(getTypeName(node.elementType) ?? "")) {
|
|
4720
|
+
return true;
|
|
4721
|
+
}
|
|
4722
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4723
|
+
if (node.child) stack.push(node.child);
|
|
4724
|
+
}
|
|
4725
|
+
return false;
|
|
4726
|
+
}
|
|
4727
|
+
function findListCellsByType(container, minItems) {
|
|
4728
|
+
const cells = [];
|
|
4729
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4730
|
+
const stack = [];
|
|
4731
|
+
if (container?.child) stack.push(container.child);
|
|
4732
|
+
while (stack.length > 0) {
|
|
4733
|
+
const node = stack.pop();
|
|
4734
|
+
if (!node || visited.has(node)) continue;
|
|
4735
|
+
visited.add(node);
|
|
4736
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4737
|
+
if (shouldSkipSubtree(node) || isModalFiber(node)) continue;
|
|
4738
|
+
if (isAlwaysListContainer(node)) continue;
|
|
4739
|
+
if (isKnownListCell(node) && hasRenderableContent(node)) {
|
|
4740
|
+
cells.push(node);
|
|
4741
|
+
continue;
|
|
4742
|
+
}
|
|
4743
|
+
if (node.child) stack.push(node.child);
|
|
4744
|
+
}
|
|
4745
|
+
return cells.length >= minItems ? cells : null;
|
|
4746
|
+
}
|
|
4747
|
+
function findMarkedListItems(container, minItems, listId) {
|
|
4748
|
+
const items = [];
|
|
4749
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4750
|
+
const stack = [];
|
|
4751
|
+
if (container?.child) stack.push(container.child);
|
|
4752
|
+
while (stack.length > 0) {
|
|
4753
|
+
const node = stack.pop();
|
|
4754
|
+
if (!node || visited.has(node)) continue;
|
|
4755
|
+
visited.add(node);
|
|
4756
|
+
if (node.sibling) stack.push(node.sibling);
|
|
4757
|
+
if (shouldSkipSubtree(node) || isModalFiber(node)) continue;
|
|
4758
|
+
if (isMarkedListItem(node, listId)) {
|
|
4759
|
+
items.push(node);
|
|
4760
|
+
continue;
|
|
4761
|
+
}
|
|
4762
|
+
if (node.child) stack.push(node.child);
|
|
4763
|
+
}
|
|
4764
|
+
if (items.length < minItems) return null;
|
|
4765
|
+
items.sort((a, b) => {
|
|
4766
|
+
const ai = numberProp(a.memoizedProps, "__appilotsItemIndex");
|
|
4767
|
+
const bi = numberProp(b.memoizedProps, "__appilotsItemIndex");
|
|
4768
|
+
if (ai === void 0 && bi === void 0) return 0;
|
|
4769
|
+
if (ai === void 0) return 1;
|
|
4770
|
+
if (bi === void 0) return -1;
|
|
4771
|
+
return ai - bi;
|
|
3995
4772
|
});
|
|
4773
|
+
return items;
|
|
4774
|
+
}
|
|
4775
|
+
function hasAuthoredReactKey(fiber) {
|
|
4776
|
+
const key = fiber?.key;
|
|
4777
|
+
if (key === null || key === void 0 || key === "") return false;
|
|
4778
|
+
const s = String(key);
|
|
4779
|
+
if (s.startsWith(".") && !s.startsWith(".$")) return false;
|
|
4780
|
+
return true;
|
|
4781
|
+
}
|
|
4782
|
+
function findKeyedSiblingItems(container, minItems) {
|
|
4783
|
+
if (!container) return null;
|
|
4784
|
+
let current = container;
|
|
4785
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4786
|
+
while (current && !visited.has(current)) {
|
|
4787
|
+
visited.add(current);
|
|
4788
|
+
const siblings = [];
|
|
4789
|
+
let cur = current.child;
|
|
4790
|
+
while (cur) {
|
|
4791
|
+
if (!isModalFiber(cur)) siblings.push(cur);
|
|
4792
|
+
cur = cur.sibling;
|
|
4793
|
+
}
|
|
4794
|
+
if (siblings.length === 0) return null;
|
|
4795
|
+
const keyed = siblings.filter(hasAuthoredReactKey);
|
|
4796
|
+
if (keyed.length >= minItems) {
|
|
4797
|
+
return keyed;
|
|
4798
|
+
}
|
|
4799
|
+
if (siblings.length > 1) {
|
|
4800
|
+
return null;
|
|
4801
|
+
}
|
|
4802
|
+
current = siblings[0];
|
|
4803
|
+
}
|
|
4804
|
+
return null;
|
|
3996
4805
|
}
|
|
3997
|
-
function
|
|
4806
|
+
function findListItems(container, minItems = 2, preferCells = false) {
|
|
4807
|
+
if (!container) return null;
|
|
4808
|
+
const listId = listIdFromProps(container.memoizedProps);
|
|
4809
|
+
const markedItems = findMarkedListItems(container, minItems, listId);
|
|
4810
|
+
if (markedItems) return markedItems;
|
|
4811
|
+
if (preferCells) {
|
|
4812
|
+
const cells = findListCellsByType(container, minItems);
|
|
4813
|
+
if (cells) return cells;
|
|
4814
|
+
}
|
|
4815
|
+
if (minItems <= 1) {
|
|
4816
|
+
const multiItemLevel = findKeyedSiblingItems(container, 2);
|
|
4817
|
+
if (multiItemLevel) return multiItemLevel;
|
|
4818
|
+
}
|
|
4819
|
+
return findKeyedSiblingItems(container, minItems) ?? findListCellsByType(container, minItems);
|
|
4820
|
+
}
|
|
4821
|
+
var TOUCHABLE_NAMES = /* @__PURE__ */ new Set([
|
|
4822
|
+
"TouchableOpacity",
|
|
4823
|
+
"TouchableHighlight",
|
|
4824
|
+
"TouchableWithoutFeedback",
|
|
4825
|
+
"TouchableNativeFeedback",
|
|
4826
|
+
"Pressable"
|
|
4827
|
+
]);
|
|
4828
|
+
function hasPressHandler(fiber) {
|
|
4829
|
+
const props = fiber?.memoizedProps ?? {};
|
|
4830
|
+
return typeof props.onPress === "function" || typeof props.onPressIn === "function";
|
|
4831
|
+
}
|
|
4832
|
+
function isPressableTarget(fiber) {
|
|
4833
|
+
return TOUCHABLE_NAMES.has(getTypeName(fiber.type) ?? "") || TOUCHABLE_NAMES.has(getTypeName(fiber.elementType) ?? "") || hasPressHandler(fiber);
|
|
4834
|
+
}
|
|
4835
|
+
function emptyDetectorCounts() {
|
|
3998
4836
|
return {
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4837
|
+
Text: 0,
|
|
4838
|
+
TextInput: 0,
|
|
4839
|
+
Switch: 0,
|
|
4840
|
+
ActivityIndicator: 0,
|
|
4841
|
+
Modal: 0,
|
|
4842
|
+
adjustable: 0,
|
|
4843
|
+
pressable: 0,
|
|
4844
|
+
listContainers: 0
|
|
4007
4845
|
};
|
|
4008
4846
|
}
|
|
4009
|
-
function
|
|
4010
|
-
return
|
|
4011
|
-
AppilotsErrorBoundary,
|
|
4012
|
-
{
|
|
4013
|
-
surface: "provider",
|
|
4014
|
-
onError: reportProviderRenderFailure,
|
|
4015
|
-
fallback: /* @__PURE__ */ React2.createElement(AppilotsContext.Provider, { value: createDegradedContext(config ?? null) }, children)
|
|
4016
|
-
},
|
|
4017
|
-
/* @__PURE__ */ React2.createElement(AppilotsProviderInner, { config, client }, children)
|
|
4018
|
-
);
|
|
4847
|
+
function totalDetectorHits(counts) {
|
|
4848
|
+
return counts.Text + counts.TextInput + counts.Switch + counts.ActivityIndicator + counts.Modal + counts.adjustable + counts.pressable + counts.listContainers;
|
|
4019
4849
|
}
|
|
4020
|
-
function
|
|
4021
|
-
|
|
4850
|
+
function addDetectorCounts(into, from) {
|
|
4851
|
+
into.Text += from.Text;
|
|
4852
|
+
into.TextInput += from.TextInput;
|
|
4853
|
+
into.Switch += from.Switch;
|
|
4854
|
+
into.ActivityIndicator += from.ActivityIndicator;
|
|
4855
|
+
into.Modal += from.Modal;
|
|
4856
|
+
into.adjustable += from.adjustable;
|
|
4857
|
+
into.pressable += from.pressable;
|
|
4858
|
+
into.listContainers += from.listContainers;
|
|
4022
4859
|
}
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
return
|
|
4860
|
+
var _lastWalk = null;
|
|
4861
|
+
function getLastWalkDiagnostics() {
|
|
4862
|
+
return _lastWalk ? { ..._lastWalk, detectors: { ..._lastWalk.detectors } } : null;
|
|
4026
4863
|
}
|
|
4027
|
-
function
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
);
|
|
4864
|
+
function walkSubtree(root, detectLists, startInModal = false) {
|
|
4865
|
+
const result = {
|
|
4866
|
+
texts: [],
|
|
4867
|
+
inputs: [],
|
|
4868
|
+
buttons: [],
|
|
4869
|
+
toggles: [],
|
|
4870
|
+
sliders: [],
|
|
4871
|
+
visitedFibers: 0,
|
|
4872
|
+
skippedHidden: 0,
|
|
4873
|
+
loading: false,
|
|
4874
|
+
modalOpen: false,
|
|
4875
|
+
lists: [],
|
|
4876
|
+
detectors: emptyDetectorCounts()
|
|
4877
|
+
};
|
|
4878
|
+
if (!root) return result;
|
|
4879
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4880
|
+
const stack = [];
|
|
4881
|
+
if (root.child) stack.push({ fiber: root.child, inModal: startInModal });
|
|
4882
|
+
while (stack.length > 0) {
|
|
4883
|
+
const { fiber, inModal, ancestorPress } = stack.pop();
|
|
4884
|
+
if (!fiber || visited.has(fiber)) continue;
|
|
4885
|
+
visited.add(fiber);
|
|
4886
|
+
result.visitedFibers++;
|
|
4887
|
+
if (fiber.sibling) stack.push({ fiber: fiber.sibling, inModal });
|
|
4888
|
+
if (shouldSkipSubtree(fiber)) {
|
|
4889
|
+
result.skippedHidden++;
|
|
4890
|
+
continue;
|
|
4891
|
+
}
|
|
4892
|
+
if (detectLists) {
|
|
4893
|
+
const isAlways = isAlwaysListContainer(fiber);
|
|
4894
|
+
const isHeuristic = !isAlways && isHeuristicListContainer(fiber);
|
|
4895
|
+
if (isAlways || isHeuristic) {
|
|
4896
|
+
result.detectors.listContainers++;
|
|
4897
|
+
const minItems = isAlways ? 1 : 2;
|
|
4898
|
+
const items = findListItems(fiber, minItems, isAlways);
|
|
4899
|
+
if (items && items.length >= minItems) {
|
|
4900
|
+
const containerType = getTypeName(fiber.type) ?? getTypeName(fiber.elementType) ?? "List";
|
|
4901
|
+
const listIndex = result.lists.length;
|
|
4902
|
+
const itemSnapshots = items.map((itemFiber, i) => {
|
|
4903
|
+
const itemResult = walkSubtree(itemFiber, false, inModal);
|
|
4904
|
+
result.visitedFibers += itemResult.visitedFibers;
|
|
4905
|
+
result.skippedHidden += itemResult.skippedHidden;
|
|
4906
|
+
addDetectorCounts(result.detectors, itemResult.detectors);
|
|
4907
|
+
if (itemResult.loading) result.loading = true;
|
|
4908
|
+
if (itemResult.modalOpen) result.modalOpen = true;
|
|
4909
|
+
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;
|
|
4910
|
+
const itemKey = typeof itemFiber.memoizedProps?.__appilotsItemKey === "string" || typeof itemFiber.memoizedProps?.__appilotsItemKey === "number" ? String(itemFiber.memoizedProps.__appilotsItemKey) : void 0;
|
|
4911
|
+
const dataIndex = numberProp(itemFiber.memoizedProps, "__appilotsItemIndex");
|
|
4912
|
+
const syntheticId = `list-${listIndex}-item-${i + 1}`;
|
|
4913
|
+
return {
|
|
4914
|
+
index: i + 1,
|
|
4915
|
+
dataIndex,
|
|
4916
|
+
reactKey,
|
|
4917
|
+
itemKey,
|
|
4918
|
+
syntheticId,
|
|
4919
|
+
texts: dedupeStringsInternal(itemResult.texts),
|
|
4920
|
+
// Scope the row's ordinals by its own synthetic id, so an
|
|
4921
|
+
// unnamed control in row 3 cannot collide with one in row 1
|
|
4922
|
+
// or with a screen-level one.
|
|
4923
|
+
buttons: dedupeButtonsInternal(itemResult.buttons, `${syntheticId}-`),
|
|
4924
|
+
inputs: dedupeInputsInternal(itemResult.inputs, `${syntheticId}-`),
|
|
4925
|
+
toggles: dedupeTogglesInternal(itemResult.toggles, `${syntheticId}-`)
|
|
4926
|
+
};
|
|
4927
|
+
});
|
|
4928
|
+
const meta2 = listMetadataFromProps(fiber.memoizedProps);
|
|
4929
|
+
result.lists.push({
|
|
4930
|
+
index: listIndex,
|
|
4931
|
+
id: meta2.id,
|
|
4932
|
+
containerType,
|
|
4933
|
+
source: meta2.source ?? "fiber",
|
|
4934
|
+
itemCount: meta2.itemCount,
|
|
4935
|
+
visibleItemCount: itemSnapshots.length,
|
|
4936
|
+
refreshing: meta2.refreshing,
|
|
4937
|
+
empty: meta2.empty,
|
|
4938
|
+
label: meta2.label,
|
|
4939
|
+
items: itemSnapshots
|
|
4940
|
+
});
|
|
4941
|
+
for (const modalFiber of collectVisibleModalFibers(fiber, items)) {
|
|
4942
|
+
result.modalOpen = true;
|
|
4943
|
+
if (modalFiber.child) stack.push({ fiber: modalFiber.child, inModal: true });
|
|
4944
|
+
}
|
|
4945
|
+
continue;
|
|
4946
|
+
}
|
|
4947
|
+
const meta = listMetadataFromProps(fiber.memoizedProps);
|
|
4948
|
+
const declaresList = meta.itemCount !== void 0 || meta.refreshing || meta.empty || !!explicitListIdFromProps(fiber.memoizedProps);
|
|
4949
|
+
if (declaresList) {
|
|
4950
|
+
const containerType = getTypeName(fiber.type) ?? getTypeName(fiber.elementType) ?? "List";
|
|
4951
|
+
result.lists.push({
|
|
4952
|
+
index: result.lists.length,
|
|
4953
|
+
id: meta.id,
|
|
4954
|
+
containerType,
|
|
4955
|
+
source: meta.source ?? "auto-tracked",
|
|
4956
|
+
itemCount: meta.itemCount,
|
|
4957
|
+
visibleItemCount: 0,
|
|
4958
|
+
refreshing: meta.refreshing,
|
|
4959
|
+
empty: meta.empty,
|
|
4960
|
+
label: meta.label,
|
|
4961
|
+
items: []
|
|
4962
|
+
});
|
|
4963
|
+
for (const modalFiber of collectVisibleModalFibers(fiber)) {
|
|
4964
|
+
result.modalOpen = true;
|
|
4965
|
+
if (modalFiber.child) stack.push({ fiber: modalFiber.child, inModal: true });
|
|
4966
|
+
}
|
|
4967
|
+
continue;
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
const props = fiber.memoizedProps ?? {};
|
|
4972
|
+
if (matchesName(fiber, "Text")) {
|
|
4973
|
+
result.detectors.Text++;
|
|
4974
|
+
const text = extractTextFromChildren(props.children);
|
|
4975
|
+
if (text && text.trim().length > 0) {
|
|
4976
|
+
result.texts.push(text.trim());
|
|
4977
|
+
}
|
|
4978
|
+
} else if (matchesName(fiber, "TextInput")) {
|
|
4979
|
+
result.detectors.TextInput++;
|
|
4980
|
+
if (props.__appilotsInternal !== true) {
|
|
4981
|
+
const isSecure = !!props.secureTextEntry || isAppilotsSensitiveMarked(props);
|
|
4982
|
+
const rawValue = typeof props.value === "string" ? props.value : void 0;
|
|
4983
|
+
const safeValue = isSecure ? rawValue && rawValue.length > 0 ? "<hidden>" : void 0 : rawValue;
|
|
4984
|
+
const identity = resolveIdentity(props, inferLabel(props));
|
|
4985
|
+
const geom = readGeometry(fiber);
|
|
4986
|
+
result.inputs.push(
|
|
4987
|
+
withRect(
|
|
4988
|
+
{
|
|
4989
|
+
id: identity.id,
|
|
4990
|
+
provenance: identity.provenance,
|
|
4991
|
+
label: inferLabel(props),
|
|
4992
|
+
value: safeValue,
|
|
4993
|
+
placeholder: props.placeholder,
|
|
4994
|
+
editable: props.editable !== false,
|
|
4995
|
+
secure: isSecure,
|
|
4996
|
+
type: inferInputType(props),
|
|
4997
|
+
// A field whose return key submits (#398). On a search box or
|
|
4998
|
+
// a one-field login this IS the submit, and without the flag
|
|
4999
|
+
// the screen looks like a form with no way to send it.
|
|
5000
|
+
...typeof props.onSubmitEditing === "function" ? { submitsOnReturn: true } : {},
|
|
5001
|
+
...inModal ? { inModal: true } : {},
|
|
5002
|
+
...geom.fields
|
|
5003
|
+
},
|
|
5004
|
+
geom
|
|
5005
|
+
)
|
|
5006
|
+
);
|
|
5007
|
+
}
|
|
5008
|
+
} else if (props.accessibilityRole === "adjustable") {
|
|
5009
|
+
result.detectors.adjustable++;
|
|
5010
|
+
if (props.__appilotsInternal !== true) {
|
|
5011
|
+
const av = props.accessibilityValue ?? {};
|
|
5012
|
+
const sliderLabel = props.accessibilityLabel ?? props.testID;
|
|
5013
|
+
const sliderIdentity = resolveIdentity(props, sliderLabel);
|
|
5014
|
+
const geom = readGeometry(fiber);
|
|
5015
|
+
result.sliders.push(
|
|
5016
|
+
withRect(
|
|
5017
|
+
{
|
|
5018
|
+
id: sliderIdentity.id,
|
|
5019
|
+
provenance: sliderIdentity.provenance,
|
|
5020
|
+
label: sliderLabel,
|
|
5021
|
+
value: typeof av.now === "number" ? av.now : void 0,
|
|
5022
|
+
min: typeof av.min === "number" ? av.min : void 0,
|
|
5023
|
+
max: typeof av.max === "number" ? av.max : void 0,
|
|
5024
|
+
...inModal ? { inModal: true } : {},
|
|
5025
|
+
...geom.fields
|
|
5026
|
+
},
|
|
5027
|
+
geom
|
|
5028
|
+
)
|
|
5029
|
+
);
|
|
5030
|
+
}
|
|
5031
|
+
} else if (isSwitchLike(fiber, props)) {
|
|
5032
|
+
result.detectors.Switch++;
|
|
5033
|
+
if (props.__appilotsInternal !== true) {
|
|
5034
|
+
const label = props.accessibilityLabel ?? props.testID ?? nearbyLabelForSwitch(fiber);
|
|
5035
|
+
const toggleIdentity = resolveIdentity(props, label);
|
|
5036
|
+
const geom = readGeometry(fiber);
|
|
5037
|
+
result.toggles.push(
|
|
5038
|
+
withRect(
|
|
5039
|
+
{
|
|
5040
|
+
// Same fallback the branch below has always had for buttons.
|
|
5041
|
+
// Without it every unnamed switch arrived as `id: undefined`
|
|
5042
|
+
// and dedupeToggles merged them all into one (#385).
|
|
5043
|
+
id: toggleIdentity.id,
|
|
5044
|
+
provenance: toggleIdentity.provenance,
|
|
5045
|
+
label,
|
|
5046
|
+
value: switchValue(props),
|
|
5047
|
+
...inModal ? { inModal: true } : {},
|
|
5048
|
+
...geom.fields
|
|
5049
|
+
},
|
|
5050
|
+
geom
|
|
5051
|
+
)
|
|
5052
|
+
);
|
|
5053
|
+
}
|
|
5054
|
+
continue;
|
|
5055
|
+
} else if (isPressableTarget(fiber)) {
|
|
5056
|
+
result.detectors.pressable++;
|
|
5057
|
+
const isRepeatOfAncestor = typeof props.onPress === "function" && props.onPress === ancestorPress;
|
|
5058
|
+
if (props.__appilotsInternal !== true && !isRepeatOfAncestor) {
|
|
5059
|
+
const label = findChildText(fiber);
|
|
5060
|
+
const state = props.accessibilityState ?? {};
|
|
5061
|
+
const on = state.checked === true || state.selected === true;
|
|
5062
|
+
const buttonIdentity = resolveIdentity(props, label);
|
|
5063
|
+
const geom = readGeometry(fiber);
|
|
5064
|
+
result.buttons.push(
|
|
5065
|
+
withRect(
|
|
5066
|
+
{
|
|
5067
|
+
id: buttonIdentity.id,
|
|
5068
|
+
provenance: buttonIdentity.provenance,
|
|
5069
|
+
label,
|
|
5070
|
+
disabled: !!props.disabled,
|
|
5071
|
+
...on ? { selected: true } : {},
|
|
5072
|
+
...inModal ? { inModal: true } : {},
|
|
5073
|
+
...geom.fields
|
|
5074
|
+
},
|
|
5075
|
+
geom
|
|
5076
|
+
)
|
|
5077
|
+
);
|
|
5078
|
+
}
|
|
5079
|
+
if (fiber.child) {
|
|
5080
|
+
stack.push({
|
|
5081
|
+
fiber: fiber.child,
|
|
5082
|
+
inModal,
|
|
5083
|
+
// Children of this pressable inherit its handler, so a nested
|
|
5084
|
+
// fiber forwarding the SAME `onPress` is recognized as a repeat.
|
|
5085
|
+
ancestorPress: typeof props.onPress === "function" ? props.onPress : ancestorPress
|
|
5086
|
+
});
|
|
5087
|
+
}
|
|
5088
|
+
continue;
|
|
5089
|
+
} else if (matchesName(fiber, "ActivityIndicator")) {
|
|
5090
|
+
result.detectors.ActivityIndicator++;
|
|
5091
|
+
result.loading = true;
|
|
5092
|
+
} else if (isModalFiber(fiber)) {
|
|
5093
|
+
result.detectors.Modal++;
|
|
5094
|
+
result.modalOpen = true;
|
|
5095
|
+
if (fiber.child) stack.push({ fiber: fiber.child, inModal: true });
|
|
5096
|
+
continue;
|
|
5097
|
+
}
|
|
5098
|
+
if (fiber.child) stack.push({ fiber: fiber.child, inModal });
|
|
4047
5099
|
}
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
const
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
);
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
5100
|
+
return result;
|
|
5101
|
+
}
|
|
5102
|
+
function walkFiber(root) {
|
|
5103
|
+
const snapshot = {
|
|
5104
|
+
route: getCurrentScreen(),
|
|
5105
|
+
texts: [],
|
|
5106
|
+
inputs: [],
|
|
5107
|
+
buttons: [],
|
|
5108
|
+
toggles: [],
|
|
5109
|
+
// Sliders come from the ComponentRegistry (useAppilotsSlider) —
|
|
5110
|
+
// captureSnapshot merges them so observation only ever advertises
|
|
5111
|
+
// sliders the executor can actually set (min/max/step + setValue).
|
|
5112
|
+
sliders: [],
|
|
5113
|
+
loading: false,
|
|
5114
|
+
modalOpen: false,
|
|
5115
|
+
lists: [],
|
|
5116
|
+
choiceGroups: [],
|
|
5117
|
+
elements: [],
|
|
5118
|
+
stats: { visitedFibers: 0, skippedHidden: 0 }
|
|
5119
|
+
};
|
|
5120
|
+
if (!root) return snapshot;
|
|
5121
|
+
const sub = walkSubtree(root, true);
|
|
5122
|
+
snapshot.texts = dedupeStringsInternal(sub.texts);
|
|
5123
|
+
snapshot.inputs = dedupeInputsInternal(sub.inputs);
|
|
5124
|
+
snapshot.buttons = dedupeButtonsInternal(sub.buttons);
|
|
5125
|
+
snapshot.toggles = dedupeTogglesInternal(sub.toggles);
|
|
5126
|
+
snapshot.sliders = dedupeSliders(sub.sliders);
|
|
5127
|
+
snapshot.loading = sub.loading;
|
|
5128
|
+
snapshot.modalOpen = sub.modalOpen;
|
|
5129
|
+
snapshot.lists = sub.lists;
|
|
5130
|
+
snapshot.choiceGroups = deriveChoiceGroups(snapshot);
|
|
5131
|
+
snapshot.stats.visitedFibers = sub.visitedFibers;
|
|
5132
|
+
snapshot.stats.skippedHidden = sub.skippedHidden;
|
|
5133
|
+
snapshot.stats.identity = tallyIdentity(snapshot);
|
|
5134
|
+
_lastWalk = {
|
|
5135
|
+
visitedFibers: sub.visitedFibers,
|
|
5136
|
+
skippedHidden: sub.skippedHidden,
|
|
5137
|
+
detectors: sub.detectors
|
|
5138
|
+
};
|
|
5139
|
+
return snapshot;
|
|
5140
|
+
}
|
|
5141
|
+
function tallyIdentity(snapshot) {
|
|
5142
|
+
const tally = { declared: 0, derived: 0, positional: 0 };
|
|
5143
|
+
const count = (entries2) => {
|
|
5144
|
+
for (const entry of entries2) {
|
|
5145
|
+
if (entry.provenance) tally[entry.provenance] += 1;
|
|
5146
|
+
}
|
|
5147
|
+
};
|
|
5148
|
+
count(snapshot.inputs);
|
|
5149
|
+
count(snapshot.buttons);
|
|
5150
|
+
count(snapshot.toggles);
|
|
5151
|
+
count(snapshot.sliders);
|
|
5152
|
+
for (const list of snapshot.lists) {
|
|
5153
|
+
for (const item of list.items) {
|
|
5154
|
+
count(item.buttons);
|
|
5155
|
+
count(item.inputs);
|
|
5156
|
+
count(item.toggles);
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
return tally;
|
|
5160
|
+
}
|
|
5161
|
+
function dedupeButtonsInternal(buttons, scope = "") {
|
|
5162
|
+
return dedupeButtons(buttons, scope);
|
|
5163
|
+
}
|
|
5164
|
+
function dedupeInputsInternal(inputs, scope = "") {
|
|
5165
|
+
return dedupeInputs(inputs, scope);
|
|
5166
|
+
}
|
|
5167
|
+
function dedupeTogglesInternal(toggles, scope = "") {
|
|
5168
|
+
return dedupeToggles(toggles, scope);
|
|
5169
|
+
}
|
|
5170
|
+
function dedupeStringsInternal(strings) {
|
|
5171
|
+
return dedupeStrings(strings);
|
|
5172
|
+
}
|
|
5173
|
+
function isCommandLikeLabel(label) {
|
|
5174
|
+
if (!label) return true;
|
|
5175
|
+
const normalized = labelToId(label);
|
|
5176
|
+
if (!normalized) return true;
|
|
5177
|
+
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(
|
|
5178
|
+
normalized
|
|
4088
5179
|
);
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
5180
|
+
}
|
|
5181
|
+
function deriveChoiceGroups(snapshot) {
|
|
5182
|
+
const groups = [];
|
|
5183
|
+
for (const list of snapshot.lists) {
|
|
5184
|
+
const options = list.items.filter((item) => item.texts.length > 0 || item.buttons.length > 0).map((item, i) => {
|
|
5185
|
+
const texts = item.texts.length > 0 ? item.texts : item.buttons.map((b) => b.label ?? b.id ?? "").filter((text) => text.length > 0);
|
|
5186
|
+
return {
|
|
5187
|
+
index: i + 1,
|
|
5188
|
+
syntheticId: `${list.id ?? `list-${list.index}`}-choice-${i + 1}`,
|
|
5189
|
+
targetId: item.syntheticId,
|
|
5190
|
+
label: texts.slice(0, 2).join(" \xB7 "),
|
|
5191
|
+
texts,
|
|
5192
|
+
disabled: false
|
|
5193
|
+
};
|
|
4097
5194
|
});
|
|
4098
|
-
|
|
4099
|
-
|
|
5195
|
+
if (options.length > 0) {
|
|
5196
|
+
groups.push({
|
|
5197
|
+
index: groups.length,
|
|
5198
|
+
id: list.id,
|
|
5199
|
+
label: list.label,
|
|
5200
|
+
source: "list",
|
|
5201
|
+
options
|
|
5202
|
+
});
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
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) => {
|
|
5206
|
+
const label = button.label ?? button.id ?? "";
|
|
5207
|
+
return {
|
|
5208
|
+
index: i + 1,
|
|
5209
|
+
syntheticId: `choice-buttons-option-${i + 1}`,
|
|
5210
|
+
targetId: button.id ?? button.label,
|
|
5211
|
+
label,
|
|
5212
|
+
texts: [label],
|
|
5213
|
+
selected: !!button.selected,
|
|
5214
|
+
disabled: !!button.disabled
|
|
4100
5215
|
};
|
|
4101
|
-
}
|
|
4102
|
-
|
|
4103
|
-
(
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
5216
|
+
});
|
|
5217
|
+
if (buttonOptions.length >= 2) {
|
|
5218
|
+
groups.push({
|
|
5219
|
+
index: groups.length,
|
|
5220
|
+
id: "visible-options",
|
|
5221
|
+
label: "Visible options",
|
|
5222
|
+
source: "buttons",
|
|
5223
|
+
options: buttonOptions
|
|
5224
|
+
});
|
|
5225
|
+
}
|
|
5226
|
+
return groups;
|
|
4107
5227
|
}
|
|
4108
|
-
function
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
5228
|
+
function positionalId(scope, kind, ordinal) {
|
|
5229
|
+
return `${scope}${kind}-${ordinal}`;
|
|
5230
|
+
}
|
|
5231
|
+
function dedupeButtons(buttons, scope = "") {
|
|
5232
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5233
|
+
const out = [];
|
|
5234
|
+
let unnamed = 0;
|
|
5235
|
+
for (const b of buttons) {
|
|
5236
|
+
const idTrim = b.id?.trim();
|
|
5237
|
+
const labelTrim = b.label?.trim();
|
|
5238
|
+
if (!idTrim && !isReferenceable(labelTrim)) {
|
|
5239
|
+
unnamed += 1;
|
|
5240
|
+
out.push({ ...b, id: positionalId(scope, "btn", unnamed), provenance: "positional" });
|
|
5241
|
+
continue;
|
|
5242
|
+
}
|
|
5243
|
+
const key = `${idTrim ?? ""}|${labelTrim ?? ""}|${b.disabled ? 1 : 0}|${b.inModal ? 1 : 0}|${b.selected ? 1 : 0}`;
|
|
5244
|
+
if (seen.has(key)) continue;
|
|
5245
|
+
seen.add(key);
|
|
5246
|
+
out.push(b);
|
|
4112
5247
|
}
|
|
4113
|
-
return
|
|
5248
|
+
return out;
|
|
5249
|
+
}
|
|
5250
|
+
function dedupeInputs(inputs, scope = "") {
|
|
5251
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5252
|
+
const out = [];
|
|
5253
|
+
let unnamed = 0;
|
|
5254
|
+
for (const i of inputs) {
|
|
5255
|
+
if (!i.id?.trim() && !i.label?.trim() && !i.placeholder?.trim()) {
|
|
5256
|
+
unnamed += 1;
|
|
5257
|
+
out.push({ ...i, id: positionalId(scope, "input", unnamed), provenance: "positional" });
|
|
5258
|
+
continue;
|
|
5259
|
+
}
|
|
5260
|
+
const key = `${i.id ?? ""}|${i.label ?? ""}|${i.placeholder ?? ""}|${i.inModal ? 1 : 0}`;
|
|
5261
|
+
if (seen.has(key)) continue;
|
|
5262
|
+
seen.add(key);
|
|
5263
|
+
out.push(i);
|
|
5264
|
+
}
|
|
5265
|
+
return out;
|
|
5266
|
+
}
|
|
5267
|
+
function dedupeSliders(sliders, scope = "") {
|
|
5268
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5269
|
+
const out = [];
|
|
5270
|
+
let unnamed = 0;
|
|
5271
|
+
for (const s of sliders) {
|
|
5272
|
+
if (!s.id?.trim() && !s.label?.trim()) {
|
|
5273
|
+
unnamed += 1;
|
|
5274
|
+
out.push({ ...s, id: positionalId(scope, "slider", unnamed), provenance: "positional" });
|
|
5275
|
+
continue;
|
|
5276
|
+
}
|
|
5277
|
+
const key = `${s.id ?? ""}|${s.label ?? ""}|${s.inModal ? 1 : 0}`;
|
|
5278
|
+
if (seen.has(key)) continue;
|
|
5279
|
+
seen.add(key);
|
|
5280
|
+
out.push(s);
|
|
5281
|
+
}
|
|
5282
|
+
return out;
|
|
5283
|
+
}
|
|
5284
|
+
function dedupeToggles(toggles, scope = "") {
|
|
5285
|
+
let unnamed = 0;
|
|
5286
|
+
return toggles.map((t) => {
|
|
5287
|
+
if (t.id?.trim()) return t;
|
|
5288
|
+
unnamed += 1;
|
|
5289
|
+
return { ...t, id: positionalId(scope, "toggle", unnamed), provenance: "positional" };
|
|
5290
|
+
});
|
|
5291
|
+
}
|
|
5292
|
+
function dedupeStrings(strings) {
|
|
5293
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5294
|
+
const out = [];
|
|
5295
|
+
for (const s of strings) {
|
|
5296
|
+
const trimmed = s.trim();
|
|
5297
|
+
if (!trimmed) continue;
|
|
5298
|
+
if (seen.has(trimmed)) continue;
|
|
5299
|
+
seen.add(trimmed);
|
|
5300
|
+
out.push(trimmed);
|
|
5301
|
+
}
|
|
5302
|
+
return out;
|
|
4114
5303
|
}
|
|
4115
5304
|
|
|
4116
5305
|
// src/introspection/probeLoadingState.ts
|
|
4117
|
-
function
|
|
5306
|
+
function getTypeName2(type) {
|
|
4118
5307
|
if (!type) return null;
|
|
4119
5308
|
if (typeof type === "string") return type;
|
|
4120
5309
|
if (typeof type === "function") return type.displayName ?? type.name ?? null;
|
|
4121
5310
|
if (typeof type === "object") {
|
|
4122
5311
|
if (type.displayName) return type.displayName;
|
|
4123
5312
|
if (type.render) return type.render.displayName ?? type.render.name ?? null;
|
|
4124
|
-
if (type.type) return
|
|
5313
|
+
if (type.type) return getTypeName2(type.type);
|
|
4125
5314
|
}
|
|
4126
5315
|
return null;
|
|
4127
5316
|
}
|
|
4128
|
-
function
|
|
4129
|
-
return
|
|
5317
|
+
function matchesName2(fiber, name) {
|
|
5318
|
+
return getTypeName2(fiber.type) === name || getTypeName2(fiber.elementType) === name;
|
|
4130
5319
|
}
|
|
4131
5320
|
var SKELETON_NAME_RE = /skeleton|shimmer|contentloader/i;
|
|
4132
5321
|
function looksLikeSkeleton(fiber) {
|
|
4133
|
-
const name =
|
|
5322
|
+
const name = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
|
|
4134
5323
|
return !!name && SKELETON_NAME_RE.test(name);
|
|
4135
5324
|
}
|
|
4136
|
-
var
|
|
5325
|
+
var SELF_REFERENTIAL_COMPONENTS2 = /* @__PURE__ */ new Set([
|
|
4137
5326
|
"AppilotsChat",
|
|
4138
5327
|
"AppilotsChatInner",
|
|
4139
5328
|
"ActionBreadcrumb",
|
|
4140
5329
|
"ConfirmDialog"
|
|
4141
5330
|
]);
|
|
4142
|
-
function
|
|
5331
|
+
function shouldSkipSubtree2(fiber) {
|
|
4143
5332
|
const props = fiber.memoizedProps;
|
|
4144
5333
|
if (!props) return false;
|
|
4145
|
-
const typeName2 =
|
|
4146
|
-
if (typeName2 &&
|
|
5334
|
+
const typeName2 = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
|
|
5335
|
+
if (typeName2 && SELF_REFERENTIAL_COMPONENTS2.has(typeName2)) return true;
|
|
4147
5336
|
if (props.__appilotsSkip === true || props["data-appilots-skip"] === true || props.appilotsSkip === true) {
|
|
4148
5337
|
return true;
|
|
4149
5338
|
}
|
|
4150
|
-
if (
|
|
4151
|
-
if (
|
|
5339
|
+
if (isModalFiber(fiber) && props.visible === false) return true;
|
|
5340
|
+
if (matchesName2(fiber, "Screen") && props.activityState === 0) return true;
|
|
4152
5341
|
const style = props.style;
|
|
4153
5342
|
if (style) {
|
|
4154
5343
|
const flat = Array.isArray(style) ? Object.assign({}, ...style.filter(Boolean)) : style;
|
|
@@ -4157,7 +5346,7 @@ function shouldSkipSubtree(fiber) {
|
|
|
4157
5346
|
}
|
|
4158
5347
|
return false;
|
|
4159
5348
|
}
|
|
4160
|
-
var
|
|
5349
|
+
var TOUCHABLE_NAMES2 = /* @__PURE__ */ new Set([
|
|
4161
5350
|
"TouchableOpacity",
|
|
4162
5351
|
"TouchableHighlight",
|
|
4163
5352
|
"TouchableWithoutFeedback",
|
|
@@ -4165,7 +5354,7 @@ var TOUCHABLE_NAMES = /* @__PURE__ */ new Set([
|
|
|
4165
5354
|
"Pressable"
|
|
4166
5355
|
]);
|
|
4167
5356
|
function isTouchable(fiber) {
|
|
4168
|
-
return
|
|
5357
|
+
return TOUCHABLE_NAMES2.has(getTypeName2(fiber.type) ?? "") || TOUCHABLE_NAMES2.has(getTypeName2(fiber.elementType) ?? "");
|
|
4169
5358
|
}
|
|
4170
5359
|
function pickId(props) {
|
|
4171
5360
|
return props?.testID ?? props?.accessibilityLabel ?? void 0;
|
|
@@ -4201,31 +5390,34 @@ function probeLoadingState(pressedComponentId, route) {
|
|
|
4201
5390
|
visited.add(fiber);
|
|
4202
5391
|
result.visitedFibers++;
|
|
4203
5392
|
if (fiber.sibling) stack.push(fiber.sibling);
|
|
4204
|
-
if (
|
|
5393
|
+
if (shouldSkipSubtree2(fiber)) continue;
|
|
4205
5394
|
const props = fiber.memoizedProps ?? {};
|
|
4206
|
-
if (
|
|
5395
|
+
if (matchesName2(fiber, "ActivityIndicator") || looksLikeSkeleton(fiber)) {
|
|
4207
5396
|
result.loading = true;
|
|
4208
|
-
} else if (
|
|
5397
|
+
} else if (isModalFiber(fiber)) {
|
|
4209
5398
|
result.modalOpen = true;
|
|
5399
|
+
} else if (isSwitchLike(fiber, props)) {
|
|
5400
|
+
const id = pickId(props);
|
|
5401
|
+
toggleSigs.push(`${id ?? "?"}:${switchValue(props) ? 1 : 0}`);
|
|
4210
5402
|
} else if (isTouchable(fiber)) {
|
|
4211
5403
|
if (props.__appilotsInternal !== true) {
|
|
4212
5404
|
const id = pickId(props);
|
|
4213
5405
|
const disabled = !!props.disabled;
|
|
4214
|
-
|
|
5406
|
+
const state = props.accessibilityState ?? {};
|
|
5407
|
+
const on = state.checked === true || state.selected === true;
|
|
5408
|
+
buttonSigs.push(`${id ?? "?"}:${disabled ? 1 : 0}:${on ? 1 : 0}`);
|
|
4215
5409
|
if (pressedComponentId && idMatches(id, pressedComponentId)) {
|
|
4216
5410
|
result.pressedFound = true;
|
|
4217
5411
|
if (disabled) result.pressedDisabled = true;
|
|
4218
5412
|
}
|
|
4219
5413
|
}
|
|
4220
|
-
} else if (
|
|
5414
|
+
} else if (matchesName2(fiber, "TextInput")) {
|
|
4221
5415
|
if (props.__appilotsInternal !== true) {
|
|
4222
5416
|
const id = pickId(props);
|
|
4223
5417
|
const editable = props.editable !== false;
|
|
4224
|
-
|
|
5418
|
+
const filled = typeof props.value === "string" && props.value.length > 0;
|
|
5419
|
+
inputSigs.push(`${id ?? "?"}:${editable ? 1 : 0}:${filled ? 1 : 0}`);
|
|
4225
5420
|
}
|
|
4226
|
-
} else if (matchesName(fiber, "Switch")) {
|
|
4227
|
-
const id = pickId(props);
|
|
4228
|
-
toggleSigs.push(`${id ?? "?"}:${props.value ? 1 : 0}`);
|
|
4229
5421
|
}
|
|
4230
5422
|
if (fiber.child) stack.push(fiber.child);
|
|
4231
5423
|
}
|
|
@@ -4236,11 +5428,48 @@ function probeLoadingState(pressedComponentId, route) {
|
|
|
4236
5428
|
return result;
|
|
4237
5429
|
}
|
|
4238
5430
|
|
|
5431
|
+
// ../shared/dist/routes/index.mjs
|
|
5432
|
+
var CONTAINER_SUFFIXES = [
|
|
5433
|
+
"screen",
|
|
5434
|
+
"route",
|
|
5435
|
+
"page",
|
|
5436
|
+
"view",
|
|
5437
|
+
"tab",
|
|
5438
|
+
"stack",
|
|
5439
|
+
"drawer",
|
|
5440
|
+
"navigator"
|
|
5441
|
+
];
|
|
5442
|
+
var CONTENT_SUFFIXES = ["list", "details", "detail", "create", "edit", "new", "form", "index"];
|
|
5443
|
+
var MIN_STEM_LENGTH = 3;
|
|
5444
|
+
function fold(name) {
|
|
5445
|
+
return name.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
5446
|
+
}
|
|
5447
|
+
function stripSuffixes(folded, suffixes) {
|
|
5448
|
+
for (const suffix of suffixes) {
|
|
5449
|
+
if (!folded.endsWith(suffix)) continue;
|
|
5450
|
+
const stem = folded.slice(0, -suffix.length);
|
|
5451
|
+
if (stem.length >= MIN_STEM_LENGTH) return stem;
|
|
5452
|
+
}
|
|
5453
|
+
return folded;
|
|
5454
|
+
}
|
|
5455
|
+
function normalizeRouteFamily(name) {
|
|
5456
|
+
const all = [...CONTAINER_SUFFIXES, ...CONTENT_SUFFIXES];
|
|
5457
|
+
let stem = stripSuffixes(stripSuffixes(fold(name), all), all);
|
|
5458
|
+
if (stem.endsWith("s") && stem.length - 1 >= MIN_STEM_LENGTH) stem = stem.slice(0, -1);
|
|
5459
|
+
return stem;
|
|
5460
|
+
}
|
|
5461
|
+
function routesBelongToSameFeature(a, b) {
|
|
5462
|
+
const na = normalizeRouteFamily(a);
|
|
5463
|
+
const nb = normalizeRouteFamily(b);
|
|
5464
|
+
if (!na || !nb) return false;
|
|
5465
|
+
return na === nb;
|
|
5466
|
+
}
|
|
5467
|
+
|
|
4239
5468
|
// src/navigation/navigationState.ts
|
|
4240
5469
|
var _currentScreen = null;
|
|
4241
5470
|
var _navigationRef = null;
|
|
4242
5471
|
function setCurrentScreen(screen) {
|
|
4243
|
-
|
|
5472
|
+
appilotsDebugLog(`setCurrentScreen: "${_currentScreen}" \u2192 "${screen}"`);
|
|
4244
5473
|
_currentScreen = screen;
|
|
4245
5474
|
}
|
|
4246
5475
|
function getCurrentScreen() {
|
|
@@ -4249,8 +5478,8 @@ function getCurrentScreen() {
|
|
|
4249
5478
|
const deepest = activePath[activePath.length - 1] ?? null;
|
|
4250
5479
|
if (deepest && deepest !== _currentScreen) {
|
|
4251
5480
|
if (_currentScreen) {
|
|
4252
|
-
|
|
4253
|
-
`
|
|
5481
|
+
appilotsDebugLog(
|
|
5482
|
+
`getCurrentScreen: active path differs from cached \u2014 deepest="${deepest}", cached="${_currentScreen}". Updating cache.`
|
|
4254
5483
|
);
|
|
4255
5484
|
}
|
|
4256
5485
|
_currentScreen = deepest;
|
|
@@ -4261,7 +5490,9 @@ function getCurrentScreen() {
|
|
|
4261
5490
|
const liveScreen = getActiveRouteFromRef(_navigationRef);
|
|
4262
5491
|
if (liveScreen) {
|
|
4263
5492
|
if (liveScreen !== _currentScreen) {
|
|
4264
|
-
|
|
5493
|
+
appilotsDebugLog(
|
|
5494
|
+
`getCurrentScreen: live reading differs from cached \u2014 live="${liveScreen}", cached="${_currentScreen}". Updating cache.`
|
|
5495
|
+
);
|
|
4265
5496
|
_currentScreen = liveScreen;
|
|
4266
5497
|
}
|
|
4267
5498
|
return liveScreen;
|
|
@@ -4305,7 +5536,9 @@ function getActiveRouteNames() {
|
|
|
4305
5536
|
function routeNamesAtState(state) {
|
|
4306
5537
|
if (!state) return [];
|
|
4307
5538
|
if (Array.isArray(state.routeNames)) {
|
|
4308
|
-
return state.routeNames.filter(
|
|
5539
|
+
return state.routeNames.filter(
|
|
5540
|
+
(name) => typeof name === "string" && name.length > 0
|
|
5541
|
+
);
|
|
4309
5542
|
}
|
|
4310
5543
|
if (Array.isArray(state.routes)) {
|
|
4311
5544
|
return state.routes.map((route) => route?.name).filter((name) => typeof name === "string" && name.length > 0);
|
|
@@ -4422,16 +5655,6 @@ function getActiveRouteFromRef(navRef) {
|
|
|
4422
5655
|
}
|
|
4423
5656
|
}
|
|
4424
5657
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4425
|
-
function normalizeRouteFamily(name) {
|
|
4426
|
-
return name.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/(tab|list|screen|details?|navigator)$/i, "");
|
|
4427
|
-
}
|
|
4428
|
-
function routesBelongToSameFeature(a, b) {
|
|
4429
|
-
const na = normalizeRouteFamily(a);
|
|
4430
|
-
const nb = normalizeRouteFamily(b);
|
|
4431
|
-
if (!na || !nb) return false;
|
|
4432
|
-
if (na === nb) return true;
|
|
4433
|
-
return na.startsWith(nb) || nb.startsWith(na);
|
|
4434
|
-
}
|
|
4435
5658
|
function navigationTargetReached(target, fromScreen) {
|
|
4436
5659
|
const cur = getCurrentScreen();
|
|
4437
5660
|
const path = getActiveRouteNames();
|
|
@@ -4607,6 +5830,365 @@ async function waitForLoadingSettle(opts = {}) {
|
|
|
4607
5830
|
};
|
|
4608
5831
|
}
|
|
4609
5832
|
|
|
5833
|
+
// src/version.ts
|
|
5834
|
+
var SDK_VERSION2 = "0.10.0";
|
|
5835
|
+
var FiberSentinel = class extends React2.Component {
|
|
5836
|
+
componentDidMount() {
|
|
5837
|
+
const fiber = this._reactInternals ?? this._reactInternalFiber ?? null;
|
|
5838
|
+
if (!fiber) {
|
|
5839
|
+
const reactVersion = React2.version ?? null;
|
|
5840
|
+
appilotsDebugWarn(
|
|
5841
|
+
`FiberSentinel: this._reactInternals is undefined; snapshot capture will fall back to render-time owner. React version may be incompatible (react=${reactVersion}).`
|
|
5842
|
+
);
|
|
5843
|
+
recordIntrospectionFailure("sentinel-missing-internals", reactVersion);
|
|
5844
|
+
this.props.onUnavailable?.({ reactVersion });
|
|
5845
|
+
return;
|
|
5846
|
+
}
|
|
5847
|
+
let top = fiber;
|
|
5848
|
+
while (top.return) top = top.return;
|
|
5849
|
+
setFiberRoot(top);
|
|
5850
|
+
}
|
|
5851
|
+
render() {
|
|
5852
|
+
return this.props.children ?? null;
|
|
5853
|
+
}
|
|
5854
|
+
};
|
|
5855
|
+
function AppilotsFiberRoot({
|
|
5856
|
+
children,
|
|
5857
|
+
onUnavailable
|
|
5858
|
+
}) {
|
|
5859
|
+
const Internals = React2.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
|
|
5860
|
+
const ownerFiber = Internals?.ReactCurrentOwner?.current;
|
|
5861
|
+
if (ownerFiber && !getFiberRoot()) {
|
|
5862
|
+
setFiberRoot(ownerFiber);
|
|
5863
|
+
}
|
|
5864
|
+
return /* @__PURE__ */ React2.createElement(FiberSentinel, { onUnavailable }, children);
|
|
5865
|
+
}
|
|
5866
|
+
|
|
5867
|
+
// src/debug/logger.ts
|
|
5868
|
+
setAppilotsDebugResolver(() => getGlobalConfig()?.debug === true);
|
|
5869
|
+
function setAppilotsDebugEnabled2(enabled) {
|
|
5870
|
+
setAppilotsDebugEnabled(enabled);
|
|
5871
|
+
}
|
|
5872
|
+
|
|
5873
|
+
// src/platform/nativeDialogTracking.ts
|
|
5874
|
+
var DEFAULT_BUTTON_LABEL = "OK";
|
|
5875
|
+
var ANSWERED_DIALOG_MEMORY_MS = 15e3;
|
|
5876
|
+
var _open = null;
|
|
5877
|
+
var _lastAnswered = null;
|
|
5878
|
+
var _counter = 0;
|
|
5879
|
+
var _installed = null;
|
|
5880
|
+
function getOpenNativeDialog() {
|
|
5881
|
+
return _open;
|
|
5882
|
+
}
|
|
5883
|
+
function getLastAnsweredNativeDialog() {
|
|
5884
|
+
if (!_lastAnswered) return null;
|
|
5885
|
+
if (Date.now() - _lastAnswered.at > ANSWERED_DIALOG_MEMORY_MS) return null;
|
|
5886
|
+
return _lastAnswered;
|
|
5887
|
+
}
|
|
5888
|
+
function labelOf(button, index) {
|
|
5889
|
+
const text = typeof button?.text === "string" ? button.text.trim() : "";
|
|
5890
|
+
if (text.length > 0) return text;
|
|
5891
|
+
return index === 0 ? DEFAULT_BUTTON_LABEL : `Button ${index + 1}`;
|
|
5892
|
+
}
|
|
5893
|
+
function installNativeDialogTracking(host) {
|
|
5894
|
+
if (_installed) return;
|
|
5895
|
+
const original = host.alert;
|
|
5896
|
+
const patched = (title, message, buttons, options) => {
|
|
5897
|
+
const id = `native-dialog-${_counter += 1}`;
|
|
5898
|
+
const source = Array.isArray(buttons) && buttons.length > 0 ? buttons : [{ text: DEFAULT_BUTTON_LABEL }];
|
|
5899
|
+
const close = (dialogId, answeredWith) => {
|
|
5900
|
+
if (_open?.id !== dialogId) return;
|
|
5901
|
+
_lastAnswered = {
|
|
5902
|
+
id: dialogId,
|
|
5903
|
+
...tracked.title ? { title: tracked.title } : {},
|
|
5904
|
+
buttons: tracked.buttons.map((button) => button.label),
|
|
5905
|
+
...answeredWith ? { answeredWith } : {},
|
|
5906
|
+
at: Date.now()
|
|
5907
|
+
};
|
|
5908
|
+
_open = null;
|
|
5909
|
+
};
|
|
5910
|
+
const tracked = {
|
|
5911
|
+
id,
|
|
5912
|
+
title: typeof title === "string" && title.trim().length > 0 ? title.trim() : void 0,
|
|
5913
|
+
message: typeof message === "string" && message.trim().length > 0 ? message.trim() : void 0,
|
|
5914
|
+
buttons: []
|
|
5915
|
+
};
|
|
5916
|
+
const wrapped = source.map((button, index) => ({
|
|
5917
|
+
...button,
|
|
5918
|
+
onPress: (value) => {
|
|
5919
|
+
close(id, labelOf(button, index));
|
|
5920
|
+
button.onPress?.(value);
|
|
5921
|
+
}
|
|
5922
|
+
}));
|
|
5923
|
+
tracked.buttons = source.map((button, index) => ({
|
|
5924
|
+
label: labelOf(button, index),
|
|
5925
|
+
...button.style ? { style: button.style } : {},
|
|
5926
|
+
press: () => {
|
|
5927
|
+
wrapped[index]?.onPress?.();
|
|
5928
|
+
}
|
|
5929
|
+
}));
|
|
5930
|
+
_open = tracked;
|
|
5931
|
+
const opts = options;
|
|
5932
|
+
const patchedOptions = opts && typeof opts === "object" ? {
|
|
5933
|
+
...opts,
|
|
5934
|
+
onDismiss: () => {
|
|
5935
|
+
close(id);
|
|
5936
|
+
opts.onDismiss?.();
|
|
5937
|
+
}
|
|
5938
|
+
} : options;
|
|
5939
|
+
return original(title, message, wrapped, patchedOptions);
|
|
5940
|
+
};
|
|
5941
|
+
host.alert = patched;
|
|
5942
|
+
_installed = { host, original, patched };
|
|
5943
|
+
}
|
|
5944
|
+
function uninstallNativeDialogTracking() {
|
|
5945
|
+
if (!_installed) return;
|
|
5946
|
+
if (_installed.host.alert === _installed.patched) {
|
|
5947
|
+
_installed.host.alert = _installed.original;
|
|
5948
|
+
}
|
|
5949
|
+
_installed = null;
|
|
5950
|
+
_open = null;
|
|
5951
|
+
_lastAnswered = null;
|
|
5952
|
+
}
|
|
5953
|
+
|
|
5954
|
+
// src/introspection/nameMangling.ts
|
|
5955
|
+
var _lastProbe = null;
|
|
5956
|
+
function getNameManglingProbe() {
|
|
5957
|
+
return _lastProbe;
|
|
5958
|
+
}
|
|
5959
|
+
function probeNameMangling(components) {
|
|
5960
|
+
const observed = {};
|
|
5961
|
+
let mangled = false;
|
|
5962
|
+
for (const [expected, component] of Object.entries(components)) {
|
|
5963
|
+
if (component === void 0) continue;
|
|
5964
|
+
const actual = getTypeName(component) ?? null;
|
|
5965
|
+
observed[expected] = actual;
|
|
5966
|
+
if (actual !== expected) mangled = true;
|
|
5967
|
+
}
|
|
5968
|
+
_lastProbe = { mangled, observed };
|
|
5969
|
+
if (mangled) {
|
|
5970
|
+
recordIntrospectionFailure("names-mangled", null);
|
|
5971
|
+
console.warn(
|
|
5972
|
+
"[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."
|
|
5973
|
+
);
|
|
5974
|
+
}
|
|
5975
|
+
return _lastProbe;
|
|
5976
|
+
}
|
|
5977
|
+
|
|
5978
|
+
// src/platform/fabricGeometry.ts
|
|
5979
|
+
function readRect(instance) {
|
|
5980
|
+
const candidate = instance;
|
|
5981
|
+
const read = candidate?.getBoundingClientRect ?? candidate?.unstable_getBoundingClientRect;
|
|
5982
|
+
if (typeof read !== "function") return null;
|
|
5983
|
+
const rect = read.call(candidate);
|
|
5984
|
+
if (!rect) return null;
|
|
5985
|
+
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
5986
|
+
}
|
|
5987
|
+
function createFabricGeometryHost(dimensions) {
|
|
5988
|
+
const fabric = globalThis.nativeFabricUIManager;
|
|
5989
|
+
if (!fabric) return null;
|
|
5990
|
+
return {
|
|
5991
|
+
get window() {
|
|
5992
|
+
return dimensions.get("window");
|
|
5993
|
+
},
|
|
5994
|
+
rectOf: readRect
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
var AppilotsErrorBoundary = class extends React2.Component {
|
|
5998
|
+
state = { degraded: false, escalate: null };
|
|
5999
|
+
/**
|
|
6000
|
+
* Instance field rather than state: `componentDidCatch` needs to know
|
|
6001
|
+
* whether THIS is the first failure, and by the time it runs
|
|
6002
|
+
* `getDerivedStateFromError` has already flipped `state.degraded`.
|
|
6003
|
+
*/
|
|
6004
|
+
hasDegradedOnce = false;
|
|
6005
|
+
static getDerivedStateFromError() {
|
|
6006
|
+
return { degraded: true };
|
|
6007
|
+
}
|
|
6008
|
+
componentDidCatch(error, info) {
|
|
6009
|
+
if (this.hasDegradedOnce) {
|
|
6010
|
+
this.setState({ escalate: error ?? new Error("Appilots: re-thrown host render error") });
|
|
6011
|
+
return;
|
|
6012
|
+
}
|
|
6013
|
+
this.hasDegradedOnce = true;
|
|
6014
|
+
console.error(
|
|
6015
|
+
`[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`,
|
|
6016
|
+
error
|
|
6017
|
+
);
|
|
6018
|
+
try {
|
|
6019
|
+
this.props.onError?.(error, info?.componentStack ?? null);
|
|
6020
|
+
} catch {
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
render() {
|
|
6024
|
+
if (this.state.escalate !== null) throw this.state.escalate;
|
|
6025
|
+
if (this.state.degraded) return this.props.fallback;
|
|
6026
|
+
return this.props.children;
|
|
6027
|
+
}
|
|
6028
|
+
};
|
|
6029
|
+
|
|
6030
|
+
// src/context/AppilotsProvider.tsx
|
|
6031
|
+
var AppilotsContext = createContext(null);
|
|
6032
|
+
function createInertClient() {
|
|
6033
|
+
const unavailable = () => Promise.reject(new Error("Appilots is unavailable: the SDK degraded after a render error."));
|
|
6034
|
+
return new Proxy({}, {
|
|
6035
|
+
get: (_target, prop) => {
|
|
6036
|
+
if (prop === "then" || typeof prop === "symbol") return void 0;
|
|
6037
|
+
return unavailable;
|
|
6038
|
+
}
|
|
6039
|
+
});
|
|
6040
|
+
}
|
|
6041
|
+
function createDegradedContext(config) {
|
|
6042
|
+
return {
|
|
6043
|
+
config: config ?? { projectId: "" },
|
|
6044
|
+
client: createInertClient(),
|
|
6045
|
+
subscribe: () => () => {
|
|
6046
|
+
},
|
|
6047
|
+
emit: () => {
|
|
6048
|
+
},
|
|
6049
|
+
remotePersonalization: null,
|
|
6050
|
+
degraded: true
|
|
6051
|
+
};
|
|
6052
|
+
}
|
|
6053
|
+
function AppilotsProvider({ config, children, client }) {
|
|
6054
|
+
return /* @__PURE__ */ React2.createElement(
|
|
6055
|
+
AppilotsErrorBoundary,
|
|
6056
|
+
{
|
|
6057
|
+
surface: "provider",
|
|
6058
|
+
onError: reportProviderRenderFailure,
|
|
6059
|
+
fallback: /* @__PURE__ */ React2.createElement(AppilotsContext.Provider, { value: createDegradedContext(config ?? null) }, children)
|
|
6060
|
+
},
|
|
6061
|
+
/* @__PURE__ */ React2.createElement(AppilotsProviderInner, { config, client }, children)
|
|
6062
|
+
);
|
|
6063
|
+
}
|
|
6064
|
+
function reportProviderRenderFailure() {
|
|
6065
|
+
recordRenderFailure(readReactVersion());
|
|
6066
|
+
}
|
|
6067
|
+
function readReactVersion() {
|
|
6068
|
+
const version = React2.version;
|
|
6069
|
+
return typeof version === "string" ? version : null;
|
|
6070
|
+
}
|
|
6071
|
+
function AppilotsProviderInner({
|
|
6072
|
+
config: configProp,
|
|
6073
|
+
children,
|
|
6074
|
+
client: externalClient
|
|
6075
|
+
}) {
|
|
6076
|
+
appilotsDebugLog(
|
|
6077
|
+
`AppilotsProvider initializing \u2014 config source: ${configProp ? "prop" : "auto (globalThis.__APPILOTS_RC__)"}`
|
|
6078
|
+
);
|
|
6079
|
+
tryAutoConfig();
|
|
6080
|
+
const globalConfig = getGlobalConfig();
|
|
6081
|
+
appilotsDebugLog(`AppilotsProvider: configProp=${!!configProp}, globalConfig=${!!globalConfig}`);
|
|
6082
|
+
const config = configProp ?? (globalConfig ? {
|
|
6083
|
+
projectId: globalConfig.projectId,
|
|
6084
|
+
apiBaseUrl: globalConfig.apiBaseUrl,
|
|
6085
|
+
apiKey: globalConfig.apiKey,
|
|
6086
|
+
permissions: globalConfig.permissions,
|
|
6087
|
+
debug: globalConfig.debug,
|
|
6088
|
+
appVersion: globalConfig.appVersion,
|
|
6089
|
+
mcpVersion: globalConfig.mcpVersion,
|
|
6090
|
+
fetchPersonalization: globalConfig.fetchPersonalization,
|
|
6091
|
+
suppressNativeConfirm: globalConfig.suppressNativeConfirm
|
|
6092
|
+
} : null);
|
|
6093
|
+
if (!config?.projectId) {
|
|
6094
|
+
throw new Error(
|
|
6095
|
+
"AppilotsProvider: No config found. Add withAppilots() to your metro.config.js and create a .appilotsrc file, or pass a config prop directly."
|
|
6096
|
+
);
|
|
6097
|
+
}
|
|
6098
|
+
useEffect(() => {
|
|
6099
|
+
setAppilotsDebugEnabled2(config.debug === true);
|
|
6100
|
+
}, [config.debug]);
|
|
6101
|
+
useEffect(() => {
|
|
6102
|
+
probeNameMangling({ Switch: Switch, Modal, Text });
|
|
6103
|
+
}, []);
|
|
6104
|
+
useEffect(() => {
|
|
6105
|
+
setGeometryHost(createFabricGeometryHost(Dimensions));
|
|
6106
|
+
return () => setGeometryHost(null);
|
|
6107
|
+
}, []);
|
|
6108
|
+
const trackNativeDialogs = config.trackNativeDialogs !== false;
|
|
6109
|
+
useEffect(() => {
|
|
6110
|
+
if (!trackNativeDialogs) return;
|
|
6111
|
+
installNativeDialogTracking(Alert);
|
|
6112
|
+
return () => {
|
|
6113
|
+
uninstallNativeDialogTracking();
|
|
6114
|
+
};
|
|
6115
|
+
}, [trackNativeDialogs]);
|
|
6116
|
+
const listenersRef = useRef(/* @__PURE__ */ new Set());
|
|
6117
|
+
const subscribe = useCallback((handler) => {
|
|
6118
|
+
listenersRef.current.add(handler);
|
|
6119
|
+
return () => {
|
|
6120
|
+
listenersRef.current.delete(handler);
|
|
6121
|
+
};
|
|
6122
|
+
}, []);
|
|
6123
|
+
const emit = useCallback((event) => {
|
|
6124
|
+
recordAppilotsEventTrace(event);
|
|
6125
|
+
listenersRef.current.forEach((handler) => handler(event));
|
|
6126
|
+
}, []);
|
|
6127
|
+
const handleIntrospectionUnavailable = useCallback(
|
|
6128
|
+
({ reactVersion }) => {
|
|
6129
|
+
emit({
|
|
6130
|
+
type: "sdk:introspection:unavailable",
|
|
6131
|
+
timestamp: Date.now(),
|
|
6132
|
+
data: { reason: "sentinel-missing-internals", reactVersion }
|
|
6133
|
+
});
|
|
6134
|
+
},
|
|
6135
|
+
[emit]
|
|
6136
|
+
);
|
|
6137
|
+
const client = useMemo(
|
|
6138
|
+
() => externalClient ?? new AppilotsClient({
|
|
6139
|
+
projectId: config.projectId,
|
|
6140
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
6141
|
+
apiKey: config.apiKey,
|
|
6142
|
+
debug: config.debug,
|
|
6143
|
+
appVersion: config.appVersion,
|
|
6144
|
+
mcpVersion: config.mcpVersion,
|
|
6145
|
+
// This package's published version, so the server knows which
|
|
6146
|
+
// SDK is actually in the field (issue #312).
|
|
6147
|
+
sdkVersion: SDK_VERSION2,
|
|
6148
|
+
user: config.user,
|
|
6149
|
+
// Only reports a FAILED reading; the happy path sends nothing.
|
|
6150
|
+
introspectionReporter: getIntrospectionDiagnostics
|
|
6151
|
+
}),
|
|
6152
|
+
[
|
|
6153
|
+
config.projectId,
|
|
6154
|
+
config.apiBaseUrl,
|
|
6155
|
+
config.apiKey,
|
|
6156
|
+
config.debug,
|
|
6157
|
+
config.appVersion,
|
|
6158
|
+
config.mcpVersion,
|
|
6159
|
+
config.user,
|
|
6160
|
+
externalClient
|
|
6161
|
+
]
|
|
6162
|
+
);
|
|
6163
|
+
const fetchPersonalizationEnabled = config.fetchPersonalization !== false;
|
|
6164
|
+
const [remotePersonalization, setRemotePersonalization] = useState(
|
|
6165
|
+
null
|
|
6166
|
+
);
|
|
6167
|
+
useEffect(() => {
|
|
6168
|
+
if (!fetchPersonalizationEnabled) return;
|
|
6169
|
+
let cancelled = false;
|
|
6170
|
+
client.getPersonalization().then((data) => {
|
|
6171
|
+
if (!cancelled) setRemotePersonalization(data);
|
|
6172
|
+
}).catch(() => {
|
|
6173
|
+
});
|
|
6174
|
+
return () => {
|
|
6175
|
+
cancelled = true;
|
|
6176
|
+
};
|
|
6177
|
+
}, [client, fetchPersonalizationEnabled]);
|
|
6178
|
+
const value = useMemo(
|
|
6179
|
+
() => ({ config, client, subscribe, emit, remotePersonalization }),
|
|
6180
|
+
[config, client, subscribe, emit, remotePersonalization]
|
|
6181
|
+
);
|
|
6182
|
+
return /* @__PURE__ */ React2.createElement(AppilotsContext.Provider, { value }, /* @__PURE__ */ React2.createElement(AppilotsFiberRoot, { onUnavailable: handleIntrospectionUnavailable }, children));
|
|
6183
|
+
}
|
|
6184
|
+
function useAppilotsContext() {
|
|
6185
|
+
const context = useContext(AppilotsContext);
|
|
6186
|
+
if (!context) {
|
|
6187
|
+
throw new Error("useAppilotsContext must be used within an <AppilotsProvider>");
|
|
6188
|
+
}
|
|
6189
|
+
return context;
|
|
6190
|
+
}
|
|
6191
|
+
|
|
4610
6192
|
// src/navigation/registerScreen.ts
|
|
4611
6193
|
var screenRegistry = /* @__PURE__ */ new Map();
|
|
4612
6194
|
function registerScreen(metadata) {
|
|
@@ -4622,4 +6204,4 @@ function clearScreenRegistry() {
|
|
|
4622
6204
|
screenRegistry.clear();
|
|
4623
6205
|
}
|
|
4624
6206
|
|
|
4625
|
-
export { ActionQueueMachine, AppilotsClient, AppilotsErrorBoundary, AppilotsProvider, AppilotsRegistryProvider, ChatSessionMachine, OPTIONAL_STEP_AUTOMATION_HINT, RateLimitedError, SDK_VERSION2 as SDK_VERSION, _patchJsxRuntimes, actionPressTargetId, appilotsDebugLog, appilotsDebugWarn, attachElementIdsToChoiceGroups, clampSnapshotToWireLimits, clearAppilotsDebugTraces, clearScreenRegistry, componentRegistry, createComponentRegistry, createElementRegistry, createListRegistry, defaultDarkTheme, defaultLightTheme, deriveInteractionElements, describeAction, elementRegistry, enableAppilotsAutoTracking, getActiveRouteNames, getAllScreens, getAppilotsDebugTraces, getCurrentScreen, getCurrentScreenSignature, getDefaultRegistry, getFiberRoot, getGlobalConfig, getIntrospectionDiagnostics, getNavigationRef, getNavigationStateSnapshot, getScreenMetadata, humanizeError, idLooselyMatches, initAppilots, isAutoTrackingEnabled, isGenericSelectValue, isListItemPressTarget, isOptionalStepAutomationFailure, listRegistry, looksDestructiveActionLabel, matchScore, mergeThemeTokens, normalize3, parseStableElementId, probeLoadingState, recordAppilotsDebugTrace, recordIntrospectionFailure, registerScreen, resolveBaseTheme, routesBelongToSameFeature, screenDepartedBaseline, setCurrentScreen, setNavigationRef, snapshotShowsOpenCreateForm, subscribeAppilotsDebugTraces, useAppilotsContext, useResolvedRegistry, waitForLoadingSettle, waitForScreenSettle };
|
|
6207
|
+
export { ActionQueueMachine, AppilotsClient, AppilotsErrorBoundary, AppilotsProvider, AppilotsRegistryProvider, ChatSessionMachine, FORM_VALIDATION_TEXT_RE, FORM_WRITE_SUCCESS_TEXT_RE, OPTIONAL_STEP_AUTOMATION_HINT, RateLimitedError, SDK_VERSION2 as SDK_VERSION, TOUCHABLE_NAMES, _patchJsxRuntimes, actionPressTargetId, appilotsDebugLog, appilotsDebugWarn, attachElementIdsToChoiceGroups, clampSnapshotToWireLimits, clearAppilotsDebugTraces, clearScreenRegistry, componentRegistry, createComponentRegistry, createElementRegistry, createListRegistry, defaultDarkTheme, defaultLightTheme, deriveInteractionElements, describeAction, describeValue, elementRegistry, emptyDetectorCounts, enableAppilotsAutoTracking, findListItems, foldForCompare, geometryAvailability, geometryStats, getActiveRouteNames, getAllScreens, getAppilotsDebugTraces, getAutoTrackingState, getCurrentScreen, getCurrentScreenSignature, getDefaultRegistry, getFiberRoot, getGlobalConfig, getIntrospectionDiagnostics, getLastAnsweredNativeDialog, getLastWalkDiagnostics, getNameManglingProbe, getNavigationRef, getNavigationStateSnapshot, getOpenNativeDialog, getScreenMetadata, getTypeName, humanizeError, idLooselyMatches, initAppilots, isAlwaysListContainer, isAutoTrackingEnabled, isGenericSelectValue, isHeuristicListContainer, isListItemPressTarget, isModalFiber, isOptionalStepAutomationFailure, isReferenceable, isSwitchLike, listRegistry, looksDestructiveActionLabel, matchScore, mergeThemeTokens, normalize3, parseStableElementId, probeLoadingState, recordAppilotsDebugTrace, recordIntrospectionFailure, rectForNode, registerScreen, resolveBaseTheme, routesBelongToSameFeature, screenDepartedBaseline, setCurrentScreen, setNavigationRef, shouldSkipSubtree, snapshotShowsOpenCreateForm, subscribeAppilotsDebugTraces, switchValue, toWireElement, totalDetectorHits, useAppilotsContext, useResolvedRegistry, waitForLoadingSettle, waitForScreenSettle, walkFiber };
|