@praxisui/crud 8.0.0-beta.2 → 8.0.0-beta.20

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
@@ -353,6 +353,39 @@ Direcao de plataforma:
353
353
  - a semantica canonica continua cobrindo metadata, tabela interna, formulario, actions, open modes e persistencia/reabertura
354
354
  - hosts consumidores devem permanecer como demonstradores do contrato, nao como donos de um editor paralelo
355
355
 
356
+ ## Agentic Authoring Contract
357
+
358
+ `PRAXIS_CRUD_AUTHORING_MANIFEST` is the executable AI authoring contract for `@praxisui/crud`.
359
+
360
+ The manifest owns CRUD orchestration only:
361
+
362
+ - `resource.path`, `resourceKey`, `idField`, endpoint key and query context binding
363
+ - list surface orchestration and stable `crudContext`
364
+ - create/edit/view action open mode, route, form binding, params, `initialValue` and back policy
365
+ - delete enablement, confirmation, canonical submit contract and capability policy
366
+ - dialog/drawer/route defaults consumed by `CrudLauncherService`
367
+ - permissions derived from resource capabilities
368
+
369
+ The manifest does not own child component semantics:
370
+
371
+ - `TableConfig` edits must delegate to `praxis-table`
372
+ - `FormConfig` and `FieldMetadata` edits must delegate to `praxis-dynamic-form` or `praxis-metadata-editor`
373
+ - dialog shell changes outside CRUD defaults must delegate to `praxis-dialog` or `praxis-settings-panel`
374
+
375
+ Governed operation families:
376
+
377
+ - `resource.bind`
378
+ - `list.surface.configure`
379
+ - `surface.create.configure`
380
+ - `surface.edit.configure`
381
+ - `surface.view.configure`
382
+ - `delete.enabled.set`
383
+ - `dialog.size.set`
384
+ - `permissions.set`
385
+ - `form.childOperation.delegate`
386
+
387
+ Stable identities are resource path/key, `crudId`, action id and child operation id. Array indexes are not canonical identities. Destructive delete behavior requires explicit confirmation and backend capability support.
388
+
356
389
  To build the library, run:
357
390
 
358
391
  ```bash
@@ -3409,7 +3409,7 @@ class PraxisCrudComponent {
3409
3409
  async onAction(action, row, runtimeEvent) {
3410
3410
  try {
3411
3411
  document.activeElement?.blur();
3412
- const openedByDiscovery = await this.tryOpenDiscoveredCrudSurface(action, row);
3412
+ const openedByDiscovery = await this.tryOpenDiscoveredCrudSurface(action, row, runtimeEvent);
3413
3413
  if (openedByDiscovery) {
3414
3414
  return;
3415
3415
  }
@@ -3704,17 +3704,27 @@ class PraxisCrudComponent {
3704
3704
  resolveFilterCriteria(meta) {
3705
3705
  return this.isRecord(meta?.filterCriteria) ? { ...meta.filterCriteria } : {};
3706
3706
  }
3707
- async tryOpenDiscoveredCrudSurface(action, row) {
3707
+ async tryOpenDiscoveredCrudSurface(action, row, runtimeEvent) {
3708
3708
  const normalizedAction = String(action || '').trim().toLowerCase();
3709
- if (!this.isDiscoveryManagedCrudAction(normalizedAction) || !this.surfaceService) {
3709
+ if (!this.surfaceService) {
3710
3710
  return false;
3711
3711
  }
3712
- const catalog = await this.resolveDiscoveredSurfaceCatalog(normalizedAction, row);
3713
- const surface = this.selectSurfaceForCrudAction(normalizedAction, catalog?.surfaces || []);
3712
+ const providedSurface = this.resolveProvidedSurface(normalizedAction, runtimeEvent?.actionConfig);
3713
+ if (!providedSurface && !this.isDiscoveryManagedCrudAction(normalizedAction)) {
3714
+ return false;
3715
+ }
3716
+ const catalog = providedSurface
3717
+ ? null
3718
+ : await this.resolveDiscoveredSurfaceCatalog(normalizedAction, row);
3719
+ const surface = providedSurface || this.selectSurfaceForCrudAction(normalizedAction, catalog?.surfaces || []);
3714
3720
  const resourcePath = String(catalog?.resourcePath || this.resolveResourcePath(this.resolvedMetadata) || '').trim();
3715
- if (!catalog || !surface || !resourcePath) {
3721
+ if (!surface || !resourcePath) {
3716
3722
  return false;
3717
3723
  }
3724
+ if (surface.availability?.allowed === false) {
3725
+ this.snack.open(translateUnavailableWorkflowMessage(this.i18n, surface.availability), undefined, { duration: 2500 });
3726
+ return true;
3727
+ }
3718
3728
  let payload;
3719
3729
  try {
3720
3730
  payload = this.surfaceOpenAdapter.toPayload(surface, {
@@ -3722,7 +3732,7 @@ class PraxisCrudComponent {
3722
3732
  resourceId: this.resolveRowResourceId(row),
3723
3733
  endpointKey: this.resolvedMetadata?.resource?.endpointKey,
3724
3734
  apiUrlEntry: this.resolveDiscoveryApiEntry(),
3725
- group: catalog.group ?? null,
3735
+ group: catalog?.group ?? null,
3726
3736
  });
3727
3737
  }
3728
3738
  catch {
@@ -3880,6 +3890,23 @@ class PraxisCrudComponent {
3880
3890
  }
3881
3891
  return candidate;
3882
3892
  }
3893
+ resolveProvidedSurface(action, candidate) {
3894
+ if (!candidate || typeof candidate !== 'object') {
3895
+ return null;
3896
+ }
3897
+ const normalizedId = String(candidate.id || '').trim().toLowerCase();
3898
+ if (!normalizedId || normalizedId !== action) {
3899
+ return null;
3900
+ }
3901
+ const surface = candidate;
3902
+ if (!surface.kind || !surface.scope || !surface.path || !surface.method) {
3903
+ return null;
3904
+ }
3905
+ if (!this.isWritableCrudSurface(surface) && !this.isReadableCrudSurface(surface)) {
3906
+ return null;
3907
+ }
3908
+ return surface;
3909
+ }
3883
3910
  selectSurfaceForCrudAction(action, surfaces) {
3884
3911
  const candidates = surfaces
3885
3912
  .filter((surface) => surface.availability?.allowed !== false)
@@ -4412,7 +4439,7 @@ class PraxisCrudComponent {
4412
4439
  />
4413
4440
  }
4414
4441
  }
4415
- `, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
4442
+ `, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "exportAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
4416
4443
  }
4417
4444
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisCrudComponent, decorators: [{
4418
4445
  type: Component,
@@ -4830,7 +4857,7 @@ class DynamicFormDialogHostComponent {
4830
4857
  (formCancel)="onCancel()"
4831
4858
  ></praxis-dynamic-form>
4832
4859
  </mat-dialog-content>
4833
- `, isInline: true, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError"] }] });
4860
+ `, isInline: true, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange"] }] });
4834
4861
  }
4835
4862
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
4836
4863
  type: Component,
@@ -5263,6 +5290,12 @@ const CRUD_AI_CAPABILITIES = {
5263
5290
  { path: 'resource.path', category: 'resource', valueKind: 'string', description: 'Endpoint base do recurso.' },
5264
5291
  { path: 'resource.idField', category: 'resource', valueKind: 'string', description: 'Campo identificador do recurso.' },
5265
5292
  { path: 'resource.endpointKey', category: 'resource', valueKind: 'string', description: 'Chave de endpoint (ApiEndpoint).' },
5293
+ { path: 'queryContext.meta.domainCatalog', category: 'resource', valueKind: 'object', description: 'Referencia leve para contexto semantico/governanca resolvido via Domain Catalog; nao materializa regras em FormConfig.' },
5294
+ { path: 'queryContext.meta.domainCatalog.schemaVersion', category: 'resource', valueKind: 'string', description: 'Versao do contrato DomainCatalogContextHint usado em runtime e prompts.' },
5295
+ { path: 'queryContext.meta.domainCatalog.resourceKey', category: 'resource', valueKind: 'string', description: 'ResourceKey semantico estavel usado para resolver vocabulario/governanca de dominio.' },
5296
+ { path: 'queryContext.meta.domainCatalog.releaseId', category: 'resource', valueKind: 'string', description: 'Release opcional do Domain Catalog usada como fonte do contexto.' },
5297
+ { path: 'queryContext.meta.domainCatalog.query', category: 'resource', valueKind: 'string', description: 'Probe de campo/conceito usado para buscar itens relevantes, como cpf, salario ou status.' },
5298
+ { path: 'queryContext.meta.domainCatalog.intent', category: 'resource', valueKind: 'enum', allowedValues: ['authoring', 'explain', 'validate', 'ai-access-control'], description: 'Intencao de uso do contexto para orientar LLMs e validadores sem expor dados alem do necessario.' },
5266
5299
  // --- Table / Form ---
5267
5300
  { path: 'table', category: 'table', valueKind: 'object', description: 'TableConfig completo (usar catalogo de tabela).' },
5268
5301
  { path: 'form', category: 'form', valueKind: 'object', description: 'FormConfig completo (usar catalogo de formulario).' },
@@ -5319,6 +5352,438 @@ const CRUD_AI_CAPABILITIES = {
5319
5352
  ],
5320
5353
  };
5321
5354
 
5355
+ const resourceBindSchema = {
5356
+ type: 'object',
5357
+ required: ['resourcePath'],
5358
+ properties: {
5359
+ resourcePath: { type: 'string' },
5360
+ resourceKey: { type: 'string' },
5361
+ idField: { type: ['string', 'number'] },
5362
+ endpointKey: { type: 'string' },
5363
+ queryContext: { type: 'object' },
5364
+ },
5365
+ };
5366
+ const surfaceConfigureSchema = {
5367
+ type: 'object',
5368
+ required: ['actionId', 'openMode'],
5369
+ properties: {
5370
+ actionId: { enum: ['create', 'edit', 'view'] },
5371
+ openMode: { enum: ['route', 'modal', 'drawer'] },
5372
+ route: { type: 'string' },
5373
+ formId: { type: 'string' },
5374
+ form: {
5375
+ type: 'object',
5376
+ properties: {
5377
+ schemaUrl: { type: 'string' },
5378
+ submitUrl: { type: 'string' },
5379
+ submitMethod: { enum: ['post', 'put', 'patch', 'delete'] },
5380
+ apiEndpointKey: { type: 'string' },
5381
+ initialValue: { type: 'object' },
5382
+ },
5383
+ },
5384
+ params: {
5385
+ type: 'array',
5386
+ items: {
5387
+ type: 'object',
5388
+ required: ['from', 'to', 'name'],
5389
+ properties: {
5390
+ from: { type: 'string' },
5391
+ to: { enum: ['routeParam', 'query', 'input'] },
5392
+ name: { type: 'string' },
5393
+ },
5394
+ },
5395
+ },
5396
+ back: { type: 'object' },
5397
+ },
5398
+ };
5399
+ const listSurfaceSchema = {
5400
+ type: 'object',
5401
+ properties: {
5402
+ tablePatch: { type: 'object' },
5403
+ queryContext: { type: 'object' },
5404
+ filterCriteria: { type: 'object' },
5405
+ },
5406
+ };
5407
+ const deleteBehaviorSchema = {
5408
+ type: 'object',
5409
+ required: ['enabled'],
5410
+ properties: {
5411
+ enabled: { type: 'boolean' },
5412
+ actionId: { type: 'string' },
5413
+ requiresConfirmation: { type: 'boolean' },
5414
+ autoDelete: { type: 'boolean' },
5415
+ form: {
5416
+ type: 'object',
5417
+ properties: {
5418
+ submitUrl: { type: 'string' },
5419
+ submitMethod: { enum: ['delete'] },
5420
+ apiEndpointKey: { type: 'string' },
5421
+ },
5422
+ },
5423
+ },
5424
+ };
5425
+ const dialogHostSchema = {
5426
+ type: 'object',
5427
+ properties: {
5428
+ defaultOpenMode: { enum: ['route', 'modal', 'drawer'] },
5429
+ modal: {
5430
+ type: 'object',
5431
+ properties: {
5432
+ width: { type: 'string' },
5433
+ height: { type: 'string' },
5434
+ minWidth: { type: 'string' },
5435
+ maxWidth: { type: 'string' },
5436
+ density: { type: 'string' },
5437
+ canMaximize: { type: 'boolean' },
5438
+ rememberLastState: { type: 'boolean' },
5439
+ startMaximized: { type: 'boolean' },
5440
+ disableCloseOnEsc: { type: 'boolean' },
5441
+ disableCloseOnBackdrop: { type: 'boolean' },
5442
+ fullscreenBreakpoint: { type: 'string' },
5443
+ },
5444
+ },
5445
+ back: { type: 'object' },
5446
+ },
5447
+ };
5448
+ const permissionsSchema = {
5449
+ type: 'object',
5450
+ properties: {
5451
+ requiredCapabilities: {
5452
+ type: 'array',
5453
+ items: { enum: ['create', 'view', 'edit', 'delete'] },
5454
+ },
5455
+ actionPermissions: { type: 'object' },
5456
+ denyWhenMissingCapability: { type: 'boolean' },
5457
+ },
5458
+ };
5459
+ const domainGovernanceContextSchema = {
5460
+ type: 'object',
5461
+ required: ['resourceKey', 'query'],
5462
+ properties: {
5463
+ resourceKey: { type: 'string' },
5464
+ releaseId: { type: 'string' },
5465
+ query: { type: 'string' },
5466
+ itemTypes: {
5467
+ type: 'array',
5468
+ items: { enum: ['governance', 'vocabulary', 'relationship'] },
5469
+ },
5470
+ intent: { enum: ['authoring', 'explain', 'validate', 'ai-access-control'] },
5471
+ },
5472
+ };
5473
+ const childDelegateSchema = {
5474
+ type: 'object',
5475
+ required: ['childComponentId', 'childOperationId', 'reason'],
5476
+ properties: {
5477
+ childComponentId: { enum: ['praxis-dynamic-form', 'praxis-table', 'praxis-dialog', 'praxis-settings-panel'] },
5478
+ childOperationId: { type: 'string' },
5479
+ reason: { type: 'string' },
5480
+ childTarget: { type: 'object' },
5481
+ childParams: { type: 'object' },
5482
+ },
5483
+ };
5484
+ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
5485
+ schemaVersion: '1.0.0',
5486
+ componentId: 'praxis-crud',
5487
+ ownerPackage: '@praxisui/crud',
5488
+ configSchemaId: 'CrudMetadata',
5489
+ manifestVersion: '1.0.0',
5490
+ runtimeInputs: [
5491
+ { name: 'metadata', type: 'CrudMetadata | string', description: 'Canonical CRUD metadata or serialized metadata document.' },
5492
+ { name: 'crudId', type: 'string', description: 'Stable CRUD instance id used for table/form identity and persistence.' },
5493
+ { name: 'componentInstanceId', type: 'string', description: 'Optional stable host instance id for multiple CRUD widgets on the same route.' },
5494
+ { name: 'context', type: 'Record<string, unknown>', description: 'Opaque host context used for authoring seeds and launcher inputs.' },
5495
+ { name: 'afterOpen', type: '{ mode: FormOpenMode; action: string }', description: 'Emitted after a CRUD action opens.' },
5496
+ { name: 'afterSave', type: '{ id: string | number; data: unknown }', description: 'Emitted after save; CRUD refetches the list.' },
5497
+ { name: 'afterDelete', type: '{ id: string | number }', description: 'Emitted after delete; CRUD refetches the list.' },
5498
+ ],
5499
+ editableTargets: [
5500
+ { kind: 'resourceBinding', resolver: 'crud-resource-by-path-or-key', description: 'Resource path/key, id field, endpoint key and query context owned by CRUD orchestration.' },
5501
+ { kind: 'listSurface', resolver: 'crud-list-surface', description: 'CRUD-hosted list surface and table delegation boundary.' },
5502
+ { kind: 'createSurface', resolver: 'crud-action-by-id:create', description: 'Create action open mode, route/form binding and launcher inputs.' },
5503
+ { kind: 'editSurface', resolver: 'crud-action-by-id:edit', description: 'Edit action open mode, route/form binding and launcher inputs.' },
5504
+ { kind: 'viewSurface', resolver: 'crud-action-by-id:view', description: 'View action open mode, route/form binding and launcher inputs.' },
5505
+ { kind: 'deleteBehavior', resolver: 'crud-action-by-id:delete', description: 'Delete action enablement, confirmation, endpoint and capability policy.' },
5506
+ { kind: 'dialogHost', resolver: 'crud-dialog-host-defaults', description: 'Route/modal/drawer defaults consumed by CrudLauncherService and DynamicFormDialogHostComponent.' },
5507
+ { kind: 'formBinding', resolver: 'crud-action-form-contract-by-action-id', description: 'CRUD-owned form binding fields: formId, schemaUrl, submitUrl, submitMethod, params and initialValue.' },
5508
+ { kind: 'permissions', resolver: 'crud-resource-capabilities', description: 'CRUD action availability derived from resource capabilities and action permissions.' },
5509
+ { kind: 'domainGovernanceContext', resolver: 'domain-catalog-context-by-resource-key', description: 'Read-only semantic/governance context resolved from Domain Catalog and referenced from CRUD queryContext.meta.' },
5510
+ { kind: 'childOperation', resolver: 'child-authoring-manifest-operation', description: 'Delegated form/table/dialog/settings-panel operation owned by the child component manifest.' },
5511
+ ],
5512
+ operations: [
5513
+ {
5514
+ operationId: 'resource.bind',
5515
+ title: 'Bind CRUD resource',
5516
+ scope: 'dataBinding',
5517
+ targetKind: 'resourceBinding',
5518
+ target: { kind: 'resourceBinding', resolver: 'crud-resource-by-path-or-key', ambiguityPolicy: 'fail', required: true },
5519
+ inputSchema: resourceBindSchema,
5520
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-resource-bind', handlerContract: {
5521
+ reads: ['CrudMetadata.resource', 'api_metadata', 'ResourceDiscoveryService', 'GET /{resource}/capabilities'],
5522
+ writes: ['CrudMetadata.resource', 'CrudMetadata.queryContext', 'PraxisCrudComponent.tableCrudContext'],
5523
+ identityKeys: ['resourcePath', 'resourceKey'],
5524
+ inputSchema: resourceBindSchema,
5525
+ failureModes: ['resource-not-found', 'schema-url-not-canonical', 'capabilities-unavailable', 'id-field-missing'],
5526
+ description: 'Binds CRUD to a canonical resource and validates it against api_metadata/resource capabilities before runtime use.',
5527
+ } }],
5528
+ validators: ['resource-exists-in-api-metadata', 'resource-path-canonical', 'resource-key-stable', 'id-field-known', 'resource-capabilities-resolvable'],
5529
+ affectedPaths: ['resource.path', 'resource.idField', 'resource.endpointKey', 'queryContext', 'filterCriteria'],
5530
+ submissionImpact: 'affects-remote-binding',
5531
+ preconditions: ['crud-metadata-loaded', 'api-metadata-available'],
5532
+ },
5533
+ {
5534
+ operationId: 'domain.governanceContext.attach',
5535
+ title: 'Attach Domain Catalog governance context',
5536
+ scope: 'dataBinding',
5537
+ targetKind: 'domainGovernanceContext',
5538
+ target: { kind: 'domainGovernanceContext', resolver: 'domain-catalog-context-by-resource-key', ambiguityPolicy: 'fail', required: true },
5539
+ inputSchema: domainGovernanceContextSchema,
5540
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-domain-governance-context-attach', handlerContract: {
5541
+ reads: ['CrudMetadata.resource', 'CrudMetadata.queryContext', 'DomainCatalogService.getGovernanceContext', 'DOMAIN_CATALOG_COMPONENT_CONTEXT_PACK', 'api_metadata'],
5542
+ writes: ['CrudMetadata.queryContext.meta.domainCatalog'],
5543
+ identityKeys: ['resourceKey', 'releaseId', 'query'],
5544
+ inputSchema: domainGovernanceContextSchema,
5545
+ failureModes: ['resource-key-missing', 'domain-catalog-release-not-found', 'governance-context-empty', 'ai-usage-forbidden'],
5546
+ description: 'Resolves read-only domain vocabulary/governance context and stores only a lightweight reference/probe under queryContext.meta.domainCatalog.',
5547
+ } }],
5548
+ validators: ['resource-key-stable', 'domain-catalog-context-resolvable', 'domain-catalog-read-only', 'ai-usage-visibility-respected', 'no-form-config-rule-materialization'],
5549
+ affectedPaths: ['queryContext.meta.domainCatalog'],
5550
+ submissionImpact: 'config-only',
5551
+ preconditions: ['crud-metadata-loaded', 'api-metadata-available', 'domain-catalog-service-available'],
5552
+ },
5553
+ {
5554
+ operationId: 'list.surface.configure',
5555
+ title: 'Configure CRUD list surface',
5556
+ scope: 'interaction',
5557
+ targetKind: 'listSurface',
5558
+ target: { kind: 'listSurface', resolver: 'crud-list-surface', ambiguityPolicy: 'fail', required: true },
5559
+ inputSchema: listSurfaceSchema,
5560
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-list-surface-configure', handlerContract: {
5561
+ reads: ['CrudMetadata.table', 'CrudMetadata.queryContext', 'PRAXIS_TABLE_AUTHORING_MANIFEST', 'CRUD_AI_CAPABILITIES'],
5562
+ writes: ['CrudMetadata.table', 'CrudMetadata.queryContext', 'CrudMetadata.filterCriteria'],
5563
+ identityKeys: ['crudId'],
5564
+ inputSchema: listSurfaceSchema,
5565
+ failureModes: ['table-operation-not-delegated', 'table-patch-not-supported', 'query-context-invalid'],
5566
+ description: 'Configures CRUD-owned list orchestration while routing table semantics to the praxis-table manifest.',
5567
+ } }],
5568
+ validators: ['table-child-operation-delegated', 'query-context-valid', 'filter-criteria-bridge-valid', 'crud-context-stable'],
5569
+ affectedPaths: ['table', 'queryContext', 'filterCriteria'],
5570
+ submissionImpact: 'config-only',
5571
+ preconditions: ['crud-metadata-loaded', 'praxis-table-manifest-available'],
5572
+ },
5573
+ {
5574
+ operationId: 'surface.create.configure',
5575
+ title: 'Configure create surface',
5576
+ scope: 'interaction',
5577
+ targetKind: 'createSurface',
5578
+ target: { kind: 'createSurface', resolver: 'crud-action-by-id:create', ambiguityPolicy: 'fail', required: true },
5579
+ inputSchema: surfaceConfigureSchema,
5580
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-create-surface-configure', handlerContract: {
5581
+ reads: ['CrudMetadata.actions', 'CrudLauncherService.resolveOpenMode', 'DynamicFormDialogHostComponent', 'api_metadata'],
5582
+ writes: ['CrudMetadata.actions[]', 'CrudMetadata.actions[].form', 'CrudMetadata.actions[].params', 'CrudMetadata.actions[].back'],
5583
+ identityKeys: ['actionId'],
5584
+ inputSchema: surfaceConfigureSchema,
5585
+ failureModes: ['action-not-found', 'open-mode-binding-incomplete', 'schema-url-not-canonical', 'submit-url-not-canonical', 'resource-create-not-supported'],
5586
+ description: 'Configures create action binding and launcher inputs without editing the child FormConfig.',
5587
+ } }],
5588
+ validators: ['action-exists', 'open-mode-binding-complete', 'schema-url-canonical', 'submit-url-canonical', 'resource-create-supported', 'form-child-operation-delegated'],
5589
+ affectedPaths: ['actions[].openMode', 'actions[].route', 'actions[].formId', 'actions[].form', 'actions[].params', 'actions[].back'],
5590
+ submissionImpact: 'affects-schema-backed-data',
5591
+ preconditions: ['crud-metadata-loaded', 'resource-capabilities-resolved'],
5592
+ },
5593
+ {
5594
+ operationId: 'surface.edit.configure',
5595
+ title: 'Configure edit surface',
5596
+ scope: 'interaction',
5597
+ targetKind: 'editSurface',
5598
+ target: { kind: 'editSurface', resolver: 'crud-action-by-id:edit', ambiguityPolicy: 'fail', required: true },
5599
+ inputSchema: surfaceConfigureSchema,
5600
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-edit-surface-configure', handlerContract: {
5601
+ reads: ['CrudMetadata.actions', 'CrudMetadata.resource.idField', 'CrudLauncherService.resolveOpenMode', 'api_metadata'],
5602
+ writes: ['CrudMetadata.actions[]', 'CrudMetadata.actions[].form', 'CrudMetadata.actions[].params', 'CrudMetadata.actions[].back'],
5603
+ identityKeys: ['actionId'],
5604
+ inputSchema: surfaceConfigureSchema,
5605
+ failureModes: ['action-not-found', 'id-param-missing', 'open-mode-binding-incomplete', 'resource-edit-not-supported', 'submit-url-not-canonical'],
5606
+ description: 'Configures edit action routing/form binding and ensures id parameter mapping is available to the launcher.',
5607
+ } }],
5608
+ validators: ['action-exists', 'id-field-known', 'id-param-mapping-valid', 'open-mode-binding-complete', 'schema-url-canonical', 'submit-url-canonical', 'resource-edit-supported', 'form-child-operation-delegated'],
5609
+ affectedPaths: ['actions[].openMode', 'actions[].route', 'actions[].formId', 'actions[].form', 'actions[].params', 'actions[].back'],
5610
+ submissionImpact: 'affects-schema-backed-data',
5611
+ preconditions: ['crud-metadata-loaded', 'resource-capabilities-resolved'],
5612
+ },
5613
+ {
5614
+ operationId: 'surface.view.configure',
5615
+ title: 'Configure view surface',
5616
+ scope: 'interaction',
5617
+ targetKind: 'viewSurface',
5618
+ target: { kind: 'viewSurface', resolver: 'crud-action-by-id:view', ambiguityPolicy: 'fail', required: true },
5619
+ inputSchema: surfaceConfigureSchema,
5620
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-view-surface-configure', handlerContract: {
5621
+ reads: ['CrudMetadata.actions', 'CrudLauncherService.resolveOpenMode', 'DynamicFormDialogHostComponent', 'api_metadata'],
5622
+ writes: ['CrudMetadata.actions[]', 'CrudMetadata.actions[].form', 'CrudMetadata.actions[].params', 'CrudMetadata.actions[].back'],
5623
+ identityKeys: ['actionId'],
5624
+ inputSchema: surfaceConfigureSchema,
5625
+ failureModes: ['action-not-found', 'open-mode-binding-incomplete', 'resource-view-not-supported', 'readonly-form-delegation-missing'],
5626
+ description: 'Configures view action binding and delegates readonly form behavior to the dynamic-form manifest.',
5627
+ } }],
5628
+ validators: ['action-exists', 'open-mode-binding-complete', 'schema-url-canonical', 'resource-view-supported', 'form-child-operation-delegated', 'readonly-form-delegation-valid'],
5629
+ affectedPaths: ['actions[].openMode', 'actions[].route', 'actions[].formId', 'actions[].form', 'actions[].params', 'actions[].back'],
5630
+ submissionImpact: 'config-only',
5631
+ preconditions: ['crud-metadata-loaded', 'resource-capabilities-resolved'],
5632
+ },
5633
+ {
5634
+ operationId: 'delete.enabled.set',
5635
+ title: 'Configure delete behavior',
5636
+ scope: 'interaction',
5637
+ targetKind: 'deleteBehavior',
5638
+ target: { kind: 'deleteBehavior', resolver: 'crud-action-by-id:delete', ambiguityPolicy: 'fail', required: true },
5639
+ inputSchema: deleteBehaviorSchema,
5640
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-delete-behavior-set', handlerContract: {
5641
+ reads: ['CrudMetadata.actions', 'CrudMetadata.resource', 'GET /{resource}/{id}/capabilities'],
5642
+ writes: ['CrudMetadata.actions[]', 'CrudMetadata.actions[].requiresConfirmation', 'CrudMetadata.actions[].autoDelete', 'CrudMetadata.actions[].form'],
5643
+ identityKeys: ['actionId'],
5644
+ inputSchema: deleteBehaviorSchema,
5645
+ failureModes: ['delete-action-not-found', 'resource-delete-not-supported', 'destructive-delete-not-confirmed', 'delete-submit-url-not-canonical'],
5646
+ description: 'Enables or disables delete behavior with capability checks and explicit confirmation for destructive changes.',
5647
+ } }],
5648
+ destructive: true,
5649
+ requiresConfirmation: true,
5650
+ validators: ['delete-action-exists', 'resource-delete-supported', 'destructive-delete-confirmed', 'submit-url-canonical', 'permissions-delete-valid'],
5651
+ affectedPaths: ['actions[].disabled', 'actions[].requiresConfirmation', 'actions[].autoDelete', 'actions[].form', 'actions[].form.submitUrl', 'actions[].form.submitMethod', 'actions[].form.apiEndpointKey'],
5652
+ submissionImpact: 'affects-schema-backed-data',
5653
+ preconditions: ['crud-metadata-loaded', 'resource-capabilities-resolved', 'explicit-confirmation-provided'],
5654
+ },
5655
+ {
5656
+ operationId: 'dialog.size.set',
5657
+ title: 'Configure CRUD dialog host defaults',
5658
+ scope: 'interaction',
5659
+ targetKind: 'dialogHost',
5660
+ target: { kind: 'dialogHost', resolver: 'crud-dialog-host-defaults', ambiguityPolicy: 'fail', required: true },
5661
+ inputSchema: dialogHostSchema,
5662
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-dialog-host-set', handlerContract: {
5663
+ reads: ['CrudMetadata.defaults', 'CrudLauncherService.resolveOpenMode', 'DialogService', 'CRUD_DRAWER_ADAPTER'],
5664
+ writes: ['CrudMetadata.defaults.openMode', 'CrudMetadata.defaults.modal', 'CrudMetadata.defaults.back'],
5665
+ identityKeys: ['crudId'],
5666
+ inputSchema: dialogHostSchema,
5667
+ failureModes: ['open-mode-unsupported', 'drawer-adapter-missing', 'modal-size-invalid', 'back-policy-invalid'],
5668
+ description: 'Configures CRUD-owned route/modal/drawer defaults consumed by the launcher and dialog host.',
5669
+ } }],
5670
+ validators: ['open-mode-supported', 'modal-size-valid', 'drawer-adapter-available-when-needed', 'back-policy-valid', 'settings-panel-shell-compatible'],
5671
+ affectedPaths: ['defaults.openMode', 'defaults.modal', 'defaults.back'],
5672
+ submissionImpact: 'config-only',
5673
+ preconditions: ['crud-metadata-loaded'],
5674
+ },
5675
+ {
5676
+ operationId: 'permissions.set',
5677
+ title: 'Configure CRUD permissions',
5678
+ scope: 'interaction',
5679
+ targetKind: 'permissions',
5680
+ target: { kind: 'permissions', resolver: 'crud-resource-capabilities', ambiguityPolicy: 'fail', required: true },
5681
+ inputSchema: permissionsSchema,
5682
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-permissions-set', handlerContract: {
5683
+ reads: ['GET /{resource}/capabilities', 'GET /{resource}/{id}/capabilities', 'CrudMetadata.actions'],
5684
+ writes: ['CrudMetadata.actions[].disabled', 'CrudMetadata.actions[].visibleWhen', 'CrudMetadata.actions[].requiresConfirmation'],
5685
+ identityKeys: ['resourcePath'],
5686
+ inputSchema: permissionsSchema,
5687
+ failureModes: ['capability-not-found', 'action-permission-conflict', 'delete-permission-without-confirmation'],
5688
+ description: 'Aligns CRUD action visibility/disablement with resource capabilities without inventing a second permission source.',
5689
+ } }],
5690
+ validators: ['resource-capabilities-resolvable', 'action-permission-supported', 'delete-permission-requires-confirmation', 'permissions-do-not-shadow-backend'],
5691
+ affectedPaths: ['actions[].disabled', 'actions[].visibleWhen', 'actions[].requiresConfirmation'],
5692
+ submissionImpact: 'config-only',
5693
+ preconditions: ['crud-metadata-loaded', 'resource-capabilities-resolved'],
5694
+ },
5695
+ {
5696
+ operationId: 'form.childOperation.delegate',
5697
+ title: 'Delegate child form/table/dialog authoring operation',
5698
+ scope: 'global',
5699
+ targetKind: 'childOperation',
5700
+ target: { kind: 'childOperation', resolver: 'child-authoring-manifest-operation', ambiguityPolicy: 'fail', required: false },
5701
+ inputSchema: childDelegateSchema,
5702
+ effects: [{ kind: 'compile-domain-patch', handler: 'crud-child-operation-delegate', handlerContract: {
5703
+ reads: ['PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST', 'PRAXIS_TABLE_AUTHORING_MANIFEST', 'PRAXIS_DIALOG_AUTHORING_MANIFEST', 'PRAXIS_SETTINGS_PANEL_AUTHORING_MANIFEST'],
5704
+ writes: ['delegatedAuthoringOperations[]'],
5705
+ identityKeys: ['childComponentId', 'childOperationId'],
5706
+ inputSchema: childDelegateSchema,
5707
+ failureModes: ['child-manifest-missing', 'child-operation-not-found', 'attempted-local-child-config-write'],
5708
+ description: 'Records explicit delegation when requested edits belong to child manifests instead of CRUD orchestration.',
5709
+ } }],
5710
+ validators: ['child-manifest-available', 'child-operation-known', 'no-local-form-config-write', 'no-local-table-config-write', 'delegation-target-valid'],
5711
+ affectedPaths: ['delegatedAuthoringOperations'],
5712
+ submissionImpact: 'none',
5713
+ preconditions: ['child-manifest-available'],
5714
+ },
5715
+ ],
5716
+ validators: [
5717
+ { validatorId: 'resource-exists-in-api-metadata', level: 'error', code: 'CRUD_RESOURCE_EXISTS', description: 'Resource must exist in api_metadata or resource discovery.' },
5718
+ { validatorId: 'resource-path-canonical', level: 'error', code: 'CRUD_RESOURCE_PATH_CANONICAL', description: 'Resource path must be canonical and not a local alias.' },
5719
+ { validatorId: 'resource-key-stable', level: 'error', code: 'CRUD_RESOURCE_KEY_STABLE', description: 'Resource key must remain stable for surfaces/actions/capabilities.' },
5720
+ { validatorId: 'id-field-known', level: 'error', code: 'CRUD_ID_FIELD_KNOWN', description: 'CRUD id field must exist for edit/view/delete flows.' },
5721
+ { validatorId: 'resource-capabilities-resolvable', level: 'error', code: 'CRUD_CAPABILITIES_RESOLVABLE', description: 'Resource capabilities must be resolvable before action enablement is authored.' },
5722
+ { validatorId: 'domain-catalog-context-resolvable', level: 'error', code: 'CRUD_DOMAIN_CATALOG_CONTEXT_RESOLVABLE', description: 'Domain Catalog governance context must resolve for the resource key and requested probe before authoring uses it.' },
5723
+ { validatorId: 'domain-catalog-read-only', level: 'error', code: 'CRUD_DOMAIN_CATALOG_READ_ONLY', description: 'Domain Catalog is read-only semantic context; CRUD may reference a probe but must not persist copied catalog rules.' },
5724
+ { validatorId: 'ai-usage-visibility-respected', level: 'error', code: 'CRUD_AI_USAGE_VISIBILITY_RESPECTED', description: 'Authoring must respect Domain Catalog aiUsage.visibility before exposing field data, prompts or generated patches.' },
5725
+ { validatorId: 'no-form-config-rule-materialization', level: 'error', code: 'CRUD_NO_FORM_RULE_MATERIALIZATION', description: 'Shared domain governance must not be materialized into FormConfig rules unless a backend policy explicitly authorizes it.' },
5726
+ { validatorId: 'table-child-operation-delegated', level: 'error', code: 'CRUD_TABLE_CHILD_DELEGATED', description: 'Table semantics must be delegated to praxis-table.' },
5727
+ { validatorId: 'query-context-valid', level: 'error', code: 'CRUD_QUERY_CONTEXT_VALID', description: 'Query context must be valid for the bound resource.' },
5728
+ { validatorId: 'filter-criteria-bridge-valid', level: 'warning', code: 'CRUD_FILTER_CRITERIA_BRIDGE_VALID', description: 'filterCriteria is a bridge; prefer queryContext for new remote authoring.' },
5729
+ { validatorId: 'crud-context-stable', level: 'error', code: 'CRUD_CONTEXT_STABLE', description: 'Authoring must not break stable crudContext references.' },
5730
+ { validatorId: 'action-exists', level: 'error', code: 'CRUD_ACTION_EXISTS', description: 'Target action must exist or be created through CRUD action orchestration.' },
5731
+ { validatorId: 'open-mode-binding-complete', level: 'error', code: 'CRUD_OPEN_MODE_BINDING_COMPLETE', description: 'route requires route; modal/drawer require formId unless resolved by governed overrides.' },
5732
+ { validatorId: 'schema-url-canonical', level: 'error', code: 'CRUD_SCHEMA_URL_CANONICAL', description: 'Schema URLs must be canonical for the bound resource/action.' },
5733
+ { validatorId: 'submit-url-canonical', level: 'error', code: 'CRUD_SUBMIT_URL_CANONICAL', description: 'Submit URL and method must be canonical and declared together.' },
5734
+ { validatorId: 'resource-create-supported', level: 'error', code: 'CRUD_CREATE_SUPPORTED', description: 'Create surface requires resource create capability.' },
5735
+ { validatorId: 'resource-edit-supported', level: 'error', code: 'CRUD_EDIT_SUPPORTED', description: 'Edit surface requires resource edit capability.' },
5736
+ { validatorId: 'resource-view-supported', level: 'error', code: 'CRUD_VIEW_SUPPORTED', description: 'View surface requires resource view capability.' },
5737
+ { validatorId: 'form-child-operation-delegated', level: 'error', code: 'CRUD_FORM_CHILD_DELEGATED', description: 'FormConfig and FieldMetadata edits must delegate to dynamic-form/metadata-editor manifests.' },
5738
+ { validatorId: 'id-param-mapping-valid', level: 'error', code: 'CRUD_ID_PARAM_MAPPING_VALID', description: 'Edit/view actions must map resource id into route/query/input as required.' },
5739
+ { validatorId: 'readonly-form-delegation-valid', level: 'error', code: 'CRUD_READONLY_FORM_DELEGATED', description: 'Readonly form behavior belongs to the dynamic-form manifest.' },
5740
+ { validatorId: 'delete-action-exists', level: 'error', code: 'CRUD_DELETE_ACTION_EXISTS', description: 'Delete behavior requires a delete action target.' },
5741
+ { validatorId: 'resource-delete-supported', level: 'error', code: 'CRUD_DELETE_SUPPORTED', description: 'Delete behavior requires resource delete capability.' },
5742
+ { validatorId: 'destructive-delete-confirmed', level: 'error', code: 'CRUD_DELETE_CONFIRMED', description: 'Destructive delete behavior requires explicit confirmation.' },
5743
+ { validatorId: 'permissions-delete-valid', level: 'error', code: 'CRUD_DELETE_PERMISSION_VALID', description: 'Delete permission cannot bypass resource capabilities or confirmation policy.' },
5744
+ { validatorId: 'open-mode-supported', level: 'error', code: 'CRUD_OPEN_MODE_SUPPORTED', description: 'Open mode must be route, modal or drawer.' },
5745
+ { validatorId: 'modal-size-valid', level: 'error', code: 'CRUD_MODAL_SIZE_VALID', description: 'Modal sizing defaults must be valid DialogConfig values.' },
5746
+ { validatorId: 'drawer-adapter-available-when-needed', level: 'error', code: 'CRUD_DRAWER_ADAPTER_AVAILABLE', description: 'Drawer open mode requires a host-provided drawer adapter.' },
5747
+ { validatorId: 'back-policy-valid', level: 'error', code: 'CRUD_BACK_POLICY_VALID', description: 'Back policy must be valid for route/modal/drawer behavior.' },
5748
+ { validatorId: 'settings-panel-shell-compatible', level: 'warning', code: 'CRUD_SETTINGS_PANEL_COMPATIBLE', description: 'Authoring shell must preserve apply/save/reset semantics.' },
5749
+ { validatorId: 'action-permission-supported', level: 'error', code: 'CRUD_ACTION_PERMISSION_SUPPORTED', description: 'Action permissions must map to supported resource capabilities.' },
5750
+ { validatorId: 'delete-permission-requires-confirmation', level: 'error', code: 'CRUD_DELETE_PERMISSION_CONFIRMATION', description: 'Delete permission enablement requires confirmation policy.' },
5751
+ { validatorId: 'permissions-do-not-shadow-backend', level: 'error', code: 'CRUD_PERMISSIONS_NO_BACKEND_SHADOW', description: 'UI permissions must not shadow backend capability denial.' },
5752
+ { validatorId: 'child-manifest-available', level: 'error', code: 'CRUD_CHILD_MANIFEST_AVAILABLE', description: 'Delegated child manifest must be available.' },
5753
+ { validatorId: 'child-operation-known', level: 'error', code: 'CRUD_CHILD_OPERATION_KNOWN', description: 'Delegated operation must exist in the child manifest.' },
5754
+ { validatorId: 'no-local-form-config-write', level: 'error', code: 'CRUD_NO_LOCAL_FORM_CONFIG_WRITE', description: 'CRUD must not locally redefine FormConfig semantics.' },
5755
+ { validatorId: 'no-local-table-config-write', level: 'error', code: 'CRUD_NO_LOCAL_TABLE_CONFIG_WRITE', description: 'CRUD must not locally redefine TableConfig semantics.' },
5756
+ { validatorId: 'delegation-target-valid', level: 'error', code: 'CRUD_DELEGATION_TARGET_VALID', description: 'Delegation target must be resolvable by the child manifest.' },
5757
+ ],
5758
+ roundTripRequirements: [
5759
+ 'CrudMetadata is the canonical CRUD document shape.',
5760
+ 'CrudAuthoringDocument wraps CrudMetadata without introducing host-local aliases.',
5761
+ 'Resource path and resource key have separate semantics: path is operational, key is discovery/capability identity.',
5762
+ 'Domain Catalog context is referenced through resourceKey/release/probe and stays read-only; CRUD may attach queryContext.meta.domainCatalog but must not copy shared governance rules into FormConfig.',
5763
+ 'Open mode round-trip must preserve route, formId, form contract, params, initialValue and back policy.',
5764
+ 'FormConfig and FieldMetadata edits must delegate to dynamic-form or metadata-editor manifests.',
5765
+ 'TableConfig edits must delegate to praxis-table; CRUD owns only shell orchestration and list surface binding.',
5766
+ 'Delete behavior is destructive and requires explicit confirmation plus backend capability support.',
5767
+ 'crudContext must remain reference-stable across change detection cycles.',
5768
+ ],
5769
+ examples: [
5770
+ { id: 'crud-bind-funcionarios-resource', request: 'Bind this CRUD to funcionarios using id as the identifier.', operationId: 'resource.bind', target: 'resource:funcionarios', params: { resourcePath: '/funcionarios', resourceKey: 'funcionarios', idField: 'id' }, isPositive: true },
5771
+ { id: 'crud-attach-lgpd-cpf-context', request: 'Use the Domain Catalog LGPD context for cpf before changing the employee CRUD.', operationId: 'domain.governanceContext.attach', target: 'domainCatalog:human-resources.funcionarios:cpf', params: { resourceKey: 'human-resources.funcionarios', query: 'cpf', itemTypes: ['governance'], intent: 'authoring' }, isPositive: true },
5772
+ { id: 'crud-list-query-context', request: 'Filter the list by active employees using the canonical query context.', operationId: 'list.surface.configure', target: 'listSurface', params: { queryContext: { filters: [{ field: 'active', operator: 'eq', value: true }] } }, isPositive: true },
5773
+ { id: 'crud-create-modal', request: 'Open create in a modal with the employee form.', operationId: 'surface.create.configure', target: 'action:create', params: { actionId: 'create', openMode: 'modal', formId: 'employee-create' }, isPositive: true },
5774
+ { id: 'crud-edit-route-with-id', request: 'Edit should navigate to /employees/:id and map the row id.', operationId: 'surface.edit.configure', target: 'action:edit', params: { actionId: 'edit', openMode: 'route', route: '/employees/:id/edit', params: [{ from: 'id', to: 'routeParam', name: 'id' }] }, isPositive: true },
5775
+ { id: 'crud-view-drawer-readonly', request: 'Open view in a drawer and keep the form readonly.', operationId: 'surface.view.configure', target: 'action:view', params: { actionId: 'view', openMode: 'drawer', formId: 'employee-view' }, isPositive: true },
5776
+ { id: 'crud-delete-enable-confirmed', request: 'Enable delete but require confirmation.', operationId: 'delete.enabled.set', target: 'action:delete', params: { enabled: true, actionId: 'delete', requiresConfirmation: true, autoDelete: true }, isPositive: true },
5777
+ { id: 'crud-dialog-size-medium', request: 'Make CRUD modals 900px wide and remember their last state.', operationId: 'dialog.size.set', target: 'dialogHost', params: { defaultOpenMode: 'modal', modal: { width: '900px', rememberLastState: true } }, isPositive: true },
5778
+ { id: 'crud-permissions-no-delete', request: 'Hide delete when the resource capabilities do not allow it.', operationId: 'permissions.set', target: 'permissions:delete', params: { requiredCapabilities: ['delete'], denyWhenMissingCapability: true }, isPositive: true },
5779
+ { id: 'crud-delegate-form-layout', request: 'Add a new field to the create form.', operationId: 'form.childOperation.delegate', target: 'praxis-dynamic-form:field.add', params: { childComponentId: 'praxis-dynamic-form', childOperationId: 'field.add', reason: 'Form field semantics belong to dynamic-form' }, isPositive: true },
5780
+ { id: 'crud-delegate-table-column', request: 'Add a salary column to the list table.', operationId: 'form.childOperation.delegate', target: 'praxis-table:column.add', params: { childComponentId: 'praxis-table', childOperationId: 'column.add', reason: 'Table column semantics belong to praxis-table' }, isPositive: true },
5781
+ { id: 'crud-reject-local-form-rules', request: 'Put this dynamic-form validation rule directly inside CRUD.', operationId: 'form.childOperation.delegate', target: 'praxis-dynamic-form:rules.add', params: { childComponentId: 'praxis-dynamic-form', childOperationId: 'rules.add', reason: 'CRUD must not redefine FormConfig rules' }, isPositive: false },
5782
+ { id: 'crud-reject-copy-domain-rule-to-formconfig', request: 'Copy the LGPD rule from Domain Catalog directly into the create form JSON.', operationId: 'domain.governanceContext.attach', target: 'domainCatalog:human-resources.funcionarios:cpf', params: { resourceKey: 'human-resources.funcionarios', query: 'cpf', itemTypes: ['governance'], intent: 'authoring' }, isPositive: false },
5783
+ { id: 'crud-reject-delete-without-capability', request: 'Force delete even though the backend does not expose delete capability.', operationId: 'delete.enabled.set', target: 'action:delete', params: { enabled: true, actionId: 'delete', requiresConfirmation: false }, isPositive: false },
5784
+ ],
5785
+ };
5786
+
5322
5787
  /*
5323
5788
  * Public API Surface of praxis-crud
5324
5789
  */
@@ -5327,4 +5792,4 @@ const CRUD_AI_CAPABILITIES = {
5327
5792
  * Generated bundle index. Do not edit.
5328
5793
  */
5329
5794
 
5330
- export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudMetadataEditorComponent, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, assertCrudMetadata, createCrudAuthoringDocument, findCrudAction, normalizeCrudAuthoringDocument, openCrudMetadataEditor, parseLegacyOrCrudDocument, providePraxisCrudMetadata, serializeCrudAuthoringDocument, validateCrudAuthoringDocument };
5795
+ export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudMetadataEditorComponent, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_AUTHORING_MANIFEST, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, assertCrudMetadata, createCrudAuthoringDocument, findCrudAction, normalizeCrudAuthoringDocument, openCrudMetadataEditor, parseLegacyOrCrudDocument, providePraxisCrudMetadata, serializeCrudAuthoringDocument, validateCrudAuthoringDocument };
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _praxisui_core from '@praxisui/core';
2
- import { ApiEndpoint, TableConfig, PraxisDataQueryContext, FormConfig, BackConfig, RowAction, ToolbarAction, ApiUrlEntry, ResourceActionCatalogItem, LoadingState, RestApiLinks, ResourceCapabilitySnapshot, GenericCrudService, AsyncConfigStorage, ComponentDocMeta, AiCapabilityCategory, AiValueKind, AiCapability, AiCapabilityCatalog, SettingsValueProvider } from '@praxisui/core';
2
+ import { ApiEndpoint, TableConfig, PraxisDataQueryContext, FormConfig, BackConfig, RowAction, ToolbarAction, ApiUrlEntry, ResourceActionCatalogItem, ResourceSurfaceCatalogItem, LoadingState, RestApiLinks, ResourceCapabilitySnapshot, GenericCrudService, AsyncConfigStorage, ComponentDocMeta, AiCapabilityCategory, AiValueKind, AiCapability, AiCapabilityCatalog, ComponentAuthoringManifest, SettingsValueProvider } from '@praxisui/core';
3
3
  export { BackConfig } from '@praxisui/core';
4
4
  import * as _angular_core from '@angular/core';
5
5
  import { NgZone, OnChanges, EventEmitter, SimpleChanges, OnInit, Provider } from '@angular/core';
@@ -123,7 +123,7 @@ interface CrudValidationContext {
123
123
  }
124
124
 
125
125
  type CrudActionRuntimeEvent = {
126
- actionConfig?: ResourceActionCatalogItem | null;
126
+ actionConfig?: ResourceActionCatalogItem | ResourceSurfaceCatalogItem | null;
127
127
  };
128
128
  type CrudContextAction = {
129
129
  action: string;
@@ -230,6 +230,7 @@ declare class PraxisCrudComponent implements OnChanges {
230
230
  private resolveDiscoveredActionCatalog;
231
231
  private selectDiscoveredWorkflowAction;
232
232
  private resolveProvidedWorkflowAction;
233
+ private resolveProvidedSurface;
233
234
  private selectSurfaceForCrudAction;
234
235
  private getPreferredSurfaceIdsForCrudAction;
235
236
  private isDiscoveryManagedCrudAction;
@@ -399,6 +400,8 @@ interface CapabilityCatalog extends AiCapabilityCatalog {
399
400
  }
400
401
  declare const CRUD_AI_CAPABILITIES: CapabilityCatalog;
401
402
 
403
+ declare const PRAXIS_CRUD_AUTHORING_MANIFEST: ComponentAuthoringManifest;
404
+
402
405
  declare function createCrudAuthoringDocument(source: {
403
406
  metadata?: unknown;
404
407
  }): CrudAuthoringDocument;
@@ -594,5 +597,5 @@ type OpenCrudMetadataEditorOptions = {
594
597
  };
595
598
  declare function openCrudMetadataEditor(settings: SettingsPanelService, opts?: OpenCrudMetadataEditorOptions): _praxisui_settings_panel.SettingsPanelRef;
596
599
 
597
- export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudMetadataEditorComponent, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, assertCrudMetadata, createCrudAuthoringDocument, findCrudAction, normalizeCrudAuthoringDocument, openCrudMetadataEditor, parseLegacyOrCrudDocument, providePraxisCrudMetadata, serializeCrudAuthoringDocument, validateCrudAuthoringDocument };
600
+ export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudMetadataEditorComponent, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_AUTHORING_MANIFEST, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, assertCrudMetadata, createCrudAuthoringDocument, findCrudAction, normalizeCrudAuthoringDocument, openCrudMetadataEditor, parseLegacyOrCrudDocument, providePraxisCrudMetadata, serializeCrudAuthoringDocument, validateCrudAuthoringDocument };
598
601
  export type { Capability, CapabilityCatalog, CapabilityCategory, CrudAction, CrudActionFormContract, CrudActionResolutionMode, CrudAuthoringDocument, CrudAuthoringWidgetPersistenceEvent, CrudDefaults, CrudEditorDiagnostic, CrudHeaderConfig, CrudHeaderVariant, CrudMetadata, CrudMetadataAssertionOptions, CrudParamMapping, CrudResource, CrudValidationContext, DialogConfig, DialogRef, FormOpenMode, OpenCrudMetadataEditorOptions, ValueKind };
package/package.json CHANGED
@@ -1,15 +1,21 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "8.0.0-beta.2",
3
+ "version": "8.0.0-beta.20",
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": "^20.1.0",
7
7
  "@angular/core": "^20.1.0",
8
- "@praxisui/dynamic-form": "^8.0.0-beta.2",
9
- "@praxisui/table": "^8.0.0-beta.2",
10
- "@praxisui/core": "^8.0.0-beta.2",
11
- "@praxisui/dynamic-fields": "^8.0.0-beta.2",
12
- "@praxisui/settings-panel": "^8.0.0-beta.2"
8
+ "@praxisui/dynamic-form": "^8.0.0-beta.20",
9
+ "@praxisui/table": "^8.0.0-beta.20",
10
+ "@praxisui/core": "^8.0.0-beta.20",
11
+ "@praxisui/dynamic-fields": "^8.0.0-beta.20",
12
+ "@praxisui/settings-panel": "^8.0.0-beta.20",
13
+ "@angular/cdk": "^20.1.0",
14
+ "@angular/forms": "^20.1.0",
15
+ "@angular/material": "^20.1.0",
16
+ "@angular/router": "^20.1.0",
17
+ "@praxisui/ai": "^8.0.0-beta.20",
18
+ "rxjs": "~7.8.0"
13
19
  },
14
20
  "dependencies": {
15
21
  "tslib": "^2.3.0"