@praxisui/dynamic-form 10.0.0-rc.15 → 10.0.0-rc.17

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-10-14T16:04:24.084Z",
3
+ "generatedAt": "2026-10-14T22:05:37.957Z",
4
4
  "packageName": "@praxisui/dynamic-form",
5
- "packageVersion": "10.0.0-rc.15",
5
+ "packageVersion": "10.0.0-rc.17",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -12023,6 +12023,8 @@ class PraxisDynamicForm {
12023
12023
  entitySelectionCleared = false;
12024
12024
  hydratedEntityId = null;
12025
12025
  loadedEntityData = null;
12026
+ loadedEntityResourceVersion = null;
12027
+ entityVersionInvalidated = false;
12026
12028
  entityHydrationPending = false;
12027
12029
  entityReadFailed = false;
12028
12030
  entityReadText(key) {
@@ -12550,9 +12552,9 @@ class PraxisDynamicForm {
12550
12552
  const method = this.normalizeSubmitMethod(this.submitMethod);
12551
12553
  const configuredSubmitUrl = String(this.submitUrl || '').trim();
12552
12554
  if (!method && !configuredSubmitUrl) {
12553
- return operation === 'update'
12555
+ return (operation === 'update'
12554
12556
  ? this.crud.update(this.resourceId, formData)
12555
- : this.crud.create(formData);
12557
+ : this.crud.create(formData)).pipe(map$1((response) => this.unwrapAndCaptureSubmitResponse(response, operation === 'update')), catchError((err) => this.handleVersionedSubmitError(err)));
12556
12558
  }
12557
12559
  if (!method || !configuredSubmitUrl) {
12558
12560
  return throwError(() => new Error('PraxisDynamicForm requires submitMethod and submitUrl together for canonical submit execution.'));
@@ -12573,28 +12575,37 @@ class PraxisDynamicForm {
12573
12575
  }
12574
12576
  if (method === 'patch') {
12575
12577
  return this.http
12576
- .patch(resolvedUrl, formData, { headers })
12577
- .pipe(map$1((response) => this.unwrapRestApiResponse(response)));
12578
+ .patch(resolvedUrl, formData, {
12579
+ headers,
12580
+ observe: 'response',
12581
+ })
12582
+ .pipe(map$1((response) => this.unwrapAndCaptureSubmitResponse(response, true)), catchError((err) => this.handleVersionedSubmitError(err)));
12578
12583
  }
12579
12584
  if (method === 'put') {
12580
12585
  return this.http
12581
- .put(resolvedUrl, formData, { headers })
12582
- .pipe(map$1((response) => this.unwrapRestApiResponse(response)));
12586
+ .put(resolvedUrl, formData, {
12587
+ headers,
12588
+ observe: 'response',
12589
+ })
12590
+ .pipe(map$1((response) => this.unwrapAndCaptureSubmitResponse(response, true)), catchError((err) => this.handleVersionedSubmitError(err)));
12583
12591
  }
12584
12592
  return this.http
12585
- .post(resolvedUrl, formData, { headers })
12586
- .pipe(map$1((response) => this.unwrapRestApiResponse(response)));
12593
+ .post(resolvedUrl, formData, {
12594
+ headers,
12595
+ observe: 'response',
12596
+ })
12597
+ .pipe(map$1((response) => this.unwrapAndCaptureSubmitResponse(response, this.mode === 'edit' || this.resourceId != null)), catchError((err) => this.handleVersionedSubmitError(err)));
12587
12598
  }));
12588
12599
  }
12589
12600
  resolveSubmitResourceVersion(formData) {
12590
- const explicitVersion = String(this.submitResourceVersion ?? '').trim();
12601
+ const explicitVersion = this.normalizeResourceVersionToken(this.submitResourceVersion);
12591
12602
  if (explicitVersion) {
12592
12603
  return of(explicitVersion);
12593
12604
  }
12594
12605
  const targetResourceKey = String(this.submitResourceVersionTargetResourceKey ?? '').trim();
12595
12606
  const targetIdField = String(this.submitResourceVersionTargetIdField ?? '').trim();
12596
12607
  if (!targetResourceKey && !targetIdField) {
12597
- return of(null);
12608
+ return of(this.resolveCurrentEntityResourceVersion(formData));
12598
12609
  }
12599
12610
  if (!targetResourceKey || !targetIdField) {
12600
12611
  return throwError(() => new Error('PraxisDynamicForm requires both submitResourceVersionTargetResourceKey and submitResourceVersionTargetIdField.'));
@@ -12635,12 +12646,119 @@ class PraxisDynamicForm {
12635
12646
  observe: 'response',
12636
12647
  });
12637
12648
  }), switchMap((response) => {
12638
- const etag = String(response.headers.get('ETag') || '').trim();
12649
+ const etag = this.normalizeResourceVersionToken(response.headers?.get?.('ETag')) ??
12650
+ this.extractResourceVersionCandidate(this.unwrapRestApiResponse(response.body));
12639
12651
  return etag
12640
12652
  ? of(etag)
12641
12653
  : throwError(() => new Error(`PraxisDynamicForm did not receive an ETag for cross-resource target "${targetResourceKey}".`));
12642
12654
  }));
12643
12655
  }
12656
+ resolveCurrentEntityResourceVersion(formData) {
12657
+ const method = this.normalizeSubmitMethod(this.submitMethod);
12658
+ const isEntityUpdate = this.mode === 'edit' || method === 'put' || method === 'patch';
12659
+ if (!isEntityUpdate) {
12660
+ return null;
12661
+ }
12662
+ const entityId = this.resourceId ?? this.pendingEntityId ?? this.loadedEntityId ?? null;
12663
+ const crudCachedVersion = entityId != null
12664
+ ? this.normalizeResourceVersionToken(this.crud?.getResourceVersion?.(entityId))
12665
+ : null;
12666
+ if (this.entityVersionInvalidated) {
12667
+ return this.loadedEntityResourceVersion || crudCachedVersion || null;
12668
+ }
12669
+ return (this.loadedEntityResourceVersion ||
12670
+ crudCachedVersion ||
12671
+ this.extractResourceVersionCandidate(this.loadedEntityData) ||
12672
+ this.extractResourceVersionCandidate(formData) ||
12673
+ this.extractResourceVersionCandidate(this.initialValue) ||
12674
+ null);
12675
+ }
12676
+ unwrapAndCaptureSubmitResponse(response, isEntityMutation) {
12677
+ const isHttpResponse = response &&
12678
+ typeof response === 'object' &&
12679
+ 'body' in response &&
12680
+ 'headers' in response;
12681
+ const etagHeader = isHttpResponse
12682
+ ? this.normalizeResourceVersionToken(response.headers?.get?.('ETag'))
12683
+ : null;
12684
+ const unwrapped = this.unwrapRestApiResponse(isHttpResponse ? response.body : response);
12685
+ this.captureUpdatedEntityResourceVersion(unwrapped, etagHeader, isEntityMutation);
12686
+ return unwrapped;
12687
+ }
12688
+ captureUpdatedEntityResourceVersion(payload, etagHeader = null, isEntityMutation = true) {
12689
+ const payloadId = payload && typeof payload === 'object' && !Array.isArray(payload)
12690
+ ? payload['id'] ?? null
12691
+ : null;
12692
+ const entityId = this.resourceId ??
12693
+ this.pendingEntityId ??
12694
+ this.loadedEntityId ??
12695
+ payloadId ??
12696
+ null;
12697
+ const nextVersion = etagHeader ??
12698
+ this.extractResourceVersionCandidate(payload) ??
12699
+ (isEntityMutation && entityId != null
12700
+ ? this.normalizeResourceVersionToken(this.crud?.getResourceVersion?.(entityId))
12701
+ : null);
12702
+ if (!nextVersion) {
12703
+ return;
12704
+ }
12705
+ this.entityVersionInvalidated = false;
12706
+ if (isEntityMutation || entityId != null) {
12707
+ this.loadedEntityResourceVersion = nextVersion;
12708
+ }
12709
+ if (this.submitResourceVersion != null &&
12710
+ !this.submitResourceVersionTargetResourceKey &&
12711
+ !this.submitResourceVersionTargetIdField) {
12712
+ this.submitResourceVersion = nextVersion;
12713
+ }
12714
+ if (this.loadedEntityData &&
12715
+ typeof this.loadedEntityData === 'object' &&
12716
+ !Array.isArray(this.loadedEntityData) &&
12717
+ 'resourceVersion' in this.loadedEntityData) {
12718
+ this.loadedEntityData = {
12719
+ ...this.loadedEntityData,
12720
+ resourceVersion: nextVersion,
12721
+ };
12722
+ }
12723
+ if (entityId != null) {
12724
+ this.crud?.setResourceVersion?.(entityId, nextVersion);
12725
+ }
12726
+ }
12727
+ handleVersionedSubmitError(err) {
12728
+ if (err?.status === 412 || err?.status === 428) {
12729
+ if (!this.submitResourceVersionTargetResourceKey &&
12730
+ !this.submitResourceVersionTargetIdField) {
12731
+ this.entityVersionInvalidated = true;
12732
+ this.loadedEntityResourceVersion = null;
12733
+ this.submitResourceVersion = null;
12734
+ const entityId = this.resourceId ?? this.pendingEntityId ?? this.loadedEntityId ?? null;
12735
+ if (entityId != null) {
12736
+ this.crud?.setResourceVersion?.(entityId, null);
12737
+ }
12738
+ }
12739
+ }
12740
+ return throwError(() => err);
12741
+ }
12742
+ normalizeResourceVersionToken(raw) {
12743
+ if (typeof raw !== 'string' && typeof raw !== 'number') {
12744
+ return null;
12745
+ }
12746
+ const trimmed = String(raw).trim();
12747
+ if (!trimmed) {
12748
+ return null;
12749
+ }
12750
+ const unquoted = trimmed
12751
+ .replace(/^W\//i, '')
12752
+ .replace(/^"|"$/g, '')
12753
+ .trim();
12754
+ return unquoted ? trimmed : null;
12755
+ }
12756
+ extractResourceVersionCandidate(source) {
12757
+ if (!source || typeof source !== 'object' || Array.isArray(source)) {
12758
+ return null;
12759
+ }
12760
+ return this.normalizeResourceVersionToken(source['resourceVersion']);
12761
+ }
12644
12762
  normalizeSubmitMethod(value) {
12645
12763
  const normalized = String(value || '')
12646
12764
  .trim()
@@ -12651,10 +12769,15 @@ class PraxisDynamicForm {
12651
12769
  return null;
12652
12770
  }
12653
12771
  formatResourceVersion(version) {
12654
- if (/^(?:W\/)?".*"$/.test(version)) {
12655
- return version;
12772
+ const trimmed = version.trim();
12773
+ if (/^(?:W\/)?"[^"]+"$/.test(trimmed)) {
12774
+ return trimmed;
12656
12775
  }
12657
- return `"${version.replace(/^"|"$/g, '')}"`;
12776
+ const unquoted = trimmed
12777
+ .replace(/^W\//i, '')
12778
+ .replace(/^"|"$/g, '')
12779
+ .trim();
12780
+ return `"${unquoted}"`;
12658
12781
  }
12659
12782
  resolveSubmitHref(submitUrl) {
12660
12783
  return this.resolveEntityAwareHref(submitUrl, 'PraxisDynamicForm cannot resolve submitUrl placeholders without a resourceId.');
@@ -13088,6 +13211,8 @@ class PraxisDynamicForm {
13088
13211
  this.entityReadCancel$.next();
13089
13212
  this.loadedEntityId = null;
13090
13213
  this.loadedEntityData = null;
13214
+ this.loadedEntityResourceVersion = null;
13215
+ this.entityVersionInvalidated = false;
13091
13216
  this.hydratedEntityId = null;
13092
13217
  this.entityHydrationPending = false;
13093
13218
  }
@@ -13107,6 +13232,9 @@ class PraxisDynamicForm {
13107
13232
  this.loadEntity();
13108
13233
  }
13109
13234
  }
13235
+ if (changes['submitResourceVersion']) {
13236
+ this.entityVersionInvalidated = false;
13237
+ }
13110
13238
  if (changes['initialValue'] &&
13111
13239
  !changes['initialValue'].firstChange &&
13112
13240
  this.usesInitialValueAsAuthoritativeSnapshot() &&
@@ -13119,6 +13247,9 @@ class PraxisDynamicForm {
13119
13247
  this.form.markAsPristine();
13120
13248
  this.form.markAsUntouched();
13121
13249
  this.loadedEntityData = normalized;
13250
+ this.loadedEntityResourceVersion =
13251
+ this.extractResourceVersionCandidate(this.initialValue);
13252
+ this.entityVersionInvalidated = false;
13122
13253
  this.hydratedEntityId = this.pendingEntityId;
13123
13254
  this.flushEntityHydrationState();
13124
13255
  this.emitPayloadPreview('entity-hydrated');
@@ -13135,6 +13266,8 @@ class PraxisDynamicForm {
13135
13266
  this.debugLog('[PDF] ngOnChanges readUrl=', this.readUrl);
13136
13267
  this.loadedEntityId = null;
13137
13268
  this.loadedEntityData = null;
13269
+ this.loadedEntityResourceVersion = null;
13270
+ this.entityVersionInvalidated = false;
13138
13271
  this.hydratedEntityId = null;
13139
13272
  if (this.config.fieldMetadata?.length && this.pendingEntityId != null) {
13140
13273
  this.loadEntity();
@@ -13766,6 +13899,8 @@ class PraxisDynamicForm {
13766
13899
  // A new identity or read projection supersedes any in-flight entity read.
13767
13900
  this.entityReadCancel$.next();
13768
13901
  this.loadedEntityData = null;
13902
+ this.loadedEntityResourceVersion = null;
13903
+ this.entityVersionInvalidated = false;
13769
13904
  this.loadedEntityId = this.pendingEntityId;
13770
13905
  this.entityHydrationPending = true;
13771
13906
  this.entityReadFailed = false;
@@ -13777,8 +13912,24 @@ class PraxisDynamicForm {
13777
13912
  .get(this.resolveReadHref(configuredReadUrl), {
13778
13913
  context: httpContext,
13779
13914
  headers: composeHeadersWithVersion(this.resolveApiEntry()),
13915
+ observe: 'response',
13780
13916
  })
13781
- .pipe(map$1((response) => this.unwrapRestApiResponse(response)))
13917
+ .pipe(map$1((response) => {
13918
+ const isHttpResponse = response &&
13919
+ typeof response === 'object' &&
13920
+ 'body' in response &&
13921
+ 'headers' in response;
13922
+ const etagHeader = isHttpResponse
13923
+ ? this.normalizeResourceVersionToken(response.headers?.get?.('ETag'))
13924
+ : null;
13925
+ if (etagHeader) {
13926
+ this.loadedEntityResourceVersion = etagHeader;
13927
+ if (this.pendingEntityId != null) {
13928
+ this.crud?.setResourceVersion?.(this.pendingEntityId, etagHeader);
13929
+ }
13930
+ }
13931
+ return this.unwrapRestApiResponse(isHttpResponse ? response.body : response);
13932
+ }))
13782
13933
  : this.crud.getById(this.pendingEntityId, { httpContext });
13783
13934
  request$
13784
13935
  .pipe(takeUntil(this.entityReadCancel$), takeUntil(this.destroy$))
@@ -13786,6 +13937,16 @@ class PraxisDynamicForm {
13786
13937
  next: (data) => {
13787
13938
  this.entityHydrationPending = false;
13788
13939
  if (data && typeof data === 'object' && !Array.isArray(data)) {
13940
+ const capturedVersion = this.loadedEntityResourceVersion ??
13941
+ this.extractResourceVersionCandidate(data) ??
13942
+ (this.pendingEntityId != null
13943
+ ? this.normalizeResourceVersionToken(this.crud?.getResourceVersion?.(this.pendingEntityId))
13944
+ : null);
13945
+ this.loadedEntityResourceVersion = capturedVersion;
13946
+ this.entityVersionInvalidated = false;
13947
+ if (capturedVersion && this.pendingEntityId != null) {
13948
+ this.crud?.setResourceVersion?.(this.pendingEntityId, capturedVersion);
13949
+ }
13789
13950
  const normalized = this.normalizeFormPatchValue(data);
13790
13951
  this.loadedEntityData = normalized;
13791
13952
  if (Object.keys(this.form.controls).length) {
@@ -13804,6 +13965,7 @@ class PraxisDynamicForm {
13804
13965
  else {
13805
13966
  this.entityReadFailed = true;
13806
13967
  this.loadedEntityId = null;
13968
+ this.loadedEntityResourceVersion = null;
13807
13969
  this.warnLog('Invalid entity data received:', data, { actionId: 'entity.load', throttleKey: 'praxis-dynamic-form:invalid-entity-data' });
13808
13970
  this.emitLoadingState('data', 'error', 'Dados inválidos recebidos.');
13809
13971
  this.flushEntityHydrationState();
@@ -13813,6 +13975,7 @@ class PraxisDynamicForm {
13813
13975
  this.entityHydrationPending = false;
13814
13976
  this.entityReadFailed = true;
13815
13977
  this.loadedEntityId = null;
13978
+ this.loadedEntityResourceVersion = null;
13816
13979
  this.flushEntityHydrationState();
13817
13980
  this.emitLoadingState('data', 'error', 'Erro ao carregar dados.', err);
13818
13981
  },
@@ -13823,6 +13986,15 @@ class PraxisDynamicForm {
13823
13986
  if (!normalized) {
13824
13987
  return false;
13825
13988
  }
13989
+ const capturedVersion = this.extractResourceVersionCandidate(this.initialValue) ??
13990
+ (this.pendingEntityId != null
13991
+ ? this.normalizeResourceVersionToken(this.crud?.getResourceVersion?.(this.pendingEntityId))
13992
+ : null);
13993
+ this.loadedEntityResourceVersion = capturedVersion;
13994
+ this.entityVersionInvalidated = false;
13995
+ if (capturedVersion && this.pendingEntityId != null) {
13996
+ this.crud?.setResourceVersion?.(this.pendingEntityId, capturedVersion);
13997
+ }
13826
13998
  this.loadedEntityId = this.pendingEntityId;
13827
13999
  this.loadedEntityData = normalized;
13828
14000
  this.entityHydrationPending = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/dynamic-form",
3
- "version": "10.0.0-rc.15",
3
+ "version": "10.0.0-rc.17",
4
4
  "description": "Angular dynamic form engine for Praxis UI: metadata-driven forms, hooks, and services integrating @praxisui/* packages.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -9,13 +9,13 @@
9
9
  "@angular/forms": "^21.0.0",
10
10
  "@angular/material": "^21.0.0",
11
11
  "@angular/router": "^21.0.0",
12
- "@praxisui/ai": "^10.0.0-rc.15",
13
- "@praxisui/dynamic-fields": "^10.0.0-rc.15",
14
- "@praxisui/metadata-editor": "^10.0.0-rc.15",
15
- "@praxisui/rich-content": "^10.0.0-rc.15",
16
- "@praxisui/settings-panel": "^10.0.0-rc.15",
17
- "@praxisui/visual-builder": "^10.0.0-rc.15",
18
- "@praxisui/core": "^10.0.0-rc.15",
12
+ "@praxisui/ai": "^10.0.0-rc.17",
13
+ "@praxisui/dynamic-fields": "^10.0.0-rc.17",
14
+ "@praxisui/metadata-editor": "^10.0.0-rc.17",
15
+ "@praxisui/rich-content": "^10.0.0-rc.17",
16
+ "@praxisui/settings-panel": "^10.0.0-rc.17",
17
+ "@praxisui/visual-builder": "^10.0.0-rc.17",
18
+ "@praxisui/core": "^10.0.0-rc.17",
19
19
  "rxjs": "^7.8.0"
20
20
  },
21
21
  "dependencies": {
@@ -1016,6 +1016,8 @@ declare class PraxisDynamicForm implements OnInit, OnChanges, OnDestroy {
1016
1016
  private entitySelectionCleared;
1017
1017
  private hydratedEntityId;
1018
1018
  private loadedEntityData;
1019
+ private loadedEntityResourceVersion;
1020
+ private entityVersionInvalidated;
1019
1021
  protected entityHydrationPending: boolean;
1020
1022
  protected entityReadFailed: boolean;
1021
1023
  protected entityReadText(key: 'loading' | 'error' | 'retry' | 'region' | 'success'): string;
@@ -1136,6 +1138,12 @@ declare class PraxisDynamicForm implements OnInit, OnChanges, OnDestroy {
1136
1138
  private resolveSubmitOperation;
1137
1139
  private buildSubmitRequest;
1138
1140
  private resolveSubmitResourceVersion;
1141
+ private resolveCurrentEntityResourceVersion;
1142
+ private unwrapAndCaptureSubmitResponse;
1143
+ private captureUpdatedEntityResourceVersion;
1144
+ private handleVersionedSubmitError;
1145
+ private normalizeResourceVersionToken;
1146
+ private extractResourceVersionCandidate;
1139
1147
  private normalizeSubmitMethod;
1140
1148
  private formatResourceVersion;
1141
1149
  private resolveSubmitHref;