@praxisui/core 9.0.0-beta.77 → 9.0.0-beta.79

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.
@@ -5817,7 +5817,6 @@ class GenericCrudService {
5817
5817
  * Manipula erros de requisição HTTP.
5818
5818
  */
5819
5819
  handleError(error) {
5820
- console.error('Erro na API:', error);
5821
5820
  // Repassa o HttpErrorResponse completo para permitir tratamentos mais ricos (status, body, etc.)
5822
5821
  return throwError(() => error);
5823
5822
  }
@@ -7207,6 +7206,9 @@ class ComponentMetadataRegistry {
7207
7206
  /** Register a component metadata entry. */
7208
7207
  register(meta) {
7209
7208
  const normalized = this.normalizeMeta(meta);
7209
+ if (this.rawMetadataMap.get(normalized.id) === meta) {
7210
+ return;
7211
+ }
7210
7212
  this.assertCanRegisterMetadata(normalized);
7211
7213
  this.rawMetadataMap.set(normalized.id, meta);
7212
7214
  this.metadataMap.set(normalized.id, normalized);
@@ -11803,30 +11805,92 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11803
11805
  * need to know about HTTP status codes or low level error objects.
11804
11806
  */
11805
11807
  class ErrorMessageService {
11808
+ i18n;
11809
+ constructor(i18n = null) {
11810
+ this.i18n = i18n;
11811
+ }
11812
+ normalizeSubmitError(error) {
11813
+ const status = this.numberValue(error?.status);
11814
+ const payload = this.objectValue(error?.error) ?? this.objectValue(error);
11815
+ const details = this.normalizeDetails(payload?.['errors']);
11816
+ if (details.length) {
11817
+ return {
11818
+ message: this.textValue(payload?.['message']) ?? details[0].message,
11819
+ details,
11820
+ };
11821
+ }
11822
+ const publicMessage = this.textValue(payload?.['message']);
11823
+ if (publicMessage && status !== undefined && status >= 400 && status < 500) {
11824
+ return { message: publicMessage, details: [] };
11825
+ }
11826
+ return { message: this.fallbackMessage(error, status), details: [] };
11827
+ }
11806
11828
  /**
11807
11829
  * Returns a generic message for an error returned during a form submit.
11808
11830
  * @param error Error object possibly containing an HTTP status code
11809
11831
  */
11810
11832
  getSubmitErrorMessage(error) {
11811
- const status = error?.status;
11833
+ return this.normalizeSubmitError(error).message;
11834
+ }
11835
+ fallbackMessage(error, status) {
11812
11836
  if (status === 0) {
11813
- return 'Não foi possível conectar ao servidor. Verifique sua conexão ou se o servidor está disponível.';
11837
+ return this.tx('global.submitError.network', 'Não foi possível conectar ao servidor. Verifique sua conexão ou se o servidor está disponível.');
11814
11838
  }
11815
- if (status >= 500) {
11816
- return 'Erro interno no servidor. Tente novamente mais tarde.';
11839
+ if (status !== undefined && status >= 500) {
11840
+ return this.tx('global.submitError.server', 'Erro interno no servidor. Tente novamente mais tarde.');
11817
11841
  }
11818
11842
  if (status === 400 || status === 422) {
11819
- return 'Dados inválidos ou inconsistentes. Verifique os campos e tente novamente.';
11843
+ return this.tx('global.submitError.invalidData', 'Dados inválidos ou inconsistentes. Verifique os campos e tente novamente.');
11820
11844
  }
11821
- return error?.message || 'Falha ao salvar. Tente novamente.';
11845
+ return status === undefined
11846
+ ? this.textValue(error?.message) ?? this.genericSubmitError()
11847
+ : this.genericSubmitError();
11848
+ }
11849
+ normalizeDetails(value) {
11850
+ if (!Array.isArray(value))
11851
+ return [];
11852
+ return value.flatMap((item) => {
11853
+ const detail = this.objectValue(item);
11854
+ const message = this.textValue(detail?.['message']) ?? this.textValue(detail?.['detail']);
11855
+ if (!message)
11856
+ return [];
11857
+ return [{
11858
+ message,
11859
+ code: this.textValue(detail?.['code']),
11860
+ target: this.textValue(detail?.['target']),
11861
+ category: this.textValue(detail?.['category']),
11862
+ }];
11863
+ });
11822
11864
  }
11823
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ErrorMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11865
+ objectValue(value) {
11866
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
11867
+ ? value
11868
+ : undefined;
11869
+ }
11870
+ textValue(value) {
11871
+ if (typeof value !== 'string')
11872
+ return undefined;
11873
+ const normalized = value.trim();
11874
+ return normalized || undefined;
11875
+ }
11876
+ numberValue(value) {
11877
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
11878
+ }
11879
+ genericSubmitError() {
11880
+ return this.tx('global.submitError.generic', 'Falha ao salvar. Tente novamente.');
11881
+ }
11882
+ tx(key, fallback) {
11883
+ return this.i18n?.t(key, undefined, fallback) ?? fallback;
11884
+ }
11885
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ErrorMessageService, deps: [{ token: PraxisI18nService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
11824
11886
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ErrorMessageService, providedIn: 'root' });
11825
11887
  }
11826
11888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ErrorMessageService, decorators: [{
11827
11889
  type: Injectable,
11828
11890
  args: [{ providedIn: 'root' }]
11829
- }] });
11891
+ }], ctorParameters: () => [{ type: PraxisI18nService, decorators: [{
11892
+ type: Optional
11893
+ }] }] });
11830
11894
 
11831
11895
  class ComponentKeyService {
11832
11896
  router = (() => {
@@ -17507,6 +17571,36 @@ const PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES = [
17507
17571
  groupBy: 'lib',
17508
17572
  throttleMs: 5 * MINUTE,
17509
17573
  },
17574
+ {
17575
+ id: 'agentic-timeout-cancel-timeout',
17576
+ name: 'Agentic timeout cancellation request timed out',
17577
+ severity: 'error',
17578
+ level: 'warn',
17579
+ threshold: 3,
17580
+ windowMs: 5 * MINUTE,
17581
+ groupBy: 'actionId',
17582
+ matchContext: {
17583
+ lib: '@praxisui/ai',
17584
+ component: 'AgenticAuthoringTurnClientService',
17585
+ actionId: 'timeout-cancel-timeout',
17586
+ },
17587
+ throttleMs: 5 * MINUTE,
17588
+ },
17589
+ {
17590
+ id: 'agentic-timeout-cancel-failed',
17591
+ name: 'Agentic timeout cancellation request failed',
17592
+ severity: 'error',
17593
+ level: 'warn',
17594
+ threshold: 3,
17595
+ windowMs: 5 * MINUTE,
17596
+ groupBy: 'actionId',
17597
+ matchContext: {
17598
+ lib: '@praxisui/ai',
17599
+ component: 'AgenticAuthoringTurnClientService',
17600
+ actionId: 'timeout-cancel-failed',
17601
+ },
17602
+ throttleMs: 5 * MINUTE,
17603
+ },
17510
17604
  ];
17511
17605
  function createCorporateObservabilityOptions() {
17512
17606
  return {
@@ -17563,6 +17657,7 @@ class ObservabilityDashboardService {
17563
17657
  byLib: [],
17564
17658
  byComponent: [],
17565
17659
  topActionIds: [],
17660
+ agenticTurns: this.emptyAgenticTurnMetrics(),
17566
17661
  });
17567
17662
  alerts$ = this.alertsSubject.asObservable();
17568
17663
  metrics$ = this.metricsSubject.asObservable();
@@ -17622,7 +17717,9 @@ class ObservabilityDashboardService {
17622
17717
  evaluateAlerts(nowMs) {
17623
17718
  for (const rule of this.options.alertRules) {
17624
17719
  const windowStart = nowMs - rule.windowMs;
17625
- const matching = this.records.filter((record) => record.timestampMs >= windowStart && record.level === rule.level);
17720
+ const matching = this.records.filter((record) => record.timestampMs >= windowStart
17721
+ && record.level === rule.level
17722
+ && this.matchesRuleContext(record, rule));
17626
17723
  const grouped = this.groupRecordsByRule(matching, rule.groupBy);
17627
17724
  for (const [groupKey, groupRecords] of grouped.entries()) {
17628
17725
  if (groupRecords.length < rule.threshold) {
@@ -17659,6 +17756,15 @@ class ObservabilityDashboardService {
17659
17756
  }
17660
17757
  }
17661
17758
  }
17759
+ matchesRuleContext(record, rule) {
17760
+ const match = rule.matchContext;
17761
+ if (!match) {
17762
+ return true;
17763
+ }
17764
+ return (!match.lib || record.context.lib === match.lib)
17765
+ && (!match.component || record.context.component === match.component)
17766
+ && (!match.actionId || record.context.actionId === match.actionId);
17767
+ }
17662
17768
  buildAlertContext(groupBy, groupKey, latest) {
17663
17769
  if (groupBy === 'global') {
17664
17770
  return {
@@ -17746,8 +17852,77 @@ class ObservabilityDashboardService {
17746
17852
  byLib: this.toSortedBuckets(byLib),
17747
17853
  byComponent: this.toSortedBuckets(byComponent),
17748
17854
  topActionIds: this.toSortedBuckets(byActionId).slice(0, this.options.topActionsLimit),
17855
+ agenticTurns: this.buildAgenticTurnMetrics(withinWindow),
17856
+ };
17857
+ }
17858
+ buildAgenticTurnMetrics(records) {
17859
+ const agentic = records.filter((record) => this.agenticTurnData(record) !== null);
17860
+ const overall = this.summarizeAgenticTurns('all', agentic);
17861
+ const { key: _key, ...summary } = overall;
17862
+ return {
17863
+ ...summary,
17864
+ byProvider: this.groupAgenticTurns(agentic, 'provider'),
17865
+ byModel: this.groupAgenticTurns(agentic, 'model'),
17866
+ };
17867
+ }
17868
+ groupAgenticTurns(records, dimension) {
17869
+ const groups = new Map();
17870
+ for (const record of records) {
17871
+ const data = this.agenticTurnData(record);
17872
+ if (!data)
17873
+ continue;
17874
+ const key = typeof data[dimension] === 'string' && data[dimension].trim()
17875
+ ? data[dimension].trim()
17876
+ : 'unspecified';
17877
+ groups.set(key, [...(groups.get(key) ?? []), record]);
17878
+ }
17879
+ return [...groups.entries()]
17880
+ .map(([key, entries]) => this.summarizeAgenticTurns(key, entries))
17881
+ .sort((a, b) => b.started - a.started || a.key.localeCompare(b.key));
17882
+ }
17883
+ summarizeAgenticTurns(key, records) {
17884
+ const outcomes = records.map((record) => this.agenticTurnData(record)).filter(Boolean);
17885
+ const started = outcomes.filter((data) => data['outcome'] === 'started').length;
17886
+ const terminal = outcomes.filter((data) => data['outcome'] !== 'started');
17887
+ const succeeded = terminal.filter((data) => data['outcome'] === 'result').length;
17888
+ const timedOut = terminal.filter((data) => data['outcome'] === 'timeout').length;
17889
+ const durations = terminal
17890
+ .map((data) => data['durationMs'])
17891
+ .filter((value) => typeof value === 'number' && Number.isFinite(value) && value >= 0)
17892
+ .sort((a, b) => a - b);
17893
+ const average = durations.length
17894
+ ? durations.reduce((sum, value) => sum + value, 0) / durations.length
17895
+ : 0;
17896
+ return {
17897
+ key,
17898
+ started,
17899
+ completed: terminal.length,
17900
+ succeeded,
17901
+ timedOut,
17902
+ successRate: started ? succeeded / started : 0,
17903
+ timeoutRate: started ? timedOut / started : 0,
17904
+ averageDurationMs: average,
17905
+ p50DurationMs: this.percentile(durations, 0.5),
17906
+ p95DurationMs: this.percentile(durations, 0.95),
17749
17907
  };
17750
17908
  }
17909
+ percentile(sorted, percentile) {
17910
+ if (!sorted.length)
17911
+ return 0;
17912
+ const index = Math.max(0, Math.ceil(percentile * sorted.length) - 1);
17913
+ return sorted[index];
17914
+ }
17915
+ agenticTurnData(record) {
17916
+ if (!record.data || typeof record.data !== 'object' || Array.isArray(record.data))
17917
+ return null;
17918
+ const data = record.data;
17919
+ return data['schemaVersion'] === 'praxis-agentic-authoring-turn-telemetry.v1' ? data : null;
17920
+ }
17921
+ emptyAgenticTurnMetrics() {
17922
+ const empty = this.summarizeAgenticTurns('all', []);
17923
+ const { key: _key, ...summary } = empty;
17924
+ return { ...summary, byProvider: [], byModel: [] };
17925
+ }
17751
17926
  incrementCounter(counter, key) {
17752
17927
  counter.set(key, (counter.get(key) ?? 0) + 1);
17753
17928
  }
@@ -17781,6 +17956,7 @@ class ObservabilityDashboardService {
17781
17956
  ? { correlationId: payload.context.correlationId }
17782
17957
  : {}),
17783
17958
  },
17959
+ data: payload.data,
17784
17960
  };
17785
17961
  }
17786
17962
  toLoggerTelemetryPayload(payload) {
@@ -39628,8 +39804,17 @@ class ResourceQuickConnectComponent {
39628
39804
  isBusy$ = new BehaviorSubject(false);
39629
39805
  // Values are provided via Inputs by the opener; no DI data coupling required.
39630
39806
  // When used standalone, consumers can bind [(resourcePath)].
39807
+ ngOnInit() {
39808
+ this.updateValidity();
39809
+ }
39810
+ ngOnChanges() {
39811
+ this.updateValidity();
39812
+ }
39631
39813
  updateState() {
39632
39814
  this.isDirty$.next(true);
39815
+ this.updateValidity();
39816
+ }
39817
+ updateValidity() {
39633
39818
  const trimmed = this.resourcePath?.trim() || '';
39634
39819
  this.isValid$.next(!!trimmed && this.isValidPath(trimmed));
39635
39820
  }
@@ -39659,7 +39844,7 @@ class ResourceQuickConnectComponent {
39659
39844
  return /^\/?[a-zA-Z0-9][a-zA-Z0-9/_-]*$/.test(value);
39660
39845
  }
39661
39846
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ResourceQuickConnectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
39662
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ResourceQuickConnectComponent, isStandalone: true, selector: "praxis-resource-quick-connect", inputs: { resourcePath: "resourcePath" }, ngImport: i0, template: `
39847
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ResourceQuickConnectComponent, isStandalone: true, selector: "praxis-resource-quick-connect", inputs: { resourcePath: "resourcePath" }, usesOnChanges: true, ngImport: i0, template: `
39663
39848
  <div class="pdx-quick-connect">
39664
39849
  <mat-form-field appearance="outline">
39665
39850
  <mat-label>Rota do recurso (API)</mat-label>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/core",
3
- "version": "9.0.0-beta.77",
3
+ "version": "9.0.0-beta.79",
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",