@praxisui/crud 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
@@ -17,6 +17,48 @@ Use this package to combine `@praxisui/table`, `@praxisui/dynamic-form`, governe
17
17
  - Quickstart app: https://github.com/codexrodrigues/praxis-ui-quickstart
18
18
  - API quickstart: https://github.com/codexrodrigues/praxis-api-quickstart-public
19
19
 
20
+ ## Public sandbox status
21
+
22
+ CRUD supports untrusted public JSON only through the owner-authored
23
+ `public-local-inspect-only` runtime profile. The profile requires
24
+ `metadata.interactionMode: 'inspect-only'`, inline data, volatile table config,
25
+ and a closed presentation-only table shape. The runtime suppresses implicit and
26
+ explicit CRUD actions, resource discovery, routes, dialogs, drawers, surfaces,
27
+ remote refresh, export, plugins, and operational renderers.
28
+
29
+ ```ts
30
+ const inputs = {
31
+ crudId: 'public-customer-inspection',
32
+ tableConfigPersistenceStrategy: 'volatile',
33
+ enableCustomization: false,
34
+ metadata: {
35
+ component: 'praxis-crud',
36
+ interactionMode: 'inspect-only',
37
+ data: [{ id: 1, name: 'Ada Lovelace', status: 'Ready' }],
38
+ table: {
39
+ columns: [
40
+ { field: 'name', header: 'Name', sortable: true, filterable: true },
41
+ { field: 'status', header: 'Status' },
42
+ ],
43
+ behavior: {
44
+ localDataMode: { enabled: true },
45
+ pagination: { enabled: true, pageSize: 10, strategy: 'client' },
46
+ sorting: { enabled: true, strategy: 'client' },
47
+ filtering: {
48
+ enabled: true,
49
+ strategy: 'client',
50
+ columnFilters: { enabled: true },
51
+ },
52
+ },
53
+ toolbar: { visible: true, position: 'top', title: 'Local inspection' },
54
+ },
55
+ },
56
+ };
57
+ ```
58
+
59
+ See the accepted runtime decision and adversarial proof matrix in
60
+ [`docs/rfc-public-inspect-only-runtime-profile.md`](docs/rfc-public-inspect-only-runtime-profile.md).
61
+
20
62
  ## Install
21
63
 
22
64
  ```bash
@@ -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/crud",
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,
@@ -17,6 +17,13 @@
17
17
  "required": false,
18
18
  "description": "Metadados de configuração do CRUD (fonte remota via resource.path ou local via metadata.data)."
19
19
  },
20
+ {
21
+ "name": "metadata.interactionMode",
22
+ "type": "'interactive' | 'inspect-only'",
23
+ "required": false,
24
+ "description": "Decisão canônica de interação. inspect-only exige dados locais e bloqueia ações, resource binding, surfaces e authoring no próprio runtime.",
25
+ "default": "interactive"
26
+ },
20
27
  {
21
28
  "name": "crudId",
22
29
  "type": "string",
@@ -7265,9 +7272,9 @@
7265
7272
  {
7266
7273
  "chunkIndex": 0,
7267
7274
  "chunkKind": "summary",
7268
- "content": "Component ID: praxis-crud\nSelector: praxis-crud\nFriendly Name: Praxis CRUD\nDescription: Tabela com operações de CRUD via metadados.\nLib/Package: @praxisui/crud\nTags: widget, crud, configurable, hasWizard, stable\nInputs:\n - metadata (CrudMetadata | string)\n - crudId (string)\n - componentInstanceId (string)\n - tableConfigPersistenceStrategy ('local-first' | 'input-first' | 'volatile')\n - context (Record<string, unknown>)\n - metadata.queryContext (PraxisDataQueryContext | null)\n - metadata.resource.schemaPath (string | null)\n - metadata.resource.title (string | null)\n - metadata.resource.formTitle (string | null)\n - metadata.filterCriteria (Record<string, unknown> | null)\n - metadata.form (FormConfig)\n - metadata.actions[].form (CrudActionFormContract)\n - enableCustomization (boolean)\n - showResourceIdentityKey (boolean)\n - authoringCapability (string | null)\nOutputs:\n - afterOpen ({ mode: FormOpenMode; action: string })\n - afterClose (void)\n - afterSave ({ id: string | number; data: unknown })\n - afterDelete ({ id: string | number })\n - reactiveDeterminationExecuted ({ action: string; event: ReactiveDeterminationExecutionEvent })\n - reactiveDeterminationPendingChange ({ action: string; pending: boolean })\n - reactiveDeterminationStabilityChange (CrudReactiveDeterminationStabilityChange)\n - rowClick ({ row: unknown; index: number })\n - rowAction (CrudActionRuntimeEvent)\n - selectionChange (unknown)\n - resourceEvent (PraxisResourceEvent<unknown>)\n - error (unknown)\n - configureRequested (void)\n - crudAuthoringDocumentApplied (CrudAuthoringWidgetPersistenceEvent)\n - crudAuthoringDocumentSaved (CrudAuthoringWidgetPersistenceEvent)\n",
7275
+ "content": "Component ID: praxis-crud\nSelector: praxis-crud\nFriendly Name: Praxis CRUD\nDescription: Tabela com operações de CRUD via metadados.\nLib/Package: @praxisui/crud\nTags: widget, crud, configurable, hasWizard, stable\nInputs:\n - metadata (CrudMetadata | string)\n - metadata.interactionMode ('interactive' | 'inspect-only')\n - crudId (string)\n - componentInstanceId (string)\n - tableConfigPersistenceStrategy ('local-first' | 'input-first' | 'volatile')\n - context (Record<string, unknown>)\n - metadata.queryContext (PraxisDataQueryContext | null)\n - metadata.resource.schemaPath (string | null)\n - metadata.resource.title (string | null)\n - metadata.resource.formTitle (string | null)\n - metadata.filterCriteria (Record<string, unknown> | null)\n - metadata.form (FormConfig)\n - metadata.actions[].form (CrudActionFormContract)\n - enableCustomization (boolean)\n - showResourceIdentityKey (boolean)\n - authoringCapability (string | null)\nOutputs:\n - afterOpen ({ mode: FormOpenMode; action: string })\n - afterClose (void)\n - afterSave ({ id: string | number; data: unknown })\n - afterDelete ({ id: string | number })\n - reactiveDeterminationExecuted ({ action: string; event: ReactiveDeterminationExecutionEvent })\n - reactiveDeterminationPendingChange ({ action: string; pending: boolean })\n - reactiveDeterminationStabilityChange (CrudReactiveDeterminationStabilityChange)\n - rowClick ({ row: unknown; index: number })\n - rowAction (CrudActionRuntimeEvent)\n - selectionChange (unknown)\n - resourceEvent (PraxisResourceEvent<unknown>)\n - error (unknown)\n - configureRequested (void)\n - crudAuthoringDocumentApplied (CrudAuthoringWidgetPersistenceEvent)\n - crudAuthoringDocumentSaved (CrudAuthoringWidgetPersistenceEvent)\n",
7269
7276
  "sourcePointer": "projects/praxis-crud/src/lib/praxis-crud.metadata.ts",
7270
- "contentHash": "b23b03f710e5ccd9109b534891d97f93780d572005599050d3c9b3ade71d57e2",
7277
+ "contentHash": "d9956416f9b8b3d9cbab9a84a3b979a013a31d5ec46c62ec634d8985676b07d6",
7271
7278
  "sourceKind": "component_definition",
7272
7279
  "sourceId": "praxis-crud",
7273
7280
  "corpusVersion": "1.0.0"
@@ -3870,6 +3870,70 @@ function openCrudMetadataEditor(settings, opts) {
3870
3870
  });
3871
3871
  }
3872
3872
 
3873
+ const OPERATIONAL_RENDERERS = new Set([
3874
+ 'image',
3875
+ 'avatar',
3876
+ 'link',
3877
+ 'button',
3878
+ 'menu',
3879
+ 'toggle',
3880
+ 'html',
3881
+ 'compose',
3882
+ ]);
3883
+ /** Materializes the effect-free Table projection owned by inspect-only CRUD. */
3884
+ function hardenCrudInspectOnlyTableConfig(config) {
3885
+ const safe = config;
3886
+ safe.behavior = {
3887
+ ...(safe.behavior || {}),
3888
+ localDataMode: { ...(safe.behavior?.localDataMode || {}), enabled: true },
3889
+ expansion: { ...(safe.behavior?.expansion || {}), enabled: false },
3890
+ };
3891
+ safe.toolbar = safe.toolbar
3892
+ ? { ...safe.toolbar, actions: [] }
3893
+ : { visible: false, position: 'top', actions: [] };
3894
+ safe.actions = {
3895
+ ...(safe.actions || {}),
3896
+ row: {
3897
+ position: 'end',
3898
+ display: 'menu',
3899
+ trigger: 'always',
3900
+ ...(safe.actions?.row || {}),
3901
+ enabled: false,
3902
+ actions: [],
3903
+ },
3904
+ bulk: {
3905
+ position: 'toolbar',
3906
+ ...(safe.actions?.bulk || {}),
3907
+ enabled: false,
3908
+ actions: [],
3909
+ },
3910
+ };
3911
+ safe.export = { formats: [], ...(safe.export || {}), enabled: false };
3912
+ safe.ai = {
3913
+ ...(safe.ai || {}),
3914
+ assistant: { ...(safe.ai?.assistant || {}), enabled: false },
3915
+ };
3916
+ safe.columnProjection = undefined;
3917
+ safe.analyticsProjection = undefined;
3918
+ safe.plugins = [];
3919
+ safe.columns = (safe.columns || []).map((column) => {
3920
+ const next = { ...column };
3921
+ if (isOperationalRenderer(next.renderer))
3922
+ next.renderer = undefined;
3923
+ if (Array.isArray(next.conditionalRenderers)) {
3924
+ next.conditionalRenderers = next.conditionalRenderers.map((conditional) => isOperationalRenderer(conditional?.renderer)
3925
+ ? { ...conditional, renderer: undefined }
3926
+ : conditional);
3927
+ }
3928
+ return next;
3929
+ });
3930
+ delete safe.resourcePath;
3931
+ return safe;
3932
+ }
3933
+ function isOperationalRenderer(renderer) {
3934
+ return OPERATIONAL_RENDERERS.has(String(renderer?.type || '').trim());
3935
+ }
3936
+
3873
3937
  class PraxisCrudComponent {
3874
3938
  metadata;
3875
3939
  crudId;
@@ -3970,7 +4034,13 @@ class PraxisCrudComponent {
3970
4034
  collectionCapabilitiesRequestSeq = 0;
3971
4035
  currentAuthoringDocument;
3972
4036
  selectedRow = null;
4037
+ get resolvedTableConfigPersistenceStrategy() {
4038
+ return this.isInspectOnly() ? 'volatile' : this.tableConfigPersistenceStrategy;
4039
+ }
3973
4040
  onTableRowAction(event) {
4041
+ if (this.isInspectOnly()) {
4042
+ return;
4043
+ }
3974
4044
  this.rowAction.emit(event);
3975
4045
  void this.onAction(event.action || '', event.row, event);
3976
4046
  }
@@ -3987,6 +4057,9 @@ class PraxisCrudComponent {
3987
4057
  return assigned ?? (this.surfaceOpenAdapterInstance ??= this.injector.get(ResourceSurfaceOpenAdapterService));
3988
4058
  }
3989
4059
  onResetPreferences() {
4060
+ if (this.isInspectOnly()) {
4061
+ return;
4062
+ }
3990
4063
  try {
3991
4064
  const keyId = this.componentKeyId();
3992
4065
  if (!keyId)
@@ -4011,9 +4084,15 @@ class PraxisCrudComponent {
4011
4084
  this.selectionChange.emit(event);
4012
4085
  }
4013
4086
  onTableResourceEvent(event) {
4087
+ if (this.isInspectOnly()) {
4088
+ return;
4089
+ }
4014
4090
  this.resourceEvent.emit(event);
4015
4091
  }
4016
4092
  async onBulkAction(event) {
4093
+ if (this.isInspectOnly()) {
4094
+ return;
4095
+ }
4017
4096
  const action = String(event?.action || '').trim();
4018
4097
  if (!action) {
4019
4098
  return;
@@ -4025,6 +4104,9 @@ class PraxisCrudComponent {
4025
4104
  await this.onAction(action, row ?? undefined, event);
4026
4105
  }
4027
4106
  async onToolbarAction(event) {
4107
+ if (this.isInspectOnly()) {
4108
+ return;
4109
+ }
4028
4110
  const action = String(event?.action || '').trim();
4029
4111
  if (!action)
4030
4112
  return;
@@ -4045,7 +4127,7 @@ class PraxisCrudComponent {
4045
4127
  ? JSON.parse(this.metadata)
4046
4128
  : this.metadata;
4047
4129
  this.resolvedMetadata = parsed;
4048
- const externalAuthoringSeed = this.context && typeof this.context === 'object'
4130
+ const externalAuthoringSeed = parsed.interactionMode !== 'inspect-only' && this.context && typeof this.context === 'object'
4049
4131
  ? (this.context['authoringDocument'] ?? this.context['authoringMetadata'])
4050
4132
  : undefined;
4051
4133
  this.currentAuthoringDocument = externalAuthoringSeed
@@ -4057,6 +4139,7 @@ class PraxisCrudComponent {
4057
4139
  assertCrudMetadata(this.resolvedMetadata, {
4058
4140
  allowDeferredActionBindings: true,
4059
4141
  });
4142
+ this.emitInspectOnlyDiagnostics(this.resolvedMetadata);
4060
4143
  this.tableQueryContext = this.resolveQueryContext(this.resolvedMetadata);
4061
4144
  this.tableFilterCriteria = this.resolveFilterCriteria(this.resolvedMetadata);
4062
4145
  this.tableCollectionLinks = null;
@@ -4071,6 +4154,9 @@ class PraxisCrudComponent {
4071
4154
  }
4072
4155
  }
4073
4156
  async onAction(action, row, runtimeEvent) {
4157
+ if (this.isInspectOnly()) {
4158
+ return;
4159
+ }
4074
4160
  if (this.openingActionLabel()) {
4075
4161
  return;
4076
4162
  }
@@ -4248,13 +4334,22 @@ class PraxisCrudComponent {
4248
4334
  }
4249
4335
  }
4250
4336
  onTableConfigChange(config) {
4337
+ if (this.isInspectOnly()) {
4338
+ return;
4339
+ }
4251
4340
  this.lastTableRuntimeConfigSnapshot = this.cloneTableConfigSnapshot(config);
4252
4341
  this.emitTableRuntimeConfigSnapshot(this.lastTableRuntimeConfigSnapshot);
4253
4342
  }
4254
4343
  onTableMetadataChange() {
4344
+ if (this.isInspectOnly()) {
4345
+ return;
4346
+ }
4255
4347
  this.emitTableRuntimeConfigSnapshot();
4256
4348
  }
4257
4349
  onTableLoadingStateChange(state) {
4350
+ if (this.isInspectOnly()) {
4351
+ return;
4352
+ }
4258
4353
  if (!state || state.status !== 'success')
4259
4354
  return;
4260
4355
  if (state.phase !== 'render' && state.phase !== 'data' && state.phase !== 'schema') {
@@ -4263,6 +4358,14 @@ class PraxisCrudComponent {
4263
4358
  this.emitTableRuntimeConfigSnapshot();
4264
4359
  }
4265
4360
  onCollectionLinksChange(links) {
4361
+ if (this.isInspectOnly()) {
4362
+ this.tableCollectionLinks = null;
4363
+ this.collectionCapabilities = null;
4364
+ this.collectionCapabilitiesRequestSeq += 1;
4365
+ this.collectionCapabilitiesRequestHref = null;
4366
+ this.collectionCapabilitiesResolvedHref = null;
4367
+ return;
4368
+ }
4266
4369
  this.tableCollectionLinks = links || null;
4267
4370
  const capabilitiesHref = this.resolveCollectionCapabilitiesHref(links);
4268
4371
  if (!capabilitiesHref) {
@@ -4308,6 +4411,9 @@ class PraxisCrudComponent {
4308
4411
  });
4309
4412
  }
4310
4413
  resolveResourcePath(meta) {
4414
+ if (this.isInspectOnly(meta)) {
4415
+ return '';
4416
+ }
4311
4417
  return (meta?.resource?.path ||
4312
4418
  meta?.table?.resourcePath ||
4313
4419
  '').trim();
@@ -4510,6 +4616,9 @@ class PraxisCrudComponent {
4510
4616
  this.resolveResourcePath(this.resolvedMetadata).length === 0);
4511
4617
  }
4512
4618
  resolveMaterializedReadUrl() {
4619
+ if (this.isInspectOnly()) {
4620
+ return '';
4621
+ }
4513
4622
  const materialization = this.context?.['materialization'];
4514
4623
  return String(materialization?.['readUrl'] || '').trim();
4515
4624
  }
@@ -5071,6 +5180,9 @@ class PraxisCrudComponent {
5071
5180
  ? JSON.parse(JSON.stringify(base))
5072
5181
  : createDefaultTableConfig();
5073
5182
  let changed = false;
5183
+ if (this.isInspectOnly(meta)) {
5184
+ return this.buildInspectOnlyTableConfig(cfg);
5185
+ }
5074
5186
  if (nextResourceIdentity !== null &&
5075
5187
  nextResourceIdentity === this.lastAppliedResourceIdentity &&
5076
5188
  (cfg.columns?.length ?? 0) === 0 &&
@@ -5271,6 +5383,9 @@ class PraxisCrudComponent {
5271
5383
  buildTableCrudContext(meta, capabilities) {
5272
5384
  if (!meta)
5273
5385
  return undefined;
5386
+ if (this.isInspectOnly(meta)) {
5387
+ return undefined;
5388
+ }
5274
5389
  const baseActions = this.resolveContextCrudActions(meta, capabilities);
5275
5390
  return {
5276
5391
  tableId: this.crudId || 'default',
@@ -5327,6 +5442,9 @@ class PraxisCrudComponent {
5327
5442
  });
5328
5443
  };
5329
5444
  isCustomizationAvailable() {
5445
+ if (this.isInspectOnly()) {
5446
+ return false;
5447
+ }
5330
5448
  if (!this.enableCustomization) {
5331
5449
  return false;
5332
5450
  }
@@ -5368,6 +5486,9 @@ class PraxisCrudComponent {
5368
5486
  };
5369
5487
  }
5370
5488
  resolveCreateToolbarAction(meta, capabilities) {
5489
+ if (this.isInspectOnly(meta)) {
5490
+ return null;
5491
+ }
5371
5492
  if (capabilities && !this.supportsCreateCapability(capabilities)) {
5372
5493
  return null;
5373
5494
  }
@@ -5378,6 +5499,9 @@ class PraxisCrudComponent {
5378
5499
  return capabilities ? { action: 'create', label: this.getCrudActionLabel('create') } : null;
5379
5500
  }
5380
5501
  resolveContextCrudActions(meta, capabilities) {
5502
+ if (this.isInspectOnly(meta)) {
5503
+ return [];
5504
+ }
5381
5505
  const declaredActions = (meta.actions || []).filter(Boolean);
5382
5506
  if (!capabilities) {
5383
5507
  return declaredActions.length
@@ -5431,6 +5555,38 @@ class PraxisCrudComponent {
5431
5555
  }
5432
5556
  return runtimeActions;
5433
5557
  }
5558
+ isInspectOnly(meta) {
5559
+ return (meta ?? this.resolvedMetadata)?.interactionMode === 'inspect-only';
5560
+ }
5561
+ buildInspectOnlyTableConfig(config) {
5562
+ return hardenCrudInspectOnlyTableConfig(this.cloneTableConfigSnapshot(config));
5563
+ }
5564
+ emitInspectOnlyDiagnostics(meta) {
5565
+ if (!this.isInspectOnly(meta)) {
5566
+ return;
5567
+ }
5568
+ const incompatiblePaths = [];
5569
+ if (String(meta.resource?.path || '').trim())
5570
+ incompatiblePaths.push('metadata.resource.path');
5571
+ if (String(meta.table?.resourcePath || '').trim()) {
5572
+ incompatiblePaths.push('metadata.table.resourcePath');
5573
+ }
5574
+ if (!Array.isArray(meta.data))
5575
+ incompatiblePaths.push('metadata.data');
5576
+ if ((meta.actions?.length || 0) > 0)
5577
+ incompatiblePaths.push('metadata.actions');
5578
+ if (meta.form)
5579
+ incompatiblePaths.push('metadata.form');
5580
+ if (meta.defaults?.openMode)
5581
+ incompatiblePaths.push('metadata.defaults.openMode');
5582
+ if (incompatiblePaths.length) {
5583
+ this.error.emit({
5584
+ code: 'CRUD_INSPECT_ONLY_INCOMPATIBLE_METADATA',
5585
+ interactionMode: 'inspect-only',
5586
+ paths: incompatiblePaths,
5587
+ });
5588
+ }
5589
+ }
5434
5590
  resolveCollectionWorkflowCrudActions(capabilities) {
5435
5591
  if (!capabilities) {
5436
5592
  return [];
@@ -5653,7 +5809,7 @@ class PraxisCrudComponent {
5653
5809
  [queryContext]="tableQueryContext"
5654
5810
  [filterCriteria]="tableFilterCriteria"
5655
5811
  [tableId]="crudId || 'default'"
5656
- [configPersistenceStrategy]="tableConfigPersistenceStrategy"
5812
+ [configPersistenceStrategy]="resolvedTableConfigPersistenceStrategy"
5657
5813
  [crudContext]="tableCrudContext"
5658
5814
  [enableCustomization]="enableCustomization"
5659
5815
  [authoringCapability]="authoringCapability"
@@ -5703,7 +5859,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5703
5859
  [queryContext]="tableQueryContext"
5704
5860
  [filterCriteria]="tableFilterCriteria"
5705
5861
  [tableId]="crudId || 'default'"
5706
- [configPersistenceStrategy]="tableConfigPersistenceStrategy"
5862
+ [configPersistenceStrategy]="resolvedTableConfigPersistenceStrategy"
5707
5863
  [crudContext]="tableCrudContext"
5708
5864
  [enableCustomization]="enableCustomization"
5709
5865
  [authoringCapability]="authoringCapability"
@@ -6852,6 +7008,153 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
6852
7008
  friendlyName: 'Praxis CRUD',
6853
7009
  description: 'Tabela com operações de CRUD via metadados.',
6854
7010
  icon: 'table_chart',
7011
+ runtimeProfiles: [
7012
+ {
7013
+ id: 'public-local-inspect-only',
7014
+ audience: 'public-untrusted',
7015
+ description: 'CRUD local somente para inspeção, sem resource binding, persistência, authoring ou ações operacionais.',
7016
+ constraints: [
7017
+ {
7018
+ path: '$',
7019
+ operator: 'keys-subset',
7020
+ values: [
7021
+ 'metadata',
7022
+ 'crudId',
7023
+ 'tableConfigPersistenceStrategy',
7024
+ 'enableCustomization',
7025
+ ],
7026
+ },
7027
+ { path: 'crudId', operator: 'present' },
7028
+ { path: 'tableConfigPersistenceStrategy', operator: 'equals', value: 'volatile' },
7029
+ { path: 'enableCustomization', operator: 'not-true' },
7030
+ { path: 'metadata.component', operator: 'equals', value: 'praxis-crud' },
7031
+ { path: 'metadata.interactionMode', operator: 'equals', value: 'inspect-only' },
7032
+ { path: 'metadata.data', operator: 'array' },
7033
+ {
7034
+ path: 'metadata',
7035
+ operator: 'keys-subset',
7036
+ values: ['component', 'interactionMode', 'data', 'table'],
7037
+ },
7038
+ { path: 'metadata.resource', operator: 'absent-or-empty' },
7039
+ { path: 'metadata.queryContext', operator: 'absent-or-empty' },
7040
+ { path: 'metadata.filterCriteria', operator: 'absent-or-empty' },
7041
+ { path: 'metadata.form', operator: 'absent-or-empty' },
7042
+ { path: 'metadata.defaults', operator: 'absent-or-empty' },
7043
+ { path: 'metadata.actions', operator: 'empty-array-or-absent' },
7044
+ { path: 'metadata.i18n', operator: 'absent-or-empty' },
7045
+ { path: 'metadata.table.columns', operator: 'array' },
7046
+ {
7047
+ path: 'metadata.table',
7048
+ operator: 'keys-subset',
7049
+ values: ['columns', 'behavior', 'toolbar'],
7050
+ },
7051
+ {
7052
+ path: 'metadata.table.columns[*]',
7053
+ operator: 'keys-subset',
7054
+ values: [
7055
+ 'field',
7056
+ 'header',
7057
+ 'type',
7058
+ 'width',
7059
+ 'visible',
7060
+ 'sortable',
7061
+ 'filterable',
7062
+ 'resizable',
7063
+ 'sticky',
7064
+ 'align',
7065
+ 'order',
7066
+ 'sortField',
7067
+ 'format',
7068
+ 'valueMapping',
7069
+ ],
7070
+ },
7071
+ { path: 'metadata.table.columns[*].renderer', operator: 'absent-or-empty' },
7072
+ {
7073
+ path: 'metadata.table.columns[*].conditionalRenderers',
7074
+ operator: 'absent-or-empty',
7075
+ },
7076
+ {
7077
+ path: 'metadata.table.behavior',
7078
+ operator: 'keys-subset',
7079
+ values: ['localDataMode', 'pagination', 'sorting', 'filtering'],
7080
+ },
7081
+ { path: 'metadata.table.behavior.localDataMode.enabled', operator: 'equals', value: true },
7082
+ {
7083
+ path: 'metadata.table.behavior.pagination.strategy',
7084
+ operator: 'none-of',
7085
+ values: ['server'],
7086
+ },
7087
+ {
7088
+ path: 'metadata.table.behavior.pagination',
7089
+ operator: 'keys-subset',
7090
+ values: [
7091
+ 'enabled',
7092
+ 'pageSize',
7093
+ 'pageSizeOptions',
7094
+ 'showFirstLastButtons',
7095
+ 'showPageNumbers',
7096
+ 'showPageInfo',
7097
+ 'position',
7098
+ 'style',
7099
+ 'totalItems',
7100
+ 'strategy',
7101
+ ],
7102
+ },
7103
+ { path: 'metadata.table.behavior.pagination.advanced', operator: 'absent-or-empty' },
7104
+ {
7105
+ path: 'metadata.table.behavior.sorting.strategy',
7106
+ operator: 'none-of',
7107
+ values: ['server'],
7108
+ },
7109
+ {
7110
+ path: 'metadata.table.behavior.sorting',
7111
+ operator: 'keys-subset',
7112
+ values: [
7113
+ 'enabled',
7114
+ 'defaultSort',
7115
+ 'multiSort',
7116
+ 'strategy',
7117
+ 'showSortIndicators',
7118
+ 'indicatorPosition',
7119
+ 'allowClearSort',
7120
+ ],
7121
+ },
7122
+ { path: 'metadata.table.behavior.sorting.advanced', operator: 'absent-or-empty' },
7123
+ {
7124
+ path: 'metadata.table.behavior.filtering.strategy',
7125
+ operator: 'none-of',
7126
+ values: ['server'],
7127
+ },
7128
+ {
7129
+ path: 'metadata.table.behavior.filtering',
7130
+ operator: 'keys-subset',
7131
+ values: ['enabled', 'columnFilters', 'strategy', 'debounceTime'],
7132
+ },
7133
+ {
7134
+ path: 'metadata.table.behavior.filtering.columnFilters',
7135
+ operator: 'keys-subset',
7136
+ values: ['enabled', 'defaultType', 'position'],
7137
+ },
7138
+ {
7139
+ path: 'metadata.table.behavior.filtering.advancedFilters',
7140
+ operator: 'absent-or-empty',
7141
+ },
7142
+ {
7143
+ path: 'metadata.table.toolbar',
7144
+ operator: 'keys-subset',
7145
+ values: ['visible', 'position', 'title', 'subtitle', 'icon', 'textAlign'],
7146
+ },
7147
+ { path: 'metadata.table.toolbar.actions', operator: 'empty-array-or-absent' },
7148
+ { path: 'metadata.table.actions', operator: 'absent-or-empty' },
7149
+ { path: 'metadata.table.export', operator: 'absent-or-empty' },
7150
+ { path: 'metadata.table.ai', operator: 'absent-or-empty' },
7151
+ { path: 'metadata.table.columnProjection', operator: 'absent-or-empty' },
7152
+ { path: 'metadata.table.analyticsProjection', operator: 'absent-or-empty' },
7153
+ { path: 'metadata.table.plugins', operator: 'empty-array-or-absent' },
7154
+ ],
7155
+ effects: [],
7156
+ },
7157
+ ],
6855
7158
  authoringManifestRef: {
6856
7159
  componentId: 'praxis-crud',
6857
7160
  source: 'PRAXIS_CRUD_AUTHORING_MANIFEST',
@@ -6866,6 +7169,12 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
6866
7169
  type: 'CrudMetadata | string',
6867
7170
  description: 'Metadados de configuração do CRUD (fonte remota via resource.path ou local via metadata.data).',
6868
7171
  },
7172
+ {
7173
+ name: 'metadata.interactionMode',
7174
+ type: "'interactive' | 'inspect-only'",
7175
+ default: 'interactive',
7176
+ description: 'Decisão canônica de interação. inspect-only exige dados locais e bloqueia ações, resource binding, surfaces e authoring no próprio runtime.',
7177
+ },
6869
7178
  {
6870
7179
  name: 'crudId',
6871
7180
  type: 'string',
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "9.0.17",
3
+ "version": "9.0.18",
4
4
  "description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/dynamic-form": "^9.0.17",
9
- "@praxisui/table": "^9.0.17",
10
- "@praxisui/core": "^9.0.17",
11
- "@praxisui/dynamic-fields": "^9.0.17",
12
- "@praxisui/settings-panel": "^9.0.17",
8
+ "@praxisui/dynamic-form": "^9.0.18",
9
+ "@praxisui/table": "^9.0.18",
10
+ "@praxisui/core": "^9.0.18",
11
+ "@praxisui/dynamic-fields": "^9.0.18",
12
+ "@praxisui/settings-panel": "^9.0.18",
13
13
  "@angular/cdk": "^21.0.0",
14
14
  "@angular/forms": "^21.0.0",
15
15
  "@angular/material": "^21.0.0",
16
16
  "@angular/router": "^21.0.0",
17
- "@praxisui/ai": "^9.0.17",
17
+ "@praxisui/ai": "^9.0.18",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {
@@ -48,6 +48,7 @@ declare class DialogService {
48
48
  }
49
49
 
50
50
  type FormOpenMode = 'route' | 'modal' | 'drawer';
51
+ type CrudInteractionMode = 'interactive' | 'inspect-only';
51
52
  type CrudReactiveDeterminationStabilityStatus = 'pending' | 'settled-satisfied' | 'settled-unsatisfied';
52
53
  /** Value-free stability evidence for the Dynamic Form opened by one CRUD action. */
53
54
  interface CrudFormReactiveDeterminationStabilityChange {
@@ -101,6 +102,12 @@ interface CrudResource {
101
102
  }
102
103
  interface CrudMetadata {
103
104
  component: 'praxis-crud';
105
+ /**
106
+ * Governs whether the CRUD may materialize operational actions.
107
+ * `interactive` preserves the historical behavior; `inspect-only` is
108
+ * fail-closed and only materializes local, non-operational inspection.
109
+ */
110
+ interactionMode?: CrudInteractionMode;
104
111
  resource?: CrudResource;
105
112
  data?: any[] | null;
106
113
  table: TableConfig;
@@ -270,6 +277,7 @@ declare class PraxisCrudComponent implements OnChanges {
270
277
  private collectionCapabilitiesRequestSeq;
271
278
  private currentAuthoringDocument?;
272
279
  selectedRow: Record<string, unknown> | null;
280
+ get resolvedTableConfigPersistenceStrategy(): TableConfigPersistenceStrategy;
273
281
  onTableRowAction(event: CrudActionRuntimeEvent): void;
274
282
  private getResourceDiscovery;
275
283
  private getActionOpenAdapter;
@@ -359,6 +367,9 @@ declare class PraxisCrudComponent implements OnChanges {
359
367
  private buildWidgetPersistencePayload;
360
368
  private resolveCreateToolbarAction;
361
369
  private resolveContextCrudActions;
370
+ private isInspectOnly;
371
+ private buildInspectOnlyTableConfig;
372
+ private emitInspectOnlyDiagnostics;
362
373
  private resolveCollectionWorkflowCrudActions;
363
374
  private resolveCollectionWorkflowToolbarActions;
364
375
  private supportsCreateCapability;
@@ -851,4 +862,4 @@ type OpenCrudMetadataEditorOptions = {
851
862
  declare function openCrudMetadataEditor(settings: SettingsPanelService, opts?: OpenCrudMetadataEditorOptions): _praxisui_settings_panel.SettingsPanelRef;
852
863
 
853
864
  export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudMetadataEditorComponent, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_AUTHORING_MANIFEST, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, PraxisCrudWidgetConfigEditor, assertCrudMetadata, createCrudAuthoringDocument, findCrudAction, normalizeCrudAuthoringDocument, openCrudMetadataEditor, parseLegacyOrCrudDocument, providePraxisCrudMetadata, serializeCrudAuthoringDocument, validateCrudAuthoringDocument };
854
- export type { Capability, CapabilityCatalog, CapabilityCategory, CrudAction, CrudActionFormContract, CrudActionResolutionMode, CrudAuthoringDocument, CrudAuthoringWidgetPersistenceEvent, CrudDefaults, CrudEditorDiagnostic, CrudFormReactiveDeterminationStabilityChange, CrudHeaderConfig, CrudHeaderVariant, CrudMetadata, CrudMetadataAssertionOptions, CrudParamMapping, CrudReactiveDeterminationStabilityChange, CrudReactiveDeterminationStabilityStatus, CrudResource, CrudValidationContext, DialogConfig, DialogRef, FormOpenMode, OpenCrudMetadataEditorOptions, PraxisCrudWidgetEditorInputs, PraxisCrudWidgetEditorValue, ValueKind };
865
+ export type { Capability, CapabilityCatalog, CapabilityCategory, CrudAction, CrudActionFormContract, CrudActionResolutionMode, CrudAuthoringDocument, CrudAuthoringWidgetPersistenceEvent, CrudDefaults, CrudEditorDiagnostic, CrudFormReactiveDeterminationStabilityChange, CrudHeaderConfig, CrudHeaderVariant, CrudInteractionMode, CrudMetadata, CrudMetadataAssertionOptions, CrudParamMapping, CrudReactiveDeterminationStabilityChange, CrudReactiveDeterminationStabilityStatus, CrudResource, CrudValidationContext, DialogConfig, DialogRef, FormOpenMode, OpenCrudMetadataEditorOptions, PraxisCrudWidgetEditorInputs, PraxisCrudWidgetEditorValue, ValueKind };