@praxisui/dynamic-form 9.0.17 → 9.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -312,6 +312,24 @@ Common outputs:
312
312
  - `customAction`, `actionConfirmation`
313
313
  - `fieldRenderError`, `ruleDiagnosticsChange`
314
314
 
315
+ ### Public untrusted runtime profile
316
+
317
+ The component metadata publishes `public-local-readonly` for hosts such as the
318
+ Dynamic Page Studio. It is intentionally narrower than the full Dynamic Form
319
+ API: the document must use `mode="view"`, global readonly,
320
+ `configPersistenceStrategy="input-first"`, local `initialValue`, and an
321
+ explicitly allowlisted config shape. It declares no runtime effects.
322
+
323
+ The profile rejects unknown input/config/field keys, remote controls and option
324
+ sources, endpoints, rich-content blocks, hooks, rules, commands, actions,
325
+ customization, AI triggers, and persistence identifiers. This is an execution
326
+ profile, not an authorization grant. Hosts still evaluate it and remain
327
+ responsible for their own network policy.
328
+
329
+ Readonly and presentation modes also fail closed at runtime: `onSubmit()` does
330
+ not call create/update and does not emit `formSubmit`, even when invoked by a
331
+ host or native browser submission path.
332
+
315
333
  ## Submit Contract
316
334
 
317
335
  `formSubmit` exposes both persistence data and full UI state:
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-08-24T20:08:13.959Z",
3
+ "generatedAt": "2026-08-24T23:27:32.982Z",
4
4
  "packageName": "@praxisui/dynamic-form",
5
- "packageVersion": "9.0.17",
5
+ "packageVersion": "9.0.18",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -15619,6 +15619,8 @@ class PraxisDynamicForm {
15619
15619
  undefined);
15620
15620
  }
15621
15621
  async onSubmit() {
15622
+ if (this.effectiveReadonly || this.effectivePresentation)
15623
+ return;
15622
15624
  if (this.submitting || this.submitPreparationInProgress)
15623
15625
  return;
15624
15626
  if (this.entityHydrationPending) {
@@ -31997,6 +31999,141 @@ const PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA = {
31997
31999
  friendlyName: 'Praxis Dynamic Form',
31998
32000
  description: 'Formulario dinamico com layout por secoes/linhas/colunas e campos renderizados por metadados.',
31999
32001
  icon: 'dynamic_form',
32002
+ runtimeProfiles: [
32003
+ {
32004
+ id: 'public-local-readonly',
32005
+ audience: 'public-untrusted',
32006
+ description: 'Formulário local somente-leitura, sem persistência, endpoints, rich content, hooks, comandos ou controles remotos.',
32007
+ constraints: [
32008
+ {
32009
+ path: '$',
32010
+ operator: 'keys-subset',
32011
+ values: [
32012
+ 'mode',
32013
+ 'readonlyModeGlobal',
32014
+ 'configPersistenceStrategy',
32015
+ 'enableCustomization',
32016
+ 'showAiAssistant',
32017
+ 'initialValue',
32018
+ 'config',
32019
+ ],
32020
+ },
32021
+ { path: 'resourcePath', operator: 'absent-or-empty' },
32022
+ { path: 'resourceId', operator: 'absent-or-empty' },
32023
+ { path: 'schemaUrl', operator: 'absent-or-empty' },
32024
+ { path: 'readUrl', operator: 'absent-or-empty' },
32025
+ { path: 'submitUrl', operator: 'absent-or-empty' },
32026
+ { path: 'submitMethod', operator: 'absent-or-empty' },
32027
+ { path: 'responseSchemaUrl', operator: 'absent-or-empty' },
32028
+ { path: 'apiEndpointKey', operator: 'absent-or-empty' },
32029
+ { path: 'apiUrlEntry', operator: 'absent-or-empty' },
32030
+ { path: 'formId', operator: 'absent-or-empty' },
32031
+ { path: 'componentInstanceId', operator: 'absent-or-empty' },
32032
+ { path: 'hooks', operator: 'absent-or-empty' },
32033
+ { path: 'backConfig', operator: 'absent-or-empty' },
32034
+ { path: 'customEndpoints', operator: 'absent-or-empty' },
32035
+ { path: 'layout', operator: 'absent-or-empty' },
32036
+ { path: 'layoutPolicy', operator: 'absent-or-empty' },
32037
+ { path: 'mode', operator: 'equals', value: 'view' },
32038
+ { path: 'readonlyModeGlobal', operator: 'equals', value: true },
32039
+ { path: 'configPersistenceStrategy', operator: 'equals', value: 'input-first' },
32040
+ { path: 'enableCustomization', operator: 'not-true' },
32041
+ { path: 'showAiAssistant', operator: 'equals', value: false },
32042
+ { path: 'config.sections', operator: 'array' },
32043
+ { path: 'config.fieldMetadata', operator: 'array' },
32044
+ {
32045
+ path: 'config',
32046
+ operator: 'keys-subset',
32047
+ values: ['sections', 'fieldMetadata'],
32048
+ },
32049
+ {
32050
+ path: 'config.sections[*]',
32051
+ operator: 'keys-subset',
32052
+ values: ['id', 'title', 'description', 'icon', 'collapsible', 'expanded', 'rows'],
32053
+ },
32054
+ {
32055
+ path: 'config.sections[*].rows[*]',
32056
+ operator: 'keys-subset',
32057
+ values: ['id', 'columns'],
32058
+ },
32059
+ {
32060
+ path: 'config.sections[*].rows[*].columns[*]',
32061
+ operator: 'keys-subset',
32062
+ values: ['id', 'fields', 'width'],
32063
+ },
32064
+ {
32065
+ path: 'config.fieldMetadata[*]',
32066
+ operator: 'keys-subset',
32067
+ values: [
32068
+ 'name',
32069
+ 'label',
32070
+ 'controlType',
32071
+ 'dataType',
32072
+ 'order',
32073
+ 'required',
32074
+ 'readOnly',
32075
+ 'disabled',
32076
+ 'hidden',
32077
+ 'defaultValue',
32078
+ 'placeholder',
32079
+ 'hint',
32080
+ 'helpText',
32081
+ 'options',
32082
+ 'valueField',
32083
+ 'displayField',
32084
+ 'width',
32085
+ 'prefixIcon',
32086
+ 'suffixIcon',
32087
+ 'ariaLabel',
32088
+ 'validators',
32089
+ 'format',
32090
+ 'selectionMode',
32091
+ ],
32092
+ },
32093
+ { path: 'config.actions', operator: 'absent-or-empty' },
32094
+ { path: 'config.api', operator: 'absent-or-empty' },
32095
+ { path: 'config.hooks', operator: 'absent-or-empty' },
32096
+ { path: 'config.formRules', operator: 'empty-array-or-absent' },
32097
+ { path: 'config.formCommandRules', operator: 'empty-array-or-absent' },
32098
+ { path: 'config.formBlocksBefore', operator: 'absent-or-empty' },
32099
+ { path: 'config.formBlocksBeforeActions', operator: 'absent-or-empty' },
32100
+ { path: 'config.formBlocksAfter', operator: 'absent-or-empty' },
32101
+ { path: 'config.behavior.redirectAfterSave', operator: 'absent-or-empty' },
32102
+ { path: 'config.behavior.reactiveValidation', operator: 'not-true' },
32103
+ { path: 'config.sections[*].headerActions', operator: 'empty-array-or-absent' },
32104
+ {
32105
+ path: 'config.sections[*].rows[*].columns[*].items',
32106
+ operator: 'empty-array-or-absent',
32107
+ },
32108
+ {
32109
+ path: 'config.fieldMetadata[*].controlType',
32110
+ operator: 'one-of',
32111
+ values: [
32112
+ 'input',
32113
+ 'textarea',
32114
+ 'number',
32115
+ 'numericTextBox',
32116
+ 'checkbox',
32117
+ 'toggle',
32118
+ 'date',
32119
+ 'time',
32120
+ 'currency',
32121
+ 'select',
32122
+ 'radio',
32123
+ ],
32124
+ },
32125
+ { path: 'config.fieldMetadata[*].endpoint', operator: 'absent-or-empty' },
32126
+ { path: 'config.fieldMetadata[*].resourcePath', operator: 'absent-or-empty' },
32127
+ { path: 'config.fieldMetadata[*].optionSource', operator: 'absent-or-empty' },
32128
+ { path: 'config.fieldMetadata[*].queryParams', operator: 'absent-or-empty' },
32129
+ { path: 'config.fieldMetadata[*].uploadUrl', operator: 'absent-or-empty' },
32130
+ { path: 'config.fieldMetadata[*].actions', operator: 'absent-or-empty' },
32131
+ { path: 'config.fieldMetadata[*].dialog', operator: 'absent-or-empty' },
32132
+ { path: 'config.fieldMetadata[*].globalAction', operator: 'absent-or-empty' },
32133
+ ],
32134
+ effects: [],
32135
+ },
32136
+ ],
32000
32137
  authoringManifestRef: {
32001
32138
  componentId: 'praxis-dynamic-form',
32002
32139
  source: 'PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/dynamic-form",
3
- "version": "9.0.17",
3
+ "version": "9.0.18",
4
4
  "description": "Angular dynamic form engine for Praxis UI: metadata-driven forms, hooks, and services integrating @praxisui/* packages.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -9,13 +9,13 @@
9
9
  "@angular/forms": "^21.0.0",
10
10
  "@angular/material": "^21.0.0",
11
11
  "@angular/router": "^21.0.0",
12
- "@praxisui/ai": "^9.0.17",
13
- "@praxisui/dynamic-fields": "^9.0.17",
14
- "@praxisui/metadata-editor": "^9.0.17",
15
- "@praxisui/rich-content": "^9.0.17",
16
- "@praxisui/settings-panel": "^9.0.17",
17
- "@praxisui/visual-builder": "^9.0.17",
18
- "@praxisui/core": "^9.0.17",
12
+ "@praxisui/ai": "^9.0.18",
13
+ "@praxisui/dynamic-fields": "^9.0.18",
14
+ "@praxisui/metadata-editor": "^9.0.18",
15
+ "@praxisui/rich-content": "^9.0.18",
16
+ "@praxisui/settings-panel": "^9.0.18",
17
+ "@praxisui/visual-builder": "^9.0.18",
18
+ "@praxisui/core": "^9.0.18",
19
19
  "rxjs": "^7.8.0"
20
20
  },
21
21
  "dependencies": {
@@ -643,6 +643,22 @@ O authoring emite o warning `dynamicForm.config.layout.groupedCommand.row.incomp
643
643
  | `fieldIconPolicy` | `'all' \| 'presentation-only' \| 'none'` | `'all'` | Active | Controla icones `prefixIcon`/`suffixIcon` vindos da metadata dos campos. `all` preserva o comportamento atual; `presentation-only` mantem icones decorativos no modo apresentacao e oculta os slots de icone em controles input/read-only tradicionais; `none` oculta esses slots em todos os modos. |
644
644
  | `visibleGlobal` | `boolean \| null` | `null` | Active | Visibilidade global propagada para loader. |
645
645
 
646
+ #### Perfil público local somente-leitura
647
+
648
+ `PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA.runtimeProfiles` publica
649
+ `public-local-readonly` para hosts públicos não confiáveis. O profile aceita
650
+ somente `mode='view'`, `readonlyModeGlobal=true`, estratégia `input-first`,
651
+ `initialValue` local e um subconjunto explícito de chaves de config, layout e
652
+ `FieldMetadata`. Controles remotos, endpoints, option sources, rich content,
653
+ hooks, rules, commands, actions, customização, assistente e IDs de persistência
654
+ invalidam o profile. Chaves desconhecidas também invalidam o documento para que
655
+ novas capacidades futuras não sejam liberadas implicitamente.
656
+
657
+ O profile declara `effects: []` e não é grant de autorização. O host continua
658
+ responsável por validar o candidate e bloquear rede por padrão. No runtime,
659
+ `onSubmit()` retorna sem emitir evento e sem chamar create/update quando o form
660
+ está em readonly ou presentation, inclusive em submit programático.
661
+
646
662
  In `compactPresentation` runtime detail surfaces, rows, columns and sections are visible only when
647
663
  they contain at least one field that can be materialized after metadata visibility and field-rule
648
664
  overrides are resolved. Boolean values, including `false`, are valid presentation values and must