@appilots/sdk 0.7.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -4
- package/dist/.build-meta.json +5 -0
- package/dist/{chunk-KYFAXT6V.js → chunk-2GCWCPSD.js} +19 -16
- package/dist/{chunk-R4D34FEW.mjs → chunk-AHHDOUVX.mjs} +2151 -333
- package/dist/{chunk-DR75QTYK.mjs → chunk-IHPLS5FE.mjs} +16 -13
- package/dist/{chunk-HFRIB4YN.js → chunk-VX7AL4SA.js} +1049 -1162
- package/dist/{chunk-KUFWRJC4.mjs → chunk-XHR65V2W.mjs} +831 -949
- package/dist/{chunk-DZ7QRFHD.js → chunk-YB77RYCC.js} +2178 -332
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +11 -11
- package/dist/hooks/index.mjs +2 -2
- package/dist/{index-nI-s3Exg.d.mts → index-BPZ0j71S.d.mts} +315 -15
- package/dist/{index-nI-s3Exg.d.ts → index-BPZ0j71S.d.ts} +315 -15
- package/dist/index.d.mts +325 -22
- package/dist/index.d.ts +325 -22
- package/dist/index.js +340 -227
- package/dist/index.mjs +274 -173
- package/dist/navigation/index.d.mts +171 -3
- package/dist/navigation/index.d.ts +171 -3
- package/dist/navigation/index.js +13 -13
- package/dist/navigation/index.mjs +2 -2
- package/metro.d.ts +16 -1
- package/metro.js +104 -10
- package/package.json +16 -6
- package/dist/registerScreen-D1oGm28T.d.mts +0 -159
- package/dist/registerScreen-D1oGm28T.d.ts +0 -159
|
@@ -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;
|
|
@@ -76,15 +263,34 @@ var AppilotsClient = class {
|
|
|
76
263
|
"Content-Type": "application/json",
|
|
77
264
|
// Version telemetry: lets the server correlate behavior per SDK
|
|
78
265
|
// release and pick the MCP doc matching this app build (B.4).
|
|
79
|
-
|
|
266
|
+
// The platform SDK supplies its own published version; the
|
|
267
|
+
// client-core constant is only the fallback (see `sdkVersion`).
|
|
268
|
+
"X-Appilots-Sdk-Version": options.sdkVersion ?? SDK_VERSION,
|
|
80
269
|
...options.appVersion ? { "X-App-Version": options.appVersion } : {},
|
|
81
270
|
...options.mcpVersion ? { "X-Appilots-Mcp-Version": options.mcpVersion } : {},
|
|
82
271
|
...options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {},
|
|
83
272
|
...options.headers
|
|
84
273
|
};
|
|
85
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
|
+
}
|
|
86
292
|
log(...args) {
|
|
87
|
-
if (this.debug)
|
|
293
|
+
if (this.debug) appilotsDebugLog("[AppilotsClient]", ...args);
|
|
88
294
|
}
|
|
89
295
|
describeNonJsonResponse(url, response, body) {
|
|
90
296
|
const contentType = response.headers?.get?.("content-type") ?? "unknown content-type";
|
|
@@ -118,13 +324,10 @@ var AppilotsClient = class {
|
|
|
118
324
|
if (response.status === 429) {
|
|
119
325
|
throw new RateLimitedError(
|
|
120
326
|
errorMsg,
|
|
121
|
-
parseRetryAfter(
|
|
122
|
-
json?.error?.retryAfterSeconds,
|
|
123
|
-
response.headers?.get?.("retry-after")
|
|
124
|
-
)
|
|
327
|
+
parseRetryAfter(json?.error?.retryAfterSeconds, response.headers?.get?.("retry-after"))
|
|
125
328
|
);
|
|
126
329
|
}
|
|
127
|
-
throw new Error(errorMsg);
|
|
330
|
+
throw new Error(this.explainIfMisroutedBaseUrl(response.status, json, errorMsg));
|
|
128
331
|
}
|
|
129
332
|
return json.data ?? json;
|
|
130
333
|
} finally {
|
|
@@ -191,7 +394,8 @@ var AppilotsClient = class {
|
|
|
191
394
|
introspectionFragment() {
|
|
192
395
|
try {
|
|
193
396
|
const report = this.introspectionReporter?.();
|
|
194
|
-
if (!report
|
|
397
|
+
if (!report) return {};
|
|
398
|
+
if (report.captured !== false && !report.failureReason) return {};
|
|
195
399
|
return { introspection: report };
|
|
196
400
|
} catch {
|
|
197
401
|
return {};
|
|
@@ -238,7 +442,11 @@ var AppilotsClient = class {
|
|
|
238
442
|
content: data.message.summary,
|
|
239
443
|
timestamp: Date.now()
|
|
240
444
|
} : void 0;
|
|
241
|
-
this.log("Response received:", {
|
|
445
|
+
this.log("Response received:", {
|
|
446
|
+
messageId: message.id,
|
|
447
|
+
actions: actions?.length ?? 0,
|
|
448
|
+
hasSummary: !!summaryMessage
|
|
449
|
+
});
|
|
242
450
|
return {
|
|
243
451
|
sessionId: this.sessionId,
|
|
244
452
|
message,
|
|
@@ -379,17 +587,17 @@ var AppilotsClient = class {
|
|
|
379
587
|
fail(
|
|
380
588
|
new RateLimitedError(
|
|
381
589
|
message2,
|
|
382
|
-
parseRetryAfter(
|
|
383
|
-
retryAfterSeconds,
|
|
384
|
-
xhr.getResponseHeader?.("retry-after")
|
|
385
|
-
)
|
|
590
|
+
parseRetryAfter(retryAfterSeconds, xhr.getResponseHeader?.("retry-after"))
|
|
386
591
|
)
|
|
387
592
|
);
|
|
388
593
|
return;
|
|
389
594
|
}
|
|
390
|
-
|
|
391
|
-
|
|
595
|
+
const explained = this.explainIfMisroutedBaseUrl(
|
|
596
|
+
xhr.status,
|
|
597
|
+
safeJson(xhr.responseText),
|
|
598
|
+
message2
|
|
392
599
|
);
|
|
600
|
+
fail(sawAnyEvent ? new Error(explained) : new StreamTransportError(explained));
|
|
393
601
|
return;
|
|
394
602
|
}
|
|
395
603
|
if (!doneEvent) {
|
|
@@ -571,7 +779,9 @@ var AppilotsClient = class {
|
|
|
571
779
|
};
|
|
572
780
|
case "navigate":
|
|
573
781
|
return {
|
|
574
|
-
|
|
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,
|
|
575
785
|
params: payload.params,
|
|
576
786
|
navigationAction: payload.navigationAction ?? "navigate",
|
|
577
787
|
path: Array.isArray(payload.path) ? payload.path : void 0
|
|
@@ -1104,8 +1314,15 @@ function subscribeAppilotsDebugTraces(listener) {
|
|
|
1104
1314
|
};
|
|
1105
1315
|
}
|
|
1106
1316
|
var INTERNAL_MARKER_RE = /^\[(?:Permission policy notice|Recovery exhausted|User rejected the proposed action)[^\]]*\]:\s*/i;
|
|
1107
|
-
var
|
|
1108
|
-
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
|
+
);
|
|
1109
1326
|
function sanitizeVisibleAssistantContent(content) {
|
|
1110
1327
|
return content.split("\n").map((line) => line.replace(INTERNAL_MARKER_RE, "").trimEnd()).filter((line) => !INTERNAL_MARKER_RE.test(line)).join("\n").trim();
|
|
1111
1328
|
}
|
|
@@ -1113,7 +1330,7 @@ function humanizeAssistantFallback(content) {
|
|
|
1113
1330
|
let text = sanitizeVisibleAssistantContent(content).replace(/\blist-\d+-item-\d+\b/gi, "o item da lista").replace(/\bel:[^\s".]+/gi, "o elemento").replace(
|
|
1114
1331
|
/"\s*(?:press|click|tap|navigate|form_fill|ui_interaction|scroll_list)\s+on\s+[^"]+"/gi,
|
|
1115
1332
|
'"a a\xE7\xE3o solicitada"'
|
|
1116
|
-
).
|
|
1333
|
+
).trim();
|
|
1117
1334
|
if (text.length < 16) return null;
|
|
1118
1335
|
const probe = {
|
|
1119
1336
|
role: "assistant",
|
|
@@ -1128,9 +1345,8 @@ function isInternalTraceMessage(message) {
|
|
|
1128
1345
|
if (!content) return true;
|
|
1129
1346
|
return TECHNICAL_TRACE_RE.test(content) || TECHNICAL_SELECTOR_RE.test(content);
|
|
1130
1347
|
}
|
|
1131
|
-
var CREATE_FORM_ROUTE_RE =
|
|
1132
|
-
var SUBMIT_BUTTON_RE =
|
|
1133
|
-
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;
|
|
1134
1350
|
function normalize2(value) {
|
|
1135
1351
|
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
1136
1352
|
}
|
|
@@ -1148,7 +1364,9 @@ function snapshotHasVisibleSubmitButton(snap) {
|
|
|
1148
1364
|
);
|
|
1149
1365
|
}
|
|
1150
1366
|
function snapshotHasVisibleValidation(snap) {
|
|
1151
|
-
return (snap.texts ?? []).some(
|
|
1367
|
+
return (snap.texts ?? []).some(
|
|
1368
|
+
(text) => typeof text === "string" && FORM_VALIDATION_TEXT_RE.test(text)
|
|
1369
|
+
);
|
|
1152
1370
|
}
|
|
1153
1371
|
function snapshotShowsOpenCreateForm(snap, activePath = []) {
|
|
1154
1372
|
if (!snap) return false;
|
|
@@ -1565,9 +1783,10 @@ var ChatSessionMachine = class {
|
|
|
1565
1783
|
};
|
|
1566
1784
|
}
|
|
1567
1785
|
const hasToolWithoutEffect = toolWithoutEffectIds.has(actionId);
|
|
1786
|
+
const observedNoEffect = hasToolWithoutEffect || r.effect === "none";
|
|
1568
1787
|
let recoveryAttempt = 0;
|
|
1569
1788
|
let recoveryExhausted = false;
|
|
1570
|
-
if (!r.success ||
|
|
1789
|
+
if (!r.skipped && (!r.success || observedNoEffect)) {
|
|
1571
1790
|
const previous = this.recoveryCountByFingerprint.get(r.fingerprint) ?? 0;
|
|
1572
1791
|
const next = previous + 1;
|
|
1573
1792
|
this.recoveryCountByFingerprint.set(r.fingerprint, next);
|
|
@@ -1584,16 +1803,18 @@ var ChatSessionMachine = class {
|
|
|
1584
1803
|
recoveryAttempt,
|
|
1585
1804
|
recoveryExhausted,
|
|
1586
1805
|
toolWithoutEffect: hasToolWithoutEffect,
|
|
1587
|
-
effect:
|
|
1806
|
+
effect: observedNoEffect ? "none" : r.effect,
|
|
1588
1807
|
userVisibleStatusKey: r.userVisibleStatusKey
|
|
1589
1808
|
};
|
|
1590
1809
|
});
|
|
1591
1810
|
const exhausted = results.filter((r) => r.recoveryExhausted);
|
|
1592
1811
|
this.setState({
|
|
1593
|
-
loadingStatusKey: results.some(
|
|
1812
|
+
loadingStatusKey: results.some(
|
|
1813
|
+
(r) => !r.success || r.toolWithoutEffect || r.effect === "none"
|
|
1814
|
+
) ? "statusAdjusting" : "statusAnalyzing"
|
|
1594
1815
|
});
|
|
1595
|
-
|
|
1596
|
-
`
|
|
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` : "") + ")"
|
|
1597
1818
|
);
|
|
1598
1819
|
const response = await this.deps.client.continueAgent(results, context, turn.hop);
|
|
1599
1820
|
this.addAssistantMessage(response.message);
|
|
@@ -1666,6 +1887,7 @@ var ChatSessionMachine = class {
|
|
|
1666
1887
|
const diagnose = event.data?.diagnose;
|
|
1667
1888
|
const effect = event.data?.effect;
|
|
1668
1889
|
const userVisibleStatusKey = event.data?.userVisibleStatusKey;
|
|
1890
|
+
const skipped = event.data?.skipped === true;
|
|
1669
1891
|
const existing = turn.results.get(actionId);
|
|
1670
1892
|
if (existing) {
|
|
1671
1893
|
existing.success = success;
|
|
@@ -1673,6 +1895,7 @@ var ChatSessionMachine = class {
|
|
|
1673
1895
|
existing.diagnose = diagnose;
|
|
1674
1896
|
existing.effect = effect;
|
|
1675
1897
|
existing.userVisibleStatusKey = userVisibleStatusKey;
|
|
1898
|
+
existing.skipped = skipped;
|
|
1676
1899
|
}
|
|
1677
1900
|
turn.reportedIds.add(actionId);
|
|
1678
1901
|
if (turn.reportedIds.size >= turn.actionIds.size) {
|
|
@@ -1709,8 +1932,8 @@ var ChatSessionMachine = class {
|
|
|
1709
1932
|
this.abortController = abortController;
|
|
1710
1933
|
try {
|
|
1711
1934
|
const context = this.deps.adapter.buildContext();
|
|
1712
|
-
|
|
1713
|
-
`
|
|
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}}`
|
|
1714
1937
|
);
|
|
1715
1938
|
let response;
|
|
1716
1939
|
if (streamingEnabled) {
|
|
@@ -1738,9 +1961,7 @@ var ChatSessionMachine = class {
|
|
|
1738
1961
|
});
|
|
1739
1962
|
} catch (err) {
|
|
1740
1963
|
if (err instanceof StreamTransportError) {
|
|
1741
|
-
|
|
1742
|
-
`[Appilots] streaming unavailable, falling back to JSON: ${err.message}`
|
|
1743
|
-
);
|
|
1964
|
+
appilotsDebugWarn(`streaming unavailable, falling back to JSON: ${err.message}`);
|
|
1744
1965
|
response = await this.deps.client.sendMessage(content.trim(), context);
|
|
1745
1966
|
} else {
|
|
1746
1967
|
throw err;
|
|
@@ -1749,8 +1970,8 @@ var ChatSessionMachine = class {
|
|
|
1749
1970
|
} else {
|
|
1750
1971
|
response = await this.deps.client.sendMessage(content.trim(), context);
|
|
1751
1972
|
}
|
|
1752
|
-
|
|
1753
|
-
`
|
|
1973
|
+
appilotsDebugLog(
|
|
1974
|
+
`ChatSessionMachine sendMessage: response received \u2014 actions=${response.actions?.length ?? 0}, hasSummary=${!!response.summaryMessage}`
|
|
1754
1975
|
);
|
|
1755
1976
|
removeStreamingPlaceholder();
|
|
1756
1977
|
this.addAssistantMessage(response.message);
|
|
@@ -1840,7 +2061,11 @@ function isStructuredRisky(value) {
|
|
|
1840
2061
|
}
|
|
1841
2062
|
function actionMetadataRequiresConfirmation(meta) {
|
|
1842
2063
|
if (meta.requiresConfirmation === true || meta.destructive === true || meta.effect === "destructive" || meta.riskLevel === "high" || meta.risk === "destructive") {
|
|
1843
|
-
return {
|
|
2064
|
+
return {
|
|
2065
|
+
required: true,
|
|
2066
|
+
label: typeof meta.label === "string" ? meta.label : void 0,
|
|
2067
|
+
source: "screen-metadata"
|
|
2068
|
+
};
|
|
1844
2069
|
}
|
|
1845
2070
|
return { required: false };
|
|
1846
2071
|
}
|
|
@@ -1881,13 +2106,9 @@ function toDestructiveConfirmPayload(confirm, policy, fallbackLabel) {
|
|
|
1881
2106
|
const labelText = policy.label ?? existingDescription ?? fallbackLabel ?? "esta a\xE7\xE3o";
|
|
1882
2107
|
return {
|
|
1883
2108
|
...payload,
|
|
1884
|
-
title: "A\xE7\xE3o destrutiva",
|
|
1885
|
-
message: `Tem certeza que deseja "${labelText}"?`,
|
|
1886
2109
|
actionDescription: labelText,
|
|
1887
2110
|
severity: "danger",
|
|
1888
|
-
destructive: true
|
|
1889
|
-
confirmLabel: typeof payload.confirmLabel === "string" && payload.confirmLabel ? payload.confirmLabel : "Sim, executar",
|
|
1890
|
-
cancelLabel: typeof payload.cancelLabel === "string" && payload.cancelLabel ? payload.cancelLabel : "Cancelar"
|
|
2111
|
+
destructive: true
|
|
1891
2112
|
};
|
|
1892
2113
|
}
|
|
1893
2114
|
function confirmNeedsDestructiveUpgrade(confirm) {
|
|
@@ -1953,8 +2174,8 @@ var ActionQueueMachine = class {
|
|
|
1953
2174
|
if (event.type !== "agent:action:start") return;
|
|
1954
2175
|
const action = event.data?.action;
|
|
1955
2176
|
if (!action?.id) return;
|
|
1956
|
-
|
|
1957
|
-
`
|
|
2177
|
+
appilotsDebugLog(
|
|
2178
|
+
`ActionQueueMachine: received agent:action:start event, action id="${action.id}", type="${action.type}"`
|
|
1958
2179
|
);
|
|
1959
2180
|
if (this.state.actions.some((a) => a.id === action.id)) return;
|
|
1960
2181
|
this.setActions([...this.state.actions, action]);
|
|
@@ -2055,8 +2276,8 @@ var ActionQueueMachine = class {
|
|
|
2055
2276
|
if (!policy.required) return false;
|
|
2056
2277
|
const confirmId = `confirm-local-${target.id}`;
|
|
2057
2278
|
if (this.state.actions.some((a) => a.id === confirmId)) return true;
|
|
2058
|
-
|
|
2059
|
-
`
|
|
2279
|
+
appilotsDebugLog(
|
|
2280
|
+
`ActionQueueMachine: action "${componentId || target.id}" requires confirmation (${policy.source ?? "local-policy"}) \u2014 synthesising local confirm (id="${confirmId}")`
|
|
2060
2281
|
);
|
|
2061
2282
|
const synth = {
|
|
2062
2283
|
id: confirmId,
|
|
@@ -2102,13 +2323,13 @@ var ActionQueueMachine = class {
|
|
|
2102
2323
|
const pending = this.state.actions.filter(
|
|
2103
2324
|
(a) => a.status === "pending" && !this.processed.has(a.id)
|
|
2104
2325
|
);
|
|
2105
|
-
|
|
2106
|
-
`
|
|
2326
|
+
appilotsDebugLog(
|
|
2327
|
+
`ActionQueueMachine: autoExecute check \u2014 ${pending.length} pending action(s) to process`
|
|
2107
2328
|
);
|
|
2108
2329
|
let lastWasNavigate = false;
|
|
2109
2330
|
let preNavScreen = null;
|
|
2110
2331
|
let preNavSignature = null;
|
|
2111
|
-
for (const action of pending) {
|
|
2332
|
+
for (const [index, action] of pending.entries()) {
|
|
2112
2333
|
if (action.type === "confirm") {
|
|
2113
2334
|
continue;
|
|
2114
2335
|
}
|
|
@@ -2118,14 +2339,14 @@ var ActionQueueMachine = class {
|
|
|
2118
2339
|
const gate = findGatingConfirm(action, this.state.actions);
|
|
2119
2340
|
if (gate) {
|
|
2120
2341
|
if (gate.status === "pending" || gate.status === "executing") {
|
|
2121
|
-
|
|
2122
|
-
`
|
|
2342
|
+
appilotsDebugLog(
|
|
2343
|
+
`ActionQueueMachine: action id="${action.id}" is gated by confirm id="${gate.id}" (status=${gate.status}); waiting for user decision`
|
|
2123
2344
|
);
|
|
2124
2345
|
continue;
|
|
2125
2346
|
}
|
|
2126
2347
|
if (gate.status === "failed") {
|
|
2127
|
-
|
|
2128
|
-
`
|
|
2348
|
+
appilotsDebugLog(
|
|
2349
|
+
`ActionQueueMachine: confirm id="${gate.id}" was rejected; skipping gated action id="${action.id}"`
|
|
2129
2350
|
);
|
|
2130
2351
|
this.processed.add(action.id);
|
|
2131
2352
|
this.updateActions(
|
|
@@ -2157,12 +2378,12 @@ var ActionQueueMachine = class {
|
|
|
2157
2378
|
fromSignature: preNavSignature,
|
|
2158
2379
|
maxMs: 3500
|
|
2159
2380
|
});
|
|
2160
|
-
|
|
2161
|
-
`
|
|
2381
|
+
appilotsDebugLog(
|
|
2382
|
+
`ActionQueueMachine: post-navigate settle done \u2014 screen="${settle.screen}" transitioned=${settle.transitioned} timedOut=${settle.timedOut} waitedMs=${settle.waitedMs}`
|
|
2162
2383
|
);
|
|
2163
2384
|
}
|
|
2164
|
-
|
|
2165
|
-
`
|
|
2385
|
+
appilotsDebugLog(
|
|
2386
|
+
`ActionQueueMachine: auto-executing action id="${action.id}" type="${action.type}"`
|
|
2166
2387
|
);
|
|
2167
2388
|
this.processed.add(action.id);
|
|
2168
2389
|
if (action.type === "navigate") {
|
|
@@ -2171,20 +2392,67 @@ var ActionQueueMachine = class {
|
|
|
2171
2392
|
}
|
|
2172
2393
|
const succeeded = await this.approveAction(action.id);
|
|
2173
2394
|
if (!succeeded) {
|
|
2395
|
+
const failed = this.state.actions.find((candidate) => candidate.id === action.id);
|
|
2174
2396
|
this.warn(
|
|
2175
2397
|
`ActionQueueMachine: stopping current action batch after failed action id="${action.id}"`
|
|
2176
2398
|
);
|
|
2399
|
+
await this.skipRemainingBatchActions(pending.slice(index + 1), action, failed?.error);
|
|
2177
2400
|
break;
|
|
2178
2401
|
}
|
|
2179
2402
|
lastWasNavigate = action.type === "navigate";
|
|
2180
2403
|
}
|
|
2181
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
|
+
}
|
|
2182
2450
|
// ── Approve & execute an action ─────────────────────────────
|
|
2183
2451
|
async approveAction(actionId) {
|
|
2184
2452
|
const action = this.state.actions.find((a) => a.id === actionId);
|
|
2185
2453
|
if (!action) return false;
|
|
2186
|
-
|
|
2187
|
-
`
|
|
2454
|
+
appilotsDebugLog(
|
|
2455
|
+
`ActionQueueMachine: action status transition: pending \u2192 executing (id="${actionId}", type="${action.type}")`
|
|
2188
2456
|
);
|
|
2189
2457
|
this.updateActions(
|
|
2190
2458
|
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "executing" } : a)
|
|
@@ -2196,8 +2464,8 @@ var ActionQueueMachine = class {
|
|
|
2196
2464
|
{ confirmedDestructive }
|
|
2197
2465
|
);
|
|
2198
2466
|
if (result.success) {
|
|
2199
|
-
|
|
2200
|
-
`
|
|
2467
|
+
appilotsDebugLog(
|
|
2468
|
+
`ActionQueueMachine: action status transition: executing \u2192 completed (id="${actionId}", type="${action.type}")`
|
|
2201
2469
|
);
|
|
2202
2470
|
this.updateActions(
|
|
2203
2471
|
(prev) => prev.map((a) => a.id === actionId ? { ...a, status: "completed" } : a)
|
|
@@ -2276,9 +2544,7 @@ var ActionQueueMachine = class {
|
|
|
2276
2544
|
return this.state.actions.filter((a) => a.status === "executing");
|
|
2277
2545
|
}
|
|
2278
2546
|
get completedActions() {
|
|
2279
|
-
return this.state.actions.filter(
|
|
2280
|
-
(a) => a.status === "completed" || a.status === "failed"
|
|
2281
|
-
);
|
|
2547
|
+
return this.state.actions.filter((a) => a.status === "completed" || a.status === "failed");
|
|
2282
2548
|
}
|
|
2283
2549
|
};
|
|
2284
2550
|
function normalize3(s) {
|
|
@@ -2307,13 +2573,11 @@ function normalizeId(value) {
|
|
|
2307
2573
|
function isOptionalCosmeticTargetId(targetId) {
|
|
2308
2574
|
if (typeof targetId !== "string" || !targetId.trim()) return false;
|
|
2309
2575
|
const id = normalizeId(targetId);
|
|
2310
|
-
return /
|
|
2576
|
+
return /sheetaction|sheet-action|photo-sheet|actionsheet|action-sheet/.test(id);
|
|
2311
2577
|
}
|
|
2312
2578
|
function routeLooksLikeFormContext(route) {
|
|
2313
2579
|
const haystack = normalizeId(route ?? "");
|
|
2314
|
-
return /create|edit|form|cadastr|register|novo|
|
|
2315
|
-
haystack
|
|
2316
|
-
);
|
|
2580
|
+
return /create|edit|form|cadastr|criar|register|registr|novo|nova/.test(haystack);
|
|
2317
2581
|
}
|
|
2318
2582
|
function screenPathParts(screen) {
|
|
2319
2583
|
if (!screen) return [];
|
|
@@ -2688,6 +2952,7 @@ function listItemElement(route, list, item) {
|
|
|
2688
2952
|
list.id ?? list.label ?? `list-${list.index}`,
|
|
2689
2953
|
key
|
|
2690
2954
|
]);
|
|
2955
|
+
const provenance = item.itemKey || item.reactKey ? "declared" : label ? "derived" : "positional";
|
|
2691
2956
|
const inModal = (item.buttons ?? []).some((button) => button.inModal) || (item.inputs ?? []).some((input) => input.inModal) || (item.toggles ?? []).some((toggle) => toggle.inModal);
|
|
2692
2957
|
return {
|
|
2693
2958
|
id,
|
|
@@ -2697,6 +2962,7 @@ function listItemElement(route, list, item) {
|
|
|
2697
2962
|
actions: ["press"],
|
|
2698
2963
|
disabled: false,
|
|
2699
2964
|
source: "list",
|
|
2965
|
+
provenance,
|
|
2700
2966
|
targetId: item.syntheticId,
|
|
2701
2967
|
...inModal ? { inModal: true } : {},
|
|
2702
2968
|
listContext: {
|
|
@@ -2726,17 +2992,14 @@ function buttonElement(route, button, index) {
|
|
|
2726
2992
|
disabled: button.disabled,
|
|
2727
2993
|
selected: button.selected,
|
|
2728
2994
|
source: "button",
|
|
2995
|
+
...button.provenance ? { provenance: button.provenance } : {},
|
|
2729
2996
|
targetId: button.id ?? button.label,
|
|
2730
2997
|
...button.inModal ? { inModal: true } : {}
|
|
2731
2998
|
};
|
|
2732
2999
|
}
|
|
2733
3000
|
function inputElement(route, input, index) {
|
|
2734
3001
|
const label = input.label ?? input.placeholder ?? input.id;
|
|
2735
|
-
const id = stableElementId([
|
|
2736
|
-
"input",
|
|
2737
|
-
route ?? "unknown",
|
|
2738
|
-
input.id ?? label ?? `input-${index}`
|
|
2739
|
-
]);
|
|
3002
|
+
const id = stableElementId(["input", route ?? "unknown", input.id ?? label ?? `input-${index}`]);
|
|
2740
3003
|
return {
|
|
2741
3004
|
id,
|
|
2742
3005
|
role: "input",
|
|
@@ -2745,6 +3008,7 @@ function inputElement(route, input, index) {
|
|
|
2745
3008
|
actions: ["focus", "setValue"],
|
|
2746
3009
|
disabled: input.editable === false,
|
|
2747
3010
|
source: "input",
|
|
3011
|
+
...input.provenance ? { provenance: input.provenance } : {},
|
|
2748
3012
|
targetId: input.id ?? input.label ?? input.placeholder,
|
|
2749
3013
|
...input.inModal ? { inModal: true } : {}
|
|
2750
3014
|
};
|
|
@@ -2764,10 +3028,45 @@ function toggleElement(route, toggle, index) {
|
|
|
2764
3028
|
actions: ["toggle", "press"],
|
|
2765
3029
|
selected: toggle.value,
|
|
2766
3030
|
source: "toggle",
|
|
3031
|
+
...toggle.provenance ? { provenance: toggle.provenance } : {},
|
|
2767
3032
|
targetId: toggle.id ?? toggle.label,
|
|
2768
3033
|
...toggle.inModal ? { inModal: true } : {}
|
|
2769
3034
|
};
|
|
2770
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
|
+
}
|
|
2771
3070
|
function sliderElement(route, slider, index) {
|
|
2772
3071
|
const label = slider.label ?? slider.id;
|
|
2773
3072
|
const id = stableElementId([
|
|
@@ -2783,37 +3082,48 @@ function sliderElement(route, slider, index) {
|
|
|
2783
3082
|
actions: ["setValue"],
|
|
2784
3083
|
disabled: slider.disabled,
|
|
2785
3084
|
source: "slider",
|
|
3085
|
+
...slider.provenance ? { provenance: slider.provenance } : {},
|
|
2786
3086
|
targetId: slider.id ?? slider.label,
|
|
2787
3087
|
...slider.inModal ? { inModal: true } : {}
|
|
2788
3088
|
};
|
|
2789
3089
|
}
|
|
2790
|
-
function deriveInteractionElements(snapshot) {
|
|
3090
|
+
function deriveInteractionElements(snapshot, options) {
|
|
2791
3091
|
const elements = [];
|
|
2792
3092
|
const seen = /* @__PURE__ */ new Set();
|
|
2793
|
-
const
|
|
3093
|
+
const rectOf = options?.rectOf;
|
|
3094
|
+
const push = (element, node) => {
|
|
2794
3095
|
if (!element.id || seen.has(element.id)) return;
|
|
2795
3096
|
seen.add(element.id);
|
|
2796
|
-
|
|
3097
|
+
const rect = rectOf && node !== void 0 ? rectOf(node) : void 0;
|
|
3098
|
+
elements.push(rect ? { ...element, rect } : element);
|
|
2797
3099
|
};
|
|
2798
3100
|
for (const list of snapshot.lists ?? []) {
|
|
2799
3101
|
for (const item of list.items ?? []) {
|
|
2800
|
-
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
|
+
}
|
|
2801
3106
|
}
|
|
2802
3107
|
}
|
|
2803
3108
|
for (const [index, button] of (snapshot.buttons ?? []).entries()) {
|
|
2804
|
-
push(buttonElement(snapshot.route, button, index));
|
|
3109
|
+
push(buttonElement(snapshot.route, button, index), button);
|
|
2805
3110
|
}
|
|
2806
3111
|
for (const [index, input] of (snapshot.inputs ?? []).entries()) {
|
|
2807
|
-
push(inputElement(snapshot.route, input, index));
|
|
3112
|
+
push(inputElement(snapshot.route, input, index), input);
|
|
2808
3113
|
}
|
|
2809
3114
|
for (const [index, toggle] of (snapshot.toggles ?? []).entries()) {
|
|
2810
|
-
push(toggleElement(snapshot.route, toggle, index));
|
|
3115
|
+
push(toggleElement(snapshot.route, toggle, index), toggle);
|
|
2811
3116
|
}
|
|
2812
3117
|
for (const [index, slider] of (snapshot.sliders ?? []).entries()) {
|
|
2813
|
-
push(sliderElement(snapshot.route, slider, index));
|
|
3118
|
+
push(sliderElement(snapshot.route, slider, index), slider);
|
|
2814
3119
|
}
|
|
2815
3120
|
return elements;
|
|
2816
3121
|
}
|
|
3122
|
+
function toWireElement(entry) {
|
|
3123
|
+
if (entry.rect === void 0) return entry;
|
|
3124
|
+
const { rect: _rect, ...wire } = entry;
|
|
3125
|
+
return wire;
|
|
3126
|
+
}
|
|
2817
3127
|
function findElementForChoice(elements, option) {
|
|
2818
3128
|
if (option.targetId) {
|
|
2819
3129
|
const byTarget = elements.find((element) => element.targetId === option.targetId);
|
|
@@ -2842,6 +3152,118 @@ function attachElementIdsToChoiceGroups(choiceGroups, elements) {
|
|
|
2842
3152
|
})
|
|
2843
3153
|
}));
|
|
2844
3154
|
}
|
|
3155
|
+
var WIRE_LIMITS = {
|
|
3156
|
+
route: 200,
|
|
3157
|
+
texts: 500,
|
|
3158
|
+
textLength: 2e3,
|
|
3159
|
+
inputs: 300,
|
|
3160
|
+
buttons: 300,
|
|
3161
|
+
toggles: 300,
|
|
3162
|
+
sliders: 100,
|
|
3163
|
+
lists: 100,
|
|
3164
|
+
listItems: 500,
|
|
3165
|
+
listDataPreview: 500,
|
|
3166
|
+
listDataPreviewTextLength: 500,
|
|
3167
|
+
scrollables: 20,
|
|
3168
|
+
choiceGroups: 100,
|
|
3169
|
+
choiceOptions: 200,
|
|
3170
|
+
elements: 1e3,
|
|
3171
|
+
elementTexts: 50,
|
|
3172
|
+
elementTextLength: 500,
|
|
3173
|
+
elementActions: 20,
|
|
3174
|
+
idLength: 160,
|
|
3175
|
+
labelLength: 300,
|
|
3176
|
+
valueLength: 4096,
|
|
3177
|
+
containerTypeLength: 60};
|
|
3178
|
+
var Cut = class {
|
|
3179
|
+
dropped = false;
|
|
3180
|
+
/** Cap an array's length, remembering whether entries were lost. */
|
|
3181
|
+
array(values, max) {
|
|
3182
|
+
if (!values || values.length <= max) return values;
|
|
3183
|
+
this.dropped = true;
|
|
3184
|
+
return values.slice(0, max);
|
|
3185
|
+
}
|
|
3186
|
+
string(value, max) {
|
|
3187
|
+
if (value === void 0 || value.length <= max) return value;
|
|
3188
|
+
this.dropped = true;
|
|
3189
|
+
return value.slice(0, max);
|
|
3190
|
+
}
|
|
3191
|
+
};
|
|
3192
|
+
function clampInput(cut, input) {
|
|
3193
|
+
input.id = cut.string(input.id, WIRE_LIMITS.idLength);
|
|
3194
|
+
input.label = cut.string(input.label, WIRE_LIMITS.labelLength);
|
|
3195
|
+
input.placeholder = cut.string(input.placeholder, WIRE_LIMITS.labelLength);
|
|
3196
|
+
input.value = cut.string(input.value, WIRE_LIMITS.valueLength);
|
|
3197
|
+
return input;
|
|
3198
|
+
}
|
|
3199
|
+
function clampList(cut, list) {
|
|
3200
|
+
list.id = cut.string(list.id, WIRE_LIMITS.idLength);
|
|
3201
|
+
list.containerType = cut.string(list.containerType, WIRE_LIMITS.containerTypeLength);
|
|
3202
|
+
list.label = cut.string(list.label, WIRE_LIMITS.labelLength);
|
|
3203
|
+
list.items = cut.array(list.items, WIRE_LIMITS.listItems) ?? [];
|
|
3204
|
+
if (list.dataPreview) {
|
|
3205
|
+
list.dataPreview = cut.array(list.dataPreview, WIRE_LIMITS.listDataPreview);
|
|
3206
|
+
for (const entry of list.dataPreview ?? []) {
|
|
3207
|
+
entry.key = cut.string(entry.key, WIRE_LIMITS.idLength);
|
|
3208
|
+
entry.text = cut.string(entry.text, WIRE_LIMITS.listDataPreviewTextLength);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
return list;
|
|
3212
|
+
}
|
|
3213
|
+
function clampChoiceGroup(cut, group) {
|
|
3214
|
+
group.id = cut.string(group.id, WIRE_LIMITS.idLength);
|
|
3215
|
+
group.label = cut.string(group.label, WIRE_LIMITS.labelLength);
|
|
3216
|
+
group.options = cut.array(group.options, WIRE_LIMITS.choiceOptions) ?? [];
|
|
3217
|
+
return group;
|
|
3218
|
+
}
|
|
3219
|
+
function clampSnapshotToWireLimits(snapshot) {
|
|
3220
|
+
const cut = new Cut();
|
|
3221
|
+
if (typeof snapshot.route === "string") {
|
|
3222
|
+
snapshot.route = cut.string(snapshot.route, WIRE_LIMITS.route);
|
|
3223
|
+
}
|
|
3224
|
+
snapshot.texts = (cut.array(snapshot.texts, WIRE_LIMITS.texts) ?? []).map(
|
|
3225
|
+
(text) => cut.string(text, WIRE_LIMITS.textLength)
|
|
3226
|
+
);
|
|
3227
|
+
snapshot.inputs = (cut.array(snapshot.inputs, WIRE_LIMITS.inputs) ?? []).map(
|
|
3228
|
+
(input) => clampInput(cut, input)
|
|
3229
|
+
);
|
|
3230
|
+
snapshot.buttons = (cut.array(snapshot.buttons, WIRE_LIMITS.buttons) ?? []).map((button) => {
|
|
3231
|
+
button.id = cut.string(button.id, WIRE_LIMITS.idLength);
|
|
3232
|
+
button.label = cut.string(button.label, WIRE_LIMITS.labelLength);
|
|
3233
|
+
return button;
|
|
3234
|
+
});
|
|
3235
|
+
snapshot.toggles = (cut.array(snapshot.toggles, WIRE_LIMITS.toggles) ?? []).map((toggle) => {
|
|
3236
|
+
toggle.id = cut.string(toggle.id, WIRE_LIMITS.idLength);
|
|
3237
|
+
toggle.label = cut.string(toggle.label, WIRE_LIMITS.labelLength);
|
|
3238
|
+
return toggle;
|
|
3239
|
+
});
|
|
3240
|
+
snapshot.sliders = (cut.array(snapshot.sliders, WIRE_LIMITS.sliders) ?? []).map((slider) => {
|
|
3241
|
+
slider.id = cut.string(slider.id, WIRE_LIMITS.idLength);
|
|
3242
|
+
slider.label = cut.string(slider.label, WIRE_LIMITS.labelLength);
|
|
3243
|
+
return slider;
|
|
3244
|
+
});
|
|
3245
|
+
snapshot.lists = (cut.array(snapshot.lists, WIRE_LIMITS.lists) ?? []).map(
|
|
3246
|
+
(list) => clampList(cut, list)
|
|
3247
|
+
);
|
|
3248
|
+
if (snapshot.scrollables) {
|
|
3249
|
+
snapshot.scrollables = cut.array(snapshot.scrollables, WIRE_LIMITS.scrollables);
|
|
3250
|
+
}
|
|
3251
|
+
snapshot.choiceGroups = (cut.array(snapshot.choiceGroups, WIRE_LIMITS.choiceGroups) ?? []).map(
|
|
3252
|
+
(group) => clampChoiceGroup(cut, group)
|
|
3253
|
+
);
|
|
3254
|
+
snapshot.elements = (cut.array(snapshot.elements, WIRE_LIMITS.elements) ?? []).map((element) => {
|
|
3255
|
+
element.id = cut.string(element.id, WIRE_LIMITS.idLength);
|
|
3256
|
+
element.label = cut.string(element.label, WIRE_LIMITS.labelLength);
|
|
3257
|
+
element.targetId = cut.string(element.targetId, WIRE_LIMITS.idLength);
|
|
3258
|
+
element.texts = (cut.array(element.texts, WIRE_LIMITS.elementTexts) ?? []).map(
|
|
3259
|
+
(text) => cut.string(text, WIRE_LIMITS.elementTextLength)
|
|
3260
|
+
);
|
|
3261
|
+
element.actions = cut.array(element.actions, WIRE_LIMITS.elementActions) ?? [];
|
|
3262
|
+
return element;
|
|
3263
|
+
});
|
|
3264
|
+
if (cut.dropped) snapshot.truncated = true;
|
|
3265
|
+
return snapshot;
|
|
3266
|
+
}
|
|
2845
3267
|
|
|
2846
3268
|
// src/registry/ComponentRegistry.ts
|
|
2847
3269
|
var ComponentRegistryImpl = class {
|
|
@@ -2853,7 +3275,9 @@ var ComponentRegistryImpl = class {
|
|
|
2853
3275
|
*/
|
|
2854
3276
|
register(id, entry) {
|
|
2855
3277
|
this.components.set(id, entry);
|
|
2856
|
-
|
|
3278
|
+
appilotsDebugLog(
|
|
3279
|
+
`ComponentRegistry.register: id="${id}" kind="${entry.kind}" \u2014 total count: ${this.components.size}`
|
|
3280
|
+
);
|
|
2857
3281
|
this.notify(id, entry);
|
|
2858
3282
|
}
|
|
2859
3283
|
/**
|
|
@@ -2861,7 +3285,9 @@ var ComponentRegistryImpl = class {
|
|
|
2861
3285
|
*/
|
|
2862
3286
|
unregister(id) {
|
|
2863
3287
|
this.components.delete(id);
|
|
2864
|
-
|
|
3288
|
+
appilotsDebugLog(
|
|
3289
|
+
`ComponentRegistry.unregister: id="${id}" \u2014 total count: ${this.components.size}`
|
|
3290
|
+
);
|
|
2865
3291
|
this.notify(id, null);
|
|
2866
3292
|
}
|
|
2867
3293
|
/**
|
|
@@ -2965,14 +3391,14 @@ var ListRegistryImpl = class {
|
|
|
2965
3391
|
listeners = /* @__PURE__ */ new Set();
|
|
2966
3392
|
register(id, entry) {
|
|
2967
3393
|
this.lists.set(id, entry);
|
|
2968
|
-
|
|
2969
|
-
`
|
|
3394
|
+
appilotsDebugLog(
|
|
3395
|
+
`ListRegistry.register: id="${id}" component="${entry.component}" items=${entry.itemCount ?? "unknown"} \u2014 total count: ${this.lists.size}`
|
|
2970
3396
|
);
|
|
2971
3397
|
this.notify(id, entry);
|
|
2972
3398
|
}
|
|
2973
3399
|
unregister(id) {
|
|
2974
3400
|
this.lists.delete(id);
|
|
2975
|
-
|
|
3401
|
+
appilotsDebugLog(`ListRegistry.unregister: id="${id}" \u2014 total count: ${this.lists.size}`);
|
|
2976
3402
|
this.notify(id, null);
|
|
2977
3403
|
}
|
|
2978
3404
|
get(id) {
|
|
@@ -3018,7 +3444,7 @@ var ElementRegistryImpl = class {
|
|
|
3018
3444
|
next.set(element.id, element);
|
|
3019
3445
|
}
|
|
3020
3446
|
this.elements = next;
|
|
3021
|
-
|
|
3447
|
+
appilotsDebugLog(`ElementRegistry.replaceAll: elements=${this.elements.size}`);
|
|
3022
3448
|
}
|
|
3023
3449
|
register(id, entry) {
|
|
3024
3450
|
this.elements.set(id, entry);
|
|
@@ -3119,26 +3545,60 @@ function sectionListFlatIndex(sections, section, localIndex) {
|
|
|
3119
3545
|
return base + localIndex;
|
|
3120
3546
|
}
|
|
3121
3547
|
|
|
3548
|
+
// src/auto/interceptGuard.ts
|
|
3549
|
+
var _intercept = null;
|
|
3550
|
+
var _failures = 0;
|
|
3551
|
+
var INTERCEPT_FAILURE_LIMIT = 25;
|
|
3552
|
+
function setInterceptor(fn) {
|
|
3553
|
+
_intercept = fn;
|
|
3554
|
+
_failures = 0;
|
|
3555
|
+
}
|
|
3556
|
+
function isInterceptionActive() {
|
|
3557
|
+
return _intercept !== null;
|
|
3558
|
+
}
|
|
3559
|
+
function safeIntercept(type, props) {
|
|
3560
|
+
const intercept = _intercept;
|
|
3561
|
+
if (!intercept) return null;
|
|
3562
|
+
try {
|
|
3563
|
+
return intercept(type, props);
|
|
3564
|
+
} catch (err) {
|
|
3565
|
+
_failures += 1;
|
|
3566
|
+
if (_failures === 1) {
|
|
3567
|
+
console.error(
|
|
3568
|
+
"[Appilots] Auto-tracking failed to inspect an element and passed it through unchanged. Your app is unaffected; the agent may not see this component. This is an Appilots bug \u2014 please report it: https://github.com/Axtern-Labs/Appilots/issues",
|
|
3569
|
+
err
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
3572
|
+
if (_failures >= INTERCEPT_FAILURE_LIMIT) {
|
|
3573
|
+
_intercept = null;
|
|
3574
|
+
console.error(
|
|
3575
|
+
`[Appilots] Auto-tracking disabled after ${INTERCEPT_FAILURE_LIMIT} failures. The agent will only see components registered explicitly via registerScreen(). Your app is unaffected.`
|
|
3576
|
+
);
|
|
3577
|
+
}
|
|
3578
|
+
return null;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3122
3582
|
// src/auto/enableAutoTracking.tsx
|
|
3123
3583
|
function extractId(props) {
|
|
3124
3584
|
const raw = props?.testID ?? props?.nativeID ?? props?.id ?? props?.accessibilityLabel ?? props?.name ?? props?.placeholder ?? (typeof props?.autoComplete === "string" && props.autoComplete !== "off" ? props.autoComplete : void 0);
|
|
3125
3585
|
if (raw && typeof raw === "string") {
|
|
3126
3586
|
const normalized = normalizeId2(raw);
|
|
3127
3587
|
if (normalized && normalized.length > 0) {
|
|
3128
|
-
|
|
3588
|
+
appilotsDebugLog(`extractId: raw="${raw}" \u2192 normalized="${normalized}"`);
|
|
3129
3589
|
return normalized;
|
|
3130
3590
|
}
|
|
3131
|
-
|
|
3591
|
+
appilotsDebugLog(`extractId: raw="${raw}" normalized to empty string, falling back`);
|
|
3132
3592
|
}
|
|
3133
3593
|
const hint = deriveSemanticInputId(props);
|
|
3134
3594
|
if (hint) {
|
|
3135
|
-
|
|
3136
|
-
`
|
|
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.`
|
|
3137
3597
|
);
|
|
3138
3598
|
return hint;
|
|
3139
3599
|
}
|
|
3140
|
-
|
|
3141
|
-
"
|
|
3600
|
+
appilotsDebugLog(
|
|
3601
|
+
"extractId: no identifiable prop found (testID/nativeID/accessibilityLabel/name/placeholder/autoComplete missing); skipping registration"
|
|
3142
3602
|
);
|
|
3143
3603
|
return null;
|
|
3144
3604
|
}
|
|
@@ -3156,15 +3616,17 @@ var TrackedTextInput = React2.forwardRef(function TrackedTextInput2(props, forwa
|
|
|
3156
3616
|
onChangeRef.current = props.onChangeText;
|
|
3157
3617
|
React2.useEffect(() => {
|
|
3158
3618
|
if (!id) {
|
|
3159
|
-
|
|
3619
|
+
appilotsDebugLog("TrackedTextInput useEffect: no id extracted, skipping registration");
|
|
3160
3620
|
return;
|
|
3161
3621
|
}
|
|
3162
|
-
|
|
3622
|
+
appilotsDebugLog(
|
|
3623
|
+
`TrackedTextInput useEffect: registering field id="${id}" label="${extractLabel(props)}"`
|
|
3624
|
+
);
|
|
3163
3625
|
const entry = {
|
|
3164
3626
|
kind: "field",
|
|
3165
3627
|
getValue: () => valueRef.current,
|
|
3166
3628
|
setValue: (v) => {
|
|
3167
|
-
|
|
3629
|
+
appilotsDebugLog(`TrackedTextInput setValue: id="${id}" value=${describeValue(v)}`);
|
|
3168
3630
|
onChangeRef.current?.(v);
|
|
3169
3631
|
inputRef.current?.setNativeProps?.({ text: v });
|
|
3170
3632
|
},
|
|
@@ -3174,15 +3636,18 @@ var TrackedTextInput = React2.forwardRef(function TrackedTextInput2(props, forwa
|
|
|
3174
3636
|
};
|
|
3175
3637
|
registry.register(id, entry);
|
|
3176
3638
|
return () => {
|
|
3177
|
-
|
|
3639
|
+
appilotsDebugLog(`TrackedTextInput useEffect cleanup: unregistering field id="${id}"`);
|
|
3178
3640
|
registry.unregister(id);
|
|
3179
3641
|
};
|
|
3180
3642
|
}, [id]);
|
|
3181
|
-
const mergedRef = React2.useCallback(
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
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
|
+
);
|
|
3186
3651
|
return _originalCreateElement(TextInput, { ...props, ref: mergedRef });
|
|
3187
3652
|
});
|
|
3188
3653
|
var TrackedSwitch = React2.forwardRef(function TrackedSwitch2(props, forwardedRef) {
|
|
@@ -3194,22 +3659,24 @@ var TrackedSwitch = React2.forwardRef(function TrackedSwitch2(props, forwardedRe
|
|
|
3194
3659
|
onChangeRef.current = props.onValueChange;
|
|
3195
3660
|
React2.useEffect(() => {
|
|
3196
3661
|
if (!id) {
|
|
3197
|
-
|
|
3662
|
+
appilotsDebugLog("TrackedSwitch useEffect: no id extracted, skipping registration");
|
|
3198
3663
|
return;
|
|
3199
3664
|
}
|
|
3200
|
-
|
|
3665
|
+
appilotsDebugLog(
|
|
3666
|
+
`TrackedSwitch useEffect: registering toggle id="${id}" label="${extractLabel(props)}"`
|
|
3667
|
+
);
|
|
3201
3668
|
const entry = {
|
|
3202
3669
|
kind: "toggle",
|
|
3203
3670
|
getValue: () => valueRef.current,
|
|
3204
3671
|
setValue: (v) => {
|
|
3205
|
-
|
|
3672
|
+
appilotsDebugLog(`TrackedSwitch setValue called: id="${id}" value=${v}`);
|
|
3206
3673
|
onChangeRef.current?.(v);
|
|
3207
3674
|
},
|
|
3208
3675
|
label: extractLabel(props)
|
|
3209
3676
|
};
|
|
3210
3677
|
registry.register(id, entry);
|
|
3211
3678
|
return () => {
|
|
3212
|
-
|
|
3679
|
+
appilotsDebugLog(`TrackedSwitch useEffect cleanup: unregistering toggle id="${id}"`);
|
|
3213
3680
|
registry.unregister(id);
|
|
3214
3681
|
};
|
|
3215
3682
|
}, [id]);
|
|
@@ -3229,21 +3696,25 @@ function getTrackedPressable(OriginalComponent) {
|
|
|
3229
3696
|
return;
|
|
3230
3697
|
}
|
|
3231
3698
|
if (!id || !props.onPress) {
|
|
3232
|
-
|
|
3699
|
+
appilotsDebugLog(
|
|
3700
|
+
`TrackedPressable useEffect: skipping registration (id=${id}, hasOnPress=${!!props.onPress})`
|
|
3701
|
+
);
|
|
3233
3702
|
return;
|
|
3234
3703
|
}
|
|
3235
|
-
|
|
3704
|
+
appilotsDebugLog(
|
|
3705
|
+
`TrackedPressable useEffect: registering target id="${id}" label="${extractLabel(props)}"`
|
|
3706
|
+
);
|
|
3236
3707
|
const entry = {
|
|
3237
3708
|
kind: "target",
|
|
3238
3709
|
press: () => {
|
|
3239
|
-
|
|
3710
|
+
appilotsDebugLog(`TrackedPressable press called: id="${id}"`);
|
|
3240
3711
|
onPressRef.current?.();
|
|
3241
3712
|
},
|
|
3242
3713
|
label: extractLabel(props)
|
|
3243
3714
|
};
|
|
3244
3715
|
registry.register(id, entry);
|
|
3245
3716
|
return () => {
|
|
3246
|
-
|
|
3717
|
+
appilotsDebugLog(`TrackedPressable useEffect cleanup: unregistering target id="${id}"`);
|
|
3247
3718
|
registry.unregister(id);
|
|
3248
3719
|
};
|
|
3249
3720
|
}, [id, !!props.onPress]);
|
|
@@ -3252,12 +3723,7 @@ function getTrackedPressable(OriginalComponent) {
|
|
|
3252
3723
|
_pressableWrapperCache.set(OriginalComponent, cached);
|
|
3253
3724
|
return cached;
|
|
3254
3725
|
}
|
|
3255
|
-
var LIST_COMPONENT_NAMES = /* @__PURE__ */ new Set([
|
|
3256
|
-
"FlatList",
|
|
3257
|
-
"SectionList",
|
|
3258
|
-
"VirtualizedList",
|
|
3259
|
-
"FlashList"
|
|
3260
|
-
]);
|
|
3726
|
+
var LIST_COMPONENT_NAMES = /* @__PURE__ */ new Set(["FlatList", "SectionList", "VirtualizedList", "FlashList"]);
|
|
3261
3727
|
var _listWrapperCache = /* @__PURE__ */ new Map();
|
|
3262
3728
|
var _generatedListIdCounter = 0;
|
|
3263
3729
|
function componentDisplayName(type) {
|
|
@@ -3311,12 +3777,10 @@ var PREVIEW_TEXT_KEYS = [
|
|
|
3311
3777
|
"label",
|
|
3312
3778
|
"description",
|
|
3313
3779
|
"descricao",
|
|
3314
|
-
"plate",
|
|
3315
|
-
"placa",
|
|
3316
|
-
"model",
|
|
3317
|
-
"modelo",
|
|
3318
3780
|
"email",
|
|
3319
|
-
"username"
|
|
3781
|
+
"username",
|
|
3782
|
+
"code",
|
|
3783
|
+
"codigo"
|
|
3320
3784
|
];
|
|
3321
3785
|
function projectItemText(item) {
|
|
3322
3786
|
if (item == null) return "";
|
|
@@ -3412,10 +3876,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3412
3876
|
const itemCount = resolveItemCount(props);
|
|
3413
3877
|
const refreshing = props.refreshing === true;
|
|
3414
3878
|
const label = listLabel(props);
|
|
3415
|
-
const dataPreview = React2.useMemo(
|
|
3416
|
-
() => buildDataPreview(props),
|
|
3417
|
-
[props.data, props.sections]
|
|
3418
|
-
);
|
|
3879
|
+
const dataPreview = React2.useMemo(() => buildDataPreview(props), [props.data, props.sections]);
|
|
3419
3880
|
const mergedRef = React2.useCallback(
|
|
3420
3881
|
(instance) => {
|
|
3421
3882
|
instanceRef.current = instance;
|
|
@@ -3450,7 +3911,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3450
3911
|
}
|
|
3451
3912
|
return false;
|
|
3452
3913
|
} catch (err) {
|
|
3453
|
-
|
|
3914
|
+
appilotsDebugWarn(`TrackedList scrollToIndex failed for "${listId}":`, err);
|
|
3454
3915
|
return false;
|
|
3455
3916
|
}
|
|
3456
3917
|
},
|
|
@@ -3469,7 +3930,7 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3469
3930
|
}
|
|
3470
3931
|
return false;
|
|
3471
3932
|
} catch (err) {
|
|
3472
|
-
|
|
3933
|
+
appilotsDebugWarn(`TrackedList scrollToOffset failed for "${listId}":`, err);
|
|
3473
3934
|
return false;
|
|
3474
3935
|
}
|
|
3475
3936
|
},
|
|
@@ -3513,26 +3974,148 @@ function getTrackedList(OriginalComponent, componentName) {
|
|
|
3513
3974
|
_listWrapperCache.set(OriginalComponent, cached);
|
|
3514
3975
|
return cached;
|
|
3515
3976
|
}
|
|
3516
|
-
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;
|
|
3517
4100
|
function enableAppilotsAutoTracking(options) {
|
|
3518
|
-
if (
|
|
3519
|
-
|
|
4101
|
+
if (_enabled2) return;
|
|
4102
|
+
_enabled2 = true;
|
|
3520
4103
|
const { trackPressable = true, debug = false } = options ?? {};
|
|
3521
4104
|
if (debug) {
|
|
3522
4105
|
componentRegistry.subscribe((id, entry) => {
|
|
3523
4106
|
if (entry) {
|
|
3524
|
-
|
|
4107
|
+
appilotsDebugLog(`Registered ${entry.kind}: "${id}" (${entry.label ?? "no label"})`);
|
|
3525
4108
|
} else {
|
|
3526
|
-
|
|
4109
|
+
appilotsDebugLog(`Unregistered: "${id}"`);
|
|
3527
4110
|
}
|
|
3528
4111
|
});
|
|
3529
4112
|
listRegistry.subscribe((id, entry) => {
|
|
3530
4113
|
if (entry) {
|
|
3531
|
-
|
|
3532
|
-
`
|
|
4114
|
+
appilotsDebugLog(
|
|
4115
|
+
`Registered list: "${id}" (${entry.component}, items=${entry.itemCount ?? "unknown"})`
|
|
3533
4116
|
);
|
|
3534
4117
|
} else {
|
|
3535
|
-
|
|
4118
|
+
appilotsDebugLog(`Unregistered list: "${id}"`);
|
|
3536
4119
|
}
|
|
3537
4120
|
});
|
|
3538
4121
|
}
|
|
@@ -3540,12 +4123,14 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3540
4123
|
_originalCreateElement = originalCreateElement;
|
|
3541
4124
|
function isComponent(type, target, name) {
|
|
3542
4125
|
if (type === target) {
|
|
3543
|
-
|
|
4126
|
+
appilotsDebugLog(`isComponent: matched "${name}" by reference equality`);
|
|
3544
4127
|
return true;
|
|
3545
4128
|
}
|
|
3546
4129
|
const dn = type?.displayName || type?.name || "";
|
|
3547
4130
|
if (dn === name) {
|
|
3548
|
-
|
|
4131
|
+
appilotsDebugLog(
|
|
4132
|
+
`isComponent: matched "${name}" by displayName/name (reference equality failed \u2014 possible monorepo issue)`
|
|
4133
|
+
);
|
|
3549
4134
|
return true;
|
|
3550
4135
|
}
|
|
3551
4136
|
return false;
|
|
@@ -3554,45 +4139,60 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3554
4139
|
if (!props || !type || props.__appilotsInternal) return null;
|
|
3555
4140
|
const displayName = componentDisplayName(type);
|
|
3556
4141
|
if (LIST_COMPONENT_NAMES.has(displayName)) {
|
|
3557
|
-
|
|
4142
|
+
appilotsDebugLog(`intercepted list component "${displayName}"`);
|
|
3558
4143
|
return { type: getTrackedList(type, displayName), props };
|
|
3559
4144
|
}
|
|
4145
|
+
if (SCROLLABLE_COMPONENT_NAMES.has(displayName)) {
|
|
4146
|
+
appilotsDebugLog(`intercepted scrollable "${displayName}"`);
|
|
4147
|
+
return { type: getTrackedScrollView(type, displayName), props };
|
|
4148
|
+
}
|
|
3560
4149
|
if (isComponent(type, TextInput, "TextInput")) {
|
|
3561
4150
|
if (props.testID || props.accessibilityLabel || props.placeholder) {
|
|
3562
|
-
|
|
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
|
+
);
|
|
3563
4156
|
return { type: TrackedTextInput, props };
|
|
3564
4157
|
}
|
|
3565
4158
|
}
|
|
3566
4159
|
if (isComponent(type, Switch, "Switch")) {
|
|
3567
4160
|
if (props.testID || props.accessibilityLabel) {
|
|
3568
|
-
|
|
4161
|
+
appilotsDebugLog(
|
|
4162
|
+
`intercepted Switch (testID="${props.testID}", label="${props.accessibilityLabel}")`
|
|
4163
|
+
);
|
|
3569
4164
|
return { type: TrackedSwitch, props };
|
|
3570
4165
|
}
|
|
3571
4166
|
}
|
|
3572
4167
|
if (isComponent(type, TouchableOpacity, "TouchableOpacity") || trackPressable && isComponent(type, Pressable, "Pressable")) {
|
|
3573
4168
|
if ((props.testID || props.accessibilityLabel) && props.onPress) {
|
|
3574
|
-
|
|
4169
|
+
appilotsDebugLog(
|
|
4170
|
+
`intercepted Pressable/TouchableOpacity (testID="${props.testID}", label="${props.accessibilityLabel}")`
|
|
4171
|
+
);
|
|
3575
4172
|
return { type: getTrackedPressable(type), props };
|
|
3576
4173
|
}
|
|
3577
4174
|
}
|
|
3578
4175
|
return null;
|
|
3579
4176
|
}
|
|
4177
|
+
setInterceptor(interceptType);
|
|
3580
4178
|
React2.createElement = function(type, props, ...children) {
|
|
3581
|
-
const replacement =
|
|
4179
|
+
const replacement = safeIntercept(type, props);
|
|
3582
4180
|
if (replacement) {
|
|
3583
4181
|
return originalCreateElement(replacement.type, replacement.props, ...children);
|
|
3584
4182
|
}
|
|
3585
4183
|
return originalCreateElement(type, props, ...children);
|
|
3586
4184
|
};
|
|
3587
|
-
|
|
3588
|
-
|
|
4185
|
+
_createElementPatched = true;
|
|
4186
|
+
appilotsDebugLog("Auto-tracking: React.createElement patched");
|
|
3589
4187
|
try {
|
|
3590
4188
|
const jsxRuntime = __require("react/jsx-runtime");
|
|
3591
4189
|
if (jsxRuntime) {
|
|
3592
4190
|
_patchJsxRuntimeModule(jsxRuntime);
|
|
3593
4191
|
}
|
|
3594
4192
|
} catch (e) {
|
|
3595
|
-
|
|
4193
|
+
appilotsDebugLog(
|
|
4194
|
+
"Auto-tracking: react/jsx-runtime not available internally (will be patched by metro shim)"
|
|
4195
|
+
);
|
|
3596
4196
|
}
|
|
3597
4197
|
try {
|
|
3598
4198
|
const jsxDevRuntime = __require("react/jsx-dev-runtime");
|
|
@@ -3600,104 +4200,121 @@ function enableAppilotsAutoTracking(options) {
|
|
|
3600
4200
|
_patchJsxDevRuntimeModule(jsxDevRuntime);
|
|
3601
4201
|
}
|
|
3602
4202
|
} catch (e) {
|
|
3603
|
-
|
|
4203
|
+
appilotsDebugLog(
|
|
4204
|
+
"Auto-tracking: react/jsx-dev-runtime not available internally (will be patched by metro shim)"
|
|
4205
|
+
);
|
|
3604
4206
|
}
|
|
3605
4207
|
}
|
|
3606
|
-
var _interceptType = null;
|
|
3607
4208
|
var _jsxRuntimePatched = false;
|
|
3608
4209
|
var _jsxDevRuntimePatched = false;
|
|
4210
|
+
var _createElementPatched = false;
|
|
3609
4211
|
function _patchJsxRuntimeModule(jsxRuntime) {
|
|
3610
|
-
if (_jsxRuntimePatched || !
|
|
4212
|
+
if (_jsxRuntimePatched || !isInterceptionActive() || !jsxRuntime) return;
|
|
3611
4213
|
_jsxRuntimePatched = true;
|
|
3612
|
-
const interceptType = _interceptType;
|
|
3613
4214
|
const origJsx = jsxRuntime.jsx;
|
|
3614
4215
|
const origJsxs = jsxRuntime.jsxs;
|
|
3615
4216
|
if (origJsx) {
|
|
3616
4217
|
jsxRuntime.jsx = function(type, props, key) {
|
|
3617
|
-
const replacement =
|
|
4218
|
+
const replacement = safeIntercept(type, props);
|
|
3618
4219
|
if (replacement) {
|
|
3619
4220
|
return origJsx(replacement.type, replacement.props, key);
|
|
3620
4221
|
}
|
|
3621
4222
|
return origJsx(type, props, key);
|
|
3622
4223
|
};
|
|
3623
|
-
|
|
4224
|
+
appilotsDebugLog("Auto-tracking: react/jsx-runtime jsx() patched");
|
|
3624
4225
|
}
|
|
3625
4226
|
if (origJsxs) {
|
|
3626
4227
|
jsxRuntime.jsxs = function(type, props, key) {
|
|
3627
|
-
const replacement =
|
|
4228
|
+
const replacement = safeIntercept(type, props);
|
|
3628
4229
|
if (replacement) {
|
|
3629
4230
|
return origJsxs(replacement.type, replacement.props, key);
|
|
3630
4231
|
}
|
|
3631
4232
|
return origJsxs(type, props, key);
|
|
3632
4233
|
};
|
|
3633
|
-
|
|
4234
|
+
appilotsDebugLog("Auto-tracking: react/jsx-runtime jsxs() patched");
|
|
3634
4235
|
}
|
|
3635
4236
|
}
|
|
3636
4237
|
function _patchJsxDevRuntimeModule(jsxDevRuntime) {
|
|
3637
|
-
if (_jsxDevRuntimePatched || !
|
|
4238
|
+
if (_jsxDevRuntimePatched || !isInterceptionActive() || !jsxDevRuntime) return;
|
|
3638
4239
|
_jsxDevRuntimePatched = true;
|
|
3639
|
-
const interceptType = _interceptType;
|
|
3640
4240
|
if (jsxDevRuntime.jsxDEV) {
|
|
3641
4241
|
const origJsxDEV = jsxDevRuntime.jsxDEV;
|
|
3642
4242
|
jsxDevRuntime.jsxDEV = function(type, props, key, isStaticChildren, source, self) {
|
|
3643
|
-
const replacement =
|
|
4243
|
+
const replacement = safeIntercept(type, props);
|
|
3644
4244
|
if (replacement) {
|
|
3645
4245
|
return origJsxDEV(replacement.type, replacement.props, key, isStaticChildren, source, self);
|
|
3646
4246
|
}
|
|
3647
4247
|
return origJsxDEV(type, props, key, isStaticChildren, source, self);
|
|
3648
4248
|
};
|
|
3649
|
-
|
|
4249
|
+
appilotsDebugLog("Auto-tracking: react/jsx-dev-runtime jsxDEV() patched");
|
|
3650
4250
|
}
|
|
3651
4251
|
}
|
|
3652
4252
|
function _patchJsxRuntimes(jsxRuntime, jsxDevRuntime) {
|
|
3653
|
-
if (!
|
|
3654
|
-
|
|
4253
|
+
if (!isInterceptionActive()) {
|
|
4254
|
+
appilotsDebugWarn("_patchJsxRuntimes called but auto-tracking is not enabled");
|
|
3655
4255
|
return;
|
|
3656
4256
|
}
|
|
3657
|
-
|
|
4257
|
+
appilotsDebugLog("_patchJsxRuntimes: patching from app context (metro shim)");
|
|
3658
4258
|
if (jsxRuntime) _patchJsxRuntimeModule(jsxRuntime);
|
|
3659
4259
|
if (jsxDevRuntime) _patchJsxDevRuntimeModule(jsxDevRuntime);
|
|
3660
4260
|
}
|
|
3661
4261
|
function isAutoTrackingEnabled() {
|
|
3662
|
-
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
|
+
};
|
|
3663
4272
|
}
|
|
3664
4273
|
|
|
3665
4274
|
// src/core/initAppilots.ts
|
|
3666
4275
|
var _globalConfig = null;
|
|
3667
4276
|
var _autoConfigAttempted = false;
|
|
3668
4277
|
function initAppilots(config) {
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
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;
|
|
3672
4283
|
}
|
|
3673
4284
|
_globalConfig = config;
|
|
3674
4285
|
const autoTrackingEnabled = config.autoTracking?.enabled !== false;
|
|
3675
4286
|
if (autoTrackingEnabled) {
|
|
3676
|
-
|
|
4287
|
+
appilotsDebugLog(
|
|
4288
|
+
"initAppilots: auto-tracking is enabled, calling enableAppilotsAutoTracking()"
|
|
4289
|
+
);
|
|
3677
4290
|
enableAppilotsAutoTracking({
|
|
3678
4291
|
trackPressable: config.autoTracking?.trackPressable ?? true,
|
|
3679
4292
|
debug: config.debug ?? false
|
|
3680
4293
|
});
|
|
3681
4294
|
} else {
|
|
3682
|
-
|
|
4295
|
+
appilotsDebugLog("initAppilots: auto-tracking is explicitly disabled");
|
|
3683
4296
|
}
|
|
3684
4297
|
}
|
|
3685
4298
|
function tryAutoConfig() {
|
|
3686
4299
|
if (_globalConfig || _autoConfigAttempted) {
|
|
3687
|
-
|
|
4300
|
+
appilotsDebugLog(
|
|
4301
|
+
`tryAutoConfig: skipping (alreadyConfigured=${!!_globalConfig}, alreadyAttempted=${_autoConfigAttempted})`
|
|
4302
|
+
);
|
|
3688
4303
|
return;
|
|
3689
4304
|
}
|
|
3690
4305
|
_autoConfigAttempted = true;
|
|
3691
4306
|
try {
|
|
3692
4307
|
const rc = globalThis.__APPILOTS_RC__;
|
|
3693
4308
|
if (rc && typeof rc === "object" && rc.projectId) {
|
|
3694
|
-
|
|
4309
|
+
appilotsDebugLog(
|
|
4310
|
+
`tryAutoConfig: found globalThis.__APPILOTS_RC__ with projectId="${rc.projectId}"`
|
|
4311
|
+
);
|
|
3695
4312
|
initAppilots(rc);
|
|
3696
4313
|
} else {
|
|
3697
|
-
|
|
4314
|
+
appilotsDebugLog("tryAutoConfig: globalThis.__APPILOTS_RC__ not found or missing projectId");
|
|
3698
4315
|
}
|
|
3699
4316
|
} catch {
|
|
3700
|
-
|
|
4317
|
+
appilotsDebugWarn("tryAutoConfig: error reading globalThis.__APPILOTS_RC__");
|
|
3701
4318
|
}
|
|
3702
4319
|
}
|
|
3703
4320
|
function getGlobalConfig() {
|
|
@@ -3714,13 +4331,20 @@ var _diagnostics = {
|
|
|
3714
4331
|
};
|
|
3715
4332
|
function setFiberRoot(fiber) {
|
|
3716
4333
|
if (_fiberRoot !== fiber) {
|
|
3717
|
-
|
|
4334
|
+
appilotsDebugLog(`Introspection: fiber root captured (type=${typeName(fiber)})`);
|
|
3718
4335
|
_fiberRoot = fiber;
|
|
3719
4336
|
}
|
|
3720
4337
|
_diagnostics.captured = true;
|
|
3721
4338
|
}
|
|
3722
4339
|
function getFiberRoot() {
|
|
3723
|
-
return _fiberRoot;
|
|
4340
|
+
return resolveCurrentRoot(_fiberRoot);
|
|
4341
|
+
}
|
|
4342
|
+
function resolveCurrentRoot(fiber) {
|
|
4343
|
+
const current = fiber?.stateNode?.current;
|
|
4344
|
+
if (current && (current === fiber || current.alternate === fiber)) {
|
|
4345
|
+
return current;
|
|
4346
|
+
}
|
|
4347
|
+
return fiber;
|
|
3724
4348
|
}
|
|
3725
4349
|
function recordIntrospectionFailure(reason, reactVersion) {
|
|
3726
4350
|
_diagnostics.failureCount += 1;
|
|
@@ -3729,6 +4353,13 @@ function recordIntrospectionFailure(reason, reactVersion) {
|
|
|
3729
4353
|
_diagnostics.reactVersion = reactVersion;
|
|
3730
4354
|
}
|
|
3731
4355
|
}
|
|
4356
|
+
function recordRenderFailure(reactVersion) {
|
|
4357
|
+
_fiberRoot = null;
|
|
4358
|
+
_diagnostics.captured = false;
|
|
4359
|
+
_diagnostics.failureCount += 1;
|
|
4360
|
+
_diagnostics.failureReason = "render-crashed";
|
|
4361
|
+
_diagnostics.reactVersion = reactVersion;
|
|
4362
|
+
}
|
|
3732
4363
|
function getIntrospectionDiagnostics() {
|
|
3733
4364
|
return { ..._diagnostics };
|
|
3734
4365
|
}
|
|
@@ -3737,182 +4368,976 @@ function typeName(fiber) {
|
|
|
3737
4368
|
if (!t) return "unknown";
|
|
3738
4369
|
if (typeof t === "string") return t;
|
|
3739
4370
|
if (typeof t === "function") return t.displayName ?? t.name ?? "anonymous";
|
|
3740
|
-
if (typeof t === "object")
|
|
4371
|
+
if (typeof t === "object")
|
|
4372
|
+
return t.displayName ?? t.render?.displayName ?? t.render?.name ?? "forwardRef/memo";
|
|
3741
4373
|
return String(t);
|
|
3742
4374
|
}
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
4375
|
+
|
|
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 });
|
|
3754
4440
|
}
|
|
3755
|
-
let top = fiber;
|
|
3756
|
-
while (top.return) top = top.return;
|
|
3757
|
-
setFiberRoot(top);
|
|
3758
|
-
}
|
|
3759
|
-
render() {
|
|
3760
|
-
return this.props.children ?? null;
|
|
3761
|
-
}
|
|
3762
|
-
};
|
|
3763
|
-
function AppilotsFiberRoot({
|
|
3764
|
-
children,
|
|
3765
|
-
onUnavailable
|
|
3766
|
-
}) {
|
|
3767
|
-
const Internals = React2.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_IN_THIS_VERSION_OF_REACT;
|
|
3768
|
-
const ownerFiber = Internals?.ReactCurrentOwner?.current;
|
|
3769
|
-
if (ownerFiber && !getFiberRoot()) {
|
|
3770
|
-
setFiberRoot(ownerFiber);
|
|
3771
4441
|
}
|
|
3772
|
-
|
|
4442
|
+
if (consulted) _asked += 1;
|
|
4443
|
+
return null;
|
|
3773
4444
|
}
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
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;
|
|
3779
4450
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
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) } };
|
|
3783
4458
|
}
|
|
3784
|
-
function
|
|
3785
|
-
if (
|
|
3786
|
-
|
|
4459
|
+
function withRect(node, reading) {
|
|
4460
|
+
if (reading.rect) _rectByNode.set(node, reading.rect);
|
|
4461
|
+
return node;
|
|
3787
4462
|
}
|
|
3788
|
-
function
|
|
3789
|
-
if (!
|
|
3790
|
-
|
|
4463
|
+
function rectForNode(node) {
|
|
4464
|
+
if (!node || typeof node !== "object") return void 0;
|
|
4465
|
+
return _rectByNode.get(node);
|
|
3791
4466
|
}
|
|
3792
4467
|
|
|
3793
|
-
// src/
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
console.log(`[Appilots] AppilotsProvider: configProp=${!!configProp}, globalConfig=${!!globalConfig}`);
|
|
3800
|
-
const config = configProp ?? (globalConfig ? {
|
|
3801
|
-
projectId: globalConfig.projectId,
|
|
3802
|
-
apiBaseUrl: globalConfig.apiBaseUrl,
|
|
3803
|
-
apiKey: globalConfig.apiKey,
|
|
3804
|
-
permissions: globalConfig.permissions,
|
|
3805
|
-
debug: globalConfig.debug,
|
|
3806
|
-
appVersion: globalConfig.appVersion,
|
|
3807
|
-
mcpVersion: globalConfig.mcpVersion,
|
|
3808
|
-
fetchPersonalization: globalConfig.fetchPersonalization
|
|
3809
|
-
} : null);
|
|
3810
|
-
if (!config?.projectId) {
|
|
3811
|
-
throw new Error(
|
|
3812
|
-
"AppilotsProvider: No config found. Add withAppilots() to your metro.config.js and create a .appilotsrc file, or pass a config prop directly."
|
|
3813
|
-
);
|
|
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;
|
|
3814
4474
|
}
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
);
|
|
3839
|
-
const
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
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;
|
|
4479
|
+
}
|
|
4480
|
+
if (type.type) return getTypeName(type.type);
|
|
4481
|
+
}
|
|
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("");
|
|
4493
|
+
}
|
|
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;
|
|
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;
|
|
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;
|
|
4805
|
+
}
|
|
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() {
|
|
4836
|
+
return {
|
|
4837
|
+
Text: 0,
|
|
4838
|
+
TextInput: 0,
|
|
4839
|
+
Switch: 0,
|
|
4840
|
+
ActivityIndicator: 0,
|
|
4841
|
+
Modal: 0,
|
|
4842
|
+
adjustable: 0,
|
|
4843
|
+
pressable: 0,
|
|
4844
|
+
listContainers: 0
|
|
4845
|
+
};
|
|
4846
|
+
}
|
|
4847
|
+
function totalDetectorHits(counts) {
|
|
4848
|
+
return counts.Text + counts.TextInput + counts.Switch + counts.ActivityIndicator + counts.Modal + counts.adjustable + counts.pressable + counts.listContainers;
|
|
4849
|
+
}
|
|
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;
|
|
4859
|
+
}
|
|
4860
|
+
var _lastWalk = null;
|
|
4861
|
+
function getLastWalkDiagnostics() {
|
|
4862
|
+
return _lastWalk ? { ..._lastWalk, detectors: { ..._lastWalk.detectors } } : null;
|
|
4863
|
+
}
|
|
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 });
|
|
5099
|
+
}
|
|
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
|
|
3852
5179
|
);
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
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
|
+
};
|
|
3861
5194
|
});
|
|
3862
|
-
|
|
3863
|
-
|
|
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
|
|
3864
5215
|
};
|
|
3865
|
-
}
|
|
3866
|
-
|
|
3867
|
-
(
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
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;
|
|
3871
5227
|
}
|
|
3872
|
-
function
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
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);
|
|
3876
5247
|
}
|
|
3877
|
-
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;
|
|
3878
5303
|
}
|
|
3879
5304
|
|
|
3880
5305
|
// src/introspection/probeLoadingState.ts
|
|
3881
|
-
function
|
|
5306
|
+
function getTypeName2(type) {
|
|
3882
5307
|
if (!type) return null;
|
|
3883
5308
|
if (typeof type === "string") return type;
|
|
3884
5309
|
if (typeof type === "function") return type.displayName ?? type.name ?? null;
|
|
3885
5310
|
if (typeof type === "object") {
|
|
3886
5311
|
if (type.displayName) return type.displayName;
|
|
3887
5312
|
if (type.render) return type.render.displayName ?? type.render.name ?? null;
|
|
3888
|
-
if (type.type) return
|
|
5313
|
+
if (type.type) return getTypeName2(type.type);
|
|
3889
5314
|
}
|
|
3890
5315
|
return null;
|
|
3891
5316
|
}
|
|
3892
|
-
function
|
|
3893
|
-
return
|
|
5317
|
+
function matchesName2(fiber, name) {
|
|
5318
|
+
return getTypeName2(fiber.type) === name || getTypeName2(fiber.elementType) === name;
|
|
3894
5319
|
}
|
|
3895
5320
|
var SKELETON_NAME_RE = /skeleton|shimmer|contentloader/i;
|
|
3896
5321
|
function looksLikeSkeleton(fiber) {
|
|
3897
|
-
const name =
|
|
5322
|
+
const name = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
|
|
3898
5323
|
return !!name && SKELETON_NAME_RE.test(name);
|
|
3899
5324
|
}
|
|
3900
|
-
var
|
|
5325
|
+
var SELF_REFERENTIAL_COMPONENTS2 = /* @__PURE__ */ new Set([
|
|
3901
5326
|
"AppilotsChat",
|
|
3902
5327
|
"AppilotsChatInner",
|
|
3903
5328
|
"ActionBreadcrumb",
|
|
3904
5329
|
"ConfirmDialog"
|
|
3905
5330
|
]);
|
|
3906
|
-
function
|
|
5331
|
+
function shouldSkipSubtree2(fiber) {
|
|
3907
5332
|
const props = fiber.memoizedProps;
|
|
3908
5333
|
if (!props) return false;
|
|
3909
|
-
const typeName2 =
|
|
3910
|
-
if (typeName2 &&
|
|
5334
|
+
const typeName2 = getTypeName2(fiber.type) ?? getTypeName2(fiber.elementType);
|
|
5335
|
+
if (typeName2 && SELF_REFERENTIAL_COMPONENTS2.has(typeName2)) return true;
|
|
3911
5336
|
if (props.__appilotsSkip === true || props["data-appilots-skip"] === true || props.appilotsSkip === true) {
|
|
3912
5337
|
return true;
|
|
3913
5338
|
}
|
|
3914
|
-
if (
|
|
3915
|
-
if (
|
|
5339
|
+
if (isModalFiber(fiber) && props.visible === false) return true;
|
|
5340
|
+
if (matchesName2(fiber, "Screen") && props.activityState === 0) return true;
|
|
3916
5341
|
const style = props.style;
|
|
3917
5342
|
if (style) {
|
|
3918
5343
|
const flat = Array.isArray(style) ? Object.assign({}, ...style.filter(Boolean)) : style;
|
|
@@ -3921,7 +5346,7 @@ function shouldSkipSubtree(fiber) {
|
|
|
3921
5346
|
}
|
|
3922
5347
|
return false;
|
|
3923
5348
|
}
|
|
3924
|
-
var
|
|
5349
|
+
var TOUCHABLE_NAMES2 = /* @__PURE__ */ new Set([
|
|
3925
5350
|
"TouchableOpacity",
|
|
3926
5351
|
"TouchableHighlight",
|
|
3927
5352
|
"TouchableWithoutFeedback",
|
|
@@ -3929,7 +5354,7 @@ var TOUCHABLE_NAMES = /* @__PURE__ */ new Set([
|
|
|
3929
5354
|
"Pressable"
|
|
3930
5355
|
]);
|
|
3931
5356
|
function isTouchable(fiber) {
|
|
3932
|
-
return
|
|
5357
|
+
return TOUCHABLE_NAMES2.has(getTypeName2(fiber.type) ?? "") || TOUCHABLE_NAMES2.has(getTypeName2(fiber.elementType) ?? "");
|
|
3933
5358
|
}
|
|
3934
5359
|
function pickId(props) {
|
|
3935
5360
|
return props?.testID ?? props?.accessibilityLabel ?? void 0;
|
|
@@ -3965,31 +5390,34 @@ function probeLoadingState(pressedComponentId, route) {
|
|
|
3965
5390
|
visited.add(fiber);
|
|
3966
5391
|
result.visitedFibers++;
|
|
3967
5392
|
if (fiber.sibling) stack.push(fiber.sibling);
|
|
3968
|
-
if (
|
|
5393
|
+
if (shouldSkipSubtree2(fiber)) continue;
|
|
3969
5394
|
const props = fiber.memoizedProps ?? {};
|
|
3970
|
-
if (
|
|
5395
|
+
if (matchesName2(fiber, "ActivityIndicator") || looksLikeSkeleton(fiber)) {
|
|
3971
5396
|
result.loading = true;
|
|
3972
|
-
} else if (
|
|
5397
|
+
} else if (isModalFiber(fiber)) {
|
|
3973
5398
|
result.modalOpen = true;
|
|
5399
|
+
} else if (isSwitchLike(fiber, props)) {
|
|
5400
|
+
const id = pickId(props);
|
|
5401
|
+
toggleSigs.push(`${id ?? "?"}:${switchValue(props) ? 1 : 0}`);
|
|
3974
5402
|
} else if (isTouchable(fiber)) {
|
|
3975
5403
|
if (props.__appilotsInternal !== true) {
|
|
3976
5404
|
const id = pickId(props);
|
|
3977
5405
|
const disabled = !!props.disabled;
|
|
3978
|
-
|
|
5406
|
+
const state = props.accessibilityState ?? {};
|
|
5407
|
+
const on = state.checked === true || state.selected === true;
|
|
5408
|
+
buttonSigs.push(`${id ?? "?"}:${disabled ? 1 : 0}:${on ? 1 : 0}`);
|
|
3979
5409
|
if (pressedComponentId && idMatches(id, pressedComponentId)) {
|
|
3980
5410
|
result.pressedFound = true;
|
|
3981
5411
|
if (disabled) result.pressedDisabled = true;
|
|
3982
5412
|
}
|
|
3983
5413
|
}
|
|
3984
|
-
} else if (
|
|
5414
|
+
} else if (matchesName2(fiber, "TextInput")) {
|
|
3985
5415
|
if (props.__appilotsInternal !== true) {
|
|
3986
5416
|
const id = pickId(props);
|
|
3987
5417
|
const editable = props.editable !== false;
|
|
3988
|
-
|
|
5418
|
+
const filled = typeof props.value === "string" && props.value.length > 0;
|
|
5419
|
+
inputSigs.push(`${id ?? "?"}:${editable ? 1 : 0}:${filled ? 1 : 0}`);
|
|
3989
5420
|
}
|
|
3990
|
-
} else if (matchesName(fiber, "Switch")) {
|
|
3991
|
-
const id = pickId(props);
|
|
3992
|
-
toggleSigs.push(`${id ?? "?"}:${props.value ? 1 : 0}`);
|
|
3993
5421
|
}
|
|
3994
5422
|
if (fiber.child) stack.push(fiber.child);
|
|
3995
5423
|
}
|
|
@@ -4000,11 +5428,48 @@ function probeLoadingState(pressedComponentId, route) {
|
|
|
4000
5428
|
return result;
|
|
4001
5429
|
}
|
|
4002
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
|
+
|
|
4003
5468
|
// src/navigation/navigationState.ts
|
|
4004
5469
|
var _currentScreen = null;
|
|
4005
5470
|
var _navigationRef = null;
|
|
4006
5471
|
function setCurrentScreen(screen) {
|
|
4007
|
-
|
|
5472
|
+
appilotsDebugLog(`setCurrentScreen: "${_currentScreen}" \u2192 "${screen}"`);
|
|
4008
5473
|
_currentScreen = screen;
|
|
4009
5474
|
}
|
|
4010
5475
|
function getCurrentScreen() {
|
|
@@ -4013,8 +5478,8 @@ function getCurrentScreen() {
|
|
|
4013
5478
|
const deepest = activePath[activePath.length - 1] ?? null;
|
|
4014
5479
|
if (deepest && deepest !== _currentScreen) {
|
|
4015
5480
|
if (_currentScreen) {
|
|
4016
|
-
|
|
4017
|
-
`
|
|
5481
|
+
appilotsDebugLog(
|
|
5482
|
+
`getCurrentScreen: active path differs from cached \u2014 deepest="${deepest}", cached="${_currentScreen}". Updating cache.`
|
|
4018
5483
|
);
|
|
4019
5484
|
}
|
|
4020
5485
|
_currentScreen = deepest;
|
|
@@ -4025,7 +5490,9 @@ function getCurrentScreen() {
|
|
|
4025
5490
|
const liveScreen = getActiveRouteFromRef(_navigationRef);
|
|
4026
5491
|
if (liveScreen) {
|
|
4027
5492
|
if (liveScreen !== _currentScreen) {
|
|
4028
|
-
|
|
5493
|
+
appilotsDebugLog(
|
|
5494
|
+
`getCurrentScreen: live reading differs from cached \u2014 live="${liveScreen}", cached="${_currentScreen}". Updating cache.`
|
|
5495
|
+
);
|
|
4029
5496
|
_currentScreen = liveScreen;
|
|
4030
5497
|
}
|
|
4031
5498
|
return liveScreen;
|
|
@@ -4069,7 +5536,9 @@ function getActiveRouteNames() {
|
|
|
4069
5536
|
function routeNamesAtState(state) {
|
|
4070
5537
|
if (!state) return [];
|
|
4071
5538
|
if (Array.isArray(state.routeNames)) {
|
|
4072
|
-
return state.routeNames.filter(
|
|
5539
|
+
return state.routeNames.filter(
|
|
5540
|
+
(name) => typeof name === "string" && name.length > 0
|
|
5541
|
+
);
|
|
4073
5542
|
}
|
|
4074
5543
|
if (Array.isArray(state.routes)) {
|
|
4075
5544
|
return state.routes.map((route) => route?.name).filter((name) => typeof name === "string" && name.length > 0);
|
|
@@ -4186,16 +5655,6 @@ function getActiveRouteFromRef(navRef) {
|
|
|
4186
5655
|
}
|
|
4187
5656
|
}
|
|
4188
5657
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4189
|
-
function normalizeRouteFamily(name) {
|
|
4190
|
-
return name.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/(tab|list|screen|details?|navigator)$/i, "");
|
|
4191
|
-
}
|
|
4192
|
-
function routesBelongToSameFeature(a, b) {
|
|
4193
|
-
const na = normalizeRouteFamily(a);
|
|
4194
|
-
const nb = normalizeRouteFamily(b);
|
|
4195
|
-
if (!na || !nb) return false;
|
|
4196
|
-
if (na === nb) return true;
|
|
4197
|
-
return na.startsWith(nb) || nb.startsWith(na);
|
|
4198
|
-
}
|
|
4199
5658
|
function navigationTargetReached(target, fromScreen) {
|
|
4200
5659
|
const cur = getCurrentScreen();
|
|
4201
5660
|
const path = getActiveRouteNames();
|
|
@@ -4371,6 +5830,365 @@ async function waitForLoadingSettle(opts = {}) {
|
|
|
4371
5830
|
};
|
|
4372
5831
|
}
|
|
4373
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
|
+
|
|
4374
6192
|
// src/navigation/registerScreen.ts
|
|
4375
6193
|
var screenRegistry = /* @__PURE__ */ new Map();
|
|
4376
6194
|
function registerScreen(metadata) {
|
|
@@ -4386,4 +6204,4 @@ function clearScreenRegistry() {
|
|
|
4386
6204
|
screenRegistry.clear();
|
|
4387
6205
|
}
|
|
4388
6206
|
|
|
4389
|
-
export { ActionQueueMachine, AppilotsClient, AppilotsProvider, AppilotsRegistryProvider, ChatSessionMachine, OPTIONAL_STEP_AUTOMATION_HINT, RateLimitedError, SDK_VERSION, _patchJsxRuntimes, actionPressTargetId, appilotsDebugLog, appilotsDebugWarn, attachElementIdsToChoiceGroups, 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 };
|