@tinya-code/gas-admin-ui 0.2.0-alpha.2 → 0.2.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/fesm2022/{tinya-code-gas-admin-ui-calendar-view-CquWGzw7.mjs → tinya-code-gas-admin-ui-calendar-view-DkYfP4n9.mjs} +4 -4
- package/fesm2022/{tinya-code-gas-admin-ui-calendar-view-CquWGzw7.mjs.map → tinya-code-gas-admin-ui-calendar-view-DkYfP4n9.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-condition-evaluator-DmvlAP9z.mjs → tinya-code-gas-admin-ui-condition-evaluator-C1tjeb6D.mjs} +2 -2
- package/fesm2022/{tinya-code-gas-admin-ui-condition-evaluator-DmvlAP9z.mjs.map → tinya-code-gas-admin-ui-condition-evaluator-C1tjeb6D.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-data-source.service-vAi2DYL9.mjs → tinya-code-gas-admin-ui-data-source.service-BUoA9kBh.mjs} +2 -2
- package/fesm2022/{tinya-code-gas-admin-ui-data-source.service-vAi2DYL9.mjs.map → tinya-code-gas-admin-ui-data-source.service-BUoA9kBh.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-error-mapper.service-dKQNLNRJ.mjs → tinya-code-gas-admin-ui-error-mapper.service-BDA1dL5V.mjs} +2 -2
- package/fesm2022/{tinya-code-gas-admin-ui-error-mapper.service-dKQNLNRJ.mjs.map → tinya-code-gas-admin-ui-error-mapper.service-BDA1dL5V.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-file-CKh878UH.mjs → tinya-code-gas-admin-ui-file-DhLi3eO0.mjs} +3 -3
- package/fesm2022/{tinya-code-gas-admin-ui-file-CKh878UH.mjs.map → tinya-code-gas-admin-ui-file-DhLi3eO0.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-form-view-_LpdZb15.mjs → tinya-code-gas-admin-ui-form-view-CjC-GOQl.mjs} +9 -9
- package/fesm2022/{tinya-code-gas-admin-ui-form-view-_LpdZb15.mjs.map → tinya-code-gas-admin-ui-form-view-CjC-GOQl.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-image-BPfPNwru.mjs → tinya-code-gas-admin-ui-image-D9-mpBhA.mjs} +3 -3
- package/fesm2022/{tinya-code-gas-admin-ui-image-BPfPNwru.mjs.map → tinya-code-gas-admin-ui-image-D9-mpBhA.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-kanban-view-C-w9mhiB.mjs → tinya-code-gas-admin-ui-kanban-view-B4u2ibPb.mjs} +4 -4
- package/fesm2022/{tinya-code-gas-admin-ui-kanban-view-C-w9mhiB.mjs.map → tinya-code-gas-admin-ui-kanban-view-B4u2ibPb.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-list-BKr33pde.mjs → tinya-code-gas-admin-ui-list-CQpL6vyE.mjs} +2 -2
- package/fesm2022/{tinya-code-gas-admin-ui-list-BKr33pde.mjs.map → tinya-code-gas-admin-ui-list-CQpL6vyE.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-multiselect-SfZJHfbh.mjs → tinya-code-gas-admin-ui-multiselect-CTQPGBOK.mjs} +3 -3
- package/fesm2022/{tinya-code-gas-admin-ui-multiselect-SfZJHfbh.mjs.map → tinya-code-gas-admin-ui-multiselect-CTQPGBOK.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-relation-WCflawIU.mjs → tinya-code-gas-admin-ui-relation-lr_XG6jt.mjs} +3 -3
- package/fesm2022/{tinya-code-gas-admin-ui-relation-WCflawIU.mjs.map → tinya-code-gas-admin-ui-relation-lr_XG6jt.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-render-registry-ghv7Mb1x.mjs → tinya-code-gas-admin-ui-render-registry-CcIgBsSl.mjs} +10 -10
- package/fesm2022/{tinya-code-gas-admin-ui-render-registry-ghv7Mb1x.mjs.map → tinya-code-gas-admin-ui-render-registry-CcIgBsSl.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-schema-catalog-CL4IZDce.mjs → tinya-code-gas-admin-ui-schema-catalog-DU_6_CtP.mjs} +11 -5
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-DU_6_CtP.mjs.map +1 -0
- package/fesm2022/{tinya-code-gas-admin-ui-seed-BnwHE1Tw.mjs → tinya-code-gas-admin-ui-seed-Bn6tl-yM.mjs} +2 -2
- package/fesm2022/{tinya-code-gas-admin-ui-seed-BnwHE1Tw.mjs.map → tinya-code-gas-admin-ui-seed-Bn6tl-yM.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-select-BaF4kSXl.mjs → tinya-code-gas-admin-ui-select-BwefyMVx.mjs} +3 -3
- package/fesm2022/{tinya-code-gas-admin-ui-select-BaF4kSXl.mjs.map → tinya-code-gas-admin-ui-select-BwefyMVx.mjs.map} +1 -1
- package/fesm2022/{tinya-code-gas-admin-ui-upload.service-C7GzSmuL.mjs → tinya-code-gas-admin-ui-upload.service-DRmKFrSH.mjs} +2 -2
- package/fesm2022/{tinya-code-gas-admin-ui-upload.service-C7GzSmuL.mjs.map → tinya-code-gas-admin-ui-upload.service-DRmKFrSH.mjs.map} +1 -1
- package/fesm2022/tinya-code-gas-admin-ui.mjs +57 -10
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/tinya-code-gas-admin-ui.d.ts +36 -15
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CL4IZDce.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tinya-code-gas-admin-ui-render-registry-ghv7Mb1x.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/columns/descriptors.ts","../../../projects/gas-admin-ui/src/lib/fields/boolean/boolean.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/currency/currency.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/date/date.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/email/email.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/file/file.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/group/group.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/image/image.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/key-value/key-value.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/list/list.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/multiselect/multiselect.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/number/number.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/phone/phone.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/readonly-text/readonly-text.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/relation/relation.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/select/select.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/slug/slug.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/string-list/string-list.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/text/text.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/textarea/textarea.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/time/time.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/url/url.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/descriptors.ts","../../../projects/gas-admin-ui/src/lib/layouts/descriptors.ts","../../../projects/gas-admin-ui/src/lib/widgets/series.ts","../../../projects/gas-admin-ui/src/lib/widgets/bar-chart/bar-chart.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/chart-line/chart-line.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/metric-card/metric-card.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/quick-actions/quick-actions.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/record-list/record-list.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/status-progress/status-progress.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/descriptors.ts","../../../projects/gas-admin-ui/src/lib/views/descriptors.ts","../../../projects/gas-admin-ui/src/lib/core/registry/render-registry.ts"],"sourcesContent":["import type { RenderDescriptor, RenderExtras } from '../core/registry/render-registry';\n\n/**\n * Manifiesto de descriptores de formato de columna (R4.3 · `kind:\n * 'column-format'`).\n *\n * Es el equivalente a `fields/descriptors.ts` y `widgets/descriptors.ts`:\n * una línea por formato y ningún import de componente — cada `load()` guarda\n * el cierre de un `import()` **con literal**, que es lo que permite al\n * bundler partir el chunk; el registro los recoge al importar este módulo\n * (`core/registry/render-registry.ts`).\n *\n * **Añadir un formato** ⇒ crear `format-<nombre>.ts` + una entrada acá.\n * `list-view` no se toca: ése es el criterio de éxito de R4.3.\n *\n * Todos comparten el cableado: el descriptor sólo expone la celda, que la\n * vista cuelga de `extras.data`.\n */\nconst inputs = (_config: unknown, extras: RenderExtras): Record<string, unknown> => ({\n cell: extras.data,\n});\n\nconst text: RenderDescriptor = {\n kind: 'column-format',\n type: 'text',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-text').then((module) => module.ColumnFormatText),\n inputs,\n};\n\nconst badge: RenderDescriptor = {\n kind: 'column-format',\n type: 'badge',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-badge').then((module) => module.ColumnFormatBadge),\n inputs,\n};\n\nconst thumbnail: RenderDescriptor = {\n kind: 'column-format',\n type: 'thumbnail',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-thumbnail').then((module) => module.ColumnFormatThumbnail),\n inputs,\n};\n\nconst boolean: RenderDescriptor = {\n kind: 'column-format',\n type: 'boolean',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-boolean').then((module) => module.ColumnFormatBoolean),\n inputs,\n};\n\nconst number: RenderDescriptor = {\n kind: 'column-format',\n type: 'number',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-number').then((module) => module.ColumnFormatNumber),\n inputs,\n};\n\nconst currency: RenderDescriptor = {\n kind: 'column-format',\n type: 'currency',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-currency').then((module) => module.ColumnFormatCurrency),\n inputs,\n};\n\nconst date: RenderDescriptor = {\n kind: 'column-format',\n type: 'date',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-date').then((module) => module.ColumnFormatDate),\n inputs,\n};\n\n/** Los 7 formatos del `@switch` que este registro reemplaza (R4.3). */\nexport const columnFormatDescriptors: readonly RenderDescriptor[] = [\n text,\n badge,\n thumbnail,\n boolean,\n number,\n currency,\n date,\n];\n\n/**\n * Los 7 nombres, como dato.\n *\n * Va en paralelo a `ListColumnFormat` (unión de `schema.model`) y lo vigila\n * `core/registry/contract.spec.ts`: la unión vive en tipos y el registro en\n * runtime, y sólo un test reconcilia los dos — el mismo tripwire que ya\n * existe para los 20 campos y los 6 widgets.\n */\nexport const COLUMN_FORMAT_TYPES = [\n 'text',\n 'badge',\n 'thumbnail',\n 'boolean',\n 'number',\n 'currency',\n 'date',\n] as const;\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `boolean` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'boolean',\n since: '1.0',\n load: () => import('./boolean').then((module) => module.FieldBoolean),\n seed: 'boolean',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `currency` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'currency',\n since: '1.0',\n load: () => import('./currency').then((module) => module.FieldCurrency),\n // Nace como `null`: no puede ser `0`, que sería un valor ya cargado.\n seed: 'number',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `date` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'date',\n since: '1.0',\n load: () => import('./date').then((module) => module.FieldDate),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `email` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'email',\n since: '1.0',\n load: () => import('./email').then((module) => module.FieldEmail),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `file` (R3.3 · R9.5.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'file',\n since: '9.5',\n load: () => import('./file').then((module) => module.FieldFile),\n // Nace como cadena: la URL pública es el valor, igual que en `image`.\n seed: 'string',\n surfaces: ['form', 'display'],\n // Como `image` (D27): control propio cuya sincronización con Signal Forms\n // vive en ɵɵcontrol, así que el host lo declara a mano con `[formField]`.\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `group` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'group',\n since: '1.0',\n load: () => import('./group').then((module) => module.FieldGroup),\n // Contenedor: siembra un nodo por cada hijo declarado.\n seed: 'recursive',\n surfaces: ['form', 'display'],\n // Compuesto: además de su árbol dibuja el template de sus hijos, que sólo\n // existe en la plantilla del host. Él es quien decide recibirlo (R3.6).\n inputs: (config, extras) => ({\n field: config,\n tree: extras.ctx.tree,\n serverErrors: extras.ctx.serverErrors,\n pathPrefix: extras.ctx.pathPrefix,\n content: extras.groupContent,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `image` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'image',\n since: '1.0',\n load: () => import('./image').then((module) => module.FieldImage),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `key-value` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'key-value',\n since: '1.0',\n load: () => import('./key-value').then((module) => module.FieldKeyValue),\n seed: 'array',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `list` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'list',\n since: '1.0',\n load: () => import('./list').then((module) => module.FieldList),\n seed: 'array',\n surfaces: ['form', 'display'],\n // Compuesto: además de su árbol dibuja el template de cada hijo y hereda\n // el `exists` del nivel para no reactivar el borrado de filas (R3.6).\n inputs: (config, extras) => ({\n field: config,\n tree: extras.ctx.tree,\n childTemplate: extras.childTemplate,\n serverErrors: extras.ctx.serverErrors,\n pathPrefix: extras.ctx.pathPrefix,\n exists: extras.ctx.exists,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `multiselect` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'multiselect',\n since: '1.0',\n load: () => import('./multiselect').then((module) => module.FieldMultiselect),\n seed: 'array',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `number` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'number',\n since: '1.0',\n load: () => import('./number').then((module) => module.FieldNumber),\n // Nace como `null`: no puede ser `0`, que sería un valor ya cargado.\n seed: 'number',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `phone` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'phone',\n since: '1.0',\n load: () => import('./phone').then((module) => module.FieldPhone),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `readonly-text` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'readonly-text',\n since: '1.0',\n load: () => import('./readonly-text').then((module) => module.FieldReadonlyText),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `relation` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'relation',\n since: '1.0',\n load: () => import('./relation').then((module) => module.FieldRelation),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `select` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'select',\n since: '1.0',\n load: () => import('./select').then((module) => module.FieldSelect),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `slug` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'slug',\n since: '1.0',\n load: () => import('./slug').then((module) => module.FieldSlug),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n // Se aparta de la forma estándar sólo por `exists`: con el registro ya\n // creado el campo queda bloqueado (`lockAfterCreate`, R3.6).\n inputs: (config, extras) => ({\n field: config,\n tree: extras.ctx.tree,\n exists: extras.ctx.exists,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `string-list` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'string-list',\n since: '1.0',\n load: () => import('./string-list').then((module) => module.FieldStringList),\n seed: 'array',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `text` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'text',\n since: '1.0',\n load: () => import('./text').then((module) => module.FieldText),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `textarea` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'textarea',\n since: '1.0',\n load: () => import('./textarea').then((module) => module.FieldTextarea),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `time` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'time',\n since: '1.0',\n load: () => import('./time').then((module) => module.FieldTime),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `url` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'url',\n since: '1.0',\n load: () => import('./url').then((module) => module.FieldUrl),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../core/registry/render-registry';\nimport type { FieldType } from '../core/models/schema.model';\n\nimport { descriptor as booleanDescriptor } from './boolean/boolean.descriptor';\nimport { descriptor as currencyDescriptor } from './currency/currency.descriptor';\nimport { descriptor as dateDescriptor } from './date/date.descriptor';\nimport { descriptor as emailDescriptor } from './email/email.descriptor';\nimport { descriptor as fileDescriptor } from './file/file.descriptor';\nimport { descriptor as groupDescriptor } from './group/group.descriptor';\nimport { descriptor as imageDescriptor } from './image/image.descriptor';\nimport { descriptor as keyValueDescriptor } from './key-value/key-value.descriptor';\nimport { descriptor as listDescriptor } from './list/list.descriptor';\nimport { descriptor as multiselectDescriptor } from './multiselect/multiselect.descriptor';\nimport { descriptor as numberDescriptor } from './number/number.descriptor';\nimport { descriptor as phoneDescriptor } from './phone/phone.descriptor';\nimport { descriptor as readonlyTextDescriptor } from './readonly-text/readonly-text.descriptor';\nimport { descriptor as relationDescriptor } from './relation/relation.descriptor';\nimport { descriptor as selectDescriptor } from './select/select.descriptor';\nimport { descriptor as slugDescriptor } from './slug/slug.descriptor';\nimport { descriptor as stringListDescriptor } from './string-list/string-list.descriptor';\nimport { descriptor as textDescriptor } from './text/text.descriptor';\nimport { descriptor as textareaDescriptor } from './textarea/textarea.descriptor';\nimport { descriptor as timeDescriptor } from './time/time.descriptor';\nimport { descriptor as urlDescriptor } from './url/url.descriptor';\n\n/**\n * Tripwire de compilación (R1.7 · D17): que la unión `FieldType` y los\n * tipos que la UI sabe dibujar sigan diciendo lo mismo.\n *\n * · un valor que no esté en la unión `FieldSchema` ⇒ `satisfies` falla;\n * · un tipo de la unión que falte acá ⇒ `FaltaEnLaUi` no es `never`.\n *\n * **No es una fuente de verdad en runtime.** El catálogo vive en\n * `core/registry/render-registry.ts`, alimentado por los descriptores de\n * abajo. Esta lista sólo existe para que `npm run build` y `npm test` vean\n * la aserción por igual, sin depender de que un test la ejecute.\n *\n * **Exportada en R3.7** para que `core/registry/contract.spec.ts` pueda\n * verificar además el sentido que este archivo no cubre — que cada nombre de\n * la lista tenga **descriptor**: aquí sólo se comprueba que está en la unión,\n * y un tipo declarado en la lista pero ausente del manifiesto se vería en el\n * primer schema que lo use.\n *\n * **Mudado acá en R3.8.** Vivía en `field-host/field-host.ts`, y §0 es\n * explícito: «si añadir un tipo obliga a editar `FieldHost`, el motor no\n * está terminado». Estaba sin uso en runtime — sólo servía a este tripwire —\n * así que trasladarlo acá no cambia comportamiento y deja el registro y su\n * aserción en el **mismo archivo**: añadir un tipo toca una carpeta y una\n * línea de este manifiesto, y el host no se entera.\n */\nexport const SUPPORTED_FIELD_TYPE_LIST = [\n 'text',\n 'textarea',\n 'slug',\n 'number',\n 'currency',\n 'boolean',\n 'select',\n 'multiselect',\n 'url',\n 'email',\n 'phone',\n 'date',\n 'time',\n 'readonly-text',\n 'group',\n 'list',\n 'string-list',\n 'key-value',\n 'relation',\n 'image',\n 'file',\n] as const satisfies readonly FieldType[];\n\ntype FaltaEnLaUi = Exclude<FieldType, (typeof SUPPORTED_FIELD_TYPE_LIST)[number]>;\nconst _exhaustivo: FaltaEnLaUi extends never\n ? true\n : ['Falta en SUPPORTED_FIELD_TYPE_LIST', FaltaEnLaUi] = true;\n\n/**\n * Manifiesto de descriptores de campo (R3.3 · registro único de render).\n *\n * Una línea por tipo: el registro los recoge al importar este módulo. Es la\n * **única** lista manual del catálogo de campos — `import.meta.glob` no\n * sobrevive a `ng build`, así que D9-1 se resuelve con «registro manual de\n * una línea». Ver `core/registry/render-registry.ts`.\n *\n * Añadir un tipo de campo ⇒ crear la carpeta + su `.descriptor.ts` + una\n * línea acá. **Ningún host se toca** (R3.8).\n */\nexport const fieldDescriptors: readonly RenderDescriptor[] = [\n booleanDescriptor,\n currencyDescriptor,\n dateDescriptor,\n emailDescriptor,\n fileDescriptor,\n groupDescriptor,\n imageDescriptor,\n keyValueDescriptor,\n listDescriptor,\n multiselectDescriptor,\n numberDescriptor,\n phoneDescriptor,\n readonlyTextDescriptor,\n relationDescriptor,\n selectDescriptor,\n slugDescriptor,\n stringListDescriptor,\n textDescriptor,\n textareaDescriptor,\n timeDescriptor,\n urlDescriptor,\n];\n","import type { LayoutDescriptor } from '../core/registry/render-registry';\n\nexport const layoutDescriptors: readonly LayoutDescriptor[] = [\n { kind: 'layout', type: 'grid', since: '9.2' },\n { kind: 'layout', type: 'tabs', since: '9.2' },\n { kind: 'layout', type: 'sections', since: '9.2' },\n { kind: 'layout', type: 'steps', since: '9.2' },\n { kind: 'layout', type: 'columns', since: '9.2' },\n];\n","import type { BarDatum } from './bar-chart/bar-chart';\n\n/**\n * Normaliza el payload de un gráfico a `{ label: string, value: number }`.\n *\n * El dashboard recibe filas de una tabla y no todas llegan limpias, así que\n * el filtro se hace en el **borde** — en el descriptor, antes de que el dato\n * llegue al componente — y no dentro de cada gráfico. `bar-chart` y\n * `chart-line` comparten la misma regla, de ahí la función suelta.\n *\n * Devuelve `[]` ante cualquier cosa que no sea un array: un `metric-card`\n * pasado por error a un gráfico no puede romper la tarjeta.\n */\nexport function toSeries(data: unknown): BarDatum[] {\n if (!Array.isArray(data)) {\n return [];\n }\n return data\n .map((row): BarDatum | null => {\n if (typeof row !== 'object' || row === null) return null;\n const r = row as { label?: unknown; group?: unknown; value?: unknown };\n // El agregado agrupado del backend (R6.2) llega como { group, value };\n // la forma histórica de tabla es { label, value }. El gráfico sólo\n // entiende label ⇒ traducimos acá, en el borde.\n const label = typeof r.label === 'string' ? r.label : r.group;\n if (typeof label !== 'string' || typeof r.value !== 'number') return null;\n return { label, value: r.value };\n })\n .filter((row): row is BarDatum => row !== null);\n}\n\n/**\n * Normaliza el payload de un contador repartido por segmentos a\n * `{ clave: número }`.\n *\n * Dos formas, igual que `toSeries`:\n * - **payload** del panel: ya es un objeto plano de contadores por clave;\n * - **fuente declarada** (F8 · R6.5): una vista con `groupBy` devuelve filas\n * `[{ group, value }]` y cada grupo es el nombre de un segmento.\n *\n * `null` ante cualquier otra cosa: `StatusProgress` lo pinta como vacío en\n * vez de sumar un `[object Object]`.\n */\nexport function toCounters(data: unknown): Record<string, unknown> | null {\n if (Array.isArray(data)) {\n const out: Record<string, unknown> = {};\n for (const row of data) {\n if (typeof row !== 'object' || row === null) continue;\n const r = row as { group?: unknown; value?: unknown };\n if (typeof r.group === 'string' && 'value' in r) out[r.group] = r.value;\n }\n return out;\n }\n if (typeof data === 'object' && data !== null) return data as Record<string, unknown>;\n return null;\n}\n","import type { BarChartWidget } from '../../core/models/schema.model';\nimport type { RenderDescriptor } from '../../core/registry/render-registry';\n\nimport { toSeries } from '../series';\n\n/** Descriptor del widget `bar-chart` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'bar-chart',\n since: '1.0',\n load: () => import('./bar-chart').then((module) => module.BarChart),\n surfaces: ['display'],\n inputs: (config, extras) => {\n const cfg = config as BarChartWidget;\n return { label: cfg.label, data: toSeries(extras.data), unit: cfg.unit ?? '' };\n },\n};\n","import type { ChartLineWidget } from '../../core/models/schema.model';\nimport type { RenderDescriptor } from '../../core/registry/render-registry';\n\nimport { toSeries } from '../series';\n\n/** Descriptor del widget `chart-line` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'chart-line',\n since: '1.0',\n load: () => import('./chart-line').then((module) => module.ChartLine),\n surfaces: ['display'],\n inputs: (config, extras) => {\n const cfg = config as ChartLineWidget;\n return { label: cfg.label, data: toSeries(extras.data), unit: cfg.unit ?? '' };\n },\n};\n","import type { MetricCardWidget } from '../../core/models/schema.model';\nimport type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/**\n * El dato de un KPI es un ESCALAR y puede llegar de dos formas:\n *\n * - **payload**: `data[widget.key]` ya es la cifra (la resuelve el handler\n * del panel);\n * - **fuente declarada** (F8 · R6.5): una vista con `aggregate` devuelve\n * `[{ value: … }]` y la cifra es ese `value`.\n *\n * Cualquier otra forma ⇒ `null`, que `MetricCard` pinta como vacío. Se\n * traduce acá, en el borde — el componente no debería saber de dónde salió\n * el número.\n */\nfunction valorDeKpi(data: unknown): number | string | null {\n if (Array.isArray(data)) {\n const first = data[0];\n if (typeof first !== 'object' || first === null) return null;\n const value = (first as { value?: unknown }).value;\n return value === undefined || value === null ? null : (value as number | string);\n }\n if (data === undefined) return null;\n return data as number | string | null;\n}\n\n/** Descriptor del widget `metric-card` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'metric-card',\n since: '1.0',\n load: () => import('./metric-card').then((module) => module.MetricCard),\n surfaces: ['display'],\n // El KPI no recibe el widget entero: sólo vive de su rótulo, del dato y de\n // la nota al pie. Pasa el dato ya traducido — `MetricCard` distingue el\n // `0` de un vacío, y el `0` de un agregado llega como `[{ value: 0 }]`.\n inputs: (config, extras) => {\n const cfg = config as MetricCardWidget;\n return { label: cfg.label, value: valorDeKpi(extras.data), hint: cfg.hint ?? '' };\n },\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del widget `quick-actions` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'quick-actions',\n since: '1.0',\n load: () => import('./quick-actions').then((module) => module.QuickActions),\n surfaces: ['display'],\n // El único widget sin dato: sus botones salen todos del schema. Por eso no\n // recibe `data` — `ngComponentOutletInputs` no admite inputs inexistentes.\n inputs: (config) => ({ widget: config }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del widget `record-list` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'record-list',\n since: '1.0',\n load: () => import('./record-list').then((module) => module.RecordList),\n surfaces: ['display'],\n // `RecordList` dibuja filas: fuera de un array no hay nada que listar, y un\n // objeto suelto no debe colarse como si fuera una única fila.\n inputs: (config, extras) => ({\n widget: config,\n data: Array.isArray(extras.data) ? (extras.data as Record<string, unknown>[]) : null,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\nimport { toCounters } from '../series';\n\n/** Descriptor del widget `status-progress` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'status-progress',\n since: '1.0',\n load: () => import('./status-progress').then((module) => module.StatusProgress),\n surfaces: ['display'],\n // Contadores por clave: un objeto plano del payload O las filas de una\n // fuente con `groupBy` (F8 · R6.5) — `toCounters` traduce en el borde y\n // devuelve `null` si no hay nada que contar.\n inputs: (config, extras) => ({\n widget: config,\n data: toCounters(extras.data),\n }),\n};\n","import type { RenderDescriptor } from '../core/registry/render-registry';\n\nimport { descriptor as barChartDescriptor } from './bar-chart/bar-chart.descriptor';\nimport { descriptor as chartLineDescriptor } from './chart-line/chart-line.descriptor';\nimport { descriptor as metricCardDescriptor } from './metric-card/metric-card.descriptor';\nimport { descriptor as quickActionsDescriptor } from './quick-actions/quick-actions.descriptor';\nimport { descriptor as recordListDescriptor } from './record-list/record-list.descriptor';\nimport { descriptor as statusProgressDescriptor } from './status-progress/status-progress.descriptor';\n\n/**\n * Manifiesto de descriptores de widget (R3.6 · registro único de render).\n *\n * Mismo criterio que `fields/descriptors.ts`: una línea por tipo y el\n * registro los recoge al importar este módulo. Es la **única** lista manual\n * del catálogo de widgets — `import.meta.glob` no sobrevive a `ng build`\n * (D9-1), así que se mantiene el «registro manual de una línea».\n *\n * Añadir un widget ⇒ crear la carpeta + su `.descriptor.ts` + una línea\n * acá. **Ningún host se toca** (R3.8): si el componente sigue la forma\n * estándar `{widget, data}` ni siquiera hay que escribir `inputs`.\n */\nexport const widgetDescriptors: readonly RenderDescriptor[] = [\n barChartDescriptor,\n chartLineDescriptor,\n metricCardDescriptor,\n quickActionsDescriptor,\n recordListDescriptor,\n statusProgressDescriptor,\n];\n","import type { RenderDescriptor } from '../core/registry/render-registry';\n\nexport const viewDescriptors: readonly RenderDescriptor[] = [\n {\n kind: 'view',\n type: 'list',\n since: '9.1',\n load: async () => (await import('../pages/resource-page/resource-page')).ResourcePage,\n },\n {\n kind: 'view',\n type: 'detail',\n since: '9.1',\n load: async () => (await import('../pages/resource-page/resource-page')).ResourcePage,\n },\n {\n kind: 'view',\n type: 'form',\n since: '9.1',\n load: async () => (await import('../pages/form-view/form-view')).FormView,\n },\n {\n kind: 'view',\n type: 'dashboard',\n since: '9.1',\n load: async () => (await import('../pages/resource-page/resource-page')).ResourcePage,\n },\n {\n kind: 'view',\n type: 'calendar',\n since: '9.8',\n load: async () => (await import('./calendar-view/calendar-view')).CalendarView,\n },\n {\n kind: 'view',\n type: 'kanban',\n since: '9.8',\n load: async () => (await import('./kanban-view/kanban-view')).KanbanView,\n },\n {\n kind: 'view',\n type: 'tabs',\n since: '9.9',\n load: async () => (await import('./composer/composer-host')).ComposerHost,\n },\n];\n","import {\n isDevMode,\n makeEnvironmentProviders,\n provideAppInitializer,\n type EnvironmentProviders,\n type TemplateRef,\n type Type,\n} from '@angular/core';\n\nimport type { RootTree } from '../../fields/field-node';\nimport type { SeedKind } from '../../shared/utils/seed';\nimport { columnFormatDescriptors } from '../../columns/descriptors';\nimport { fieldDescriptors } from '../../fields/descriptors';\nimport { layoutDescriptors } from '../../layouts/descriptors';\nimport { widgetDescriptors } from '../../widgets/descriptors';\nimport { viewDescriptors } from '../../views/descriptors';\nimport type { FieldSchema, LayoutMode, ListColumn, WidgetSchema } from '../models/schema.model';\n\n/**\n * Registro único de render (R3.1 · `doc/plan-refactor-motor.md` §3.1).\n *\n * Un solo sitio declara qué componentes sabe dibujar la UI: cada tipo de\n * campo, widget, acción o vista aporta un `RenderDescriptor` y el registro\n * lo resuelve por `(kind, type)`. Los hosts dejan de mantener `Set`s de tipos\n * e imports a mano — **en runtime el registro es la única fuente de verdad**.\n *\n * Se carga a scope de módulo y no desde DI: `buildFormSchema()` es una\n * función pura que consulta el registro sin inyectar nada, así que un\n * registro llenado únicamente por un provider dejaría esa consulta correr\n * contra un catálogo vacío. `provideRenderDescriptors()` existe igualmente\n * como punto de entrada explícito para `app.config.ts` (R3.2).\n *\n * Autodescubrimiento (D9-1): se opta por **registro manual** — un manifiesto\n * con una línea por descriptor — porque `import.meta.glob` no está\n * implementada por el bundler de producción (`@angular/build:application`\n * usa esbuild) y la llamada cruda sobreviviría en `dist/`, rompiendo el\n * arranque. Con Vite sólo en el dev-server y en el runner de tests, además,\n * el comportamiento habría diferido entre `npm test` y `npm run build`.\n */\n\n/** Qué clase de cosa se está resolviendo. Identificador único dentro de su `kind`. */\nexport type RenderKind = 'field' | 'column-format' | 'widget' | 'action' | 'view' | 'layout';\nexport type ComponentRenderKind = Exclude<RenderKind, 'layout'>;\n\n/** Superficies en las que un descriptor puede aparecer. */\nexport type RenderSurface = 'form' | 'display' | 'list' | 'filter';\n\n/**\n * Qué puede describir un `RenderDescriptor`: un campo del formulario, un\n * widget del dashboard o una columna de listado (R4.3).\n *\n * Es el tipo del input `config` del host. Los descriptores sólo comparten\n * `key` y `label`; lo demás lo decide cada `kind`, y quien necesita el resto\n * lo castea con su propia mirada (`cfgCampo()`, que es lo que el host ya\n * hacía antes de que existieran las columnas).\n */\nexport type RenderConfig = FieldSchema | WidgetSchema | ListColumn;\n\n/**\n * Contexto de render que el host expone por DI (R3.4 · `doc/plan-refactor-motor.md` §3.4).\n *\n * Los controles viven en un componente distinto por cada tipo, así que en vez\n * de repetir cinco inputs en los 20 el host lo provee una sola vez y cada\n * control lo inyecta si lo necesita — el mismo criterio que ya usaba\n * `FIELD_ARIA`.\n *\n * `tree` viaja en el contexto y no como input suelto porque es **por\n * instancia**: los hijos de un `group` o de un `list` navegan su subárbol, no\n * el del padre.\n *\n * Vive acá y no en `render-host.ts` porque el contrato de los descriptores\n * (`RenderExtras`) necesita nombrarlo: el descriptor de un campo compuesto\n * decide qué se le pasa al componente y eso sólo es escribible conociendo el\n * tipo. `render-host.ts` lo re-exporta para no mover a sus consumidores.\n */\nexport interface RenderContext {\n /** Árbol del formulario del nivel de este campo. */\n tree: RootTree;\n /** Errores del backend por ruta (`address.street`, `faq[2].answer`). */\n serverErrors: Record<string, string>;\n /** Prefijo de ruta de este campo en el registro raíz (`address.`). */\n pathPrefix: string;\n /** Si el registro ya existe (lo consume `slug` con `lockAfterCreate`). */\n exists: boolean;\n /** Recurso dueño del formulario (lo consume `image` para la firma, §11.2). */\n resource: string;\n}\n\n/**\n * Contexto vacío con el que arranca `RenderHost.ctx`.\n *\n * Sólo lo alcanza quien renderiza sin contexto — los widgets, que no forman\n * parte de un formulario. Un campo montado sin `ctx` es un error de\n * programación y `childTree` lo dice con su propio mensaje, igual que lo\n * decía el `input.required` anterior.\n */\nexport const RENDER_CONTEXT_VACIO: RenderContext = {\n tree: undefined as unknown as RootTree,\n serverErrors: {},\n pathPrefix: '',\n exists: false,\n resource: '',\n};\n\n/**\n * Segundo argumento de `RenderDescriptor.inputs`.\n *\n * Aporta lo que el host conoce y el descriptor no: el contexto de formulario\n * (sólo para campos), el payload del dato (sólo para widgets) y los dos\n * templates con los que se componen `group` y `list`.\n */\nexport interface RenderExtras {\n /** Contexto del nivel; `RENDER_CONTEXT_VACIO` cuando no hay formulario. */\n ctx: RenderContext;\n /** Payload del widget (`<app-widget-host [data]>`); `null` en campos. */\n data: unknown;\n /** Template del contenido de un `group`. */\n groupContent?: TemplateRef<unknown>;\n /** Template de los hijos de un `list`. */\n childTemplate?: TemplateRef<unknown>;\n}\n\n/**\n * Declaración de un componente renderizable.\n *\n * No lleva `configSchema`: esa cláusula de `refactormotor.md` no existe en\n * el código y el plan la descartó (D17).\n */\nexport interface RenderDescriptor<C = unknown> {\n /** Clase de cosa. */\n kind: ComponentRenderKind;\n /** Identificador `kebab-case`, único dentro de `kind`. */\n type: string;\n /** Versión del contrato en que apareció (`doc/refactormotor.md`). */\n since: string;\n /**\n * Carga perezosa del componente. El registro se resuelve sin arrastrar el\n * bundle: los descriptores sólo guardan el cierre, no la clase.\n */\n load: () => Promise<Type<any>>;\n /** Superficies en las que se usa. Sólo lo que se pueda evidenciar en el código. */\n surfaces?: RenderSurface[];\n /** Valores por defecto de la configuración del descriptor. */\n defaults?: Partial<C>;\n /**\n * Forma del valor inicial que siembra este campo en el modelo del\n * formulario (D32 · R3.8).\n *\n * **Dato, no función**: un nombre resuelto por `shared/utils/seed.ts`, no\n * una clausura. Así el descriptor sigue siendo puro —`kind`, `type`,\n * `load`, `surfaces`, `seed`— y `seed.ts` es el único dueño de recorrer\n * un árbol de campos. La alternativa (`seed: (cfg) => …`) obligaría a que\n * `group` importara `seedFields`, lo que cerraría un ciclo\n * registro → manifiesto → descriptor → `seed` → registro.\n *\n * Sólo los campos lo declaran. **Obligatorio para `kind: 'field'`** — lo\n * vigila `core/registry/contract.spec.ts`, porque la exhaustividad de una\n * unión de strings no puede imponerse en tiempo de compilación.\n */\n seed?: SeedKind;\n /**\n * Cableado del componente: qué inputs recibe y con qué valor.\n *\n * Es **la** pieza que mantiene el host genérico: sin esto el motor tendría\n * que saber que `metric-card` recibe `label/value/hint` y que `group`\n * necesita además el template de sus hijos, y «añadir un tipo» volvería a\n * tocar `render-host.ts` (R3.8). Cada descriptor declara el suyo y el host\n * sólo hace `ngComponentOutletInputs = descriptor.inputs(config, extras)`.\n *\n * No es obligatorio: el 80 % de los tipos comparten la forma estándar de su\n * `kind` — `{field, tree}` en campos, `{widget, data}` en widgets — y para\n * ellos el host aplica esa forma por defecto. Se declara sólo cuando el\n * componente se desvía de ella.\n */\n inputs?: (config: C, extras: RenderExtras) => Record<string, unknown>;\n}\n\n/** Declaración de un layout manejado por el host de formularios. */\nexport interface LayoutDescriptor {\n kind: 'layout';\n type: LayoutMode;\n since: string;\n}\n\n/** Catálogo en memoria: `kind:type` → descriptor. */\nconst RENDERERS = new Map<string, RenderDescriptor>();\nconst LAYOUTS = new Map<string, LayoutDescriptor>();\n\nfunction keyOf(kind: RenderKind, type: string): string {\n return `${kind}:${type}`;\n}\n\n/**\n * Registra un descriptor. Idempotente por clave: volver a registrar el mismo\n * `(kind, type)` lo sobrescribe, lo que permite que un módulo se recargue en\n * desarrollo sin duplicar entradas.\n */\nexport function register(kind: 'layout', descriptor: LayoutDescriptor): void;\nexport function register(kind: ComponentRenderKind, descriptor: RenderDescriptor): void;\nexport function register(kind: RenderKind, descriptor: RenderDescriptor | LayoutDescriptor): void {\n if (kind === 'layout') {\n if (!('kind' in descriptor) || descriptor.kind !== 'layout') {\n throw new Error(`El descriptor de layout \"${descriptor.type}\" no declara kind: \"layout\".`);\n }\n LAYOUTS.set(descriptor.type, descriptor);\n return;\n }\n if (descriptor.kind === 'layout') {\n throw new Error(\n `El descriptor de layout \"${descriptor.type}\" no puede registrarse como ${kind}.`,\n );\n }\n RENDERERS.set(keyOf(kind, descriptor.type), descriptor);\n}\n\n/**\n * Quita un descriptor (R7.6). Simétrico a `register`: los specs que\n * registran entradas de prueba deben retirarlas, o el catálogo global queda\n * contaminado para cualquier otro spec que lea `renderKinds()` (flaky por\n * orden de ejecución).\n */\nexport function unregister(kind: RenderKind, type: string): void {\n if (kind === 'layout') {\n LAYOUTS.delete(type);\n } else {\n RENDERERS.delete(keyOf(kind, type));\n }\n}\n\n/** Descriptor declarado para `(kind, type)`, o `undefined` si nadie lo registró. */\nexport function get(kind: ComponentRenderKind, type: string): RenderDescriptor | undefined {\n return RENDERERS.get(keyOf(kind, type));\n}\n\n/** ¿La UI sabe dibujar este `(kind, type)`? Reemplaza a los `Set`s de los hosts. */\nexport function isSupported(kind: RenderKind, type: string): boolean {\n return kind === 'layout' ? LAYOUTS.has(type) : RENDERERS.has(keyOf(kind, type));\n}\n\n/**\n * Componente para `(kind, type)`, o `fallback` si el tipo no está en el\n * catálogo.\n *\n * Es **asíncrona** porque `load()` importa el componente bajo demanda; el\n * `fallback` es síncrono y sólo se usa cuando no hay descriptor. Un fallo de\n * `load()` NO se traga: propagarlo es lo que hace visible un descriptor mal\n * escrito, y la política «nunca pantalla rota» la resuelve el llamante con el\n * `fallback` que ya recibe.\n */\nexport async function resolve<C = unknown>(\n kind: ComponentRenderKind,\n type: string,\n fallback: Type<C>,\n): Promise<Type<C>> {\n const descriptor = get(kind, type);\n if (!descriptor) return fallback;\n return (await descriptor.load()) as Type<C>;\n}\n\n// Catálogo a scope de módulo: se registra al importar, sin esperar a DI.\nfor (const descriptor of [\n ...fieldDescriptors,\n ...widgetDescriptors,\n ...columnFormatDescriptors,\n ...viewDescriptors,\n ...layoutDescriptors,\n]) {\n if (descriptor.kind === 'layout') {\n register('layout', descriptor);\n } else {\n register(descriptor.kind, descriptor);\n }\n}\n\n/**\n * Punto de entrada para `app.config.ts` (R3.2).\n *\n * El catálogo ya quedó cargado al importar este módulo; el provider sirve\n * para que el arranque sea explícito y para avisar en desarrollo si el\n * manifiesto vino vacío — que es exactamente lo que pasaría si alguien\n * borra una línea del manifiesto sin reemplazarla.\n */\nexport function provideRenderDescriptors(): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideAppInitializer(() => {\n if (isDevMode() && RENDERERS.size === 0 && LAYOUTS.size === 0) {\n console.warn('[render-registry] el catálogo de descriptores vino vacío');\n }\n }),\n ]);\n}\n\n/** Cuántos descriptores hay registrados. Sólo para comprobaciones y tests. */\nexport function renderDescriptorCount(): number {\n return RENDERERS.size + LAYOUTS.size;\n}\n\n/**\n * `kind`s que hoy tienen al menos un descriptor.\n *\n * Existe para el test de exhaustividad (R3.7): él recorre los schemas de los\n * `kind`s que tienen registro y **falla** si aparece un `kind` con descriptor\n * que nadie recorre — de lo contrario «tener registro» y «estar cubierto»\n * serían dos afirmaciones distintas que nadie reconcilia.\n */\nexport function renderKinds(): RenderKind[] {\n const kinds = new Set<RenderKind>([...RENDERERS.values()].map((descriptor) => descriptor.kind));\n if (LAYOUTS.size > 0) {\n kinds.add('layout');\n }\n return [...kinds];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tinya-code-gas-admin-ui-render-registry-CcIgBsSl.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/columns/descriptors.ts","../../../projects/gas-admin-ui/src/lib/fields/boolean/boolean.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/currency/currency.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/date/date.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/email/email.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/file/file.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/group/group.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/image/image.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/key-value/key-value.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/list/list.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/multiselect/multiselect.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/number/number.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/phone/phone.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/readonly-text/readonly-text.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/relation/relation.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/select/select.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/slug/slug.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/string-list/string-list.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/text/text.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/textarea/textarea.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/time/time.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/url/url.descriptor.ts","../../../projects/gas-admin-ui/src/lib/fields/descriptors.ts","../../../projects/gas-admin-ui/src/lib/layouts/descriptors.ts","../../../projects/gas-admin-ui/src/lib/widgets/series.ts","../../../projects/gas-admin-ui/src/lib/widgets/bar-chart/bar-chart.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/chart-line/chart-line.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/metric-card/metric-card.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/quick-actions/quick-actions.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/record-list/record-list.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/status-progress/status-progress.descriptor.ts","../../../projects/gas-admin-ui/src/lib/widgets/descriptors.ts","../../../projects/gas-admin-ui/src/lib/views/descriptors.ts","../../../projects/gas-admin-ui/src/lib/core/registry/render-registry.ts"],"sourcesContent":["import type { RenderDescriptor, RenderExtras } from '../core/registry/render-registry';\n\n/**\n * Manifiesto de descriptores de formato de columna (R4.3 · `kind:\n * 'column-format'`).\n *\n * Es el equivalente a `fields/descriptors.ts` y `widgets/descriptors.ts`:\n * una línea por formato y ningún import de componente — cada `load()` guarda\n * el cierre de un `import()` **con literal**, que es lo que permite al\n * bundler partir el chunk; el registro los recoge al importar este módulo\n * (`core/registry/render-registry.ts`).\n *\n * **Añadir un formato** ⇒ crear `format-<nombre>.ts` + una entrada acá.\n * `list-view` no se toca: ése es el criterio de éxito de R4.3.\n *\n * Todos comparten el cableado: el descriptor sólo expone la celda, que la\n * vista cuelga de `extras.data`.\n */\nconst inputs = (_config: unknown, extras: RenderExtras): Record<string, unknown> => ({\n cell: extras.data,\n});\n\nconst text: RenderDescriptor = {\n kind: 'column-format',\n type: 'text',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-text').then((module) => module.ColumnFormatText),\n inputs,\n};\n\nconst badge: RenderDescriptor = {\n kind: 'column-format',\n type: 'badge',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-badge').then((module) => module.ColumnFormatBadge),\n inputs,\n};\n\nconst thumbnail: RenderDescriptor = {\n kind: 'column-format',\n type: 'thumbnail',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-thumbnail').then((module) => module.ColumnFormatThumbnail),\n inputs,\n};\n\nconst boolean: RenderDescriptor = {\n kind: 'column-format',\n type: 'boolean',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-boolean').then((module) => module.ColumnFormatBoolean),\n inputs,\n};\n\nconst number: RenderDescriptor = {\n kind: 'column-format',\n type: 'number',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-number').then((module) => module.ColumnFormatNumber),\n inputs,\n};\n\nconst currency: RenderDescriptor = {\n kind: 'column-format',\n type: 'currency',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-currency').then((module) => module.ColumnFormatCurrency),\n inputs,\n};\n\nconst date: RenderDescriptor = {\n kind: 'column-format',\n type: 'date',\n since: '1.0',\n surfaces: ['list'],\n load: () => import('./format-date').then((module) => module.ColumnFormatDate),\n inputs,\n};\n\n/** Los 7 formatos del `@switch` que este registro reemplaza (R4.3). */\nexport const columnFormatDescriptors: readonly RenderDescriptor[] = [\n text,\n badge,\n thumbnail,\n boolean,\n number,\n currency,\n date,\n];\n\n/**\n * Los 7 nombres, como dato.\n *\n * Va en paralelo a `ListColumnFormat` (unión de `schema.model`) y lo vigila\n * `core/registry/contract.spec.ts`: la unión vive en tipos y el registro en\n * runtime, y sólo un test reconcilia los dos — el mismo tripwire que ya\n * existe para los 20 campos y los 6 widgets.\n */\nexport const COLUMN_FORMAT_TYPES = [\n 'text',\n 'badge',\n 'thumbnail',\n 'boolean',\n 'number',\n 'currency',\n 'date',\n] as const;\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `boolean` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'boolean',\n since: '1.0',\n load: () => import('./boolean').then((module) => module.FieldBoolean),\n seed: 'boolean',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `currency` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'currency',\n since: '1.0',\n load: () => import('./currency').then((module) => module.FieldCurrency),\n // Nace como `null`: no puede ser `0`, que sería un valor ya cargado.\n seed: 'number',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `date` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'date',\n since: '1.0',\n load: () => import('./date').then((module) => module.FieldDate),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `email` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'email',\n since: '1.0',\n load: () => import('./email').then((module) => module.FieldEmail),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `file` (R3.3 · R9.5.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'file',\n since: '9.5',\n load: () => import('./file').then((module) => module.FieldFile),\n // Nace como cadena: la URL pública es el valor, igual que en `image`.\n seed: 'string',\n surfaces: ['form', 'display'],\n // Como `image` (D27): control propio cuya sincronización con Signal Forms\n // vive en ɵɵcontrol, así que el host lo declara a mano con `[formField]`.\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `group` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'group',\n since: '1.0',\n load: () => import('./group').then((module) => module.FieldGroup),\n // Contenedor: siembra un nodo por cada hijo declarado.\n seed: 'recursive',\n surfaces: ['form', 'display'],\n // Compuesto: además de su árbol dibuja el template de sus hijos, que sólo\n // existe en la plantilla del host. Él es quien decide recibirlo (R3.6).\n inputs: (config, extras) => ({\n field: config,\n tree: extras.ctx.tree,\n serverErrors: extras.ctx.serverErrors,\n pathPrefix: extras.ctx.pathPrefix,\n content: extras.groupContent,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `image` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'image',\n since: '1.0',\n load: () => import('./image').then((module) => module.FieldImage),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `key-value` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'key-value',\n since: '1.0',\n load: () => import('./key-value').then((module) => module.FieldKeyValue),\n seed: 'array',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `list` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'list',\n since: '1.0',\n load: () => import('./list').then((module) => module.FieldList),\n seed: 'array',\n surfaces: ['form', 'display'],\n // Compuesto: además de su árbol dibuja el template de cada hijo y hereda\n // el `exists` del nivel para no reactivar el borrado de filas (R3.6).\n inputs: (config, extras) => ({\n field: config,\n tree: extras.ctx.tree,\n childTemplate: extras.childTemplate,\n serverErrors: extras.ctx.serverErrors,\n pathPrefix: extras.ctx.pathPrefix,\n exists: extras.ctx.exists,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `multiselect` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'multiselect',\n since: '1.0',\n load: () => import('./multiselect').then((module) => module.FieldMultiselect),\n seed: 'array',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `number` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'number',\n since: '1.0',\n load: () => import('./number').then((module) => module.FieldNumber),\n // Nace como `null`: no puede ser `0`, que sería un valor ya cargado.\n seed: 'number',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `phone` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'phone',\n since: '1.0',\n load: () => import('./phone').then((module) => module.FieldPhone),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `readonly-text` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'readonly-text',\n since: '1.0',\n load: () => import('./readonly-text').then((module) => module.FieldReadonlyText),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `relation` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'relation',\n since: '1.0',\n load: () => import('./relation').then((module) => module.FieldRelation),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `select` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'select',\n since: '1.0',\n load: () => import('./select').then((module) => module.FieldSelect),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `slug` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'slug',\n since: '1.0',\n load: () => import('./slug').then((module) => module.FieldSlug),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n // Se aparta de la forma estándar sólo por `exists`: con el registro ya\n // creado el campo queda bloqueado (`lockAfterCreate`, R3.6).\n inputs: (config, extras) => ({\n field: config,\n tree: extras.ctx.tree,\n exists: extras.ctx.exists,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `string-list` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'string-list',\n since: '1.0',\n load: () => import('./string-list').then((module) => module.FieldStringList),\n seed: 'array',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `text` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'text',\n since: '1.0',\n load: () => import('./text').then((module) => module.FieldText),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `textarea` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'textarea',\n since: '1.0',\n load: () => import('./textarea').then((module) => module.FieldTextarea),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `time` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'time',\n since: '1.0',\n load: () => import('./time').then((module) => module.FieldTime),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del campo `url` (R3.3). */\nexport const descriptor: RenderDescriptor = {\n kind: 'field',\n type: 'url',\n since: '1.0',\n load: () => import('./url').then((module) => module.FieldUrl),\n // Nace como cadena: los textos, fechas, urls, selects y relacionales.\n seed: 'string',\n surfaces: ['form', 'display'],\n};\n","import type { RenderDescriptor } from '../core/registry/render-registry';\nimport type { FieldType } from '../core/models/schema.model';\n\nimport { descriptor as booleanDescriptor } from './boolean/boolean.descriptor';\nimport { descriptor as currencyDescriptor } from './currency/currency.descriptor';\nimport { descriptor as dateDescriptor } from './date/date.descriptor';\nimport { descriptor as emailDescriptor } from './email/email.descriptor';\nimport { descriptor as fileDescriptor } from './file/file.descriptor';\nimport { descriptor as groupDescriptor } from './group/group.descriptor';\nimport { descriptor as imageDescriptor } from './image/image.descriptor';\nimport { descriptor as keyValueDescriptor } from './key-value/key-value.descriptor';\nimport { descriptor as listDescriptor } from './list/list.descriptor';\nimport { descriptor as multiselectDescriptor } from './multiselect/multiselect.descriptor';\nimport { descriptor as numberDescriptor } from './number/number.descriptor';\nimport { descriptor as phoneDescriptor } from './phone/phone.descriptor';\nimport { descriptor as readonlyTextDescriptor } from './readonly-text/readonly-text.descriptor';\nimport { descriptor as relationDescriptor } from './relation/relation.descriptor';\nimport { descriptor as selectDescriptor } from './select/select.descriptor';\nimport { descriptor as slugDescriptor } from './slug/slug.descriptor';\nimport { descriptor as stringListDescriptor } from './string-list/string-list.descriptor';\nimport { descriptor as textDescriptor } from './text/text.descriptor';\nimport { descriptor as textareaDescriptor } from './textarea/textarea.descriptor';\nimport { descriptor as timeDescriptor } from './time/time.descriptor';\nimport { descriptor as urlDescriptor } from './url/url.descriptor';\n\n/**\n * Tripwire de compilación (R1.7 · D17): que la unión `FieldType` y los\n * tipos que la UI sabe dibujar sigan diciendo lo mismo.\n *\n * · un valor que no esté en la unión `FieldSchema` ⇒ `satisfies` falla;\n * · un tipo de la unión que falte acá ⇒ `FaltaEnLaUi` no es `never`.\n *\n * **No es una fuente de verdad en runtime.** El catálogo vive en\n * `core/registry/render-registry.ts`, alimentado por los descriptores de\n * abajo. Esta lista sólo existe para que `npm run build` y `npm test` vean\n * la aserción por igual, sin depender de que un test la ejecute.\n *\n * **Exportada en R3.7** para que `core/registry/contract.spec.ts` pueda\n * verificar además el sentido que este archivo no cubre — que cada nombre de\n * la lista tenga **descriptor**: aquí sólo se comprueba que está en la unión,\n * y un tipo declarado en la lista pero ausente del manifiesto se vería en el\n * primer schema que lo use.\n *\n * **Mudado acá en R3.8.** Vivía en `field-host/field-host.ts`, y §0 es\n * explícito: «si añadir un tipo obliga a editar `FieldHost`, el motor no\n * está terminado». Estaba sin uso en runtime — sólo servía a este tripwire —\n * así que trasladarlo acá no cambia comportamiento y deja el registro y su\n * aserción en el **mismo archivo**: añadir un tipo toca una carpeta y una\n * línea de este manifiesto, y el host no se entera.\n */\nexport const SUPPORTED_FIELD_TYPE_LIST = [\n 'text',\n 'textarea',\n 'slug',\n 'number',\n 'currency',\n 'boolean',\n 'select',\n 'multiselect',\n 'url',\n 'email',\n 'phone',\n 'date',\n 'time',\n 'readonly-text',\n 'group',\n 'list',\n 'string-list',\n 'key-value',\n 'relation',\n 'image',\n 'file',\n] as const satisfies readonly FieldType[];\n\ntype FaltaEnLaUi = Exclude<FieldType, (typeof SUPPORTED_FIELD_TYPE_LIST)[number]>;\nconst _exhaustivo: FaltaEnLaUi extends never\n ? true\n : ['Falta en SUPPORTED_FIELD_TYPE_LIST', FaltaEnLaUi] = true;\n\n/**\n * Manifiesto de descriptores de campo (R3.3 · registro único de render).\n *\n * Una línea por tipo: el registro los recoge al importar este módulo. Es la\n * **única** lista manual del catálogo de campos — `import.meta.glob` no\n * sobrevive a `ng build`, así que D9-1 se resuelve con «registro manual de\n * una línea». Ver `core/registry/render-registry.ts`.\n *\n * Añadir un tipo de campo ⇒ crear la carpeta + su `.descriptor.ts` + una\n * línea acá. **Ningún host se toca** (R3.8).\n */\nexport const fieldDescriptors: readonly RenderDescriptor[] = [\n booleanDescriptor,\n currencyDescriptor,\n dateDescriptor,\n emailDescriptor,\n fileDescriptor,\n groupDescriptor,\n imageDescriptor,\n keyValueDescriptor,\n listDescriptor,\n multiselectDescriptor,\n numberDescriptor,\n phoneDescriptor,\n readonlyTextDescriptor,\n relationDescriptor,\n selectDescriptor,\n slugDescriptor,\n stringListDescriptor,\n textDescriptor,\n textareaDescriptor,\n timeDescriptor,\n urlDescriptor,\n];\n","import type { LayoutDescriptor } from '../core/registry/render-registry';\n\nexport const layoutDescriptors: readonly LayoutDescriptor[] = [\n { kind: 'layout', type: 'grid', since: '9.2' },\n { kind: 'layout', type: 'tabs', since: '9.2' },\n { kind: 'layout', type: 'sections', since: '9.2' },\n { kind: 'layout', type: 'steps', since: '9.2' },\n { kind: 'layout', type: 'columns', since: '9.2' },\n];\n","import type { BarDatum } from './bar-chart/bar-chart';\n\n/**\n * Normaliza el payload de un gráfico a `{ label: string, value: number }`.\n *\n * El dashboard recibe filas de una tabla y no todas llegan limpias, así que\n * el filtro se hace en el **borde** — en el descriptor, antes de que el dato\n * llegue al componente — y no dentro de cada gráfico. `bar-chart` y\n * `chart-line` comparten la misma regla, de ahí la función suelta.\n *\n * Devuelve `[]` ante cualquier cosa que no sea un array: un `metric-card`\n * pasado por error a un gráfico no puede romper la tarjeta.\n */\nexport function toSeries(data: unknown): BarDatum[] {\n if (!Array.isArray(data)) {\n return [];\n }\n return data\n .map((row): BarDatum | null => {\n if (typeof row !== 'object' || row === null) return null;\n const r = row as { label?: unknown; group?: unknown; value?: unknown };\n // El agregado agrupado del backend (R6.2) llega como { group, value };\n // la forma histórica de tabla es { label, value }. El gráfico sólo\n // entiende label ⇒ traducimos acá, en el borde.\n const label = typeof r.label === 'string' ? r.label : r.group;\n if (typeof label !== 'string' || typeof r.value !== 'number') return null;\n return { label, value: r.value };\n })\n .filter((row): row is BarDatum => row !== null);\n}\n\n/**\n * Normaliza el payload de un contador repartido por segmentos a\n * `{ clave: número }`.\n *\n * Dos formas, igual que `toSeries`:\n * - **payload** del panel: ya es un objeto plano de contadores por clave;\n * - **fuente declarada** (F8 · R6.5): una vista con `groupBy` devuelve filas\n * `[{ group, value }]` y cada grupo es el nombre de un segmento.\n *\n * `null` ante cualquier otra cosa: `StatusProgress` lo pinta como vacío en\n * vez de sumar un `[object Object]`.\n */\nexport function toCounters(data: unknown): Record<string, unknown> | null {\n if (Array.isArray(data)) {\n const out: Record<string, unknown> = {};\n for (const row of data) {\n if (typeof row !== 'object' || row === null) continue;\n const r = row as { group?: unknown; value?: unknown };\n if (typeof r.group === 'string' && 'value' in r) out[r.group] = r.value;\n }\n return out;\n }\n if (typeof data === 'object' && data !== null) return data as Record<string, unknown>;\n return null;\n}\n","import type { BarChartWidget } from '../../core/models/schema.model';\nimport type { RenderDescriptor } from '../../core/registry/render-registry';\n\nimport { toSeries } from '../series';\n\n/** Descriptor del widget `bar-chart` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'bar-chart',\n since: '1.0',\n load: () => import('./bar-chart').then((module) => module.BarChart),\n surfaces: ['display'],\n inputs: (config, extras) => {\n const cfg = config as BarChartWidget;\n return { label: cfg.label, data: toSeries(extras.data), unit: cfg.unit ?? '' };\n },\n};\n","import type { ChartLineWidget } from '../../core/models/schema.model';\nimport type { RenderDescriptor } from '../../core/registry/render-registry';\n\nimport { toSeries } from '../series';\n\n/** Descriptor del widget `chart-line` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'chart-line',\n since: '1.0',\n load: () => import('./chart-line').then((module) => module.ChartLine),\n surfaces: ['display'],\n inputs: (config, extras) => {\n const cfg = config as ChartLineWidget;\n return { label: cfg.label, data: toSeries(extras.data), unit: cfg.unit ?? '' };\n },\n};\n","import type { MetricCardWidget } from '../../core/models/schema.model';\nimport type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/**\n * El dato de un KPI es un ESCALAR y puede llegar de dos formas:\n *\n * - **payload**: `data[widget.key]` ya es la cifra (la resuelve el handler\n * del panel);\n * - **fuente declarada** (F8 · R6.5): una vista con `aggregate` devuelve\n * `[{ value: … }]` y la cifra es ese `value`.\n *\n * Cualquier otra forma ⇒ `null`, que `MetricCard` pinta como vacío. Se\n * traduce acá, en el borde — el componente no debería saber de dónde salió\n * el número.\n */\nfunction valorDeKpi(data: unknown): number | string | null {\n if (Array.isArray(data)) {\n const first = data[0];\n if (typeof first !== 'object' || first === null) return null;\n const value = (first as { value?: unknown }).value;\n return value === undefined || value === null ? null : (value as number | string);\n }\n if (data === undefined) return null;\n return data as number | string | null;\n}\n\n/** Descriptor del widget `metric-card` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'metric-card',\n since: '1.0',\n load: () => import('./metric-card').then((module) => module.MetricCard),\n surfaces: ['display'],\n // El KPI no recibe el widget entero: sólo vive de su rótulo, del dato y de\n // la nota al pie. Pasa el dato ya traducido — `MetricCard` distingue el\n // `0` de un vacío, y el `0` de un agregado llega como `[{ value: 0 }]`.\n inputs: (config, extras) => {\n const cfg = config as MetricCardWidget;\n return { label: cfg.label, value: valorDeKpi(extras.data), hint: cfg.hint ?? '' };\n },\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del widget `quick-actions` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'quick-actions',\n since: '1.0',\n load: () => import('./quick-actions').then((module) => module.QuickActions),\n surfaces: ['display'],\n // El único widget sin dato: sus botones salen todos del schema. Por eso no\n // recibe `data` — `ngComponentOutletInputs` no admite inputs inexistentes.\n inputs: (config) => ({ widget: config }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\n/** Descriptor del widget `record-list` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'record-list',\n since: '1.0',\n load: () => import('./record-list').then((module) => module.RecordList),\n surfaces: ['display'],\n // `RecordList` dibuja filas: fuera de un array no hay nada que listar, y un\n // objeto suelto no debe colarse como si fuera una única fila.\n inputs: (config, extras) => ({\n widget: config,\n data: Array.isArray(extras.data) ? (extras.data as Record<string, unknown>[]) : null,\n }),\n};\n","import type { RenderDescriptor } from '../../core/registry/render-registry';\n\nimport { toCounters } from '../series';\n\n/** Descriptor del widget `status-progress` (R3.6). */\nexport const descriptor: RenderDescriptor = {\n kind: 'widget',\n type: 'status-progress',\n since: '1.0',\n load: () => import('./status-progress').then((module) => module.StatusProgress),\n surfaces: ['display'],\n // Contadores por clave: un objeto plano del payload O las filas de una\n // fuente con `groupBy` (F8 · R6.5) — `toCounters` traduce en el borde y\n // devuelve `null` si no hay nada que contar.\n inputs: (config, extras) => ({\n widget: config,\n data: toCounters(extras.data),\n }),\n};\n","import type { RenderDescriptor } from '../core/registry/render-registry';\n\nimport { descriptor as barChartDescriptor } from './bar-chart/bar-chart.descriptor';\nimport { descriptor as chartLineDescriptor } from './chart-line/chart-line.descriptor';\nimport { descriptor as metricCardDescriptor } from './metric-card/metric-card.descriptor';\nimport { descriptor as quickActionsDescriptor } from './quick-actions/quick-actions.descriptor';\nimport { descriptor as recordListDescriptor } from './record-list/record-list.descriptor';\nimport { descriptor as statusProgressDescriptor } from './status-progress/status-progress.descriptor';\n\n/**\n * Manifiesto de descriptores de widget (R3.6 · registro único de render).\n *\n * Mismo criterio que `fields/descriptors.ts`: una línea por tipo y el\n * registro los recoge al importar este módulo. Es la **única** lista manual\n * del catálogo de widgets — `import.meta.glob` no sobrevive a `ng build`\n * (D9-1), así que se mantiene el «registro manual de una línea».\n *\n * Añadir un widget ⇒ crear la carpeta + su `.descriptor.ts` + una línea\n * acá. **Ningún host se toca** (R3.8): si el componente sigue la forma\n * estándar `{widget, data}` ni siquiera hay que escribir `inputs`.\n */\nexport const widgetDescriptors: readonly RenderDescriptor[] = [\n barChartDescriptor,\n chartLineDescriptor,\n metricCardDescriptor,\n quickActionsDescriptor,\n recordListDescriptor,\n statusProgressDescriptor,\n];\n","import type { RenderDescriptor } from '../core/registry/render-registry';\n\nexport const viewDescriptors: readonly RenderDescriptor[] = [\n {\n kind: 'view',\n type: 'list',\n since: '9.1',\n load: async () => (await import('../pages/resource-page/resource-page')).ResourcePage,\n },\n {\n kind: 'view',\n type: 'detail',\n since: '9.1',\n load: async () => (await import('../pages/resource-page/resource-page')).ResourcePage,\n },\n {\n kind: 'view',\n type: 'form',\n since: '9.1',\n load: async () => (await import('../pages/form-view/form-view')).FormView,\n },\n {\n kind: 'view',\n type: 'dashboard',\n since: '9.1',\n load: async () => (await import('../pages/resource-page/resource-page')).ResourcePage,\n },\n {\n kind: 'view',\n type: 'calendar',\n since: '9.8',\n load: async () => (await import('./calendar-view/calendar-view')).CalendarView,\n },\n {\n kind: 'view',\n type: 'kanban',\n since: '9.8',\n load: async () => (await import('./kanban-view/kanban-view')).KanbanView,\n },\n {\n kind: 'view',\n type: 'tabs',\n since: '9.9',\n load: async () => (await import('./composer/composer-host')).ComposerHost,\n },\n];\n","import {\n isDevMode,\n makeEnvironmentProviders,\n provideAppInitializer,\n type EnvironmentProviders,\n type TemplateRef,\n type Type,\n} from '@angular/core';\n\nimport type { RootTree } from '../../fields/field-node';\nimport type { SeedKind } from '../../shared/utils/seed';\nimport { columnFormatDescriptors } from '../../columns/descriptors';\nimport { fieldDescriptors } from '../../fields/descriptors';\nimport { layoutDescriptors } from '../../layouts/descriptors';\nimport { widgetDescriptors } from '../../widgets/descriptors';\nimport { viewDescriptors } from '../../views/descriptors';\nimport type { FieldSchema, LayoutMode, ListColumn, WidgetSchema } from '../models/schema.model';\n\n/**\n * Registro único de render (R3.1 · `doc/plan-refactor-motor.md` §3.1).\n *\n * Un solo sitio declara qué componentes sabe dibujar la UI: cada tipo de\n * campo, widget, acción o vista aporta un `RenderDescriptor` y el registro\n * lo resuelve por `(kind, type)`. Los hosts dejan de mantener `Set`s de tipos\n * e imports a mano — **en runtime el registro es la única fuente de verdad**.\n *\n * Se carga a scope de módulo y no desde DI: `buildFormSchema()` es una\n * función pura que consulta el registro sin inyectar nada, así que un\n * registro llenado únicamente por un provider dejaría esa consulta correr\n * contra un catálogo vacío. `provideRenderDescriptors()` existe igualmente\n * como punto de entrada explícito para `app.config.ts` (R3.2).\n *\n * Autodescubrimiento (D9-1): se opta por **registro manual** — un manifiesto\n * con una línea por descriptor — porque `import.meta.glob` no está\n * implementada por el bundler de producción (`@angular/build:application`\n * usa esbuild) y la llamada cruda sobreviviría en `dist/`, rompiendo el\n * arranque. Con Vite sólo en el dev-server y en el runner de tests, además,\n * el comportamiento habría diferido entre `npm test` y `npm run build`.\n */\n\n/** Qué clase de cosa se está resolviendo. Identificador único dentro de su `kind`. */\nexport type RenderKind = 'field' | 'column-format' | 'widget' | 'action' | 'view' | 'layout';\nexport type ComponentRenderKind = Exclude<RenderKind, 'layout'>;\n\n/** Superficies en las que un descriptor puede aparecer. */\nexport type RenderSurface = 'form' | 'display' | 'list' | 'filter';\n\n/**\n * Qué puede describir un `RenderDescriptor`: un campo del formulario, un\n * widget del dashboard o una columna de listado (R4.3).\n *\n * Es el tipo del input `config` del host. Los descriptores sólo comparten\n * `key` y `label`; lo demás lo decide cada `kind`, y quien necesita el resto\n * lo castea con su propia mirada (`cfgCampo()`, que es lo que el host ya\n * hacía antes de que existieran las columnas).\n */\nexport type RenderConfig = FieldSchema | WidgetSchema | ListColumn;\n\n/**\n * Contexto de render que el host expone por DI (R3.4 · `doc/plan-refactor-motor.md` §3.4).\n *\n * Los controles viven en un componente distinto por cada tipo, así que en vez\n * de repetir cinco inputs en los 20 el host lo provee una sola vez y cada\n * control lo inyecta si lo necesita — el mismo criterio que ya usaba\n * `FIELD_ARIA`.\n *\n * `tree` viaja en el contexto y no como input suelto porque es **por\n * instancia**: los hijos de un `group` o de un `list` navegan su subárbol, no\n * el del padre.\n *\n * Vive acá y no en `render-host.ts` porque el contrato de los descriptores\n * (`RenderExtras`) necesita nombrarlo: el descriptor de un campo compuesto\n * decide qué se le pasa al componente y eso sólo es escribible conociendo el\n * tipo. `render-host.ts` lo re-exporta para no mover a sus consumidores.\n */\nexport interface RenderContext {\n /** Árbol del formulario del nivel de este campo. */\n tree: RootTree;\n /** Errores del backend por ruta (`address.street`, `faq[2].answer`). */\n serverErrors: Record<string, string>;\n /** Prefijo de ruta de este campo en el registro raíz (`address.`). */\n pathPrefix: string;\n /** Si el registro ya existe (lo consume `slug` con `lockAfterCreate`). */\n exists: boolean;\n /** Recurso dueño del formulario (lo consume `image` para la firma, §11.2). */\n resource: string;\n}\n\n/**\n * Contexto vacío con el que arranca `RenderHost.ctx`.\n *\n * Sólo lo alcanza quien renderiza sin contexto — los widgets, que no forman\n * parte de un formulario. Un campo montado sin `ctx` es un error de\n * programación y `childTree` lo dice con su propio mensaje, igual que lo\n * decía el `input.required` anterior.\n */\nexport const RENDER_CONTEXT_VACIO: RenderContext = {\n tree: undefined as unknown as RootTree,\n serverErrors: {},\n pathPrefix: '',\n exists: false,\n resource: '',\n};\n\n/**\n * Segundo argumento de `RenderDescriptor.inputs`.\n *\n * Aporta lo que el host conoce y el descriptor no: el contexto de formulario\n * (sólo para campos), el payload del dato (sólo para widgets) y los dos\n * templates con los que se componen `group` y `list`.\n */\nexport interface RenderExtras {\n /** Contexto del nivel; `RENDER_CONTEXT_VACIO` cuando no hay formulario. */\n ctx: RenderContext;\n /** Payload del widget (`<app-widget-host [data]>`); `null` en campos. */\n data: unknown;\n /** Template del contenido de un `group`. */\n groupContent?: TemplateRef<unknown>;\n /** Template de los hijos de un `list`. */\n childTemplate?: TemplateRef<unknown>;\n}\n\n/**\n * Declaración de un componente renderizable.\n *\n * No lleva `configSchema`: esa cláusula de `refactormotor.md` no existe en\n * el código y el plan la descartó (D17).\n */\nexport interface RenderDescriptor<C = unknown> {\n /** Clase de cosa. */\n kind: ComponentRenderKind;\n /** Identificador `kebab-case`, único dentro de `kind`. */\n type: string;\n /** Versión del contrato en que apareció (`doc/refactormotor.md`). */\n since: string;\n /**\n * Carga perezosa del componente. El registro se resuelve sin arrastrar el\n * bundle: los descriptores sólo guardan el cierre, no la clase.\n */\n load: () => Promise<Type<any>>;\n /** Superficies en las que se usa. Sólo lo que se pueda evidenciar en el código. */\n surfaces?: RenderSurface[];\n /** Valores por defecto de la configuración del descriptor. */\n defaults?: Partial<C>;\n /**\n * Forma del valor inicial que siembra este campo en el modelo del\n * formulario (D32 · R3.8).\n *\n * **Dato, no función**: un nombre resuelto por `shared/utils/seed.ts`, no\n * una clausura. Así el descriptor sigue siendo puro —`kind`, `type`,\n * `load`, `surfaces`, `seed`— y `seed.ts` es el único dueño de recorrer\n * un árbol de campos. La alternativa (`seed: (cfg) => …`) obligaría a que\n * `group` importara `seedFields`, lo que cerraría un ciclo\n * registro → manifiesto → descriptor → `seed` → registro.\n *\n * Sólo los campos lo declaran. **Obligatorio para `kind: 'field'`** — lo\n * vigila `core/registry/contract.spec.ts`, porque la exhaustividad de una\n * unión de strings no puede imponerse en tiempo de compilación.\n */\n seed?: SeedKind;\n /**\n * Cableado del componente: qué inputs recibe y con qué valor.\n *\n * Es **la** pieza que mantiene el host genérico: sin esto el motor tendría\n * que saber que `metric-card` recibe `label/value/hint` y que `group`\n * necesita además el template de sus hijos, y «añadir un tipo» volvería a\n * tocar `render-host.ts` (R3.8). Cada descriptor declara el suyo y el host\n * sólo hace `ngComponentOutletInputs = descriptor.inputs(config, extras)`.\n *\n * No es obligatorio: el 80 % de los tipos comparten la forma estándar de su\n * `kind` — `{field, tree}` en campos, `{widget, data}` en widgets — y para\n * ellos el host aplica esa forma por defecto. Se declara sólo cuando el\n * componente se desvía de ella.\n */\n inputs?: (config: C, extras: RenderExtras) => Record<string, unknown>;\n}\n\n/** Declaración de un layout manejado por el host de formularios. */\nexport interface LayoutDescriptor {\n kind: 'layout';\n type: LayoutMode;\n since: string;\n}\n\n/** Catálogo en memoria: `kind:type` → descriptor. */\nconst RENDERERS = new Map<string, RenderDescriptor>();\nconst LAYOUTS = new Map<string, LayoutDescriptor>();\n\nfunction keyOf(kind: RenderKind, type: string): string {\n return `${kind}:${type}`;\n}\n\n/**\n * Registra un descriptor. Idempotente por clave: volver a registrar el mismo\n * `(kind, type)` lo sobrescribe, lo que permite que un módulo se recargue en\n * desarrollo sin duplicar entradas.\n */\nexport function register(kind: 'layout', descriptor: LayoutDescriptor): void;\nexport function register(kind: ComponentRenderKind, descriptor: RenderDescriptor): void;\nexport function register(kind: RenderKind, descriptor: RenderDescriptor | LayoutDescriptor): void {\n if (kind === 'layout') {\n if (!('kind' in descriptor) || descriptor.kind !== 'layout') {\n throw new Error(`El descriptor de layout \"${descriptor.type}\" no declara kind: \"layout\".`);\n }\n LAYOUTS.set(descriptor.type, descriptor);\n return;\n }\n if (descriptor.kind === 'layout') {\n throw new Error(\n `El descriptor de layout \"${descriptor.type}\" no puede registrarse como ${kind}.`,\n );\n }\n RENDERERS.set(keyOf(kind, descriptor.type), descriptor);\n}\n\n/**\n * Quita un descriptor (R7.6). Simétrico a `register`: los specs que\n * registran entradas de prueba deben retirarlas, o el catálogo global queda\n * contaminado para cualquier otro spec que lea `renderKinds()` (flaky por\n * orden de ejecución).\n */\nexport function unregister(kind: RenderKind, type: string): void {\n if (kind === 'layout') {\n LAYOUTS.delete(type);\n } else {\n RENDERERS.delete(keyOf(kind, type));\n }\n}\n\n/** Descriptor declarado para `(kind, type)`, o `undefined` si nadie lo registró. */\nexport function get(kind: ComponentRenderKind, type: string): RenderDescriptor | undefined {\n return RENDERERS.get(keyOf(kind, type));\n}\n\n/** ¿La UI sabe dibujar este `(kind, type)`? Reemplaza a los `Set`s de los hosts. */\nexport function isSupported(kind: RenderKind, type: string): boolean {\n return kind === 'layout' ? LAYOUTS.has(type) : RENDERERS.has(keyOf(kind, type));\n}\n\n/**\n * Componente para `(kind, type)`, o `fallback` si el tipo no está en el\n * catálogo.\n *\n * Es **asíncrona** porque `load()` importa el componente bajo demanda; el\n * `fallback` es síncrono y sólo se usa cuando no hay descriptor. Un fallo de\n * `load()` NO se traga: propagarlo es lo que hace visible un descriptor mal\n * escrito, y la política «nunca pantalla rota» la resuelve el llamante con el\n * `fallback` que ya recibe.\n */\nexport async function resolve<C = unknown>(\n kind: ComponentRenderKind,\n type: string,\n fallback: Type<C>,\n): Promise<Type<C>> {\n const descriptor = get(kind, type);\n if (!descriptor) return fallback;\n return (await descriptor.load()) as Type<C>;\n}\n\n// Catálogo a scope de módulo: se registra al importar, sin esperar a DI.\nfor (const descriptor of [\n ...fieldDescriptors,\n ...widgetDescriptors,\n ...columnFormatDescriptors,\n ...viewDescriptors,\n ...layoutDescriptors,\n]) {\n if (descriptor.kind === 'layout') {\n register('layout', descriptor);\n } else {\n register(descriptor.kind, descriptor);\n }\n}\n\n/**\n * Punto de entrada para `app.config.ts` (R3.2).\n *\n * El catálogo ya quedó cargado al importar este módulo; el provider sirve\n * para que el arranque sea explícito y para avisar en desarrollo si el\n * manifiesto vino vacío — que es exactamente lo que pasaría si alguien\n * borra una línea del manifiesto sin reemplazarla.\n */\nexport function provideRenderDescriptors(): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideAppInitializer(() => {\n if (isDevMode() && RENDERERS.size === 0 && LAYOUTS.size === 0) {\n console.warn('[render-registry] el catálogo de descriptores vino vacío');\n }\n }),\n ]);\n}\n\n/** Cuántos descriptores hay registrados. Sólo para comprobaciones y tests. */\nexport function renderDescriptorCount(): number {\n return RENDERERS.size + LAYOUTS.size;\n}\n\n/**\n * `kind`s que hoy tienen al menos un descriptor.\n *\n * Existe para el test de exhaustividad (R3.7): él recorre los schemas de los\n * `kind`s que tienen registro y **falla** si aparece un `kind` con descriptor\n * que nadie recorre — de lo contrario «tener registro» y «estar cubierto»\n * serían dos afirmaciones distintas que nadie reconcilia.\n */\nexport function renderKinds(): RenderKind[] {\n const kinds = new Set<RenderKind>([...RENDERERS.values()].map((descriptor) => descriptor.kind));\n if (LAYOUTS.size > 0) {\n kinds.add('layout');\n }\n return [...kinds];\n}\n"],"mappings":"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"}
|
|
@@ -91,10 +91,12 @@ var AuthService = class AuthService {
|
|
|
91
91
|
next: () => {
|
|
92
92
|
this.clear();
|
|
93
93
|
this.firebaseAdapter?.signOut();
|
|
94
|
+
this.goLogin();
|
|
94
95
|
},
|
|
95
96
|
error: () => {
|
|
96
97
|
this.clear();
|
|
97
98
|
this.firebaseAdapter?.signOut();
|
|
99
|
+
this.goLogin();
|
|
98
100
|
}
|
|
99
101
|
}), map(() => void 0));
|
|
100
102
|
}
|
|
@@ -316,6 +318,7 @@ var ApiService = class ApiService {
|
|
|
316
318
|
auth = inject(AuthService);
|
|
317
319
|
driver = inject(API_DRIVER, { optional: true }) ?? "gas";
|
|
318
320
|
valueSources = inject(VALUE_SOURCES, { optional: true }) ?? {};
|
|
321
|
+
firebaseAuthAdapter = inject(FIREBASE_AUTH_ADAPTER, { optional: true });
|
|
319
322
|
injector = inject(Injector);
|
|
320
323
|
list(endpoint, payload) {
|
|
321
324
|
return this.send(endpoint.list, "GET", payload).pipe(map((raw) => unwrapList(raw)));
|
|
@@ -369,9 +372,13 @@ var ApiService = class ApiService {
|
|
|
369
372
|
}
|
|
370
373
|
sendRest(path, method, payload) {
|
|
371
374
|
const url = this.buildUrl(path);
|
|
372
|
-
|
|
375
|
+
const headers = new HttpHeaders();
|
|
376
|
+
if (this.firebaseAuthAdapter?.getIdToken) return from(this.firebaseAuthAdapter.getIdToken()).pipe(switchMap((freshToken) => this.doRestRequest(url, method, payload, headers.set("Authorization", `Bearer ${freshToken}`))));
|
|
373
377
|
const token = this.auth.token();
|
|
374
|
-
|
|
378
|
+
const finalHeaders = token ? headers.set("Authorization", `Bearer ${token}`) : headers;
|
|
379
|
+
return this.doRestRequest(url, method, payload, finalHeaders);
|
|
380
|
+
}
|
|
381
|
+
doRestRequest(url, method, payload, headers) {
|
|
375
382
|
if (method === "GET" || method === "DELETE") {
|
|
376
383
|
const params = toHttpParams(payload);
|
|
377
384
|
return this.http.request(method, url, {
|
|
@@ -379,9 +386,8 @@ var ApiService = class ApiService {
|
|
|
379
386
|
params
|
|
380
387
|
});
|
|
381
388
|
}
|
|
382
|
-
headers = headers.set("Content-Type", "application/json");
|
|
383
389
|
return this.http.request(method, url, {
|
|
384
|
-
headers,
|
|
390
|
+
headers: headers.set("Content-Type", "application/json"),
|
|
385
391
|
body: payload ?? {}
|
|
386
392
|
});
|
|
387
393
|
}
|
|
@@ -540,4 +546,4 @@ function provideFirebaseAdapter(adapter) {
|
|
|
540
546
|
}
|
|
541
547
|
export { FIREBASE_AUTH_ADAPTER as _, provideFirebaseAdapter as a, renewedTokenOf as b, ConfigService as c, AuthService as d, LOGIN_PATH as f, API_URL as g, API_DRIVER as h, getSchema as i, ApiService as l, SESSION_USER_KEY as m, allSchemas as n, provideGasAdmin as o, SESSION_TOKEN_KEY as p, getAuth as r, setSchemaCatalog as s, GAS_SCHEMAS as t, apiErrorInterceptor as u, VALUE_SOURCES as v, ApiError as y };
|
|
542
548
|
|
|
543
|
-
//# sourceMappingURL=tinya-code-gas-admin-ui-schema-catalog-
|
|
549
|
+
//# sourceMappingURL=tinya-code-gas-admin-ui-schema-catalog-DU_6_CtP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tinya-code-gas-admin-ui-schema-catalog-DU_6_CtP.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/core/models/api.model.ts","../../../projects/gas-admin-ui/src/lib/core/tokens.ts","../../../projects/gas-admin-ui/src/lib/core/services/auth.service.ts","../../../projects/gas-admin-ui/src/lib/core/interceptors/api-error.interceptor.ts","../../../projects/gas-admin-ui/src/lib/core/utils/context-resolver.ts","../../../projects/gas-admin-ui/src/lib/core/services/api.service.ts","../../../projects/gas-admin-ui/src/lib/core/services/config.service.ts","../../../projects/gas-admin-ui/src/lib/core/registry/schema-catalog.ts"],"sourcesContent":["/**\n * Contrato HTTP con el backend (doc/baseapi.md §8 «Errores»).\n *\n * El backend responde SIEMPRE HTTP 200; los errores viajan en el body\n * con `{ error: { status, message, errors?: [{ path, message }] } }`.\n * El interceptor (Fase 3) los convierte en `ApiError`.\n */\n\n/**\n * Un error con ruta del422 (auth.md §6): `path` para pintarlo en el campo y\n * `code` MAYUS_SNAKE (`VALIDATION_MIN_LENGTH`) para mapearlo sin leer texto.\n */\nexport interface ApiIssue {\n /** Ruta del campo afectado (`faq[2].answer`, `sku`). */\n path: string;\n /** Mensaje en español, listo para pintar. */\n message: string;\n /** Código estable del validador (B11); el mensaje puede cambiar, el código no. */\n code?: string;\n}\n\n/** Shape del body de error (doc/baseapi.md §8 «Errores»). */\nexport interface ApiErrorBody {\n error: {\n status: number;\n message: string;\n /** Ruta del campo afectado cuando el backend la envía (base.md §10). */\n path?: string;\n /**\n * Código de sesión de §4.5 (R2.9): `AUTH_REQUIRED` · `AUTH_INVALID` ·\n * `AUTH_EXPIRED` · `FORBIDDEN` · `RATE_LIMITED`. Aparece SÓLO en esas\n * cinco respuestas — el sobre de error es aditivo (O7 / D10-5).\n */\n code?: string;\n /**\n * Errores de validación con ruta (base.md §5, guía §10): el 422 los manda\n * ACÁ (`error.errors[]`), no en `path`. Sin modelarlos el form veía\n * «Errores de validación» genérico y ningún campo se pintaba (D42).\n */\n errors?: ApiIssue[];\n };\n}\n\n/** Error de API estandarizado que lanza el interceptor de errores. */\nexport class ApiError extends Error {\n readonly status: number;\n /** Ruta del campo que provocó el error (ej. `faq[2].answer`), si existe. */\n readonly fieldPath?: string;\n /** Código de sesión de §4.5 si el backend lo envió; `undefined` en el resto. */\n readonly code?: string;\n /** Issues con ruta (422); vacío cuando el error no trae `errors[]`. */\n readonly issues: readonly ApiIssue[];\n\n constructor(\n status: number,\n message: string,\n fieldPath?: string,\n code?: string,\n issues: readonly ApiIssue[] = [],\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.fieldPath = fieldPath;\n this.code = code;\n this.issues = issues;\n }\n}\n\n/**\n * `meta.renewedToken` (auth.md §3.2/§3.4): el back lo adjunta a la respuesta\n * cuando la vida del token bajó del umbral de renovación. Es el ÚNICO modo\n * de que el front se entere — el token nuevo no viaja en cabeceras ni en la\n * URL. Devuelve `null` cuando no hay renovación (que es lo normal).\n *\n * Vive acá y no en el `ApiService` porque lo consumen DOS servicios\n * (`ApiService` y `AuthService`) y éste modelo no importa a ninguno de los\n * dos: sin este archivo no hay ciclo de imports.\n */\nexport function renewedTokenOf(body: unknown): string | null {\n if (body === null || typeof body !== 'object') return null;\n const meta = (body as { meta?: unknown }).meta;\n if (meta === null || typeof meta !== 'object') return null;\n const token = (meta as { renewedToken?: unknown }).renewedToken;\n return typeof token === 'string' && token !== '' ? token : null;\n}\n","import { InjectionToken } from '@angular/core';\n\n/**\n * URL base de la API (baseapi §2.3): es la URL directa del Web App de Apps\n * Script; ahí se hace el `POST` del envelope `{ token, method, path, payload }`.\n * OBLIGATORIO proporcionarlo en `app.config.ts`:\n * `{ provide: API_URL, useValue: 'https://…' }` (Fase 3). Vacío = mismo origen.\n */\nexport const API_URL = new InjectionToken<string>('API_URL');\n\n/**\n * Superficie mínima que `gas-admin-ui` usa de Firebase Auth.\n * Implementada por la aplicación consumidora; inyectada vía `FIREBASE_AUTH_ADAPTER`.\n * Mantener la interfaz fina evita acoplar la librería a una versión específica de Firebase.\n */\nexport interface FirebaseAuthAdapter {\n /**\n * Abre un popup de Google sign-in y resuelve al ID token de Firebase.\n * Rechaza si el usuario cancela o hay error.\n */\n signInWithGoogle(): Promise<string>;\n\n /**\n * Inicia sesión con email y contraseña vía Firebase y resuelve al ID token.\n * Se usa cuando provider === 'password' Y firebase está configurado (Firebase email auth).\n */\n signInWithEmailAndPassword(email: string, password: string): Promise<string>;\n\n /**\n * Cierra sesión en Firebase (llamado junto al clear de la sesión de la app).\n */\n signOut(): Promise<void>;\n\n /**\n * Obtiene un ID token de Firebase FRESCO para usarlo en la cabecera\n * `Authorization: Bearer <token>` de las peticiones REST.\n *\n * OPCIONAL: si el adaptador no lo implementa, `gas-admin-ui` envía el token\n * de sesión almacenado tal cual (comportamiento actual). Implementarlo\n * permite refrescar el ID token antes de cada petición REST, evitando\n * errores \"ID token inválido\" tras la expiración de Firebase (1 hora).\n *\n * El adaptador decide cuándo refrescar (p. ej. `getIdToken(true)` de Firebase\n * SDK fuerza la renovación; sin argumento devuelve el cacheado si aún es válido).\n */\n getIdToken?(): Promise<string>;\n}\n\n/**\n * Token DI opcional para el adaptador de Firebase Auth.\n * Proporcionado por la aplicación consumidora cuando usa proveedores federados o Firebase Auth.\n */\nexport const FIREBASE_AUTH_ADAPTER = new InjectionToken<FirebaseAuthAdapter>(\n 'gas.admin.firebaseAuthAdapter',\n);\n\n/**\n * Driver de transporte HTTP del motor.\n *\n * Vive acá (capa de infraestructura) y NO en `ResourceSchema` (capa de dominio)\n * porque es una decisión de red, no de modelo de datos. Cambiar el driver\n * no debería requerir tocar ningún schema de recursos.\n *\n * - `'gas'` (default): tunnel `POST text/plain` con envelope\n * `{ token, method, path, payload }` — contrato de Google Apps Script.\n * - `'rest'`: verbos HTTP reales (`GET`, `POST`, `PUT`, `DELETE`) con\n * `Authorization: Bearer <token>` en cabeceras y params en la URL (GET/DELETE)\n * o en el body JSON (POST/PUT/PATCH).\n *\n * Se fija en `provideGasAdmin({ driver })`. Sin declaración explícita el\n * motor usa `'gas'` — cero impacto para consumidores existentes.\n */\nexport const API_DRIVER = new InjectionToken<'gas' | 'rest'>('gas.admin.driver');\n\n/**\n * Registro de FUENTES DE CONTEXTO para marcadores en paths de endpoints.\n *\n * Cada clave es el nombre de una fuente — `{ <fuente> }` o\n * `{ <fuente>.<ruta.anidada> }` dentro de un path declarado en\n * `ResourceEndpoints` — y cada valor es un getter sin argumentos que devuelve\n * el dato ACTUAL en el momento de la petición (no al registrar).\n *\n * El motor ya aporta dos fuentes construidas y éstas NO se declaran acá:\n *\n * - `auth` → la sesión que dueña `AuthService` (usuario y token).\n * - `config` → el mapa global que puebla `ConfigService` desde los singletons.\n *\n * Este registro es sólo para fuentes que aporta el consumidor, para que el\n * motor no conozca nombres de dominio: qué campo se lee lo decide el schema,\n * no el engine.\n *\n * Un marcador que apunte a una fuente no registrada, o a un valor ausente,\n * revienta con un error que nombra el path y el marcador — jamás se manda un\n * segmento vacío al backend.\n */\nexport type ValueSources = Readonly<Record<string, () => unknown>>;\n\nexport const VALUE_SOURCES = new InjectionToken<ValueSources>('gas.admin.valueSources');\n\n","import { HttpClient, HttpHeaders } from '@angular/common/http';\nimport { computed, inject, Service, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { catchError, from, map, Observable, of, switchMap, tap, throwError } from 'rxjs';\n\nimport type { FieldSchema, ResourceSchema } from '../models/schema.model';\nimport { renewedTokenOf } from '../models/api.model';\nimport { API_DRIVER, API_URL, FIREBASE_AUTH_ADAPTER } from '../tokens';\nimport { getAuth } from '../registry/schema-catalog';\n\n\n/**\n * Sesión del admin (auth.md §5.2) — **única dueña** del token y del usuario.\n *\n * Qué vive acá y por qué:\n *\n * - **Señales** `user` / `token` / `isAuthenticated` / `role`: el único\n * estado que el resto de la app lee (guards, menú, interceptor).\n * - **`login()` / `logout()` / `logoutAll()` / `restore()`**: operaciones de sesión.\n * El `AuthService` NO inyecta `ApiService` — sólo `HttpClient` — porque\n * `ApiService` inyecta a éste para leer el token, y un ciclo de DI en\n * Angular es un error en tiempo de arranque, no un warning.\n * - **`applyAuthCode()` (R2.9): EL único intérprete de la tabla de §4.5.**\n * Ningún otro archivo decide qué pasa ante `AUTH_*` o `FORBIDDEN`.\n * - **Almacenamiento (R2.12): `sessionStorage`, siempre.** `rememberMe`\n * no puede usar `localStorage` porque D10-3 lo negó — ver más abajo.\n *\n * La sesión arranca desde el storage y `restore()` la revalida contra\n * `/auth/me` en el initializer: un usuario desactivado mientras la pestaña\n * estaba abierta cae al primer reload, no en mitad de una edición.\n */\n\n/** Usuario tal y como lo sirve `publicUser_` (lista blanca, R2.17). */\nexport interface SessionUser {\n id?: string;\n email?: string;\n name?: string;\n role?: string;\n active?: boolean;\n last_login_at?: string;\n created_at?: string;\n}\n\n/** Los cinco códigos de §4.5. El resto de errores no son de sesión. */\nexport type AuthErrorCode =\n 'AUTH_REQUIRED' | 'AUTH_INVALID' | 'AUTH_EXPIRED' | 'FORBIDDEN' | 'RATE_LIMITED';\n\n/** Ruta de login: fuera del shell para que ningún guard la proteja. */\nexport const LOGIN_PATH = '/login';\n\n/**\n * Claves del `sessionStorage`. Exportadas porque **la sesión se restaura\n * desde ahí en el constructor**: un test que quiera una sesión arrancando\n * tiene que sembrarlas antes de inyectar el servicio, y el que las use con\n * otro nombre quebraría el restore en silencio.\n */\nexport const SESSION_TOKEN_KEY = 'admin.session.token';\nexport const SESSION_USER_KEY = 'admin.session.user';\n\ninterface LoginResponse {\n token: string;\n user: SessionUser;\n}\ninterface MeResponse {\n user: SessionUser;\n}\n\n@Service()\nexport class AuthService {\n private readonly http = inject(HttpClient);\n private readonly apiUrl = inject(API_URL);\n private readonly router = inject(Router);\n private readonly firebaseAdapter = inject(FIREBASE_AUTH_ADAPTER, { optional: true });\n private readonly driver = inject(API_DRIVER, { optional: true }) ?? 'gas';\n\n /**\n * D10-3: **sólo `sessionStorage`.** La opción «recordarme» con\n * `localStorage` quedó denegada en §9.3 — muere con la pestaña ⇒ cero\n * token legible por un XSS futuro. `authSchema.rememberMe` es `false`\n * por la misma razón: declarar `true` sería prometer un storage que\n * este servicio no usa.\n *\n * Nunca en la URL ni en el cuerpo de un GET (§5.4): el token sólo viaja\n * dentro del envelope `{ token, method, path, payload }`.\n */\n private readonly storage: Storage = sessionStorage;\n\n private readonly _user = signal<SessionUser | null>(null);\n private readonly _token = signal<string | null>(null);\n /** Ruta previa a un `AUTH_EXPIRED`, para volver tras reloguear (§4.5). */\n private readonly _returnUrl = signal<string | null>(null);\n\n readonly user = this._user.asReadonly();\n readonly token = this._token.asReadonly();\n readonly returnUrl = this._returnUrl.asReadonly();\n /** Sólo hay sesión con token Y usuario: uno sin el otro es estado corrupto. */\n readonly isAuthenticated = computed(() => this._token() !== null && this._user() !== null);\n readonly role = computed(() => this._user()?.role ?? '');\n\n constructor() {\n const token = this.storage.getItem(SESSION_TOKEN_KEY);\n if (token !== null && token !== '') this._token.set(token);\n const raw = this.storage.getItem(SESSION_USER_KEY);\n if (raw !== null && raw !== '') {\n try {\n this._user.set(JSON.parse(raw) as SessionUser);\n } catch {\n // JSON corrupto ⇒ no hay sesión que restaurar; storage sucio.\n this.storage.removeItem(SESSION_USER_KEY);\n this.storage.removeItem(SESSION_TOKEN_KEY);\n this._token.set(null);\n }\n }\n }\n\n // ── Sesión ───────────────────────────────────────────────────────────────\n\n login(email: string, password: string): Observable<SessionUser> {\n return this.post<LoginResponse>('/auth/login', { email, password }).pipe(\n map((res) => {\n this.open(res.token, res.user);\n const renewed = renewedTokenOf(res);\n if (renewed !== null) this.applyRenewedToken(renewed);\n return res.user;\n }),\n );\n }\n\n /**\n * Inicia sesión con Google mediante el adaptador de Firebase y canjea\n * el ID token contra el backend.\n */\n loginWithGoogle(): Observable<SessionUser> {\n if (!this.firebaseAdapter) {\n return throwError(() => new Error('[gas-admin-ui] FIREBASE_AUTH_ADAPTER is not provided.'));\n }\n return from(this.firebaseAdapter.signInWithGoogle()).pipe(\n switchMap((idToken) => this.exchangeFirebaseToken(idToken)),\n );\n }\n\n /**\n * Inicia sesión con correo y contraseña en Firebase y canjea el ID token\n * resultante contra el backend.\n */\n loginWithFirebasePassword(email: string, password: string): Observable<SessionUser> {\n if (!this.firebaseAdapter) {\n return throwError(() => new Error('[gas-admin-ui] FIREBASE_AUTH_ADAPTER is not provided.'));\n }\n return from(this.firebaseAdapter.signInWithEmailAndPassword(email, password)).pipe(\n switchMap((idToken) => this.exchangeFirebaseToken(idToken)),\n );\n }\n\n /**\n * Canjea un token de identidad de Firebase por la sesión propia de la app,\n * manteniendo idéntico el contrato del envelope y almacenamiento en sessionStorage.\n */\n private exchangeFirebaseToken(idToken: string): Observable<SessionUser> {\n const endpoint = getAuth()?.firebase?.exchangeEndpoint ?? '/auth/firebase';\n return this.post<LoginResponse>(endpoint, { idToken }).pipe(\n map((res) => {\n this.open(res.token, res.user);\n const renewed = renewedTokenOf(res);\n if (renewed !== null) this.applyRenewedToken(renewed);\n return res.user;\n }),\n );\n }\n\n /**\n * Revocación best-effort (§9.7): se pide al back que ponga el `sid` en la\n * lista de revocados, pero **la sesión local se ciere haya o no respuesta**.\n * Un logout que «falla» por red y deja al usuario dentro es peor que uno\n * que deja una sesión muerta en el servidor.\n */\n logout(): Observable<void> {\n return this.post<{ loggedOut?: boolean }>('/auth/logout').pipe(\n tap({\n next: () => {\n this.clear();\n void this.firebaseAdapter?.signOut();\n this.goLogin();\n },\n error: () => {\n // La sesión local se cierra haya o no respuesta del back (§9.7).\n // Firebase también se cierra best-effort y se va al login igual.\n this.clear();\n void this.firebaseAdapter?.signOut();\n this.goLogin();\n },\n }),\n map(() => undefined),\n );\n }\n\n\n /** Revoca las sesiones del usuario en todos sus dispositivos. */\n logoutAll(): Observable<{ effectiveWithinSeconds: number }> {\n return this.post<{ sessionsRevoked: boolean; effectiveWithinSeconds: number }>(\n '/auth/logout-all',\n ).pipe(\n tap(() => {\n this.clear();\n this.goLogin();\n }),\n map((res) => ({ effectiveWithinSeconds: res.effectiveWithinSeconds })),\n );\n }\n\n /** Revalida la sesión restaurada contra `/auth/me`; sin token ⇒ sin viaje. */\n restore(): Observable<void> {\n if (this._token() === null) return of(undefined);\n return this.post<MeResponse>('/auth/me').pipe(\n tap((res) => {\n this.open(this._token(), res.user);\n }),\n // El interceptor ya interpretó el `AUTH_*` (R2.9) antes de llegar\n // acá: esto sólo evita que un fallo rompa el arranque de la app.\n catchError(() => of(undefined)),\n map(() => undefined),\n );\n }\n\n /**\n * Renovación deslizante (§3.2): el back manda `meta.renewedToken` cuando\n * la vida del token bajó del umbral. Se persiste el MISMO día que el\n * token original — el `sid` no cambia, sólo se emite una firma nueva.\n */\n applyRenewedToken(token: string): void {\n if (token === '' || this._token() === null) return;\n this._token.set(token);\n this.storage.setItem(SESSION_TOKEN_KEY, token);\n }\n\n // ── R2.9 · la tabla de §4.5, en un solo sitio ───────────────────────────\n\n /**\n * ÚNICO intérprete de los códigos de sesión (auth.md §4.5 / §5.2).\n * El interceptor `apiErrorInterceptor` lo llama y nada más: cualquier\n * archivo que decida «si ve AUTH_EXPIRED hago X» está fuera de contrato.\n *\n * | Código | Qué hace |\n * | --------------- | ------------------------------------------- |\n * | `AUTH_REQUIRED` | limpia sesión y va a login |\n * | `AUTH_INVALID` | limpia sesión y va a login |\n * | `AUTH_EXPIRED` | guarda la ruta previa, limpia, va a login |\n * | `FORBIDDEN` | **no** cierra sesión (el usuario sigue dentro) |\n * | `RATE_LIMITED` | no hace nada: lo espera el formulario |\n */\n applyAuthCode(code: string | undefined): void {\n switch (code) {\n case 'AUTH_EXPIRED':\n // La ruta previa se guarda ANTES de borrar: después de limpiar no\n // queda desde dónde navegar (y `router.url` ya cambió al llegar).\n this._returnUrl.set(this.currentUrl());\n this.clear();\n this.goLogin();\n break;\n case 'AUTH_REQUIRED':\n case 'AUTH_INVALID':\n // «La sesión terminó» — no hay vuelta atrás que conservar.\n this.clear();\n this.goLogin();\n break;\n case 'FORBIDDEN':\n case 'RATE_LIMITED':\n // Decisión explícita de NO tocar la sesión: §4.5 exige que el\n // usuario vea «sin permiso» o «espera» **siguiendo dentro**.\n break;\n default:\n // No es un código de sesión: nada que interpretar.\n break;\n }\n }\n\n // ── Helpers ──────────────────────────────────────────────────────────────\n\n /** ¿El rol de la sesión cubre `required`? (mínimo: admin ≥ staff, §4.4) */\n hasRole(required: string): boolean {\n if (required === '' || required === 'session') return this.isAuthenticated();\n const role = this.role();\n if (role === 'admin') return true;\n return role !== '' && role === required;\n }\n\n /**\n * §5.3 — el ÚNICO criterio de visibilidad del front (menú, rutas y\n * botones). `requires` viene del vocabulario de §4.4: `public` no pide\n * sesión; todo lo demás sí; `roles` estrecha.\n *\n * Es **pura decisión de UI**. El back re-verifica `accessLevel` en cada\n * petición (R2.6), así que filtrar acá no protege nada: sólo evita\n * enseñarle a alguien un botón que le va a devolver 403.\n */\n canAccess(access?: { requires?: string; roles?: string[] }): boolean {\n const requires = access?.requires ?? 'session';\n if (requires === 'public') return true;\n if (!this.isAuthenticated()) return false;\n\n // §4.4 escribe el requerimiento como `role:admin`; §5.3 como\n // `roles: ['admin']`. Se aceptan las dos formas en lugar de elegir una:\n // son el mismo dato visto desde el nivel de acceso y desde el recurso.\n const role = requires.startsWith('role:') ? requires.slice('role:'.length) : '';\n const required = role === '' ? (access?.roles ?? []) : [role];\n if (required.length === 0) return true;\n return required.some((one) => this.hasRole(one));\n }\n\n // ── R7.5 · Consumo de políticas en UI (F10) ───────────────────────────────\n\n /**\n * Evalúa si el usuario puede ejecutar una operación específica según\n * `policies.access.operations` del recurso (B8 · R7.4a).\n *\n * El override por operación tiene prioridad sobre el umbral del grupo.\n * Si no hay override, se usa el umbral `policies.access.read` o\n * `policies.access.write` según la operación.\n *\n * Fallback a `permissions` (legacy) si `policies` no existe: mientras\n * los schemas locales no tengan `policies`, se mantiene compatibilidad.\n *\n * Es **pura decisión de UI**. El back vuelve a verificar en cada petición\n * (24-crud), así que esto sólo evita mostrar botones que darían 403.\n */\n canDoOperation(\n schema: ResourceSchema,\n operation: 'list' | 'get' | 'create' | 'update' | 'remove',\n ): boolean {\n // R7.5: intentar policies primero\n const ops = schema.policies?.access?.operations;\n if (ops && ops[operation]) {\n return this.meetsLevel(ops[operation]);\n }\n\n // Sin override en policies: usar el umbral del grupo\n if (schema.policies?.access) {\n const isWrite = operation === 'create' || operation === 'update' || operation === 'remove';\n const groupLevel = isWrite ? schema.policies.access.write : schema.policies.access.read;\n return this.meetsLevel(groupLevel ?? 'admin');\n }\n\n // Fallback a permissions (legacy, R7.5 transición)\n const perms = schema.permissions;\n if (perms) {\n const permMap: Record<string, boolean | undefined> = {\n list: true, // list no tiene en permissions, siempre true si no hay policies\n get: true,\n create: perms.create,\n update: perms.update,\n remove: perms.remove,\n };\n return permMap[operation] !== false;\n }\n\n // Sin policies ni permissions: UX optimista (§5.3 legacy) — el back es\n // la autoridad y responde 403 si el rol no alcanza.\n return true;\n }\n\n /**\n * Evalúa si el usuario puede leer un campo según `field.access.read`\n * (B8 · R7.4b).\n *\n * Es **pura decisión de UI**. El back vuelve a filtrar la respuesta en\n * cada petición (24-crud), así que esto sólo evita mostrar datos que el\n * usuario no debería ver.\n */\n canReadField(field: FieldSchema): boolean {\n if (!field.access || !field.access.read) return true; // sin restricción ⇒ visible\n return this.meetsLevel(field.access.read);\n }\n\n /**\n * Evalúa si el usuario puede escribir un campo según `field.access.write`\n * (B8 · R7.4b).\n *\n * Es **pura decisión de UI**. El back rechaza la escritura con 422 si el\n * usuario no tiene permiso, así que esto sólo pone el campo en\n * solo-lectura en el frontend.\n */\n canWriteField(field: FieldSchema): boolean {\n if (!field.access || !field.access.write) return true; // sin restricción ⇒ editable\n return this.meetsLevel(field.access.write);\n }\n\n /**\n * Verifica si el rol actual cumple un nivel de acceso dado.\n * Usa la misma jerarquía que `ACCESS_ROLE_RANK` del back (11-auth):\n * admin ≥ staff.\n */\n private meetsLevel(level: string): boolean {\n if (level === 'public') return true;\n if (level === 'session') return this.isAuthenticated();\n if (level.startsWith('role:')) {\n const requiredRole = level.slice('role:'.length);\n return this.hasRole(requiredRole);\n }\n // fallback conservador: nivel desconocido ⇒ sólo admin\n return this.hasRole('admin');\n }\n\n private open(token: string | null, user: SessionUser | null): void {\n if (token !== null && token !== '') this._token.set(token);\n this._user.set(user);\n if (token !== null && token !== '') this.storage.setItem(SESSION_TOKEN_KEY, token);\n if (user !== null) this.storage.setItem(SESSION_USER_KEY, JSON.stringify(user));\n }\n\n /**\n * Borra token **y** usuario (§5.4) — en ese orden de importancia: un token\n * sin usuario no autentica, pero un usuario sin token engaña al menú.\n */\n /**\n * Ruta previa guardada por un `AUTH_EXPIRED` (§4.5), **consumida**: se\n * devuelve una sola vez y se borra. Sin el consume, cada login futuro\n * volvería a la misma URL vieja aunque el usuario hubiera llegado al\n * login desde el menú.\n */\n consumeReturnUrl(): string | null {\n const url = this._returnUrl();\n this._returnUrl.set(null);\n return url;\n }\n\n private clear(): void {\n this._token.set(null);\n this._user.set(null);\n this.storage.removeItem(SESSION_TOKEN_KEY);\n this.storage.removeItem(SESSION_USER_KEY);\n }\n\n private goLogin(): void {\n if (this.currentUrl().startsWith(LOGIN_PATH)) return;\n // Angular rechaza la promesa cuando no hay ruta que empate (NG04002).\n // Navegar es un efecto, no una operación cuyo resultado alguien espere:\n // el catch evita una rejection sin handler en arranques y en tests.\n void this.router.navigate([LOGIN_PATH]).catch(() => undefined);\n }\n\n private currentUrl(): string {\n return this.router.url;\n }\n\n private buildUrl(path: string): string {\n const base = this.apiUrl ? this.apiUrl.replace(/\\/+$/, '') : '';\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return `${base}${cleanPath}`;\n }\n\n private envelope(path: string, payload: Record<string, unknown> = {}) {\n return { token: this._token(), method: 'POST' as const, path, payload };\n }\n\n /**\n * Despacho dual-driver para peticiones de autenticación:\n *\n * - **`gas` (default)**: `POST` a `API_URL` con cuerpo `text/plain` y envelope\n * `{ token, method: 'POST', path, payload }`.\n * - **`rest`**: llamadas REST reales:\n * - `/auth/me`: `GET` a `${apiUrl}/auth/me` con `Authorization: Bearer ${token}`.\n * - resto (`/auth/login`, `/auth/firebase`, `/auth/logout`, etc.): `POST` con\n * `Content-Type: application/json` y payload directo como body JSON. Si existe\n * token activo, se adjunta en cabecera `Authorization`.\n */\n private buildAuthRequest<T>(\n path: string,\n payload: Record<string, unknown> = {},\n ): Observable<T> {\n if (this.driver === 'rest') {\n const url = this.buildUrl(path);\n let headers = new HttpHeaders();\n const token = this._token();\n if (token) {\n headers = headers.set('Authorization', `Bearer ${token}`);\n }\n\n if (path === '/auth/me') {\n return this.http.get<T>(url, { headers });\n }\n\n headers = headers.set('Content-Type', 'application/json');\n return this.http.post<T>(url, payload, { headers });\n }\n\n return this.http.post<T>(this.apiUrl, this.envelope(path, payload), {\n headers: { 'Content-Type': 'text/plain' },\n });\n }\n\n private post<T>(path: string, payload: Record<string, unknown> = {}): Observable<T> {\n return this.buildAuthRequest<T>(path, payload);\n }\n}\n","import { HttpErrorResponse, HttpInterceptorFn, HttpResponse } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { catchError, map, throwError } from 'rxjs';\n\nimport { ApiError } from '../models/api.model';\nimport type { ApiErrorBody } from '../models/api.model';\nimport { AuthService } from '../services/auth.service';\n\n/**\n * Mensaje humano para un error HTTP real (§2).\n *\n * El backend siempre responde 200 con el error dentro del body —eso ya\n * viene en español—, así que esto sólo cubre lo que el transporte falla:\n * red caída, proxy y 5xx. Sin esto el usuario ve el texto crudo de Angular\n * («Http failure response for http://…»), que es inglés técnico.\n */\nfunction httpMessage(status: number): string {\n switch (status) {\n case 0:\n return 'No se pudo conectar con el servidor. Revisa tu conexión.';\n case 401:\n return 'Tu sesión ha expirado. Vuelve a iniciar sesión.';\n case 403:\n return 'No tienes permisos para realizar esta acción.';\n case 404:\n return 'No se encontró el recurso solicitado.';\n case 409:\n return 'El registro cambió o ya existe. Recarga la página y vuelve a intentarlo.';\n case 413:\n return 'El archivo es demasiado grande.';\n case 422:\n return 'Los datos enviados no son válidos.';\n default:\n if (status >= 500) {\n return 'El servidor tuvo un problema. Vuelve a intentarlo en unos segundos.';\n }\n return status >= 400 ? 'No se pudo completar la operación.' : 'Error inesperado.';\n }\n}\n\nfunction isErrorBody(body: unknown): body is ApiErrorBody {\n if (typeof body !== 'object' || body === null) {\n return false;\n }\n const error = (body as ApiErrorBody).error;\n return typeof error === 'object' && error !== null && typeof error.message === 'string';\n}\n\n/**\n * El backend responde SIEMPRE HTTP 200 y pone los errores en el body\n * (api.md §7), así que se detectan en el canal de **éxito** y se relanzan\n * como `ApiError` (con `fieldPath` cuando el backend envía `path`).\n * Los errores HTTP reales (red caída, proxy) también se normalizan.\n *\n * **R2.9 — paso dos de dos.** Éste archivo es el ÚNICO punto donde el\n * sobre de error se abre; lo que decide qué ocurre con un `AUTH_*` es\n * `AuthService.applyAuthCode` (la tabla de §4.5). Aquí sólo se le entrega\n * el código y se propaga el `ApiError` con él — un segundo sitio que\n * «interprete» el código sería una regla duplicada y, a la larga, una que\n * se contradiga con la primera.\n */\nexport const apiErrorInterceptor: HttpInterceptorFn = (req, next) => {\n const auth = inject(AuthService);\n\n return next(req).pipe(\n map((event) => {\n if (event instanceof HttpResponse && isErrorBody(event.body)) {\n const { status, message, path, code, errors } = event.body.error;\n // R2.9: ésta es la ÚNICA llamada a la tabla de §4.5.\n auth.applyAuthCode(code);\n // `errors[]` es el422 con rutas (D42): va entero al ApiError para que\n // `ErrorMapperService` lo convierta en `serverErrors[prefix + key]`.\n throw new ApiError(status, message, path, code, errors ?? []);\n }\n return event;\n }),\n catchError((err: unknown) => {\n if (err instanceof ApiError) {\n return throwError(() => err);\n }\n if (err instanceof HttpErrorResponse) {\n return throwError(() => new ApiError(err.status, httpMessage(err.status)));\n }\n return throwError(() => err);\n }),\n );\n};\n","/**\n * Resolución de marcadores de contexto en paths de endpoints.\n *\n * Plan `doc/context-sources-plan.md` · Fase 2. Lógica PURA: ni HTTP ni DI —\n * recibe un path y un mapa de getters de fuentes y devuelve el path ya\n * resuelto, o revienta.\n *\n * Reglas del contrato (R1–R4 del plan):\n *\n * - `{<fuente>}` / `{<fuente>.<ruta.anidada>}` → valor de la fuente en el\n * momento de la llamada (los getters se evalúan SOLO si hay marcadores).\n * - Cada valor se codifica como segmento de URL (`encodeURIComponent`).\n * - **Fail loud**: fuente no registrada, valor ausente/vacío, valor no\n * escalar o `{key}` sin resolver ⇒ error que nombra el path y el marcador.\n * Jamás se devuelve un segmento vacío ni el texto literal del marcador.\n * - Sin marcadores ⇒ el path vuelve intacto y ninguna fuente se evalúa.\n *\n * OJO con `interpolateTemplate` (R3): su semántica «ausente ⇒ cadena vacía»\n * es correcta para acciones y fuentes de datos, pero acá sería un 404 mudo.\n * Por eso la lectura y la validación viven en este fichero y ese helper no\n * se toca.\n */\n\n/** `{nombre}` o `{nombre.ruta.anidada}` — misma forma que usa el interpolador. */\nconst MARKER = /\\{([a-zA-Z0-9_.]+)\\}/g;\n\n/**\n * Resuelve los marcadores de `path` contra `sources`.\n *\n * @param path Path declarado en `ResourceEndpoints`, tal cual vino del schema.\n * @param sources Getters por nombre de fuente (`auth`, `config`, `valueSources`…).\n * @returns El path con cada marcador sustituido y codificado.\n * @throws Error nombrando path y marcador ante cualquier valor irresoluble.\n */\nexport function resolveEndpointPath(\n path: string,\n sources: Readonly<Record<string, () => unknown>>,\n): string {\n if (!path.includes('{')) return path;\n return path.replace(MARKER, (marker, nombre: string) =>\n sustituir(path, marker, nombre, sources),\n );\n}\n\n/** Sustituye UN marcador. `path` completo sólo viaja para el mensaje de error. */\nfunction sustituir(\n path: string,\n marker: string,\n nombre: string,\n sources: Readonly<Record<string, () => unknown>>,\n): string {\n if (nombre === 'key') {\n throw new Error(\n `[gas-admin-ui] path '${path}': ${marker} llegó sin resolver — ` +\n `la clave sólo la sustituyen get/update/remove antes de resolver el contexto.`,\n );\n }\n\n const punto = nombre.indexOf('.');\n const fuente = punto === -1 ? nombre : nombre.slice(0, punto);\n const getter = sources[fuente];\n if (typeof getter !== 'function') {\n throw new Error(\n `[gas-admin-ui] path '${path}': no se pudo resolver ${marker} — ` +\n `la fuente '${fuente}' no está registrada.`,\n );\n }\n\n // El getter se evalúa acá y sólo acá: si revienta, su error se propaga.\n const valor = punto === -1 ? getter() : leerRuta(getter(), nombre.slice(punto + 1));\n\n if (valor === undefined || valor === null) {\n throw new Error(\n `[gas-admin-ui] path '${path}': no se pudo resolver ${marker} — sin valor para '${nombre}'.`,\n );\n }\n if (typeof valor === 'object') {\n throw new Error(\n `[gas-admin-ui] path '${path}': no se pudo resolver ${marker} — ` +\n `el valor de '${fuente}' no es un segmento de URL (no es escalar).`,\n );\n }\n\n const texto = String(valor);\n if (texto === '') {\n throw new Error(\n `[gas-admin-ui] path '${path}': no se pudo resolver ${marker} — valor vacío en '${fuente}'.`,\n );\n }\n return encodeURIComponent(texto);\n}\n\n/** Lectura profunda `a.b.c` sobre el valor de la fuente; inexistente ⇒ `undefined`. */\nfunction leerRuta(valor: unknown, ruta: string): unknown {\n let actual: unknown = valor;\n for (const paso of ruta.split('.')) {\n if (actual === null || typeof actual !== 'object') return undefined;\n actual = (actual as Record<string, unknown>)[paso];\n }\n return actual;\n}\n","import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';\nimport { inject, Injector, Service } from '@angular/core';\nimport { from, map, Observable, switchMap, tap, throwError } from 'rxjs';\n\nimport { renewedTokenOf } from '../models/api.model';\nimport type { ResourceEndpoints } from '../models/schema.model';\nimport { API_DRIVER, API_URL, FIREBASE_AUTH_ADAPTER, VALUE_SOURCES } from '../tokens';\nimport type { ValueSources } from '../tokens';\nimport { resolveEndpointPath } from '../utils/context-resolver';\nimport { ConfigService } from './config.service';\nimport { AuthService } from './auth.service';\n\n/** Métodos que entiende el envelope del backend (baseapi §2.3). */\nexport type EnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\n/**\n * CRUD genérico sobre el contrato v2 (baseapi §2.3) con soporte dual-driver:\n *\n * - **`gas` (default)**: TODA llamada a la API es un `POST` a `API_URL` con cuerpo\n * `text/plain` y el JSON `{ token, method, path, payload }`.\n * - **`rest`**: Verbos HTTP reales (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) hacia\n * `${API_URL}${path}` con `Authorization: Bearer ${token}`. `payload` va como query\n * params en GET/DELETE (aplanando objetos anidados como `params`) o como body JSON\n * en POST/PUT/PATCH.\n */\n@Service()\nexport class ApiService {\n private readonly http = inject(HttpClient);\n private readonly apiUrl = inject(API_URL);\n private readonly auth = inject(AuthService);\n private readonly driver = inject(API_DRIVER, { optional: true }) ?? 'gas';\n /** Fuentes que aporta el consumidor (`provideGasAdmin({ valueSources })`). */\n private readonly valueSources: ValueSources = inject(VALUE_SOURCES, { optional: true }) ?? {};\n /** Adaptador opcional de Firebase Auth para refrescar el ID token antes de peticiones REST. */\n private readonly firebaseAuthAdapter = inject(FIREBASE_AUTH_ADAPTER, { optional: true });\n /**\n * Acceso PEREZOSO a `ConfigService` (R2 · plan doc/context-sources-plan.md).\n *\n * `config` es fuente construida del motor, pero inyectarla en el constructor\n * formaría un ciclo: `ConfigService` inyecta `ApiService` (opcional) para\n * cargar los singletons ⇒ NG0200 en el arranque. Se pide en el momento de la\n * petición, cuando ambos servicios ya están vivos. Si no existe (TestBed\n * parcial), devuelve `null` y el resolver falla ruidoso con «sin valor».\n */\n private readonly injector = inject(Injector);\n\n /**\n * Listado. El backend envuelve la respuesta cuando la operación declara\n * `shape.envelope: 'list'` (F5) ⇒ `{ items, nextCursor, total }` (R6.4);\n * la UI consume `T[]` en todos lados (F7-3) y este es el ÚNICO punto que\n * habla de listas, así que el desenvolvimiento vive acá. `nextCursor` es\n * el token de la página siguiente y `null` en la última — éste servicio\n * lo descarta hoy porque la lista no pagina todavía (backlog-ui-ux).\n * Un listado sin envelope (arreglo plano, el contrato histórico) pasa\n * tal cual.\n *\n * `payload` sólo lo usa una fuente declarada (F8 · R6.5): `{ view, params }`\n * pide el listado filtrado por una vista y sus parámetros. Sin él, la\n * llamada es la de siempre — la lectura llana del recurso.\n */\n list<T>(endpoint: ResourceEndpoints, payload?: Record<string, unknown>): Observable<T[]> {\n return this.send<unknown>(endpoint.list, 'GET', payload).pipe(map((raw) => unwrapList<T>(raw)));\n }\n\n /** `key` solo aplica a colecciones (`{key}` en la plantilla); los singleton no la llevan. */\n get<T>(endpoint: ResourceEndpoints, key?: string): Observable<T> {\n return this.send<T>(this.withKey(endpoint.get, key), 'GET');\n }\n\n create<T>(endpoint: ResourceEndpoints, payload: Record<string, unknown>): Observable<T> {\n return this.send<T>(endpoint.create, 'POST', payload);\n }\n\n update<T>(\n endpoint: ResourceEndpoints,\n key: string | undefined,\n payload: Record<string, unknown>,\n ): Observable<T> {\n return this.send<T>(this.withKey(endpoint.update, key), 'PUT', payload);\n }\n\n remove(endpoint: ResourceEndpoints, key?: string): Observable<void> {\n return this.send<void>(this.withKey(endpoint.remove, key), 'DELETE');\n }\n\n /**\n * Operación genérica sobre una ruta ya resuelta: reorder de colección\n * (`PUT /admin/x` con `{ reorder: … }`), `/admin/schema`,\n * `/admin/upload-signature`, etc.\n */\n request<T>(\n method: EnvelopeMethod,\n path: string | undefined,\n payload?: Record<string, unknown>,\n ): Observable<T> {\n return this.send<T>(path, method, payload);\n }\n\n private send<T>(\n path: string | undefined,\n method: EnvelopeMethod,\n payload?: Record<string, unknown>,\n ): Observable<T> {\n if (!path) {\n return throwError(() => new Error('Operación no disponible para este recurso'));\n }\n\n // Resolución de marcadores de contexto ANTES del despacho (plan Fase 3):\n // un solo punto para todas las operaciones y para ambos drivers. Sin\n // marcadores el path vuelve intacto y ninguna fuente se evalúa.\n let resolvedPath: string;\n try {\n resolvedPath = resolveEndpointPath(path, this.buildSources());\n } catch (error) {\n // Fail loud por el CANAL de errores (no como throw síncrono): el\n // consumidor lo trata como cualquier otro fallo de petición y JAMÁS\n // sale un request con un segmento vacío o el marcador literal.\n return throwError(() => error);\n }\n\n const request$ =\n this.driver === 'rest'\n ? this.sendRest<T>(resolvedPath, method, payload)\n : this.sendGas<T>(resolvedPath, method, payload);\n\n return request$.pipe(\n // Renovación deslizante (§3.2): si el back adjuntó `meta.renewedToken`\n // se adopta YA, con lo que la siguiente petición ya va firmada nueva.\n tap((res) => {\n const renewed = renewedTokenOf(res);\n if (renewed !== null) this.auth.applyRenewedToken(renewed);\n }),\n );\n }\n\n /**\n * Fuentes de contexto de ESTA petición: getters, no valores — se evalúan\n * sólo si el path trae marcadores.\n *\n * Las construidas del motor (`auth`, `config`) van ÚLTIMAS: el motor es\n * dueño de sus fuentes y el registro del consumidor no las pisa.\n */\n private buildSources(): Readonly<Record<string, () => unknown>> {\n return {\n ...this.valueSources,\n auth: () => ({ user: this.auth.user(), token: this.auth.token() }),\n config: () => this.injector.get(ConfigService, null)?.globalConfig() ?? null,\n };\n }\n\n private sendGas<T>(\n path: string,\n method: EnvelopeMethod,\n payload?: Record<string, unknown>,\n ): Observable<T> {\n // Token de la SESIÓN, leído en cada petición (R2.8): no hay credencial\n // fija que inyectar en el arranque.\n const body = { token: this.auth.token(), method, path, payload: payload ?? {} };\n return this.http.post<T>(this.apiUrl, body, { headers: { 'Content-Type': 'text/plain' } });\n }\n\n private sendRest<T>(\n path: string,\n method: EnvelopeMethod,\n payload?: Record<string, unknown>,\n ): Observable<T> {\n const url = this.buildUrl(path);\n const headers = new HttpHeaders();\n\n // Resuelve el token ANTES de armar la cabecera Authorization:\n // si el adaptador de Firebase provee `getIdToken()`, se llama para obtener\n // un ID token fresco (evita \"ID token inválido\" tras la expiración de 1 hora).\n // Si no provee `getIdToken()`, se usa el token de sesión almacenado (fallback).\n // Cualquier error de `getIdToken()` se propaga por el canal de errores:\n // nunca se envía una petición con un token inválido ni se falla silenciosamente.\n if (this.firebaseAuthAdapter?.getIdToken) {\n return from(this.firebaseAuthAdapter.getIdToken()).pipe(\n switchMap((freshToken) =>\n this.doRestRequest<T>(\n url,\n method,\n payload,\n headers.set('Authorization', `Bearer ${freshToken}`),\n ),\n ),\n );\n }\n\n const token = this.auth.token();\n const finalHeaders = token ? headers.set('Authorization', `Bearer ${token}`) : headers;\n return this.doRestRequest<T>(url, method, payload, finalHeaders);\n }\n\n private doRestRequest<T>(\n url: string,\n method: EnvelopeMethod,\n payload: Record<string, unknown> | undefined,\n headers: HttpHeaders,\n ): Observable<T> {\n if (method === 'GET' || method === 'DELETE') {\n const params = toHttpParams(payload);\n return this.http.request<T>(method, url, { headers, params });\n }\n\n return this.http.request<T>(method, url, {\n headers: headers.set('Content-Type', 'application/json'),\n body: payload ?? {},\n });\n }\n\n private buildUrl(path: string): string {\n const base = this.apiUrl ? this.apiUrl.replace(/\\/+$/, '') : '';\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return `${base}${cleanPath}`;\n }\n\n private withKey(path: string | undefined, key?: string): string | undefined {\n if (!path) {\n return undefined;\n }\n return path.includes('{key}') ? path.replace('{key}', encodeURIComponent(key ?? '')) : path;\n }\n}\n\n/**\n * Convierte un payload en HttpParams para peticiones GET y DELETE en modo REST.\n * Si un valor es un objeto plano (ej: `{ view: 'calendar', params: { orden: 'es' } }`),\n * lo aplana un nivel para que sus claves viajen como query params reales\n * en lugar de serializarse como `params=[object Object]`.\n */\nfunction toHttpParams(payload?: Record<string, unknown>): HttpParams {\n let params = new HttpParams();\n if (!payload) {\n return params;\n }\n\n for (const [key, value] of Object.entries(payload)) {\n if (value === undefined || value === null) {\n continue;\n }\n if (typeof value === 'object' && !Array.isArray(value)) {\n for (const [nestedKey, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n if (nestedValue !== undefined && nestedValue !== null) {\n params = params.set(nestedKey, String(nestedValue));\n }\n }\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (item !== undefined && item !== null) {\n params = params.append(key, String(item));\n }\n }\n } else {\n params = params.set(key, String(value));\n }\n }\n return params;\n}\n\n/** `{ items, nextCursor, total }` (F5/R6.4) → `T[]`; arreglo plano → tal cual; otro ⇒ []. */\nfunction unwrapList<T>(raw: unknown): T[] {\n if (Array.isArray(raw)) {\n return raw as T[];\n }\n if (raw !== null && typeof raw === 'object') {\n const items = (raw as { items?: unknown }).items;\n if (Array.isArray(items)) {\n return items as T[];\n }\n }\n return [];\n}\n","import { inject, Service, signal } from '@angular/core';\nimport { catchError, firstValueFrom, forkJoin, of } from 'rxjs';\n\nimport { ApiService } from './api.service';\nimport type { ResourceEndpoints } from '../models/schema.model';\n\n/**\n * Servicio global de configuración: reúne los singletons declarativos y los\n * deja disponibles para interpolación de plantillas (enlaces públicos,\n * mensajes para compartir).\n *\n * Es genérico a propósito: NO conoce ningún singleton concreto. Los\n * endpoints los inyecta `app.config.ts` con `provideAppInitializer`,\n * derivándolos del registry — agregar un singleton de configuración nuevo\n * = declarar su schema, sin tocar este fichero (refactormotor.md §1 — el\n * schema es el único plano de control).\n *\n * No hay efectos en el constructor: la carga es explícita (`load`), así un\n * TestBed que sólo provee un ApiService parcial no recibe una llamada\n * síncrona a `get` durante la construcción.\n */\n@Service()\nexport class ConfigService {\n private readonly api = inject(ApiService, { optional: true });\n\n /** Mapa reactivo de configuración global (p. ej. `public_base_url`). */\n readonly globalConfig = signal<Record<string, unknown>>({\n // Fallback genérico: el origen actual. Lo que declare el singleton\n // (p. ej. `share_message_template`) lo pone el backend, no el front.\n public_base_url: typeof window !== 'undefined' ? window.location.origin : '',\n });\n\n /** Endpoints conocidos; los guarda `load` para que `ensureLoaded` pueda reintentar. */\n private endpoints: readonly ResourceEndpoints[] = [];\n\n /**\n * `true` cuando la última carga terminó SIN fallos. Si algún singleton\n * falló (o ninguno respondió), queda `false` y el próximo punto crítico\n * vuelve a intentar: no hay estado «cargado a medias para siempre».\n */\n private loaded = false;\n\n /** Carga en curso: `ensureLoaded` la reutiliza en vez de duplicar viajes. */\n private inFlight: Promise<void> | null = null;\n\n /**\n * Carga todos los endpoints de configuración recibidos. Silenciosa: un\n * singleton que falle o que devuelva vacío no rompe nada, simplemente no\n * aporta claves (y deja `loaded = false` para poder reintentar).\n */\n load(endpoints: readonly ResourceEndpoints[]): void {\n this.endpoints = endpoints;\n void this.ensure();\n }\n\n /**\n * Garantiza la config global antes de un punto que la necesita.\n *\n * El load del bootstrap NO es bloqueante y sólo intenta una vez: si la app\n * arrancó sin sesión (el 401 de los singletons se traga en el `catchError`)\n * o la red tardó, la sesión seguía con la config vacía y, por ejemplo, el\n * mensaje de `copy-share` degradaba a «sólo la URL» (R8.7). Acá el reintento\n * corre con la sesión ya iniciada y responde bien; si ya se cargó, es un\n * no-op síncrono.\n */\n ensureLoaded(): Promise<void> {\n return this.ensure();\n }\n\n private ensure(): Promise<void> {\n if (this.loaded) return Promise.resolve();\n if (this.inFlight) return this.inFlight;\n if (!this.api || typeof this.api.get !== 'function' || this.endpoints.length === 0) {\n return Promise.resolve();\n }\n\n this.inFlight = firstValueFrom(\n forkJoin(\n this.endpoints.map((endpoint) =>\n this.api!.get<Record<string, unknown>>(endpoint).pipe(\n // `null` marca el fallo: con `of({})` un singleton caído era\n // indistinguible de uno realmente vacío y la carga quedaría\n // «hecha» sin datos.\n catchError(() => of(null)),\n ),\n ),\n ),\n ).then((blocks) => {\n this.inFlight = null;\n blocks.forEach((data) => {\n if (data && typeof data === 'object' && Object.keys(data).length > 0) {\n this.globalConfig.update((current) => ({ ...current, ...data }));\n }\n });\n this.loaded = blocks.every((data) => data !== null);\n });\n\n return this.inFlight;\n }\n\n /**\n * Contexto de datos para interpolar plantillas del schema\n * (`urlTemplate`, `shareTextTemplate`, `postCreate.description`, …).\n *\n * Sólo aporta datos y la base pública — la FORMA de la URL pública\n * (`/p/{ref}`) la declara el propio schema en su `urlTemplate`; este\n * servicio no decide cómo se arma el enlace.\n */\n buildInterpolationContext(record: Record<string, unknown>): Record<string, unknown> {\n const config = this.globalConfig();\n const baseUrl = String(config['public_base_url'] || window.location.origin).replace(/\\/+$/, '');\n\n return {\n ...config,\n ...record,\n public_base_url: baseUrl,\n };\n }\n}\n","import {\n EnvironmentProviders,\n InjectionToken,\n Provider,\n inject,\n provideAppInitializer,\n} from '@angular/core';\nimport { provideHttpClient, withInterceptors } from '@angular/common/http';\n\nimport { apiErrorInterceptor } from '../interceptors/api-error.interceptor';\nimport { AuthService } from '../services/auth.service';\nimport { ConfigService } from '../services/config.service';\nimport type { AuthSchema, ResourceSchema } from '../models/schema.model';\nimport type { FirebaseAuthAdapter } from '../adapters/firebase-auth.adapter';\nimport { API_DRIVER, FIREBASE_AUTH_ADAPTER, VALUE_SOURCES } from '../tokens';\nimport type { ValueSources } from '../tokens';\n\n\n/**\n * Catálogo de schemas del consumidor (F1 · G-F2).\n *\n * Antes de la extracción el motor importaba `getSchema` del\n * `schemas/registry.ts` de la app. Ese acoplamiento se rompe con un\n * catálogo a nivel módulo: el consumidor llama\n * `provideGasAdmin({ schemas, auth })` en su `app.config.ts` y el motor\n * resuelve en runtime con la MISMA semántica que antes (un solo catálogo\n * por aplicación, seteado una vez en el bootstrap).\n *\n * `provideGasAdmin` se ejecuta al evaluarse el `app.config` (módulo), y\n * todo uso del catálogo (guards, componentes, rutas dinámicas) ocurre en\n * runtime posterior al bootstrap — el orden es determinístico.\n */\n\nlet catalog: readonly ResourceSchema[] = [];\nlet authCatalog: AuthSchema | undefined;\n\n/** Fija el catálogo (uso interno de `provideGasAdmin`; testable directo). */\nexport function setSchemaCatalog(\n schemas: readonly ResourceSchema[],\n auth?: AuthSchema,\n): void {\n catalog = schemas;\n authCatalog = auth;\n}\n\n/** Busca un schema por `id`; `undefined` si no existe. */\nexport function getSchema(id: string): ResourceSchema | undefined {\n return catalog.find((schema) => schema.id === id);\n}\n\n/** Catálogo completo en orden (menú lateral). */\nexport function allSchemas(): readonly ResourceSchema[] {\n return catalog;\n}\n\n/** Declaración de la pantalla de login; `undefined` sin `auth` en config. */\nexport function getAuth(): AuthSchema | undefined {\n return authCatalog;\n}\n\n/** Token DI del catálogo (para inyectarlo donde un `inject()` es posible). */\nexport const GAS_SCHEMAS = new InjectionToken<readonly ResourceSchema[]>(\n 'gas.admin.schemas',\n);\n\n/** Configuración del motor. */\nexport interface GasAdminConfig {\n /** Catálogo ordenado de recursos (menú + rutas + resolución `getSchema`). */\n schemas: readonly ResourceSchema[];\n /** Declaración de la pantalla de login (opcional: sin ella, sin `/login`). */\n auth?: AuthSchema;\n /**\n * Driver de transporte HTTP.\n *\n * - `'gas'` (default): tunnel `POST text/plain` con envelope Apps Script.\n * - `'rest'`: verbos HTTP reales con `Authorization: Bearer` en cabeceras.\n *\n * Sin declaración el motor usa `'gas'` — cero impacto en consumidores existentes.\n */\n driver?: 'gas' | 'rest';\n /**\n * Fuentes de contexto que el consumidor aporta para marcadores en paths de\n * endpoints: `{ <fuente> }` / `{ <fuente>.<ruta> }` se resuelven en el\n * momento de la petición contra este registro más las fuentes construidas\n * `auth` (sesión) y `config` (config global).\n *\n * Opcional: sin él, sólo existen `auth` y `config`. Declararlo no cambia\n * ningún path que no lleve marcadores — cero impacto en schemas existentes.\n */\n valueSources?: ValueSources;\n}\n\n/**\n * Wiring completo del motor: catálogo + HTTP con el interceptor de\n * errores del envelope + loaders de config y sesión en el bootstrap\n * (antes en el `app.config` del consumidor).\n *\n * El consumidor conserva `provideRouter` y `{ provide: API_URL, ... }`\n * (su environment decide la URL del Web App).\n */\nexport function provideGasAdmin(\n config: GasAdminConfig,\n): (Provider | EnvironmentProviders)[] {\n setSchemaCatalog(config.schemas, config.auth);\n // Singletons de configuración: los declara el catálogo, no el servicio.\n // Un singleton nuevo con `endpoint.get` se carga solo.\n const configEndpoints = () =>\n config.schemas.flatMap((schema) =>\n schema.kind === 'singleton' && schema.endpoint?.get\n ? [{ get: schema.endpoint.get }]\n : [],\n );\n return [\n { provide: GAS_SCHEMAS, useValue: config.schemas },\n // Driver de transporte: 'gas' por defecto para cero breaking changes.\n { provide: API_DRIVER, useValue: config.driver ?? 'gas' },\n // Fuentes de contexto del consumidor: vacío por defecto ⇒ sólo `auth` y\n // `config`, que el motor arma por su cuenta en cada petición.\n { provide: VALUE_SOURCES, useValue: config.valueSources ?? {} },\n // Sin `authInterceptor` en modo GAS: el token viaja en el cuerpo del envelope\n // (baseapi §2.3, criterio 11: ninguna cabecera custom).\n // En modo REST el token viaja como cabecera `Authorization` dentro de ApiService.\n provideHttpClient(withInterceptors([apiErrorInterceptor])),\n // Carga de config en bootstrap (fuera del constructor del servicio):\n // un singleton que falle no rompe el arranque.\n provideAppInitializer(() => inject(ConfigService).load(configEndpoints())),\n // Sesión (R2.12): revalida contra `/auth/me` el token que restauró el\n // AuthService de `sessionStorage`. Corre ANTES del primer guard.\n provideAppInitializer(() => inject(AuthService).restore()),\n ];\n}\n\n/**\n * Registra una implementación concreta de FirebaseAuthAdapter en el árbol de DI.\n * Usar junto con provideGasAdmin() en app.config.ts al configurar proveedores federados\n * (como 'google') o autenticación por Firebase.\n *\n * @example\n * // app.config.ts\n * provideGasAdmin({ schemas, auth }),\n * provideFirebaseAdapter(new MyFirebaseAuthAdapter(getAuth(firebaseApp))),\n */\nexport function provideFirebaseAdapter(adapter: FirebaseAuthAdapter): Provider {\n return { provide: FIREBASE_AUTH_ADAPTER, useValue: adapter };\n}\n\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as get } from "./tinya-code-gas-admin-ui-render-registry-
|
|
1
|
+
import { n as get } from "./tinya-code-gas-admin-ui-render-registry-CcIgBsSl.mjs";
|
|
2
2
|
const COL_SPANS = {
|
|
3
3
|
1: "sm:col-span-1",
|
|
4
4
|
2: "sm:col-span-2",
|
|
@@ -77,4 +77,4 @@ function isRecord(value) {
|
|
|
77
77
|
}
|
|
78
78
|
export { colSpanClass as a, seedFields as i, mergeRecord as n, seedFieldValue as r, isRecord as t };
|
|
79
79
|
|
|
80
|
-
//# sourceMappingURL=tinya-code-gas-admin-ui-seed-
|
|
80
|
+
//# sourceMappingURL=tinya-code-gas-admin-ui-seed-Bn6tl-yM.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tinya-code-gas-admin-ui-seed-
|
|
1
|
+
{"version":3,"file":"tinya-code-gas-admin-ui-seed-Bn6tl-yM.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/shared/utils/col-span.ts","../../../projects/gas-admin-ui/src/lib/shared/utils/seed.ts"],"sourcesContent":["/**\n * Anchos de columna del layout (base.md §4: `width` 1–12).\n *\n * `sm:col-span-*` como literales: Tailwind v4 solo genera las clases que ve\n * en el código, así que no pueden construirse por concatenación.\n */\nconst COL_SPANS: Record<number, string> = {\n 1: 'sm:col-span-1',\n 2: 'sm:col-span-2',\n 3: 'sm:col-span-3',\n 4: 'sm:col-span-4',\n 5: 'sm:col-span-5',\n 6: 'sm:col-span-6',\n 7: 'sm:col-span-7',\n 8: 'sm:col-span-8',\n 9: 'sm:col-span-9',\n 10: 'sm:col-span-10',\n 11: 'sm:col-span-11',\n 12: 'sm:col-span-12',\n};\n\n/** Clase `sm:col-span-*` para el ancho del campo (por defecto: 12). */\nexport function colSpanClass(width: number | undefined): string {\n return COL_SPANS[width ?? 12];\n}\n","import type { FieldSchema, GroupField } from '../../core/models/schema.model';\nimport { get } from '../../core/registry/render-registry';\n\n/**\n * Forma del valor inicial que siembra un campo (D32 · R3.8).\n *\n * Son **todos** los valores posibles del dominio: un campo de formulario\n * nuevo nace como cadena, número, booleano, lista o como contenedor que\n * siembra sus hijos. Si un tipo llegara a necesitar otra forma, ampliar este\n * `type` obliga a compilar el `switch` de `seedValue` — que es exactamente\n * el fail-loud que la regla 4 pide.\n */\nexport type SeedKind = 'string' | 'number' | 'boolean' | 'array' | 'recursive';\n\n/**\n * Siembra del modelo del formulario: un valor por defecto para CADA campo\n * declarado en el schema (base.md §10).\n *\n * Compartido entre `form-model` (crear/recargar) y los componentes de lista\n * (crear ítems nuevos con la forma de sus `itemFields`).\n */\n\n/**\n * Valor inicial cuando el schema no declara `default`.\n *\n * **D32 · ya no despacha por tipo.** El `switch` de 20 ramas que obligaba a\n * editar este fichero al añadir un campo — y que §0 contaba como tocar el\n * motor — pasa a resolver el nombre que declara el propio descriptor\n * (`RenderDescriptor.seed`). «Añadir un tipo» queda en carpeta + manifiesto\n * + unión: este archivo no se entera.\n *\n * Dos salidas distintas, ninguna silenciosa:\n *\n * · **tipo fuera del catálogo** ⇒ `undefined`, la misma degradación\n * controlada que el render (R1.10): el campo no se pinta, así que su\n * valor no existe;\n * · **descriptor sin `seed`** ⇒ lanza. Es un error de programación que\n * `core/registry/contract.spec.ts` detecta en CI antes de llegar acá.\n */\nexport function seedValue(field: FieldSchema): unknown {\n const descriptor = get('field', field.type);\n\n if (!descriptor) {\n return undefined;\n }\n if (!descriptor.seed) {\n throw new Error(`[seed] el descriptor de \"${field.type}\" no declara \\`seed\\`.`);\n }\n\n switch (descriptor.seed) {\n case 'string':\n return '';\n case 'number':\n return null;\n case 'boolean':\n return false;\n case 'array':\n return [];\n case 'recursive':\n return seedHijos(field, descriptor.seed);\n }\n}\n\n/**\n * Siembra recursiva de un contenedor.\n *\n * El único `seed` recursivo del catálogo es `group`, pero el registro no lo\n * sabe — sólo lleva un nombre. El estrechado se comprueba aquí y **falla\n * ruidoso** si algún día un tipo declara `'recursive'` sin traer subcampos.\n */\nfunction seedHijos(field: FieldSchema, seed: 'recursive'): unknown {\n if (seed !== 'recursive' || !esGrupo(field)) {\n throw new Error(`[seed] \"${field.type}\" declara \\`seed: 'recursive'\\` pero no trae subcampos.`);\n }\n return seedFields(field.fields);\n}\n\n/** Estrechado de forma, no de despacho: no decide qué hacer, sólo accede. */\nfunction esGrupo(field: FieldSchema): field is GroupField {\n return field.type === 'group';\n}\n\n/** Valor sembrado de un campo, fusionando `default` con la forma del schema. */\nexport function seedFieldValue(field: FieldSchema): unknown {\n if (field.default === undefined) {\n return seedValue(field);\n }\n if (field.type === 'group' && isRecord(field.default)) {\n // Un `default` de grupo podría traer claves incompletas: los hijos se\n // acceden por árbol, así que la forma completa es obligatoria.\n return { ...seedFields(field.fields), ...field.default };\n }\n if (field.type === 'list' && Array.isArray(field.default)) {\n return field.default.map((item) =>\n isRecord(item) ? { ...seedFields(field.itemFields), ...item } : item,\n );\n }\n return field.default;\n}\n\n/**\n * Siembra recursiva: los `group` anidan sus propios campos. Exportada porque\n * la lista crea sus ítems nuevos con la misma forma (`base.md §6`).\n */\nexport function seedFields(fields: readonly FieldSchema[]): Record<string, unknown> {\n const model: Record<string, unknown> = {};\n for (const field of fields) {\n model[field.key] = seedFieldValue(field);\n }\n return model;\n}\n\n/**\n * Fusiona el registro leído con la forma sembrada del schema.\n *\n * El backend puede omitir claves (p. ej. un ítem guardado antes de que el\n * schema agregara un campo): sin esta fusión el árbol no tendría nodo para\n * ese campo y `childTree` lanzaría al renderizar. Lo ausente toma su\n * `default` y los extras del registro (p. ej. `position` de los ítems) se\n * conservan.\n */\nexport function mergeRecord(\n fields: readonly FieldSchema[],\n record: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n for (const field of fields) {\n const incoming = record[field.key];\n if (field.type === 'group') {\n merged[field.key] = isRecord(incoming)\n ? mergeRecord(field.fields, incoming)\n : seedFieldValue(field);\n continue;\n }\n if (field.type === 'list') {\n const itemSeed = seedFields(field.itemFields);\n merged[field.key] = Array.isArray(incoming)\n ? incoming.map((item) => (isRecord(item) ? { ...itemSeed, ...item } : item))\n : seedFieldValue(field);\n continue;\n }\n merged[field.key] = incoming !== undefined ? incoming : seedFieldValue(field);\n }\n return merged;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"mappings":";AAMA,MAAM,YAAoC;CACxC,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,IAAI;CACJ,IAAI;CACJ,IAAI;AACL;AAGD,SAAgB,aAAa,OAAyB;CACpD,OAAO,UAAU,SAAS;AAC5B;ACeA,SAAgB,UAAU,OAAkB;CAC1C,MAAM,aAAa,IAAI,SAAS,MAAM,IAAI;CAE1C,IAAI,CAAC,YACH;CAEF,IAAI,CAAC,WAAW,MACd,MAAM,IAAI,MAAM,4BAA4B,MAAM,KAAI,uBAAwB;CAGhF,QAAQ,WAAW,MAAnB;EACE,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,SACH,OAAO,CAAA;EACT,KAAK,aACH,OAAO,UAAU,OAAO,WAAW,IAAI;CAC3C;AACF;AASA,SAAS,UAAU,OAAoB,MAAiB;CACtD,IAAI,SAAS,eAAe,CAAC,QAAQ,KAAK,GACxC,MAAM,IAAI,MAAM,WAAW,MAAM,KAAI,wDAAyD;CAEhG,OAAO,WAAW,MAAM,MAAM;AAChC;AAGA,SAAS,QAAQ,OAAkB;CACjC,OAAO,MAAM,SAAS;AACxB;AAGA,SAAgB,eAAe,OAAkB;CAC/C,IAAI,MAAM,YAAY,QACpB,OAAO,UAAU,KAAK;CAExB,IAAI,MAAM,SAAS,WAAW,SAAS,MAAM,OAAO,GAGlD,OAAO;EAAE,GAAG,WAAW,MAAM,MAAM;EAAG,GAAG,MAAM;CAAO;CAExD,IAAI,MAAM,SAAS,UAAU,MAAM,QAAQ,MAAM,OAAO,GACtD,OAAO,MAAM,QAAQ,KAAK,SACxB,SAAS,IAAI,IAAI;EAAE,GAAG,WAAW,MAAM,UAAU;EAAG,GAAG;CAAI,IAAK,IAAI;CAGxE,OAAO,MAAM;AACf;AAMA,SAAgB,WAAW,QAA8B;CACvD,MAAM,QAAiC,CAAA;CACvC,KAAK,MAAM,SAAS,QAClB,MAAM,MAAM,OAAO,eAAe,KAAK;CAEzC,OAAO;AACT;AAWA,SAAgB,YACd,QACA,QAA+B;CAE/B,MAAM,SAAkC,CAAA;CACxC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,OAAO,MAAM;EAC9B,IAAI,MAAM,SAAS,SAAS;GAC1B,OAAO,MAAM,OAAO,SAAS,QAAQ,IACjC,YAAY,MAAM,QAAQ,QAAQ,IAClC,eAAe,KAAK;GACxB;EACF;EACA,IAAI,MAAM,SAAS,QAAQ;GACzB,MAAM,WAAW,WAAW,MAAM,UAAU;GAC5C,OAAO,MAAM,OAAO,MAAM,QAAQ,QAAQ,IACtC,SAAS,KAAK,SAAU,SAAS,IAAI,IAAI;IAAE,GAAG;IAAU,GAAG;GAAI,IAAK,IAAK,IACzE,eAAe,KAAK;GACxB;EACF;EACA,OAAO,MAAM,OAAO,aAAa,SAAY,WAAW,eAAe,KAAK;CAC9E;CACA,OAAO;AACT;AAEA,SAAgB,SAAS,OAAc;CACrC,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as getSchema } from "./tinya-code-gas-admin-ui-schema-catalog-
|
|
2
|
-
import { n as dataSourceKey, t as DataSourceService } from "./tinya-code-gas-admin-ui-data-source.service-
|
|
1
|
+
import { i as getSchema } from "./tinya-code-gas-admin-ui-schema-catalog-DU_6_CtP.mjs";
|
|
2
|
+
import { n as dataSourceKey, t as DataSourceService } from "./tinya-code-gas-admin-ui-data-source.service-BUoA9kBh.mjs";
|
|
3
3
|
import { n as FieldAria } from "./tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs";
|
|
4
4
|
import { t as childTree } from "./tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs";
|
|
5
5
|
import { t as optionsFromRows } from "./tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs";
|
|
@@ -289,4 +289,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
289
289
|
});
|
|
290
290
|
export { FieldSelect };
|
|
291
291
|
|
|
292
|
-
//# sourceMappingURL=tinya-code-gas-admin-ui-select-
|
|
292
|
+
//# sourceMappingURL=tinya-code-gas-admin-ui-select-BwefyMVx.mjs.map
|