@tinya-code/gas-admin-ui 0.2.0-alpha.1 → 0.2.0-alpha.11

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 (48) hide show
  1. package/README.md +300 -2
  2. package/fesm2022/{tinya-code-gas-admin-ui-calendar-view-CRTEx4f5.mjs → tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs} +12 -7
  3. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +1 -0
  4. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +107 -0
  5. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +1 -0
  6. package/fesm2022/{tinya-code-gas-admin-ui-condition-evaluator-DL15SFPt.mjs → tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs} +2 -2
  7. package/fesm2022/{tinya-code-gas-admin-ui-condition-evaluator-DL15SFPt.mjs.map → tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map} +1 -1
  8. package/fesm2022/{tinya-code-gas-admin-ui-data-source.service-BtZqPzmP.mjs → tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs} +2 -2
  9. package/fesm2022/{tinya-code-gas-admin-ui-data-source.service-BtZqPzmP.mjs.map → tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map} +1 -1
  10. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +122 -0
  11. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +1 -0
  12. package/fesm2022/{tinya-code-gas-admin-ui-error-mapper.service-pEOAeAzK.mjs → tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs} +2 -2
  13. package/fesm2022/{tinya-code-gas-admin-ui-error-mapper.service-pEOAeAzK.mjs.map → tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map} +1 -1
  14. package/fesm2022/{tinya-code-gas-admin-ui-file-B6_vveO5.mjs → tinya-code-gas-admin-ui-file-Dmxioog7.mjs} +3 -3
  15. package/fesm2022/{tinya-code-gas-admin-ui-file-B6_vveO5.mjs.map → tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map} +1 -1
  16. package/fesm2022/{tinya-code-gas-admin-ui-form-view-D1QLZWnG.mjs → tinya-code-gas-admin-ui-form-view-C709rc57.mjs} +13 -10
  17. package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs.map +1 -0
  18. package/fesm2022/{tinya-code-gas-admin-ui-image-lUe8nrUf.mjs → tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs} +3 -3
  19. package/fesm2022/{tinya-code-gas-admin-ui-image-lUe8nrUf.mjs.map → tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map} +1 -1
  20. package/fesm2022/{tinya-code-gas-admin-ui-kanban-view-B1rUNjlp.mjs → tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs} +4 -4
  21. package/fesm2022/{tinya-code-gas-admin-ui-kanban-view-B1rUNjlp.mjs.map → tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map} +1 -1
  22. package/fesm2022/{tinya-code-gas-admin-ui-list-CH7b7dKh.mjs → tinya-code-gas-admin-ui-list-C8ijJHAx.mjs} +2 -2
  23. package/fesm2022/{tinya-code-gas-admin-ui-list-CH7b7dKh.mjs.map → tinya-code-gas-admin-ui-list-C8ijJHAx.mjs.map} +1 -1
  24. package/fesm2022/{tinya-code-gas-admin-ui-multiselect-Bg4cnf6N.mjs → tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs} +3 -3
  25. package/fesm2022/{tinya-code-gas-admin-ui-multiselect-Bg4cnf6N.mjs.map → tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map} +1 -1
  26. package/fesm2022/{tinya-code-gas-admin-ui-relation-CDEuUpKU.mjs → tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs} +3 -3
  27. package/fesm2022/{tinya-code-gas-admin-ui-relation-CDEuUpKU.mjs.map → tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map} +1 -1
  28. package/fesm2022/{tinya-code-gas-admin-ui-render-registry-mG0BDGZI.mjs → tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs} +59 -29
  29. package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs.map +1 -0
  30. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +161 -0
  31. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +1 -0
  32. package/fesm2022/{tinya-code-gas-admin-ui-schema-catalog-Bb0WJRwc.mjs → tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs} +249 -58
  33. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +1 -0
  34. package/fesm2022/{tinya-code-gas-admin-ui-seed-C_tWI-Jr.mjs → tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs} +2 -2
  35. package/fesm2022/{tinya-code-gas-admin-ui-seed-C_tWI-Jr.mjs.map → tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs.map} +1 -1
  36. package/fesm2022/{tinya-code-gas-admin-ui-select-CL2kj7pM.mjs → tinya-code-gas-admin-ui-select-CDwX9yNO.mjs} +57 -53
  37. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +1 -0
  38. package/fesm2022/{tinya-code-gas-admin-ui-upload.service-D700Obgh.mjs → tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs} +2 -2
  39. package/fesm2022/{tinya-code-gas-admin-ui-upload.service-D700Obgh.mjs.map → tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map} +1 -1
  40. package/fesm2022/tinya-code-gas-admin-ui.mjs +98 -46
  41. package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -1
  42. package/package.json +1 -1
  43. package/types/tinya-code-gas-admin-ui.d.ts +385 -51
  44. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-CRTEx4f5.mjs.map +0 -1
  45. package/fesm2022/tinya-code-gas-admin-ui-form-view-D1QLZWnG.mjs.map +0 -1
  46. package/fesm2022/tinya-code-gas-admin-ui-render-registry-mG0BDGZI.mjs.map +0 -1
  47. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-Bb0WJRwc.mjs.map +0 -1
  48. package/fesm2022/tinya-code-gas-admin-ui-select-CL2kj7pM.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinya-code/gas-admin-ui",
3
- "version": "0.2.0-alpha.1",
3
+ "version": "0.2.0-alpha.11",
4
4
  "description": "Motor de render Angular para admin CRUD: declará ResourceSchema y el motor dibuja vistas, campos y widgets.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -2,7 +2,7 @@ import * as i0 from "@angular/core";
2
2
  import { EnvironmentProviders, InjectionToken, Provider, Signal, TemplateRef, Type } from "@angular/core";
3
3
  import { CanActivateFn, Route, Router, Routes } from "@angular/router";
4
4
  import { Observable } from "rxjs";
5
- import { HttpEvent, HttpInterceptorFn } from "@angular/common/http";
5
+ import { HttpClient, HttpEvent, HttpHeaders, HttpInterceptorFn, HttpParams } from "@angular/common/http";
6
6
  import { CdkDragDrop } from "@angular/cdk/drag-drop";
7
7
  import { LucideIcon } from "@lucide/angular";
8
8
  import { FieldTree, PathKind, SchemaFn, SchemaPath, SchemaPathRules } from "@angular/forms/signals";
@@ -44,17 +44,25 @@ export interface FieldValidators {
44
44
  }
45
45
  /** Propiedades comunes a todo campo (base.md §4). */
46
46
  export interface FieldBase<T> {
47
- /** Nombre de la propiedad en el JSON. */
47
+ /** Nombre de la propiedad en el JSON que envía/recibe el back. */
48
48
  key: string;
49
+ /** Rótulo visible para el usuario en el formulario y la ficha. */
49
50
  label: string;
51
+ /** Texto corto de ayuda que se muestra debajo del campo. */
50
52
  help?: string;
53
+ /** Texto sugerente que se muestra dentro del input cuando está vacío. */
51
54
  placeholder?: string;
55
+ /** Si es obligatorio. El back también lo valida (R7.4). */
52
56
  required?: boolean;
57
+ /** Valor por defecto si el registro no trae este campo (ej. `default: 'none'` en un `select`/`dropdown`). */
53
58
  default?: T;
59
+ /** Si el campo es solo lectura (no editable en el formulario). */
54
60
  readonly?: boolean;
55
- /** Ciclo de vida: bloquea el campo solo en creación, solo en edición o siempre. */
61
+ /** Bloquea el campo solo en creación, solo en edición o siempre. */
56
62
  readonlyOn?: 'create' | 'update' | 'always';
63
+ /** Condición bajo la cual el campo es solo lectura (expresión sobre otros campos). */
57
64
  readonlyWhen?: FieldCondition;
65
+ /** Condición bajo la cual el campo se muestra u oculta. */
58
66
  visibleWhen?: FieldCondition;
59
67
  /**
60
68
  * Ciclo de vida de la VISIBILIDAD: oculta el campo en creación, en edición
@@ -75,7 +83,9 @@ export interface FieldBase<T> {
75
83
  * defecto (plan 6.6).
76
84
  */
77
85
  dependsOn?: string;
86
+ /** Reglas de validación (mínimo, máximo, patrón, unicidad, etc.). */
78
87
  validators?: FieldValidators;
88
+ /** Ancho en la grilla del formulario (1–12), igual que un widget del dashboard. */
79
89
  width?: GridWidth;
80
90
  /** Id de la sección/pestaña del layout a la que pertenece (base.md §4). */
81
91
  section?: string;
@@ -84,9 +94,18 @@ export interface FieldBase<T> {
84
94
  * verificar en cada petición.
85
95
  */
86
96
  access?: {
97
+ /** Código de nivel de acceso requerido para leer el campo. */
87
98
  read?: string;
99
+ /** Código de nivel de acceso requerido para escribir el campo. */
88
100
  write?: string;
89
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;
90
109
  }
91
110
  export interface SelectOption {
92
111
  value: string;
@@ -98,6 +117,9 @@ export interface TextField extends FieldBase<string> {
98
117
  maxLength?: number;
99
118
  pattern?: string;
100
119
  }
120
+ export interface ColorField extends FieldBase<string> {
121
+ type: 'color';
122
+ }
101
123
  export interface TextareaField extends FieldBase<string> {
102
124
  type: 'textarea';
103
125
  rows?: number;
@@ -169,6 +191,55 @@ export type SelectField = FieldBase<string> & {
169
191
  export type MultiselectField = FieldBase<string[]> & {
170
192
  type: 'multiselect';
171
193
  } & SelectData;
194
+ /**
195
+ * Desplegable nativo (`<select>`) que SIEMPRE se renderiza como lista
196
+ * desplegable, independientemente de cuántas opciones tenga.
197
+ *
198
+ * A diferencia de `select` (que usa radio buttons para 2–4 opciones
199
+ * estáticas por la guía de control §1), este tipo nunca muta a radios.
200
+ * Úsalo cuando necesites un dropdown verdadero aunque tenga pocas opciones.
201
+ */
202
+ export interface DropdownField extends FieldBase<string> {
203
+ type: 'dropdown';
204
+ options: SelectOption[];
205
+ }
206
+ /**
207
+ * Opción de un selector visual enriquecido (`rich-select`).
208
+ *
209
+ * Además de `value`/`label`, cada opción puede declarar una `preview` con
210
+ * HTML seguro que el motor renderiza como tarjeta visual (badge, chip con
211
+ * icono, color swatch, etc.). El HTML viaja en el schema y el motor lo
212
+ * pinta tal cual tras pasar por el sanitizador de Angular.
213
+ */
214
+ export interface RichSelectOption {
215
+ value: string;
216
+ label: string;
217
+ disabled?: boolean;
218
+ /** HTML seguro para pintar como preview de la opción (ej. `<span class="chip">…</span>`). */
219
+ preview?: string;
220
+ }
221
+ /**
222
+ * Selector visual enriquecido: cada opción se renderiza como tarjeta con su
223
+ * `preview` HTML en vez de un `<select>` nativo o radio buttons planos.
224
+ * Sólo opciones estáticas (sin `source`).
225
+ */
226
+ export interface RichSelectField extends FieldBase<string> {
227
+ type: 'rich-select';
228
+ options: RichSelectOption[];
229
+ /**
230
+ * Ancho de cada tarjeta de opción en la grilla (1–12), igual que `width`
231
+ * de un campo. Por defecto `4` (3 tarjetas por fila en escritorio).
232
+ * Úsalo para opciones con preview larga (menos tarjetas por fila) o
233
+ * preview corta (más tarjetas).
234
+ */
235
+ cardWidth?: GridWidth;
236
+ /**
237
+ * Cuántas tarjetas mostrar por fila en el punto de ruptura `lg`.
238
+ * Sobrescribe el cálculo automático basado en `cardWidth`.
239
+ * Ejemplo: `2` obliga a 2 columnas grandes sin importar el ancho.
240
+ */
241
+ cardCols?: 1 | 2 | 3 | 4 | 5 | 6;
242
+ }
172
243
  export interface UrlField extends FieldBase<string> {
173
244
  type: 'url';
174
245
  }
@@ -322,6 +393,12 @@ export interface WidgetBase {
322
393
  help?: string;
323
394
  /** Ancho en la grilla del panel (1–12), igual que un campo. */
324
395
  width?: GridWidth;
396
+ /**
397
+ * Ancho en la grilla en mobile (1–12). Por defecto `12` (ancho completo,
398
+ * apilado). Declararlo permite controlar el layout responsive en pantallas
399
+ * chicas sin tocar el ancho de escritorio (`width`).
400
+ */
401
+ mobileWidth?: GridWidth;
325
402
  /**
326
403
  * De dónde saca sus datos (F8 · R6.5).
327
404
  *
@@ -400,7 +477,7 @@ export interface StatusProgressWidget extends WidgetBase {
400
477
  }
401
478
  export type WidgetSchema = MetricCardWidget | BarChartWidget | ChartLineWidget | RecordListWidget | QuickActionsWidget | StatusProgressWidget;
402
479
  export type WidgetType = WidgetSchema['type'];
403
- export type FieldSchema = TextField | TextareaField | SlugField | NumberField | CurrencyField | BooleanField | SelectField | MultiselectField | UrlField | EmailField | PhoneField | DateField | TimeField | ReadonlyTextField | RelationField | ImageField | FileField | GroupField | ListField | StringListField | KeyValueField;
480
+ 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;
404
481
  export type FieldType = FieldSchema['type'];
405
482
  export type ListColumnFormat = 'text' | 'badge' | 'thumbnail' | 'boolean' | 'number' | 'currency' | 'date';
406
483
  export interface ListColumn {
@@ -496,8 +573,19 @@ export interface ResourcePermissions {
496
573
  }
497
574
  /**
498
575
  * Rutas de la API relativas a `API_URL` (base.md §3, api.md §6 Opción A).
499
- * Pueden incluir el marcador `{key}`. Los singleton solo usan
500
- * `get` / `update`.
576
+ *
577
+ * Marcadores que un path acepta — se resuelven ANTES de despachar la petición
578
+ * (y para ambos drivers):
579
+ *
580
+ * - `{key}` — clave del registro actual; sólo aplica a las operaciones con
581
+ * clave (`get`, `update`, `remove`). Si llega sin resolver, es error.
582
+ * - `{<fuente>}` / `{<fuente>.<ruta.anidada>}` — valor de una fuente de
583
+ * contexto: las construidas `auth` (sesión) y `config` (config global), o
584
+ * cualquier fuente registrada en `provideGasAdmin({ valueSources })`.
585
+ *
586
+ * El valor se codifica como segmento de URL. Un marcador sin valor, o con una
587
+ * fuente no registrada, lanza un error que nombra el path y el marcador: nunca
588
+ * se envía un segmento vacío. Los singleton solo usan `get` / `update`.
501
589
  */
502
590
  export interface ResourceEndpoints {
503
591
  list?: string;
@@ -505,6 +593,79 @@ export interface ResourceEndpoints {
505
593
  create?: string;
506
594
  update?: string;
507
595
  remove?: string;
596
+ /**
597
+ * Declaración opcional de cómo unwrappear la respuesta de esta operación.
598
+ * Cuando se declara, el motor extrae el contenido de `dataKey` antes de
599
+ * devolverlo al consumidor (ej.: `{ data: [...] }` → `[...]`).
600
+ * Si no se declara, el motor mantiene el comportamiento actual:
601
+ * respuesta pasada tal cual (array plano, `{ items }`, o objeto crudo).
602
+ * Esta declaración es por operación; si se desea un valor global, usarlo
603
+ * en cada operación o considerar un default a nivel de config en futuro.
604
+ */
605
+ responseUnwrap?: ResponseUnwrap;
606
+ }
607
+ /**
608
+ * Cómo el motor debe desempaquetar la respuesta HTTP antes de entregarla.
609
+ * Opcional y opt-in: schemas sin este campo siguen el contrato actual.
610
+ *
611
+ * - `dataKey`: clave en el body que contiene el payload real (ej. `data`,
612
+ * `items`, `result`). Si el body no tiene esta clave, se devuelve `undefined`
613
+ * para lista / objeto vacío según corresponda, y se lanza error en modo
614
+ * estricto si se configura.
615
+ * - `metaKey`: clave con metadatos (paginación, total, etc.). Se ignora en
616
+ * el valor devuelto al consumidor pero puede leerse si el motor la expone
617
+ * en futuro.
618
+ * - `apply`: `'list' | 'get' | 'create' | 'update' | 'remove' | 'all'`.
619
+ * Qué operaciones aplican el unwrap. Por defecto `'all'` cuando se declara.
620
+ */
621
+ export interface ResponseUnwrap {
622
+ dataKey: string;
623
+ metaKey?: string;
624
+ apply?: 'list' | 'get' | 'create' | 'update' | 'remove' | 'all';
625
+ /**
626
+ * Claves de metadata de paginación dentro del sobre. Usadas por
627
+ * `ApiService.listPage()` para armar `PageResult<T>`. Opcional: sin
628
+ * declaración, el motor intenta auto-detectar claves comunes
629
+ * (`nextCursor`, `total`, `hasMore`) y, si no hay, `hasMore = false`.
630
+ */
631
+ pagination?: {
632
+ /** Clave del cursor siguiente dentro de `meta` (o del body si no hay `metaKey`). */
633
+ cursorKey?: string;
634
+ /** Clave del total de registros. */
635
+ totalKey?: string;
636
+ /** Clave del flag booleano "hay más páginas". */
637
+ hasMoreKey?: string;
638
+ };
639
+ }
640
+ /**
641
+ * Método HTTP soportado por endpoints personalizados.
642
+ * Declarado aquí (y no en api.service.ts) para evitar ciclos de importación
643
+ * entre modelos y servicios.
644
+ */
645
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
646
+ /**
647
+ * Definición de un endpoint personalizado más allá de los cinco estándar
648
+ * (list/get/create/update/remove). Permite al consumidor declarar rutas con
649
+ * verbos arbitrarios, segmentos extra, payloads JSON o multipart, y unwrap de
650
+ * respuesta propio.
651
+ *
652
+ * Ejemplo:
653
+ * name: 'recalculate-ranks'
654
+ * method: 'POST'
655
+ * path: '/admin/businesses/{businessId}/ranks/recalculate'
656
+ * payloadMode: 'json'
657
+ */
658
+ export interface CustomEndpointDefinition {
659
+ /** Nombre único del endpoint dentro del recurso. Se usa para invocarlo. */
660
+ name: string;
661
+ /** Método HTTP de la petición. */
662
+ method: HttpMethod;
663
+ /** Ruta relativa a `API_URL`. Puede incluir marcadores {key}, {auth.user.id}, etc. */
664
+ path: string;
665
+ /** Modo de payload: 'json' (default) o 'multipart' para FormData. */
666
+ payloadMode?: 'json' | 'multipart';
667
+ /** Unwrap de respuesta override para este endpoint. Si no se declara, se usa el del recurso. */
668
+ responseUnwrap?: ResponseUnwrap;
508
669
  }
509
670
  export interface ResourceSchema {
510
671
  /** Identificador interno (`categories`). */
@@ -522,6 +683,8 @@ export interface ResourceSchema {
522
683
  keyField: string;
523
684
  /** Campo que se muestra como nombre del registro. */
524
685
  titleField: string;
686
+ /** Endpoints personalizados más allá de list/get/create/update/remove. */
687
+ customEndpoints?: Record<string, CustomEndpointDefinition>;
525
688
  /** Si el listado se reordena (requiere `positionField`). */
526
689
  sortable?: boolean;
527
690
  positionField?: string;
@@ -799,24 +962,99 @@ export interface AuthSchema {
799
962
  firebase?: FirebaseAuthConfig;
800
963
  }
801
964
  /**
802
- * Narrow surface the library uses from Firebase Auth.
803
- * Implemented by the consumer app; injected via FIREBASE_AUTH_ADAPTER token.
804
- * Keeping the interface thin avoids coupling to any specific Firebase version.
965
+ * URL base de la API (baseapi §2.3): es la URL directa del Web App de Apps
966
+ * Script; ahí se hace el `POST` del envelope `{ token, method, path, payload }`.
967
+ * OBLIGATORIO proporcionarlo en `app.config.ts`:
968
+ * `{ provide: API_URL, useValue: 'https://…' }` (Fase 3). Vacío = mismo origen.
969
+ */
970
+ export declare const API_URL: InjectionToken<string>;
971
+ /**
972
+ * Superficie mínima que `gas-admin-ui` usa de Firebase Auth.
973
+ * Implementada por la aplicación consumidora; inyectada vía `FIREBASE_AUTH_ADAPTER`.
974
+ * Mantener la interfaz fina evita acoplar la librería a una versión específica de Firebase.
805
975
  */
806
976
  interface FirebaseAuthAdapter {
807
977
  /**
808
- * Opens a Google sign-in popup and resolves to the Firebase ID token.
809
- * Rejects on cancellation or error.
978
+ * Abre un popup de Google sign-in y resuelve al ID token de Firebase.
979
+ * Rechaza si el usuario cancela o hay error.
810
980
  */
811
981
  signInWithGoogle(): Promise<string>;
812
982
  /**
813
- * Signs in with email and password via Firebase and resolves to the ID token.
814
- * Used when provider === 'password' AND firebase is configured (Firebase email auth).
983
+ * Inicia sesión con email y contraseña vía Firebase y resuelve al ID token.
984
+ * Se usa cuando provider === 'password' Y firebase está configurado (Firebase email auth).
815
985
  */
816
986
  signInWithEmailAndPassword(email: string, password: string): Promise<string>;
817
- /** Signs out from Firebase (called alongside the app session clear). */
987
+ /**
988
+ * Cierra sesión en Firebase (llamado junto al clear de la sesión de la app).
989
+ */
818
990
  signOut(): Promise<void>;
991
+ /**
992
+ * Obtiene un ID token de Firebase FRESCO para usarlo en la cabecera
993
+ * `Authorization: Bearer <token>` de las peticiones REST.
994
+ *
995
+ * OPCIONAL: si el adaptador no lo implementa, `gas-admin-ui` envía el token
996
+ * de sesión almacenado tal cual (comportamiento actual). Implementarlo
997
+ * permite refrescar el ID token antes de cada petición REST, evitando
998
+ * errores "ID token inválido" tras la expiración de Firebase (1 hora).
999
+ *
1000
+ * El adaptador decide cuándo refrescar (p. ej. `getIdToken(true)` de Firebase
1001
+ * SDK fuerza la renovación; sin argumento devuelve el cacheado si aún es válido).
1002
+ */
1003
+ getIdToken?(): Promise<string>;
819
1004
  }
1005
+ /**
1006
+ * Token DI opcional para el adaptador de Firebase Auth.
1007
+ * Proporcionado por la aplicación consumidora cuando usa proveedores federados o Firebase Auth.
1008
+ */
1009
+ export declare const FIREBASE_AUTH_ADAPTER: InjectionToken<FirebaseAuthAdapter>;
1010
+ /**
1011
+ * Driver de transporte HTTP del motor.
1012
+ *
1013
+ * Vive acá (capa de infraestructura) y NO en `ResourceSchema` (capa de dominio)
1014
+ * porque es una decisión de red, no de modelo de datos. Cambiar el driver
1015
+ * no debería requerir tocar ningún schema de recursos.
1016
+ *
1017
+ * - `'gas'` (default): tunnel `POST text/plain` con envelope
1018
+ * `{ token, method, path, payload }` — contrato de Google Apps Script.
1019
+ * - `'rest'`: verbos HTTP reales (`GET`, `POST`, `PUT`, `DELETE`) con
1020
+ * `Authorization: Bearer <token>` en cabeceras y params en la URL (GET/DELETE)
1021
+ * o en el body JSON (POST/PUT/PATCH).
1022
+ *
1023
+ * Se fija en `provideGasAdmin({ driver })`. Sin declaración explícita el
1024
+ * motor usa `'gas'` — cero impacto para consumidores existentes.
1025
+ */
1026
+ export declare const API_DRIVER: InjectionToken<"gas" | "rest">;
1027
+ /**
1028
+ * Registro de FUENTES DE CONTEXTO para marcadores en paths de endpoints.
1029
+ *
1030
+ * Cada clave es el nombre de una fuente — `{ <fuente> }` o
1031
+ * `{ <fuente>.<ruta.anidada> }` dentro de un path declarado en
1032
+ * `ResourceEndpoints` — y cada valor es un getter sin argumentos que devuelve
1033
+ * el dato ACTUAL en el momento de la petición (no al registrar).
1034
+ *
1035
+ * El motor ya aporta dos fuentes construidas y éstas NO se declaran acá:
1036
+ *
1037
+ * - `auth` → la sesión que dueña `AuthService` (usuario y token).
1038
+ * - `config` → el mapa global que puebla `ConfigService` desde los singletons.
1039
+ *
1040
+ * Este registro es sólo para fuentes que aporta el consumidor, para que el
1041
+ * motor no conozca nombres de dominio: qué campo se lee lo decide el schema,
1042
+ * no el engine.
1043
+ *
1044
+ * Un marcador que apunte a una fuente no registrada, o a un valor ausente,
1045
+ * revienta con un error que nombra el path y el marcador — jamás se manda un
1046
+ * segmento vacío al backend.
1047
+ */
1048
+ type ValueSources = Readonly<Record<string, () => unknown>>;
1049
+ export declare const VALUE_SOURCES: InjectionToken<Readonly<Record<string, () => unknown>>>;
1050
+ /**
1051
+ * Unwrap de respuesta global por defecto (`GasAdminConfig.defaultResponseUnwrap`).
1052
+ *
1053
+ * Se fusiona con la declaración `endpoint.responseUnwrap` de cada recurso:
1054
+ * la del recurso tiene prioridad en las claves que declara. Sin token, sólo
1055
+ * aplica lo declarado por recurso — cero impacto en schemas existentes.
1056
+ */
1057
+ export declare const DEFAULT_RESPONSE_UNWRAP: InjectionToken<ResponseUnwrap>;
820
1058
  /** Fija el catálogo (uso interno de `provideGasAdmin`; testable directo). */
821
1059
  export declare function setSchemaCatalog(schemas: readonly ResourceSchema[], auth?: AuthSchema): void;
822
1060
  /** Busca un schema por `id`; `undefined` si no existe. */
@@ -842,6 +1080,23 @@ interface GasAdminConfig {
842
1080
  * Sin declaración el motor usa `'gas'` — cero impacto en consumidores existentes.
843
1081
  */
844
1082
  driver?: 'gas' | 'rest';
1083
+ /**
1084
+ * Fuentes de contexto que el consumidor aporta para marcadores en paths de
1085
+ * endpoints: `{ <fuente> }` / `{ <fuente>.<ruta> }` se resuelven en el
1086
+ * momento de la petición contra este registro más las fuentes construidas
1087
+ * `auth` (sesión) y `config` (config global).
1088
+ *
1089
+ * Opcional: sin él, sólo existen `auth` y `config`. Declararlo no cambia
1090
+ * ningún path que no lleve marcadores — cero impacto en schemas existentes.
1091
+ */
1092
+ valueSources?: ValueSources;
1093
+ /**
1094
+ * Unwrap de respuesta global por defecto. Se fusiona con la declaración
1095
+ * `endpoint.responseUnwrap` de cada recurso (la del recurso tiene prioridad
1096
+ * en claves declaradas). Opcional: sin él, sólo aplica lo declarado por
1097
+ * recurso — cero impacto en schemas existentes (R2).
1098
+ */
1099
+ defaultResponseUnwrap?: ResponseUnwrap;
845
1100
  }
846
1101
  /**
847
1102
  * Wiring completo del motor: catálogo + HTTP con el interceptor de
@@ -926,34 +1181,22 @@ export declare class ApiError extends Error {
926
1181
  */
927
1182
  export declare function renewedTokenOf(body: unknown): string | null;
928
1183
  /**
929
- * URL base de la API (baseapi §2.3): es la URL directa del Web App de Apps
930
- * Script; ahí se hace el `POST` del envelope `{ token, method, path, payload }`.
931
- * OBLIGATORIO proporcionarlo en `app.config.ts`:
932
- * `{ provide: API_URL, useValue: 'https://…' }` (Fase 3). Vacío = mismo origen.
933
- */
934
- export declare const API_URL: InjectionToken<string>;
935
- /**
936
- * Token DI opcional para el adaptador de Firebase Auth.
937
- * Proporcionado por la aplicación consumidora cuando usa proveedores federados o Firebase Auth.
938
- */
939
- export declare const FIREBASE_AUTH_ADAPTER: InjectionToken<FirebaseAuthAdapter>;
940
- /**
941
- * Driver de transporte HTTP del motor.
942
- *
943
- * Vive acá (capa de infraestructura) y NO en `ResourceSchema` (capa de dominio)
944
- * porque es una decisión de red, no de modelo de datos. Cambiar el driver
945
- * no debería requerir tocar ningún schema de recursos.
1184
+ * Resultado de una página para `ApiService.listPage()`.
946
1185
  *
947
- * - `'gas'` (default): tunnel `POST text/plain` con envelope
948
- * `{ token, method, path, payload }` — contrato de Google Apps Script.
949
- * - `'rest'`: verbos HTTP reales (`GET`, `POST`, `PUT`, `DELETE`) con
950
- * `Authorization: Bearer <token>` en cabeceras y params en la URL (GET/DELETE)
951
- * o en el body JSON (POST/PUT/PATCH).
952
- *
953
- * Se fija en `provideGasAdmin({ driver })`. Sin declaración explícita el
954
- * motor usa `'gas'` — cero impacto para consumidores existentes.
1186
+ * El backend envía la metadata de paginación dentro del sobre (típicamente
1187
+ * en `meta`); el motor la extrae según la declaración `responseUnwrap.pagination`
1188
+ * y la entrega en este shape. `list()` NO usa este tipo: sigue devolviendo `T[]`.
955
1189
  */
956
- export declare const API_DRIVER: InjectionToken<"gas" | "rest">;
1190
+ export interface PageResult<T> {
1191
+ /** Registros de la página actual. */
1192
+ items: T[];
1193
+ /** Cursor para pedir la página siguiente; `null` cuando no hay más. */
1194
+ nextCursor: string | null;
1195
+ /** Total de registros del conjunto, si el backend lo informa; `0` si no. */
1196
+ total: number;
1197
+ /** `true` si hay al menos una página más. `false` sin cursor informativo. */
1198
+ hasMore: boolean;
1199
+ }
957
1200
  /**
958
1201
  * Guards de sesión (auth.md §5.3).
959
1202
  *
@@ -1062,6 +1305,40 @@ export declare class NotificationService {
1062
1305
  }
1063
1306
  /** Métodos que entiende el envelope del backend (baseapi §2.3). */
1064
1307
  type EnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
1308
+ /** Dependencias del transporte, aportadas por la fachada (no hay singleton acá). */
1309
+ interface TransportDeps {
1310
+ http: HttpClient;
1311
+ /** URL base (`API_URL`): el Web App de Apps Script o el origen REST. */
1312
+ apiUrl: string;
1313
+ /** Getter del token de SESIÓN, leído en CADA petición (R2.8). */
1314
+ sessionToken: () => string | null;
1315
+ /**
1316
+ * Refresco opcional de ID token (Fase Firebase): si existe, se llama antes
1317
+ * de armar la cabecera Authorization. Un rechazo viaja por el canal de
1318
+ * errores — nunca se manda una petición con token inválido.
1319
+ */
1320
+ getIdToken?: () => Promise<string>;
1321
+ }
1322
+ /** `POST text/plain` a `apiUrl` con el envelope GAS. */
1323
+ declare function sendGas<T>(deps: TransportDeps, path: string, method: EnvelopeMethod, payload?: Record<string, unknown> | FormData, multipart?: boolean): Observable<T>;
1324
+ /**
1325
+ * Verbo HTTP real con `Authorization: Bearer`. Si el adaptador provee
1326
+ * `getIdToken()`, se resuelve el token fresco ANTES de la cabecera; si no,
1327
+ * se usa el de sesión almacenado (fallback). Cualquier error de `getIdToken()`
1328
+ * se propaga por el canal de errores.
1329
+ */
1330
+ declare function sendRest<T>(deps: TransportDeps, path: string, method: EnvelopeMethod, payload?: Record<string, unknown> | FormData, multipart?: boolean): Observable<T>;
1331
+ /** Despacho REST concreto sobre una URL ya construida. */
1332
+ declare function doRestRequest<T>(deps: TransportDeps, url: string, method: EnvelopeMethod, payload: Record<string, unknown> | FormData | undefined, headers?: HttpHeaders, multipart?: boolean): Observable<T>;
1333
+ /** `${apiUrl}${path}` normalizada: sin dobles slashes ni path sin slash inicial. */
1334
+ declare function buildUrl(apiUrl: string, path: string): string;
1335
+ /**
1336
+ * Convierte un payload en HttpParams para peticiones GET y DELETE en modo REST.
1337
+ * Si un valor es un objeto plano (ej: `{ view: 'calendar', params: { orden: 'es' } }`),
1338
+ * lo aplana un nivel para que sus claves viajen como query params reales
1339
+ * en lugar de serializarse como `params=[object Object]`.
1340
+ */
1341
+ declare function toHttpParams(payload?: Record<string, unknown>): HttpParams;
1065
1342
  /**
1066
1343
  * CRUD genérico sobre el contrato v2 (baseapi §2.3) con soporte dual-driver:
1067
1344
  *
@@ -1077,6 +1354,28 @@ export declare class ApiService {
1077
1354
  private readonly apiUrl;
1078
1355
  private readonly auth;
1079
1356
  private readonly driver;
1357
+ /** Fuentes que aporta el consumidor (`provideGasAdmin({ valueSources })`). */
1358
+ private readonly valueSources;
1359
+ /** Adaptador opcional de Firebase Auth para refrescar el ID token antes de peticiones REST. */
1360
+ private readonly firebaseAuthAdapter;
1361
+ /**
1362
+ * Acceso PEREZOSO a `ConfigService` (R2 · plan doc/context-sources-plan.md).
1363
+ *
1364
+ * `config` es fuente construida del motor, pero inyectarla en el constructor
1365
+ * formaría un ciclo: `ConfigService` inyecta `ApiService` (opcional) para
1366
+ * cargar los singletons ⇒ NG0200 en el arranque. Se pide en el momento de la
1367
+ * petición, cuando ambos servicios ya están vivos. Si no existe (TestBed
1368
+ * parcial), devuelve `null` y el resolver falla ruidoso con «sin valor».
1369
+ */
1370
+ private readonly injector;
1371
+ /** Unwrap global por defecto (`GasAdminConfig.defaultResponseUnwrap`); opcional. */
1372
+ private readonly defaultUnwrap;
1373
+ /**
1374
+ * Declaración de unwrap EFECTIVA para un endpoint: fusión del default
1375
+ * global con la declaración por recurso (recurso gana en las claves que
1376
+ * declara). Sin ninguna de las dos ⇒ `undefined` (comportamiento actual).
1377
+ */
1378
+ private effectiveUnwrap;
1080
1379
  /**
1081
1380
  * Listado. El backend envuelve la respuesta cuando la operación declara
1082
1381
  * `shape.envelope: 'list'` (F5) ⇒ `{ items, nextCursor, total }` (R6.4);
@@ -1092,6 +1391,21 @@ export declare class ApiService {
1092
1391
  * llamada es la de siempre — la lectura llana del recurso.
1093
1392
  */
1094
1393
  list<T>(endpoint: ResourceEndpoints, payload?: Record<string, unknown>): Observable<T[]>;
1394
+ /**
1395
+ * Listado paginado OPT-IN (plan doc/engine-refactor-plan.md · Fase 3).
1396
+ *
1397
+ * Misma petición que `list()` pero devuelve `PageResult<T>` con la metadata
1398
+ * de paginación que el backend envía en el sobre (`{ items, nextCursor,
1399
+ * total }` o dentro de `meta` con unwrap declarado). Las claves se leen
1400
+ * según `responseUnwrap.pagination` (cursorKey/totalKey/hasMoreKey) o se
1401
+ * auto-detectan (`nextCursor`, `total`, `hasMore`).
1402
+ *
1403
+ * `payload` acepta normalmente `{ cursor, limit }` para pedir la página
1404
+ * siguiente; viaja como cualquier otro payload (query params en GET REST,
1405
+ * campo `payload` del envelope en GAS). `list()` no cambia: sigue
1406
+ * devolviendo `T[]` (R11).
1407
+ */
1408
+ listPage<T>(endpoint: ResourceEndpoints, payload?: Record<string, unknown>): Observable<PageResult<T>>;
1095
1409
  /** `key` solo aplica a colecciones (`{key}` en la plantilla); los singleton no la llevan. */
1096
1410
  get<T>(endpoint: ResourceEndpoints, key?: string): Observable<T>;
1097
1411
  create<T>(endpoint: ResourceEndpoints, payload: Record<string, unknown>): Observable<T>;
@@ -1103,10 +1417,34 @@ export declare class ApiService {
1103
1417
  * `/admin/upload-signature`, etc.
1104
1418
  */
1105
1419
  request<T>(method: EnvelopeMethod, path: string | undefined, payload?: Record<string, unknown>): Observable<T>;
1420
+ /**
1421
+ * Invoca un endpoint personalizado declarado por el consumidor.
1422
+ *
1423
+ * Acepta bien la definición directa (`CustomEndpointDefinition`) o un schema
1424
+ * + nombre (resuelve en `schema.customEndpoints`). El despacho respeta el
1425
+ * método, la ruta, el modo de payload (json/multipart) y el unwrap propios
1426
+ * de la declaración.
1427
+ */
1428
+ invokeCustomEndpoint<T>(definitionOrSchema: CustomEndpointDefinition | ResourceSchema, nameOrPayload?: string | Record<string, unknown> | FormData, payload?: Record<string, unknown> | FormData, key?: string): Observable<T>;
1429
+ /** Alias corto de `invokeCustomEndpoint`. */
1430
+ invokeCustom<T>(schemaOrDefinition: ResourceSchema | CustomEndpointDefinition, nameOrPayload?: string | Record<string, unknown> | FormData, payload?: Record<string, unknown> | FormData, key?: string): Observable<T>;
1431
+ /** Alias adicional por compatibilidad. */
1432
+ custom<T>(schemaOrDefinition: ResourceSchema | CustomEndpointDefinition, nameOrPayload?: string | Record<string, unknown> | FormData, payload?: Record<string, unknown> | FormData, key?: string): Observable<T>;
1106
1433
  private send;
1107
- private sendGas;
1108
- private sendRest;
1109
- private buildUrl;
1434
+ /**
1435
+ * Dependencias del transporte (Fase 4): la fachada sólo arma este objeto y
1436
+ * delega en `transport.ts`. El token de sesión y el ID token fresco se
1437
+ * resuelven vía getters en el momento de la petición (R2.8 / refresh).
1438
+ */
1439
+ private transportDeps;
1440
+ /**
1441
+ * Fuentes de contexto de ESTA petición: getters, no valores — se evalúan
1442
+ * sólo si el path trae marcadores.
1443
+ *
1444
+ * Las construidas del motor (`auth`, `config`) van ÚLTIMAS: el motor es
1445
+ * dueño de sus fuentes y el registro del consumidor no las pisa.
1446
+ */
1447
+ private buildSources;
1110
1448
  private withKey;
1111
1449
  static ɵfac: i0.ɵɵFactoryDeclaration<ApiService, never>;
1112
1450
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
@@ -1204,10 +1542,6 @@ export declare class AuthService {
1204
1542
  * que deja una sesión muerta en el servidor.
1205
1543
  */
1206
1544
  logout(): Observable<void>;
1207
- /** Revoca las sesiones del usuario en todos sus dispositivos. */
1208
- logoutAll(): Observable<{
1209
- effectiveWithinSeconds: number;
1210
- }>;
1211
1545
  /** Revalida la sesión restaurada contra `/auth/me`; sin token ⇒ sin viaje. */
1212
1546
  restore(): Observable<void>;
1213
1547
  /**
@@ -1454,14 +1788,14 @@ export declare class Sidebar {
1454
1788
  private readonly auth;
1455
1789
  private readonly notifications;
1456
1790
  readonly isAuthenticated: import("@angular/core").Signal<boolean>;
1457
- readonly closingAllSessions: import("@angular/core").WritableSignal<boolean>;
1791
+ readonly loggingOut: import("@angular/core").WritableSignal<boolean>;
1458
1792
  /**
1459
1793
  * R2.10 — el menú se corta dos veces: `isVisible` (permisos declarados
1460
1794
  * en el schema) y `canAccess` (auth.md §5.3, `access` de la sesión).
1461
1795
  * Sólo UX: filtrar acá no protege nada, el back re-verifica en R2.6.
1462
1796
  */
1463
1797
  readonly menuItems: import("@angular/core").Signal<MenuEntry[]>;
1464
- closeAllSessions(): Promise<void>;
1798
+ closeSession(): Promise<void>;
1465
1799
  static ɵfac: i0.ɵɵFactoryDeclaration<Sidebar, never>;
1466
1800
  static ɵcmp: i0.ɵɵComponentDeclaration<Sidebar, "app-sidebar", never, {
1467
1801
  "open": {
@@ -2949,7 +3283,7 @@ export declare class Toast {
2949
3283
  * aserción en el **mismo archivo**: añadir un tipo toca una carpeta y una
2950
3284
  * línea de este manifiesto, y el host no se entera.
2951
3285
  */
2952
- 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"];
3286
+ 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"];
2953
3287
  /**
2954
3288
  * Manifiesto de descriptores de campo (R3.3 · registro único de render).
2955
3289
  *
@@ -3186,4 +3520,4 @@ export declare function actionsFor(surface: ActionSurface): ActionDescriptor[];
3186
3520
  export declare function actionType(action: ResourceAction): string;
3187
3521
  /** Tipos registrados. Sólo para comprobaciones y tests. */
3188
3522
  export declare function actionTypes(): string[];
3189
- export { type ComponentRenderKind, type FirebaseAuthAdapter, type GasAdminConfig, type RenderKind, type ResolvedShare, type ValidatorName, get as getRenderDescriptor, isSupported as isRenderSupported, register as registerRenderDescriptor, unregister as unregisterRenderDescriptor };
3523
+ 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 };