@tinya-code/gas-admin-ui 0.2.0-alpha.15 → 0.2.0-alpha.18

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 (368) hide show
  1. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
  2. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
  3. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
  4. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
  5. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
  6. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
  7. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
  8. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
  9. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +1027 -0
  10. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +1 -0
  11. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
  12. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
  13. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +107 -0
  14. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +1 -0
  15. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +101 -0
  16. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +1 -0
  17. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
  18. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
  19. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +88 -0
  20. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +1 -0
  21. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
  22. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
  23. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
  24. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
  25. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
  26. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
  27. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
  28. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
  29. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
  30. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
  31. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
  32. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
  33. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
  34. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
  35. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
  36. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
  37. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +122 -0
  38. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +1 -0
  39. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
  40. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
  41. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
  42. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
  43. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +45 -0
  44. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +1 -0
  45. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
  46. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
  47. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
  48. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
  49. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
  50. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
  51. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +408 -0
  52. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +1 -0
  53. package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs +4516 -0
  54. package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs.map +1 -0
  55. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
  56. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
  57. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
  58. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
  59. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
  60. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
  61. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
  62. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
  63. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
  64. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
  65. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
  66. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
  67. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
  68. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
  69. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
  70. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
  71. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
  72. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
  73. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +416 -0
  74. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +1 -0
  75. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +439 -0
  76. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +1 -0
  77. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
  78. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
  79. package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs +637 -0
  80. package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs.map +1 -0
  81. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
  82. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
  83. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +251 -0
  84. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +1 -0
  85. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
  86. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
  87. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
  88. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
  89. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
  90. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
  91. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
  92. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
  93. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
  94. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
  95. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
  96. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
  97. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
  98. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
  99. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
  100. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
  101. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +309 -0
  102. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +1 -0
  103. package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs +576 -0
  104. package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs.map +1 -0
  105. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +161 -0
  106. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +1 -0
  107. package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
  108. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +685 -0
  109. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +1 -0
  110. package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs +80 -0
  111. package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs.map +1 -0
  112. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +296 -0
  113. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +1 -0
  114. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
  115. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
  116. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
  117. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
  118. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
  119. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
  120. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
  121. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
  122. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
  123. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
  124. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
  125. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
  126. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
  127. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
  128. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
  129. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
  130. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
  131. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
  132. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +60 -0
  133. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +1 -0
  134. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
  135. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
  136. package/fesm2022/tinya-code-gas-admin-ui.mjs +4738 -0
  137. package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
  138. package/package.json +15 -3
  139. package/types/tinya-code-gas-admin-ui.d.ts +3634 -0
  140. package/ng-package.json +0 -7
  141. package/src/lib/actions/action-registry.spec.ts +0 -52
  142. package/src/lib/actions/action-registry.ts +0 -126
  143. package/src/lib/actions/action-service.spec.ts +0 -215
  144. package/src/lib/actions/action-service.ts +0 -87
  145. package/src/lib/actions/descriptors.spec.ts +0 -155
  146. package/src/lib/actions/descriptors.ts +0 -154
  147. package/src/lib/columns/column-cell.ts +0 -33
  148. package/src/lib/columns/column-defaults.spec.ts +0 -99
  149. package/src/lib/columns/column-defaults.ts +0 -84
  150. package/src/lib/columns/column-format.spec.ts +0 -274
  151. package/src/lib/columns/descriptors.ts +0 -113
  152. package/src/lib/columns/format-badge.ts +0 -51
  153. package/src/lib/columns/format-boolean.ts +0 -58
  154. package/src/lib/columns/format-currency.ts +0 -30
  155. package/src/lib/columns/format-date.ts +0 -35
  156. package/src/lib/columns/format-number.ts +0 -32
  157. package/src/lib/columns/format-text.ts +0 -23
  158. package/src/lib/columns/format-thumbnail.ts +0 -63
  159. package/src/lib/core/adapters/firebase-auth.adapter.ts +0 -6
  160. package/src/lib/core/guards/auth.guard.spec.ts +0 -88
  161. package/src/lib/core/guards/auth.guard.ts +0 -48
  162. package/src/lib/core/guards/unsaved-changes.guard.ts +0 -16
  163. package/src/lib/core/interceptors/api-error.interceptor.spec.ts +0 -182
  164. package/src/lib/core/interceptors/api-error.interceptor.ts +0 -87
  165. package/src/lib/core/models/api.model.ts +0 -104
  166. package/src/lib/core/models/schema-descriptions.ts +0 -118
  167. package/src/lib/core/models/schema.model.ts +0 -1097
  168. package/src/lib/core/registry/composition-validator.spec.ts +0 -358
  169. package/src/lib/core/registry/composition-validator.ts +0 -270
  170. package/src/lib/core/registry/field-validators.spec.ts +0 -199
  171. package/src/lib/core/registry/field-validators.ts +0 -440
  172. package/src/lib/core/registry/render-host.spec.ts +0 -208
  173. package/src/lib/core/registry/render-host.ts +0 -646
  174. package/src/lib/core/registry/render-registry.spec.ts +0 -96
  175. package/src/lib/core/registry/render-registry.ts +0 -312
  176. package/src/lib/core/registry/schema-catalog.ts +0 -156
  177. package/src/lib/core/services/api.service.spec.ts +0 -1142
  178. package/src/lib/core/services/api.service.ts +0 -371
  179. package/src/lib/core/services/auth.service.spec.ts +0 -653
  180. package/src/lib/core/services/auth.service.ts +0 -503
  181. package/src/lib/core/services/config.service.spec.ts +0 -121
  182. package/src/lib/core/services/config.service.ts +0 -119
  183. package/src/lib/core/services/data-source.service.spec.ts +0 -179
  184. package/src/lib/core/services/data-source.service.ts +0 -142
  185. package/src/lib/core/services/error-mapper.service.spec.ts +0 -50
  186. package/src/lib/core/services/error-mapper.service.ts +0 -49
  187. package/src/lib/core/services/notification.service.ts +0 -73
  188. package/src/lib/core/services/relation-drawer.service.ts +0 -41
  189. package/src/lib/core/services/theme.service.spec.ts +0 -106
  190. package/src/lib/core/services/theme.service.ts +0 -86
  191. package/src/lib/core/services/transport.spec.ts +0 -198
  192. package/src/lib/core/services/transport.ts +0 -164
  193. package/src/lib/core/services/upload.service.ts +0 -81
  194. package/src/lib/core/testing/categories.schema.ts +0 -125
  195. package/src/lib/core/testing/orders.schema.ts +0 -372
  196. package/src/lib/core/testing/products.schema.ts +0 -258
  197. package/src/lib/core/testing/site.schema.ts +0 -195
  198. package/src/lib/core/testing/test-catalog.ts +0 -21
  199. package/src/lib/core/tokens.ts +0 -112
  200. package/src/lib/core/utils/clipboard.spec.ts +0 -55
  201. package/src/lib/core/utils/clipboard.ts +0 -90
  202. package/src/lib/core/utils/context-resolver.spec.ts +0 -95
  203. package/src/lib/core/utils/context-resolver.ts +0 -101
  204. package/src/lib/core/utils/response-unwrapper.spec.ts +0 -70
  205. package/src/lib/core/utils/response-unwrapper.ts +0 -103
  206. package/src/lib/core/utils/source-options.spec.ts +0 -96
  207. package/src/lib/core/utils/source-options.ts +0 -54
  208. package/src/lib/core/utils/template-interpolator.spec.ts +0 -59
  209. package/src/lib/core/utils/template-interpolator.ts +0 -56
  210. package/src/lib/core/view-navigation/view-navigation.service.spec.ts +0 -207
  211. package/src/lib/core/view-navigation/view-navigation.service.ts +0 -132
  212. package/src/lib/fields/boolean/boolean.descriptor.ts +0 -11
  213. package/src/lib/fields/boolean/boolean.ts +0 -40
  214. package/src/lib/fields/color/color.descriptor.ts +0 -12
  215. package/src/lib/fields/color/color.ts +0 -34
  216. package/src/lib/fields/currency/currency.descriptor.ts +0 -12
  217. package/src/lib/fields/currency/currency.ts +0 -67
  218. package/src/lib/fields/date/date.descriptor.ts +0 -12
  219. package/src/lib/fields/date/date.ts +0 -36
  220. package/src/lib/fields/descriptors.ts +0 -122
  221. package/src/lib/fields/display/display-badge.ts +0 -65
  222. package/src/lib/fields/display/display-date.ts +0 -39
  223. package/src/lib/fields/display/display-defaults.ts +0 -84
  224. package/src/lib/fields/display/display-file.ts +0 -41
  225. package/src/lib/fields/display/display-image.ts +0 -54
  226. package/src/lib/fields/display/display-list.ts +0 -107
  227. package/src/lib/fields/display/display-number.ts +0 -41
  228. package/src/lib/fields/display/display-text.ts +0 -42
  229. package/src/lib/fields/display/display.spec.ts +0 -242
  230. package/src/lib/fields/dropdown/dropdown.descriptor.ts +0 -12
  231. package/src/lib/fields/dropdown/dropdown.ts +0 -45
  232. package/src/lib/fields/email/email.descriptor.ts +0 -12
  233. package/src/lib/fields/email/email.ts +0 -28
  234. package/src/lib/fields/field-aria.ts +0 -60
  235. package/src/lib/fields/field-host/field-host.spec.ts +0 -118
  236. package/src/lib/fields/field-host/field-host.ts +0 -47
  237. package/src/lib/fields/field-node.ts +0 -88
  238. package/src/lib/fields/file/file.descriptor.ts +0 -14
  239. package/src/lib/fields/file/file.spec.ts +0 -134
  240. package/src/lib/fields/file/file.ts +0 -281
  241. package/src/lib/fields/group/group.descriptor.ts +0 -21
  242. package/src/lib/fields/group/group.ts +0 -110
  243. package/src/lib/fields/image/image.descriptor.ts +0 -12
  244. package/src/lib/fields/image/image.spec.ts +0 -117
  245. package/src/lib/fields/image/image.ts +0 -281
  246. package/src/lib/fields/key-value/key-value.descriptor.ts +0 -11
  247. package/src/lib/fields/key-value/key-value.ts +0 -283
  248. package/src/lib/fields/list/list.descriptor.ts +0 -21
  249. package/src/lib/fields/list/list.ts +0 -376
  250. package/src/lib/fields/multiselect/multiselect.descriptor.ts +0 -11
  251. package/src/lib/fields/multiselect/multiselect.spec.ts +0 -142
  252. package/src/lib/fields/multiselect/multiselect.ts +0 -152
  253. package/src/lib/fields/number/number.descriptor.ts +0 -12
  254. package/src/lib/fields/number/number.ts +0 -40
  255. package/src/lib/fields/phone/phone.descriptor.ts +0 -12
  256. package/src/lib/fields/phone/phone.ts +0 -46
  257. package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +0 -12
  258. package/src/lib/fields/readonly-text/readonly-text.ts +0 -58
  259. package/src/lib/fields/relation/relation.descriptor.ts +0 -12
  260. package/src/lib/fields/relation/relation.spec.ts +0 -193
  261. package/src/lib/fields/relation/relation.ts +0 -224
  262. package/src/lib/fields/rich-select/rich-select.descriptor.ts +0 -12
  263. package/src/lib/fields/rich-select/rich-select.ts +0 -175
  264. package/src/lib/fields/select/radio-mode.ts +0 -25
  265. package/src/lib/fields/select/select.descriptor.ts +0 -12
  266. package/src/lib/fields/select/select.spec.ts +0 -162
  267. package/src/lib/fields/select/select.ts +0 -210
  268. package/src/lib/fields/slug/slug.descriptor.ts +0 -19
  269. package/src/lib/fields/slug/slug.ts +0 -67
  270. package/src/lib/fields/string-list/string-list.descriptor.ts +0 -11
  271. package/src/lib/fields/string-list/string-list.ts +0 -222
  272. package/src/lib/fields/text/text.descriptor.ts +0 -12
  273. package/src/lib/fields/text/text.ts +0 -35
  274. package/src/lib/fields/textarea/textarea.descriptor.ts +0 -12
  275. package/src/lib/fields/textarea/textarea.spec.ts +0 -117
  276. package/src/lib/fields/textarea/textarea.ts +0 -126
  277. package/src/lib/fields/time/time.descriptor.ts +0 -12
  278. package/src/lib/fields/time/time.ts +0 -28
  279. package/src/lib/fields/unknown-render/unknown-render.ts +0 -49
  280. package/src/lib/fields/url/url.descriptor.ts +0 -12
  281. package/src/lib/fields/url/url.ts +0 -53
  282. package/src/lib/gas-admin-ui.spec.ts +0 -21
  283. package/src/lib/gas-admin-ui.ts +0 -9
  284. package/src/lib/layouts/descriptors.ts +0 -9
  285. package/src/lib/pages/dashboard/dashboard.ts +0 -167
  286. package/src/lib/pages/detail-view/detail-view.spec.ts +0 -333
  287. package/src/lib/pages/detail-view/detail-view.ts +0 -570
  288. package/src/lib/pages/drawer-form/drawer-form.ts +0 -228
  289. package/src/lib/pages/form-view/form-model.ts +0 -88
  290. package/src/lib/pages/form-view/form-schema.ts +0 -255
  291. package/src/lib/pages/form-view/form-view.ts +0 -1622
  292. package/src/lib/pages/list-view/list-view.formats.spec.ts +0 -214
  293. package/src/lib/pages/list-view/list-view.ts +0 -882
  294. package/src/lib/pages/login/login.spec.ts +0 -109
  295. package/src/lib/pages/login/login.ts +0 -227
  296. package/src/lib/pages/resource-page/resource-page.ts +0 -234
  297. package/src/lib/shared/components/badge/badge.ts +0 -35
  298. package/src/lib/shared/components/button/button.ts +0 -65
  299. package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +0 -48
  300. package/src/lib/shared/components/copy-button/copy-button.ts +0 -56
  301. package/src/lib/shared/components/copy-share/copy-share.spec.ts +0 -122
  302. package/src/lib/shared/components/copy-share/copy-share.ts +0 -107
  303. package/src/lib/shared/components/drawer/drawer.spec.ts +0 -127
  304. package/src/lib/shared/components/drawer/drawer.ts +0 -172
  305. package/src/lib/shared/components/empty-state/empty-state.ts +0 -30
  306. package/src/lib/shared/components/modal/modal.spec.ts +0 -149
  307. package/src/lib/shared/components/modal/modal.ts +0 -145
  308. package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +0 -165
  309. package/src/lib/shared/components/post-create-modal/post-create-modal.ts +0 -159
  310. package/src/lib/shared/components/skeleton/skeleton.ts +0 -31
  311. package/src/lib/shared/components/toast/toast.ts +0 -58
  312. package/src/lib/shared/utils/col-span.ts +0 -25
  313. package/src/lib/shared/utils/condition-evaluator.ts +0 -53
  314. package/src/lib/shared/utils/depends-on.ts +0 -48
  315. package/src/lib/shared/utils/dialog-focus.ts +0 -28
  316. package/src/lib/shared/utils/field-path.ts +0 -49
  317. package/src/lib/shared/utils/gap-sorting.ts +0 -144
  318. package/src/lib/shared/utils/seed.spec.ts +0 -64
  319. package/src/lib/shared/utils/seed.ts +0 -149
  320. package/src/lib/shared/utils/slugify.ts +0 -13
  321. package/src/lib/shared/utils/validators.ts +0 -41
  322. package/src/lib/shell/layout/layout.ts +0 -63
  323. package/src/lib/shell/shell.routes.spec.ts +0 -59
  324. package/src/lib/shell/shell.routes.ts +0 -122
  325. package/src/lib/shell/sidebar/sidebar.ts +0 -139
  326. package/src/lib/shell/theme-toggle/theme-toggle.ts +0 -74
  327. package/src/lib/views/calendar-view/calendar-model.ts +0 -426
  328. package/src/lib/views/calendar-view/calendar-view.spec.ts +0 -557
  329. package/src/lib/views/calendar-view/calendar-view.ts +0 -568
  330. package/src/lib/views/composer/composer-host.spec.ts +0 -301
  331. package/src/lib/views/composer/composer-host.ts +0 -248
  332. package/src/lib/views/composer/composer-imports.spec.ts +0 -54
  333. package/src/lib/views/composer/view-context.spec.ts +0 -117
  334. package/src/lib/views/composer/view-context.ts +0 -109
  335. package/src/lib/views/composer/view-slot.spec.ts +0 -130
  336. package/src/lib/views/composer/view-slot.ts +0 -178
  337. package/src/lib/views/descriptors.ts +0 -46
  338. package/src/lib/views/kanban-view/kanban-model.ts +0 -173
  339. package/src/lib/views/kanban-view/kanban-view.spec.ts +0 -540
  340. package/src/lib/views/kanban-view/kanban-view.ts +0 -327
  341. package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +0 -17
  342. package/src/lib/widgets/bar-chart/bar-chart.spec.ts +0 -66
  343. package/src/lib/widgets/bar-chart/bar-chart.ts +0 -76
  344. package/src/lib/widgets/chart-line/chart-line.descriptor.ts +0 -17
  345. package/src/lib/widgets/chart-line/chart-line.spec.ts +0 -113
  346. package/src/lib/widgets/chart-line/chart-line.ts +0 -219
  347. package/src/lib/widgets/descriptors.ts +0 -29
  348. package/src/lib/widgets/metric-card/metric-card.descriptor.ts +0 -41
  349. package/src/lib/widgets/metric-card/metric-card.spec.ts +0 -52
  350. package/src/lib/widgets/metric-card/metric-card.ts +0 -48
  351. package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +0 -13
  352. package/src/lib/widgets/quick-actions/quick-actions.spec.ts +0 -61
  353. package/src/lib/widgets/quick-actions/quick-actions.ts +0 -100
  354. package/src/lib/widgets/record-list/record-list.descriptor.ts +0 -16
  355. package/src/lib/widgets/record-list/record-list.spec.ts +0 -79
  356. package/src/lib/widgets/record-list/record-list.ts +0 -115
  357. package/src/lib/widgets/series.spec.ts +0 -42
  358. package/src/lib/widgets/series.ts +0 -56
  359. package/src/lib/widgets/status-progress/status-progress.descriptor.ts +0 -19
  360. package/src/lib/widgets/status-progress/status-progress.spec.ts +0 -63
  361. package/src/lib/widgets/status-progress/status-progress.ts +0 -104
  362. package/src/lib/widgets/widget-host/widget-host.spec.ts +0 -179
  363. package/src/lib/widgets/widget-host/widget-host.ts +0 -99
  364. package/src/public-api.ts +0 -104
  365. package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
  366. package/tsconfig.lib.json +0 -13
  367. package/tsconfig.lib.prod.json +0 -11
  368. package/tsconfig.spec.json +0 -10
@@ -0,0 +1,3634 @@
1
+ import * as i0 from "@angular/core";
2
+ import { EnvironmentProviders, InjectionToken, Provider, Signal, TemplateRef, Type } from "@angular/core";
3
+ import { CanActivateFn, Route, Router, Routes } from "@angular/router";
4
+ import { Observable } from "rxjs";
5
+ import { HttpClient, HttpEvent, HttpHeaders, HttpInterceptorFn, HttpParams } from "@angular/common/http";
6
+ import { CdkDragDrop } from "@angular/cdk/drag-drop";
7
+ import { LucideIcon } from "@lucide/angular";
8
+ import { FieldTree, PathKind, SchemaFn, SchemaPath, SchemaPathRules } from "@angular/forms/signals";
9
+ /**
10
+ * Modelos del schema de recursos — única fuente de verdad de la UI.
11
+ * Especificación: doc/base.md §3–§7 y §9.
12
+ *
13
+ * Contrato de imágenes (decisión api.md §1): `image_url` en todo el JSON.
14
+ */
15
+ /** Anchos de columna dentro de una fila del layout (base.md §4: 1–12). */
16
+ export type GridWidth = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
17
+ /** Operadores de `visibleWhen` / `readonlyWhen` (base.md §4). */
18
+ export type ConditionOperator = 'eq' | 'ne' | 'empty' | 'contains';
19
+ /**
20
+ * Condición simple sobre otro campo del mismo nivel o del registro raíz.
21
+ * Ej.: `{ field: 'availability', operator: 'eq', value: 'PreOrder' }`.
22
+ */
23
+ export interface FieldCondition {
24
+ field: string;
25
+ operator: ConditionOperator;
26
+ /** Valor de comparación; no aplica para `empty`. */
27
+ value?: string | number | boolean;
28
+ }
29
+ /**
30
+ * Reglas de validación comunes (base.md §4).
31
+ * Nota: algunas se solapan con opciones propias del tipo (§5); el motor de
32
+ * validación (Fase 10) los fusiona y `validators` tiene prioridad.
33
+ */
34
+ export interface FieldValidators {
35
+ minLength?: number;
36
+ maxLength?: number;
37
+ pattern?: string;
38
+ min?: number;
39
+ max?: number;
40
+ minWords?: number;
41
+ maxWords?: number;
42
+ /** El valor debe ser único en el recurso (validación asíncrona). */
43
+ unique?: boolean;
44
+ }
45
+ /** Propiedades comunes a todo campo (base.md §4). */
46
+ export interface FieldBase<T> {
47
+ /** Nombre de la propiedad en el JSON que envía/recibe el back. */
48
+ key: string;
49
+ /** Rótulo visible para el usuario en el formulario y la ficha. */
50
+ label: string;
51
+ /** Texto corto de ayuda que se muestra debajo del campo. */
52
+ help?: string;
53
+ /** Texto sugerente que se muestra dentro del input cuando está vacío. */
54
+ placeholder?: string;
55
+ /** Si es obligatorio. El back también lo valida (R7.4). */
56
+ required?: boolean;
57
+ /** Valor por defecto si el registro no trae este campo (ej. `default: 'none'` en un `select`/`dropdown`). */
58
+ default?: T;
59
+ /** Si el campo es solo lectura (no editable en el formulario). */
60
+ readonly?: boolean;
61
+ /** Bloquea el campo solo en creación, solo en edición o siempre. */
62
+ readonlyOn?: 'create' | 'update' | 'always';
63
+ /** Condición bajo la cual el campo es solo lectura (expresión sobre otros campos). */
64
+ readonlyWhen?: FieldCondition;
65
+ /** Condición bajo la cual el campo se muestra u oculta. */
66
+ visibleWhen?: FieldCondition;
67
+ /**
68
+ * Ciclo de vida de la VISIBILIDAD: oculta el campo en creación, en edición
69
+ * o siempre. Símétrico a `readonlyOn`, pero NO lo mismo que `visibleWhen`,
70
+ * que expresa una condición sobre el valor de otro campo.
71
+ *
72
+ * Vale para los campos que LLENA EL BACK (`ref`, `pin`): en alta no existen
73
+ * aún, así que mostrarlos vacíos y solo-lectura con un `help` que dice "se
74
+ * genera solo al guardar" es ruido ocupando la sección. `hidden()` de Signal
75
+ * Forms además los deja FUERA de la validación y del payload
76
+ * (field-host:311 no los dibuja, form-model:52 los salta).
77
+ */
78
+ hiddenOn?: 'create' | 'update' | 'always';
79
+ /**
80
+ * Selección dependiente (guía §1: país → ciudad). Clave del campo padre
81
+ * del MISMO nivel: mientras el padre esté vacío este campo queda
82
+ * `disabled`, y si el padre cambia el hijo se reinicia a su valor por
83
+ * defecto (plan 6.6).
84
+ */
85
+ dependsOn?: string;
86
+ /** Reglas de validación (mínimo, máximo, patrón, unicidad, etc.). */
87
+ validators?: FieldValidators;
88
+ /** Ancho en la grilla del formulario (1–12), igual que un widget del dashboard. */
89
+ width?: GridWidth;
90
+ /** Id de la sección/pestaña del layout a la que pertenece (base.md §4). */
91
+ section?: string;
92
+ /**
93
+ * R7.5: acceso por campo (B8 · R7.4b) — sólo UX; el back vuelve a
94
+ * verificar en cada petición.
95
+ */
96
+ access?: {
97
+ /** Código de nivel de acceso requerido para leer el campo. */
98
+ read?: string;
99
+ /** Código de nivel de acceso requerido para escribir el campo. */
100
+ write?: string;
101
+ };
102
+ /**
103
+ * Descripción corta de para qué sirve este campo. Se muestra como tooltip
104
+ * al pasar el cursor sobre el rótulo en el formulario y la ficha.
105
+ */
106
+ description?: string;
107
+ /** URL de documentación externa para este campo (se abre al hacer clic en el tooltip). */
108
+ docLink?: string;
109
+ /** Si el valor se puede copiar al portapapeles con un botón junto al campo en la vista de detalle. */
110
+ copyable?: boolean;
111
+ }
112
+ export interface SelectOption {
113
+ value: string;
114
+ label: string;
115
+ disabled?: boolean;
116
+ }
117
+ export interface TextField extends FieldBase<string> {
118
+ type: 'text';
119
+ maxLength?: number;
120
+ pattern?: string;
121
+ }
122
+ export interface ColorField extends FieldBase<string> {
123
+ type: 'color';
124
+ }
125
+ export interface TextareaField extends FieldBase<string> {
126
+ type: 'textarea';
127
+ rows?: number;
128
+ minWords?: number;
129
+ maxWords?: number;
130
+ /**
131
+ * Marcadores insertables como chips bajo el área (plantillas de mensaje).
132
+ * Cada chip escribe `{key}` en la posición del cursor, no al final: el
133
+ * operador escribe, apreta, sigue escribiendo. El motor back no se entera —
134
+ * recibe el string final y lo interpola (`interpolateTemplate`).
135
+ *
136
+ * `label` es el rótulo humano del botón ("PIN"); `key` es la clave que se
137
+ * inserta y que el back resuelve contra `config + registro`.
138
+ */
139
+ tokens?: readonly TemplateToken[];
140
+ }
141
+ /** Chip insertable en un `textarea` de plantilla. */
142
+ export interface TemplateToken {
143
+ /** Clave insertada como `{key}` y resuelta por el motor. */
144
+ key: string;
145
+ /** Rótulo visible en el chip (el token va como tooltip). */
146
+ label: string;
147
+ }
148
+ export interface SlugField extends FieldBase<string> {
149
+ type: 'slug';
150
+ /** Campo del que se autogenera el slug (ej. `name`). */
151
+ from: string;
152
+ /** Bloquea el campo una vez creado el registro (por defecto: sí). */
153
+ lockAfterCreate?: boolean;
154
+ }
155
+ export interface NumberField extends FieldBase<number> {
156
+ type: 'number';
157
+ min?: number;
158
+ max?: number;
159
+ step?: number;
160
+ decimals?: number;
161
+ }
162
+ export interface CurrencyField extends FieldBase<number> {
163
+ type: 'currency';
164
+ /** Campo del registro del que se toma la moneda (ej. `currency`). */
165
+ currencyFrom?: string;
166
+ }
167
+ export interface BooleanField extends FieldBase<boolean> {
168
+ type: 'boolean';
169
+ trueLabel?: string;
170
+ falseLabel?: string;
171
+ }
172
+ /**
173
+ * De dónde salen las opciones de un desplegable: **exactamente una** de las
174
+ * dos formas (XOR en el tipo, D62).
175
+ *
176
+ * - `options` — estáticas en el schema (el caso de las enums: `status`).
177
+ * - `source` — pedidas al fetcher declarado (F8 · R6.5) para listados que
178
+ * viven en otro recurso.
179
+ *
180
+ * Ninguna ⇒ un desplegable mudo; las dos ⇒ dos verdades para el mismo campo.
181
+ * Ambas reglas viven en este tipo, no en un chequeo en runtime.
182
+ */
183
+ type SelectData = {
184
+ options: SelectOption[];
185
+ source?: never;
186
+ } | {
187
+ options?: never;
188
+ source: DataSource;
189
+ };
190
+ export type SelectField = FieldBase<string> & {
191
+ type: 'select';
192
+ } & SelectData;
193
+ export type MultiselectField = FieldBase<string[]> & {
194
+ type: 'multiselect';
195
+ } & SelectData;
196
+ /**
197
+ * Desplegable nativo (`<select>`) que SIEMPRE se renderiza como lista
198
+ * desplegable, independientemente de cuántas opciones tenga.
199
+ *
200
+ * A diferencia de `select` (que usa radio buttons para 2–4 opciones
201
+ * estáticas por la guía de control §1), este tipo nunca muta a radios.
202
+ * Úsalo cuando necesites un dropdown verdadero aunque tenga pocas opciones.
203
+ */
204
+ export interface DropdownField extends FieldBase<string> {
205
+ type: 'dropdown';
206
+ options: SelectOption[];
207
+ }
208
+ /**
209
+ * Opción de un selector visual enriquecido (`rich-select`).
210
+ *
211
+ * Además de `value`/`label`, cada opción puede declarar una `preview` con
212
+ * HTML seguro que el motor renderiza como tarjeta visual (badge, chip con
213
+ * icono, color swatch, etc.). El HTML viaja en el schema y el motor lo
214
+ * pinta tal cual tras pasar por el sanitizador de Angular.
215
+ */
216
+ export interface RichSelectOption {
217
+ value: string;
218
+ label: string;
219
+ disabled?: boolean;
220
+ /** HTML seguro para pintar como preview de la opción (ej. `<span class="chip">…</span>`). */
221
+ preview?: string;
222
+ }
223
+ /**
224
+ * Selector visual enriquecido: cada opción se renderiza como tarjeta con su
225
+ * `preview` HTML en vez de un `<select>` nativo o radio buttons planos.
226
+ * Sólo opciones estáticas (sin `source`).
227
+ */
228
+ export interface RichSelectField extends FieldBase<string> {
229
+ type: 'rich-select';
230
+ options: RichSelectOption[];
231
+ /**
232
+ * Ancho de cada tarjeta de opción en la grilla (1–12), igual que `width`
233
+ * de un campo. Por defecto `4` (3 tarjetas por fila en escritorio).
234
+ * Úsalo para opciones con preview larga (menos tarjetas por fila) o
235
+ * preview corta (más tarjetas).
236
+ */
237
+ cardWidth?: GridWidth;
238
+ /**
239
+ * Cuántas tarjetas mostrar por fila en el punto de ruptura `lg`.
240
+ * Sobrescribe el cálculo automático basado en `cardWidth`.
241
+ * Ejemplo: `2` obliga a 2 columnas grandes sin importar el ancho.
242
+ */
243
+ cardCols?: 1 | 2 | 3 | 4 | 5 | 6;
244
+ }
245
+ export interface UrlField extends FieldBase<string> {
246
+ type: 'url';
247
+ }
248
+ export interface EmailField extends FieldBase<string> {
249
+ type: 'email';
250
+ }
251
+ export interface PhoneField extends FieldBase<string> {
252
+ type: 'phone';
253
+ /** Normalización del valor guardado. */
254
+ format?: 'e164' | 'digits';
255
+ }
256
+ export interface DateField extends FieldBase<string> {
257
+ type: 'date';
258
+ /** Fecha ISO `YYYY-MM-DD` permitida. */
259
+ min?: string;
260
+ max?: string;
261
+ }
262
+ export interface TimeField extends FieldBase<string> {
263
+ type: 'time';
264
+ }
265
+ export interface ReadonlyTextField extends FieldBase<string> {
266
+ type: 'readonly-text';
267
+ /** Formato de presentación (texto, fecha, moneda…). */
268
+ format?: string;
269
+ }
270
+ /**
271
+ * Fuente de datos declarada (F8 · R6.5): UN vocabulario para relations,
272
+ * selects y widgets en lugar de tres. El fetcher es único
273
+ * (`DataSourceService`) y la clave de su caché es `resource+view+params`.
274
+ *
275
+ * `params` son valores interpolados `{campo}` que el front resuelve contra su
276
+ * contexto (el registro del formulario o la fila del widget) y manda como
277
+ * `payload.params`; el back los inyecta en el ctx de las vistas, donde una
278
+ * condición los lee con el operando `param: '<nombre>'`. Una vista que no
279
+ * referencia ese parámetro no se ve afectada por él — sólo cambia la clave
280
+ * de caché.
281
+ *
282
+ * Los parámetros llegan del navegador, así que son ENTRADA: sólo filtran
283
+ * dentro del recurso que la ruta ya autoriza a leer (nunca ensanchan el
284
+ * acceso) y una vista que pide un parámetro ausente queda fail-closed.
285
+ */
286
+ export interface DataSource {
287
+ /** Recurso declarado (`resource.id`): su listado es el de las opciones. */
288
+ resource: string;
289
+ /** Vista declarada en `resource.views` (opcional): filtra en el back. */
290
+ view?: string;
291
+ /** Valores interpolados para la vista (`{campo}` ← contexto). */
292
+ params?: Record<string, string>;
293
+ /** Etiqueta por defecto de las filas (si no, `titleField` del recurso). */
294
+ labelField?: string;
295
+ /** Valor por defecto de las filas (si no, `keyField` del recurso). */
296
+ valueField?: string;
297
+ }
298
+ export interface RelationField extends FieldBase<string> {
299
+ type: 'relation';
300
+ /**
301
+ * De dónde salen las opciones (F8 · R6.5): `{ resource, view?, params?, … }`.
302
+ * Antes eran tres claves planas (`resource`/`labelField`/`valueField`);
303
+ * D62 las unifica con el resto de consumidores.
304
+ */
305
+ source: DataSource;
306
+ /**
307
+ * Segundo dato a mostrar junto a la etiqueta (guía §1: «Juan Pérez ·
308
+ * DNI 123»). Se concatena con « · » y forma parte de la opción, así que
309
+ * también se busca sobre él.
310
+ */
311
+ secondaryField?: string;
312
+ /** Cargar solo registros activos (lo exige además la FK en el back). */
313
+ onlyActive?: boolean;
314
+ }
315
+ export interface ImageField extends FieldBase<string> {
316
+ type: 'image';
317
+ /** Tipos aceptados (ej. `image/*`, `image/png,image/jpeg`). */
318
+ accept?: string;
319
+ maxSizeMB?: number;
320
+ /** Relación de aspecto de la vista previa (ej. `16/9`). */
321
+ aspectRatio?: string;
322
+ previewSize?: string;
323
+ }
324
+ export interface FileField extends FieldBase<string> {
325
+ type: 'file';
326
+ /** Formatos aceptados (ej. `image/*`, `pdf,docx`). Si falta, usa los del backend. */
327
+ accept?: string;
328
+ maxSizeMB?: number;
329
+ }
330
+ export interface GroupField extends FieldBase<Record<string, unknown>> {
331
+ type: 'group';
332
+ fields: FieldSchema[];
333
+ /** Presentación del grupo (base.md §5.4). */
334
+ display?: 'card' | 'collapsible' | 'inline';
335
+ }
336
+ /** Propiedades comunes a las listas (base.md §6). */
337
+ export interface ListOptions {
338
+ min?: number;
339
+ max?: number;
340
+ /** Si se puede reordenar (por defecto: sí). */
341
+ sortable?: boolean;
342
+ /** Campo donde se guarda el orden dentro de cada item (`position`). */
343
+ positionField?: string;
344
+ addLabel?: string;
345
+ emptyText?: string;
346
+ itemDisplay?: 'card' | 'row' | 'accordion';
347
+ }
348
+ export interface ListField extends FieldBase<Record<string, unknown>[]>, ListOptions {
349
+ type: 'list';
350
+ /** Campos de cada item (cualquier tipo, incluso `group` o `list`). */
351
+ itemFields: FieldSchema[];
352
+ /** Campo del item usado como título cuando está colapsado. */
353
+ itemTitle?: string;
354
+ /** El primer item tiene significado especial (ej. imagen principal). */
355
+ primaryFirst?: boolean;
356
+ }
357
+ export interface StringListField extends FieldBase<string[]>, ListOptions {
358
+ type: 'string-list';
359
+ /** Tipo permitido de cada item (base.md §9: solo text, url o email). */
360
+ itemType?: 'text' | 'url' | 'email';
361
+ }
362
+ export interface KeyValueItem {
363
+ key: string;
364
+ value: string;
365
+ }
366
+ export interface KeyValueField extends FieldBase<KeyValueItem[]>, ListOptions {
367
+ type: 'key-value';
368
+ }
369
+ /**
370
+ * Base de un widget de panel. Se declara en `ResourceSchema.widgets` — no
371
+ * en `fields`, porque un KPI no es un campo: no se valida, no se envía y
372
+ * `field-host` no lo renderiza. Sólo un recurso `kind: 'dashboard'` los
373
+ * dibuja, y agregar un widget es tocar el schema, no el template.
374
+ *
375
+ * Su `key` es además la clave del dato en la respuesta del endpoint del
376
+ * dashboard (`{ key: 'pendientes' }` ↔ `{ pendientes: 3 }`), salvo que el
377
+ * widget declare otra fuente (F8 · R6.5).
378
+ */
379
+ /**
380
+ * Fuente de datos de un widget: el mismo vocabulario declarado de F8 que usan
381
+ * relations y selects (D62), más el caso «payload del panel».
382
+ *
383
+ * - `DataSource` (`{ resource, view?, params? }`): el widget pide sus filas o
384
+ * su agregado al recurso declarado vía `DataSourceService`; deja de depender
385
+ * de que un handler haya armado `data[widget.key]`.
386
+ * - `{ kind: 'payload' }`: lee la respuesta COMPLETA del endpoint del
387
+ * dashboard (varias claves a la vez).
388
+ */
389
+ export type WidgetSource = DataSource | {
390
+ kind: 'payload';
391
+ };
392
+ export interface WidgetBase {
393
+ key: string;
394
+ label: string;
395
+ help?: string;
396
+ /** Ancho en la grilla del panel (1–12), igual que un campo. */
397
+ width?: GridWidth;
398
+ /**
399
+ * Ancho en la grilla en mobile (1–12). Por defecto `12` (ancho completo,
400
+ * apilado). Declararlo permite controlar el layout responsive en pantallas
401
+ * chicas sin tocar el ancho de escritorio (`width`).
402
+ */
403
+ mobileWidth?: GridWidth;
404
+ /**
405
+ * De dónde saca sus datos (F8 · R6.5).
406
+ *
407
+ * - **ausente** (por defecto): `data[widget.key]`. Es el contrato normal y
408
+ * el que usan los KPI (`{ key: 'pendientes' }` ↔ `{ pendientes: 3 }`).
409
+ * - **`{ kind: 'payload' }`**: la respuesta COMPLETA, para widgets que se
410
+ * componen de varias claves (`status-progress` leyendo `pendientes` +
411
+ * `entregados` + `cancelados`) sin que el orquestador tenga que saber
412
+ * qué tipo de widget es.
413
+ * - **`DataSource`**: las filas salen del recurso declarado (vista
414
+ * incluida), pedidas al fetcher común.
415
+ */
416
+ source?: WidgetSource;
417
+ }
418
+ /** KPI de una cifra suelta («3 pendientes»). */
419
+ export interface MetricCardWidget extends WidgetBase {
420
+ type: 'metric-card';
421
+ /** Texto secundario bajo la cifra (estático, va en el schema). */
422
+ hint?: string;
423
+ }
424
+ /** Barras por categoría con CSS/SVG — sin librería. */
425
+ export interface BarChartWidget extends WidgetBase {
426
+ type: 'bar-chart';
427
+ /** Sufijo de las cifras del eje («unidades», «S/»). */
428
+ unit?: string;
429
+ }
430
+ /**
431
+ * Serie temporal con Chart.js.
432
+ *
433
+ * La librería (61,4 K gz medidos) vive en el chunk que abre el descriptor
434
+ * (`import('./chart-line')`), así que **no entra en el bundle inicial**; el
435
+ * `@defer (on viewport)` que el propio componente dibuja tarda además la
436
+ * creación del lienzo hasta que el gráfico se ve. Ver
437
+ * `widgets/chart-line/chart-line.ts`.
438
+ */
439
+ export interface ChartLineWidget extends WidgetBase {
440
+ type: 'chart-line';
441
+ /** Sufijo de las cifras del eje («unidades», «S/»). */
442
+ unit?: string;
443
+ }
444
+ /** Tabla embebida de registros operativos (ej. pedidos que requieren atención). */
445
+ export interface RecordListWidget extends WidgetBase {
446
+ type: 'record-list';
447
+ /**
448
+ * Claves de columnas a mostrar en la tabla compacta.
449
+ *
450
+ * No lleva `resource`: si las filas vienen declaradas, ese recurso ya está
451
+ * en `source.resource` (D63 retira la clave duplicada — estaba declarada y
452
+ * nadie la leía).
453
+ */
454
+ columns: string[];
455
+ /** Acción de navegación por fila. */
456
+ action?: {
457
+ label: string;
458
+ routeTemplate: string;
459
+ };
460
+ }
461
+ /** Grid de botones de accesos directos operativos. */
462
+ export interface QuickActionsWidget extends WidgetBase {
463
+ type: 'quick-actions';
464
+ actions: {
465
+ label: string;
466
+ icon?: string;
467
+ navigateTo: string;
468
+ variant?: 'primary' | 'secondary' | 'outline';
469
+ }[];
470
+ }
471
+ /** Barra de distribución porcentual de estados o categorías. */
472
+ export interface StatusProgressWidget extends WidgetBase {
473
+ type: 'status-progress';
474
+ segments: {
475
+ key: string;
476
+ label: string;
477
+ color: 'success' | 'warning' | 'danger' | 'neutral';
478
+ }[];
479
+ }
480
+ export type WidgetSchema = MetricCardWidget | BarChartWidget | ChartLineWidget | RecordListWidget | QuickActionsWidget | StatusProgressWidget;
481
+ export type WidgetType = WidgetSchema['type'];
482
+ export type FieldSchema = TextField | TextareaField | SlugField | NumberField | CurrencyField | BooleanField | SelectField | MultiselectField | RichSelectField | DropdownField | UrlField | EmailField | PhoneField | DateField | TimeField | ReadonlyTextField | RelationField | ImageField | FileField | GroupField | ListField | StringListField | KeyValueField | ColorField;
483
+ export type FieldType = FieldSchema['type'];
484
+ export type ListColumnFormat = 'text' | 'badge' | 'thumbnail' | 'boolean' | 'number' | 'currency' | 'date';
485
+ export interface ListColumn {
486
+ key: string;
487
+ label: string;
488
+ /**
489
+ * Sólo para sobreescribir: sin él manda la tabla de
490
+ * `columns/column-defaults` (`FORMATO_POR_DEFECTO`) según el `type` del
491
+ * campo con esta `key` (R4.4).
492
+ */
493
+ format?: ListColumnFormat;
494
+ }
495
+ export interface ListFilter {
496
+ key: string;
497
+ label: string;
498
+ type?: 'text' | 'select' | 'boolean';
499
+ options?: SelectOption[];
500
+ }
501
+ export interface LayoutSection {
502
+ /** Coincide con `field.section`. */
503
+ id: string;
504
+ label: string;
505
+ }
506
+ export type LayoutMode = 'grid' | 'tabs' | 'sections' | 'steps' | 'columns';
507
+ export interface ResourceLayout {
508
+ /** Presentación del formulario. */
509
+ mode?: LayoutMode;
510
+ sections?: LayoutSection[];
511
+ /** Número de columnas para `mode: 'columns'` (por defecto 2). */
512
+ columns?: 2 | 3;
513
+ }
514
+ export interface ResourceActionBase {
515
+ id: string;
516
+ label: string;
517
+ icon?: string;
518
+ variant?: 'primary' | 'secondary' | 'outline' | 'danger';
519
+ visibleWhen?: {
520
+ field: string;
521
+ op: 'eq' | 'neq' | 'in';
522
+ value: unknown;
523
+ }[];
524
+ }
525
+ export interface CopyShareAction extends ResourceActionBase {
526
+ type: 'copy-share';
527
+ urlTemplate: string;
528
+ shareTextTemplate?: string;
529
+ }
530
+ export interface NavigateAction extends ResourceActionBase {
531
+ type?: 'navigate';
532
+ /** Plantilla con marcadores, ej. `/products/{slug}` o `/admin/orders?id={ref}`. */
533
+ urlTemplate: string;
534
+ }
535
+ export interface TriggerAction extends ResourceActionBase {
536
+ /**
537
+ * Acción de dominio **declarada en el back** (B3 `actions.<name>`,
538
+ * publicada como `POST /admin/<recurso>/:id/actions/<name>` — R5.4).
539
+ * El destino no se declara acá: la ruta lógica se deriva en R3.12.
540
+ */
541
+ type: 'trigger';
542
+ /** Nombre de la acción en el back si difiere del `id` de esta declaración. */
543
+ name?: string;
544
+ confirmMessage?: string;
545
+ }
546
+ /** Acciones extra de registro o listado (duplicar, compartir, navegar…). */
547
+ export type ResourceAction = CopyShareAction | NavigateAction | TriggerAction;
548
+ /** Configuración de diálogo/modal o acción tras crear un registro con éxito. */
549
+ export interface PostCreateSchema {
550
+ mode: 'modal' | 'toast' | 'redirect_detail' | 'stay';
551
+ title?: string;
552
+ description?: string;
553
+ summaryFields?: string[];
554
+ actions?: ResourceAction[];
555
+ }
556
+ /** Configuración de Ficha de Detalle (modo lectura estructurado). */
557
+ export interface DetailViewSchema {
558
+ enabled: boolean;
559
+ headerFields?: string[];
560
+ sections?: {
561
+ id: string;
562
+ label: string;
563
+ fields: string[];
564
+ }[];
565
+ actions?: ResourceAction[];
566
+ allowEdit?: boolean;
567
+ allowDelete?: boolean;
568
+ }
569
+ /** Operaciones permitidas (base.md §3). `undefined` = permitido. */
570
+ export interface ResourcePermissions {
571
+ create?: boolean;
572
+ update?: boolean;
573
+ remove?: boolean;
574
+ reorder?: boolean;
575
+ }
576
+ /**
577
+ * Rutas de la API relativas a `API_URL` (base.md §3, api.md §6 Opción A).
578
+ *
579
+ * Marcadores que un path acepta — se resuelven ANTES de despachar la petición
580
+ * (y para ambos drivers):
581
+ *
582
+ * - `{key}` — clave del registro actual; sólo aplica a las operaciones con
583
+ * clave (`get`, `update`, `remove`). Si llega sin resolver, es error.
584
+ * - `{<fuente>}` / `{<fuente>.<ruta.anidada>}` — valor de una fuente de
585
+ * contexto: las construidas `auth` (sesión) y `config` (config global), o
586
+ * cualquier fuente registrada en `provideGasAdmin({ valueSources })`.
587
+ *
588
+ * El valor se codifica como segmento de URL. Un marcador sin valor, o con una
589
+ * fuente no registrada, lanza un error que nombra el path y el marcador: nunca
590
+ * se envía un segmento vacío. Los singleton solo usan `get` / `update`.
591
+ */
592
+ export interface ResourceEndpoints {
593
+ list?: string;
594
+ get?: string;
595
+ create?: string;
596
+ update?: string;
597
+ remove?: string;
598
+ /**
599
+ * Declaración opcional de cómo unwrappear la respuesta de esta operación.
600
+ * Cuando se declara, el motor extrae el contenido de `dataKey` antes de
601
+ * devolverlo al consumidor (ej.: `{ data: [...] }` → `[...]`).
602
+ * Si no se declara, el motor mantiene el comportamiento actual:
603
+ * respuesta pasada tal cual (array plano, `{ items }`, o objeto crudo).
604
+ * Esta declaración es por operación; si se desea un valor global, usarlo
605
+ * en cada operación o considerar un default a nivel de config en futuro.
606
+ */
607
+ responseUnwrap?: ResponseUnwrap;
608
+ }
609
+ /**
610
+ * Cómo el motor debe desempaquetar la respuesta HTTP antes de entregarla.
611
+ * Opcional y opt-in: schemas sin este campo siguen el contrato actual.
612
+ *
613
+ * - `dataKey`: clave en el body que contiene el payload real (ej. `data`,
614
+ * `items`, `result`). Si el body no tiene esta clave, se devuelve `undefined`
615
+ * para lista / objeto vacío según corresponda, y se lanza error en modo
616
+ * estricto si se configura.
617
+ * - `metaKey`: clave con metadatos (paginación, total, etc.). Se ignora en
618
+ * el valor devuelto al consumidor pero puede leerse si el motor la expone
619
+ * en futuro.
620
+ * - `apply`: `'list' | 'get' | 'create' | 'update' | 'remove' | 'all'`.
621
+ * Qué operaciones aplican el unwrap. Por defecto `'all'` cuando se declara.
622
+ */
623
+ export interface ResponseUnwrap {
624
+ dataKey: string;
625
+ metaKey?: string;
626
+ apply?: 'list' | 'get' | 'create' | 'update' | 'remove' | 'all';
627
+ /**
628
+ * Claves de metadata de paginación dentro del sobre. Usadas por
629
+ * `ApiService.listPage()` para armar `PageResult<T>`. Opcional: sin
630
+ * declaración, el motor intenta auto-detectar claves comunes
631
+ * (`nextCursor`, `total`, `hasMore`) y, si no hay, `hasMore = false`.
632
+ */
633
+ pagination?: {
634
+ /** Clave del cursor siguiente dentro de `meta` (o del body si no hay `metaKey`). */
635
+ cursorKey?: string;
636
+ /** Clave del total de registros. */
637
+ totalKey?: string;
638
+ /** Clave del flag booleano "hay más páginas". */
639
+ hasMoreKey?: string;
640
+ };
641
+ }
642
+ /**
643
+ * Método HTTP soportado por endpoints personalizados.
644
+ * Declarado aquí (y no en api.service.ts) para evitar ciclos de importación
645
+ * entre modelos y servicios.
646
+ */
647
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
648
+ /**
649
+ * Definición de un endpoint personalizado más allá de los cinco estándar
650
+ * (list/get/create/update/remove). Permite al consumidor declarar rutas con
651
+ * verbos arbitrarios, segmentos extra, payloads JSON o multipart, y unwrap de
652
+ * respuesta propio.
653
+ *
654
+ * Ejemplo:
655
+ * name: 'recalculate-ranks'
656
+ * method: 'POST'
657
+ * path: '/admin/businesses/{businessId}/ranks/recalculate'
658
+ * payloadMode: 'json'
659
+ */
660
+ export interface CustomEndpointDefinition {
661
+ /** Nombre único del endpoint dentro del recurso. Se usa para invocarlo. */
662
+ name: string;
663
+ /** Método HTTP de la petición. */
664
+ method: HttpMethod;
665
+ /** Ruta relativa a `API_URL`. Puede incluir marcadores {key}, {auth.user.id}, etc. */
666
+ path: string;
667
+ /** Modo de payload: 'json' (default) o 'multipart' para FormData. */
668
+ payloadMode?: 'json' | 'multipart';
669
+ /** Unwrap de respuesta override para este endpoint. Si no se declara, se usa el del recurso. */
670
+ responseUnwrap?: ResponseUnwrap;
671
+ }
672
+ export interface ResourceSchema {
673
+ /** Identificador interno (`categories`). */
674
+ id: string;
675
+ label: string;
676
+ labelPlural: string;
677
+ /**
678
+ * `collection` → listado, `singleton` → formulario directo,
679
+ * `dashboard` → panel de sólo lectura (refactormotor.md F4; sus datos llegan
680
+ * un endpoint declarativo, no de CRUD).
681
+ */
682
+ kind: 'collection' | 'singleton' | 'dashboard';
683
+ endpoint: ResourceEndpoints;
684
+ /** Campo que identifica el registro (`slug`). */
685
+ keyField: string;
686
+ /** Campo que se muestra como nombre del registro. */
687
+ titleField: string;
688
+ /** Endpoints personalizados más allá de list/get/create/update/remove. */
689
+ customEndpoints?: Record<string, CustomEndpointDefinition>;
690
+ /** Si el listado se reordena (requiere `positionField`). */
691
+ sortable?: boolean;
692
+ positionField?: string;
693
+ listColumns: ListColumn[];
694
+ filters?: ListFilter[];
695
+ /** Campos buscables en el listado. */
696
+ search?: string[];
697
+ /** Árbol de campos del formulario (base.md §4–§6). */
698
+ fields: FieldSchema[];
699
+ /**
700
+ * Widgets del panel — sólo `kind: 'dashboard'` (refactormotor.md F4).
701
+ *
702
+ * Van aparte de `fields`: un KPI no es un campo y no comparte `FieldBase`
703
+ * (`required`/`visibleWhen`/`validators`…). Decisión tomada tras medir el
704
+ * ripple de unirlos a `FieldSchema`: 18+ errores de tipos en 4 ficheros.
705
+ * `contract-check` sólo recorre `fields`, así que esta propiedad no se
706
+ * compara back↔front — el widget es config de UI, no dato almacenado.
707
+ */
708
+ widgets?: WidgetSchema[];
709
+ layout?: ResourceLayout;
710
+ actions?: ResourceAction[];
711
+ /** Configuración de Ficha de Detalle estructurada para el recurso. */
712
+ detail?: DetailViewSchema;
713
+ /** Configuración de diálogo/modal post-creación. */
714
+ postCreate?: PostCreateSchema;
715
+ permissions?: ResourcePermissions;
716
+ /**
717
+ * Acceso declarado por recurso (auth.md §5.3) — **sólo UX**: oculta
718
+ * ruta, entrada de menú y botones. Aunque el front se manipule, el back
719
+ * vuelve a verificar en cada petición: esto nunca es una barrera.
720
+ *
721
+ * No lo declara ningún schema mientras sólo exista el rol `admin`
722
+ * (§9.4): sin declaración el efecto es «sesión», que es lo correcto hoy.
723
+ * Aparece el día que haya un segundo rol.
724
+ */
725
+ access?: ResourceAccess;
726
+ /**
727
+ * Claves de `fields` que el listado edita en un drawer sin navegar
728
+ * (plan 6.5, guía §5: «edición rápida de pocos campos, panel lateral en
729
+ * lugar de pantalla nueva»). 1–3 campos; si no se declara, no hay atajo.
730
+ */
731
+ quickEdit?: string[];
732
+ /** Rutas efectivas por operación, con la forma pública si la declara. */
733
+ operations?: ResourceOperations;
734
+ /** Vistas declaradas como metadatos (sin operandos internos). */
735
+ views?: Record<string, ViewDescriptor>;
736
+ /** Páginas y rutas del frontend; separado de `views` del backend. */
737
+ uiViews?: Record<string, UiViewDescriptor>;
738
+ /** Lo mínimo del backend para menú/acciones: `access` normalizado. */
739
+ policies?: ResourcePolicies;
740
+ }
741
+ /** Forma pública de la respuesta de una operación (declaración `shape`). */
742
+ export interface OperationShape {
743
+ pick?: 'full' | 'list' | string[];
744
+ include?: string[];
745
+ rename?: Record<string, string>;
746
+ nest?: Record<string, string[]>;
747
+ envelope?: 'plain' | 'list';
748
+ }
749
+ /** Ruta efectiva de una operación: método + path (con `{key}` si aplica). */
750
+ export interface OperationDescriptor {
751
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
752
+ path: string;
753
+ shape?: OperationShape;
754
+ }
755
+ /** Operaciones CRUD efectivas (derivadas de la misma ruta que el router). */
756
+ export interface ResourceOperations {
757
+ list?: OperationDescriptor;
758
+ get?: OperationDescriptor;
759
+ create?: OperationDescriptor;
760
+ update?: OperationDescriptor;
761
+ remove?: OperationDescriptor;
762
+ }
763
+ /**
764
+ * Condición de vista como metadato público: el literal `value` y el `ref`
765
+ * (metadato de relación) viajan; el operando interno `from: '$item.x'` no.
766
+ */
767
+ export interface ViewCondition {
768
+ field: string;
769
+ op: string;
770
+ not?: boolean;
771
+ value?: unknown;
772
+ ref?: string;
773
+ }
774
+ /** Vista declarada publicada como metadato (F7-1). */
775
+ export interface ViewDescriptor {
776
+ where?: ViewCondition[];
777
+ sort?: {
778
+ field: string;
779
+ dir?: 'asc' | 'desc';
780
+ }[];
781
+ limit?: number;
782
+ extends?: string;
783
+ /** Vista con handler propio (escape hatch): su existencia, nada más. */
784
+ custom?: boolean;
785
+ }
786
+ /** Config para una `uiView` de tipo `calendar`. */
787
+ export interface CalendarViewConfig {
788
+ /** Campo `date` (o `datetime` futuro): inicio del evento. Obligatorio. */
789
+ startField: string;
790
+ /** Campo `date`/`time`: fin. Ausente ⇒ evento de 1 día sobre `startField`. */
791
+ endField?: string;
792
+ /** Campo `text`/`relation`: rótulo del evento. Obligatorio. */
793
+ titleField: string;
794
+ /** Campo `select`/`text`: color/estado (token de paleta o valor crudo). */
795
+ colorField?: string;
796
+ /** Vista por defecto al abrir. Por defecto `month`. */
797
+ defaultView?: 'month' | 'week' | 'day' | 'agenda';
798
+ /** Origen de filas; ausente ⇒ la propia instancia del recurso (su listado). */
799
+ source?: DataSource;
800
+ }
801
+ /** Config para una `uiView` de tipo `kanban`. */
802
+ export interface KanbanViewConfig {
803
+ /** Campo `select`/`relation`: columna por valor. Obligatorio. */
804
+ groupField: string;
805
+ /** Orden estable de columnas. Valores no declarados ⇒ columna extra al final. */
806
+ columns: KanbanColumn[];
807
+ /** Rótulo de la tarjeta; ausente ⇒ `keyField` del recurso. */
808
+ titleField?: string;
809
+ /** Segundo renglón de la tarjeta (opcional). */
810
+ subtitleField?: string;
811
+ /** Origen de filas; ausente ⇒ la propia instancia del recurso. */
812
+ source?: DataSource;
813
+ }
814
+ /** Una columna del tablero Kanban. */
815
+ export interface KanbanColumn {
816
+ /** Valor del `groupField` que agrupa esta columna. */
817
+ value: string;
818
+ /** Etiqueta visible de la columna. */
819
+ label: string;
820
+ }
821
+ /**
822
+ * Evento que dispara un `select` declarado (R9.9.4): `open`, `select` o una
823
+ * acción con nombre (`open-detail`). El string libre cubre las acciones con
824
+ * nombre sin perder autocompletado para los dos literales fijos.
825
+ */
826
+ export type SelectionTrigger = 'open' | 'select' | (string & {});
827
+ /**
828
+ * Destino de navegación declarado (R9.9.4 · spec view-navigation): recurso +
829
+ * `uiView` declarada + params de ruta permitidos. Las URLs crudas o
830
+ * externas NO son destinos válidos — el validador las rechaza pre-render.
831
+ */
832
+ export interface NavigationTarget {
833
+ /** Recurso declarado (`schema.id` del front). */
834
+ resource: string;
835
+ /** Clave de `uiViews` en ese recurso. */
836
+ view: string;
837
+ /** Nombres de params de la ruta del destino (`:key` ⇒ `['key']`). */
838
+ params?: string[];
839
+ }
840
+ /**
841
+ * Vinculación selección ⇒ transición declarada en una `uiView` o en un hijo
842
+ * de composición (R9.9.4). `route` exige un `NavigationTarget` declarado;
843
+ * `inline` selecciona en local y `none` es un no-op explícito.
844
+ */
845
+ export type SelectionBinding = {
846
+ on: SelectionTrigger;
847
+ mode: 'none' | 'inline';
848
+ } | {
849
+ on: SelectionTrigger;
850
+ mode: 'route';
851
+ target: NavigationTarget;
852
+ };
853
+ /** Base común de todo hijo de una composición embebida (R9.9.2). */
854
+ export interface ViewChildBase {
855
+ /** Identificador estable y único entre hermanos del mismo contenedor. */
856
+ id: string;
857
+ /** Etiqueta visible (rótulo de la pestaña). */
858
+ label: string;
859
+ /** Conserva el estado al cambiar de pestaña; ausente ⇒ destruir (R9.9.5). */
860
+ retain?: boolean;
861
+ /** Visibilidad declarativa (condiciones sobre campos del formulario). */
862
+ visibleWhen?: FieldCondition[];
863
+ }
864
+ /**
865
+ * Hijo declarado inline (`type` + `config`) o por referencia a una `uiView`
866
+ * del mismo schema (`ref`). Los `ref` hacen posible que un contenedor
867
+ * contenga —directa o transitivamente— a sí mismo: por eso el
868
+ * `composition-validator` es obligatorio antes del render (D2).
869
+ */
870
+ export type ViewChildNode = (ViewChildBase & UiViewDescriptor) | (ViewChildBase & {
871
+ ref: string;
872
+ });
873
+ /** Config de una `uiView` contenedora `tabs` (R9.9.2). */
874
+ export interface TabsConfig {
875
+ /** Lista ordenada de hijos; el orden declarado es el orden de render. */
876
+ children: ViewChildNode[];
877
+ /** `id` del hijo seleccionado al montar; ausente ⇒ el primero. */
878
+ initial?: string;
879
+ }
880
+ /** Vista `tabs` declarada en el schema local del frontend. */
881
+ export interface UiViewTabs extends UiViewBase {
882
+ type: 'tabs';
883
+ config: TabsConfig;
884
+ }
885
+ /** Base común de toda `uiView`: tipo + ruta relativa al recurso. */
886
+ export interface UiViewBase {
887
+ /** Tipo resuelto por `kind: 'view'` en el registro de render. */
888
+ type: string;
889
+ /** Ruta relativa al recurso; ausente o vacía significa la ruta raíz. */
890
+ path?: string;
891
+ /**
892
+ * Binding de selección declarado (R9.9.4). Ausente ⇒ la hoja conserva su
893
+ * comportamiento local y NO navega; una URL cruda nunca es un destino
894
+ * válido (el `composition-validator` la rechaza antes del render, D2).
895
+ */
896
+ select?: SelectionBinding;
897
+ }
898
+ /** Vista `calendar` declarada en el schema local del frontend. */
899
+ export interface UiViewCalendar extends UiViewBase {
900
+ type: 'calendar';
901
+ config: CalendarViewConfig;
902
+ }
903
+ /** Vista `kanban` declarada en el schema local del frontend. */
904
+ export interface UiViewKanban extends UiViewBase {
905
+ type: 'kanban';
906
+ config: KanbanViewConfig;
907
+ }
908
+ /**
909
+ * Una página/ruta declarada en el schema local del frontend, discriminada
910
+ * por `type`. Los tipos base (`list`/`detail`/`form`/`dashboard`) conservan
911
+ * la forma plana sin `config`; `calendar` y `kanban` llevan su config tipada.
912
+ */
913
+ export type UiViewDescriptor = UiViewBase | UiViewCalendar | UiViewKanban | UiViewTabs;
914
+ /** Políticas públicas: sólo `access`, lo mínimo para menú/acciones. */
915
+ export interface ResourcePolicies {
916
+ access: {
917
+ read: string;
918
+ write: string;
919
+ /** R7.5: override por operación (B8 · R7.4a) */
920
+ operations?: Record<string, string>;
921
+ };
922
+ }
923
+ /** §5.3 — `access` de un recurso. `requires` usa el vocabulario de §4.4. */
924
+ export interface ResourceAccess {
925
+ requires?: string;
926
+ roles?: string[];
927
+ }
928
+ /** Un campo de la pantalla de login (auth.md §5.1). */
929
+ export interface AuthFieldDeclaration {
930
+ key: string;
931
+ /** `email` | `password` — tipos primitivos; el registro de render los
932
+ * absorberá en Fase 3 (R3.3), cuando exista el descriptor `password`. */
933
+ type: string;
934
+ required?: boolean;
935
+ }
936
+ /** Supported auth providers. Extensible without breaking existing schemas. */
937
+ export type AuthProvider = 'password' | 'google' | (string & {});
938
+ /** Firebase config block — present only when at least one Firebase provider is listed. */
939
+ export interface FirebaseAuthConfig {
940
+ /**
941
+ * The backend endpoint that exchanges a Firebase ID token for the app token.
942
+ * Defaults to '/auth/firebase' when omitted.
943
+ */
944
+ exchangeEndpoint?: string;
945
+ }
946
+ /**
947
+ * Pantalla de login declarada (auth.md §5.1): **vive sólo en el schema del
948
+ * front** — el back no conoce estas etiquetas ni este layout, sólo declara
949
+ * el `input` de `/auth/login`. El `contract-check` (R2.11) verifica que las
950
+ * claves de `loginFields` existan en ese `input`.
951
+ */
952
+ export interface AuthSchema {
953
+ /** Proveedores disponibles: 'password' | 'google' u otros federados. */
954
+ providers: AuthProvider[];
955
+ loginFields: AuthFieldDeclaration[];
956
+ redirectAfterLogin: string;
957
+ /**
958
+ * D10-3 → **`false` siempre.** La opción «recordarme» con `localStorage`
959
+ * quedó denegada (§9.3): el token vive en `sessionStorage` y muere con la
960
+ * pestaña. Declarar `true` prometería un storage que nadie usa.
961
+ */
962
+ rememberMe: boolean;
963
+ /** Configuración de Firebase cuando se usan proveedores federados o Firebase Auth. */
964
+ firebase?: FirebaseAuthConfig;
965
+ }
966
+ /**
967
+ * URL base de la API (baseapi §2.3): es la URL directa del Web App de Apps
968
+ * Script; ahí se hace el `POST` del envelope `{ token, method, path, payload }`.
969
+ * OBLIGATORIO proporcionarlo en `app.config.ts`:
970
+ * `{ provide: API_URL, useValue: 'https://…' }` (Fase 3). Vacío = mismo origen.
971
+ */
972
+ export declare const API_URL: InjectionToken<string>;
973
+ /**
974
+ * Superficie mínima que `gas-admin-ui` usa de Firebase Auth.
975
+ * Implementada por la aplicación consumidora; inyectada vía `FIREBASE_AUTH_ADAPTER`.
976
+ * Mantener la interfaz fina evita acoplar la librería a una versión específica de Firebase.
977
+ */
978
+ interface FirebaseAuthAdapter {
979
+ /**
980
+ * Abre un popup de Google sign-in y resuelve al ID token de Firebase.
981
+ * Rechaza si el usuario cancela o hay error.
982
+ */
983
+ signInWithGoogle(): Promise<string>;
984
+ /**
985
+ * Inicia sesión con email y contraseña vía Firebase y resuelve al ID token.
986
+ * Se usa cuando provider === 'password' Y firebase está configurado (Firebase email auth).
987
+ */
988
+ signInWithEmailAndPassword(email: string, password: string): Promise<string>;
989
+ /**
990
+ * Cierra sesión en Firebase (llamado junto al clear de la sesión de la app).
991
+ */
992
+ signOut(): Promise<void>;
993
+ /**
994
+ * Obtiene un ID token de Firebase FRESCO para usarlo en la cabecera
995
+ * `Authorization: Bearer <token>` de las peticiones REST.
996
+ *
997
+ * OPCIONAL: si el adaptador no lo implementa, `gas-admin-ui` envía el token
998
+ * de sesión almacenado tal cual (comportamiento actual). Implementarlo
999
+ * permite refrescar el ID token antes de cada petición REST, evitando
1000
+ * errores "ID token inválido" tras la expiración de Firebase (1 hora).
1001
+ *
1002
+ * El adaptador decide cuándo refrescar (p. ej. `getIdToken(true)` de Firebase
1003
+ * SDK fuerza la renovación; sin argumento devuelve el cacheado si aún es válido).
1004
+ */
1005
+ getIdToken?(): Promise<string>;
1006
+ }
1007
+ /**
1008
+ * Token DI opcional para el adaptador de Firebase Auth.
1009
+ * Proporcionado por la aplicación consumidora cuando usa proveedores federados o Firebase Auth.
1010
+ */
1011
+ export declare const FIREBASE_AUTH_ADAPTER: InjectionToken<FirebaseAuthAdapter>;
1012
+ /**
1013
+ * Driver de transporte HTTP del motor.
1014
+ *
1015
+ * Vive acá (capa de infraestructura) y NO en `ResourceSchema` (capa de dominio)
1016
+ * porque es una decisión de red, no de modelo de datos. Cambiar el driver
1017
+ * no debería requerir tocar ningún schema de recursos.
1018
+ *
1019
+ * - `'gas'` (default): tunnel `POST text/plain` con envelope
1020
+ * `{ token, method, path, payload }` — contrato de Google Apps Script.
1021
+ * - `'rest'`: verbos HTTP reales (`GET`, `POST`, `PUT`, `DELETE`) con
1022
+ * `Authorization: Bearer <token>` en cabeceras y params en la URL (GET/DELETE)
1023
+ * o en el body JSON (POST/PUT/PATCH).
1024
+ *
1025
+ * Se fija en `provideGasAdmin({ driver })`. Sin declaración explícita el
1026
+ * motor usa `'gas'` — cero impacto para consumidores existentes.
1027
+ */
1028
+ export declare const API_DRIVER: InjectionToken<"gas" | "rest">;
1029
+ /**
1030
+ * Registro de FUENTES DE CONTEXTO para marcadores en paths de endpoints.
1031
+ *
1032
+ * Cada clave es el nombre de una fuente — `{ <fuente> }` o
1033
+ * `{ <fuente>.<ruta.anidada> }` dentro de un path declarado en
1034
+ * `ResourceEndpoints` — y cada valor es un getter sin argumentos que devuelve
1035
+ * el dato ACTUAL en el momento de la petición (no al registrar).
1036
+ *
1037
+ * El motor ya aporta dos fuentes construidas y éstas NO se declaran acá:
1038
+ *
1039
+ * - `auth` → la sesión que dueña `AuthService` (usuario y token).
1040
+ * - `config` → el mapa global que puebla `ConfigService` desde los singletons.
1041
+ *
1042
+ * Este registro es sólo para fuentes que aporta el consumidor, para que el
1043
+ * motor no conozca nombres de dominio: qué campo se lee lo decide el schema,
1044
+ * no el engine.
1045
+ *
1046
+ * Un marcador que apunte a una fuente no registrada, o a un valor ausente,
1047
+ * revienta con un error que nombra el path y el marcador — jamás se manda un
1048
+ * segmento vacío al backend.
1049
+ */
1050
+ type ValueSources = Readonly<Record<string, () => unknown>>;
1051
+ export declare const VALUE_SOURCES: InjectionToken<Readonly<Record<string, () => unknown>>>;
1052
+ /**
1053
+ * Unwrap de respuesta global por defecto (`GasAdminConfig.defaultResponseUnwrap`).
1054
+ *
1055
+ * Se fusiona con la declaración `endpoint.responseUnwrap` de cada recurso:
1056
+ * la del recurso tiene prioridad en las claves que declara. Sin token, sólo
1057
+ * aplica lo declarado por recurso — cero impacto en schemas existentes.
1058
+ */
1059
+ export declare const DEFAULT_RESPONSE_UNWRAP: InjectionToken<ResponseUnwrap>;
1060
+ /** Fija el catálogo (uso interno de `provideGasAdmin`; testable directo). */
1061
+ export declare function setSchemaCatalog(schemas: readonly ResourceSchema[], auth?: AuthSchema): void;
1062
+ /** Busca un schema por `id`; `undefined` si no existe. */
1063
+ export declare function getSchema(id: string): ResourceSchema | undefined;
1064
+ /** Catálogo completo en orden (menú lateral). */
1065
+ export declare function allSchemas(): readonly ResourceSchema[];
1066
+ /** Declaración de la pantalla de login; `undefined` sin `auth` en config. */
1067
+ export declare function getAuth(): AuthSchema | undefined;
1068
+ /** Token DI del catálogo (para inyectarlo donde un `inject()` es posible). */
1069
+ export declare const GAS_SCHEMAS: InjectionToken<readonly ResourceSchema[]>;
1070
+ /** Configuración del motor. */
1071
+ interface GasAdminConfig {
1072
+ /** Catálogo ordenado de recursos (menú + rutas + resolución `getSchema`). */
1073
+ schemas: readonly ResourceSchema[];
1074
+ /** Declaración de la pantalla de login (opcional: sin ella, sin `/login`). */
1075
+ auth?: AuthSchema;
1076
+ /**
1077
+ * Driver de transporte HTTP.
1078
+ *
1079
+ * - `'gas'` (default): tunnel `POST text/plain` con envelope Apps Script.
1080
+ * - `'rest'`: verbos HTTP reales con `Authorization: Bearer` en cabeceras.
1081
+ *
1082
+ * Sin declaración el motor usa `'gas'` — cero impacto en consumidores existentes.
1083
+ */
1084
+ driver?: 'gas' | 'rest';
1085
+ /**
1086
+ * Fuentes de contexto que el consumidor aporta para marcadores en paths de
1087
+ * endpoints: `{ <fuente> }` / `{ <fuente>.<ruta> }` se resuelven en el
1088
+ * momento de la petición contra este registro más las fuentes construidas
1089
+ * `auth` (sesión) y `config` (config global).
1090
+ *
1091
+ * Opcional: sin él, sólo existen `auth` y `config`. Declararlo no cambia
1092
+ * ningún path que no lleve marcadores — cero impacto en schemas existentes.
1093
+ */
1094
+ valueSources?: ValueSources;
1095
+ /**
1096
+ * Unwrap de respuesta global por defecto. Se fusiona con la declaración
1097
+ * `endpoint.responseUnwrap` de cada recurso (la del recurso tiene prioridad
1098
+ * en claves declaradas). Opcional: sin él, sólo aplica lo declarado por
1099
+ * recurso — cero impacto en schemas existentes (R2).
1100
+ */
1101
+ defaultResponseUnwrap?: ResponseUnwrap;
1102
+ }
1103
+ /**
1104
+ * Wiring completo del motor: catálogo + HTTP con el interceptor de
1105
+ * errores del envelope + loaders de config y sesión en el bootstrap
1106
+ * (antes en el `app.config` del consumidor).
1107
+ *
1108
+ * El consumidor conserva `provideRouter` y `{ provide: API_URL, ... }`
1109
+ * (su environment decide la URL del Web App).
1110
+ */
1111
+ export declare function provideGasAdmin(config: GasAdminConfig): (Provider | EnvironmentProviders)[];
1112
+ /**
1113
+ * Registra una implementación concreta de FirebaseAuthAdapter en el árbol de DI.
1114
+ * Usar junto con provideGasAdmin() en app.config.ts al configurar proveedores federados
1115
+ * (como 'google') o autenticación por Firebase.
1116
+ *
1117
+ * @example
1118
+ * // app.config.ts
1119
+ * provideGasAdmin({ schemas, auth }),
1120
+ * provideFirebaseAdapter(new MyFirebaseAuthAdapter(getAuth(firebaseApp))),
1121
+ */
1122
+ export declare function provideFirebaseAdapter(adapter: FirebaseAuthAdapter): Provider;
1123
+ /**
1124
+ * Contrato HTTP con el backend (doc/baseapi.md §8 «Errores»).
1125
+ *
1126
+ * El backend responde SIEMPRE HTTP 200; los errores viajan en el body
1127
+ * con `{ error: { status, message, errors?: [{ path, message }] } }`.
1128
+ * El interceptor (Fase 3) los convierte en `ApiError`.
1129
+ */
1130
+ /**
1131
+ * Un error con ruta del422 (auth.md §6): `path` para pintarlo en el campo y
1132
+ * `code` MAYUS_SNAKE (`VALIDATION_MIN_LENGTH`) para mapearlo sin leer texto.
1133
+ */
1134
+ export interface ApiIssue {
1135
+ /** Ruta del campo afectado (`faq[2].answer`, `sku`). */
1136
+ path: string;
1137
+ /** Mensaje en español, listo para pintar. */
1138
+ message: string;
1139
+ /** Código estable del validador (B11); el mensaje puede cambiar, el código no. */
1140
+ code?: string;
1141
+ }
1142
+ /** Shape del body de error (doc/baseapi.md §8 «Errores»). */
1143
+ export interface ApiErrorBody {
1144
+ error: {
1145
+ status: number;
1146
+ message: string;
1147
+ /** Ruta del campo afectado cuando el backend la envía (base.md §10). */
1148
+ path?: string;
1149
+ /**
1150
+ * Código de sesión de §4.5 (R2.9): `AUTH_REQUIRED` · `AUTH_INVALID` ·
1151
+ * `AUTH_EXPIRED` · `FORBIDDEN` · `RATE_LIMITED`. Aparece SÓLO en esas
1152
+ * cinco respuestas — el sobre de error es aditivo (O7 / D10-5).
1153
+ */
1154
+ code?: string;
1155
+ /**
1156
+ * Errores de validación con ruta (base.md §5, guía §10): el 422 los manda
1157
+ * ACÁ (`error.errors[]`), no en `path`. Sin modelarlos el form veía
1158
+ * «Errores de validación» genérico y ningún campo se pintaba (D42).
1159
+ */
1160
+ errors?: ApiIssue[];
1161
+ };
1162
+ }
1163
+ /** Error de API estandarizado que lanza el interceptor de errores. */
1164
+ export declare class ApiError extends Error {
1165
+ readonly status: number;
1166
+ /** Ruta del campo que provocó el error (ej. `faq[2].answer`), si existe. */
1167
+ readonly fieldPath?: string;
1168
+ /** Código de sesión de §4.5 si el backend lo envió; `undefined` en el resto. */
1169
+ readonly code?: string;
1170
+ /** Issues con ruta (422); vacío cuando el error no trae `errors[]`. */
1171
+ readonly issues: readonly ApiIssue[];
1172
+ constructor(status: number, message: string, fieldPath?: string, code?: string, issues?: readonly ApiIssue[]);
1173
+ }
1174
+ /**
1175
+ * `meta.renewedToken` (auth.md §3.2/§3.4): el back lo adjunta a la respuesta
1176
+ * cuando la vida del token bajó del umbral de renovación. Es el ÚNICO modo
1177
+ * de que el front se entere — el token nuevo no viaja en cabeceras ni en la
1178
+ * URL. Devuelve `null` cuando no hay renovación (que es lo normal).
1179
+ *
1180
+ * Vive acá y no en el `ApiService` porque lo consumen DOS servicios
1181
+ * (`ApiService` y `AuthService`) y éste modelo no importa a ninguno de los
1182
+ * dos: sin este archivo no hay ciclo de imports.
1183
+ */
1184
+ export declare function renewedTokenOf(body: unknown): string | null;
1185
+ /**
1186
+ * Resultado de una página para `ApiService.listPage()`.
1187
+ *
1188
+ * El backend envía la metadata de paginación dentro del sobre (típicamente
1189
+ * en `meta`); el motor la extrae según la declaración `responseUnwrap.pagination`
1190
+ * y la entrega en este shape. `list()` NO usa este tipo: sigue devolviendo `T[]`.
1191
+ */
1192
+ export interface PageResult<T> {
1193
+ /** Registros de la página actual. */
1194
+ items: T[];
1195
+ /** Cursor para pedir la página siguiente; `null` cuando no hay más. */
1196
+ nextCursor: string | null;
1197
+ /** Total de registros del conjunto, si el backend lo informa; `0` si no. */
1198
+ total: number;
1199
+ /** `true` si hay al menos una página más. `false` sin cursor informativo. */
1200
+ hasMore: boolean;
1201
+ }
1202
+ /**
1203
+ * Guards de sesión (auth.md §5.3).
1204
+ *
1205
+ * Dos puertas y ninguna es una barrera de seguridad: el back re-verifica
1206
+ * `accessLevel` en **cada** petición desde R2.6. Éstas sólo evitan pintar
1207
+ * una pantalla que no le toca al usuario — enseñarle un formulario que va
1208
+ * a recibir 403 es mala UX, no un agujero.
1209
+ *
1210
+ * `authGuard` va en la ruta madre (todo el shell); `roleGuard` en cada
1211
+ * `:id`, que es donde aparece el recurso que puede tener `access`.
1212
+ */
1213
+ /** Sin sesión → login. Corre ANTES que cualquier ruta del shell. */
1214
+ export declare const authGuard: CanActivateFn;
1215
+ /**
1216
+ * El recurso declara `access` y esta sesión no lo cumple → se cancela la
1217
+ * navegación. No hay pantalla de 403 (tampoco la pide §5.3) y el menú ya
1218
+ * oculta la entrada, así que queda sólo el caso de alguien tecleando la URL.
1219
+ */
1220
+ declare const roleGuard: CanActivateFn;
1221
+ /**
1222
+ * Rutas de los schemas recibidos (por defecto, la registry completa).
1223
+ * Cada schema se valida con `validateComposition` ANTES de generar rutas
1224
+ * (D3): una composición inválida revienta al arrancar, no en el render.
1225
+ */
1226
+ export declare function resourceRoutes(catalog?: readonly ResourceSchema[]): Route[];
1227
+ /**
1228
+ * Las rutas del shell salen de `uiViews`, no de `ResourceSchema.views` (que
1229
+ * conserva los metadatos de consultas del backend). Añadir o cambiar una
1230
+ * página requiere una declaración y un descriptor `kind: 'view'`.
1231
+ *
1232
+ * Los hijos se construyen DENTRO del `loadChildren` (en la primera
1233
+ * navegación), nunca en la evaluación del módulo: el consumidor importa
1234
+ * este barrel desde `app.routes.ts` ANTES de que `provideGasAdmin` siembre
1235
+ * el catálogo (corre en el cuerpo de `app.config`), y el paquete compilado
1236
+ * es eager — construir en el import congelaría `children: []` para siempre
1237
+ * y toda URL protegida renderizaría vacío (regresión: shell.routes.spec en
1238
+ * orden de producción).
1239
+ */
1240
+ export declare const shellRoutes: Routes;
1241
+ /**
1242
+ * Login (auth.md §5.1).
1243
+ *
1244
+ * **Pintada a partir de `authSchema.loginFields`**: si mañana cambian los
1245
+ * campos, éste componente no se toca — sólo se mueven claves y etiquetas.
1246
+ * No usa `FieldHost` porque el tipo `password` todavía no existe en
1247
+ * `FieldSchema` (es pieza nueva de Fase 3, R3.3); el `@switch` de abajo es
1248
+ * exactamente el reemplazo que R3.x hará por el registro de render.
1249
+ *
1250
+ * **No hay casilla «recordarme»**: D10-3 negó `localStorage` (§9.3) y el
1251
+ * token muere con la pestaña. Poner un checkbox sería prometer que la
1252
+ * sesión sobrevive a cerrar el navegador.
1253
+ */
1254
+ export declare class Login {
1255
+ private readonly auth;
1256
+ private readonly router;
1257
+ readonly schema: import("@tinya-code/gas-admin-ui").AuthSchema;
1258
+ readonly providers: import("@tinya-code/gas-admin-ui").AuthProvider[];
1259
+ readonly error: import("@angular/core").WritableSignal<string | null>;
1260
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
1261
+ readonly providerLoading: import("@angular/core").WritableSignal<string | null>;
1262
+ /** Valores por clave: el set lo pinta el `@for` del schema. */
1263
+ private readonly values;
1264
+ label(key: string): string;
1265
+ value(key: string): string;
1266
+ setValue(key: string, next: string): void;
1267
+ loginWithGoogle(): void;
1268
+ submit(event: Event): void;
1269
+ private goAfterLogin;
1270
+ static ɵfac: i0.ɵɵFactoryDeclaration<Login, never>;
1271
+ static ɵcmp: i0.ɵɵComponentDeclaration<Login, "app-login", never, {}, {}, never, never, true, never>;
1272
+ }
1273
+ type ToastKind$1 = 'success' | 'error' | 'info';
1274
+ interface Toast$1 {
1275
+ id: number;
1276
+ kind: ToastKind$1;
1277
+ message: string;
1278
+ }
1279
+ interface ConfirmOptions {
1280
+ title: string;
1281
+ message: string;
1282
+ confirmLabel?: string;
1283
+ cancelLabel?: string;
1284
+ }
1285
+ /**
1286
+ * Notificaciones de la app: toasts (señales, se renderizan en `app-toast`,
1287
+ * Fase 5) y un diálogo de confirmación con respuesta como `Promise`
1288
+ * (base.md §10: borrado con confirmación, aviso de cambios sin guardar).
1289
+ */
1290
+ export declare class NotificationService {
1291
+ readonly toasts: import("@angular/core").WritableSignal<Toast$1[]>;
1292
+ /** Confirmación en curso; `null` = sin diálogo. */
1293
+ readonly confirmation: import("@angular/core").WritableSignal<ConfirmOptions | null>;
1294
+ private nextId;
1295
+ private pendingResolve;
1296
+ success(message: string): void;
1297
+ error(message: string): void;
1298
+ info(message: string): void;
1299
+ dismiss(id: number): void;
1300
+ /** Abre el diálogo de confirmación y espera la respuesta del usuario. */
1301
+ confirm(options: ConfirmOptions): Promise<boolean>;
1302
+ /** Resuelve la confirmación pendiente (lo llama el componente de diálogo, Fase 5). */
1303
+ resolveConfirmation(accepted: boolean): void;
1304
+ private push;
1305
+ static ɵfac: i0.ɵɵFactoryDeclaration<NotificationService, never>;
1306
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1307
+ }
1308
+ /** Métodos que entiende el envelope del backend (baseapi §2.3). */
1309
+ type EnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
1310
+ /** Dependencias del transporte, aportadas por la fachada (no hay singleton acá). */
1311
+ interface TransportDeps {
1312
+ http: HttpClient;
1313
+ /** URL base (`API_URL`): el Web App de Apps Script o el origen REST. */
1314
+ apiUrl: string;
1315
+ /** Getter del token de SESIÓN, leído en CADA petición (R2.8). */
1316
+ sessionToken: () => string | null;
1317
+ /**
1318
+ * Refresco opcional de ID token (Fase Firebase): si existe, se llama antes
1319
+ * de armar la cabecera Authorization. Un rechazo viaja por el canal de
1320
+ * errores — nunca se manda una petición con token inválido.
1321
+ */
1322
+ getIdToken?: () => Promise<string>;
1323
+ }
1324
+ /** `POST text/plain` a `apiUrl` con el envelope GAS. */
1325
+ declare function sendGas<T>(deps: TransportDeps, path: string, method: EnvelopeMethod, payload?: Record<string, unknown> | FormData, multipart?: boolean): Observable<T>;
1326
+ /**
1327
+ * Verbo HTTP real con `Authorization: Bearer`. Si el adaptador provee
1328
+ * `getIdToken()`, se resuelve el token fresco ANTES de la cabecera; si no,
1329
+ * se usa el de sesión almacenado (fallback). Cualquier error de `getIdToken()`
1330
+ * se propaga por el canal de errores.
1331
+ */
1332
+ declare function sendRest<T>(deps: TransportDeps, path: string, method: EnvelopeMethod, payload?: Record<string, unknown> | FormData, multipart?: boolean): Observable<T>;
1333
+ /** Despacho REST concreto sobre una URL ya construida. */
1334
+ declare function doRestRequest<T>(deps: TransportDeps, url: string, method: EnvelopeMethod, payload: Record<string, unknown> | FormData | undefined, headers?: HttpHeaders, multipart?: boolean): Observable<T>;
1335
+ /** `${apiUrl}${path}` normalizada: sin dobles slashes ni path sin slash inicial. */
1336
+ declare function buildUrl(apiUrl: string, path: string): string;
1337
+ /**
1338
+ * Convierte un payload en HttpParams para peticiones GET y DELETE en modo REST.
1339
+ * Si un valor es un objeto plano (ej: `{ view: 'calendar', params: { orden: 'es' } }`),
1340
+ * lo aplana un nivel para que sus claves viajen como query params reales
1341
+ * en lugar de serializarse como `params=[object Object]`.
1342
+ */
1343
+ declare function toHttpParams(payload?: Record<string, unknown>): HttpParams;
1344
+ /**
1345
+ * CRUD genérico sobre el contrato v2 (baseapi §2.3) con soporte dual-driver:
1346
+ *
1347
+ * - **`gas` (default)**: TODA llamada a la API es un `POST` a `API_URL` con cuerpo
1348
+ * `text/plain` y el JSON `{ token, method, path, payload }`.
1349
+ * - **`rest`**: Verbos HTTP reales (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) hacia
1350
+ * `${API_URL}${path}` con `Authorization: Bearer ${token}`. `payload` va como query
1351
+ * params en GET/DELETE (aplanando objetos anidados como `params`) o como body JSON
1352
+ * en POST/PUT/PATCH.
1353
+ */
1354
+ export declare class ApiService {
1355
+ private readonly http;
1356
+ private readonly apiUrl;
1357
+ private readonly auth;
1358
+ private readonly driver;
1359
+ /** Fuentes que aporta el consumidor (`provideGasAdmin({ valueSources })`). */
1360
+ private readonly valueSources;
1361
+ /** Adaptador opcional de Firebase Auth para refrescar el ID token antes de peticiones REST. */
1362
+ private readonly firebaseAuthAdapter;
1363
+ /**
1364
+ * Acceso PEREZOSO a `ConfigService` (R2 · plan doc/context-sources-plan.md).
1365
+ *
1366
+ * `config` es fuente construida del motor, pero inyectarla en el constructor
1367
+ * formaría un ciclo: `ConfigService` inyecta `ApiService` (opcional) para
1368
+ * cargar los singletons ⇒ NG0200 en el arranque. Se pide en el momento de la
1369
+ * petición, cuando ambos servicios ya están vivos. Si no existe (TestBed
1370
+ * parcial), devuelve `null` y el resolver falla ruidoso con «sin valor».
1371
+ */
1372
+ private readonly injector;
1373
+ /** Unwrap global por defecto (`GasAdminConfig.defaultResponseUnwrap`); opcional. */
1374
+ private readonly defaultUnwrap;
1375
+ /**
1376
+ * Declaración de unwrap EFECTIVA para un endpoint: fusión del default
1377
+ * global con la declaración por recurso (recurso gana en las claves que
1378
+ * declara). Sin ninguna de las dos ⇒ `undefined` (comportamiento actual).
1379
+ */
1380
+ private effectiveUnwrap;
1381
+ /**
1382
+ * Listado. El backend envuelve la respuesta cuando la operación declara
1383
+ * `shape.envelope: 'list'` (F5) ⇒ `{ items, nextCursor, total }` (R6.4);
1384
+ * la UI consume `T[]` en todos lados (F7-3) y este es el ÚNICO punto que
1385
+ * habla de listas, así que el desenvolvimiento vive acá. `nextCursor` es
1386
+ * el token de la página siguiente y `null` en la última — éste servicio
1387
+ * lo descarta hoy porque la lista no pagina todavía (backlog-ui-ux).
1388
+ * Un listado sin envelope (arreglo plano, el contrato histórico) pasa
1389
+ * tal cual.
1390
+ *
1391
+ * `payload` sólo lo usa una fuente declarada (F8 · R6.5): `{ view, params }`
1392
+ * pide el listado filtrado por una vista y sus parámetros. Sin él, la
1393
+ * llamada es la de siempre — la lectura llana del recurso.
1394
+ */
1395
+ list<T>(endpoint: ResourceEndpoints, payload?: Record<string, unknown>): Observable<T[]>;
1396
+ /**
1397
+ * Listado paginado OPT-IN (plan doc/engine-refactor-plan.md · Fase 3).
1398
+ *
1399
+ * Misma petición que `list()` pero devuelve `PageResult<T>` con la metadata
1400
+ * de paginación que el backend envía en el sobre (`{ items, nextCursor,
1401
+ * total }` o dentro de `meta` con unwrap declarado). Las claves se leen
1402
+ * según `responseUnwrap.pagination` (cursorKey/totalKey/hasMoreKey) o se
1403
+ * auto-detectan (`nextCursor`, `total`, `hasMore`).
1404
+ *
1405
+ * `payload` acepta normalmente `{ cursor, limit }` para pedir la página
1406
+ * siguiente; viaja como cualquier otro payload (query params en GET REST,
1407
+ * campo `payload` del envelope en GAS). `list()` no cambia: sigue
1408
+ * devolviendo `T[]` (R11).
1409
+ */
1410
+ listPage<T>(endpoint: ResourceEndpoints, payload?: Record<string, unknown>): Observable<PageResult<T>>;
1411
+ /** `key` solo aplica a colecciones (`{key}` en la plantilla); los singleton no la llevan. */
1412
+ get<T>(endpoint: ResourceEndpoints, key?: string): Observable<T>;
1413
+ create<T>(endpoint: ResourceEndpoints, payload: Record<string, unknown>): Observable<T>;
1414
+ update<T>(endpoint: ResourceEndpoints, key: string | undefined, payload: Record<string, unknown>): Observable<T>;
1415
+ remove(endpoint: ResourceEndpoints, key?: string): Observable<void>;
1416
+ /**
1417
+ * Operación genérica sobre una ruta ya resuelta: reorder de colección
1418
+ * (`PUT /admin/x` con `{ reorder: … }`), `/admin/schema`,
1419
+ * `/admin/upload-signature`, etc.
1420
+ */
1421
+ request<T>(method: EnvelopeMethod, path: string | undefined, payload?: Record<string, unknown>): Observable<T>;
1422
+ /**
1423
+ * Invoca un endpoint personalizado declarado por el consumidor.
1424
+ *
1425
+ * Acepta bien la definición directa (`CustomEndpointDefinition`) o un schema
1426
+ * + nombre (resuelve en `schema.customEndpoints`). El despacho respeta el
1427
+ * método, la ruta, el modo de payload (json/multipart) y el unwrap propios
1428
+ * de la declaración.
1429
+ */
1430
+ invokeCustomEndpoint<T>(definitionOrSchema: CustomEndpointDefinition | ResourceSchema, nameOrPayload?: string | Record<string, unknown> | FormData, payload?: Record<string, unknown> | FormData, key?: string): Observable<T>;
1431
+ /** Alias corto de `invokeCustomEndpoint`. */
1432
+ invokeCustom<T>(schemaOrDefinition: ResourceSchema | CustomEndpointDefinition, nameOrPayload?: string | Record<string, unknown> | FormData, payload?: Record<string, unknown> | FormData, key?: string): Observable<T>;
1433
+ /** Alias adicional por compatibilidad. */
1434
+ custom<T>(schemaOrDefinition: ResourceSchema | CustomEndpointDefinition, nameOrPayload?: string | Record<string, unknown> | FormData, payload?: Record<string, unknown> | FormData, key?: string): Observable<T>;
1435
+ private send;
1436
+ /**
1437
+ * Dependencias del transporte (Fase 4): la fachada sólo arma este objeto y
1438
+ * delega en `transport.ts`. El token de sesión y el ID token fresco se
1439
+ * resuelven vía getters en el momento de la petición (R2.8 / refresh).
1440
+ */
1441
+ private transportDeps;
1442
+ /**
1443
+ * Fuentes de contexto de ESTA petición: getters, no valores — se evalúan
1444
+ * sólo si el path trae marcadores.
1445
+ *
1446
+ * Las construidas del motor (`auth`, `config`) van ÚLTIMAS: el motor es
1447
+ * dueño de sus fuentes y el registro del consumidor no las pisa.
1448
+ */
1449
+ private buildSources;
1450
+ private withKey;
1451
+ static ɵfac: i0.ɵɵFactoryDeclaration<ApiService, never>;
1452
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1453
+ }
1454
+ /**
1455
+ * Sesión del admin (auth.md §5.2) — **única dueña** del token y del usuario.
1456
+ *
1457
+ * Qué vive acá y por qué:
1458
+ *
1459
+ * - **Señales** `user` / `token` / `isAuthenticated` / `role`: el único
1460
+ * estado que el resto de la app lee (guards, menú, interceptor).
1461
+ * - **`login()` / `logout()` / `logoutAll()` / `restore()`**: operaciones de sesión.
1462
+ * El `AuthService` NO inyecta `ApiService` — sólo `HttpClient` — porque
1463
+ * `ApiService` inyecta a éste para leer el token, y un ciclo de DI en
1464
+ * Angular es un error en tiempo de arranque, no un warning.
1465
+ * - **`applyAuthCode()` (R2.9): EL único intérprete de la tabla de §4.5.**
1466
+ * Ningún otro archivo decide qué pasa ante `AUTH_*` o `FORBIDDEN`.
1467
+ * - **Almacenamiento (R2.12): `sessionStorage`, siempre.** `rememberMe`
1468
+ * no puede usar `localStorage` porque D10-3 lo negó — ver más abajo.
1469
+ *
1470
+ * La sesión arranca desde el storage y `restore()` la revalida contra
1471
+ * `/auth/me` en el initializer: un usuario desactivado mientras la pestaña
1472
+ * estaba abierta cae al primer reload, no en mitad de una edición.
1473
+ */
1474
+ /** Usuario tal y como lo sirve `publicUser_` (lista blanca, R2.17). */
1475
+ interface SessionUser {
1476
+ id?: string;
1477
+ email?: string;
1478
+ name?: string;
1479
+ role?: string;
1480
+ active?: boolean;
1481
+ last_login_at?: string;
1482
+ created_at?: string;
1483
+ }
1484
+ /** Los cinco códigos de §4.5. El resto de errores no son de sesión. */
1485
+ type AuthErrorCode = 'AUTH_REQUIRED' | 'AUTH_INVALID' | 'AUTH_EXPIRED' | 'FORBIDDEN' | 'RATE_LIMITED';
1486
+ /** Ruta de login: fuera del shell para que ningún guard la proteja. */
1487
+ declare const LOGIN_PATH = "/login";
1488
+ /**
1489
+ * Claves del `sessionStorage`. Exportadas porque **la sesión se restaura
1490
+ * desde ahí en el constructor**: un test que quiera una sesión arrancando
1491
+ * tiene que sembrarlas antes de inyectar el servicio, y el que las use con
1492
+ * otro nombre quebraría el restore en silencio.
1493
+ */
1494
+ export declare const SESSION_TOKEN_KEY = "admin.session.token";
1495
+ export declare const SESSION_USER_KEY = "admin.session.user";
1496
+ export declare class AuthService {
1497
+ private readonly http;
1498
+ private readonly apiUrl;
1499
+ private readonly router;
1500
+ private readonly firebaseAdapter;
1501
+ private readonly driver;
1502
+ /**
1503
+ * D10-3: **sólo `sessionStorage`.** La opción «recordarme» con
1504
+ * `localStorage` quedó denegada en §9.3 — muere con la pestaña ⇒ cero
1505
+ * token legible por un XSS futuro. `authSchema.rememberMe` es `false`
1506
+ * por la misma razón: declarar `true` sería prometer un storage que
1507
+ * este servicio no usa.
1508
+ *
1509
+ * Nunca en la URL ni en el cuerpo de un GET (§5.4): el token sólo viaja
1510
+ * dentro del envelope `{ token, method, path, payload }`.
1511
+ */
1512
+ private readonly storage;
1513
+ private readonly _user;
1514
+ private readonly _token;
1515
+ /** Ruta previa a un `AUTH_EXPIRED`, para volver tras reloguear (§4.5). */
1516
+ private readonly _returnUrl;
1517
+ readonly user: import("@angular/core").Signal<SessionUser | null>;
1518
+ readonly token: import("@angular/core").Signal<string | null>;
1519
+ readonly returnUrl: import("@angular/core").Signal<string | null>;
1520
+ /** Sólo hay sesión con token Y usuario: uno sin el otro es estado corrupto. */
1521
+ readonly isAuthenticated: import("@angular/core").Signal<boolean>;
1522
+ readonly role: import("@angular/core").Signal<string>;
1523
+ constructor();
1524
+ login(email: string, password: string): Observable<SessionUser>;
1525
+ /**
1526
+ * Inicia sesión con Google mediante el adaptador de Firebase y canjea
1527
+ * el ID token contra el backend.
1528
+ */
1529
+ loginWithGoogle(): Observable<SessionUser>;
1530
+ /**
1531
+ * Inicia sesión con correo y contraseña en Firebase y canjea el ID token
1532
+ * resultante contra el backend.
1533
+ */
1534
+ loginWithFirebasePassword(email: string, password: string): Observable<SessionUser>;
1535
+ /**
1536
+ * Canjea un token de identidad de Firebase por la sesión propia de la app,
1537
+ * manteniendo idéntico el contrato del envelope y almacenamiento en sessionStorage.
1538
+ */
1539
+ private exchangeFirebaseToken;
1540
+ /**
1541
+ * Revocación best-effort (§9.7): se pide al back que ponga el `sid` en la
1542
+ * lista de revocados, pero **la sesión local se ciere haya o no respuesta**.
1543
+ * Un logout que «falla» por red y deja al usuario dentro es peor que uno
1544
+ * que deja una sesión muerta en el servidor.
1545
+ */
1546
+ logout(): Observable<void>;
1547
+ /** Revalida la sesión restaurada contra `/auth/me`; sin token ⇒ sin viaje. */
1548
+ restore(): Observable<void>;
1549
+ /**
1550
+ * Renovación deslizante (§3.2): el back manda `meta.renewedToken` cuando
1551
+ * la vida del token bajó del umbral. Se persiste el MISMO día que el
1552
+ * token original — el `sid` no cambia, sólo se emite una firma nueva.
1553
+ */
1554
+ applyRenewedToken(token: string): void;
1555
+ /**
1556
+ * ÚNICO intérprete de los códigos de sesión (auth.md §4.5 / §5.2).
1557
+ * El interceptor `apiErrorInterceptor` lo llama y nada más: cualquier
1558
+ * archivo que decida «si ve AUTH_EXPIRED hago X» está fuera de contrato.
1559
+ *
1560
+ * | Código | Qué hace |
1561
+ * | --------------- | ------------------------------------------- |
1562
+ * | `AUTH_REQUIRED` | limpia sesión y va a login |
1563
+ * | `AUTH_INVALID` | limpia sesión y va a login |
1564
+ * | `AUTH_EXPIRED` | guarda la ruta previa, limpia, va a login |
1565
+ * | `FORBIDDEN` | **no** cierra sesión (el usuario sigue dentro) |
1566
+ * | `RATE_LIMITED` | no hace nada: lo espera el formulario |
1567
+ */
1568
+ applyAuthCode(code: string | undefined): void;
1569
+ /** ¿El rol de la sesión cubre `required`? (mínimo: admin ≥ staff, §4.4) */
1570
+ hasRole(required: string): boolean;
1571
+ /**
1572
+ * §5.3 — el ÚNICO criterio de visibilidad del front (menú, rutas y
1573
+ * botones). `requires` viene del vocabulario de §4.4: `public` no pide
1574
+ * sesión; todo lo demás sí; `roles` estrecha.
1575
+ *
1576
+ * Es **pura decisión de UI**. El back re-verifica `accessLevel` en cada
1577
+ * petición (R2.6), así que filtrar acá no protege nada: sólo evita
1578
+ * enseñarle a alguien un botón que le va a devolver 403.
1579
+ */
1580
+ canAccess(access?: {
1581
+ requires?: string;
1582
+ roles?: string[];
1583
+ }): boolean;
1584
+ /**
1585
+ * Evalúa si el usuario puede ejecutar una operación específica según
1586
+ * `policies.access.operations` del recurso (B8 · R7.4a).
1587
+ *
1588
+ * El override por operación tiene prioridad sobre el umbral del grupo.
1589
+ * Si no hay override, se usa el umbral `policies.access.read` o
1590
+ * `policies.access.write` según la operación.
1591
+ *
1592
+ * Fallback a `permissions` (legacy) si `policies` no existe: mientras
1593
+ * los schemas locales no tengan `policies`, se mantiene compatibilidad.
1594
+ *
1595
+ * Es **pura decisión de UI**. El back vuelve a verificar en cada petición
1596
+ * (24-crud), así que esto sólo evita mostrar botones que darían 403.
1597
+ */
1598
+ canDoOperation(schema: ResourceSchema, operation: 'list' | 'get' | 'create' | 'update' | 'remove'): boolean;
1599
+ /**
1600
+ * Evalúa si el usuario puede leer un campo según `field.access.read`
1601
+ * (B8 · R7.4b).
1602
+ *
1603
+ * Es **pura decisión de UI**. El back vuelve a filtrar la respuesta en
1604
+ * cada petición (24-crud), así que esto sólo evita mostrar datos que el
1605
+ * usuario no debería ver.
1606
+ */
1607
+ canReadField(field: FieldSchema): boolean;
1608
+ /**
1609
+ * Evalúa si el usuario puede escribir un campo según `field.access.write`
1610
+ * (B8 · R7.4b).
1611
+ *
1612
+ * Es **pura decisión de UI**. El back rechaza la escritura con 422 si el
1613
+ * usuario no tiene permiso, así que esto sólo pone el campo en
1614
+ * solo-lectura en el frontend.
1615
+ */
1616
+ canWriteField(field: FieldSchema): boolean;
1617
+ /**
1618
+ * Verifica si el rol actual cumple un nivel de acceso dado.
1619
+ * Usa la misma jerarquía que `ACCESS_ROLE_RANK` del back (11-auth):
1620
+ * admin ≥ staff.
1621
+ */
1622
+ private meetsLevel;
1623
+ private open;
1624
+ /**
1625
+ * Borra token **y** usuario (§5.4) — en ese orden de importancia: un token
1626
+ * sin usuario no autentica, pero un usuario sin token engaña al menú.
1627
+ */
1628
+ /**
1629
+ * Ruta previa guardada por un `AUTH_EXPIRED` (§4.5), **consumida**: se
1630
+ * devuelve una sola vez y se borra. Sin el consume, cada login futuro
1631
+ * volvería a la misma URL vieja aunque el usuario hubiera llegado al
1632
+ * login desde el menú.
1633
+ */
1634
+ consumeReturnUrl(): string | null;
1635
+ private clear;
1636
+ private goLogin;
1637
+ private currentUrl;
1638
+ private buildUrl;
1639
+ private envelope;
1640
+ /**
1641
+ * Despacho dual-driver para peticiones de autenticación:
1642
+ *
1643
+ * - **`gas` (default)**: `POST` a `API_URL` con cuerpo `text/plain` y envelope
1644
+ * `{ token, method: 'POST', path, payload }`.
1645
+ * - **`rest`**: llamadas REST reales:
1646
+ * - `/auth/me`: `GET` a `${apiUrl}/auth/me` con `Authorization: Bearer ${token}`.
1647
+ * - resto (`/auth/login`, `/auth/firebase`, `/auth/logout`, etc.): `POST` con
1648
+ * `Content-Type: application/json` y payload directo como body JSON. Si existe
1649
+ * token activo, se adjunta en cabecera `Authorization`.
1650
+ */
1651
+ private buildAuthRequest;
1652
+ private post;
1653
+ static ɵfac: i0.ɵɵFactoryDeclaration<AuthService, never>;
1654
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1655
+ }
1656
+ /** Fila cruda de un listado: el motor no impone forma a las columnas. */
1657
+ type SourceRow = Record<string, unknown>;
1658
+ /**
1659
+ * `source.params` con cada `{campo}` resuelto contra `ctx`.
1660
+ *
1661
+ * - Un valor que se resuelve **vacío se OMITE** en vez de mandar `''`: el
1662
+ * back distingue «parámetro ausente» (fail-closed ⇒ sin filas) de un
1663
+ * `eq ''` que podría cruzarse con filas realmente vacías.
1664
+ * - Un parámetro sin marcador `{}` es literal y viaja tal cual.
1665
+ */
1666
+ declare function interpolateParams(params: Record<string, string> | undefined, ctx?: Record<string, unknown>): Record<string, string>;
1667
+ /**
1668
+ * Clave de caché de una fuente: `resource|view|params` (F8 · R6.5).
1669
+ *
1670
+ * Los parámetros van **resueltos** y ordenados, así que dos fuentes que
1671
+ * terminan pidiendo lo mismo comparten entrada, y un `{campo}` que cambia
1672
+ * es otra entrada. Pura: es también el valor que `resource({ params })` de
1673
+ * Angular compara para decidir si vuelve a cargar.
1674
+ */
1675
+ declare function dataSourceKey(source: DataSource, ctx?: Record<string, unknown>): string;
1676
+ /**
1677
+ * Fetcher único de `source` (F8 · R6.5): relations, selects y widgets del
1678
+ * panel piden sus filas ACÁ y no cada uno desde su componente.
1679
+ *
1680
+ * - **Caché por `resource+view+params`** con TTL e invalidación explícita —
1681
+ * es la pieza que evita que seis widgets del panel disparen seis vueltas
1682
+ * iguales a la misma vista.
1683
+ * - **Deduplicación de vuelta en vuelo**: dos consumidores que pidan la misma
1684
+ * clave en el mismo ciclo comparten la promesa (no la respuesta, la vuelta).
1685
+ * - Un error **no queda cacheado**: se borra la entrada para que la recarga
1686
+ * del consumidor vuelva a intentarlo en vez de devolver siempre la misma
1687
+ * rechazada.
1688
+ */
1689
+ export declare class DataSourceService {
1690
+ private readonly api;
1691
+ private readonly cache;
1692
+ /**
1693
+ * Filas de la fuente declarada.
1694
+ *
1695
+ * `fresh` salta la caché (recarga tras una escritura); `ctx` sólo se usa
1696
+ * para resolver `{campo}` en `params` y no cambia la forma de la respuesta.
1697
+ */
1698
+ rows(source: DataSource, ctx?: Record<string, unknown>, fresh?: boolean): Promise<SourceRow[]>;
1699
+ /**
1700
+ * Vacía las filas ya pedidas. Sin argumento, todas; con un `resourceId`,
1701
+ * sólo las de ese recurso (lo que acaba de crear el drawer de relación).
1702
+ */
1703
+ invalidate(resourceId?: string): void;
1704
+ private fetch;
1705
+ static ɵfac: i0.ɵɵFactoryDeclaration<DataSourceService, never>;
1706
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1707
+ }
1708
+ interface MappedError {
1709
+ /** Errores por ruta de campo (ej. `faq[2].answer`); se pintan en el campo exacto. */
1710
+ fields: Record<string, string>;
1711
+ /** Mensaje general; presente cuando el error no tiene ruta de campo (base.md §10). */
1712
+ general?: string;
1713
+ }
1714
+ /**
1715
+ * Convierte un error recibido en el formato que consume la UI:
1716
+ * rutas de campo para pintar el error donde corresponde, o mensaje general.
1717
+ *
1718
+ * Un422 trae VARIOS issues con ruta (`error.errors[]`, auth.md §6); cada uno
1719
+ * se convierte en `fields[path] = mensaje`, que es exactamente la clave que
1720
+ * después resuelve el host como `serverErrors[prefix + key]`. Si al final no
1721
+ * hay ninguna ruta pintable (error sin `errors[]`, issue sin `path`), el
1722
+ * mensaje del sobre queda en `general` como hasta ahora (base.md §10).
1723
+ */
1724
+ export declare class ErrorMapperService {
1725
+ map(error: unknown): MappedError;
1726
+ messageOf(error: unknown): string;
1727
+ static ɵfac: i0.ɵɵFactoryDeclaration<ErrorMapperService, never>;
1728
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1729
+ }
1730
+ /**
1731
+ * Subida de imágenes en DOS PASOS (baseapi §11.2, opción B):
1732
+ * 1. Firma firmada por el backend → `POST /admin/upload-signature`
1733
+ * con `{ resource }` (mismo envelope text/plain que todo `/admin/*`).
1734
+ * 2. El navegador sube directo a Cloudinary (FormData a `upload_url`) con
1735
+ * los parámetros que fijó y firmó el backend; el API secret jamás sale
1736
+ * de acá (§11.1–§11.3).
1737
+ *
1738
+ * Se guarda SOLO `secure_url` (base.md §7: la URL pública es el valor del
1739
+ * campo; la imagen anterior no se borra al reemplazar — §11.6).
1740
+ *
1741
+ * Los errores (firma caducada, rate-limit 429, red) llegan por el canal de
1742
+ * error; el componente conserva la imagen anterior (§11.2 paso 6).
1743
+ */
1744
+ export declare class UploadService {
1745
+ private readonly http;
1746
+ private readonly api;
1747
+ /**
1748
+ * Misma subida con eventos de progreso (base.md §7, estado «Subiendo»):
1749
+ * emite `UploadProgress` durante la subida a Cloudinary y termina con
1750
+ * `Response` llevando la URL en el body. El campo sólo cambia cuando
1751
+ * llega la respuesta.
1752
+ */
1753
+ uploadWithProgress(file: File, resource: string, field?: string): Observable<HttpEvent<string>>;
1754
+ static ɵfac: i0.ɵɵFactoryDeclaration<UploadService, never>;
1755
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1756
+ }
1757
+ /**
1758
+ * El backend responde SIEMPRE HTTP 200 y pone los errores en el body
1759
+ * (api.md §7), así que se detectan en el canal de **éxito** y se relanzan
1760
+ * como `ApiError` (con `fieldPath` cuando el backend envía `path`).
1761
+ * Los errores HTTP reales (red caída, proxy) también se normalizan.
1762
+ *
1763
+ * **R2.9 — paso dos de dos.** Éste archivo es el ÚNICO punto donde el
1764
+ * sobre de error se abre; lo que decide qué ocurre con un `AUTH_*` es
1765
+ * `AuthService.applyAuthCode` (la tabla de §4.5). Aquí sólo se le entrega
1766
+ * el código y se propaga el `ApiError` con él — un segundo sitio que
1767
+ * «interprete» el código sería una regla duplicada y, a la larga, una que
1768
+ * se contradiga con la primera.
1769
+ */
1770
+ export declare const apiErrorInterceptor: HttpInterceptorFn;
1771
+ interface MenuEntry {
1772
+ id: string;
1773
+ label: string;
1774
+ }
1775
+ /**
1776
+ * Menú lateral generado desde el catálogo EFECTIVO (F7-4): locales
1777
+ * fusionados + recursos sintetizados del schema remoto — un recurso nuevo
1778
+ * del backend aparece acá con 0 cambios en `src/`. Reactivo a la señal del
1779
+ * schema remoto (el initializer lo resuelve antes del primer render, pero
1780
+ * el `computed` cubre el caso de respuesta tardía).
1781
+ * `labelPlural` + `permissions`: un recurso se oculta solo si declara
1782
+ * create, update y remove explícitamente en falso (no hay permiso de
1783
+ * lectura).
1784
+ */
1785
+ export declare class Sidebar {
1786
+ /** Estado del offcanvas (sólo relevante en mobile; ver `layout.ts`).
1787
+ * `model()`: el padre lo setea (abrir) y el hijo también (cerrar al
1788
+ * navegar) — binding de dos vías `[(open)]`. */
1789
+ readonly open: import("@angular/core").ModelSignal<boolean>;
1790
+ private readonly auth;
1791
+ private readonly notifications;
1792
+ readonly isAuthenticated: import("@angular/core").Signal<boolean>;
1793
+ readonly loggingOut: import("@angular/core").WritableSignal<boolean>;
1794
+ /**
1795
+ * R2.10 — el menú se corta dos veces: `isVisible` (permisos declarados
1796
+ * en el schema) y `canAccess` (auth.md §5.3, `access` de la sesión).
1797
+ * Sólo UX: filtrar acá no protege nada, el back re-verifica en R2.6.
1798
+ */
1799
+ readonly menuItems: import("@angular/core").Signal<MenuEntry[]>;
1800
+ closeSession(): Promise<void>;
1801
+ static ɵfac: i0.ɵɵFactoryDeclaration<Sidebar, never>;
1802
+ static ɵcmp: i0.ɵɵComponentDeclaration<Sidebar, "app-sidebar", never, {
1803
+ "open": {
1804
+ "alias": "open";
1805
+ "required": false;
1806
+ "isSignal": true;
1807
+ };
1808
+ }, {
1809
+ "open": "openChange";
1810
+ }, never, never, true, never>;
1811
+ }
1812
+ /**
1813
+ * Panel de métricas — tercer `kind` del registry (refactormotor.md F4).
1814
+ *
1815
+ * Sólo lectura: no hay CRUD y los datos llegan de un endpoint declarativo
1816
+ * (Etapa 2, S4). Los widgets se declaran en `widgets` del schema — no en
1817
+ * `fields`, porque un KPI no es un campo (ver `ResourceSchema.widgets`).
1818
+ *
1819
+ * Dos formas de dato, la que declare cada widget (F8 · R6.5):
1820
+ *
1821
+ * - **payload**: el `key` del widget es su clave en la respuesta del
1822
+ * endpoint; `source: { kind: 'payload' }` lee la respuesta entera.
1823
+ * - **fuente declarada** (`source: { resource, view?, params? }`): el widget
1824
+ * pide sus filas al `DataSourceService` y deja de depender de que un
1825
+ * handler haya armado `data[key]`. Acá se piden TODAS juntas, así que un
1826
+ * panel con seis fuentes iguales hace una vuelta, no seis (la caché del
1827
+ * servicio es compartida con relations y selects).
1828
+ */
1829
+ export declare class Dashboard {
1830
+ private readonly api;
1831
+ private readonly ds;
1832
+ /** Descriptor del dashboard (lo resuelve `ResourcePage` contra el registry). */
1833
+ readonly schema: import("@angular/core").InputSignal<ResourceSchema>;
1834
+ /**
1835
+ * Respuesta del endpoint de métricas indexada por `key` de widget.
1836
+ * Si se pasa explícitamente (como en tests con fixtures), se usa directamente;
1837
+ * de lo contrario se obtienen los datos de forma reactiva desde el endpoint `get`.
1838
+ */
1839
+ readonly data: import("@angular/core").InputSignal<Record<string, unknown> | null>;
1840
+ private readonly fetchedData;
1841
+ protected readonly effectiveData: import("@angular/core").Signal<Record<string, unknown>>;
1842
+ protected readonly widgets: import("@angular/core").Signal<WidgetSchema[]>;
1843
+ /**
1844
+ * Claves de los widgets con fuente declarada, en una cadena: es el
1845
+ * `params` del `resource` y sólo cambia cuando cambia el panel. `''`
1846
+ * (ninguna fuente) ⇒ `undefined` ⇒ el resource queda idle y no pide nada.
1847
+ */
1848
+ private readonly clavesFuente;
1849
+ /**
1850
+ * Filas de todos los widgets con `source`, pedidas de una sola vez.
1851
+ *
1852
+ * Cada fila queda bajo `widget.key`, que es el mismo contrato de lectura
1853
+ * que tiene el payload: `datum()` devuelve filas cuando hay fuente y la
1854
+ * clave de siempre cuando no, y el descriptor del widget traduce la forma
1855
+ * que necesita.
1856
+ */
1857
+ private readonly fuentes;
1858
+ /** ¿Alguna fuente del panel falló? (`status()` no tira; `value()` sí.) */
1859
+ protected readonly falloFuentes: import("@angular/core").Signal<boolean>;
1860
+ /**
1861
+ * Estado de carga del panel — mientras dure, cada tarjeta reserva su
1862
+ * espacio con skeleton (`cargar ≠ vacío`, doc/ux-ui-mobile.md §estados).
1863
+ * Cubre LOS dos caminos de dato: el payload (`null` inicial = sin
1864
+ * respuesta todavía) y las fuentes declaradas (`resource` en loading;
1865
+ * con params vacíos queda idle, que NO es carga).
1866
+ */
1867
+ protected readonly cargando: import("@angular/core").Signal<boolean>;
1868
+ /** `null` explícito cuando la clave no vino: el widget muestra su vacío. */
1869
+ protected datum(widget: WidgetSchema): unknown;
1870
+ static ɵfac: i0.ɵɵFactoryDeclaration<Dashboard, never>;
1871
+ static ɵcmp: i0.ɵɵComponentDeclaration<Dashboard, "app-dashboard", never, {
1872
+ "schema": {
1873
+ "alias": "schema";
1874
+ "required": true;
1875
+ "isSignal": true;
1876
+ };
1877
+ "data": {
1878
+ "alias": "data";
1879
+ "required": false;
1880
+ "isSignal": true;
1881
+ };
1882
+ }, {}, never, never, true, never>;
1883
+ }
1884
+ type Row$1 = Record<string, unknown>;
1885
+ /**
1886
+ * Contexto de una celda de listado (R4.3 · `kind: 'column-format'`).
1887
+ *
1888
+ * Es lo que antes cada `@case` de `list-view` pedía a la vista como
1889
+ * `row`/`column` y lo que el descriptor cuelga de `extras.data`: el
1890
+ * componente de formato no conoce a la vista, sólo a su celda.
1891
+ *
1892
+ * `fields`, `canUpdate`, `saving`, `currency` y `toggle` viajan acá porque
1893
+ * son decisiones de la vista (permisos, moneda del schema, escritura de la
1894
+ * fila); un formato nuevo no necesita tocar `list-view` para leerlas.
1895
+ */
1896
+ interface ColumnCell {
1897
+ /** Fila completa. */
1898
+ row: Row$1;
1899
+ /** Columna que se pinta (`key`, `label`, `format`). */
1900
+ column: ListColumn;
1901
+ /** Campos del recurso: de acá salen `trueLabel`/`falseLabel`. */
1902
+ fields: FieldSchema[];
1903
+ /** `titleField` de la fila, para el `aria-label` del interruptor. */
1904
+ title: string;
1905
+ /** ¿La fila admite escritura? (`ListView.canUpdate`) */
1906
+ canUpdate: boolean;
1907
+ /** ¿La fila está guardando? (`ListView.savingKey === keyOf(row)`) */
1908
+ saving: boolean;
1909
+ /** Moneda del schema, para el formato `currency`. */
1910
+ currency: string;
1911
+ /** Escribe el valor en la fila. Sólo lo usa el interruptor `active`. */
1912
+ toggle?: (checked: boolean) => void;
1913
+ }
1914
+ type Row = Record<string, unknown>;
1915
+ type Status = 'loading' | 'ready' | 'error';
1916
+ /**
1917
+ * Listado genérico de una colección (base.md §10 «Listado»): tabla desde
1918
+ * `listColumns` con los 7 formatos **resueltos por el registro** (R4.3),
1919
+ * búsqueda/filtros **en el cliente** (el
1920
+ * contrato de api.md no define parámetros de consulta: el endpoint admin
1921
+ * devuelve todos los registros), interruptor `active`, acciones de fila y
1922
+ * estados universales (skeleton, vacío, error con reintento).
1923
+ */
1924
+ export declare class ListView {
1925
+ readonly schema: import("@angular/core").InputSignal<ResourceSchema>;
1926
+ /** Emitido al pulsar «Editar» cuando `schema.detail.enabled === true`. */
1927
+ readonly viewRecord: import("@angular/core").OutputEmitterRef<Row>;
1928
+ private readonly api;
1929
+ private readonly auth;
1930
+ private readonly router;
1931
+ readonly notifications: NotificationService;
1932
+ private readonly actionService;
1933
+ readonly status: import("@angular/core").WritableSignal<Status>;
1934
+ readonly rows: import("@angular/core").WritableSignal<Row[]>;
1935
+ readonly query: import("@angular/core").WritableSignal<string>;
1936
+ readonly filterValues: import("@angular/core").WritableSignal<Record<string, string>>;
1937
+ /** Clave del registro cuyo interruptor `active` está guardando. */
1938
+ readonly savingKey: import("@angular/core").WritableSignal<string | null>;
1939
+ /** Fila que se está editando en el drawer (plan 6.5); `null` = cerrado. */
1940
+ readonly quickEditRow: import("@angular/core").WritableSignal<Row | null>;
1941
+ /** Evita que una respuesta lenta pise el recurso ya cambiado. */
1942
+ private loadEpoch;
1943
+ constructor();
1944
+ /** Búsqueda + filtros + orden `positionField` ascendente (cliente). */
1945
+ readonly visibleRows: import("@angular/core").Signal<Row[]>;
1946
+ readonly currencyCode: import("@angular/core").Signal<string>;
1947
+ private readonly permissions;
1948
+ readonly canCreate: import("@angular/core").Signal<boolean>;
1949
+ readonly canUpdate: import("@angular/core").Signal<boolean>;
1950
+ readonly canRemove: import("@angular/core").Signal<boolean>;
1951
+ /** El atajo existe sólo si el schema lo declara y hay permiso de editar. */
1952
+ readonly canQuickEdit: import("@angular/core").Signal<boolean>;
1953
+ /** Subconjunto de campos del drawer, en el orden declarado en el schema. */
1954
+ readonly quickEditFields: import("@angular/core").Signal<readonly FieldSchema[]>;
1955
+ readonly hasRowActions: import("@angular/core").Signal<boolean>;
1956
+ /** Mientras persiste un movimiento: bloquea drag y botones. */
1957
+ readonly reordering: import("@angular/core").WritableSignal<boolean>;
1958
+ /** Sin búsqueda ni filtros aplicados (base.md §10). */
1959
+ private readonly hasActiveFilters;
1960
+ /**
1961
+ * Reordenable sólo si el schema lo declara, el permiso lo permite y no hay
1962
+ * filtros activos: con filtros filtrados, el índice visual no corresponde a
1963
+ * la posición real.
1964
+ */
1965
+ readonly canReorder: import("@angular/core").Signal<boolean>;
1966
+ readonly dropDisabled: import("@angular/core").Signal<boolean>;
1967
+ setQuery(value: string): void;
1968
+ setFilter(key: string, value: string): void;
1969
+ clearFilters(): void;
1970
+ retry(): void;
1971
+ isRowClickable(): boolean;
1972
+ goView(row: Row): void;
1973
+ onRowClick(row: Row): void;
1974
+ goCreate(): void;
1975
+ goEdit(row: Row): void;
1976
+ /** Cerró con guardado: se recarga la fila (la respuesta puede venir achatada). */
1977
+ quickEditSaved(): void;
1978
+ toggleActive(row: Row, checked: boolean): Promise<void>;
1979
+ /** Drop del CDK: `currentIndex` ya es el índice en la lista sin el elemento. */
1980
+ onDrop(event: CdkDragDrop<Row[]>): void;
1981
+ /** Botones subir/bajar (accesibilidad y móvil, base.md §13.10). */
1982
+ moveBy(key: string, delta: -1 | 1): void;
1983
+ /**
1984
+ * Un movimiento → UNA intención al backend (baseapi §2.3 y §9):
1985
+ * `{ reorder: { key, after | toStart | toEnd } }`, nunca posiciones
1986
+ * calculadas. El índice objetivo es la posición FINAL del registro (el
1987
+ * `currentIndex` del CDK ya es el índice de inserción en la lista sin el
1988
+ * elemento, idéntico al índice final; subir/bajar suma/resta 1). El
1989
+ * backend re-balancea, recalcula `position` y devuelve el listado fresco:
1990
+ * sin escrituras optimistas — si falla, no cambió nada. `gap-sorting`
1991
+ * quedó sólo para los campos con listas locales.
1992
+ */
1993
+ private moveRow;
1994
+ removeRow(row: Row): Promise<void>;
1995
+ /**
1996
+ * Ejecuta una acción del schema. El comportamiento vive en el registro de
1997
+ * acciones (R3.9–R3.11): la vista sólo aporta el registro y los puertos.
1998
+ */
1999
+ runAction(action: ResourceAction, row: Row): Promise<void>;
2000
+ /** Icono que el descriptor declara para la acción; sin él, el rótulo. */
2001
+ protected iconOf(action: ResourceAction): LucideIcon | undefined;
2002
+ keyOf(row: Row): string;
2003
+ titleOf(row: Row): string;
2004
+ /**
2005
+ * Formato efectivo de la columna (R4.4 · «declarada, no implícita»).
2006
+ *
2007
+ * `column.format` sólo sobreescribe; si no, manda la tabla de
2008
+ * `column-defaults` según el `type` del campo — nunca el `?? 'text'`
2009
+ * implícito que había.
2010
+ */
2011
+ formatOf(column: ListColumn): ListColumnFormat;
2012
+ /**
2013
+ * Contexto de la celda que recibe el formato (R4.3 · `extras.data`).
2014
+ *
2015
+ * Concentra aquí lo que sólo la vista sabe — permisos, moneda, guardado y
2016
+ * la escritura de la fila — para que «añadir un formato» no la toque.
2017
+ */
2018
+ cellOf(row: Row, column: ListColumn): ColumnCell;
2019
+ filterOptions(filter: ListFilter): SelectOption[];
2020
+ inputValue(event: Event): string;
2021
+ selectValue(event: Event): string;
2022
+ private load;
2023
+ static ɵfac: i0.ɵɵFactoryDeclaration<ListView, never>;
2024
+ static ɵcmp: i0.ɵɵComponentDeclaration<ListView, "app-list-view", never, {
2025
+ "schema": {
2026
+ "alias": "schema";
2027
+ "required": true;
2028
+ "isSignal": true;
2029
+ };
2030
+ }, {
2031
+ "viewRecord": "viewRecord";
2032
+ }, never, never, true, never>;
2033
+ }
2034
+ /** Pedido de «Crear nuevo» lanzado desde el dropdown de una relación. */
2035
+ interface RelationCreateRequest {
2036
+ /** Recurso a crear (el `source.resource` de la relación). */
2037
+ resource: string;
2038
+ /**
2039
+ * Se invoca con la clave recién creada. Vive en la relación porque es ella
2040
+ * la que tiene que recargar sus opciones y preseleccionar el valor.
2041
+ */
2042
+ onCreated: (key: string) => void;
2043
+ }
2044
+ /**
2045
+ * Puente entre el control `relation` y el drawer que abre el formulario.
2046
+ *
2047
+ * Existe como señal inyectable y NO como un `output()` encadenado porque el
2048
+ * camino directo — `relation → DrawerForm → FieldHost → relation` — arma un
2049
+ * ciclo de imports de módulos: `field-host` se decoraría con
2050
+ * `FieldRelation === undefined` y Angular tiraría `Cannot read properties of
2051
+ * undefined (reading 'ɵcmp')`. El campo no puede importar un componente que,
2052
+ * a su vez, lo importa a él.
2053
+ *
2054
+ * Sólo hay un drawer a la vez, así que una única señal alcanza: quien lo
2055
+ * abre lo cierra con `clear()` al recibir `close`/`saved`.
2056
+ */
2057
+ declare class RelationDrawerService {
2058
+ private readonly pending;
2059
+ /** Pedido en curso; `null` cuando no hay panel abierto. */
2060
+ readonly request: import("@angular/core").Signal<RelationCreateRequest | null>;
2061
+ open(resource: string, onCreated: (key: string) => void): void;
2062
+ clear(): void;
2063
+ static ɵfac: i0.ɵɵFactoryDeclaration<RelationDrawerService, never>;
2064
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
2065
+ }
2066
+ type FormMode = 'create' | 'edit' | 'single';
2067
+ type FormStatus = 'loading' | 'ready' | 'error';
2068
+ interface SectionView {
2069
+ id: string;
2070
+ label: string;
2071
+ fields: FieldSchema[];
2072
+ }
2073
+ /**
2074
+ * Un ítem del resumen de errores (§4.11). `id` es el que ya genera
2075
+ * `state().name()`: es a la vez el valor de `data-field` del envoltorio y el
2076
+ * `id` real del control, así que no se inventa ningún ancla nueva.
2077
+ */
2078
+ interface ErrorEntry {
2079
+ id: string;
2080
+ label: string;
2081
+ message: string;
2082
+ /** Sección/pestaña del campo raíz; `undefined` si no está en ninguna. */
2083
+ section?: string;
2084
+ }
2085
+ /**
2086
+ * Formulario genérico: lo construye el schema del recurso (base.md §4–§6,
2087
+ * §10) con `form()` de Signal Forms sobre un modelo `Record<string, unknown>`.
2088
+ *
2089
+ * - **Acceso dinámico**: `form()[field.key]` no compila con claves
2090
+ * arbitrarias; el modelo se tipa como `Record<string, unknown>` y el acceso
2091
+ * vive en `fields/field-node.ts` (`childTree`) y en `form-schema.ts`
2092
+ * (`pathAt`), que centralizan los dos casts.
2093
+ * - **Sin `debounce('blur')`**: difiere `controlValue → value` (el modelo), no
2094
+ * la visibilidad de errores, y con controles custom (`multiselect`, que
2095
+ * nunca emite `touch`) se perderían datos en el payload. La validación al
2096
+ * perder foco la resuelve FieldHost (`invalid() && touched()`).
2097
+ * - `<form novalidate>`: Signal Forms expone `required` como atributo nativo,
2098
+ * y la validación nativa del navegador bloquearía el submit sin mostrar
2099
+ * nada (campos en `<details>` cerrados).
2100
+ */
2101
+ export declare class FormView {
2102
+ /** Puente con el control `relation` (ver `relation-drawer.service`). */
2103
+ readonly relationDrawer: RelationDrawerService;
2104
+ private readonly route;
2105
+ private readonly router;
2106
+ private readonly nav;
2107
+ private readonly api;
2108
+ private readonly auth;
2109
+ private readonly notifications;
2110
+ private readonly errorMapper;
2111
+ /** `form()` puede reconstruirse fuera de contexto (cambio de ruta). */
2112
+ private readonly injector;
2113
+ /** Estabilidad de la app: cambio de sección + carga de chunks (ver `goToError`). */
2114
+ private readonly appRef;
2115
+ private readonly params;
2116
+ private readonly routeData;
2117
+ readonly schema: import("@angular/core").Signal<ResourceSchema | undefined>;
2118
+ /** Recurso actual (lo consumen `image` para pedir la firma, §11.2). */
2119
+ readonly resourceId: import("@angular/core").Signal<string>;
2120
+ /** Registro recién creado para alimentar el modal de resumen post-creación. */
2121
+ readonly createdWithSummary: import("@angular/core").WritableSignal<Record<string, unknown> | null>;
2122
+ readonly mode: import("@angular/core").Signal<FormMode>;
2123
+ readonly heading: import("@angular/core").Signal<string>;
2124
+ readonly status: import("@angular/core").WritableSignal<FormStatus>;
2125
+ /**
2126
+ * D4 — el error ORIGINAL de `load()` con identidad intacta (el mensaje
2127
+ * mostrado es display-only). Se limpia cuando un reintento termina bien.
2128
+ */
2129
+ private errorCarga;
2130
+ errorDeCarga(): unknown;
2131
+ /** Estado «guardando» del botón (además del bloqueo interno de `submit`). */
2132
+ readonly saving: import("@angular/core").WritableSignal<boolean>;
2133
+ /** Errores del backend: rutas de campo + mensaje general (base.md §10). */
2134
+ readonly serverErrors: import("@angular/core").WritableSignal<MappedError>;
2135
+ /** Pestaña activa (solo `layout.mode === 'tabs'`). */
2136
+ readonly activeSection: import("@angular/core").WritableSignal<string | null>;
2137
+ private readonly attemptedSteps;
2138
+ private readonly openSections;
2139
+ private readonly formTree;
2140
+ /** Evita que una respuesta lenta pise el formulario ya reconstruido. */
2141
+ private loadEpoch;
2142
+ /**
2143
+ * Borrador (§4.6 / plan 5.4). `draftOffer` queda en `null` cuando no hay
2144
+ * nada que ofrecer: NUNCA se restaura solo, sólo con un clic del usuario.
2145
+ */
2146
+ private readonly draftOffer;
2147
+ /** Snapshot de lo que vino del servidor; sólo se guarda si el usuario cambió algo. */
2148
+ private loadedSnapshot;
2149
+ /** Registro original, para «Restablecer» (§5). */
2150
+ private loadedRecord;
2151
+ /** Snapshot de los padres de `dependsOn` y el árbol al que pertenece. */
2152
+ private readonly dependsOnSnapshot;
2153
+ private dependsOnTree;
2154
+ /**
2155
+ * Hubo un intento de guardado. Sin este guardia el resumen aparecería de
2156
+ * entrada en cualquier formulario con `required` vacíos: `invalid()` es
2157
+ * cierto desde el primer render, no sólo después de enviar.
2158
+ *
2159
+ * También gobierna el `⚠` de las pestañas y el bloqueo al cambiar de
2160
+ * pestaña (decisión A): antes del primer Guardar un campo `required` vacío
2161
+ * YA es inválido, y andar gritando `⚠` —o trancando la navegación— al
2162
+ * usuario que todavía no le erró a nada es ruido, no ayuda.
2163
+ */
2164
+ readonly submitted: import("@angular/core").WritableSignal<boolean>;
2165
+ constructor();
2166
+ readonly layoutMode: import("@angular/core").Signal<LayoutMode>;
2167
+ readonly layoutColumns: import("@angular/core").Signal<2 | 3>;
2168
+ /** Campos que la UI sabe dibujar (los de Fase 11 se omiten, no se borran del payload). */
2169
+ readonly renderedFields: import("@angular/core").Signal<FieldSchema[]>;
2170
+ /**
2171
+ * Ítems del resumen de errores (§4.11), vacío hasta que se intente guardar.
2172
+ *
2173
+ * Recorre el ÁRBOL, no `serverErrors.fields`: el backend sólo puede
2174
+ * devolver un `path` por respuesta (`api.md §7`), así que ahí nunca hay más
2175
+ * de un ítem. Los errores múltiples los produce la validación del cliente,
2176
+ * que `submit()` deja marcados como tocados de una.
2177
+ */
2178
+ readonly summaryErrors: import("@angular/core").Signal<ErrorEntry[]>;
2179
+ /** Secciones con al menos un campo dibujable. */
2180
+ readonly sectionViews: import("@angular/core").Signal<SectionView[]>;
2181
+ /** Campos sin sección (o con una sección inexistente): siempre visibles. */
2182
+ readonly unsectionedFields: import("@angular/core").Signal<FieldSchema[]>;
2183
+ /** En pestañas solo se dibuja la activa; en secciones, todas (colapsables). */
2184
+ readonly visibleSections: import("@angular/core").Signal<SectionView[]>;
2185
+ /** Indicador _dirty_ + aviso al salir (base.md §10). Agrega descendientes. */
2186
+ readonly dirty: import("@angular/core").Signal<boolean>;
2187
+ /** `slug` con `lockAfterCreate` se bloquea solo si el registro ya existe. */
2188
+ readonly exists: import("@angular/core").Signal<boolean>;
2189
+ readonly canSave: import("@angular/core").Signal<boolean>;
2190
+ readonly canDelete: import("@angular/core").Signal<boolean | undefined>;
2191
+ /** Reconstruye el formulario al cambiar de ruta o de recurso. */
2192
+ private rebuild;
2193
+ private load;
2194
+ /**
2195
+ * Siembra defaults + registro: los campos ausentes en la respuesta conservan
2196
+ * su valor por defecto y los grupos/ítems parciales se fusionan en profundidad
2197
+ * (si no, `childTree` no encontraría nodos y revienta al renderizar).
2198
+ * `value.set` es programático: no marca `dirty`.
2199
+ */
2200
+ private applyRecord;
2201
+ /** `draft:<recurso>:<key|nuevo>` — aislado por recurso Y registro. */
2202
+ private draftStorageKey;
2203
+ /** Best-effort: localStorage puede fallar (modo privado, cuota). */
2204
+ private saveDraft;
2205
+ /**
2206
+ * Ofrece el borrador si es más reciente que el servidor. Si el registro no
2207
+ * trae fecha, cualquier borrador sirve — sólo existe si el usuario cambió
2208
+ * algo, porque `loadedSnapshot` filtra los idénticos.
2209
+ */
2210
+ private offerDraft;
2211
+ /** Restaurar es una acción explícita del usuario (§4.6: nunca en silencio). */
2212
+ restoreDraft(): void;
2213
+ /** Descarta el borrador sin tocar lo cargado. */
2214
+ discardDraft(): void;
2215
+ private clearDraft;
2216
+ /** «hace 1 min» — relativo y discreto (§4.6). */
2217
+ draftAge(savedAt: number): string;
2218
+ /**
2219
+ * Botón «Restablecer» (§5): vuelve a lo que vino del servidor y limpia el
2220
+ * borrador. Es la única vía de descartar cambios en modo edición.
2221
+ */
2222
+ restablecer(): void;
2223
+ retry(): void;
2224
+ /**
2225
+ * D4 — «way back» desde el estado de error: en la ruta directa
2226
+ * (`/:id/new`, `/:id/:key/edit`) no hay output que escape, así que el
2227
+ * servicio declarado navega al origen registrado o a la raíz del recurso.
2228
+ */
2229
+ protected volverDeError(): void;
2230
+ onSubmit(event: Event): Promise<void>;
2231
+ /**
2232
+ * create/update según el modo; nunca lanza (los errores van a `serverErrors`).
2233
+ *
2234
+ * **Regla de éxito por flujo** (guía §6: «toast y volver a la lista o
2235
+ * quedarse según el flujo») — es la misma para los cuatro recursos:
2236
+ *
2237
+ * - **Crear** desde la pantalla completa → **vuelve a la lista** del
2238
+ * recurso: el registro quedó creado y no hay nada más que editar acá.
2239
+ * - **Editar** → **se queda** y recarga lo guardado: el usuario sigue
2240
+ * trabajando sobre el mismo registro.
2241
+ * - **Eliminar** → **vuelve a la lista** (`back()`).
2242
+ * - **Desde un drawer** (crear una relación desde el dropdown, edición
2243
+ * rápida desde la lista) → **se queda** donde estaba: salir destruiría
2244
+ * el contexto que el drawer existía para conservar. Ver
2245
+ * `drawer-form.ts` y `relation-drawer.service.ts`.
2246
+ *
2247
+ * El toast se emite **antes** de navegar y el contenedor vive en el
2248
+ * layout, así que sobrevive al cambio de ruta y el mensaje se lee igual.
2249
+ */
2250
+ private persist;
2251
+ /**
2252
+ * Errores del backend → mapa por ruta; el mensaje se queda en `general`
2253
+ * sólo si su raíz (`address`, `faq[2]` → `faq`) no es un campo dibujado
2254
+ * (base.md §10). Los hijos anidados se resuelven en el host con
2255
+ * `serverErrors[prefix + key]`.
2256
+ */
2257
+ private applyServerErrors;
2258
+ private hasServerErrors;
2259
+ /** Muestra todos los errores: abre secciones, activa la pestaña con problemas. */
2260
+ private revealErrors;
2261
+ /** Scroll + focus al primer campo visible con mensaje de error. */
2262
+ private scrollToFirstError;
2263
+ remove(): Promise<void>;
2264
+ /** Consultado por `unsavedChangesGuard` al salir de la ruta. */
2265
+ canDeactivate(): Promise<boolean>;
2266
+ back(): void;
2267
+ onPostCreateClosed(): void;
2268
+ colSpan(field: FieldSchema): string;
2269
+ /** Id estable de una pestaña: ancla del foco programático. */
2270
+ tabId(sectionId: string): string;
2271
+ /**
2272
+ * `role="tab"` exige moverse con `ArrowLeft`/`ArrowRight`/`Home`/`End`
2273
+ * (WAI-ARIA APG). El foco se reengancha al que quedó ACTIVO, no al que se
2274
+ * pidió: si `selectSection()` bloquea el cambio por validación (§4.9,
2275
+ * decisión A) la selección no se mueve y el foco se queda donde estaba.
2276
+ *
2277
+ * Este handler es también lo que hace correcto el `Tab`: con el roving
2278
+ * `tabindex` sólo la pestaña activa entra en el orden de tabulación, así
2279
+ * que `Tab` va del tablist al primer campo de la sección en lugar de
2280
+ * recorrer las pestañas apagadas.
2281
+ */
2282
+ onTablistKeydown(event: KeyboardEvent): void;
2283
+ tabClass(section: SectionView): string;
2284
+ isFirstStep(): boolean;
2285
+ isLastStep(): boolean;
2286
+ previousStep(): void;
2287
+ nextStep(): void;
2288
+ private unsectionedHasLocalErrors;
2289
+ private revealUnsectionedErrors;
2290
+ private scrollToStep;
2291
+ selectSection(id: string): void;
2292
+ /** Sólo validación local: los errores del backend no bloquean navegar. */
2293
+ private sectionHasLocalErrors;
2294
+ /**
2295
+ * Marca la sección como tocada (los mensajes sólo aparecen una vez tocado)
2296
+ * y devuelve el foco al primer campo con problema, sin cambiar de pestaña.
2297
+ */
2298
+ private revealSectionErrors;
2299
+ isSectionOpen(id: string): boolean;
2300
+ setSectionOpen(id: string, event: Event): void;
2301
+ /**
2302
+ * Índice lateral (§5.3) → sección. Abre el `<details>` primero: si estaba
2303
+ * plegado, saltar antes daría con un elemento sin altura. El foco va al
2304
+ * `<summary>` para que el teclado siga teniendo dónde continuar.
2305
+ */
2306
+ goToSection(id: string): void;
2307
+ /** Invalido (propio, anidado o del backend): marca la pestaña/sección. */
2308
+ fieldHasProblem(field: FieldSchema): boolean;
2309
+ sectionHasProblem(section: SectionView): boolean;
2310
+ /**
2311
+ * Estado de progreso de una sección (§4.3 / decisión A).
2312
+ *
2313
+ * - `complete`: sin problemas → `✓` siempre, es progreso positivo.
2314
+ * - `problem`: con problemas **después** del primer envío → `⚠`.
2315
+ * - `pending`: con problemas pero todavía no se envió → sin marca, para
2316
+ * no amedrentar a quien recién abrió el formulario.
2317
+ */
2318
+ sectionProgress(section: SectionView): 'problem' | 'complete' | 'pending';
2319
+ /**
2320
+ * Un ítem por campo con problema, recorriendo el árbol. Los `group` se
2321
+ * descienden para quedar en la hoja (ahí está el mensaje real); los
2322
+ * compuestos (`list`, `key-value`, `string-list`) se listan como uno,
2323
+ * porque el envoltorio es el único ancla a la que se puede saltar.
2324
+ */
2325
+ private collectErrors;
2326
+ /** Mensaje del backend para una ruta raíz (`faq` cubre `faq[2].answer`). */
2327
+ private serverMessageFor;
2328
+ /**
2329
+ * Enlace del resumen → campo. Cambia de pestaña/sección si hace falta y
2330
+ * sólo después salta: en modo tabs el campo de otra pestaña todavía no
2331
+ * existe en el DOM en este punto.
2332
+ *
2333
+ * Esperar la estabilidad sustituye al `setTimeout` anterior: cubre de una
2334
+ * vez la pasada de change detection del cambio de sección **y** la carga
2335
+ * del chunk perezoso del descriptor (`load()`, R3.4). Sin esa espera el
2336
+ * foco se pierde sobre el envoltorio, que sí existe pero todavía no tiene
2337
+ * control dentro.
2338
+ */
2339
+ goToError(entry: ErrorEntry, event: Event): Promise<void>;
2340
+ /** Scroll + focus al campo con `data-field="id"` (mismo selector que `scrollToFirstError`). */
2341
+ private focusField;
2342
+ /**
2343
+ * Con 2+ errores el foco va al resumen: es la única forma de que el usuario
2344
+ * sepa que hay varios. Con uno solo se lo roba el campo, que ya está a la
2345
+ * vista — por eso el resumen ni se pinta entonces.
2346
+ */
2347
+ private focusSummary;
2348
+ private title;
2349
+ private recordKey;
2350
+ /** Schema destino del panel «Crear nuevo»; `undefined` si no existe. */
2351
+ drawerSchema(resource: string): ResourceSchema | undefined;
2352
+ /**
2353
+ * Cerró el panel con un registro nuevo: se cierra, y si vino con clave se
2354
+ * le pasa a la relación que lo pidió (recarga sus opciones y lo selecciona).
2355
+ */
2356
+ relationCreated(record: Record<string, unknown>, request: RelationCreateRequest): void;
2357
+ static ɵfac: i0.ɵɵFactoryDeclaration<FormView, never>;
2358
+ static ɵcmp: i0.ɵɵComponentDeclaration<FormView, "app-form-view", never, {}, {}, never, never, true, never>;
2359
+ }
2360
+ /**
2361
+ * Reglas del formulario desde el schema (base.md §4, §6, §9, §10).
2362
+ *
2363
+ * Hay DOS clases de reglas y viven en sitios distintos:
2364
+ *
2365
+ * 1. **Ciclo de vida** — `visibleWhen`, `readonlyOn`, `hiddenOn`, `dependsOn`:
2366
+ * decides si el campo se toca, se ve o se manda. Vive acá, con la
2367
+ * estructura del formulario.
2368
+ * 2. **Validadores** — obligatoriedad, longitud, patrón, rangos, únicos…:
2369
+ * vive en `core/registry/field-validators.ts` (F7 + B11), con los MISMOS
2370
+ * nombres que el back. Este módulo sólo RECORRE ese registro por cada
2371
+ * campo: añadir un validador es una entrada allá, no un `case` acá.
2372
+ *
2373
+ * El schema de Signal Forms recibe el path RAÍZ; las reglas se atan a la ruta
2374
+ * dinámica de cada campo. El barrido es **recursivo** — un `group` registra
2375
+ * las reglas de sus hijos bajo su propia ruta y un `list` registra las de
2376
+ * cada ítem con `applyEach`, de modo que las reglas corran también para los
2377
+ * ítems que se agregan después.
2378
+ *
2379
+ * Solo se generan reglas para los tipos que la UI sabe editar (`isSupported('field', …)`):
2380
+ * un campo invisible con `required` bloquearía el guardado para siempre.
2381
+ *
2382
+ * `api` y `excludeKey` alimentan la validación `unique`: se consulta el
2383
+ * listado del recurso y se descarta el propio registro (la clave del registro
2384
+ * en edición, tomada de la ruta).
2385
+ */
2386
+ export declare function buildFormSchema(schema: ResourceSchema, api: ApiService, excludeKey?: string): SchemaFn<Record<string, unknown>>;
2387
+ /** Árbol de un formulario con modelo dinámico. */
2388
+ export type RootTree = FieldTree<Record<string, unknown>>;
2389
+ /**
2390
+ * Contexto que el `ng-template` del `list` recibe por `ngTemplateOutlet`
2391
+ * para renderizar los campos de un ítem. El template vive en el despachador
2392
+ * (`field-host`) porque es el único que puede instanciar `app-field-host`;
2393
+ * el list lo instancia una vez por ítem con este contexto.
2394
+ */
2395
+ export interface ListChildContext {
2396
+ fields: readonly FieldSchema[];
2397
+ tree: RootTree;
2398
+ /** Prefijo de ruta de los errores del backend para este nivel (`faq[2].`). */
2399
+ prefix: string;
2400
+ errors: Record<string, string>;
2401
+ exists: boolean;
2402
+ /** `itemDisplay: 'row'` → los campos van en flujo compacto, no en grilla. */
2403
+ row: boolean;
2404
+ /** Clase `sm:col-span-*` de un campo (literal: Tailwind v4 es JIT). */
2405
+ colSpan: (width?: number) => string;
2406
+ }
2407
+ /** Subárbol del hijo directo bajo `key`. Lanza un error claro si el modelo no
2408
+ * contiene la clave (los modelos se siembran con los `default` del schema
2409
+ * antes de llamar a `form()`).
2410
+ */
2411
+ export declare function childTree<T>(parent: RootTree, key: string): FieldTree<T>;
2412
+ /** Subárbol de un campo `list`/`group` cuyo valor es un objeto. */
2413
+ export declare function groupTree(parent: RootTree, key: string): RootTree;
2414
+ /** Subárbol de un campo `list` (los ítems se acceden por índice). */
2415
+ export declare function listTree<T>(parent: RootTree, key: string): FieldTree<T[]>;
2416
+ /**
2417
+ * Ítem de una lista como árbol navegable.
2418
+ *
2419
+ * **Los nodos de ítem se atan a la identidad del elemento**, no al índice:
2420
+ * tras un cambio del arreglo el nodo viejo queda huérfano y leerlo lanza
2421
+ * `NG01904 «Orphan field»`. Por eso este helper se llama SIEMPRE fresco, en
2422
+ * cada render/computed — nunca se cachea un nodo entre mutaciones.
2423
+ *
2424
+ * El `as unknown` es porque `FieldTree<Item, number>` no es assignable a
2425
+ * `RootTree` (le falta el index signature de `string`), pero se navega igual.
2426
+ */
2427
+ export declare function itemTree<T>(items: FieldTree<T[]>, index: number): RootTree;
2428
+ /**
2429
+ * Ítem escalar de un `string-list` como nodo (`FieldTree<string>`), listo
2430
+ * para `[formField]`. Mismo criterio de frescura que `itemTree`.
2431
+ */
2432
+ export declare function stringItemTree(items: FieldTree<string[]>, index: number): FieldTree<string>;
2433
+ /**
2434
+ * Modelo y payload del formulario genérico (Fase 10, ampliado en Fase 11).
2435
+ *
2436
+ * - `seedModel` siembra un valor inicial para CADA campo declarado en el
2437
+ * schema antes de crear el formulario: el árbol de Signal Forms replica la
2438
+ * forma del modelo, así que un campo ausente no tendría estado (ni valor
2439
+ * que enviar). La siembra vive en `shared/utils/seed.ts`.
2440
+ * - `buildPayload` decide qué viaja al backend (base.md §10): solo las claves
2441
+ * declaradas en el schema, sin los ocultos y sin `positionField` raíz.
2442
+ */
2443
+ /** Valores por defecto del schema, en el mismo orden/shape que el JSON. */
2444
+ export declare function seedModel(schema: ResourceSchema): Record<string, unknown>;
2445
+ /**
2446
+ * Payload del guardado: whitelist de `schema.fields`.
2447
+ *
2448
+ * - Los campos ocultos (`visibleWhen` falso) no se muestran ni se envían
2449
+ * (base.md §10): usar `hidden()` garantiza que no aparezcan acá aunque
2450
+ * tengan valor sembrado — también dentro de `group` y de los ítems de una
2451
+ * `list` (recursivo).
2452
+ * - `positionField` del recurso se excluye aunque esté declarado: el orden
2453
+ * del listado lo gestiona el backend (api.md R3, nunca se expone). El
2454
+ * `positionField` de los ÍTEMS de una lista sí viaja: el orden local lo
2455
+ * calcula el front (base.md §8).
2456
+ * - Los tipos que la UI todavía no edita SÍ viajan: sus valores llegan del
2457
+ * registro leído, así que reenviarlos evita pisarlos.
2458
+ */
2459
+ export declare function buildPayload(schema: ResourceSchema, root: RootTree): Record<string, unknown>;
2460
+ type View = 'list' | 'detail';
2461
+ /**
2462
+ * Página del recurso: resuelve el `id` de la ruta contra el registry y
2463
+ * despacha según `kind` — colección → list view, singleton → form view,
2464
+ * dashboard → panel de sólo lectura — aplicando `permissions` (estado
2465
+ * «sin permisos»).
2466
+ *
2467
+ * Cuando `schema.detail.enabled === true`, intercepta el click de fila del
2468
+ * `ListView` y muestra el `DetailView` en lugar de navegar al formulario.
2469
+ * «Editar» desde el `DetailView` sí navega al formulario de edición.
2470
+ */
2471
+ export declare class ResourcePage {
2472
+ private readonly route;
2473
+ private readonly router;
2474
+ private readonly auth;
2475
+ private readonly params;
2476
+ private readonly routeData;
2477
+ readonly schema: import("@angular/core").Signal<ResourceSchema | undefined>;
2478
+ /** Vista activa: 'list' (por defecto) o 'detail' (al hacer clic en una fila con detail.enabled). */
2479
+ readonly currentView: import("@angular/core").WritableSignal<View>;
2480
+ /** Registro seleccionado para la vista de detalle. */
2481
+ readonly selectedRecord: import("@angular/core").WritableSignal<Record<string, unknown> | null>;
2482
+ constructor();
2483
+ private openFromRoute;
2484
+ /**
2485
+ * Clic de fila. R9.9.4 · decision table: si el schema declara `select`,
2486
+ * el contrato declarado REEMPLAZA el comportamiento local (`route` ⇒
2487
+ * emisión al dispatcher, `inline` ⇒ selección local sin URL, `none` ⇒
2488
+ * no-op); sin declaración, la navegación local actual sigue intacta
2489
+ * (backward compat — R9.9 «existing routes untouched»).
2490
+ */
2491
+ switchToDetail(record: Record<string, unknown>): void;
2492
+ switchToList(): void;
2493
+ goEdit(record: Record<string, unknown>): void;
2494
+ /**
2495
+ * Colección sin ninguna operación posible u singleton sin `update`: no hay
2496
+ * nada que mostrar con sentido (base.md §10 «Sin permisos»).
2497
+ *
2498
+ * R7.5: usa policies cuando existan, fallback a permissions.
2499
+ *
2500
+ * Un dashboard nunca entra aquí: es un panel de sólo lectura, `ResourcePermissions`
2501
+ * no tiene `read` y el acceso real a los datos lo decide el endpoint
2502
+ * (A4: el cliente es interfaz, no autoridad).
2503
+ */
2504
+ hasNoAccess(schema: ResourceSchema): boolean;
2505
+ static ɵfac: i0.ɵɵFactoryDeclaration<ResourcePage, never>;
2506
+ static ɵcmp: i0.ɵɵComponentDeclaration<ResourcePage, "app-resource-page", never, {}, {}, never, never, true, never>;
2507
+ }
2508
+ /**
2509
+ * R9.9 · slice 3 — Host genérico de composición (`uiView` tipo `tabs`).
2510
+ *
2511
+ * Un SOLO componente recursivo renderiza cualquier composición declarada:
2512
+ * resuelve cada hijo por `(kind:'view', type)` dentro del `ViewSlot` (D2,
2513
+ * sin `switch` de dominio y sin importar hojas), monta la validación del
2514
+ * schema ANTES del primer render (fail-loud) y maneja el tablist accesible
2515
+ * con roving tabindex (R9.9.5).
2516
+ *
2517
+ * Decisiones R9.9.5:
2518
+ * - Retención: un hijo `retain:true` visitado queda MONTADO pero oculto
2519
+ * (`hidden`); sin `retain` se destruye al cambiar de pestaña.
2520
+ * - Pestaña activa LOCAL por componente: no hay estado compartido entre
2521
+ * instancias de tabs anidadas (documentado en los docs de slice 5).
2522
+ * - `visibleWhen` se evalúa sin record (el contenedor no es un formulario):
2523
+ * contra registro vacío, TODAS las condiciones deben pasar (AND).
2524
+ * Si la pestaña activa queda oculta, la selección cae visiblemente a la
2525
+ * primera visible — nunca se pierde en silencio.
2526
+ */
2527
+ export declare class ComposerHost {
2528
+ private readonly route;
2529
+ private readonly routeData;
2530
+ /** Schema: explícito en `data` (fixtures/ruta embebida) o vía `resourceId`. */
2531
+ readonly schema: import("@angular/core").Signal<ResourceSchema | undefined>;
2532
+ /** instanceId de ESTE contenedor (raíz ⇒ nombre de la uiView de ruta). */
2533
+ readonly uiViewName: import("@angular/core").Signal<string>;
2534
+ /** Profundidad de ESTE contenedor (raíz ⇒ 0; embebido ⇒ payload.depth). */
2535
+ readonly profundidad: import("@angular/core").Signal<number>;
2536
+ /** Config `tabs` declarada para esta instancia. */
2537
+ readonly vista: import("@angular/core").Signal<UiViewTabs | undefined>;
2538
+ /** Error determinista de configuración (schema/vista ausente o no-tabs). */
2539
+ private readonly errorConfig;
2540
+ /** Error de validación D2 (se recalcula una sola vez, en el constructor). */
2541
+ private readonly errorValidacion;
2542
+ /** Error mostrado: validación del schema primero, luego configuración. */
2543
+ readonly error: import("@angular/core").Signal<string | null>;
2544
+ /** Todos los hijos declarados (orden de declaración). */
2545
+ private readonly hijos;
2546
+ /** Hijos visibles: TODAS sus condiciones `visibleWhen` pasan sin record. */
2547
+ readonly visibles: import("@angular/core").Signal<readonly ViewChildNode[]>;
2548
+ /** Selección del usuario (intención; vacío ⇒ usar `initial`/primera). */
2549
+ private readonly seleccion;
2550
+ /**
2551
+ * Pestaña activa EFECTIVA: derivada pura de la selección y de las
2552
+ * visibles — si la selección dejó de existir (u oculta), cae a `initial`
2553
+ * o a la primera visible de forma SINCRÓNICA y visible (R9.9.5: nunca
2554
+ * perder la vista elegida en silencio). Estado LOCAL por instancia.
2555
+ */
2556
+ readonly activo: import("@angular/core").Signal<string>;
2557
+ /** Ids ya seleccionados alguna vez (sustento de la retención). */
2558
+ private readonly visitados;
2559
+ /** Paneles en DOM: activo + retenidos visitados que siguen visibles. */
2560
+ readonly montados: import("@angular/core").Signal<readonly ViewChildNode[]>;
2561
+ constructor();
2562
+ /** Selecciona una pestaña y, opcionalmente, mueve el foco a su botón. */
2563
+ seleccionar(id: string, conFoco: boolean): void;
2564
+ /** Navegación por teclado del tablist: flechas con wrap, Home/End. */
2565
+ onKeydown(event: KeyboardEvent): void;
2566
+ /** El panel de este hijo está montado (⇒ existe su `aria-controls`). */
2567
+ montado(id: string): boolean;
2568
+ /** Clases del botón de pestaña: activo resaltado con token `primary`. */
2569
+ claseTab(id: string): string;
2570
+ /** Etiqueta a11y del tablist (la del recurso). */
2571
+ etiqueta(): string;
2572
+ static ɵfac: i0.ɵɵFactoryDeclaration<ComposerHost, never>;
2573
+ static ɵcmp: i0.ɵɵComponentDeclaration<ComposerHost, "app-composer-host", never, {}, {}, never, never, true, never>;
2574
+ }
2575
+ /**
2576
+ * Contexto de accesibilidad que `app-field-host` expone a los controles.
2577
+ *
2578
+ * El host conoce la ayuda y el error (ambos viven en SU plantilla), pero el
2579
+ * `<input>` real vive dentro de un componente hijo distinto por cada tipo de
2580
+ * campo. En lugar de pasar dos inputs por cada uno de los 14 controles, el
2581
+ * host provee este contexto y cada control importa `FieldAria`, que lo lee
2582
+ * por DI y lo vuelca en el elemento.
2583
+ *
2584
+ * Selector por `[id]`: casa sólo con el elemento que ya recibe el `for` de la
2585
+ * etiqueta, así el mismo id es ancla de label y de `aria-describedby`. Los
2586
+ * controles fuera de un `app-field-host` inyectan `null` y no emiten nada.
2587
+ */
2588
+ interface FieldAriaContext {
2589
+ /** `id` del texto de ayuda y/o del error, separados por espacio. */
2590
+ readonly describedBy: Signal<string | null>;
2591
+ /** `true` cuando el campo muestra error visible (mismo criterio que el mensaje). */
2592
+ readonly invalid: Signal<boolean>;
2593
+ /**
2594
+ * Requerido por schema. Necesario porque la marca visual `*` desaparece en
2595
+ * los recursos donde mandan los `(opcional)` (§2): sin este atributo el
2596
+ * requerimiento quedaría sólo en pantalla.
2597
+ */
2598
+ readonly required: Signal<boolean>;
2599
+ }
2600
+ declare const FIELD_ARIA: InjectionToken<FieldAriaContext>;
2601
+ /**
2602
+ * Vincula `aria-invalid` y `aria-describedby` a un control de formulario.
2603
+ *
2604
+ * `aria-invalid` se emite SIEMPRE (`"true"`/`"false"`, nunca ausente) para
2605
+ * que el atributo sea inspeccionable en cualquier estado; `aria-describedby`
2606
+ * se omite cuando no hay ni ayuda ni error, que es el caso por defecto.
2607
+ * `aria-required` sigue la misma regla que `aria-invalid`.
2608
+ *
2609
+ * Convención con `role="alert"` en el mensaje de error: el `alert` anuncia el
2610
+ * error en el momento en que aparece (fallo de submit), mientras que
2611
+ * `aria-describedby` lo lee al enfocar el campo. Son momentos distintos; no
2612
+ * compiten.
2613
+ */
2614
+ declare class FieldAria {
2615
+ private readonly ctx;
2616
+ protected readonly ariaInvalid: Signal<string | null>;
2617
+ protected readonly ariaRequired: Signal<string | null>;
2618
+ protected readonly ariaDescribedBy: Signal<string | null>;
2619
+ static ɵfac: i0.ɵɵFactoryDeclaration<FieldAria, never>;
2620
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FieldAria, "input[id], select[id], textarea[id], output[id]", never, {}, {}, never, never, true, never>;
2621
+ }
2622
+ /**
2623
+ * Forma del valor inicial que siembra un campo (D32 · R3.8).
2624
+ *
2625
+ * Son **todos** los valores posibles del dominio: un campo de formulario
2626
+ * nuevo nace como cadena, número, booleano, lista o como contenedor que
2627
+ * siembra sus hijos. Si un tipo llegara a necesitar otra forma, ampliar este
2628
+ * `type` obliga a compilar el `switch` de `seedValue` — que es exactamente
2629
+ * el fail-loud que la regla 4 pide.
2630
+ */
2631
+ type SeedKind = 'string' | 'number' | 'boolean' | 'array' | 'recursive';
2632
+ /**
2633
+ * Siembra del modelo del formulario: un valor por defecto para CADA campo
2634
+ * declarado en el schema (base.md §10).
2635
+ *
2636
+ * Compartido entre `form-model` (crear/recargar) y los componentes de lista
2637
+ * (crear ítems nuevos con la forma de sus `itemFields`).
2638
+ */
2639
+ /**
2640
+ * Valor inicial cuando el schema no declara `default`.
2641
+ *
2642
+ * **D32 · ya no despacha por tipo.** El `switch` de 20 ramas que obligaba a
2643
+ * editar este fichero al añadir un campo — y que §0 contaba como tocar el
2644
+ * motor — pasa a resolver el nombre que declara el propio descriptor
2645
+ * (`RenderDescriptor.seed`). «Añadir un tipo» queda en carpeta + manifiesto
2646
+ * + unión: este archivo no se entera.
2647
+ *
2648
+ * Dos salidas distintas, ninguna silenciosa:
2649
+ *
2650
+ * · **tipo fuera del catálogo** ⇒ `undefined`, la misma degradación
2651
+ * controlada que el render (R1.10): el campo no se pinta, así que su
2652
+ * valor no existe;
2653
+ * · **descriptor sin `seed`** ⇒ lanza. Es un error de programación que
2654
+ * `core/registry/contract.spec.ts` detecta en CI antes de llegar acá.
2655
+ */
2656
+ declare function seedValue(field: FieldSchema): unknown;
2657
+ /** Valor sembrado de un campo, fusionando `default` con la forma del schema. */
2658
+ declare function seedFieldValue(field: FieldSchema): unknown;
2659
+ /**
2660
+ * Siembra recursiva: los `group` anidan sus propios campos. Exportada porque
2661
+ * la lista crea sus ítems nuevos con la misma forma (`base.md §6`).
2662
+ */
2663
+ declare function seedFields(fields: readonly FieldSchema[]): Record<string, unknown>;
2664
+ /**
2665
+ * Fusiona el registro leído con la forma sembrada del schema.
2666
+ *
2667
+ * El backend puede omitir claves (p. ej. un ítem guardado antes de que el
2668
+ * schema agregara un campo): sin esta fusión el árbol no tendría nodo para
2669
+ * ese campo y `childTree` lanzaría al renderizar. Lo ausente toma su
2670
+ * `default` y los extras del registro (p. ej. `position` de los ítems) se
2671
+ * conservan.
2672
+ */
2673
+ declare function mergeRecord(fields: readonly FieldSchema[], record: Record<string, unknown>): Record<string, unknown>;
2674
+ declare function isRecord(value: unknown): value is Record<string, unknown>;
2675
+ /**
2676
+ * Registro único de render (R3.1 · `doc/plan-refactor-motor.md` §3.1).
2677
+ *
2678
+ * Un solo sitio declara qué componentes sabe dibujar la UI: cada tipo de
2679
+ * campo, widget, acción o vista aporta un `RenderDescriptor` y el registro
2680
+ * lo resuelve por `(kind, type)`. Los hosts dejan de mantener `Set`s de tipos
2681
+ * e imports a mano — **en runtime el registro es la única fuente de verdad**.
2682
+ *
2683
+ * Se carga a scope de módulo y no desde DI: `buildFormSchema()` es una
2684
+ * función pura que consulta el registro sin inyectar nada, así que un
2685
+ * registro llenado únicamente por un provider dejaría esa consulta correr
2686
+ * contra un catálogo vacío. `provideRenderDescriptors()` existe igualmente
2687
+ * como punto de entrada explícito para `app.config.ts` (R3.2).
2688
+ *
2689
+ * Autodescubrimiento (D9-1): se opta por **registro manual** — un manifiesto
2690
+ * con una línea por descriptor — porque `import.meta.glob` no está
2691
+ * implementada por el bundler de producción (`@angular/build:application`
2692
+ * usa esbuild) y la llamada cruda sobreviviría en `dist/`, rompiendo el
2693
+ * arranque. Con Vite sólo en el dev-server y en el runner de tests, además,
2694
+ * el comportamiento habría diferido entre `npm test` y `npm run build`.
2695
+ */
2696
+ /** Qué clase de cosa se está resolviendo. Identificador único dentro de su `kind`. */
2697
+ type RenderKind = 'field' | 'column-format' | 'widget' | 'action' | 'view' | 'layout';
2698
+ type ComponentRenderKind = Exclude<RenderKind, 'layout'>;
2699
+ /** Superficies en las que un descriptor puede aparecer. */
2700
+ type RenderSurface = 'form' | 'display' | 'list' | 'filter';
2701
+ /**
2702
+ * Qué puede describir un `RenderDescriptor`: un campo del formulario, un
2703
+ * widget del dashboard o una columna de listado (R4.3).
2704
+ *
2705
+ * Es el tipo del input `config` del host. Los descriptores sólo comparten
2706
+ * `key` y `label`; lo demás lo decide cada `kind`, y quien necesita el resto
2707
+ * lo castea con su propia mirada (`cfgCampo()`, que es lo que el host ya
2708
+ * hacía antes de que existieran las columnas).
2709
+ */
2710
+ type RenderConfig = FieldSchema | WidgetSchema | ListColumn;
2711
+ /**
2712
+ * Contexto de render que el host expone por DI (R3.4 · `doc/plan-refactor-motor.md` §3.4).
2713
+ *
2714
+ * Los controles viven en un componente distinto por cada tipo, así que en vez
2715
+ * de repetir cinco inputs en los 20 el host lo provee una sola vez y cada
2716
+ * control lo inyecta si lo necesita — el mismo criterio que ya usaba
2717
+ * `FIELD_ARIA`.
2718
+ *
2719
+ * `tree` viaja en el contexto y no como input suelto porque es **por
2720
+ * instancia**: los hijos de un `group` o de un `list` navegan su subárbol, no
2721
+ * el del padre.
2722
+ *
2723
+ * Vive acá y no en `render-host.ts` porque el contrato de los descriptores
2724
+ * (`RenderExtras`) necesita nombrarlo: el descriptor de un campo compuesto
2725
+ * decide qué se le pasa al componente y eso sólo es escribible conociendo el
2726
+ * tipo. `render-host.ts` lo re-exporta para no mover a sus consumidores.
2727
+ */
2728
+ interface RenderContext {
2729
+ /** Árbol del formulario del nivel de este campo. */
2730
+ tree: RootTree;
2731
+ /** Errores del backend por ruta (`address.street`, `faq[2].answer`). */
2732
+ serverErrors: Record<string, string>;
2733
+ /** Prefijo de ruta de este campo en el registro raíz (`address.`). */
2734
+ pathPrefix: string;
2735
+ /** Si el registro ya existe (lo consume `slug` con `lockAfterCreate`). */
2736
+ exists: boolean;
2737
+ /** Recurso dueño del formulario (lo consume `image` para la firma, §11.2). */
2738
+ resource: string;
2739
+ }
2740
+ /**
2741
+ * Contexto vacío con el que arranca `RenderHost.ctx`.
2742
+ *
2743
+ * Sólo lo alcanza quien renderiza sin contexto — los widgets, que no forman
2744
+ * parte de un formulario. Un campo montado sin `ctx` es un error de
2745
+ * programación y `childTree` lo dice con su propio mensaje, igual que lo
2746
+ * decía el `input.required` anterior.
2747
+ */
2748
+ declare const RENDER_CONTEXT_VACIO: RenderContext;
2749
+ /**
2750
+ * Segundo argumento de `RenderDescriptor.inputs`.
2751
+ *
2752
+ * Aporta lo que el host conoce y el descriptor no: el contexto de formulario
2753
+ * (sólo para campos), el payload del dato (sólo para widgets) y los dos
2754
+ * templates con los que se componen `group` y `list`.
2755
+ */
2756
+ interface RenderExtras {
2757
+ /** Contexto del nivel; `RENDER_CONTEXT_VACIO` cuando no hay formulario. */
2758
+ ctx: RenderContext;
2759
+ /** Payload del widget (`<app-widget-host [data]>`); `null` en campos. */
2760
+ data: unknown;
2761
+ /** Template del contenido de un `group`. */
2762
+ groupContent?: TemplateRef<unknown>;
2763
+ /** Template de los hijos de un `list`. */
2764
+ childTemplate?: TemplateRef<unknown>;
2765
+ }
2766
+ /**
2767
+ * Declaración de un componente renderizable.
2768
+ *
2769
+ * No lleva `configSchema`: esa cláusula de `refactormotor.md` no existe en
2770
+ * el código y el plan la descartó (D17).
2771
+ */
2772
+ interface RenderDescriptor<C = unknown> {
2773
+ /** Clase de cosa. */
2774
+ kind: ComponentRenderKind;
2775
+ /** Identificador `kebab-case`, único dentro de `kind`. */
2776
+ type: string;
2777
+ /** Versión del contrato en que apareció (`doc/refactormotor.md`). */
2778
+ since: string;
2779
+ /**
2780
+ * Carga perezosa del componente. El registro se resuelve sin arrastrar el
2781
+ * bundle: los descriptores sólo guardan el cierre, no la clase.
2782
+ */
2783
+ load: () => Promise<Type<any>>;
2784
+ /** Superficies en las que se usa. Sólo lo que se pueda evidenciar en el código. */
2785
+ surfaces?: RenderSurface[];
2786
+ /** Valores por defecto de la configuración del descriptor. */
2787
+ defaults?: Partial<C>;
2788
+ /**
2789
+ * Forma del valor inicial que siembra este campo en el modelo del
2790
+ * formulario (D32 · R3.8).
2791
+ *
2792
+ * **Dato, no función**: un nombre resuelto por `shared/utils/seed.ts`, no
2793
+ * una clausura. Así el descriptor sigue siendo puro —`kind`, `type`,
2794
+ * `load`, `surfaces`, `seed`— y `seed.ts` es el único dueño de recorrer
2795
+ * un árbol de campos. La alternativa (`seed: (cfg) => …`) obligaría a que
2796
+ * `group` importara `seedFields`, lo que cerraría un ciclo
2797
+ * registro → manifiesto → descriptor → `seed` → registro.
2798
+ *
2799
+ * Sólo los campos lo declaran. **Obligatorio para `kind: 'field'`** — lo
2800
+ * vigila `core/registry/contract.spec.ts`, porque la exhaustividad de una
2801
+ * unión de strings no puede imponerse en tiempo de compilación.
2802
+ */
2803
+ seed?: SeedKind;
2804
+ /**
2805
+ * Cableado del componente: qué inputs recibe y con qué valor.
2806
+ *
2807
+ * Es **la** pieza que mantiene el host genérico: sin esto el motor tendría
2808
+ * que saber que `metric-card` recibe `label/value/hint` y que `group`
2809
+ * necesita además el template de sus hijos, y «añadir un tipo» volvería a
2810
+ * tocar `render-host.ts` (R3.8). Cada descriptor declara el suyo y el host
2811
+ * sólo hace `ngComponentOutletInputs = descriptor.inputs(config, extras)`.
2812
+ *
2813
+ * No es obligatorio: el 80 % de los tipos comparten la forma estándar de su
2814
+ * `kind` — `{field, tree}` en campos, `{widget, data}` en widgets — y para
2815
+ * ellos el host aplica esa forma por defecto. Se declara sólo cuando el
2816
+ * componente se desvía de ella.
2817
+ */
2818
+ inputs?: (config: C, extras: RenderExtras) => Record<string, unknown>;
2819
+ }
2820
+ /** Declaración de un layout manejado por el host de formularios. */
2821
+ interface LayoutDescriptor {
2822
+ kind: 'layout';
2823
+ type: LayoutMode;
2824
+ since: string;
2825
+ }
2826
+ /**
2827
+ * Registra un descriptor. Idempotente por clave: volver a registrar el mismo
2828
+ * `(kind, type)` lo sobrescribe, lo que permite que un módulo se recargue en
2829
+ * desarrollo sin duplicar entradas.
2830
+ */
2831
+ declare function register(kind: 'layout', descriptor: LayoutDescriptor): void;
2832
+ declare function register(kind: ComponentRenderKind, descriptor: RenderDescriptor): void;
2833
+ /**
2834
+ * Quita un descriptor (R7.6). Simétrico a `register`: los specs que
2835
+ * registran entradas de prueba deben retirarlas, o el catálogo global queda
2836
+ * contaminado para cualquier otro spec que lea `renderKinds()` (flaky por
2837
+ * orden de ejecución).
2838
+ */
2839
+ declare function unregister(kind: RenderKind, type: string): void;
2840
+ /** Descriptor declarado para `(kind, type)`, o `undefined` si nadie lo registró. */
2841
+ declare function get(kind: ComponentRenderKind, type: string): RenderDescriptor | undefined;
2842
+ /** ¿La UI sabe dibujar este `(kind, type)`? Reemplaza a los `Set`s de los hosts. */
2843
+ export declare function isSupported(kind: RenderKind, type: string): boolean;
2844
+ /**
2845
+ * Componente para `(kind, type)`, o `fallback` si el tipo no está en el
2846
+ * catálogo.
2847
+ *
2848
+ * Es **asíncrona** porque `load()` importa el componente bajo demanda; el
2849
+ * `fallback` es síncrono y sólo se usa cuando no hay descriptor. Un fallo de
2850
+ * `load()` NO se traga: propagarlo es lo que hace visible un descriptor mal
2851
+ * escrito, y la política «nunca pantalla rota» la resuelve el llamante con el
2852
+ * `fallback` que ya recibe.
2853
+ */
2854
+ declare function resolve<C = unknown>(kind: ComponentRenderKind, type: string, fallback: Type<C>): Promise<Type<C>>;
2855
+ /**
2856
+ * Punto de entrada para `app.config.ts` (R3.2).
2857
+ *
2858
+ * El catálogo ya quedó cargado al importar este módulo; el provider sirve
2859
+ * para que el arranque sea explícito y para avisar en desarrollo si el
2860
+ * manifiesto vino vacío — que es exactamente lo que pasaría si alguien
2861
+ * borra una línea del manifiesto sin reemplazarla.
2862
+ */
2863
+ export declare function provideRenderDescriptors(): EnvironmentProviders;
2864
+ /** Cuántos descriptores hay registrados. Sólo para comprobaciones y tests. */
2865
+ declare function renderDescriptorCount(): number;
2866
+ /**
2867
+ * `kind`s que hoy tienen al menos un descriptor.
2868
+ *
2869
+ * Existe para el test de exhaustividad (R3.7): él recorre los schemas de los
2870
+ * `kind`s que tienen registro y **falla** si aparece un `kind` con descriptor
2871
+ * que nadie recorre — de lo contrario «tener registro» y «estar cubierto»
2872
+ * serían dos afirmaciones distintas que nadie reconcilia.
2873
+ */
2874
+ export declare function renderKinds(): RenderKind[];
2875
+ /** Token con el que los controles inyectan el `RenderContext` del host. */
2876
+ declare const RENDER_CONTEXT: InjectionToken<RenderContext>;
2877
+ /**
2878
+ * Host genérico de render (R3.4).
2879
+ *
2880
+ * Sustituye el `@switch` de 21 ramas de `FieldHost` por una resolución contra
2881
+ * `core/registry/render-registry.ts`: el tipo llega por `[type]`, el registro
2882
+ * devuelve el componente y `NgComponentOutlet` lo monta. Añadir un tipo deja
2883
+ * de tocar este archivo.
2884
+ *
2885
+ * Conserva el envoltorio que `FieldHost` dibujaba — `<label>`, marca
2886
+ * required/opcional, `data-field`, `colSpanClass`, mensaje de error y ayuda —
2887
+ * porque eso es lo que el operador ve y no depende del tipo de control.
2888
+ *
2889
+ * **Sin imports de los 20 controles**: sólo llegan por `load()` perezoso del
2890
+ * descriptor. Mientras el `import()` resuelve se pinta `Skeleton`, y un tipo
2891
+ * fuera del catálogo degrada a `UnknownRender` en dev (nunca pantalla rota).
2892
+ *
2893
+ * **Modo `display` (R4.1 · R4.2).** El mismo host pinta la ficha de detalle:
2894
+ * con `mode="display"` no hay árbol, ni etiqueta, ni error — sólo el valor
2895
+ * crudo que llega por `[value]` y el componente que elige la tabla
2896
+ * `fields/display/display-defaults.ts`. El descriptor declara la superficie
2897
+ * (`surfaces: ['form', 'display']`) y, si no la declara o el tipo no está en
2898
+ * la tabla, corre la misma degradación controlada de R1.10.
2899
+ */
2900
+ declare class RenderHost implements FieldAriaContext, RenderContext {
2901
+ /** Categoría de render. Resuelve campos y widgets (R3.6). */
2902
+ readonly kind: import("@angular/core").InputSignal<ComponentRenderKind>;
2903
+ /** Identificador del tipo dentro de su `kind`. */
2904
+ readonly type: import("@angular/core").InputSignal<string>;
2905
+ /**
2906
+ * Configuración del descriptor: el `FieldSchema` del campo, el
2907
+ * `WidgetSchema` del widget o la `ListColumn` de la columna (R4.3).
2908
+ */
2909
+ readonly config: import("@angular/core").InputSignal<RenderConfig>;
2910
+ /**
2911
+ * Contexto de render del nivel de este campo.
2912
+ *
2913
+ * Con valor por defecto y no requerido: los widgets no pertenecen a un
2914
+ * formulario y llegan sin árbol (R3.6). El default es un contexto vacío
2915
+ * que nunca se lee en esa rama — un campo montado sin contexto real ya
2916
+ * era un error antes y `childTree` lo sigue siendo.
2917
+ */
2918
+ readonly ctx: import("@angular/core").InputSignal<RenderContext>;
2919
+ /** Payload del widget (`app-widget-host [data]`). `null` en campos. */
2920
+ readonly data: import("@angular/core").InputSignal<unknown>;
2921
+ /**
2922
+ * Superficie de render (R4.1).
2923
+ *
2924
+ * `'form'` (por defecto) es el formulario de siempre: árbol, etiqueta,
2925
+ * marca required, error y ayuda. `'display'` es la ficha de detalle:
2926
+ * valor crudo, sin envoltorio — es el `mode="display"` con el que
2927
+ * `detail-view` deja de dibujar sus propias celdas (R4.2).
2928
+ */
2929
+ readonly mode: import("@angular/core").InputSignal<"form" | "display">;
2930
+ /**
2931
+ * Valor crudo del registro, **sólo para el modo `display`**.
2932
+ *
2933
+ * En el formulario el valor vive en el árbol de Signal Forms y este
2934
+ * input se ignora: un campo de detalle no tiene árbol que consultar.
2935
+ */
2936
+ readonly value: import("@angular/core").InputSignal<unknown>;
2937
+ /**
2938
+ * Presentación en chip, **sólo para el modo `display`** (R4.2).
2939
+ *
2940
+ * La cabecera de la ficha siempre mostró `headerFields` como etiquetas
2941
+ * compactas, aunque el valor fuera texto plano. Sin este matiz el host
2942
+ * pintaría «ORD-42» como texto suelto y la cabecera cambiaría de cara en
2943
+ * los 7 detalles. El aviso va en el contrato de entrada de los display —
2944
+ * uniforme para los 6 — y sólo lo aprovechan los que dibujan texto: los
2945
+ * que ya son chip (`badge`) o no son un valor escalar (`image`, `list`)
2946
+ * lo ignoran a propósito.
2947
+ */
2948
+ readonly chip: import("@angular/core").InputSignal<boolean>;
2949
+ private readonly dev;
2950
+ /** Mantiene `whenStable()` bloqueado mientras el descriptor carga su chunk. */
2951
+ private readonly pendientes;
2952
+ /** `true` cuando se dibuja un campo, `false` para cualquier otro `kind`. */
2953
+ protected readonly esCampo: import("@angular/core").Signal<boolean>;
2954
+ /** `true` en la rama de detalle (R4.1): sin árbol, etiqueta ni error. */
2955
+ protected readonly esDisplay: import("@angular/core").Signal<boolean>;
2956
+ /**
2957
+ * ¿El descriptor declara la superficie `display`? (R4.1)
2958
+ *
2959
+ * Es la respuesta a «cada descriptor de campo declara si sabe pintarse en
2960
+ * `display`»: sin esa declaración el host no pinta el valor en la ficha,
2961
+ * y `contract.spec.ts` vigila que los 20 la traigan.
2962
+ */
2963
+ private readonly declaraDisplay;
2964
+ /**
2965
+ * Componente que pinta este tipo en `display`.
2966
+ *
2967
+ * El descriptor sólo dice **si** sabe pintarse; **qué** componente usa lo
2968
+ * decide la tabla por tipo (`fields/display/display-defaults.ts`), que es
2969
+ * la que garantiza que el mismo `field.type` se pinte igual en todas las
2970
+ * fichas.
2971
+ */
2972
+ private readonly loaderDisplay;
2973
+ /** El modo display tiene componente para este tipo. */
2974
+ protected readonly soportaDisplay: import("@angular/core").Signal<boolean>;
2975
+ /**
2976
+ * Valor vacío, con el mismo criterio que `formatValue` de `detail-view`
2977
+ * antes de R4.2 (`null`, `undefined` o `''`): se pinta un `—` y el
2978
+ * componente ni se monta. `false`, `0` y `[]` **no** están vacíos.
2979
+ */
2980
+ protected readonly vacio: import("@angular/core").Signal<boolean>;
2981
+ /** Configuración leída como campo. Sólo válida en la rama de campo. */
2982
+ private readonly cfgCampo;
2983
+ private readonly node;
2984
+ protected readonly state: import("@angular/core").Signal<import("@angular/forms/signals").FieldState<unknown, string | number>>;
2985
+ /** Nombre del control: ancla del `<label for>` y base de `*-help`/`*-error`. */
2986
+ protected readonly name: import("@angular/core").Signal<string>;
2987
+ /**
2988
+ * Fuera del catálogo o oculto por `visibleWhen`: no se dibuja.
2989
+ *
2990
+ * El `isSupported` va primero y corta antes de leer `state()`: un tipo
2991
+ * desconocido se siembra como `undefined`, Signal Forms no le crea nodo y
2992
+ * `childTree` lanzaría «El formulario no contiene el campo».
2993
+ *
2994
+ * El `esCampo()` corta lo mismo para los widgets: no hay árbol que ocultar
2995
+ * ni nodo que consultar, así que el widget se dibuja si está en el
2996
+ * catálogo y ya.
2997
+ */
2998
+ protected readonly supported: import("@angular/core").Signal<boolean>;
2999
+ /**
3000
+ * Sin renderizador para este tipo **en la superficie actual**.
3001
+ *
3002
+ * En `display`: el descriptor no declara la superficie o el tipo no está
3003
+ * en la tabla de display. En `form`: sigue siendo «fuera del catálogo».
3004
+ * Ninguna de las dos ramas toca `state()`, que en display no existe.
3005
+ */
3006
+ private readonly unsupported;
3007
+ protected readonly showUnknown: import("@angular/core").Signal<boolean>;
3008
+ private readonly _trazaTipoDesconocido;
3009
+ /** Componente montado, o `null` mientras el `load()` perezoso resuelve. */
3010
+ private readonly control;
3011
+ /** Descarta cargas obsoletas cuando el tipo cambia antes de que resuelva. */
3012
+ private carga;
3013
+ private readonly _cargar;
3014
+ /**
3015
+ * Inputs del componente de display.
3016
+ *
3017
+ * Forma fija y uniforme para los 6 componentes de
3018
+ * `fields/display/`: `field` (el `FieldSchema` completo, para los que
3019
+ * necesitan `options`), `value` (el valor crudo) y `chip` (la
3020
+ * presentación de la cabecera). El descriptor no interviene acá — en
3021
+ * display no hay ni control ni árbol que cablear.
3022
+ */
3023
+ private readonly displayInputs;
3024
+ /**
3025
+ * Inputs del componente montado.
3026
+ *
3027
+ * El motor no sabe qué recibe cada tipo: pregunta al descriptor (`inputs`)
3028
+ * y sólo aplica la forma estándar del `kind` cuando el descriptor no la
3029
+ * sobreescribe — `{field, tree}` en campos, `{widget, data}` en widgets.
3030
+ * Ésa es la pieza que hace que «añadir un tipo» no toque este archivo
3031
+ * (R3.8). `multiselect` e `image` no pasan por aquí: se declaran a mano
3032
+ * por D27.
3033
+ */
3034
+ private readonly controlInputs;
3035
+ protected readonly group: import("@angular/core").Signal<GroupField>;
3036
+ protected readonly list: import("@angular/core").Signal<ListField>;
3037
+ private readonly groupTpl;
3038
+ private readonly childTpl;
3039
+ /** Subárbol y prefijo que heredan los hijos del grupo. */
3040
+ protected readonly groupChildCtx: import("@angular/core").Signal<RenderContext>;
3041
+ protected listChildCtx(child: ListChildContext): RenderContext;
3042
+ /** `sm:col-span-*` compartido con el grupo (literales: Tailwind v4 es JIT). */
3043
+ colSpan(width?: number): string;
3044
+ /**
3045
+ * Los tres `FormValueControl` se declaran en la plantilla (D27), así que
3046
+ * su campo necesita el tipo estrecho que exige cada input — el mismo
3047
+ * estrechado que hacía `FieldHost`.
3048
+ */
3049
+ protected readonly multiselectCfg: import("@angular/core").Signal<MultiselectField>;
3050
+ protected readonly imageCfg: import("@angular/core").Signal<ImageField>;
3051
+ protected readonly fileCfg: import("@angular/core").Signal<FileField>;
3052
+ private readonly multiselectNode;
3053
+ private readonly imageNode;
3054
+ private readonly fileNode;
3055
+ /**
3056
+ * Error visible: primero el del backend (si existe para esta ruta) y
3057
+ * después el propio, solo cuando el campo ya se tocó (validación al
3058
+ * perder el foco).
3059
+ */
3060
+ protected readonly errorMessage: import("@angular/core").Signal<string | null>;
3061
+ /**
3062
+ * Compuestos: varios controles internos, ninguno único. Para ellos
3063
+ * `<label for>` no tendría ancla, porque ningún elemento lleva el id del
3064
+ * campo padre (los hijos tienen los suyos propios).
3065
+ */
3066
+ protected readonly composite: import("@angular/core").Signal<boolean>;
3067
+ /**
3068
+ * Ancla de la ayuda, sólo mientras no hay error: §2 dice que el error
3069
+ * **reemplaza** la ayuda, de modo que el id no puede apuntar a un nodo
3070
+ * que ya no está en el DOM.
3071
+ */
3072
+ readonly helpId: import("@angular/core").Signal<string | null>;
3073
+ readonly errorId: import("@angular/core").Signal<string | null>;
3074
+ readonly describedBy: import("@angular/core").Signal<string | null>;
3075
+ /** Mismo criterio que `errorMessage()` — el estado del control no es público. */
3076
+ readonly invalid: import("@angular/core").Signal<boolean>;
3077
+ readonly required: import("@angular/core").Signal<boolean>;
3078
+ private readonly schema;
3079
+ /**
3080
+ * Se decide una vez por recurso y se aplica a todos los niveles: es un
3081
+ * juicio de diseño sobre el formulario, no sobre un campo suelto. Contado
3082
+ * por nivel (sólo top-level) — un conteo plano mezcla los subcampos de
3083
+ * `group`/`list` y da ratios imposibles.
3084
+ */
3085
+ protected readonly markOptionals: import("@angular/core").Signal<boolean>;
3086
+ protected readonly showRequiredMark: import("@angular/core").Signal<boolean | undefined>;
3087
+ protected readonly showOptionalMark: import("@angular/core").Signal<boolean>;
3088
+ get tree(): RootTree;
3089
+ get serverErrors(): Record<string, string>;
3090
+ get pathPrefix(): string;
3091
+ get exists(): boolean;
3092
+ get resource(): string;
3093
+ static ɵfac: i0.ɵɵFactoryDeclaration<RenderHost, never>;
3094
+ static ɵcmp: i0.ɵɵComponentDeclaration<RenderHost, "app-render-host", never, {
3095
+ "kind": {
3096
+ "alias": "kind";
3097
+ "required": false;
3098
+ "isSignal": true;
3099
+ };
3100
+ "type": {
3101
+ "alias": "type";
3102
+ "required": true;
3103
+ "isSignal": true;
3104
+ };
3105
+ "config": {
3106
+ "alias": "config";
3107
+ "required": true;
3108
+ "isSignal": true;
3109
+ };
3110
+ "ctx": {
3111
+ "alias": "ctx";
3112
+ "required": false;
3113
+ "isSignal": true;
3114
+ };
3115
+ "data": {
3116
+ "alias": "data";
3117
+ "required": false;
3118
+ "isSignal": true;
3119
+ };
3120
+ "mode": {
3121
+ "alias": "mode";
3122
+ "required": false;
3123
+ "isSignal": true;
3124
+ };
3125
+ "value": {
3126
+ "alias": "value";
3127
+ "required": false;
3128
+ "isSignal": true;
3129
+ };
3130
+ "chip": {
3131
+ "alias": "chip";
3132
+ "required": false;
3133
+ "isSignal": true;
3134
+ };
3135
+ }, {}, never, never, true, never>;
3136
+ }
3137
+ /**
3138
+ * Entrada estable de los formularios (base.md §5).
3139
+ *
3140
+ * **R3.5 · ya no despacha.** Este host redujo sus 493 líneas —el `@switch`
3141
+ * de 20 ramas, los 21 imports a mano y el envoltorio de label/errores— a un
3142
+ * adaptador que traduce su API histórica (`field` + `tree` + `pathPrefix` …)
3143
+ * al `RenderContext` que espera `<app-render-host>`, el motor único de
3144
+ * render (F1 · `refactormotor.md`).
3145
+ *
3146
+ * El rótulo, las marcas required/opcional, el error, la ayuda,
3147
+ * `FIELD_ARIA` y la **recursión de `group`/`list`** (los `ng-template` con
3148
+ * `ngTemplateOutlet` y `ListChildContext`) viven ahora en `RenderHost`, que
3149
+ * se auto-importa para instanciar sus propios hijos.
3150
+ *
3151
+ * Rutas del backend: `pathPrefix` encadena el prefijo del contenedor
3152
+ * (`address.`, `faq[2].`) y `serverErrors` lleva el mapa completo; el error
3153
+ * propio de este campo se resuelve como `serverErrors[prefix + key]`.
3154
+ */
3155
+ export declare class FieldHost {
3156
+ readonly field: import("@angular/core").InputSignal<FieldSchema>;
3157
+ readonly tree: import("@angular/core").InputSignal<RootTree>;
3158
+ readonly exists: import("@angular/core").InputSignal<boolean>;
3159
+ readonly serverErrors: import("@angular/core").InputSignal<Record<string, string>>;
3160
+ readonly pathPrefix: import("@angular/core").InputSignal<string>;
3161
+ readonly resource: import("@angular/core").InputSignal<string>;
3162
+ protected readonly ctx: import("@angular/core").Signal<RenderContext>;
3163
+ static ɵfac: i0.ɵɵFactoryDeclaration<FieldHost, never>;
3164
+ static ɵcmp: i0.ɵɵComponentDeclaration<FieldHost, "app-field-host", never, {
3165
+ "field": {
3166
+ "alias": "field";
3167
+ "required": true;
3168
+ "isSignal": true;
3169
+ };
3170
+ "tree": {
3171
+ "alias": "tree";
3172
+ "required": true;
3173
+ "isSignal": true;
3174
+ };
3175
+ "exists": {
3176
+ "alias": "exists";
3177
+ "required": false;
3178
+ "isSignal": true;
3179
+ };
3180
+ "serverErrors": {
3181
+ "alias": "serverErrors";
3182
+ "required": false;
3183
+ "isSignal": true;
3184
+ };
3185
+ "pathPrefix": {
3186
+ "alias": "pathPrefix";
3187
+ "required": false;
3188
+ "isSignal": true;
3189
+ };
3190
+ "resource": {
3191
+ "alias": "resource";
3192
+ "required": false;
3193
+ "isSignal": true;
3194
+ };
3195
+ }, {}, never, never, true, never>;
3196
+ }
3197
+ /**
3198
+ * Opciones del diálogo. Es estructuralmente compatible con
3199
+ * `core.NotificationService['confirmation']` (mismos campos) — `shared` no
3200
+ * importa `core`, quien une ambos lados es el shell (Fase 7).
3201
+ */
3202
+ interface ConfirmDialogOptions {
3203
+ title: string;
3204
+ message: string;
3205
+ confirmLabel?: string;
3206
+ cancelLabel?: string;
3207
+ }
3208
+ export declare class ConfirmDialog {
3209
+ /** `null` = oculto (viene de `NotificationService.confirmation`). */
3210
+ readonly options: import("@angular/core").InputSignal<ConfirmDialogOptions | null>;
3211
+ readonly danger: import("@angular/core").InputSignal<boolean>;
3212
+ readonly confirmed: import("@angular/core").OutputEmitterRef<void>;
3213
+ readonly cancelled: import("@angular/core").OutputEmitterRef<void>;
3214
+ static ɵfac: i0.ɵɵFactoryDeclaration<ConfirmDialog, never>;
3215
+ static ɵcmp: i0.ɵɵComponentDeclaration<ConfirmDialog, "app-confirm-dialog", never, {
3216
+ "options": {
3217
+ "alias": "options";
3218
+ "required": false;
3219
+ "isSignal": true;
3220
+ };
3221
+ "danger": {
3222
+ "alias": "danger";
3223
+ "required": false;
3224
+ "isSignal": true;
3225
+ };
3226
+ }, {
3227
+ "confirmed": "confirmed";
3228
+ "cancelled": "cancelled";
3229
+ }, never, never, true, never>;
3230
+ }
3231
+ type ToastKind = 'success' | 'error' | 'info';
3232
+ /** Estructuralmente idéntico a `core`'s `Toast` — no importa `core`. */
3233
+ interface ToastItem {
3234
+ id: number;
3235
+ kind: ToastKind;
3236
+ message: string;
3237
+ }
3238
+ /**
3239
+ * Contenedor visual de toasts: fijo arriba a la derecha, región `status`
3240
+ * viva (polite) para que se anuncien los mensajes. Recibe los toasts por
3241
+ * `input()` desde quien sí tiene `NotificationService` (Fase 7).
3242
+ */
3243
+ export declare class Toast {
3244
+ readonly toasts: import("@angular/core").InputSignal<ToastItem[]>;
3245
+ readonly closeLabel: import("@angular/core").InputSignal<string>;
3246
+ readonly dismissed: import("@angular/core").OutputEmitterRef<number>;
3247
+ static ɵfac: i0.ɵɵFactoryDeclaration<Toast, never>;
3248
+ static ɵcmp: i0.ɵɵComponentDeclaration<Toast, "app-toast", never, {
3249
+ "toasts": {
3250
+ "alias": "toasts";
3251
+ "required": false;
3252
+ "isSignal": true;
3253
+ };
3254
+ "closeLabel": {
3255
+ "alias": "closeLabel";
3256
+ "required": false;
3257
+ "isSignal": true;
3258
+ };
3259
+ }, {
3260
+ "dismissed": "dismissed";
3261
+ }, never, never, true, never>;
3262
+ }
3263
+ /**
3264
+ * Tripwire de compilación (R1.7 · D17): que la unión `FieldType` y los
3265
+ * tipos que la UI sabe dibujar sigan diciendo lo mismo.
3266
+ *
3267
+ * · un valor que no esté en la unión `FieldSchema` ⇒ `satisfies` falla;
3268
+ * · un tipo de la unión que falte acá ⇒ `FaltaEnLaUi` no es `never`.
3269
+ *
3270
+ * **No es una fuente de verdad en runtime.** El catálogo vive en
3271
+ * `core/registry/render-registry.ts`, alimentado por los descriptores de
3272
+ * abajo. Esta lista sólo existe para que `npm run build` y `npm test` vean
3273
+ * la aserción por igual, sin depender de que un test la ejecute.
3274
+ *
3275
+ * **Exportada en R3.7** para que `core/registry/contract.spec.ts` pueda
3276
+ * verificar además el sentido que este archivo no cubre — que cada nombre de
3277
+ * la lista tenga **descriptor**: aquí sólo se comprueba que está en la unión,
3278
+ * y un tipo declarado en la lista pero ausente del manifiesto se vería en el
3279
+ * primer schema que lo use.
3280
+ *
3281
+ * **Mudado acá en R3.8.** Vivía en `field-host/field-host.ts`, y §0 es
3282
+ * explícito: «si añadir un tipo obliga a editar `FieldHost`, el motor no
3283
+ * está terminado». Estaba sin uso en runtime — sólo servía a este tripwire —
3284
+ * así que trasladarlo acá no cambia comportamiento y deja el registro y su
3285
+ * aserción en el **mismo archivo**: añadir un tipo toca una carpeta y una
3286
+ * línea de este manifiesto, y el host no se entera.
3287
+ */
3288
+ export declare const SUPPORTED_FIELD_TYPE_LIST: readonly ["text", "textarea", "slug", "number", "currency", "boolean", "select", "multiselect", "url", "email", "phone", "date", "time", "readonly-text", "group", "list", "string-list", "key-value", "relation", "image", "file", "color", "rich-select", "dropdown"];
3289
+ /**
3290
+ * Manifiesto de descriptores de campo (R3.3 · registro único de render).
3291
+ *
3292
+ * Una línea por tipo: el registro los recoge al importar este módulo. Es la
3293
+ * **única** lista manual del catálogo de campos — `import.meta.glob` no
3294
+ * sobrevive a `ng build`, así que D9-1 se resuelve con «registro manual de
3295
+ * una línea». Ver `core/registry/render-registry.ts`.
3296
+ *
3297
+ * Añadir un tipo de campo ⇒ crear la carpeta + su `.descriptor.ts` + una
3298
+ * línea acá. **Ningún host se toca** (R3.8).
3299
+ */
3300
+ export declare const fieldDescriptors: readonly RenderDescriptor[];
3301
+ /** Los 7 formatos del `@switch` que este registro reemplaza (R4.3). */
3302
+ export declare const columnFormatDescriptors: readonly RenderDescriptor[];
3303
+ /**
3304
+ * Los 7 nombres, como dato.
3305
+ *
3306
+ * Va en paralelo a `ListColumnFormat` (unión de `schema.model`) y lo vigila
3307
+ * `core/registry/contract.spec.ts`: la unión vive en tipos y el registro en
3308
+ * runtime, y sólo un test reconcilia los dos — el mismo tripwire que ya
3309
+ * existe para los 20 campos y los 6 widgets.
3310
+ */
3311
+ export declare const COLUMN_FORMAT_TYPES: readonly ["text", "badge", "thumbnail", "boolean", "number", "currency", "date"];
3312
+ /**
3313
+ * Descripciones centralizadas del catálogo de campos del motor `@tinya-code/gas-admin-ui`.
3314
+ *
3315
+ * Este archivo es el único lugar donde se editan los textos descriptivos de
3316
+ * cada propiedad del schema. Los JSDoc de `schema.model.ts` usan estos mismos
3317
+ * textos para que el editor (VSCode) muestre tooltips al pasar el cursor
3318
+ * sobre una propiedad del schema del consumidor.
3319
+ *
3320
+ * Estructura: clave de propiedad → descripción en español.
3321
+ *
3322
+ * Uso en el consumidor (loyalty-admin, etc.):
3323
+ * import { schemaDescriptions } from '@tinya-code/gas-admin-ui';
3324
+ * schemaDescriptions['docLink']; // 'URL de documentación externa para este campo.'
3325
+ *
3326
+ * Nota: este objeto no se renderiza en la UI; solo existe para documentación
3327
+ * y tooltips del editor. Para renderizar tooltips en el formulario usa
3328
+ * `description` en cada campo del schema del consumidor.
3329
+ */
3330
+ export declare const schemaDescriptions: {
3331
+ readonly key: "Nombre de la propiedad en el JSON que envía/recibe el back.";
3332
+ readonly label: "Rótulo visible para el usuario en el formulario y la ficha.";
3333
+ readonly help: "Texto corto de ayuda que se muestra debajo del campo.";
3334
+ readonly placeholder: "Texto sugerente que se muestra dentro del input cuando está vacío.";
3335
+ readonly required: "Si es obligatorio. El back también lo valida (R7.4).";
3336
+ readonly default: "Valor por defecto si el registro no trae este campo (ej. `default: \"none\"` en un `select`/`dropdown`).";
3337
+ readonly readonly: "Si el campo es solo lectura (no editable en el formulario).";
3338
+ readonly readonlyOn: "Bloquea el campo solo en creación, solo en edición o siempre.";
3339
+ readonly readonlyWhen: "Condición bajo la cual el campo es solo lectura (expresión sobre otros campos).";
3340
+ readonly visibleWhen: "Condición bajo la cual el campo se muestra u oculta.";
3341
+ readonly hiddenOn: "Oculta el campo en creación, en edición o siempre.";
3342
+ readonly dependsOn: "Selección dependiente (guía §1: país → ciudad). Clave del campo padre del mismo nivel.";
3343
+ readonly validators: "Reglas de validación (mínimo, máximo, patrón, unicidad, etc.).";
3344
+ readonly width: "Ancho en la grilla del formulario (1–12), igual que un widget del dashboard.";
3345
+ readonly section: "Id de la sección/pestaña del layout a la que pertenece (base.md §4).";
3346
+ readonly read: "Código de nivel de acceso requerido para leer el campo.";
3347
+ readonly write: "Código de nivel de acceso requerido para escribir el campo.";
3348
+ readonly description: "Descripción corta de para qué sirve este campo. Se muestra como tooltip al pasar el cursor sobre el rótulo.";
3349
+ readonly docLink: "URL de documentación externa para este campo (se abre al hacer clic en el tooltip).";
3350
+ readonly value: "Valor que se envía al back cuando se elige esta opción.";
3351
+ readonly labelOption: "Texto visible de la opción.";
3352
+ readonly disabled: "Si la opción está deshabilitada (no se puede elegir).";
3353
+ readonly type: "Tipo de campo: `text`, `color`, `number`, `currency`, `boolean`, `select`, `dropdown`, `rich-select`, `multiselect`, `url`, `email`, `phone`, `date`, `time`, `readonly-text`, `slug`, `textarea`, `relation`, `image`, `file`, `group`, `list`, `string-list`, `key-value`.";
3354
+ readonly maxLength: "Longitud máxima de caracteres permitida.";
3355
+ readonly pattern: "Expresión regular que debe cumplir el valor.";
3356
+ readonly rows: "Número de filas visibles del textarea.";
3357
+ readonly minWords: "Cantidad mínima de palabras.";
3358
+ readonly maxWords: "Cantidad máxima de palabras.";
3359
+ readonly tokens: "Marcadores insertables como chips bajo el área (plantillas de mensaje).";
3360
+ readonly from: "Campo del registro del que se autogenera el slug.";
3361
+ readonly lockAfterCreate: "Bloquea el campo una vez creado el registro (por defecto: sí).";
3362
+ readonly min: "Valor mínimo permitido.";
3363
+ readonly max: "Valor máximo permitido.";
3364
+ readonly step: "Incremento entre valores permitidos.";
3365
+ readonly decimals: "Número de decimales mostrados.";
3366
+ readonly currencyFrom: "Campo del registro del que se toma la moneda (ej. `currency`).";
3367
+ readonly trueLabel: "Texto que se muestra cuando el valor es verdadero.";
3368
+ readonly falseLabel: "Texto que se muestra cuando el valor es falso.";
3369
+ readonly source: "De dónde salen las opciones: `{ resource, view?, params?, … }` (F8 · R6.5).";
3370
+ readonly resource: "Recurso declarado (`resource.id`): su listado es el de las opciones.";
3371
+ readonly view: "Vista declarada en `resource.views` (opcional): filtra en el back.";
3372
+ readonly params: "Valores interpolados para la vista (`{campo}` ← contexto).";
3373
+ readonly labelField: "Etiqueta por defecto de las filas (si no, `titleField` del recurso).";
3374
+ readonly valueField: "Valor por defecto de las filas (si no, `keyField` del recurso).";
3375
+ readonly secondaryField: "Segundo dato a mostrar junto a la etiqueta (ej. \"Juan Pérez · DNI 123\").";
3376
+ readonly onlyActive: "Cargar solo registros activos (lo exige además la FK en el back).";
3377
+ readonly accept: "Tipos aceptados (ej. `image/*`, `image/png,image/jpeg` o `pdf,docx`).";
3378
+ readonly maxSizeMB: "Tamaño máximo del archivo en megabytes.";
3379
+ readonly aspectRatio: "Relación de aspecto de la vista previa (ej. `16/9`).";
3380
+ readonly previewSize: "Tamaño de la vista previa.";
3381
+ readonly formatCurrency: "Campo del registro del que se toma la moneda (ej. `currency`).";
3382
+ readonly display: "Presentación del grupo: `card`, `collapsible` o `inline`.";
3383
+ readonly fields: "Campos de cada item (cualquier tipo, incluso `group` o `list`).";
3384
+ readonly minItems: "Cantidad mínima de items.";
3385
+ readonly maxItems: "Cantidad máxima de items.";
3386
+ readonly sortable: "Si se puede reordenar (por defecto: sí).";
3387
+ readonly positionField: "Campo donde se guarda el orden dentro de cada item (`position`).";
3388
+ readonly addLabel: "Etiqueta del botón para agregar un item nuevo.";
3389
+ readonly emptyText: "Texto que se muestra cuando no hay items.";
3390
+ readonly itemDisplay: "Cómo se muestra cada item: `card`, `row` o `accordion`.";
3391
+ readonly itemFields: "Campos de cada item (cualquier tipo, incluso `group` o `list`).";
3392
+ readonly itemTitle: "Campo del item usado como título cuando está colapsado.";
3393
+ readonly primaryFirst: "El primer item tiene significado especial (ej. imagen principal).";
3394
+ readonly itemType: "Tipo permitido de cada item (base.md §9: solo `text`, `url` o `email`).";
3395
+ readonly sort: "Si la lista se ordena alfabéticamente o por posición.";
3396
+ readonly filter: "Si el listado permite filtrado por texto.";
3397
+ readonly search: "Campos buscables en el listado.";
3398
+ readonly filters: "Filtros declarados para el listado.";
3399
+ readonly listColumns: "Columnas que se muestran en el listado.";
3400
+ readonly searchable: "Si el campo es buscable.";
3401
+ readonly filterable: "Si el campo es filtrable.";
3402
+ readonly sortableItems: "Si los items de la lista se pueden reordenar.";
3403
+ readonly groupBy: "Campo por el que agrupar los items.";
3404
+ readonly cardWidth: "Ancho de cada tarjeta de opción en la grilla (1–12). Por defecto `4` (3 tarjetas por fila en escritorio).";
3405
+ readonly cardCols: "Cuántas tarjetas mostrar por fila en el punto de ruptura `lg`. Sobrescribe el cálculo automático basado en `cardWidth`.";
3406
+ readonly hintMetric: "Texto secundario bajo la cifra (estático, va en el schema).";
3407
+ readonly unitChart: "Sufijo de las cifras del eje («unidades», «S/»).";
3408
+ readonly segments: "Segmentos de la barra de distribución porcentual.";
3409
+ readonly actions: "Acciones de navegación por fila.";
3410
+ readonly labelAction: "Etiqueta de la acción.";
3411
+ readonly icon: "Icono de la acción.";
3412
+ readonly navigateTo: "Ruta de destino de la acción.";
3413
+ readonly variant: "Variante visual: `primary`, `secondary` u `outline`.";
3414
+ readonly widgets: "Widgets del panel. Se declara en `ResourceSchema.widgets` — no en `fields`, porque un KPI no es un campo.";
3415
+ readonly mobileWidth: "Ancho en la grilla en mobile (1–12). Por defecto `12` (ancho completo, apilado).";
3416
+ readonly sourceWidget: "De dónde saca sus datos: `DataSource` o `{ kind: \"payload\" }`.";
3417
+ readonly hintWidget: "Texto secundario bajo la cifra (estático, va en el schema).";
3418
+ readonly unitWidget: "Sufijo de las cifras del eje.";
3419
+ };
3420
+ export type SchemaDescriptionKey = keyof typeof schemaDescriptions;
3421
+ export declare const layoutDescriptors: readonly LayoutDescriptor[];
3422
+ export declare const viewDescriptors: readonly RenderDescriptor[];
3423
+ /**
3424
+ * Validador de composición de vistas (R9.9.2 · D2/D3).
3425
+ *
3426
+ * Una composición (un `uiView` contenedor con `config.children`, p. ej.
3427
+ * `tabs`) se valida ANTES del render con esta única función pura y síncrona:
3428
+ *
3429
+ * - identidad de hijos (`id` presente y único entre hermanos);
3430
+ * - tipos resueltos con `get('view', type)` — **nunca** `resolve()`, cuyo
3431
+ * `fallback` silencioso dejaría un hijo inválido pintándose de todos modos;
3432
+ * - `ref` rotos y ciclos (directos o transitivos) que los `ref` permiten;
3433
+ * - profundidad y cantidad de nodos con topes configurables;
3434
+ * - destinos de navegación (`resource` + `view` + params) y fuentes de
3435
+ * filas — una URL cruda nunca es un destino válido;
3436
+ *
3437
+ * El llamante es D3: `resourceRoutes()` en `shell.routes.ts` (fail-loud al
3438
+ * arrancar) y el guardián de montaje del composer. Cualquier violación lanza
3439
+ * `SchemaCompositionError` con un mensaje legible que nombra recurso,
3440
+ * contenedor e hijo: nada se oculta ni se sustituye en silencio.
3441
+ *
3442
+ * El tipo de una `uiView` RAÍZ no se chequea acá: eso ya lo hace
3443
+ * `resourceRoutes()` con su error genérico de descriptor inexistente. Este
3444
+ * validador cubre lo que ese recorrido no ve: la estructura de los hijos.
3445
+ */
3446
+ /** Profundidad máxima de anidamiento de contenedores (hijos raíz = 1). */
3447
+ export declare const MAX_DEPTH = 3;
3448
+ /** Cantidad máxima de nodos-hijo declarados en la composición de un schema. */
3449
+ declare const MAX_NODES = 50;
3450
+ /** Violación del contrato de composición, detectada antes del render. */
3451
+ export declare class SchemaCompositionError extends Error {
3452
+ constructor(message: string);
3453
+ }
3454
+ /**
3455
+ * Valida TODA la composición de un schema. Lanza `SchemaCompositionError`
3456
+ * en la primera violación; no lanzar significa «apto para renderizar».
3457
+ */
3458
+ export declare function validateComposition(schema: ResourceSchema): void;
3459
+ /** Ruta de un campo en el árbol de Signal Forms. Los casts viven acá. */
3460
+ type FieldPath<V> = SchemaPath<V, SchemaPathRules.Supported, PathKind.Child>;
3461
+ /**
3462
+ * Vocabulario compartido con el back (`VALIDATORS_` en 23-validate.js).
3463
+ *
3464
+ * El orden es el de registro de `FIELD_VALIDATORS`, que fija el orden de los
3465
+ * errores en `field.errors()`: el primero es el que pinta el campo.
3466
+ */
3467
+ type ValidatorName = 'required' | 'email' | 'url' | 'min-length' | 'max-length' | 'pattern' | 'min-words' | 'max-words' | 'min' | 'max' | 'min-items' | 'max-items' | 'date-min' | 'date-max' | 'unique';
3468
+ /** Todo lo que un validador necesita para decidir y para registrarse. */
3469
+ interface ValidatorContext {
3470
+ path: FieldPath<unknown>;
3471
+ field: FieldSchema;
3472
+ schema: ResourceSchema;
3473
+ api: ApiService;
3474
+ excludeKey: string | undefined;
3475
+ }
3476
+ interface ValidatorEntry {
3477
+ readonly name: ValidatorName;
3478
+ /** ¿Este campo declara este validador? (tipo + props del schema) */
3479
+ readonly declares: (ctx: ValidatorContext) => boolean;
3480
+ /** Ata la regla de Signal Forms a `ctx.path`. */
3481
+ readonly register: (ctx: ValidatorContext) => void;
3482
+ }
3483
+ /** Formato de URL para una ruta cualquiera (`string-list`, ítems de list). */
3484
+ declare function registerUrlRule(path: FieldPath<unknown>): void;
3485
+ /** Formato de correo para una ruta cualquiera (`string-list`, ítems de list). */
3486
+ declare function registerEmailRule(path: FieldPath<unknown>): void;
3487
+ declare const FIELD_VALIDATORS: readonly ValidatorEntry[];
3488
+ /** Nombres del registro: lo que `contract-check.mjs` compara con el back. */
3489
+ declare const VALIDATOR_NAMES: readonly ValidatorName[];
3490
+ /**
3491
+ * Resuelve una plantilla de texto reemplazando marcadores `{key}` o `{nested.key}`
3492
+ * con los valores presentes en el objeto `data` o en el contexto adicional `extraContext`.
3493
+ *
3494
+ * Si un valor es `null` o `undefined`, se reemplaza por una cadena vacía.
3495
+ * No arroja excepciones ante claves no encontradas ni ante objetos nulos.
3496
+ */
3497
+ export declare function interpolateTemplate(template: string, data?: Record<string, unknown>, extraContext?: Record<string, unknown>): string;
3498
+ interface ParsedRoute {
3499
+ path: string;
3500
+ queryParams: Record<string, string>;
3501
+ }
3502
+ /**
3503
+ * Descompone una ruta relativa o absoluta en path y queryParams para routerLink.
3504
+ */
3505
+ declare function parseNavigationUrl(url: string): ParsedRoute;
3506
+ /**
3507
+ * Descriptores de las tres acciones que hoy declara el modelo
3508
+ * (R3.10 · `doc/plan-refactor-motor.md` §3.2).
3509
+ *
3510
+ * Éste es el fichero que **cierra la triplicación de `copy-share`**: la
3511
+ * resolución de enlace y de mensaje vivía en `list-view.runAction`,
3512
+ * `detail-view.resolveUrl/resolveShareText` y
3513
+ * `post-create-modal.resolvedShareUrl/resolvedShareText`. Aquí se resuelve
3514
+ * una sola vez (`resolveShare`) y las vistas consumen ese resultado en R3.11.
3515
+ *
3516
+ * Se registra al importar, desde `action-registry.ts`, con el mismo criterio
3517
+ * que el registro de render: sin esperar a DI, porque el registro es la única
3518
+ * fuente de verdad en runtime.
3519
+ */
3520
+ /** Enlace y mensaje ya interpolados, listos para copiar o compartir. */
3521
+ interface ResolvedShare {
3522
+ url: string;
3523
+ shareText: string;
3524
+ }
3525
+ /**
3526
+ * `true` cuando la acción es de las que las vistas presentan como botón de
3527
+ * compartir (`detail-view`, `post-create-modal`).
3528
+ *
3529
+ * Vive acá y no en la vista porque es **el único sitio donde se puede nombrar
3530
+ * `'copy-share'`**: fuera de `descriptors.ts` ninguna vista decide por tipo
3531
+ * (criterio de R3.11).
3532
+ */
3533
+ export declare function isShareAction(action: ResourceAction): action is CopyShareAction;
3534
+ /**
3535
+ * `true` cuando la acción es un `trigger` (acción de dominio del back).
3536
+ *
3537
+ * Mismo criterio que `isShareAction`: el único sitio que nombra el tipo,
3538
+ * para que las vistas no decidan por string literal (R3.11). `detail-view`
3539
+ * lo usa en R8.8 para renderizar «Regenerar PIN» junto a los botones de
3540
+ * compartir.
3541
+ */
3542
+ export declare function isTriggerAction(action: ResourceAction): action is TriggerAction;
3543
+ /**
3544
+ * Resuelve `urlTemplate` y el mensaje de compartir contra `vars`
3545
+ * (config global + registro).
3546
+ *
3547
+ * **Única implementación** de esta lógica en el admin. El fallback a
3548
+ * `share_message_template` (la plantilla que declara el singleton de
3549
+ * configuración) lo aplican los tres puntos por igual: hasta ahora la fila
3550
+ * del listado no lo honraba y se quedaba sin saludo.
3551
+ */
3552
+ export declare function resolveShare(action: CopyShareAction, vars: Record<string, unknown>): ResolvedShare;
3553
+ /** Catálogo — una línea por acción, en el orden en que se registran. */
3554
+ declare const actionDescriptors: readonly ActionDescriptor[];
3555
+ /**
3556
+ * Registro único de acciones (R3.9 · `refactormotor.md`).
3557
+ *
3558
+ * Es el gemelo del registro de render (`core/registry/render-registry.ts`):
3559
+ * en lugar de «qué componente pinta un tipo de campo», responde «qué
3560
+ * comportamiento ejecuta un tipo de acción» y permite **migrar el dispatch
3561
+ * if/copiado-pegado de las vistas (R3.11) a datos + un solo `run`**.
3562
+ *
3563
+ * Como los descriptores son objetos planos de módulo (no los inyecta Angular),
3564
+ * todo lo que necesiten llegar por el `ActionContext`: los puertos (`api`,
3565
+ * `router`, `toast`) y los datos (`record`, `resource`, `vars`).
3566
+ */
3567
+ /** Superficies donde una acción puede aparecer. */
3568
+ export type ActionSurface = 'row' | 'detail' | 'header' | 'bulk' | 'post-create';
3569
+ /**
3570
+ * Lo que aporta el llamador al ejecutar una acción: el registro sobre el que
3571
+ * corre y los puertos que el descriptor usará para actuar.
3572
+ *
3573
+ * `record` es `null` en las superficies sin fila (`header`, `bulk`), para que
3574
+ * un descriptor pueda distinguir «sin registro» de «registro vacío».
3575
+ */
3576
+ export interface ActionRequest {
3577
+ record: Record<string, unknown> | null;
3578
+ /** Id del recurso (schema) dueño de la acción. */
3579
+ resource: string;
3580
+ api: ApiService;
3581
+ router: Router;
3582
+ toast: NotificationService;
3583
+ user: SessionUser | null;
3584
+ /**
3585
+ * `keyField` del schema. **R3.12 (desviación nº 3 del contrato):** con
3586
+ * `record` forma la clave que necesita la ruta lógica de `trigger`
3587
+ * (`/admin/<recurso>/<key>/actions/<name>`). Lo aporta la vista, que es
3588
+ * quien tiene el schema.
3589
+ */
3590
+ keyField: string;
3591
+ }
3592
+ /**
3593
+ * Contexto que recibe el descriptor: la petición del llamador más el contexto
3594
+ * de interpolación ya resuelto. `ActionService.run` lo arma, así que ninguna
3595
+ * vista repite `buildInterpolationContext(record)`.
3596
+ */
3597
+ export interface ActionContext extends ActionRequest {
3598
+ /** `{...configGlobal, ...record, public_base_url}` — listo para las plantillas. */
3599
+ vars: Record<string, unknown>;
3600
+ }
3601
+ /** Resultado que la vista puede leer para saber si debe recargar. */
3602
+ export interface ActionResult {
3603
+ /** `false` cuando la acción no llegó a ejecutarse; el error ya se notificó. */
3604
+ ok: boolean;
3605
+ /** `true` cuando tocó datos y la vista debe recargar su listado/detalle. */
3606
+ reload?: boolean;
3607
+ }
3608
+ export interface ActionDescriptor {
3609
+ /** Clave del dispatch: el `type` del schema (`'navigate'` no lo declara). */
3610
+ type: string;
3611
+ /** Superficies donde esta acción puede aparecer. */
3612
+ surfaces: ActionSurface[];
3613
+ run: (ctx: ActionContext, action: ResourceAction) => Promise<ActionResult>;
3614
+ /** `true` si el descriptor exige confirmación antes de ejecutar. */
3615
+ confirm?: boolean;
3616
+ /**
3617
+ * Icono lucide que representa la acción donde el espacio es reducido
3618
+ * (la fila del listado). Sin él, la vista muestra `action.label`.
3619
+ */
3620
+ icon?: LucideIcon;
3621
+ }
3622
+ /** Registra (o reemplaza) un descriptor de acción. */
3623
+ export declare function registerAction(descriptor: ActionDescriptor): void;
3624
+ export declare function getAction(type: string): ActionDescriptor | undefined;
3625
+ /** Descriptores disponibles en una superficie. */
3626
+ export declare function actionsFor(surface: ActionSurface): ActionDescriptor[];
3627
+ /**
3628
+ * Clave de dispatch de una acción. `NavigateAction.type` es **opcional**
3629
+ * (schema.model.ts), así que una acción sin `type` es una navegación.
3630
+ */
3631
+ export declare function actionType(action: ResourceAction): string;
3632
+ /** Tipos registrados. Sólo para comprobaciones y tests. */
3633
+ export declare function actionTypes(): string[];
3634
+ export { type ComponentRenderKind, type FirebaseAuthAdapter, type GasAdminConfig, type RenderKind, type ResolvedShare, type ValidatorName, type ValueSources, get as getRenderDescriptor, isSupported as isRenderSupported, register as registerRenderDescriptor, unregister as unregisterRenderDescriptor };