@praxisui/core 9.0.0-beta.37 → 9.0.0-beta.39

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-07-06T00:01:30.215Z",
3
+ "generatedAt": "2026-07-06T14:21:26.432Z",
4
4
  "packageName": "@praxisui/core",
5
- "packageVersion": "9.0.0-beta.37",
5
+ "packageVersion": "9.0.0-beta.39",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 4,
@@ -3392,6 +3392,36 @@
3392
3392
  "id": "praxis-related-resource-outlet",
3393
3393
  "description": "Runtime genérico para materializar surface.relatedResource como tabela filha governada por metadata.",
3394
3394
  "inputs": [
3395
+ {
3396
+ "name": "surfaceId",
3397
+ "type": "string | null",
3398
+ "required": false
3399
+ },
3400
+ {
3401
+ "name": "surfaceCatalog",
3402
+ "type": "ResourceSurfaceCatalogResponse | null",
3403
+ "required": false
3404
+ },
3405
+ {
3406
+ "name": "discoverySource",
3407
+ "type": "ResourceLinkSource | null",
3408
+ "required": false
3409
+ },
3410
+ {
3411
+ "name": "parentLinks",
3412
+ "type": "RestApiLinks | RestApiResponse<unknown> | null",
3413
+ "required": false
3414
+ },
3415
+ {
3416
+ "name": "apiEndpointKey",
3417
+ "type": "ApiEndpoint | null",
3418
+ "required": false
3419
+ },
3420
+ {
3421
+ "name": "apiUrlEntry",
3422
+ "type": "ApiUrlEntry | null",
3423
+ "required": false
3424
+ },
3395
3425
  {
3396
3426
  "name": "surface",
3397
3427
  "type": "ResourceSurfaceCatalogItem | null",
@@ -3473,9 +3503,9 @@
3473
3503
  {
3474
3504
  "chunkIndex": 0,
3475
3505
  "chunkKind": "summary",
3476
- "content": "Component ID: praxis-related-resource-outlet\nSelector: praxis-related-resource-outlet\nFriendly Name: Related Resource Outlet\nDescription: Runtime genérico para materializar surface.relatedResource como tabela filha governada por metadata.\nLib/Package: @praxisui/core\nTags: resource, surface, related-resource, runtime, metadata-driven\nInputs:\n - surface (ResourceSurfaceCatalogItem | null)\n - parentRecord (Record<string, unknown> | null)\n - parentResourceId (string | number | null)\n - parentResourcePath (string | null)\n - queryContext (RelatedResourceQueryContext | null)\n - mode ('inline' | 'open-action')\n - state (RelatedResourceResolutionState | null)\n - compact (boolean)\n - strictValidation (boolean)\nOutputs:\n - surfaceOpen (SurfaceOpenPayload)\n - widgetEvent (WidgetEventEnvelope)\n - resourceEvent (PraxisResourceEvent)\n",
3506
+ "content": "Component ID: praxis-related-resource-outlet\nSelector: praxis-related-resource-outlet\nFriendly Name: Related Resource Outlet\nDescription: Runtime genérico para materializar surface.relatedResource como tabela filha governada por metadata.\nLib/Package: @praxisui/core\nTags: resource, surface, related-resource, runtime, metadata-driven\nInputs:\n - surfaceId (string | null)\n - surfaceCatalog (ResourceSurfaceCatalogResponse | null)\n - discoverySource (ResourceLinkSource | null)\n - parentLinks (RestApiLinks | RestApiResponse<unknown> | null)\n - apiEndpointKey (ApiEndpoint | null)\n - apiUrlEntry (ApiUrlEntry | null)\n - surface (ResourceSurfaceCatalogItem | null)\n - parentRecord (Record<string, unknown> | null)\n - parentResourceId (string | number | null)\n - parentResourcePath (string | null)\n - queryContext (RelatedResourceQueryContext | null)\n - mode ('inline' | 'open-action')\n - state (RelatedResourceResolutionState | null)\n - compact (boolean)\n - strictValidation (boolean)\nOutputs:\n - surfaceOpen (SurfaceOpenPayload)\n - widgetEvent (WidgetEventEnvelope)\n - resourceEvent (PraxisResourceEvent)\n",
3477
3507
  "sourcePointer": "praxis-ui-angular/projects/praxis-core/src/lib/surfaces/praxis-related-resource-outlet.metadata.ts",
3478
- "contentHash": "e70544431cec6448df159ac05ec1a8ab672b70cf9c60fd76fd01ab849f18d48d",
3508
+ "contentHash": "116c99da68c5132d32f60a7ad56cd1ed8d5cb918111fffbae4e93eaed2097735",
3479
3509
  "sourceKind": "component_definition",
3480
3510
  "sourceId": "praxis-related-resource-outlet",
3481
3511
  "corpusVersion": "1.0.0"
@@ -3483,9 +3513,9 @@
3483
3513
  {
3484
3514
  "chunkIndex": 1,
3485
3515
  "chunkKind": "recipe",
3486
- "content": "{\n \"_comment\": \"Recipe para agentes usarem surface.relatedResource com o runtime canonico praxis-related-resource-outlet.\",\n \"_comment2\": \"O filtro pai-filho deve vir de relatedResource.childParentField; nao duplique a mesma regra em composition.links quando o outlet puder resolver.\",\n \"templateMeta\": {\n \"registryKey\": \"praxis-related-resource-outlet\",\n \"variantId\": \"inline-table-child-resource\",\n \"status\": \"public-example\",\n \"useWhen\": \"Render a related child resource from ResourceSurfaceCatalogItem.relatedResource without hand-writing table filters or host-specific code.\",\n \"examplePrompts\": [\n \"renderize recursos relacionados de uma linha selecionada\",\n \"crie tabela filha a partir de relatedResource\",\n \"mostrar participantes filtrados pela missao selecionada sem composition manual\"\n ]\n },\n \"platformValue\": {\n \"canonicalContract\": \"praxis-metadata-starter publishes ResourceSurfaceCatalogItem.relatedResource in the surfaces catalog.\",\n \"angularRuntime\": \"@praxisui/core resolves relatedResource into a SurfaceOpenPayload and materializes praxis-table through DynamicWidgetLoader.\",\n \"migrationValue\": \"Hosts keep only parentRecord or parentResourceId in the page; parent-child filtering is declared once in metadata.\",\n \"agentCorpus\": \"This recipe keeps parentResourceKey, childResourceKey, childResourcePath and childParentField explicit so agents do not infer relationships from labels.\"\n },\n \"authoringGuidance\": [\n \"Use praxis-related-resource-outlet when a catalog surface already includes relatedResource.\",\n \"Pass parentRecord when the selected row is available; pass parentResourceId when only the id is available.\",\n \"Do not repeat childParentField filters in composition.links unless the page intentionally overrides queryContext.\",\n \"Keep availability fail-closed; permission-limited and not-found states should render as outlet states instead of empty tables.\",\n \"Use mode=open-action when the host owns drawer/dialog orchestration; use mode=inline for embedded child tables.\"\n ],\n \"teaches\": [\n \"surface.relatedResource\",\n \"praxis-related-resource-outlet\",\n \"related-resource.childParentField\",\n \"related-resource.childResourcePath\",\n \"resourceEvent\",\n \"surfaceOpen\"\n ],\n \"page\": {\n \"layout\": {\n \"orientation\": \"columns\",\n \"columns\": 2,\n \"gap\": \"16px\"\n },\n \"widgets\": [\n {\n \"key\": \"missionParticipants\",\n \"shell\": {\n \"kind\": \"dashboard-card\",\n \"icon\": \"hub\",\n \"title\": \"Related participants\",\n \"subtitle\": \"Child table resolved from surface.relatedResource\"\n },\n \"definition\": {\n \"id\": \"praxis-related-resource-outlet\",\n \"bindingOrder\": [\n \"surface\",\n \"parentRecord\",\n \"parentResourcePath\",\n \"mode\"\n ],\n \"inputs\": {\n \"mode\": \"inline\",\n \"parentResourcePath\": \"operations/missoes\",\n \"parentRecord\": {\n \"id\": 1,\n \"titulo\": \"Atlantic Shield\",\n \"status\": \"PLANEJADA\"\n },\n \"surface\": {\n \"id\": \"team\",\n \"resourceKey\": \"operations.missoes\",\n \"kind\": \"READ_PROJECTION\",\n \"scope\": \"ITEM\",\n \"title\": \"Equipe da missão\",\n \"description\": \"Lista participantes, papéis e resultados associados à missão\",\n \"operationId\": \"getTeam\",\n \"path\": \"/api/operations/missoes/{id}/team\",\n \"method\": \"GET\",\n \"schemaId\": \"missao-team-response\",\n \"schemaUrl\": \"/schemas/filtered?path=%2Fapi%2Foperations%2Fmissoes%2F%7Bid%7D%2Fteam&operation=get&schemaType=response\",\n \"responseCardinality\": \"COLLECTION\",\n \"availability\": {\n \"allowed\": true,\n \"metadata\": {}\n },\n \"order\": 50,\n \"tags\": [\n \"mission\",\n \"team\",\n \"read-projection\",\n \"related-resource\"\n ],\n \"relatedResource\": {\n \"parentResourceKey\": \"operations.missoes\",\n \"parentIdPathVariable\": \"id\",\n \"childResourceKey\": \"operations.missao-participantes\",\n \"childResourcePath\": \"/api/operations/missao-participantes\",\n \"childParentField\": \"missaoId\",\n \"selectable\": true,\n \"selectionKeyField\": \"id\",\n \"childOperations\": [\n \"FILTER\",\n \"LIST\",\n \"CREATE\",\n \"UPDATE\",\n \"DELETE\"\n ]\n }\n }\n },\n \"outputs\": {\n \"widgetEvent\": \"emit\",\n \"resourceEvent\": \"emit\",\n \"surfaceOpen\": \"emit\"\n }\n }\n }\n ]\n }\n}\n",
3516
+ "content": "{\n \"_comment\": \"Recipe para agentes usarem surface.relatedResource com o runtime canonico praxis-related-resource-outlet.\",\n \"_comment2\": \"O filtro pai-filho deve vir de relatedResource.childParentField; nao duplique a mesma regra em composition.links quando o outlet puder resolver.\",\n \"templateMeta\": {\n \"registryKey\": \"praxis-related-resource-outlet\",\n \"variantId\": \"inline-table-child-resource\",\n \"status\": \"public-example\",\n \"useWhen\": \"Render a related child resource from surfaceId or ResourceSurfaceCatalogItem.relatedResource without hand-writing table filters or host-specific code.\",\n \"examplePrompts\": [\n \"renderize recursos relacionados de uma linha selecionada\",\n \"crie tabela filha a partir de relatedResource\",\n \"mostrar participantes filtrados pela missao selecionada sem composition manual\"\n ]\n },\n \"platformValue\": {\n \"canonicalContract\": \"praxis-metadata-starter publishes ResourceSurfaceCatalogItem.relatedResource in the surfaces catalog.\",\n \"angularRuntime\": \"@praxisui/core discovers the requested surface by surfaceId when needed, resolves relatedResource into a SurfaceOpenPayload and materializes praxis-table through DynamicWidgetLoader.\",\n \"migrationValue\": \"Hosts keep only surfaceId plus parentRecord or parentResourceId in the page; parent-child filtering is declared once in metadata.\",\n \"agentCorpus\": \"This recipe keeps surfaceId, parentResourcePath, parentResourceId and parentRecord explicit so agents do not infer relationships from labels.\"\n },\n \"authoringGuidance\": [\n \"Use praxis-related-resource-outlet with surfaceId when the parent resource publishes item-level surfaces.\",\n \"Pass surface directly only when the host already has the ResourceSurfaceCatalogItem available.\",\n \"Pass parentRecord when the selected row is available; pass parentResourceId when only the id is available.\",\n \"Do not repeat childParentField filters in composition.links unless the page intentionally overrides queryContext.\",\n \"Keep availability fail-closed; permission-limited and not-found states should render as outlet states instead of empty tables.\",\n \"Use mode=open-action when the host owns drawer/dialog orchestration; use mode=inline for embedded child tables.\"\n ],\n \"teaches\": [\n \"surface.relatedResource\",\n \"surfaceId\",\n \"praxis-related-resource-outlet\",\n \"related-resource.childParentField\",\n \"related-resource.childResourcePath\",\n \"resourceEvent\",\n \"surfaceOpen\"\n ],\n \"page\": {\n \"layout\": {\n \"orientation\": \"columns\",\n \"columns\": 2,\n \"gap\": \"16px\"\n },\n \"widgets\": [\n {\n \"key\": \"missionParticipants\",\n \"shell\": {\n \"kind\": \"dashboard-card\",\n \"icon\": \"hub\",\n \"title\": \"Related participants\",\n \"subtitle\": \"Child table resolved from surface.relatedResource\"\n },\n \"definition\": {\n \"id\": \"praxis-related-resource-outlet\",\n \"bindingOrder\": [\n \"surfaceId\",\n \"parentRecord\",\n \"parentResourceId\",\n \"parentResourcePath\",\n \"mode\"\n ],\n \"inputs\": {\n \"mode\": \"inline\",\n \"surfaceId\": \"team\",\n \"parentResourcePath\": \"operations/missoes\",\n \"parentResourceId\": 1,\n \"parentRecord\": {\n \"id\": 1,\n \"titulo\": \"Atlantic Shield\",\n \"status\": \"PLANEJADA\"\n }\n },\n \"outputs\": {\n \"widgetEvent\": \"emit\",\n \"resourceEvent\": \"emit\",\n \"surfaceOpen\": \"emit\"\n }\n }\n }\n ]\n }\n}\n",
3487
3517
  "sourcePointer": "praxis-ui-angular/examples/ai-recipes/praxis-related-resource-outlet.json",
3488
- "contentHash": "e1c32c7df8fe6f70e0987eacb3215f61ba4170ed2f2059fe3b4113c9d3e9db29",
3518
+ "contentHash": "e9e842635ad7211ff2f021ebbcc1071a8a491cd5c88885525ba153a8a49223b8",
3489
3519
  "sourceKind": "component_definition",
3490
3520
  "sourceId": "praxis-related-resource-outlet",
3491
3521
  "corpusVersion": "1.0.0"
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, InjectionToken, Injectable, inject, Inject, Optional, makeEnvironmentProviders, APP_INITIALIZER, signal, computed, DestroyRef, ENVIRONMENT_INITIALIZER, ErrorHandler, EventEmitter, Output, Input, ChangeDetectionStrategy, Directive, SecurityContext, ViewContainerRef, SimpleChange, ContentChild, HostBinding, HostListener, ViewChildren, ViewChild, input, output } from '@angular/core';
2
+ import { Component, InjectionToken, Injectable, inject, Inject, Optional, makeEnvironmentProviders, APP_INITIALIZER, signal, computed, DestroyRef, ENVIRONMENT_INITIALIZER, ErrorHandler, EventEmitter, Output, Input, ChangeDetectionStrategy, Directive, SecurityContext, ViewContainerRef, SimpleChange, ContentChild, HostBinding, HostListener, ViewChildren, ViewChild, Injector, input, output, effect } from '@angular/core';
3
3
  import * as i1 from '@angular/common/http';
4
4
  import { HttpHeaders, HttpClient, HttpParams, HttpResponse, HttpContextToken, HTTP_INTERCEPTORS, withInterceptors } from '@angular/common/http';
5
5
  import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject, map as map$1, switchMap as switchMap$1 } from 'rxjs';
@@ -14092,6 +14092,9 @@ class SurfaceOpenMaterializerService {
14092
14092
  if (widgetId !== 'praxis-table') {
14093
14093
  return true;
14094
14094
  }
14095
+ if (payload.context?.['relatedResource']) {
14096
+ return false;
14097
+ }
14095
14098
  const inputs = widget.inputs || {};
14096
14099
  return Boolean(inputs['resourcePath']
14097
14100
  || inputs['queryContext']
@@ -36769,8 +36772,19 @@ const RELATED_RESOURCE_OUTLET_I18N_CONFIG = {
36769
36772
 
36770
36773
  class PraxisRelatedResourceOutletComponent {
36771
36774
  resolver = inject(RelatedResourceSurfaceResolverService);
36775
+ materializer = inject(SurfaceOpenMaterializerService);
36772
36776
  i18n = inject(PraxisI18nService);
36777
+ injector = inject(Injector);
36778
+ discoverySubscription;
36779
+ discoveryRequestKey = '';
36780
+ materializationRequestKey = '';
36773
36781
  surface = input(null, ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
36782
+ surfaceId = input(null, ...(ngDevMode ? [{ debugName: "surfaceId" }] : /* istanbul ignore next */ []));
36783
+ surfaceCatalog = input(null, ...(ngDevMode ? [{ debugName: "surfaceCatalog" }] : /* istanbul ignore next */ []));
36784
+ discoverySource = input(null, ...(ngDevMode ? [{ debugName: "discoverySource" }] : /* istanbul ignore next */ []));
36785
+ parentLinks = input(null, ...(ngDevMode ? [{ debugName: "parentLinks" }] : /* istanbul ignore next */ []));
36786
+ apiEndpointKey = input(null, ...(ngDevMode ? [{ debugName: "apiEndpointKey" }] : /* istanbul ignore next */ []));
36787
+ apiUrlEntry = input(null, ...(ngDevMode ? [{ debugName: "apiUrlEntry" }] : /* istanbul ignore next */ []));
36774
36788
  parentRecord = input(null, ...(ngDevMode ? [{ debugName: "parentRecord" }] : /* istanbul ignore next */ []));
36775
36789
  parentResourceId = input(null, ...(ngDevMode ? [{ debugName: "parentResourceId" }] : /* istanbul ignore next */ []));
36776
36790
  parentResourcePath = input(null, ...(ngDevMode ? [{ debugName: "parentResourcePath" }] : /* istanbul ignore next */ []));
@@ -36789,13 +36803,21 @@ class PraxisRelatedResourceOutletComponent {
36789
36803
  surfaceOpen = output();
36790
36804
  widgetEvent = output();
36791
36805
  resourceEvent = output();
36806
+ discoveredSurface = signal(null, ...(ngDevMode ? [{ debugName: "discoveredSurface" }] : /* istanbul ignore next */ []));
36807
+ discoveryState = signal(null, ...(ngDevMode ? [{ debugName: "discoveryState" }] : /* istanbul ignore next */ []));
36808
+ discoveryStateReason = signal(null, ...(ngDevMode ? [{ debugName: "discoveryStateReason" }] : /* istanbul ignore next */ []));
36809
+ materializedPayload = signal(null, ...(ngDevMode ? [{ debugName: "materializedPayload" }] : /* istanbul ignore next */ []));
36792
36810
  resolution = computed(() => {
36793
36811
  const state = this.state();
36794
36812
  if (state && state !== 'ready') {
36795
36813
  return this.resolver.state(state, this.stateReason() || undefined);
36796
36814
  }
36815
+ const discoveryState = this.discoveryState();
36816
+ if (discoveryState && discoveryState !== 'ready') {
36817
+ return this.resolver.state(discoveryState, this.discoveryStateReason() || undefined);
36818
+ }
36797
36819
  return this.resolver.resolve({
36798
- surface: this.surface(),
36820
+ surface: this.surface() || this.discoveredSurface(),
36799
36821
  parentRecord: this.parentRecord(),
36800
36822
  parentResourceId: this.parentResourceId(),
36801
36823
  parentResourcePath: this.parentResourcePath(),
@@ -36807,10 +36829,113 @@ class PraxisRelatedResourceOutletComponent {
36807
36829
  queryContext: this.queryContext(),
36808
36830
  });
36809
36831
  }, ...(ngDevMode ? [{ debugName: "resolution" }] : /* istanbul ignore next */ []));
36832
+ renderResolution = computed(() => {
36833
+ const resolution = this.resolution();
36834
+ const materializedPayload = this.materializedPayload();
36835
+ if (resolution.state !== 'ready') {
36836
+ return resolution;
36837
+ }
36838
+ if (!materializedPayload && this.mode() === 'inline' && resolution.payload?.widget) {
36839
+ return this.resolver.state('loading', 'related-resource-materializing');
36840
+ }
36841
+ if (!materializedPayload) {
36842
+ return resolution;
36843
+ }
36844
+ return {
36845
+ ...resolution,
36846
+ payload: materializedPayload,
36847
+ };
36848
+ }, ...(ngDevMode ? [{ debugName: "renderResolution" }] : /* istanbul ignore next */ []));
36810
36849
  isBusy = computed(() => {
36811
- const state = this.resolution().state;
36850
+ const state = this.renderResolution().state;
36812
36851
  return state === 'resolving' || state === 'loading';
36813
36852
  }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
36853
+ constructor() {
36854
+ effect((onCleanup) => {
36855
+ const surface = this.surface();
36856
+ const surfaceId = this.surfaceId();
36857
+ const catalog = this.surfaceCatalog();
36858
+ const discoverySource = this.discoverySource() || this.parentLinks();
36859
+ const parentResourcePath = this.parentResourcePath();
36860
+ const parentResourceId = this.parentResourceId();
36861
+ const apiEndpointKey = this.apiEndpointKey();
36862
+ const apiUrlEntry = this.apiUrlEntry();
36863
+ this.discoverySubscription?.unsubscribe();
36864
+ this.discoverySubscription = undefined;
36865
+ if (surface || !this.trim(surfaceId)) {
36866
+ this.resetDiscoveryState();
36867
+ return;
36868
+ }
36869
+ if (catalog) {
36870
+ this.applyCatalogSurface(surfaceId, catalog);
36871
+ return;
36872
+ }
36873
+ const href = this.resolveFallbackSurfaceCatalogHref(parentResourcePath, parentResourceId);
36874
+ if (!discoverySource && !href) {
36875
+ this.discoveredSurface.set(null);
36876
+ this.discoveryState.set('not-found');
36877
+ this.discoveryStateReason.set('surface-discovery-source-not-provided');
36878
+ return;
36879
+ }
36880
+ const options = this.discoveryOptions(apiEndpointKey, apiUrlEntry);
36881
+ const requestKey = this.buildDiscoveryRequestKey(surfaceId, discoverySource, href, options);
36882
+ this.discoveryRequestKey = requestKey;
36883
+ this.discoveredSurface.set(null);
36884
+ this.discoveryState.set('resolving');
36885
+ this.discoveryStateReason.set(null);
36886
+ const discovery = this.injector.get(ResourceDiscoveryService);
36887
+ const catalog$ = discoverySource
36888
+ ? discovery.getSurfaces(discoverySource, options)
36889
+ : discovery.fetchJson(href, options);
36890
+ this.discoverySubscription = catalog$.subscribe({
36891
+ next: (response) => {
36892
+ if (this.discoveryRequestKey !== requestKey) {
36893
+ return;
36894
+ }
36895
+ this.applyCatalogSurface(surfaceId, response);
36896
+ },
36897
+ error: (error) => {
36898
+ if (this.discoveryRequestKey !== requestKey) {
36899
+ return;
36900
+ }
36901
+ this.discoveredSurface.set(null);
36902
+ this.discoveryState.set(this.isPermissionError(error) ? 'permission-limited' : 'error');
36903
+ this.discoveryStateReason.set(this.isPermissionError(error)
36904
+ ? 'surface-discovery-permission-limited'
36905
+ : 'surface-discovery-failed');
36906
+ },
36907
+ });
36908
+ onCleanup(() => {
36909
+ this.discoverySubscription?.unsubscribe();
36910
+ this.discoverySubscription = undefined;
36911
+ });
36912
+ });
36913
+ effect((onCleanup) => {
36914
+ const resolution = this.resolution();
36915
+ const payload = resolution.state === 'ready' ? resolution.payload : null;
36916
+ const requestKey = this.buildMaterializationRequestKey(resolution);
36917
+ this.materializationRequestKey = requestKey;
36918
+ this.materializedPayload.set(null);
36919
+ if (!payload) {
36920
+ return;
36921
+ }
36922
+ let cancelled = false;
36923
+ this.materializer.materialize(payload)
36924
+ .then((materialized) => {
36925
+ if (!cancelled && this.materializationRequestKey === requestKey) {
36926
+ this.materializedPayload.set(materialized);
36927
+ }
36928
+ })
36929
+ .catch(() => {
36930
+ if (!cancelled && this.materializationRequestKey === requestKey) {
36931
+ this.materializedPayload.set(payload);
36932
+ }
36933
+ });
36934
+ onCleanup(() => {
36935
+ cancelled = true;
36936
+ });
36937
+ });
36938
+ }
36814
36939
  openRelated() {
36815
36940
  const payload = this.resolution().payload;
36816
36941
  if (payload) {
@@ -36827,7 +36952,7 @@ class PraxisRelatedResourceOutletComponent {
36827
36952
  this.widgetEvent.emit(event);
36828
36953
  }
36829
36954
  stateIcon() {
36830
- switch (this.resolution().state) {
36955
+ switch (this.renderResolution().state) {
36831
36956
  case 'ready':
36832
36957
  return 'hub';
36833
36958
  case 'permission-limited':
@@ -36843,11 +36968,11 @@ class PraxisRelatedResourceOutletComponent {
36843
36968
  }
36844
36969
  }
36845
36970
  stateTitle() {
36846
- const state = this.resolution().state;
36971
+ const state = this.renderResolution().state;
36847
36972
  return this.t(`state.${state}.title`, this.defaultTitle(state));
36848
36973
  }
36849
36974
  stateDescription() {
36850
- const state = this.resolution().state;
36975
+ const state = this.renderResolution().state;
36851
36976
  return this.t(`state.${state}.description`, this.defaultDescription(state));
36852
36977
  }
36853
36978
  t(key, fallback) {
@@ -36859,7 +36984,76 @@ class PraxisRelatedResourceOutletComponent {
36859
36984
  : 'Related resource';
36860
36985
  }
36861
36986
  defaultDescription(state) {
36862
- return this.resolution().reason || state;
36987
+ return this.renderResolution().reason || state;
36988
+ }
36989
+ applyCatalogSurface(surfaceId, catalog) {
36990
+ const normalizedId = this.trim(surfaceId);
36991
+ const surface = (catalog?.surfaces || []).find((candidate) => this.trim(candidate.id) === normalizedId) || null;
36992
+ this.discoveredSurface.set(surface);
36993
+ this.discoveryState.set(surface ? null : 'not-found');
36994
+ this.discoveryStateReason.set(surface ? null : 'surface-id-not-found');
36995
+ }
36996
+ resetDiscoveryState() {
36997
+ this.discoveredSurface.set(null);
36998
+ this.discoveryState.set(null);
36999
+ this.discoveryStateReason.set(null);
37000
+ this.discoveryRequestKey = '';
37001
+ }
37002
+ discoveryOptions(endpointKey, apiUrlEntry) {
37003
+ if (!endpointKey && !apiUrlEntry) {
37004
+ return undefined;
37005
+ }
37006
+ return {
37007
+ endpointKey: endpointKey || undefined,
37008
+ apiUrlEntry: apiUrlEntry || null,
37009
+ };
37010
+ }
37011
+ resolveFallbackSurfaceCatalogHref(parentResourcePath, parentResourceId) {
37012
+ const path = this.normalizeResourcePath(parentResourcePath || '');
37013
+ if (!path || parentResourceId == null || parentResourceId === '') {
37014
+ return null;
37015
+ }
37016
+ return `/api/${path}/${encodeURIComponent(String(parentResourceId))}/surfaces`;
37017
+ }
37018
+ normalizeResourcePath(resourcePath) {
37019
+ let normalized = this.trim(resourcePath);
37020
+ if (/^https?:\/\//i.test(normalized)) {
37021
+ try {
37022
+ normalized = new URL(normalized).pathname;
37023
+ }
37024
+ catch {
37025
+ return '';
37026
+ }
37027
+ }
37028
+ return normalized
37029
+ .replace(/^\/+/, '')
37030
+ .replace(/^(?:api\/)+/i, '')
37031
+ .replace(/\/+$/, '');
37032
+ }
37033
+ buildDiscoveryRequestKey(surfaceId, source, href, options) {
37034
+ return JSON.stringify({
37035
+ surfaceId: this.trim(surfaceId),
37036
+ source,
37037
+ href,
37038
+ endpointKey: options?.endpointKey || null,
37039
+ apiUrlEntry: options?.apiUrlEntry || null,
37040
+ });
37041
+ }
37042
+ buildMaterializationRequestKey(resolution) {
37043
+ return JSON.stringify({
37044
+ state: resolution.state,
37045
+ surfaceId: resolution.surface?.id ?? null,
37046
+ parentResourceId: resolution.parentResourceId ?? null,
37047
+ childResourcePath: resolution.childResourcePath ?? null,
37048
+ queryContext: resolution.queryContext ?? null,
37049
+ mode: this.mode(),
37050
+ });
37051
+ }
37052
+ isPermissionError(error) {
37053
+ const status = typeof error === 'object' && error
37054
+ ? error.status
37055
+ : null;
37056
+ return status === 401 || status === 403;
36863
37057
  }
36864
37058
  extractResourceEvent(event) {
36865
37059
  if (event.output !== 'resourceEvent' || !event.payload || typeof event.payload !== 'object') {
@@ -36872,14 +37066,17 @@ class PraxisRelatedResourceOutletComponent {
36872
37066
  ? candidate
36873
37067
  : null;
36874
37068
  }
37069
+ trim(value) {
37070
+ return typeof value === 'string' ? value.trim() : '';
37071
+ }
36875
37072
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisRelatedResourceOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
36876
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisRelatedResourceOutletComponent, isStandalone: true, selector: "praxis-related-resource-outlet", inputs: { surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null }, parentRecord: { classPropertyName: "parentRecord", publicName: "parentRecord", isSignal: true, isRequired: false, transformFunction: null }, parentResourceId: { classPropertyName: "parentResourceId", publicName: "parentResourceId", isSignal: true, isRequired: false, transformFunction: null }, parentResourcePath: { classPropertyName: "parentResourcePath", publicName: "parentResourcePath", isSignal: true, isRequired: false, transformFunction: null }, presentation: { classPropertyName: "presentation", publicName: "presentation", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tableId: { classPropertyName: "tableId", publicName: "tableId", isSignal: true, isRequired: false, transformFunction: null }, queryContext: { classPropertyName: "queryContext", publicName: "queryContext", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, stateReason: { classPropertyName: "stateReason", publicName: "stateReason", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, strictValidation: { classPropertyName: "strictValidation", publicName: "strictValidation", isSignal: true, isRequired: false, transformFunction: null }, ownerWidgetKey: { classPropertyName: "ownerWidgetKey", publicName: "ownerWidgetKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { surfaceOpen: "surfaceOpen", widgetEvent: "widgetEvent", resourceEvent: "resourceEvent" }, host: { attributes: { "data-praxis-related-resource-outlet": "core" }, properties: { "attr.data-state": "resolution().state" } }, providers: [providePraxisI18nConfig(RELATED_RESOURCE_OUTLET_I18N_CONFIG)], ngImport: i0, template: `
37073
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisRelatedResourceOutletComponent, isStandalone: true, selector: "praxis-related-resource-outlet", inputs: { surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null }, surfaceId: { classPropertyName: "surfaceId", publicName: "surfaceId", isSignal: true, isRequired: false, transformFunction: null }, surfaceCatalog: { classPropertyName: "surfaceCatalog", publicName: "surfaceCatalog", isSignal: true, isRequired: false, transformFunction: null }, discoverySource: { classPropertyName: "discoverySource", publicName: "discoverySource", isSignal: true, isRequired: false, transformFunction: null }, parentLinks: { classPropertyName: "parentLinks", publicName: "parentLinks", isSignal: true, isRequired: false, transformFunction: null }, apiEndpointKey: { classPropertyName: "apiEndpointKey", publicName: "apiEndpointKey", isSignal: true, isRequired: false, transformFunction: null }, apiUrlEntry: { classPropertyName: "apiUrlEntry", publicName: "apiUrlEntry", isSignal: true, isRequired: false, transformFunction: null }, parentRecord: { classPropertyName: "parentRecord", publicName: "parentRecord", isSignal: true, isRequired: false, transformFunction: null }, parentResourceId: { classPropertyName: "parentResourceId", publicName: "parentResourceId", isSignal: true, isRequired: false, transformFunction: null }, parentResourcePath: { classPropertyName: "parentResourcePath", publicName: "parentResourcePath", isSignal: true, isRequired: false, transformFunction: null }, presentation: { classPropertyName: "presentation", publicName: "presentation", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tableId: { classPropertyName: "tableId", publicName: "tableId", isSignal: true, isRequired: false, transformFunction: null }, queryContext: { classPropertyName: "queryContext", publicName: "queryContext", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, stateReason: { classPropertyName: "stateReason", publicName: "stateReason", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, strictValidation: { classPropertyName: "strictValidation", publicName: "strictValidation", isSignal: true, isRequired: false, transformFunction: null }, ownerWidgetKey: { classPropertyName: "ownerWidgetKey", publicName: "ownerWidgetKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { surfaceOpen: "surfaceOpen", widgetEvent: "widgetEvent", resourceEvent: "resourceEvent" }, host: { attributes: { "data-praxis-related-resource-outlet": "core" }, properties: { "attr.data-state": "renderResolution().state" } }, providers: [providePraxisI18nConfig(RELATED_RESOURCE_OUTLET_I18N_CONFIG)], ngImport: i0, template: `
36877
37074
  <section class="pdx-related-outlet" [class.pdx-related-outlet--compact]="compact()">
36878
- @if (resolution().state === 'ready' && mode() === 'inline' && resolution().payload?.widget) {
37075
+ @if (renderResolution().state === 'ready' && mode() === 'inline' && renderResolution().payload?.widget) {
36879
37076
  <ng-container
36880
- [dynamicWidgetLoader]="resolution().payload!.widget"
37077
+ [dynamicWidgetLoader]="renderResolution().payload!.widget"
36881
37078
  [ownerWidgetKey]="ownerWidgetKey()"
36882
- [context]="resolution().payload!.context || null"
37079
+ [context]="renderResolution().payload!.context || null"
36883
37080
  [strictValidation]="strictValidation()"
36884
37081
  [autoWireOutputs]="true"
36885
37082
  (widgetEvent)="onWidgetEvent($event)"
@@ -36887,7 +37084,7 @@ class PraxisRelatedResourceOutletComponent {
36887
37084
  } @else {
36888
37085
  <div
36889
37086
  class="pdx-related-outlet__state"
36890
- [class.pdx-related-outlet__state--ready]="resolution().state === 'ready'"
37087
+ [class.pdx-related-outlet__state--ready]="renderResolution().state === 'ready'"
36891
37088
  [class.pdx-related-outlet__state--busy]="isBusy()"
36892
37089
  role="status"
36893
37090
  aria-live="polite"
@@ -36918,7 +37115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
36918
37115
  type: Component,
36919
37116
  args: [{ selector: 'praxis-related-resource-outlet', standalone: true, host: {
36920
37117
  'data-praxis-related-resource-outlet': 'core',
36921
- '[attr.data-state]': 'resolution().state',
37118
+ '[attr.data-state]': 'renderResolution().state',
36922
37119
  }, imports: [
36923
37120
  DynamicWidgetLoaderDirective,
36924
37121
  MatButtonModule,
@@ -36926,11 +37123,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
36926
37123
  MatProgressSpinnerModule,
36927
37124
  ], providers: [providePraxisI18nConfig(RELATED_RESOURCE_OUTLET_I18N_CONFIG)], template: `
36928
37125
  <section class="pdx-related-outlet" [class.pdx-related-outlet--compact]="compact()">
36929
- @if (resolution().state === 'ready' && mode() === 'inline' && resolution().payload?.widget) {
37126
+ @if (renderResolution().state === 'ready' && mode() === 'inline' && renderResolution().payload?.widget) {
36930
37127
  <ng-container
36931
- [dynamicWidgetLoader]="resolution().payload!.widget"
37128
+ [dynamicWidgetLoader]="renderResolution().payload!.widget"
36932
37129
  [ownerWidgetKey]="ownerWidgetKey()"
36933
- [context]="resolution().payload!.context || null"
37130
+ [context]="renderResolution().payload!.context || null"
36934
37131
  [strictValidation]="strictValidation()"
36935
37132
  [autoWireOutputs]="true"
36936
37133
  (widgetEvent)="onWidgetEvent($event)"
@@ -36938,7 +37135,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
36938
37135
  } @else {
36939
37136
  <div
36940
37137
  class="pdx-related-outlet__state"
36941
- [class.pdx-related-outlet__state--ready]="resolution().state === 'ready'"
37138
+ [class.pdx-related-outlet__state--ready]="renderResolution().state === 'ready'"
36942
37139
  [class.pdx-related-outlet__state--busy]="isBusy()"
36943
37140
  role="status"
36944
37141
  aria-live="polite"
@@ -36964,7 +37161,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
36964
37161
  }
36965
37162
  </section>
36966
37163
  `, styles: [":host{display:block;min-width:0}.pdx-related-outlet{display:block;min-width:0;color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet__state{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:var(--pdx-related-outlet-gap, 12px);min-height:var(--pdx-related-outlet-min-height, 64px);padding:var(--pdx-related-outlet-padding, 12px);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .16));border-radius:var(--pdx-related-outlet-radius, 8px);background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface, transparent))}.pdx-related-outlet--compact .pdx-related-outlet__state{min-height:var(--pdx-related-outlet-compact-min-height, 48px);padding:var(--pdx-related-outlet-compact-padding, 8px 10px)}.pdx-related-outlet__state--ready{border-color:var(--md-sys-color-primary, currentColor)}.pdx-related-outlet__icon{display:inline-grid;place-items:center;width:32px;height:32px;color:var(--md-sys-color-primary, currentColor)}.pdx-related-outlet__state--busy .pdx-related-outlet__icon{color:var(--md-sys-color-secondary, currentColor)}.pdx-related-outlet__copy{display:grid;gap:2px;min-width:0}.pdx-related-outlet__copy h3,.pdx-related-outlet__copy p{margin:0}.pdx-related-outlet__copy h3{font:var(--md-sys-typescale-title-small, 600 .95rem/1.25rem system-ui);color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet__copy p{font:var(--md-sys-typescale-body-small, 400 .82rem/1.15rem system-ui);color:var(--md-sys-color-on-surface-variant, currentColor)}@media(max-width:600px){.pdx-related-outlet__state{grid-template-columns:auto minmax(0,1fr)}.pdx-related-outlet__state button{grid-column:1 / -1;justify-self:start}}\n"] }]
36967
- }], propDecorators: { surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], parentRecord: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentRecord", required: false }] }], parentResourceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentResourceId", required: false }] }], parentResourcePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentResourcePath", required: false }] }], presentation: [{ type: i0.Input, args: [{ isSignal: true, alias: "presentation", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tableId: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableId", required: false }] }], queryContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryContext", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], stateReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateReason", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], strictValidation: [{ type: i0.Input, args: [{ isSignal: true, alias: "strictValidation", required: false }] }], ownerWidgetKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "ownerWidgetKey", required: false }] }], surfaceOpen: [{ type: i0.Output, args: ["surfaceOpen"] }], widgetEvent: [{ type: i0.Output, args: ["widgetEvent"] }], resourceEvent: [{ type: i0.Output, args: ["resourceEvent"] }] } });
37164
+ }], ctorParameters: () => [], propDecorators: { surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], surfaceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceId", required: false }] }], surfaceCatalog: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceCatalog", required: false }] }], discoverySource: [{ type: i0.Input, args: [{ isSignal: true, alias: "discoverySource", required: false }] }], parentLinks: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentLinks", required: false }] }], apiEndpointKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "apiEndpointKey", required: false }] }], apiUrlEntry: [{ type: i0.Input, args: [{ isSignal: true, alias: "apiUrlEntry", required: false }] }], parentRecord: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentRecord", required: false }] }], parentResourceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentResourceId", required: false }] }], parentResourcePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentResourcePath", required: false }] }], presentation: [{ type: i0.Input, args: [{ isSignal: true, alias: "presentation", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tableId: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableId", required: false }] }], queryContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryContext", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], stateReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateReason", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], strictValidation: [{ type: i0.Input, args: [{ isSignal: true, alias: "strictValidation", required: false }] }], ownerWidgetKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "ownerWidgetKey", required: false }] }], surfaceOpen: [{ type: i0.Output, args: ["surfaceOpen"] }], widgetEvent: [{ type: i0.Output, args: ["widgetEvent"] }], resourceEvent: [{ type: i0.Output, args: ["resourceEvent"] }] } });
36968
37165
 
36969
37166
  const PRAXIS_RELATED_RESOURCE_OUTLET_COMPONENT_METADATA = {
36970
37167
  id: 'praxis-related-resource-outlet',
@@ -36974,6 +37171,12 @@ const PRAXIS_RELATED_RESOURCE_OUTLET_COMPONENT_METADATA = {
36974
37171
  description: 'Runtime genérico para materializar surface.relatedResource como tabela filha governada por metadata.',
36975
37172
  icon: 'hub',
36976
37173
  inputs: [
37174
+ { name: 'surfaceId', type: 'string | null', description: 'Identidade declarativa da surface quando o outlet deve descobri-la no catálogo do recurso.' },
37175
+ { name: 'surfaceCatalog', type: 'ResourceSurfaceCatalogResponse | null', description: 'Catálogo de surfaces já carregado pelo host; evita nova consulta quando disponível.' },
37176
+ { name: 'discoverySource', type: 'ResourceLinkSource | null', description: 'Resposta HATEOAS ou mapa de links usado para seguir rel surfaces quando surface não foi fornecida.' },
37177
+ { name: 'parentLinks', type: 'RestApiLinks | RestApiResponse<unknown> | null', description: 'Atalho para links do registro pai; tem o mesmo papel de discoverySource para rel surfaces.' },
37178
+ { name: 'apiEndpointKey', type: 'ApiEndpoint | null', description: 'Endpoint Praxis usado pelo ResourceDiscoveryService ao resolver links ou fallback do catálogo.' },
37179
+ { name: 'apiUrlEntry', type: 'ApiUrlEntry | null', description: 'Entrada de API já resolvida para hosts destacados ou integrações remotas.' },
36977
37180
  { name: 'surface', type: 'ResourceSurfaceCatalogItem | null', description: 'Surface de catálogo que pode publicar relatedResource.' },
36978
37181
  { name: 'parentRecord', type: 'Record<string, unknown> | null', description: 'Registro pai usado para resolver parentIdPathVariable.' },
36979
37182
  { name: 'parentResourceId', type: 'string | number | null', description: 'Identificador explícito do registro pai quando não vem do record.' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/core",
3
- "version": "9.0.0-beta.37",
3
+ "version": "9.0.0-beta.39",
4
4
  "description": "Core library for Praxis UI Workspace: types, tokens, services and utilities shared across @praxisui/* packages.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -14924,8 +14924,19 @@ declare class PraxisSurfaceHostComponent implements AfterViewInit, OnChanges {
14924
14924
  type RelatedResourceOutletMode = 'inline' | 'open-action';
14925
14925
  declare class PraxisRelatedResourceOutletComponent {
14926
14926
  private readonly resolver;
14927
+ private readonly materializer;
14927
14928
  private readonly i18n;
14929
+ private readonly injector;
14930
+ private discoverySubscription?;
14931
+ private discoveryRequestKey;
14932
+ private materializationRequestKey;
14928
14933
  readonly surface: i0.InputSignal<ResourceSurfaceCatalogItem | null>;
14934
+ readonly surfaceId: i0.InputSignal<string | null>;
14935
+ readonly surfaceCatalog: i0.InputSignal<ResourceSurfaceCatalogResponse | null>;
14936
+ readonly discoverySource: i0.InputSignal<ResourceLinkSource | null>;
14937
+ readonly parentLinks: i0.InputSignal<RestApiLinks | RestApiResponse<unknown> | null>;
14938
+ readonly apiEndpointKey: i0.InputSignal<ApiEndpoint | null>;
14939
+ readonly apiUrlEntry: i0.InputSignal<ApiUrlEntry | null>;
14929
14940
  readonly parentRecord: i0.InputSignal<Record<string, unknown> | null>;
14930
14941
  readonly parentResourceId: i0.InputSignal<string | number | null>;
14931
14942
  readonly parentResourcePath: i0.InputSignal<string | null>;
@@ -14944,8 +14955,14 @@ declare class PraxisRelatedResourceOutletComponent {
14944
14955
  readonly surfaceOpen: i0.OutputEmitterRef<SurfaceOpenPayload>;
14945
14956
  readonly widgetEvent: i0.OutputEmitterRef<WidgetEventEnvelope>;
14946
14957
  readonly resourceEvent: i0.OutputEmitterRef<PraxisResourceEvent<unknown>>;
14958
+ private readonly discoveredSurface;
14959
+ private readonly discoveryState;
14960
+ private readonly discoveryStateReason;
14961
+ private readonly materializedPayload;
14947
14962
  readonly resolution: i0.Signal<RelatedResourceSurfaceResolution>;
14963
+ readonly renderResolution: i0.Signal<RelatedResourceSurfaceResolution>;
14948
14964
  readonly isBusy: i0.Signal<boolean>;
14965
+ constructor();
14949
14966
  openRelated(): void;
14950
14967
  onWidgetEvent(event: WidgetEventEnvelope): void;
14951
14968
  stateIcon(): string;
@@ -14954,9 +14971,18 @@ declare class PraxisRelatedResourceOutletComponent {
14954
14971
  t(key: string, fallback: string): string;
14955
14972
  private defaultTitle;
14956
14973
  private defaultDescription;
14974
+ private applyCatalogSurface;
14975
+ private resetDiscoveryState;
14976
+ private discoveryOptions;
14977
+ private resolveFallbackSurfaceCatalogHref;
14978
+ private normalizeResourcePath;
14979
+ private buildDiscoveryRequestKey;
14980
+ private buildMaterializationRequestKey;
14981
+ private isPermissionError;
14957
14982
  private extractResourceEvent;
14983
+ private trim;
14958
14984
  static ɵfac: i0.ɵɵFactoryDeclaration<PraxisRelatedResourceOutletComponent, never>;
14959
- static ɵcmp: i0.ɵɵComponentDeclaration<PraxisRelatedResourceOutletComponent, "praxis-related-resource-outlet", never, { "surface": { "alias": "surface"; "required": false; "isSignal": true; }; "parentRecord": { "alias": "parentRecord"; "required": false; "isSignal": true; }; "parentResourceId": { "alias": "parentResourceId"; "required": false; "isSignal": true; }; "parentResourcePath": { "alias": "parentResourcePath"; "required": false; "isSignal": true; }; "presentation": { "alias": "presentation"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tableId": { "alias": "tableId"; "required": false; "isSignal": true; }; "queryContext": { "alias": "queryContext"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "stateReason": { "alias": "stateReason"; "required": false; "isSignal": true; }; "compact": { "alias": "compact"; "required": false; "isSignal": true; }; "strictValidation": { "alias": "strictValidation"; "required": false; "isSignal": true; }; "ownerWidgetKey": { "alias": "ownerWidgetKey"; "required": false; "isSignal": true; }; }, { "surfaceOpen": "surfaceOpen"; "widgetEvent": "widgetEvent"; "resourceEvent": "resourceEvent"; }, never, never, true, never>;
14985
+ static ɵcmp: i0.ɵɵComponentDeclaration<PraxisRelatedResourceOutletComponent, "praxis-related-resource-outlet", never, { "surface": { "alias": "surface"; "required": false; "isSignal": true; }; "surfaceId": { "alias": "surfaceId"; "required": false; "isSignal": true; }; "surfaceCatalog": { "alias": "surfaceCatalog"; "required": false; "isSignal": true; }; "discoverySource": { "alias": "discoverySource"; "required": false; "isSignal": true; }; "parentLinks": { "alias": "parentLinks"; "required": false; "isSignal": true; }; "apiEndpointKey": { "alias": "apiEndpointKey"; "required": false; "isSignal": true; }; "apiUrlEntry": { "alias": "apiUrlEntry"; "required": false; "isSignal": true; }; "parentRecord": { "alias": "parentRecord"; "required": false; "isSignal": true; }; "parentResourceId": { "alias": "parentResourceId"; "required": false; "isSignal": true; }; "parentResourcePath": { "alias": "parentResourcePath"; "required": false; "isSignal": true; }; "presentation": { "alias": "presentation"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tableId": { "alias": "tableId"; "required": false; "isSignal": true; }; "queryContext": { "alias": "queryContext"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "stateReason": { "alias": "stateReason"; "required": false; "isSignal": true; }; "compact": { "alias": "compact"; "required": false; "isSignal": true; }; "strictValidation": { "alias": "strictValidation"; "required": false; "isSignal": true; }; "ownerWidgetKey": { "alias": "ownerWidgetKey"; "required": false; "isSignal": true; }; }, { "surfaceOpen": "surfaceOpen"; "widgetEvent": "widgetEvent"; "resourceEvent": "resourceEvent"; }, never, never, true, never>;
14960
14986
  }
14961
14987
 
14962
14988
  declare const PRAXIS_RELATED_RESOURCE_OUTLET_COMPONENT_METADATA: ComponentDocMeta;