xonecode 0.1.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.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
@@ -0,0 +1,595 @@
1
+ # XOne JavaScript — Patrones críticos, seguridad y rendimiento
2
+
3
+ > Fuente: `xone/v2/xone-help-docs/topics/03e-js-patrones-buenas-practicas.md` §9–§11. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
4
+
5
+ Contenido: §9 patrones críticos (lock/unlock, startBrowse/endBrowse, filter/restore, preservación de contexto en callbacks asíncronos) · §10 seguridad (prevención de SQL injection, validación de entrada, encriptación, manejo de credenciales) · §11 optimización y rendimiento
6
+
7
+ ---
8
+
9
+ ## 9. Patrones Críticos de Código
10
+
11
+ Patrones fundamentales que todo desarrollador XOne debe dominar. El uso incorrecto de estos patrones es la causa principal de errores y memory leaks.
12
+
13
+ ### 9.1 Patron lock/unlock (Modificación de Colecciones)
14
+
15
+ **Siempre** usar `finally` para garantizar que se ejecute `lock()`, incluso si hay error:
16
+
17
+ ```javascript
18
+ function agregarRegistro(nombreColl, datos) {
19
+ var coll = appData.getCollection(nombreColl);
20
+ coll.unlock();
21
+ try {
22
+ var obj = coll.createObject();
23
+ for (var key in datos) {
24
+ if (datos.hasOwnProperty(key)) {
25
+ obj[key] = datos[key];
26
+ }
27
+ }
28
+ coll.addItem(obj);
29
+ obj.save();
30
+ return true;
31
+ } catch(error) {
32
+ ui.showToast("Error: " + error);
33
+ return false;
34
+ } finally {
35
+ coll.lock(); // SIEMPRE se ejecuta
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### 9.2 Patron startBrowse/endBrowse (Navegación de Colecciones)
41
+
42
+ **Siempre** usar `finally` para garantizar que se ejecute `endBrowse()`:
43
+
44
+ ```javascript
45
+ function procesarColeccion(nombreColl) {
46
+ var coll = appData.getCollection(nombreColl);
47
+ coll.startBrowse();
48
+ try {
49
+ coll.moveFirst();
50
+ while (coll.getCurrentItem() != null) {
51
+ var obj = coll.getCurrentItem();
52
+ console.log(obj.MAP_NOMBRE);
53
+ coll.moveNext();
54
+ }
55
+ } finally {
56
+ coll.endBrowse(); // SIEMPRE se ejecuta
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### 9.3 Patron filter/restore (Filtrado Seguro)
62
+
63
+ **Siempre** guardar el filtro original y restaurarlo después de usar, idealmente en `finally`:
64
+
65
+ ```javascript
66
+ function procesarRegistrosActivos(nombreColl) {
67
+ var coll = appData.getCollection(nombreColl);
68
+ var filtroOriginal = coll.getFilter();
69
+
70
+ try {
71
+ coll.setFilter("ACTIVO = 1 AND ESTADO = 'PENDIENTE'");
72
+ coll.loadAll();
73
+
74
+ var count = coll.count();
75
+ for (var i = 0; i < count; i++) {
76
+ var obj = coll.get(i);
77
+ // Procesar...
78
+ }
79
+
80
+ coll.clear();
81
+ } finally {
82
+ coll.setFilter(filtroOriginal); // Restaurar SIEMPRE
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### 9.4 Patron de Preservacion de Contexto en Callbacks Asíncronos
88
+
89
+ En callbacks asíncronos de `$http`, `executeActionAfterDelay` y otros, el objeto `self` **puede cambiar de contexto**. Se debe guardar la referencia **antes** de la llamada:
90
+
91
+ ```javascript
92
+ function cargarDatosServidor(url) {
93
+ var contexto = self; // GUARDAR referencia antes del callback
94
+
95
+ $http.get(url,
96
+ function(sData) {
97
+ // INCORRECTO: self.MAP_DATO = sData;
98
+ contexto.MAP_DATO = sData;
99
+ ui.refresh("MAP_DATO");
100
+ },
101
+ function(nError, sDesc) {
102
+ ui.showToast("Error: " + sDesc);
103
+ }
104
+ );
105
+ }
106
+ ```
107
+
108
+ ### 9.5 Patron WaitDialog Seguro
109
+
110
+ **Siempre** ocultar el WaitDialog en un bloque `finally`:
111
+
112
+ ```javascript
113
+ function operacionLarga() {
114
+ ui.showWaitDialog("Procesando...");
115
+ try {
116
+ var coll = appData.getCollection("Datos");
117
+ coll.loadAll();
118
+ // ...procesar...
119
+ } catch(ex) {
120
+ ui.showToast("Error: " + ex);
121
+ } finally {
122
+ ui.hideWaitDialog();
123
+ }
124
+ }
125
+ ```
126
+
127
+ ### 9.6 Patron Cursor SQL Seguro
128
+
129
+ **Siempre** cerrar el cursor y la conexión en bloques `finally`:
130
+
131
+ ```javascript
132
+ function consultarDatos(query, parametros) {
133
+ var sqlManager = new SqlManager();
134
+ try {
135
+ sqlManager.openDatabase({
136
+ databasePath: "gestion.db",
137
+ useExistingConnection: true
138
+ });
139
+
140
+ var cursor = sqlManager.doRawQuery(query, parametros);
141
+ try {
142
+ if (cursor.getCount() > 0) {
143
+ cursor.moveToFirst();
144
+ return {
145
+ id: cursor.getInteger("ID"),
146
+ nombre: cursor.getString("NOMBRE")
147
+ };
148
+ }
149
+ return null;
150
+ } finally {
151
+ cursor.close();
152
+ }
153
+ } finally {
154
+ sqlManager.close();
155
+ }
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 10. Seguridad en JavaScript XOne
162
+
163
+ ### 10.1 Prevencion de SQL Injection
164
+
165
+ **Código VULNERABLE (NUNCA hacer esto):**
166
+
167
+ ```javascript
168
+ // PELIGROSO: concatenacion directa de input del usuario
169
+ function buscarUsuario(loginUsuario) {
170
+ let coll = appData.getCollection("Usuarios");
171
+ // Si loginUsuario = "' OR 1=1 --" obtendria todos los registros
172
+ let usuario = coll.findObject("LOGIN = '" + loginUsuario + "'");
173
+ return usuario;
174
+ }
175
+
176
+ // PELIGROSO: SQL directo sin parametrizar
177
+ function eliminarRegistro(id) {
178
+ appData.executeSql("DELETE FROM gen_productos WHERE ID = " + id);
179
+ }
180
+ ```
181
+
182
+ **Código SEGURO (hacer SIEMPRE esto):**
183
+
184
+ ```javascript
185
+ // SEGURO: SqlManager con parametros (consultas parametrizadas)
186
+ function buscarUsuarioSeguro(loginUsuario) {
187
+ let sqlManager = new SqlManager();
188
+ try {
189
+ sqlManager.openDatabase({
190
+ databasePath: "gestion.db",
191
+ useExistingConnection: true
192
+ });
193
+
194
+ let cursor = sqlManager.doRawQuery(
195
+ "SELECT * FROM gen_usuarios WHERE LOGIN=?",
196
+ loginUsuario // El parametro se escapa automaticamente
197
+ );
198
+ try {
199
+ if (cursor.getCount() > 0) {
200
+ cursor.moveToFirst();
201
+ return {
202
+ id : cursor.getInteger("ID"),
203
+ nombre: cursor.getString("NOMBRE"),
204
+ login : cursor.getString("LOGIN")
205
+ };
206
+ }
207
+ return null;
208
+ } finally {
209
+ cursor.close();
210
+ }
211
+ } finally {
212
+ sqlManager.close();
213
+ }
214
+ }
215
+
216
+ // SEGURO: Escapar comillas simples para findObject
217
+ function buscarObjetoSeguro(nombreColl, campo, valor) {
218
+ let valorEscapado = cstr(valor).replace(/'/g, "''");
219
+ let coll = appData.getCollection(nombreColl);
220
+ return coll.findObject(campo + "='" + valorEscapado + "'");
221
+ }
222
+
223
+ // SEGURO: Validar que sea numerico antes de concatenar
224
+ function eliminarRegistroSeguro(id) {
225
+ let nId = parseInt(id);
226
+ if (isNaN(nId) || nId <= 0) {
227
+ ui.showToast("ID no valido");
228
+ return;
229
+ }
230
+ appData.executeSql("DELETE FROM gen_productos WHERE ID = " + nId);
231
+ }
232
+ ```
233
+
234
+ ### 10.2 Validación de Entrada
235
+
236
+ ```javascript
237
+ // === Funciones de validación esenciales ===
238
+
239
+ function isEmpty(val) {
240
+ return val === undefined || val === null || val === "";
241
+ }
242
+
243
+ function cstr(val) {
244
+ if (val === undefined || val === null) return "";
245
+ return val.toString();
246
+ }
247
+
248
+ function cnum(val) {
249
+ if (val === undefined || val === null || val === "") return 0;
250
+ let num = parseFloat(val);
251
+ return isNaN(num) ? 0 : num;
252
+ }
253
+
254
+ // === Sanitizacion ===
255
+ function sanearEntrada(valor, maxLength) {
256
+ if (isEmpty(valor)) return "";
257
+ let sValor = cstr(valor).trim();
258
+ if (maxLength && sValor.length > maxLength) {
259
+ sValor = sValor.substring(0, maxLength);
260
+ }
261
+ return sValor;
262
+ }
263
+
264
+ function validarEmail(email) {
265
+ if (isEmpty(email)) return false;
266
+ return email.indexOf("@") > 0 && email.indexOf(".") > 0;
267
+ }
268
+
269
+ function validarTelefono(telefono) {
270
+ if (isEmpty(telefono)) return false;
271
+ let limpio = telefono.replace(/[^\d+]/g, '');
272
+ return limpio.length >= 9;
273
+ }
274
+
275
+ function validarRango(valor, min, max) {
276
+ let num = cnum(valor);
277
+ return num >= min && num <= max;
278
+ }
279
+
280
+ // === Patron completo antes de guardar ===
281
+ function validarFormulario() {
282
+ if (isEmpty(self.MAP_NOMBRE)) {
283
+ ui.showToast("El nombre es obligatorio");
284
+ return false;
285
+ }
286
+ if (cstr(self.MAP_NOMBRE).length > 100) {
287
+ ui.showToast("El nombre no puede superar 100 caracteres");
288
+ return false;
289
+ }
290
+ if (cnum(self.MAP_CANTIDAD) <= 0) {
291
+ ui.showToast("La cantidad debe ser mayor a 0");
292
+ return false;
293
+ }
294
+ if (!validarEmail(self.MAP_EMAIL)) {
295
+ ui.showToast("Email no valido");
296
+ return false;
297
+ }
298
+ return true;
299
+ }
300
+ ```
301
+
302
+ ### 10.3 Encriptación de Datos Sensibles
303
+
304
+ ```javascript
305
+ // === Encriptacion básica del framework ===
306
+ let encrypted = appData.encryptString("dato sensible");
307
+ let decrypted = appData.decryptString(encrypted);
308
+
309
+ // === API Crypto avanzada ===
310
+
311
+ // Hashing (unidireccional, para passwords)
312
+ function hashPassword(password) {
313
+ return crypto.sha256({
314
+ data : password,
315
+ outputFormat: "hex"
316
+ });
317
+ }
318
+
319
+ // Cifrado simetrico AES
320
+ function cifrarDato(texto) {
321
+ let aesKey = crypto.generateAesKey({
322
+ alias : "app_datos_key",
323
+ keySize : 256,
324
+ useSecureHardware: true,
325
+ useStrongBox : true
326
+ });
327
+
328
+ return crypto.encrypt({
329
+ data : texto,
330
+ dataFormat : "string",
331
+ algorithm : "AES/GCM/NoPadding",
332
+ key : aesKey,
333
+ outputFormat: "base64"
334
+ });
335
+ }
336
+
337
+ function descifrarDato(textoCifrado) {
338
+ let aesKey = crypto.generateAesKey({
339
+ alias : "app_datos_key",
340
+ keySize : 256,
341
+ useSecureHardware: true
342
+ });
343
+
344
+ return crypto.decrypt({
345
+ data : textoCifrado,
346
+ dataFormat : "base64",
347
+ algorithm : "AES/GCM/NoPadding",
348
+ key : aesKey,
349
+ outputFormat: "string"
350
+ });
351
+ }
352
+
353
+ // Cifrar/descifrar archivos
354
+ crypto.encrypt({
355
+ data: "documento.pdf", dataFormat: "file",
356
+ algorithm: "AES/GCM/NoPadding", key: aesKey,
357
+ outputFormat: "file", output: "documento.pdf.enc"
358
+ });
359
+
360
+ // Firma digital con clave pública/privada
361
+ let keyPair = crypto.generateKeyPair({
362
+ alias: "firma_app", algorithm: "EC", keySize: 384,
363
+ output: "key", outputFormat: "file", useSecureHardware: true
364
+ });
365
+
366
+ let signature = crypto.sign({
367
+ data: "datos a firmar", algorithm: "SHA256withECDSA",
368
+ privateKey: keyPair.getPrivateKey().toPem(), outputFormat: "base64"
369
+ });
370
+
371
+ // Encoding
372
+ let base64 = crypto.toBase64({ data: "texto", urlSafe: true });
373
+ let decoded = crypto.fromBase64({ data: base64 });
374
+
375
+ // Checksum
376
+ let crc32 = crypto.getChecksum({ type: "crc32", data: "texto" });
377
+ ```
378
+
379
+ ### 10.4 Manejo Seguro de Credenciales
380
+
381
+ ```javascript
382
+ // INCORRECTO: hardcodear passwords / loguear datos sensibles
383
+ let password = "admin123"; // NUNCA
384
+ console.log("Password: " + password); // NUNCA
385
+
386
+ // CORRECTO: macros globales encriptadas
387
+ function guardarTokenSesion(token) {
388
+ let tokenCifrado = appData.encryptString(token);
389
+ appData.setGlobalMacro("##SESSION_TOKEN##", tokenCifrado);
390
+ }
391
+
392
+ function obtenerTokenSesion() {
393
+ let tokenCifrado = appData.getGlobalMacro("##SESSION_TOKEN##");
394
+ if (isEmpty(tokenCifrado)) return null;
395
+ return appData.decryptString(tokenCifrado);
396
+ }
397
+
398
+ // CORRECTO: limpiar credenciales al cerrar sesion
399
+ function cerrarSesion() {
400
+ appData.setGlobalMacro("##SESSION_TOKEN##", "");
401
+ appData.setGlobalMacro("##USERID##", "");
402
+ appData.setGlobalMacro("##USERNAME##", "");
403
+ appData.setGlobalMacro("##USERROLE##", "");
404
+ appData.logout();
405
+ }
406
+
407
+ // CORRECTO: comunicaciones seguras
408
+ function crearRequestSeguro(token) {
409
+ return {
410
+ headers: {
411
+ "Authorization": "Bearer " + token,
412
+ "Content-Type" : "application/json"
413
+ },
414
+ parameters: {
415
+ connectTimeout : 30000,
416
+ readTimeout : 30000,
417
+ allowUnsafeCertificates: false, // NUNCA true en produccion
418
+ enablePinning : true
419
+ }
420
+ };
421
+ }
422
+ ```
423
+
424
+ ---
425
+
426
+ ## 11. Optimización y Rendimiento
427
+
428
+ ### 11.1 Minimizar Refreshes
429
+
430
+ ```javascript
431
+ // INCORRECTO: multiples refresh individuales
432
+ self.MAP_NOMBRE = "Juan";
433
+ ui.refresh("MAP_NOMBRE");
434
+ self.MAP_ESTADO = "Activo";
435
+ ui.refresh("MAP_ESTADO");
436
+
437
+ // CORRECTO: un solo refresh con multiples campos
438
+ self.MAP_NOMBRE = "Juan";
439
+ self.MAP_ESTADO = "Activo";
440
+ ui.refresh("MAP_NOMBRE,MAP_ESTADO");
441
+
442
+ // Solo actualizar el valor sin reconstruir la vista
443
+ ui.refreshValue("MAP_CAMPO");
444
+ ```
445
+
446
+ ### 11.2 Gestion de Colecciones
447
+
448
+ ```javascript
449
+ // Usar lock/unlock para evitar recargas innecesarias
450
+ function modificarContentEficiente(contentName, datos) {
451
+ let content = self.getContents(contentName);
452
+ content.unlock();
453
+ try {
454
+ let obj = content.createObject();
455
+ for (let key in datos) obj[key] = datos[key];
456
+ content.addItem(obj);
457
+ } finally {
458
+ content.lock();
459
+ }
460
+ content.saveAll();
461
+ ui.refresh(contentName);
462
+ }
463
+
464
+ // findObject en lugar de loadAll cuando solo se busca uno
465
+ // INCORRECTO (carga TODOS los registros):
466
+ let coll = appData.getCollection("Productos");
467
+ coll.loadAll();
468
+ for (let i = 0; i < coll.getCount(); i++) {
469
+ if (coll.get(i).CODIGO == "PROD001") { break; }
470
+ }
471
+
472
+ // CORRECTO (busca directamente en BD):
473
+ let coll = appData.getCollection("Productos");
474
+ let producto = coll.findObject("CODIGO = 'PROD001'");
475
+ ```
476
+
477
+ ### 11.3 Evitar Bucles Costosos
478
+
479
+ ```javascript
480
+ // INCORRECTO: saveAll dentro de un bucle
481
+ for (let i = 0; i < items.length; i++) {
482
+ let obj = coll.createObject();
483
+ obj.MAP_NOMBRE = items[i].nombre;
484
+ coll.addItem(obj);
485
+ obj.save(); // Escribe en BD en cada iteracion
486
+ }
487
+
488
+ // CORRECTO: un solo saveAll al final
489
+ coll.unlock();
490
+ for (let i = 0; i < items.length; i++) {
491
+ let obj = coll.createObject();
492
+ obj.MAP_NOMBRE = items[i].nombre;
493
+ coll.addItem(obj);
494
+ }
495
+ coll.lock();
496
+ coll.saveAll();
497
+
498
+ // CORRECTO: para inserciones masivas, usar batch SQL
499
+ let sqlManager = new SqlManager();
500
+ try {
501
+ sqlManager.openDatabase({ databasePath: "gestion.db", useWal: true, useExistingConnection: true });
502
+ let sqls = [];
503
+ for (let i = 0; i < items.length; i++) {
504
+ sqls.push("INSERT INTO gen_tabla (NOMBRE) VALUES ('" +
505
+ items[i].nombre.replace(/'/g, "''") + "')");
506
+ }
507
+ sqlManager.doBatchParseSqls(sqls);
508
+ } finally {
509
+ sqlManager.close();
510
+ }
511
+ ```
512
+
513
+ ### 11.4 Uso Eficiente de Contents
514
+
515
+ ```javascript
516
+ // Filtrar para limitar la carga
517
+ let content = self.getContents("@Lineas");
518
+ content.setFilter("ACTIVO = 1");
519
+ content.unlock();
520
+ content.clear();
521
+ content.loadAll();
522
+ content.lock();
523
+
524
+ // Limitar registros para listas grandes
525
+ function cargarUltimosRegistros(nombreColl, limite) {
526
+ limite = limite || 50;
527
+ let coll = appData.getCollection(nombreColl);
528
+ coll.clear();
529
+ coll.loadAll();
530
+ coll.doSort("FECHA DESC");
531
+
532
+ let items = [];
533
+ let count = Math.min(coll.getCount(), limite);
534
+ for (let i = 0; i < count; i++) {
535
+ items.push(coll.get(i));
536
+ }
537
+ return items;
538
+ }
539
+ ```
540
+
541
+ ### 11.5 Promesas para Operaciones Asíncronas
542
+
543
+ El motor XOne tiene una implementación custom de `Promise` compatible con ES2024 (`.then`, `.catch`, `.finally`, `Promise.all/allSettled/race/any/withResolvers`). Soporta ramificación (varias `.then` sobre la misma promesa producen cadenas independientes).
544
+
545
+ ```javascript
546
+ function startUpdateGpsLoop() {
547
+ new Promise((resolve, reject) => {
548
+ let ventana = ui.getView(self);
549
+ while (!bBreakUpdateGpsLoop) {
550
+ if (!ventana) { reject(new Error("Sin ventana")); return; }
551
+ try {
552
+ actualizarGps();
553
+ } catch (error) {
554
+ reject(error);
555
+ return;
556
+ }
557
+ ventana.refreshValue("MAP_LONGITUD", "MAP_LATITUD");
558
+ }
559
+ resolve();
560
+ })
561
+ .then(() => ui.showToast("GPS loop terminado OK"))
562
+ .catch(err => ui.showToast("Error en GPS loop: " + err.message))
563
+ .finally(() => console.log("cleanup"));
564
+ }
565
+ ```
566
+
567
+ **Patrón con `Promise.all` (paralelo, fast-fail):**
568
+
569
+ ```javascript
570
+ Promise.all([cargarUsuario(), cargarConfig(), cargarPermisos()])
571
+ .then(([usuario, config, permisos]) => {
572
+ pintarPantalla(usuario, config, permisos);
573
+ })
574
+ .catch(err => ui.msgBox("Error cargando datos: " + err));
575
+ ```
576
+
577
+ **Patrón con `Promise.allSettled` (paralelo, espera todas):**
578
+
579
+ ```javascript
580
+ Promise.allSettled([p1, p2, p3]).then(results => {
581
+ results.forEach((r, i) => {
582
+ if (r.status === "fulfilled") console.log("OK p" + i + ":", r.value);
583
+ else console.log("FAIL p" + i + ":", r.reason);
584
+ });
585
+ });
586
+ ```
587
+
588
+ **Patrón con `Promise.withResolvers` (ES2024, evita la indirección del constructor):**
589
+
590
+ ```javascript
591
+ const { promise, resolve, reject } = Promise.withResolvers();
592
+ // resolve/reject pueden invocarse desde cualquier callback posterior
593
+ ui.startGps(coords => resolve(coords));
594
+ return promise;
595
+ ```