@praxisui/core 4.0.0-beta.0 → 6.0.0-beta.0

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.
@@ -788,13 +788,6 @@ class SchemaNormalizerService {
788
788
  }
789
789
  return undefined;
790
790
  }
791
- /** Parse a predicate function: () => boolean */
792
- parsePredicateFunction(fn) {
793
- const parsed = this.parseFunction(fn);
794
- if (!parsed)
795
- return undefined;
796
- return parsed;
797
- }
798
791
  /** Parse a transform function: (val: any) => any */
799
792
  parseTransformFunction(fn) {
800
793
  const parsed = this.parseFunction(fn);
@@ -802,14 +795,32 @@ class SchemaNormalizerService {
802
795
  return undefined;
803
796
  return parsed;
804
797
  }
805
- /**
806
- * Parse de validador condicional: (value: any, model?: any) => boolean | string | Promise<boolean | string>
807
- */
808
- parseConditionalValidator(fn) {
809
- const parsed = this.parseFunction(fn);
810
- if (!parsed)
798
+ parseConditionalValidationRules(value, fieldName) {
799
+ if (value === undefined) {
811
800
  return undefined;
812
- return parsed;
801
+ }
802
+ if (!Array.isArray(value)) {
803
+ console.warn(`[SchemaNormalizer] Ignoring non-array x-ui.conditionalValidation for field "${fieldName}". Expected an array of declarative rules.`);
804
+ return undefined;
805
+ }
806
+ const rules = [];
807
+ value.forEach((candidate, index) => {
808
+ if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
809
+ console.warn(`[SchemaNormalizer] Ignoring invalid x-ui.conditionalValidation[${index}] for field "${fieldName}". Expected an object rule.`);
810
+ return;
811
+ }
812
+ const condition = this.parseJsonLogicExpression(candidate['condition'], fieldName, `x-ui.conditionalValidation[${index}].condition`);
813
+ const validatorsSource = candidate['validators'];
814
+ if (!validatorsSource || typeof validatorsSource !== 'object' || Array.isArray(validatorsSource)) {
815
+ console.warn(`[SchemaNormalizer] Ignoring x-ui.conditionalValidation[${index}] for field "${fieldName}" because validators is missing or invalid.`);
816
+ return;
817
+ }
818
+ rules.push({
819
+ condition: condition ?? null,
820
+ validators: this.parseValidators(validatorsSource),
821
+ });
822
+ });
823
+ return rules.length ? rules : undefined;
813
824
  }
814
825
  /**
815
826
  * Extrai e normaliza propriedades de validação a partir do objeto x-ui.
@@ -818,7 +829,7 @@ class SchemaNormalizerService {
818
829
  * arquivos (allowedFileTypes, fileTypeMessage, maxFileSize), custom/async validators,
819
830
  * minWords e comportamento (validationTrigger(s), validationDebounce, showInlineErrors, errorPosition).
820
831
  */
821
- parseValidators(ui) {
832
+ parseValidators(ui, fieldName = 'unknown') {
822
833
  if (!ui || typeof ui !== 'object') {
823
834
  return {};
824
835
  }
@@ -893,9 +904,9 @@ class SchemaNormalizerService {
893
904
  if (uniq)
894
905
  out.uniqueValidator = uniq;
895
906
  }
896
- // Conditional validation rule
907
+ // Conditional validation rules
897
908
  if (ui.conditionalValidation !== undefined) {
898
- const cond = this.parseConditionalValidator(ui.conditionalValidation);
909
+ const cond = this.parseConditionalValidationRules(ui.conditionalValidation, fieldName);
899
910
  if (cond)
900
911
  out.conditionalValidation = cond;
901
912
  }
@@ -949,6 +960,19 @@ class SchemaNormalizerService {
949
960
  }
950
961
  return value.map((v) => ({ ...v }));
951
962
  }
963
+ parseJsonLogicExpression(value, fieldName, sourceKey) {
964
+ if (value === undefined) {
965
+ return undefined;
966
+ }
967
+ if (value === null) {
968
+ return null;
969
+ }
970
+ if (typeof value === 'object' && !Array.isArray(value)) {
971
+ return JSON.parse(JSON.stringify(value));
972
+ }
973
+ console.warn(`[SchemaNormalizer] Ignoring non-Json-Logic ${sourceKey} for field "${fieldName}". Expected object/null, received ${typeof value}.`);
974
+ return undefined;
975
+ }
952
976
  resolveNonEmptyStringToken(...values) {
953
977
  for (const value of values) {
954
978
  const text = String(value ?? '').trim();
@@ -1085,14 +1109,13 @@ class SchemaNormalizerService {
1085
1109
  if (ui.viewOnlyStyle !== undefined) {
1086
1110
  field.viewOnlyStyle = String(ui.viewOnlyStyle);
1087
1111
  }
1088
- Object.assign(field, this.parseValidators(ui));
1089
- // Inline conditional functions
1090
- const condDisplay = this.parsePredicateFunction(ui.conditionalDisplay);
1091
- if (condDisplay) {
1112
+ Object.assign(field, this.parseValidators(ui, name));
1113
+ const condDisplay = this.parseJsonLogicExpression(ui.conditionalDisplay, name, 'x-ui.conditionalDisplay');
1114
+ if (condDisplay !== undefined) {
1092
1115
  field.conditionalDisplay = condDisplay;
1093
1116
  }
1094
- const condRequired = this.parsePredicateFunction(ui.conditionalRequired);
1095
- if (condRequired) {
1117
+ const condRequired = this.parseJsonLogicExpression(ui.conditionalRequired, name, 'x-ui.conditionalRequired');
1118
+ if (condRequired !== undefined) {
1096
1119
  field.conditionalRequired = condRequired;
1097
1120
  }
1098
1121
  const transformFn = this.parseTransformFunction(ui.transformValueFunction);
@@ -1122,7 +1145,10 @@ class SchemaNormalizerService {
1122
1145
  field.hint = ui.hint;
1123
1146
  }
1124
1147
  if (ui.hiddenCondition !== undefined) {
1125
- field.hiddenCondition = ui.hiddenCondition;
1148
+ const hiddenCondition = this.parseJsonLogicExpression(ui.hiddenCondition, name, 'x-ui.hiddenCondition');
1149
+ if (hiddenCondition !== undefined) {
1150
+ field.hiddenCondition = hiddenCondition;
1151
+ }
1126
1152
  }
1127
1153
  if (ui.icon !== undefined) {
1128
1154
  field.icon = ui.icon;
@@ -1519,7 +1545,6 @@ function resolveDefaultHeaders() {
1519
1545
  * HTTP-based storage that talks to praxis-config-starter user-config API.
1520
1546
  */
1521
1547
  class ApiConfigStorage {
1522
- static unavailableLoadBaseUrls = new Set();
1523
1548
  static loadAvailabilityProbes = new Map();
1524
1549
  http = inject(HttpClient);
1525
1550
  opts = inject(API_CONFIG_STORAGE_OPTIONS, { optional: true });
@@ -1551,9 +1576,6 @@ class ApiConfigStorage {
1551
1576
  return this.shouldPropagateClearError(key);
1552
1577
  }
1553
1578
  loadConfig(key) {
1554
- if (ApiConfigStorage.unavailableLoadBaseUrls.has(this.baseUrl)) {
1555
- return of(null);
1556
- }
1557
1579
  const existingProbe = ApiConfigStorage.loadAvailabilityProbes.get(this.baseUrl);
1558
1580
  if (existingProbe) {
1559
1581
  return defer(() => from(existingProbe).pipe(switchMap((available) => available ? this.executeLoadConfigRequest(key, false) : of(null))));
@@ -1582,11 +1604,6 @@ class ApiConfigStorage {
1582
1604
  resolveProbe(true);
1583
1605
  previousRelease();
1584
1606
  };
1585
- const markUnavailable = () => {
1586
- ApiConfigStorage.unavailableLoadBaseUrls.add(this.baseUrl);
1587
- resolveProbe(false);
1588
- previousRelease();
1589
- };
1590
1607
  return this.http
1591
1608
  .get(url, { observe: 'response', headers, params })
1592
1609
  .pipe(map((resp) => {
@@ -1603,7 +1620,7 @@ class ApiConfigStorage {
1603
1620
  return of(cached.payload);
1604
1621
  }
1605
1622
  if (err.status === 404) {
1606
- markUnavailable();
1623
+ releaseProbe?.();
1607
1624
  return of(null);
1608
1625
  }
1609
1626
  releaseProbe?.();
@@ -1630,7 +1647,6 @@ class ApiConfigStorage {
1630
1647
  return of(cached.payload);
1631
1648
  }
1632
1649
  if (err.status === 404) {
1633
- ApiConfigStorage.unavailableLoadBaseUrls.add(this.baseUrl);
1634
1650
  return of(null);
1635
1651
  }
1636
1652
  if (this.shouldLogLoadError(key, err)) {
@@ -1645,7 +1661,8 @@ class ApiConfigStorage {
1645
1661
  const { type, id } = this.resolveKey(key);
1646
1662
  const url = `${this.baseUrl}`;
1647
1663
  const params = this.buildParams(type, id);
1648
- const headers = this.buildHeaders();
1664
+ const cached = this.cache.get(key);
1665
+ const headers = this.buildHeaders(cached?.etag ? { 'If-Match': this.formatEtag(cached.etag) } : undefined);
1649
1666
  return this.http
1650
1667
  .put(url, { payload: config }, { observe: 'response', headers, params })
1651
1668
  .pipe(map((resp) => {
@@ -1686,7 +1703,8 @@ class ApiConfigStorage {
1686
1703
  const { type, id } = this.resolveKey(key);
1687
1704
  const url = `${this.baseUrl}`;
1688
1705
  const params = this.buildParams(type, id);
1689
- const headers = this.buildHeaders();
1706
+ const cached = this.cache.get(key);
1707
+ const headers = this.buildHeaders(cached?.etag ? { 'If-Match': this.formatEtag(cached.etag) } : undefined);
1690
1708
  return this.http
1691
1709
  .delete(url, { observe: 'response', headers, params })
1692
1710
  .pipe(map(() => {
@@ -1822,9 +1840,9 @@ function isObject(value) {
1822
1840
  */
1823
1841
  function deepMerge(left, right) {
1824
1842
  if (right === undefined || right === null)
1825
- return clone$1(left);
1843
+ return clone$2(left);
1826
1844
  if (Array.isArray(left)) {
1827
- return (Array.isArray(right) ? right : clone$1(left));
1845
+ return (Array.isArray(right) ? right : clone$2(left));
1828
1846
  }
1829
1847
  if (!isObject(left) || !isObject(right)) {
1830
1848
  // Right wins when defined, else left
@@ -1837,13 +1855,13 @@ function deepMerge(left, right) {
1837
1855
  if (r === undefined)
1838
1856
  continue; // keep left as-is
1839
1857
  if (Array.isArray(r)) {
1840
- out[key] = r.map((v) => clone$1(v));
1858
+ out[key] = r.map((v) => clone$2(v));
1841
1859
  }
1842
1860
  else if (isObject(r) && isObject(l)) {
1843
1861
  out[key] = deepMerge(l, r);
1844
1862
  }
1845
1863
  else {
1846
- out[key] = clone$1(r);
1864
+ out[key] = clone$2(r);
1847
1865
  }
1848
1866
  }
1849
1867
  return out;
@@ -1855,7 +1873,7 @@ function deepMerge(left, right) {
1855
1873
  */
1856
1874
  function fillUndefined(target, defaults) {
1857
1875
  if (defaults === undefined || defaults === null)
1858
- return clone$1(target);
1876
+ return clone$2(target);
1859
1877
  if (!isObject(target) || !isObject(defaults)) {
1860
1878
  return (target === undefined || target === null ? defaults : target);
1861
1879
  }
@@ -1864,7 +1882,7 @@ function fillUndefined(target, defaults) {
1864
1882
  const d = defaults[key];
1865
1883
  const t = target[key];
1866
1884
  if (t === undefined || t === null) {
1867
- out[key] = clone$1(d);
1885
+ out[key] = clone$2(d);
1868
1886
  }
1869
1887
  else if (isObject(t) && isObject(d)) {
1870
1888
  out[key] = fillUndefined(t, d);
@@ -1875,7 +1893,7 @@ function fillUndefined(target, defaults) {
1875
1893
  }
1876
1894
  return out;
1877
1895
  }
1878
- function clone$1(v) {
1896
+ function clone$2(v) {
1879
1897
  if (v == null || typeof v !== 'object')
1880
1898
  return v;
1881
1899
  try {
@@ -3726,10 +3744,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
3726
3744
  args: [API_URL]
3727
3745
  }] }] });
3728
3746
 
3729
- /**
3730
- * Nova arquitetura modular do TableConfig v2.0
3731
- * Preparada para crescimento exponencial e alinhada com as 5 abas do editor
3732
- */
3733
3747
  // =============================================================================
3734
3748
  // HELPER FUNCTIONS
3735
3749
  // =============================================================================
@@ -5586,53 +5600,1067 @@ class OverlayDeciderService {
5586
5600
  reason: this.matrix.fallback.reason,
5587
5601
  };
5588
5602
  }
5589
- matches(ctx, match) {
5590
- if (match.device && !match.device.includes(ctx.device)) {
5603
+ matches(ctx, match) {
5604
+ if (match.device && !match.device.includes(ctx.device)) {
5605
+ return false;
5606
+ }
5607
+ if (match.fieldCount) {
5608
+ if (match.fieldCount.min !== undefined &&
5609
+ ctx.fieldCount < match.fieldCount.min) {
5610
+ return false;
5611
+ }
5612
+ if (match.fieldCount.max !== undefined &&
5613
+ ctx.fieldCount > match.fieldCount.max) {
5614
+ return false;
5615
+ }
5616
+ }
5617
+ if (match.dependencyCount) {
5618
+ if (match.dependencyCount.min !== undefined &&
5619
+ ctx.dependencyCount < match.dependencyCount.min) {
5620
+ return false;
5621
+ }
5622
+ if (match.dependencyCount.max !== undefined &&
5623
+ ctx.dependencyCount > match.dependencyCount.max) {
5624
+ return false;
5625
+ }
5626
+ }
5627
+ if (Array.isArray(match.any)) {
5628
+ return match.any.some((sub) => this.matches(ctx, sub));
5629
+ }
5630
+ return true;
5631
+ }
5632
+ log(ctx, decision) {
5633
+ if (this.debug) {
5634
+ console.debug('Overlay decision', {
5635
+ device: ctx.device,
5636
+ fieldCount: ctx.fieldCount,
5637
+ dependencyCount: ctx.dependencyCount,
5638
+ pattern: decision.pattern,
5639
+ });
5640
+ }
5641
+ }
5642
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OverlayDeciderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5643
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OverlayDeciderService, providedIn: 'root' });
5644
+ }
5645
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OverlayDeciderService, decorators: [{
5646
+ type: Injectable,
5647
+ args: [{ providedIn: 'root' }]
5648
+ }] });
5649
+
5650
+ const PRAXIS_JSON_LOGIC_OPERATORS = new InjectionToken('PRAXIS_JSON_LOGIC_OPERATORS');
5651
+ function providePraxisJsonLogicOperator(definition) {
5652
+ return {
5653
+ provide: PRAXIS_JSON_LOGIC_OPERATORS,
5654
+ multi: true,
5655
+ useValue: definition,
5656
+ };
5657
+ }
5658
+
5659
+ class PraxisJsonLogicError extends Error {
5660
+ code;
5661
+ constructor(code, message) {
5662
+ super(message);
5663
+ this.code = code;
5664
+ this.name = 'PraxisJsonLogicError';
5665
+ }
5666
+ }
5667
+ class PraxisJsonLogicService {
5668
+ customOperators = new Map();
5669
+ constructor(operatorDefinitions) {
5670
+ for (const definition of operatorDefinitions ?? DEFAULT_JSON_LOGIC_OPERATORS) {
5671
+ this.customOperators.set(definition.operator, definition);
5672
+ }
5673
+ }
5674
+ evaluate(expression, data, options) {
5675
+ const context = this.createContext(data, options);
5676
+ return this.evaluateValue(expression, context);
5677
+ }
5678
+ evaluateResult(expression, data, options) {
5679
+ const value = this.evaluate(expression, data, options);
5680
+ return {
5681
+ value,
5682
+ truthy: this.isTruthy(value),
5683
+ };
5684
+ }
5685
+ validate(expression, options) {
5686
+ const result = this.validateResult(expression, options);
5687
+ if (!result.valid) {
5688
+ const first = result.issues[0];
5689
+ throw new PraxisJsonLogicError(first.code, first.message);
5690
+ }
5691
+ }
5692
+ validateResult(expression, options) {
5693
+ const issues = [];
5694
+ const context = this.createContext({}, options);
5695
+ if (options?.requireExpressionObject && !this.isTopLevelExpressionObject(expression)) {
5696
+ issues.push({
5697
+ code: 'RULE_SHAPE_INVALID',
5698
+ message: 'JSON Logic condition must be a single-operator object.',
5699
+ path: '$',
5700
+ });
5701
+ return { valid: false, issues };
5702
+ }
5703
+ this.validateValue(expression, context, '$', issues, true);
5704
+ return {
5705
+ valid: issues.length === 0,
5706
+ issues,
5707
+ };
5708
+ }
5709
+ matches(input) {
5710
+ if (!input.condition) {
5711
+ return true;
5712
+ }
5713
+ return this.evaluateResult(input.condition, input.data, input.options).truthy;
5714
+ }
5715
+ truthy(value) {
5716
+ return this.isTruthy(value);
5717
+ }
5718
+ createContext(data, options) {
5719
+ return {
5720
+ data,
5721
+ availableRoots: options?.availableRoots,
5722
+ defaultRoot: options?.defaultRoot,
5723
+ allowImplicitRoot: options?.allowImplicitRoot,
5724
+ nowUtc: options?.nowUtc,
5725
+ userTimeZone: options?.userTimeZone,
5726
+ };
5727
+ }
5728
+ evaluateValue(value, context) {
5729
+ if (value === null) {
5730
+ return null;
5731
+ }
5732
+ if (Array.isArray(value)) {
5733
+ return value.map((item) => this.evaluateValue(item, context));
5734
+ }
5735
+ if (typeof value !== 'object') {
5736
+ return value;
5737
+ }
5738
+ const candidateRecord = value;
5739
+ if (!this.isExpressionObject(candidateRecord)) {
5740
+ return this.evaluateRecord(candidateRecord, context);
5741
+ }
5742
+ const [operator, rawArgs] = this.getOperatorEntry(candidateRecord);
5743
+ if (operator === 'var') {
5744
+ return this.evaluateVar(rawArgs, context);
5745
+ }
5746
+ if (operator === 'and') {
5747
+ return this.evaluateAnd(rawArgs, context);
5748
+ }
5749
+ if (operator === 'or') {
5750
+ return this.evaluateOr(rawArgs, context);
5751
+ }
5752
+ if (operator === '!') {
5753
+ return !this.isTruthy(this.evaluateArgs(operator, rawArgs, context, 1, 1)[0]);
5754
+ }
5755
+ if (operator === '!!') {
5756
+ return this.isTruthy(this.evaluateArgs(operator, rawArgs, context, 1, 1)[0]);
5757
+ }
5758
+ if (operator === 'if') {
5759
+ return this.evaluateIf(rawArgs, context);
5760
+ }
5761
+ if (isComparisonOperator(operator)) {
5762
+ return this.evaluateComparison(operator, rawArgs, context);
5763
+ }
5764
+ if (isArithmeticOperator(operator)) {
5765
+ return this.evaluateArithmetic(operator, rawArgs, context);
5766
+ }
5767
+ if (operator === 'in') {
5768
+ return this.evaluateIn(rawArgs, context);
5769
+ }
5770
+ if (operator === 'cat') {
5771
+ return this.evaluateCat(rawArgs, context);
5772
+ }
5773
+ if (operator === 'substr') {
5774
+ return this.evaluateSubstr(rawArgs, context);
5775
+ }
5776
+ const custom = this.customOperators.get(operator);
5777
+ if (!custom) {
5778
+ throw new PraxisJsonLogicError('RULE_OPERATOR_UNKNOWN', `Unsupported JSON Logic operator: ${operator}`);
5779
+ }
5780
+ const args = this.toArgumentArray(rawArgs).map((arg) => this.evaluateValue(arg, context));
5781
+ const helpers = {
5782
+ evaluate: (expression) => this.evaluateValue(expression, context),
5783
+ truthy: (candidate) => this.isTruthy(candidate),
5784
+ resolvePath: (path) => this.resolveVarPath(path, context),
5785
+ requireArgs: (candidateOperator, candidateArgs, min, max) => this.requireArgs(candidateOperator, candidateArgs, min, max),
5786
+ };
5787
+ return custom.evaluate(args, context, helpers);
5788
+ }
5789
+ evaluateRecord(value, context) {
5790
+ const result = {};
5791
+ for (const [key, child] of Object.entries(value)) {
5792
+ result[key] = this.evaluateValue(child, context);
5793
+ }
5794
+ return result;
5795
+ }
5796
+ getOperatorEntry(expression) {
5797
+ const entries = Object.entries(expression);
5798
+ if (entries.length !== 1) {
5799
+ throw new PraxisJsonLogicError('RULE_OPERATOR_UNKNOWN', 'JSON Logic expression must contain exactly one operator.');
5800
+ }
5801
+ return [entries[0][0], entries[0][1]];
5802
+ }
5803
+ evaluateVar(rawArgs, context) {
5804
+ if (typeof rawArgs === 'string') {
5805
+ return this.resolveVarPath(rawArgs, context);
5806
+ }
5807
+ if (Array.isArray(rawArgs) && rawArgs.length >= 1 && rawArgs.length <= 2) {
5808
+ const path = rawArgs[0];
5809
+ if (typeof path !== 'string') {
5810
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`var` requires a string path as the first argument.');
5811
+ }
5812
+ const resolved = this.resolveVarPath(path, context);
5813
+ return resolved === undefined && rawArgs.length === 2 ? rawArgs[1] : resolved;
5814
+ }
5815
+ throw new PraxisJsonLogicError('RULE_ARITY_INVALID', '`var` requires a string path or [path, defaultValue].');
5816
+ }
5817
+ evaluateAnd(rawArgs, context) {
5818
+ const args = this.toArgumentArray(rawArgs);
5819
+ this.requireArgs('and', args, 1);
5820
+ let result = null;
5821
+ for (const arg of args) {
5822
+ result = this.evaluateValue(arg, context);
5823
+ if (!this.isTruthy(result)) {
5824
+ return result;
5825
+ }
5826
+ }
5827
+ return result;
5828
+ }
5829
+ evaluateOr(rawArgs, context) {
5830
+ const args = this.toArgumentArray(rawArgs);
5831
+ this.requireArgs('or', args, 1);
5832
+ let result = null;
5833
+ for (const arg of args) {
5834
+ result = this.evaluateValue(arg, context);
5835
+ if (this.isTruthy(result)) {
5836
+ return result;
5837
+ }
5838
+ }
5839
+ return result;
5840
+ }
5841
+ evaluateIf(rawArgs, context) {
5842
+ const args = this.toArgumentArray(rawArgs);
5843
+ this.requireArgs('if', args, 2);
5844
+ for (let index = 0; index + 1 < args.length; index += 2) {
5845
+ const condition = this.evaluateValue(args[index], context);
5846
+ if (this.isTruthy(condition)) {
5847
+ return this.evaluateValue(args[index + 1], context);
5848
+ }
5849
+ }
5850
+ if (args.length % 2 === 1) {
5851
+ return this.evaluateValue(args[args.length - 1], context);
5852
+ }
5853
+ return null;
5854
+ }
5855
+ evaluateComparison(operator, rawArgs, context) {
5856
+ const [left, right] = this.evaluateArgs(operator, rawArgs, context, 2, 2);
5857
+ switch (operator) {
5858
+ case '==':
5859
+ return left == right;
5860
+ case '===':
5861
+ return left === right;
5862
+ case '!=':
5863
+ return left != right;
5864
+ case '!==':
5865
+ return left !== right;
5866
+ case '>':
5867
+ this.assertComparable(operator, left, right);
5868
+ return left > right;
5869
+ case '>=':
5870
+ this.assertComparable(operator, left, right);
5871
+ return left >= right;
5872
+ case '<':
5873
+ this.assertComparable(operator, left, right);
5874
+ return left < right;
5875
+ case '<=':
5876
+ this.assertComparable(operator, left, right);
5877
+ return left <= right;
5878
+ }
5879
+ }
5880
+ evaluateIn(rawArgs, context) {
5881
+ const [needle, haystack] = this.evaluateArgs('in', rawArgs, context, 2, 2);
5882
+ if (typeof haystack === 'string' && typeof needle === 'string') {
5883
+ return haystack.includes(needle);
5884
+ }
5885
+ if (Array.isArray(haystack)) {
5886
+ return haystack.some((item) => item === needle);
5887
+ }
5888
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`in` requires a string haystack or an array haystack.');
5889
+ }
5890
+ evaluateArithmetic(operator, rawArgs, context) {
5891
+ const args = this.evaluateArgs(operator, rawArgs, context, operator === '-' ? 1 : 1);
5892
+ const values = args.map((candidate) => this.toFiniteNumber(operator, candidate));
5893
+ switch (operator) {
5894
+ case '+':
5895
+ return values.reduce((sum, item) => sum + item, 0);
5896
+ case '-':
5897
+ return values.length === 1
5898
+ ? -values[0]
5899
+ : values.slice(1).reduce((acc, item) => acc - item, values[0]);
5900
+ case '*':
5901
+ return values.reduce((product, item) => product * item, 1);
5902
+ case '/': {
5903
+ this.requireArgs('/', values, 2);
5904
+ return values.slice(1).reduce((acc, item) => acc / item, values[0]);
5905
+ }
5906
+ case '%': {
5907
+ this.requireArgs('%', values, 2, 2);
5908
+ return values[0] % values[1];
5909
+ }
5910
+ case 'min':
5911
+ return Math.min(...values);
5912
+ case 'max':
5913
+ return Math.max(...values);
5914
+ }
5915
+ }
5916
+ evaluateCat(rawArgs, context) {
5917
+ const args = this.evaluateArgs('cat', rawArgs, context, 1);
5918
+ return args.map((value) => value == null ? '' : String(value)).join('');
5919
+ }
5920
+ evaluateSubstr(rawArgs, context) {
5921
+ const args = this.evaluateArgs('substr', rawArgs, context, 2, 3);
5922
+ const value = args[0] == null ? '' : String(args[0]);
5923
+ const start = Math.trunc(this.toFiniteNumber('substr', args[1]));
5924
+ const length = args.length === 3 ? Math.trunc(this.toFiniteNumber('substr', args[2])) : undefined;
5925
+ const normalizedStart = start < 0 ? Math.max(value.length + start, 0) : start;
5926
+ if (length === undefined) {
5927
+ return value.slice(normalizedStart);
5928
+ }
5929
+ if (length < 0) {
5930
+ return value.slice(normalizedStart, Math.max(value.length + length, normalizedStart));
5931
+ }
5932
+ return value.slice(normalizedStart, normalizedStart + length);
5933
+ }
5934
+ evaluateArgs(operator, rawArgs, context, min, max) {
5935
+ const args = this.toArgumentArray(rawArgs).map((arg) => this.evaluateValue(arg, context));
5936
+ return this.requireArgs(operator, args, min, max);
5937
+ }
5938
+ requireArgs(operator, rawArgs, min, max) {
5939
+ if (rawArgs.length < min) {
5940
+ throw new PraxisJsonLogicError('RULE_ARITY_INVALID', `Operator ${operator} requires at least ${min} arguments.`);
5941
+ }
5942
+ if (typeof max === 'number' && rawArgs.length > max) {
5943
+ throw new PraxisJsonLogicError('RULE_ARITY_INVALID', `Operator ${operator} accepts at most ${max} arguments.`);
5944
+ }
5945
+ return rawArgs;
5946
+ }
5947
+ resolveVarPath(path, context) {
5948
+ if (!path.length) {
5949
+ return context.data;
5950
+ }
5951
+ const availableRoots = context.availableRoots ?? [];
5952
+ const allowImplicitRoot = context.allowImplicitRoot ?? availableRoots.length <= 1;
5953
+ const firstToken = splitPath(path)[0];
5954
+ if (!allowImplicitRoot && availableRoots.length > 1 && !availableRoots.includes(firstToken)) {
5955
+ throw new PraxisJsonLogicError('RULE_CONTEXT_AMBIGUOUS', `Implicit root is not allowed for path "${path}".`);
5956
+ }
5957
+ return readPath(context.data, path);
5958
+ }
5959
+ assertComparable(operator, left, right) {
5960
+ const comparable = (typeof left === 'number' && typeof right === 'number') ||
5961
+ (typeof left === 'string' && typeof right === 'string');
5962
+ if (!comparable) {
5963
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', `Operator ${operator} requires comparable operands.`);
5964
+ }
5965
+ }
5966
+ toFiniteNumber(operator, value) {
5967
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
5968
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', `Operator ${operator} requires finite numeric operands.`);
5969
+ }
5970
+ return value;
5971
+ }
5972
+ toArgumentArray(rawArgs) {
5973
+ return Array.isArray(rawArgs) ? rawArgs : [rawArgs];
5974
+ }
5975
+ isExpressionObject(value) {
5976
+ const keys = Object.keys(value);
5977
+ return keys.length === 1;
5978
+ }
5979
+ isTruthy(value) {
5980
+ if (value === undefined || value === null) {
5981
+ return false;
5982
+ }
5983
+ if (typeof value === 'boolean') {
5984
+ return value;
5985
+ }
5986
+ if (typeof value === 'number') {
5987
+ return value !== 0;
5988
+ }
5989
+ if (typeof value === 'string') {
5990
+ return value.length > 0;
5991
+ }
5992
+ if (Array.isArray(value)) {
5993
+ return value.length > 0;
5994
+ }
5995
+ return true;
5996
+ }
5997
+ validateValue(value, context, path, issues, topLevel = false) {
5998
+ if (value === null || typeof value !== 'object') {
5999
+ return;
6000
+ }
6001
+ if (Array.isArray(value)) {
6002
+ if (topLevel) {
6003
+ issues.push({
6004
+ code: 'RULE_SHAPE_INVALID',
6005
+ message: 'Top-level JSON Logic condition cannot be an array.',
6006
+ path,
6007
+ });
6008
+ }
6009
+ value.forEach((item, index) => this.validateValue(item, context, `${path}[${index}]`, issues));
6010
+ return;
6011
+ }
6012
+ const record = value;
6013
+ const keys = Object.keys(record);
6014
+ if (keys.length !== 1) {
6015
+ if (topLevel) {
6016
+ issues.push({
6017
+ code: 'RULE_SHAPE_INVALID',
6018
+ message: 'Top-level JSON Logic condition must contain exactly one operator.',
6019
+ path,
6020
+ });
6021
+ }
6022
+ for (const [key, child] of Object.entries(record)) {
6023
+ this.validateValue(child, context, `${path}.${key}`, issues);
6024
+ }
6025
+ return;
6026
+ }
6027
+ const operator = keys[0];
6028
+ const rawArgs = record[operator];
6029
+ if (operator === 'var') {
6030
+ this.validateVar(rawArgs, context, path, issues);
6031
+ return;
6032
+ }
6033
+ if (!this.isSupportedOperator(operator)) {
6034
+ issues.push({
6035
+ code: 'RULE_OPERATOR_UNKNOWN',
6036
+ message: `Unsupported JSON Logic operator: ${operator}`,
6037
+ path,
6038
+ operator,
6039
+ });
6040
+ return;
6041
+ }
6042
+ const args = this.toArgumentArray(rawArgs);
6043
+ const arity = this.getOperatorArity(operator);
6044
+ if (arity) {
6045
+ this.validateArity(operator, args.length, arity.min, arity.max, path, issues);
6046
+ }
6047
+ args.forEach((arg, index) => this.validateValue(arg, context, `${path}.${operator}[${index}]`, issues));
6048
+ }
6049
+ validateVar(rawArgs, context, path, issues) {
6050
+ if (typeof rawArgs === 'string') {
6051
+ this.validateVarPath(rawArgs, context, path, issues);
6052
+ return;
6053
+ }
6054
+ if (Array.isArray(rawArgs) && rawArgs.length >= 1 && rawArgs.length <= 2) {
6055
+ const candidatePath = rawArgs[0];
6056
+ if (typeof candidatePath !== 'string') {
6057
+ issues.push({
6058
+ code: 'RULE_ARGUMENT_TYPE_INVALID',
6059
+ message: '`var` requires a string path as the first argument.',
6060
+ path,
6061
+ operator: 'var',
6062
+ });
6063
+ return;
6064
+ }
6065
+ this.validateVarPath(candidatePath, context, path, issues);
6066
+ if (rawArgs.length === 2) {
6067
+ this.validateValue(rawArgs[1], context, `${path}.var[1]`, issues);
6068
+ }
6069
+ return;
6070
+ }
6071
+ issues.push({
6072
+ code: 'RULE_ARITY_INVALID',
6073
+ message: '`var` requires a string path or [path, defaultValue].',
6074
+ path,
6075
+ operator: 'var',
6076
+ });
6077
+ }
6078
+ validateVarPath(rawPath, context, path, issues) {
6079
+ if (!rawPath.length) {
6080
+ return;
6081
+ }
6082
+ const availableRoots = context.availableRoots ?? [];
6083
+ if (!availableRoots.length) {
6084
+ return;
6085
+ }
6086
+ const firstToken = splitPath(rawPath)[0];
6087
+ if (!firstToken) {
6088
+ return;
6089
+ }
6090
+ if (ALL_RULE_CONTEXT_ROOTS.includes(firstToken)
6091
+ && !availableRoots.includes(firstToken)) {
6092
+ issues.push({
6093
+ code: 'RULE_ROOT_UNKNOWN',
6094
+ message: `Root "${firstToken}" is not available in this JSON Logic context.`,
6095
+ path,
6096
+ operator: 'var',
6097
+ });
6098
+ return;
6099
+ }
6100
+ const allowImplicitRoot = context.allowImplicitRoot ?? availableRoots.length <= 1;
6101
+ if (!allowImplicitRoot
6102
+ && availableRoots.length > 1
6103
+ && !availableRoots.includes(firstToken)) {
6104
+ issues.push({
6105
+ code: 'RULE_CONTEXT_AMBIGUOUS',
6106
+ message: `Implicit root is not allowed for path "${rawPath}".`,
6107
+ path,
6108
+ operator: 'var',
6109
+ });
6110
+ }
6111
+ }
6112
+ validateArity(operator, count, min, max, path, issues) {
6113
+ if (count < min) {
6114
+ issues.push({
6115
+ code: 'RULE_ARITY_INVALID',
6116
+ message: `Operator ${operator} requires at least ${min} arguments.`,
6117
+ path,
6118
+ operator,
6119
+ });
6120
+ }
6121
+ if (typeof max === 'number' && count > max) {
6122
+ issues.push({
6123
+ code: 'RULE_ARITY_INVALID',
6124
+ message: `Operator ${operator} accepts at most ${max} arguments.`,
6125
+ path,
6126
+ operator,
6127
+ });
6128
+ }
6129
+ }
6130
+ getOperatorArity(operator) {
6131
+ switch (operator) {
6132
+ case 'and':
6133
+ case 'or':
6134
+ return { min: 1 };
6135
+ case '!':
6136
+ case '!!':
6137
+ return { min: 1, max: 1 };
6138
+ case 'if':
6139
+ return { min: 2 };
6140
+ case 'in':
6141
+ case '==':
6142
+ case '===':
6143
+ case '!=':
6144
+ case '!==':
6145
+ case '>':
6146
+ case '>=':
6147
+ case '<':
6148
+ case '<=':
6149
+ return { min: 2, max: 2 };
6150
+ case 'cat':
6151
+ return { min: 1 };
6152
+ case 'substr':
6153
+ return { min: 2, max: 3 };
6154
+ case '+':
6155
+ case '*':
6156
+ case 'min':
6157
+ case 'max':
6158
+ return { min: 1 };
6159
+ case '-':
6160
+ return { min: 1 };
6161
+ case '/':
6162
+ return { min: 2 };
6163
+ case '%':
6164
+ return { min: 2, max: 2 };
6165
+ default: {
6166
+ const custom = this.customOperators.get(operator);
6167
+ if (!custom) {
6168
+ return undefined;
6169
+ }
6170
+ return {
6171
+ min: custom.minArgs ?? 0,
6172
+ max: custom.maxArgs,
6173
+ };
6174
+ }
6175
+ }
6176
+ }
6177
+ isSupportedOperator(operator) {
6178
+ return BUILTIN_JSON_LOGIC_OPERATORS.includes(operator)
6179
+ || this.customOperators.has(operator);
6180
+ }
6181
+ isTopLevelExpressionObject(value) {
6182
+ return !!value && typeof value === 'object' && !Array.isArray(value);
6183
+ }
6184
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisJsonLogicService, deps: [{ token: PRAXIS_JSON_LOGIC_OPERATORS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
6185
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisJsonLogicService, providedIn: 'root' });
6186
+ }
6187
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisJsonLogicService, decorators: [{
6188
+ type: Injectable,
6189
+ args: [{ providedIn: 'root' }]
6190
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
6191
+ type: Optional
6192
+ }, {
6193
+ type: Inject,
6194
+ args: [PRAXIS_JSON_LOGIC_OPERATORS]
6195
+ }] }] });
6196
+ const DEFAULT_JSON_LOGIC_OPERATORS = [
6197
+ {
6198
+ operator: 'contains',
6199
+ minArgs: 2,
6200
+ maxArgs: 2,
6201
+ evaluate: (args, _context, helpers) => {
6202
+ const [container, candidate] = helpers.requireArgs('contains', args, 2, 2);
6203
+ if (typeof container === 'string' && typeof candidate === 'string') {
6204
+ return container.includes(candidate);
6205
+ }
6206
+ if (Array.isArray(container)) {
6207
+ return container.some((item) => item === candidate);
6208
+ }
6209
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`contains` requires a string or array as the first argument.');
6210
+ },
6211
+ },
6212
+ {
6213
+ operator: 'startsWith',
6214
+ minArgs: 2,
6215
+ maxArgs: 2,
6216
+ evaluate: (args, _context, helpers) => {
6217
+ const [value, prefix] = helpers.requireArgs('startsWith', args, 2, 2);
6218
+ if (typeof value !== 'string' || typeof prefix !== 'string') {
6219
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`startsWith` requires string arguments.');
6220
+ }
6221
+ return value.startsWith(prefix);
6222
+ },
6223
+ },
6224
+ {
6225
+ operator: 'endsWith',
6226
+ minArgs: 2,
6227
+ maxArgs: 2,
6228
+ evaluate: (args, _context, helpers) => {
6229
+ const [value, suffix] = helpers.requireArgs('endsWith', args, 2, 2);
6230
+ if (typeof value !== 'string' || typeof suffix !== 'string') {
6231
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`endsWith` requires string arguments.');
6232
+ }
6233
+ return value.endsWith(suffix);
6234
+ },
6235
+ },
6236
+ {
6237
+ operator: 'matches',
6238
+ minArgs: 2,
6239
+ maxArgs: 2,
6240
+ evaluate: (args, _context, helpers) => {
6241
+ const [value, pattern] = helpers.requireArgs('matches', args, 2, 2);
6242
+ if (typeof value !== 'string' || typeof pattern !== 'string') {
6243
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`matches` requires string arguments.');
6244
+ }
6245
+ try {
6246
+ return new RegExp(pattern).test(value);
6247
+ }
6248
+ catch {
6249
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`matches` received an invalid regular expression.');
6250
+ }
6251
+ },
6252
+ },
6253
+ {
6254
+ operator: 'isBlank',
6255
+ minArgs: 1,
6256
+ maxArgs: 1,
6257
+ evaluate: (args, _context, helpers) => {
6258
+ const [value] = helpers.requireArgs('isBlank', args, 1, 1);
6259
+ if (value === undefined || value === null) {
6260
+ return true;
6261
+ }
6262
+ if (typeof value === 'string') {
6263
+ return value.trim().length === 0;
6264
+ }
6265
+ if (Array.isArray(value)) {
6266
+ return value.length === 0;
6267
+ }
6268
+ return false;
6269
+ },
6270
+ },
6271
+ {
6272
+ operator: 'len',
6273
+ minArgs: 1,
6274
+ maxArgs: 1,
6275
+ evaluate: (args, _context, helpers) => {
6276
+ const [value] = helpers.requireArgs('len', args, 1, 1);
6277
+ if (value === undefined || value === null) {
6278
+ return null;
6279
+ }
6280
+ if (typeof value === 'string' || Array.isArray(value)) {
6281
+ return value.length;
6282
+ }
6283
+ if (value instanceof Set || value instanceof Map) {
6284
+ return value.size;
6285
+ }
6286
+ if (typeof value === 'object') {
6287
+ return Object.keys(value).length;
6288
+ }
6289
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`len` requires a string, array, set, map, or object target.');
6290
+ },
6291
+ },
6292
+ {
6293
+ operator: 'round',
6294
+ minArgs: 1,
6295
+ maxArgs: 1,
6296
+ evaluate: (args, _context, helpers) => {
6297
+ const [value] = helpers.requireArgs('round', args, 1, 1);
6298
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
6299
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`round` requires a finite numeric argument.');
6300
+ }
6301
+ return Math.round(value);
6302
+ },
6303
+ },
6304
+ {
6305
+ operator: 'ceil',
6306
+ minArgs: 1,
6307
+ maxArgs: 1,
6308
+ evaluate: (args, _context, helpers) => {
6309
+ const [value] = helpers.requireArgs('ceil', args, 1, 1);
6310
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
6311
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`ceil` requires a finite numeric argument.');
6312
+ }
6313
+ return Math.ceil(value);
6314
+ },
6315
+ },
6316
+ {
6317
+ operator: 'floor',
6318
+ minArgs: 1,
6319
+ maxArgs: 1,
6320
+ evaluate: (args, _context, helpers) => {
6321
+ const [value] = helpers.requireArgs('floor', args, 1, 1);
6322
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
6323
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`floor` requires a finite numeric argument.');
6324
+ }
6325
+ return Math.floor(value);
6326
+ },
6327
+ },
6328
+ {
6329
+ operator: 'abs',
6330
+ minArgs: 1,
6331
+ maxArgs: 1,
6332
+ evaluate: (args, _context, helpers) => {
6333
+ const [value] = helpers.requireArgs('abs', args, 1, 1);
6334
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
6335
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`abs` requires a finite numeric argument.');
6336
+ }
6337
+ return Math.abs(value);
6338
+ },
6339
+ },
6340
+ {
6341
+ operator: 'coalesce',
6342
+ minArgs: 1,
6343
+ evaluate: (args, _context, helpers) => {
6344
+ const values = helpers.requireArgs('coalesce', args, 1);
6345
+ for (const value of values) {
6346
+ if (value !== undefined && value !== null && value !== '') {
6347
+ return value;
6348
+ }
6349
+ }
6350
+ return null;
6351
+ },
6352
+ },
6353
+ {
6354
+ operator: 'now',
6355
+ minArgs: 0,
6356
+ maxArgs: 0,
6357
+ evaluate: () => Date.now(),
6358
+ },
6359
+ {
6360
+ operator: 'date',
6361
+ minArgs: 1,
6362
+ maxArgs: 1,
6363
+ evaluate: (args, _context, helpers) => {
6364
+ const [value] = helpers.requireArgs('date', args, 1, 1);
6365
+ const parsed = parseTemporalValue(value);
6366
+ if (!parsed) {
6367
+ return null;
6368
+ }
6369
+ if (parsed.kind === 'date-only') {
6370
+ return Date.parse(`${parsed.ymd}T00:00:00.000Z`);
6371
+ }
6372
+ return parsed.date.getTime();
6373
+ },
6374
+ },
6375
+ {
6376
+ operator: 'yearsSince',
6377
+ minArgs: 1,
6378
+ maxArgs: 1,
6379
+ evaluate: (args, context, helpers) => {
6380
+ const [value] = helpers.requireArgs('yearsSince', args, 1, 1);
6381
+ const parsed = parseTemporalValue(value);
6382
+ if (!parsed) {
6383
+ return null;
6384
+ }
6385
+ const now = context.nowUtc ? new Date(context.nowUtc) : new Date();
6386
+ return Math.floor((now.getTime() - temporalValueToInstant(parsed).getTime()) / 31557600000);
6387
+ },
6388
+ },
6389
+ {
6390
+ operator: 'monthsSince',
6391
+ minArgs: 1,
6392
+ maxArgs: 1,
6393
+ evaluate: (args, context, helpers) => {
6394
+ const [value] = helpers.requireArgs('monthsSince', args, 1, 1);
6395
+ const parsed = parseTemporalValue(value);
6396
+ if (!parsed) {
6397
+ return null;
6398
+ }
6399
+ const now = context.nowUtc ? new Date(context.nowUtc) : new Date();
6400
+ return Math.floor((now.getTime() - temporalValueToInstant(parsed).getTime()) / 2629800000);
6401
+ },
6402
+ },
6403
+ {
6404
+ operator: 'daysSince',
6405
+ minArgs: 1,
6406
+ maxArgs: 1,
6407
+ evaluate: (args, context, helpers) => {
6408
+ const [value] = helpers.requireArgs('daysSince', args, 1, 1);
6409
+ const parsed = parseTemporalValue(value);
6410
+ if (!parsed) {
6411
+ return null;
6412
+ }
6413
+ const now = context.nowUtc ? new Date(context.nowUtc) : new Date();
6414
+ return Math.floor((now.getTime() - temporalValueToInstant(parsed).getTime()) / 86400000);
6415
+ },
6416
+ },
6417
+ {
6418
+ operator: 'toNumber',
6419
+ minArgs: 1,
6420
+ maxArgs: 1,
6421
+ evaluate: (args, _context, helpers) => {
6422
+ const [value] = helpers.requireArgs('toNumber', args, 1, 1);
6423
+ const parsed = Number(value);
6424
+ return Number.isFinite(parsed) ? parsed : null;
6425
+ },
6426
+ },
6427
+ {
6428
+ operator: 'stringify',
6429
+ minArgs: 1,
6430
+ maxArgs: 1,
6431
+ evaluate: (args, _context, helpers) => {
6432
+ const [value] = helpers.requireArgs('stringify', args, 1, 1);
6433
+ return value == null ? '' : String(value);
6434
+ },
6435
+ },
6436
+ {
6437
+ operator: 'jsonGet',
6438
+ minArgs: 2,
6439
+ maxArgs: 2,
6440
+ evaluate: (args, _context, helpers) => {
6441
+ const [target, path] = helpers.requireArgs('jsonGet', args, 2, 2);
6442
+ if (typeof path !== 'string') {
6443
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`jsonGet` requires a string path.');
6444
+ }
6445
+ if (target === null || typeof target !== 'object' || Array.isArray(target)) {
6446
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`jsonGet` requires an object target.');
6447
+ }
6448
+ return readPath(target, path);
6449
+ },
6450
+ },
6451
+ {
6452
+ operator: 'hasKey',
6453
+ minArgs: 2,
6454
+ maxArgs: 2,
6455
+ evaluate: (args, _context, helpers) => {
6456
+ const [target, path] = helpers.requireArgs('hasKey', args, 2, 2);
6457
+ if (typeof path !== 'string') {
6458
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`hasKey` requires a string path.');
6459
+ }
6460
+ if (target === null || typeof target !== 'object' || Array.isArray(target)) {
6461
+ throw new PraxisJsonLogicError('RULE_ARGUMENT_TYPE_INVALID', '`hasKey` requires an object target.');
6462
+ }
6463
+ return hasPath(target, path);
6464
+ },
6465
+ },
6466
+ {
6467
+ operator: 'isToday',
6468
+ minArgs: 1,
6469
+ maxArgs: 1,
6470
+ evaluate: (args, context, helpers) => {
6471
+ const [candidate] = helpers.requireArgs('isToday', args, 1, 1);
6472
+ const parsed = parseTemporalValue(candidate);
6473
+ if (!parsed) {
6474
+ return false;
6475
+ }
6476
+ return temporalValueToYmd(parsed, context) === currentYmd(context);
6477
+ },
6478
+ },
6479
+ {
6480
+ operator: 'inLast',
6481
+ minArgs: 3,
6482
+ maxArgs: 3,
6483
+ evaluate: (args, context, helpers) => {
6484
+ const [candidate, rawAmount, rawUnit] = helpers.requireArgs('inLast', args, 3, 3);
6485
+ const parsed = parseTemporalValue(candidate);
6486
+ const amount = Math.abs(Number(rawAmount));
6487
+ const unit = String(rawUnit || '').toLowerCase();
6488
+ if (!parsed || !Number.isFinite(amount) || !unit) {
6489
+ return false;
6490
+ }
6491
+ const today = currentYmd(context);
6492
+ const floor = addYmdByUnit(today, -amount, unit);
6493
+ const candidateYmd = temporalValueToYmd(parsed, context);
6494
+ return candidateYmd >= floor && candidateYmd <= today;
6495
+ },
6496
+ },
6497
+ {
6498
+ operator: 'weekdayIn',
6499
+ minArgs: 2,
6500
+ maxArgs: 2,
6501
+ evaluate: (args, context, helpers) => {
6502
+ const [candidate, rawDays] = helpers.requireArgs('weekdayIn', args, 2, 2);
6503
+ const parsed = parseTemporalValue(candidate);
6504
+ if (!parsed || !Array.isArray(rawDays)) {
6505
+ return false;
6506
+ }
6507
+ const normalized = rawDays.map((item) => Number(item)).filter(Number.isFinite);
6508
+ if (!normalized.length) {
6509
+ return false;
6510
+ }
6511
+ return normalized.includes(ymdToWeekday(temporalValueToYmd(parsed, context)));
6512
+ },
6513
+ },
6514
+ ];
6515
+ const ALL_RULE_CONTEXT_ROOTS = [
6516
+ 'form',
6517
+ 'row',
6518
+ 'computed',
6519
+ 'meta',
6520
+ 'source',
6521
+ 'event',
6522
+ 'payload',
6523
+ 'state',
6524
+ 'context',
6525
+ ];
6526
+ const BUILTIN_JSON_LOGIC_OPERATORS = [
6527
+ 'var',
6528
+ '==',
6529
+ '===',
6530
+ '!=',
6531
+ '!==',
6532
+ '>',
6533
+ '>=',
6534
+ '<',
6535
+ '<=',
6536
+ '!',
6537
+ '!!',
6538
+ 'and',
6539
+ 'or',
6540
+ 'if',
6541
+ 'in',
6542
+ 'cat',
6543
+ 'substr',
6544
+ '+',
6545
+ '-',
6546
+ '*',
6547
+ '/',
6548
+ '%',
6549
+ 'min',
6550
+ 'max',
6551
+ ];
6552
+ function isComparisonOperator(operator) {
6553
+ return ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(operator);
6554
+ }
6555
+ function isArithmeticOperator(operator) {
6556
+ return ['+', '-', '*', '/', '%', 'min', 'max'].includes(operator);
6557
+ }
6558
+ function parseTemporalValue(value) {
6559
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
6560
+ return { kind: 'instant', date: value };
6561
+ }
6562
+ if (typeof value === 'number') {
6563
+ const date = new Date(value);
6564
+ return Number.isNaN(date.getTime()) ? null : { kind: 'instant', date };
6565
+ }
6566
+ const raw = String(value ?? '').trim();
6567
+ if (!raw) {
6568
+ return null;
6569
+ }
6570
+ if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) {
6571
+ return { kind: 'date-only', ymd: raw };
6572
+ }
6573
+ const date = new Date(raw);
6574
+ return Number.isNaN(date.getTime()) ? null : { kind: 'instant', date };
6575
+ }
6576
+ function temporalValueToYmd(value, context) {
6577
+ if (value.kind === 'date-only') {
6578
+ return value.ymd;
6579
+ }
6580
+ return formatDateYmd(value.date, context.userTimeZone);
6581
+ }
6582
+ function temporalValueToInstant(value) {
6583
+ if (value.kind === 'instant') {
6584
+ return value.date;
6585
+ }
6586
+ return new Date(`${value.ymd}T00:00:00.000Z`);
6587
+ }
6588
+ function currentYmd(context) {
6589
+ const now = context.nowUtc ? new Date(context.nowUtc) : new Date();
6590
+ return formatDateYmd(now, context.userTimeZone);
6591
+ }
6592
+ function formatDateYmd(date, timeZone) {
6593
+ if (!timeZone) {
6594
+ return date.toISOString().slice(0, 10);
6595
+ }
6596
+ const formatter = new Intl.DateTimeFormat('en-CA', {
6597
+ timeZone,
6598
+ year: 'numeric',
6599
+ month: '2-digit',
6600
+ day: '2-digit',
6601
+ });
6602
+ return formatter.format(date);
6603
+ }
6604
+ function addYmdByUnit(ymd, amount, unit) {
6605
+ const [year, month, day] = ymd.split('-').map((item) => Number(item));
6606
+ const base = new Date(Date.UTC(year, month - 1, day));
6607
+ const normalized = String(unit || 'days').toLowerCase();
6608
+ if (normalized.startsWith('week')) {
6609
+ base.setUTCDate(base.getUTCDate() + amount * 7);
6610
+ return base.toISOString().slice(0, 10);
6611
+ }
6612
+ if (normalized.startsWith('month')) {
6613
+ return new Date(Date.UTC(base.getUTCFullYear(), base.getUTCMonth() + amount, Math.min(base.getUTCDate(), 28)))
6614
+ .toISOString()
6615
+ .slice(0, 10);
6616
+ }
6617
+ base.setUTCDate(base.getUTCDate() + amount);
6618
+ return base.toISOString().slice(0, 10);
6619
+ }
6620
+ function ymdToWeekday(ymd) {
6621
+ const [year, month, day] = ymd.split('-').map((item) => Number(item));
6622
+ const date = new Date(Date.UTC(year, month - 1, day));
6623
+ const weekday = date.getUTCDay();
6624
+ return weekday === 0 ? 7 : weekday;
6625
+ }
6626
+ function splitPath(path) {
6627
+ const normalized = path.startsWith('$.') ? path.slice(2) : path;
6628
+ return normalized
6629
+ .replace(/\["([^"]+)"\]/g, '.$1')
6630
+ .replace(/\[(\d+)\]/g, '.$1')
6631
+ .split('.')
6632
+ .filter(Boolean);
6633
+ }
6634
+ function readPath(target, path) {
6635
+ let current = target;
6636
+ for (const segment of splitPath(path)) {
6637
+ if (current === null || typeof current !== 'object') {
6638
+ return undefined;
6639
+ }
6640
+ current = current[segment];
6641
+ }
6642
+ return current;
6643
+ }
6644
+ function hasPath(target, path) {
6645
+ let current = target;
6646
+ for (const segment of splitPath(path)) {
6647
+ if (current === null || typeof current !== 'object') {
5591
6648
  return false;
5592
6649
  }
5593
- if (match.fieldCount) {
5594
- if (match.fieldCount.min !== undefined &&
5595
- ctx.fieldCount < match.fieldCount.min) {
5596
- return false;
5597
- }
5598
- if (match.fieldCount.max !== undefined &&
5599
- ctx.fieldCount > match.fieldCount.max) {
5600
- return false;
5601
- }
5602
- }
5603
- if (match.dependencyCount) {
5604
- if (match.dependencyCount.min !== undefined &&
5605
- ctx.dependencyCount < match.dependencyCount.min) {
5606
- return false;
5607
- }
5608
- if (match.dependencyCount.max !== undefined &&
5609
- ctx.dependencyCount > match.dependencyCount.max) {
5610
- return false;
5611
- }
5612
- }
5613
- if (Array.isArray(match.any)) {
5614
- return match.any.some((sub) => this.matches(ctx, sub));
5615
- }
5616
- return true;
5617
- }
5618
- log(ctx, decision) {
5619
- if (this.debug) {
5620
- console.debug('Overlay decision', {
5621
- device: ctx.device,
5622
- fieldCount: ctx.fieldCount,
5623
- dependencyCount: ctx.dependencyCount,
5624
- pattern: decision.pattern,
5625
- });
6650
+ if (!Object.prototype.hasOwnProperty.call(current, segment)) {
6651
+ return false;
5626
6652
  }
6653
+ current = current[segment];
5627
6654
  }
5628
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OverlayDeciderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5629
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OverlayDeciderService, providedIn: 'root' });
6655
+ return true;
5630
6656
  }
5631
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OverlayDeciderService, decorators: [{
5632
- type: Injectable,
5633
- args: [{ providedIn: 'root' }]
5634
- }] });
5635
6657
 
6658
+ const DEFAULT_JSON_LOGIC$3 = new PraxisJsonLogicService(null);
6659
+ const FORM_RULE_OPTIONS = {
6660
+ availableRoots: ['form'],
6661
+ defaultRoot: 'form',
6662
+ allowImplicitRoot: true,
6663
+ };
5636
6664
  // ----------------------- Utils -----------------------
5637
6665
  function toMessageError(key, base, message) {
5638
6666
  if (!message)
@@ -5788,14 +6816,32 @@ function debounceAsyncValidator(validator, waitMs) {
5788
6816
  };
5789
6817
  }
5790
6818
  // ----------------------- Conditional rules (metadata) -----------------------
5791
- function composeConditionalValidators(rules) {
6819
+ function matchesConditionalValidationRule(jsonLogic, condition, model) {
6820
+ if (!condition) {
6821
+ return true;
6822
+ }
6823
+ try {
6824
+ return jsonLogic.matches({
6825
+ condition,
6826
+ data: { form: model ?? {} },
6827
+ options: FORM_RULE_OPTIONS,
6828
+ });
6829
+ }
6830
+ catch (error) {
6831
+ if (error instanceof PraxisJsonLogicError) {
6832
+ return false;
6833
+ }
6834
+ throw error;
6835
+ }
6836
+ }
6837
+ function composeConditionalValidators(rules, jsonLogic = DEFAULT_JSON_LOGIC$3) {
5792
6838
  // Build per-rule validators once
5793
- const built = rules.map((r) => buildValidatorsFromValidatorOptions(r.validators));
6839
+ const built = rules.map((r) => buildValidatorsFromValidatorOptions(r.validators, jsonLogic));
5794
6840
  const sync = (control) => {
5795
6841
  const model = control.root?.value;
5796
6842
  let errors = null;
5797
6843
  rules.forEach((rule, idx) => {
5798
- if (rule.condition(model)) {
6844
+ if (matchesConditionalValidationRule(jsonLogic, rule.condition, model)) {
5799
6845
  for (const vf of built[idx].validators) {
5800
6846
  const res = vf(control);
5801
6847
  if (res)
@@ -5810,7 +6856,7 @@ function composeConditionalValidators(rules) {
5810
6856
  let errors = null;
5811
6857
  for (let idx = 0; idx < rules.length; idx++) {
5812
6858
  const rule = rules[idx];
5813
- if (!rule.condition(model))
6859
+ if (!matchesConditionalValidationRule(jsonLogic, rule.condition, model))
5814
6860
  continue;
5815
6861
  const avs = built[idx].asyncValidators;
5816
6862
  for (const av of avs) {
@@ -6028,9 +7074,13 @@ function buildAngularValidators(field) {
6028
7074
  av.push(customAsyncValidatorFn(field.asyncValidator));
6029
7075
  if (field.uniqueValidator)
6030
7076
  av.push(uniqueAsyncValidator(field.uniqueValidator, field.uniqueMessage));
6031
- // Conditional (function-based)
6032
- if (field.conditionalValidation && typeof field.conditionalValidation === 'function') {
6033
- av.push(conditionalAsyncValidator(field.conditionalValidation));
7077
+ // Conditional (canonical declarative array)
7078
+ if (Array.isArray(field.conditionalValidation)) {
7079
+ const composed = composeConditionalValidators(field.conditionalValidation, DEFAULT_JSON_LOGIC$3);
7080
+ if (composed.sync)
7081
+ v.push(composed.sync);
7082
+ if (composed.async)
7083
+ av.push(composed.async);
6034
7084
  }
6035
7085
  // Debounce async validators if requested
6036
7086
  const debounceMs = field.validationDebounce;
@@ -6039,7 +7089,7 @@ function buildAngularValidators(field) {
6039
7089
  }
6040
7090
  return { validators: v, asyncValidators: av };
6041
7091
  }
6042
- function buildValidatorsFromValidatorOptions(opts = {}) {
7092
+ function buildValidatorsFromValidatorOptions(opts = {}, jsonLogic = DEFAULT_JSON_LOGIC$3) {
6043
7093
  const v = [];
6044
7094
  let av = [];
6045
7095
  if (opts.required)
@@ -6080,17 +7130,12 @@ function buildValidatorsFromValidatorOptions(opts = {}) {
6080
7130
  v.push(maxFileSizeValidator(mfs));
6081
7131
  // Conditional (metadata array-based)
6082
7132
  if (opts.conditionalValidation && Array.isArray(opts.conditionalValidation)) {
6083
- const composed = composeConditionalValidators(opts.conditionalValidation);
7133
+ const composed = composeConditionalValidators(opts.conditionalValidation, jsonLogic);
6084
7134
  if (composed.sync)
6085
7135
  v.push(composed.sync);
6086
7136
  if (composed.async)
6087
7137
  av.push(composed.async);
6088
7138
  }
6089
- // Conditional function support via 'conditionalValidationFn' ad-hoc entry
6090
- const condFn = opts.conditionalValidationFn;
6091
- if (typeof condFn === 'function') {
6092
- av.push(conditionalAsyncValidator(condFn));
6093
- }
6094
7139
  // Debounce async validators if requested
6095
7140
  const debounceMs = opts.validationDebounce;
6096
7141
  if (debounceMs && debounceMs > 0 && av.length) {
@@ -7002,15 +8047,15 @@ class DefaultLoadingRenderer {
7002
8047
  defaultLabel(ctx) {
7003
8048
  switch (ctx.phase) {
7004
8049
  case 'schema':
7005
- return 'Carregando configuração…';
8050
+ return 'Carregando configuracao...';
7006
8051
  case 'mount':
7007
- return 'Montando interface';
8052
+ return 'Montando interface...';
7008
8053
  case 'data':
7009
- return 'Carregando dados';
8054
+ return 'Carregando dados...';
7010
8055
  case 'action':
7011
- return 'Processando';
8056
+ return 'Processando...';
7012
8057
  default:
7013
- return 'Carregando';
8058
+ return 'Carregando...';
7014
8059
  }
7015
8060
  }
7016
8061
  ensureRoot(doc) {
@@ -7041,6 +8086,7 @@ class DefaultLoadingRenderer {
7041
8086
  }
7042
8087
  .pfx-loading-entry[data-blocking="false"] {
7043
8088
  inset: 0;
8089
+ z-index: var(--praxis-layer-loading-inline, 120);
7044
8090
  }
7045
8091
  .pfx-loading-backdrop {
7046
8092
  position: absolute;
@@ -7072,22 +8118,41 @@ class DefaultLoadingRenderer {
7072
8118
  }
7073
8119
  .pfx-loading-topbar {
7074
8120
  position: absolute;
7075
- top: 0;
7076
- left: 0;
7077
- right: 0;
7078
- height: 3px;
8121
+ top: var(--pdx-loading-topbar-top, 0);
8122
+ left: var(--pdx-loading-topbar-left, 0);
8123
+ right: var(--pdx-loading-topbar-right, 0);
8124
+ height: var(--pdx-loading-topbar-height, 3px);
7079
8125
  background: var(
7080
8126
  --pdx-loading-topbar-gradient,
7081
8127
  linear-gradient(90deg, transparent, var(--pdx-loading-spinner-accent, #8b5cf6), transparent)
7082
8128
  );
8129
+ border-radius: var(--pdx-loading-topbar-radius, 999px);
8130
+ box-shadow: var(--pdx-loading-topbar-shadow, none);
7083
8131
  animation: pfxBar 1.1s ease-in-out infinite;
7084
8132
  }
7085
8133
  .pfx-loading-inline-text {
7086
8134
  position: absolute;
7087
- top: 8px;
7088
- right: 12px;
8135
+ top: var(--pdx-loading-inline-top, 8px);
8136
+ right: var(--pdx-loading-inline-right, 12px);
8137
+ left: auto;
8138
+ display: var(--pdx-loading-inline-display, inline-flex);
8139
+ align-items: center;
8140
+ min-height: var(--pdx-loading-inline-min-height, 28px);
8141
+ max-width: min(
8142
+ var(--pdx-loading-inline-max-width, 320px),
8143
+ calc(100vw - var(--pdx-loading-inline-right, 12px) - var(--pdx-loading-inline-left, 12px))
8144
+ );
8145
+ padding: var(--pdx-loading-inline-padding, 0);
8146
+ border-radius: var(--pdx-loading-inline-radius, 999px);
8147
+ border: var(--pdx-loading-inline-border, 0 solid transparent);
8148
+ background: var(--pdx-loading-inline-bg, transparent);
8149
+ box-shadow: var(--pdx-loading-inline-shadow, none);
7089
8150
  font-size: 12px;
7090
8151
  color: var(--pdx-loading-topbar-text, #5f6368);
8152
+ line-height: 1.2;
8153
+ white-space: nowrap;
8154
+ overflow: hidden;
8155
+ text-overflow: ellipsis;
7091
8156
  }
7092
8157
  @keyframes pfxSpin { to { transform: rotate(360deg); } }
7093
8158
  @keyframes pfxBar {
@@ -11688,6 +12753,7 @@ function getEditorialFormTemplateById(templateId) {
11688
12753
  const RULE_PROPERTY_SCHEMA = {
11689
12754
  field: [
11690
12755
  { name: 'visible', type: 'boolean', label: 'Visível' },
12756
+ { name: 'hidden', type: 'boolean', label: 'Oculto' },
11691
12757
  { name: 'required', type: 'boolean', label: 'Obrigatório' },
11692
12758
  { name: 'readonly', type: 'boolean', label: 'Somente leitura' },
11693
12759
  { name: 'disabled', type: 'boolean', label: 'Desabilitado' },
@@ -12841,11 +13907,11 @@ function normalizeCompositionLink(link) {
12841
13907
  intent: link.intent,
12842
13908
  };
12843
13909
  if (link.transform) {
12844
- normalized.transform = clone(link.transform);
13910
+ normalized.transform = clone$1(link.transform);
12845
13911
  }
12846
- const conditions = normalizeConditions(link.conditions);
12847
- if (conditions) {
12848
- normalized.conditions = conditions;
13912
+ const condition = normalizeCondition(link.condition);
13913
+ if (condition !== undefined) {
13914
+ normalized.condition = condition;
12849
13915
  }
12850
13916
  const policy = normalizePolicy(link.policy);
12851
13917
  if (policy) {
@@ -12868,6 +13934,9 @@ function normalizeCompositionLinks(links) {
12868
13934
  /**
12869
13935
  * Canonical persisted write surface for CompositionLink[].
12870
13936
  * New saved shapes must serialize links under `composition.links`.
13937
+ * Semantic guards persist only through `link.condition` as Json Logic.
13938
+ * Operational delivery behavior remains under `link.policy` and must not be
13939
+ * folded into the condition payload.
12871
13940
  */
12872
13941
  function serializeCompositionLinks(links) {
12873
13942
  return {
@@ -12880,7 +13949,7 @@ function serializeCompositionLinks(links) {
12880
13949
  * The canonical writer omits legacy `connections` to avoid dual-write ambiguity.
12881
13950
  */
12882
13951
  function writeCompositionLinksToPersistedPage(page, links) {
12883
- const { composition: _legacyComposition, ...rest } = clone(page);
13952
+ const { composition: _legacyComposition, ...rest } = clone$1(page);
12884
13953
  return {
12885
13954
  ...rest,
12886
13955
  composition: serializeCompositionLinks(links),
@@ -12907,20 +13976,14 @@ function normalizeEndpoint$1(endpoint) {
12907
13976
  },
12908
13977
  };
12909
13978
  }
12910
- function normalizeConditions(conditions) {
12911
- if (!conditions?.length) {
13979
+ function normalizeCondition(condition) {
13980
+ if (condition === undefined) {
12912
13981
  return undefined;
12913
13982
  }
12914
- return conditions
12915
- .map((condition) => ({
12916
- kind: condition.kind,
12917
- ...(condition.expression ? { expression: condition.expression } : {}),
12918
- ...(condition.path ? { path: condition.path } : {}),
12919
- ...(condition.value !== undefined ? { value: clone(condition.value) } : {}),
12920
- ...(condition.negate !== undefined ? { negate: condition.negate } : {}),
12921
- ...(condition.description ? { description: condition.description } : {}),
12922
- }))
12923
- .sort(compareConditions);
13983
+ if (condition === null) {
13984
+ return null;
13985
+ }
13986
+ return clone$1(condition);
12924
13987
  }
12925
13988
  function normalizePolicy(policy) {
12926
13989
  if (!policy) {
@@ -12955,34 +14018,19 @@ function normalizeMetadata(metadata) {
12955
14018
  };
12956
14019
  return Object.keys(normalized).length ? normalized : undefined;
12957
14020
  }
12958
- function compareConditions(a, b) {
12959
- return compareValues([
12960
- a.kind,
12961
- a.expression || '',
12962
- a.path || '',
12963
- JSON.stringify(a.value ?? null),
12964
- String(a.negate ?? ''),
12965
- a.description || '',
12966
- ], [
12967
- b.kind,
12968
- b.expression || '',
12969
- b.path || '',
12970
- JSON.stringify(b.value ?? null),
12971
- String(b.negate ?? ''),
12972
- b.description || '',
12973
- ]);
12974
- }
12975
14021
  function compareNormalizedLinks(a, b) {
12976
14022
  return compareValues([
12977
14023
  a.id,
12978
14024
  a.intent,
12979
14025
  endpointSortKey(a.from),
12980
14026
  endpointSortKey(a.to),
14027
+ JSON.stringify(a.condition ?? null),
12981
14028
  ], [
12982
14029
  b.id,
12983
14030
  b.intent,
12984
14031
  endpointSortKey(b.from),
12985
14032
  endpointSortKey(b.to),
14033
+ JSON.stringify(b.condition ?? null),
12986
14034
  ]);
12987
14035
  }
12988
14036
  function endpointSortKey(endpoint) {
@@ -13011,7 +14059,7 @@ function compareValues(left, right) {
13011
14059
  }
13012
14060
  return 0;
13013
14061
  }
13014
- function clone(value) {
14062
+ function clone$1(value) {
13015
14063
  if (value == null || typeof value !== 'object') {
13016
14064
  return value;
13017
14065
  }
@@ -13532,6 +14580,12 @@ function mapFieldDefinitionToMetadata(field) {
13532
14580
  if (field.hiddenCondition !== undefined) {
13533
14581
  metadata.hiddenCondition = field.hiddenCondition;
13534
14582
  }
14583
+ if (field.conditionalDisplay !== undefined) {
14584
+ metadata.conditionalDisplay = field.conditionalDisplay;
14585
+ }
14586
+ if (field.conditionalRequired !== undefined) {
14587
+ metadata.conditionalRequired = field.conditionalRequired;
14588
+ }
13535
14589
  if (field.clearButton !== undefined) {
13536
14590
  metadata.clearButton = field.clearButton;
13537
14591
  }
@@ -13598,15 +14652,12 @@ function mapFieldDefinitionToMetadata(field) {
13598
14652
  validators.customValidator = field.customValidator;
13599
14653
  if (field.asyncValidator !== undefined)
13600
14654
  validators.asyncValidator = field.asyncValidator;
13601
- // Conditional: suportar função direta e/ou regras compostas
14655
+ // Conditional: canonical declarative rules only
13602
14656
  if (field.conditionalValidation !== undefined) {
13603
14657
  const cv = field.conditionalValidation;
13604
14658
  if (Array.isArray(cv)) {
13605
14659
  validators.conditionalValidation = cv;
13606
14660
  }
13607
- else if (typeof cv === 'function') {
13608
- validators.conditionalValidationFn = cv;
13609
- }
13610
14661
  }
13611
14662
  // Arquivos (extensão do contrato)
13612
14663
  if (field.allowedFileTypes !== undefined)
@@ -15449,11 +16500,12 @@ const FIELD_METADATA_CAPABILITIES = {
15449
16500
  version: 'v1.3',
15450
16501
  enums: ENUMS$1,
15451
16502
  notes: [
16503
+ 'Condições booleanas declarativas devem usar Json Logic canônico. Funções permanecem restritas a escapes host-level como transforms e validators customizados.',
15452
16504
  'Este catálogo define as capacidades genéricas de FieldMetadata.',
15453
16505
  'O host típico para estes metadados é FormConfig (via fieldMetadata[]) ou FormLayout.',
15454
16506
  'Detalhes específicos de cada controle (ex: opções de datepicker, máscaras específicas) devem ser consultados nos catálogos de microcomponentes.',
15455
16507
  'POLICY: Arrays e objetos (ex: options, validators) devem sofrer merge/append, nunca substituição completa sem confirmação.',
15456
- 'Funções (conditionalRequired, transforms) não são serializáveis. O LLM não deve gerar código de função, apenas expressões string se suportado pelo interpretador, ou solicitar configuração manual.',
16508
+ 'Não gere função para conditionalDisplay/conditionalRequired. Use Json Logic serializável; funções ficam restritas a transforms e validators customizados.',
15457
16509
  ],
15458
16510
  capabilities: [
15459
16511
  // =============================================================================
@@ -15986,14 +17038,14 @@ const FIELD_METADATA_CAPABILITIES = {
15986
17038
  category: 'validation',
15987
17039
  valueKind: 'array',
15988
17040
  description: 'Regras de validacao condicional.',
15989
- safetyNotes: 'Condicoes com funcao exigem wiring manual.',
17041
+ safetyNotes: 'Use regras declarativas com Json Logic serializavel; nao gere funcoes aqui.',
15990
17042
  },
15991
17043
  {
15992
17044
  path: 'validators.conditionalValidation[].condition',
15993
17045
  category: 'validation',
15994
17046
  valueKind: 'expression',
15995
17047
  description: 'Condicao para aplicar validadores.',
15996
- safetyNotes: 'Funcoes nao sao serializaveis; use expressao quando suportado.',
17048
+ safetyNotes: 'Use Json Logic canônico avaliado sobre o root `form`.',
15997
17049
  },
15998
17050
  {
15999
17051
  path: 'validators.conditionalValidation[].validators',
@@ -16149,16 +17201,16 @@ const FIELD_METADATA_CAPABILITIES = {
16149
17201
  path: 'conditionalRequired',
16150
17202
  category: 'dependency',
16151
17203
  valueKind: 'expression',
16152
- description: 'Condição para obrigatoriedade (função ou string).',
16153
- safetyNotes: 'Funções não são serializáveis. Use string se o parser suportar.',
17204
+ description: 'Condição Json Logic para obrigatoriedade declarativa.',
17205
+ safetyNotes: 'Use Json Logic serializável; não gere função nem DSL textual.',
16154
17206
  intentExamples: ['obrigatório se outro campo for X', 'condicionalmente requerido'],
16155
17207
  },
16156
17208
  {
16157
17209
  path: 'conditionalDisplay',
16158
17210
  category: 'dependency',
16159
17211
  valueKind: 'expression',
16160
- description: 'Condição para visibilidade (função ou string).',
16161
- safetyNotes: 'Funções não são serializáveis.',
17212
+ description: 'Condição Json Logic para visibilidade declarativa.',
17213
+ safetyNotes: 'Use Json Logic serializável; não gere função nem DSL textual.',
16162
17214
  intentExamples: ['mostrar se checkbox marcado', 'visibilidade condicional'],
16163
17215
  },
16164
17216
  {
@@ -16367,8 +17419,8 @@ const CAPS = [
16367
17419
  { path: 'page.composition.links[].to', category: 'connections', valueKind: 'object', description: 'Endpoint de destino do link.' },
16368
17420
  { path: 'page.composition.links[].intent', category: 'connections', valueKind: 'string', description: 'Intencao semantica do link.' },
16369
17421
  { path: 'page.composition.links[].transform', category: 'connections', valueKind: 'object', description: 'Pipeline de transformacao do link.' },
16370
- { path: 'page.composition.links[].conditions', category: 'connections', valueKind: 'array', description: 'Condicoes opcionais de entrega.' },
16371
- { path: 'page.composition.links[].policy', category: 'connections', valueKind: 'object', description: 'Politicas opcionais de entrega.' },
17422
+ { path: 'page.composition.links[].condition', category: 'connections', valueKind: 'expression', description: 'Guarda semantica opcional do link, expressa como um unico AST Json Logic canonico.' },
17423
+ { path: 'page.composition.links[].policy', category: 'connections', valueKind: 'object', description: 'Politicas operacionais opcionais do link, como debounce, distinct e missing-value.' },
16372
17424
  { path: 'page.composition.links[].metadata', category: 'connections', valueKind: 'object', description: 'Metadados opcionais do link.' },
16373
17425
  ];
16374
17426
  const DYNAMIC_PAGE_AI_CAPABILITIES = {
@@ -16378,6 +17430,7 @@ const DYNAMIC_PAGE_AI_CAPABILITIES = {
16378
17430
  notes: [
16379
17431
  'Este catalogo e especifico para o componente praxis-dynamic-page.',
16380
17432
  'Widgets e page.composition.links sao arrays; o adapter faz merge por key estavel.',
17433
+ 'Taxonomia editorial: condition usa Json Logic canonico; transform usa pipeline declarativo; nao trate ambos como a mesma "expression".',
16381
17434
  'Para remocao/replace, use flags {_remove:true} ou {_replace:true} no item.',
16382
17435
  'Para renomear um link, inclua {_beforeKey:"link-id-anterior"} no item.',
16383
17436
  'Inputs de widgets dependem do componente (ex: praxis-table, praxis-dynamic-form). Evite inventar campos; prefira pedir confirmacao.',
@@ -19211,8 +20264,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
19211
20264
  args: [{ providedIn: 'root' }]
19212
20265
  }] });
19213
20266
 
20267
+ const DEFAULT_JSON_LOGIC$2 = new PraxisJsonLogicService(null);
19214
20268
  class WidgetPageStateRuntimeService {
20269
+ jsonLogic;
19215
20270
  connections = new ConnectionManagerService();
20271
+ selectCaseRuleOptions = {
20272
+ availableRoots: ['state', 'context'],
20273
+ defaultRoot: 'state',
20274
+ allowImplicitRoot: true,
20275
+ };
20276
+ derivedValueRuleOptions = {
20277
+ availableRoots: ['state', 'context'],
20278
+ defaultRoot: 'state',
20279
+ allowImplicitRoot: true,
20280
+ };
20281
+ constructor(jsonLogic = DEFAULT_JSON_LOGIC$2) {
20282
+ this.jsonLogic = jsonLogic;
20283
+ }
19216
20284
  normalizeState(state) {
19217
20285
  if (!state)
19218
20286
  return { values: {} };
@@ -19324,8 +20392,8 @@ class WidgetPageStateRuntimeService {
19324
20392
  if (compute.kind === 'template') {
19325
20393
  return this.resolveTemplate(compute.value, env);
19326
20394
  }
19327
- if (compute.kind === 'expr') {
19328
- return this.readPath(env, compute.expression.trim());
20395
+ if (compute.kind === 'json-logic') {
20396
+ return this.evaluateDerivedJsonLogic(compute.expression, env, diagnostics, key);
19329
20397
  }
19330
20398
  if (compute.kind === 'transformer') {
19331
20399
  diagnostics.push(`Derived state '${key}' references transformer '${compute.transformerId}', but transformer runtime is not implemented yet.`);
@@ -19441,7 +20509,7 @@ class WidgetPageStateRuntimeService {
19441
20509
  return Object.values(source).filter((value) => !this.isEmptyValue(value)).length;
19442
20510
  }
19443
20511
  case 'select-case':
19444
- return this.selectCase(options, env);
20512
+ return this.selectCase(options, env, diagnostics, key);
19445
20513
  case 'template':
19446
20514
  return this.resolveTemplate(options['value'], env);
19447
20515
  default:
@@ -19541,13 +20609,31 @@ class WidgetPageStateRuntimeService {
19541
20609
  metric: this.formatFieldValue(total, options['metricFormat'] || 'integer'),
19542
20610
  }));
19543
20611
  }
19544
- selectCase(options, env) {
20612
+ selectCase(options, env, diagnostics, key) {
19545
20613
  const cases = Array.isArray(options['cases']) ? options['cases'] : [];
19546
20614
  for (const item of cases) {
19547
20615
  if (!item || typeof item !== 'object')
19548
20616
  continue;
19549
- if (!this.matchesCase(item['when'], env.state))
20617
+ const condition = item['when'];
20618
+ try {
20619
+ const matched = this.jsonLogic.matches({
20620
+ condition: condition ?? null,
20621
+ data: {
20622
+ state: this.clone(env.state) || {},
20623
+ context: this.clone(env.context) || {},
20624
+ },
20625
+ options: this.selectCaseRuleOptions,
20626
+ });
20627
+ if (!matched)
20628
+ continue;
20629
+ }
20630
+ catch (error) {
20631
+ const detail = error instanceof PraxisJsonLogicError
20632
+ ? error.message
20633
+ : this.describeError(error);
20634
+ diagnostics.push(`Derived state '${key}' uses invalid select-case Json Logic condition: ${detail}`);
19550
20635
  continue;
20636
+ }
19551
20637
  return this.resolveCaseValue(item, env);
19552
20638
  }
19553
20639
  return this.resolveCaseValue({
@@ -19556,42 +20642,20 @@ class WidgetPageStateRuntimeService {
19556
20642
  path: options['defaultPath'],
19557
20643
  }, env);
19558
20644
  }
19559
- matchesCase(condition, state) {
19560
- if (!condition || typeof condition !== 'object')
19561
- return false;
19562
- const path = typeof condition['path'] === 'string' ? condition['path'].trim() : '';
19563
- const value = path ? this.readPath(state, path) : undefined;
19564
- const compareValue = condition['comparePath']
19565
- ? this.readPath(state, String(condition['comparePath']).trim())
19566
- : condition['value'];
19567
- if ('equals' in condition)
19568
- return value === condition['equals'];
19569
- if ('notEquals' in condition)
19570
- return value !== condition['notEquals'];
19571
- if (condition['truthy'] === true)
19572
- return Boolean(value);
19573
- if (condition['falsy'] === true)
19574
- return !value;
19575
- if (condition['notEmpty'] === true)
19576
- return value != null && String(value).trim().length > 0;
19577
- if ('lt' in condition)
19578
- return this.toNumber(value) < this.toNumber(condition['lt']);
19579
- if ('lte' in condition)
19580
- return this.toNumber(value) <= this.toNumber(condition['lte']);
19581
- if ('gt' in condition)
19582
- return this.toNumber(value) > this.toNumber(condition['gt']);
19583
- if ('gte' in condition)
19584
- return this.toNumber(value) >= this.toNumber(condition['gte']);
19585
- if ('comparePath' in condition && 'op' in condition) {
19586
- switch (String(condition['op']).trim()) {
19587
- case 'lt': return this.toNumber(value) < this.toNumber(compareValue);
19588
- case 'lte': return this.toNumber(value) <= this.toNumber(compareValue);
19589
- case 'gt': return this.toNumber(value) > this.toNumber(compareValue);
19590
- case 'gte': return this.toNumber(value) >= this.toNumber(compareValue);
19591
- case 'equals': return value === compareValue;
20645
+ evaluateDerivedJsonLogic(expression, env, diagnostics, key) {
20646
+ try {
20647
+ return this.jsonLogic.evaluate(expression, {
20648
+ state: this.clone(env.state) || {},
20649
+ context: this.clone(env.context) || {},
20650
+ }, this.derivedValueRuleOptions);
20651
+ }
20652
+ catch (error) {
20653
+ if (error instanceof PraxisJsonLogicError) {
20654
+ diagnostics.push(`Derived state '${key}' has invalid JSON Logic: ${error.message}`);
20655
+ return undefined;
19592
20656
  }
20657
+ throw error;
19593
20658
  }
19594
- return false;
19595
20659
  }
19596
20660
  resolveCaseValue(item, env) {
19597
20661
  if ('path' in item && typeof item['path'] === 'string' && item['path'].trim()) {
@@ -19721,13 +20785,13 @@ class WidgetPageStateRuntimeService {
19721
20785
  return error;
19722
20786
  return 'Unknown runtime error';
19723
20787
  }
19724
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
20788
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, deps: [{ token: PraxisJsonLogicService }], target: i0.ɵɵFactoryTarget.Injectable });
19725
20789
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, providedIn: 'root' });
19726
20790
  }
19727
20791
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetPageStateRuntimeService, decorators: [{
19728
20792
  type: Injectable,
19729
20793
  args: [{ providedIn: 'root' }]
19730
- }] });
20794
+ }], ctorParameters: () => [{ type: PraxisJsonLogicService }] });
19731
20795
 
19732
20796
  class CompositionRuntimeStore {
19733
20797
  snapshot;
@@ -20001,12 +21065,20 @@ function isSupportedTransformKind(kind) {
20001
21065
 
20002
21066
  const DEFAULT_PATH_ACCESSOR$1 = new ConnectionManagerService();
20003
21067
  const DEFAULT_TEMPLATE_RESOLVER = new SurfaceBindingRuntimeService();
21068
+ const DEFAULT_JSON_LOGIC$1 = new PraxisJsonLogicService(null);
21069
+ const TRANSFORM_RULE_OPTIONS = {
21070
+ availableRoots: ['event', 'payload', 'state', 'context', 'source'],
21071
+ defaultRoot: 'source',
21072
+ allowImplicitRoot: true,
21073
+ };
20004
21074
  class TransformRuntimeService {
20005
21075
  pathAccessor;
20006
21076
  templateResolver;
20007
- constructor(pathAccessor = DEFAULT_PATH_ACCESSOR$1, templateResolver = DEFAULT_TEMPLATE_RESOLVER) {
21077
+ jsonLogic;
21078
+ constructor(pathAccessor = DEFAULT_PATH_ACCESSOR$1, templateResolver = DEFAULT_TEMPLATE_RESOLVER, jsonLogic = DEFAULT_JSON_LOGIC$1) {
20008
21079
  this.pathAccessor = pathAccessor;
20009
21080
  this.templateResolver = templateResolver;
21081
+ this.jsonLogic = jsonLogic;
20010
21082
  }
20011
21083
  executePipeline(pipeline, runtime) {
20012
21084
  const diagnostics = [];
@@ -20153,25 +21225,7 @@ class TransformRuntimeService {
20153
21225
  if (!condition) {
20154
21226
  return true;
20155
21227
  }
20156
- const sourceValue = condition.source
20157
- ? this.resolveBindingSource(condition.source, env, undefined)
20158
- : env['current'];
20159
- const value = condition.path
20160
- ? this.pathAccessor.extractByPath(sourceValue, condition.path)
20161
- : sourceValue;
20162
- if (condition.truthy === true && !value) {
20163
- return false;
20164
- }
20165
- if (condition.equals !== undefined && value !== condition.equals) {
20166
- return false;
20167
- }
20168
- if (condition.notEquals !== undefined && value === condition.notEquals) {
20169
- return false;
20170
- }
20171
- if (condition.notEmpty === true && this.isEmptyValue(value)) {
20172
- return false;
20173
- }
20174
- return true;
21228
+ return this.matchesJsonLogic(condition, env);
20175
21229
  }
20176
21230
  resolveConstantStep(step, inputs) {
20177
21231
  if (step.input?.value !== undefined) {
@@ -20291,7 +21345,11 @@ class TransformRuntimeService {
20291
21345
  ? step.config?.['cases']
20292
21346
  : [];
20293
21347
  for (const item of cases) {
20294
- if (!this.matchesCase(item['when'], env, source)) {
21348
+ if (!this.matchesJsonLogic(item['when'], {
21349
+ ...env,
21350
+ source,
21351
+ current: source,
21352
+ })) {
20295
21353
  continue;
20296
21354
  }
20297
21355
  return this.resolveCaseValue(item, env, source);
@@ -20302,30 +21360,30 @@ class TransformRuntimeService {
20302
21360
  path: step.config?.['defaultPath'],
20303
21361
  }, env, source);
20304
21362
  }
20305
- matchesCase(condition, env, source) {
20306
- if (!condition || typeof condition !== 'object') {
20307
- return false;
20308
- }
20309
- const conditionRecord = condition;
20310
- const path = typeof conditionRecord['path'] === 'string'
20311
- ? conditionRecord['path'].trim()
20312
- : '';
20313
- const value = path
20314
- ? this.resolveCasePath(path, env, source)
20315
- : source;
20316
- if ('equals' in conditionRecord) {
20317
- return value === conditionRecord['equals'];
20318
- }
20319
- if ('notEquals' in conditionRecord) {
20320
- return value !== conditionRecord['notEquals'];
21363
+ matchesJsonLogic(condition, env) {
21364
+ if (!condition) {
21365
+ return true;
20321
21366
  }
20322
- if (conditionRecord['truthy'] === true) {
20323
- return Boolean(value);
21367
+ const data = {
21368
+ event: this.toJsonLogicValue(this.clone(env['event'])),
21369
+ payload: this.toJsonLogicValue(this.clone(env['payload'])),
21370
+ state: this.toJsonLogicValue(this.clone(env['state']) || {}),
21371
+ context: this.toJsonLogicValue(this.clone(env['context']) || {}),
21372
+ source: this.toJsonLogicValue(this.clone(env['current'])),
21373
+ };
21374
+ try {
21375
+ return this.jsonLogic.matches({
21376
+ condition,
21377
+ data,
21378
+ options: TRANSFORM_RULE_OPTIONS,
21379
+ });
20324
21380
  }
20325
- if (conditionRecord['notEmpty'] === true) {
20326
- return !this.isEmptyValue(value);
21381
+ catch (error) {
21382
+ if (error instanceof PraxisJsonLogicError) {
21383
+ return false;
21384
+ }
21385
+ throw error;
20327
21386
  }
20328
- return false;
20329
21387
  }
20330
21388
  resolveCaseValue(item, env, source) {
20331
21389
  if (typeof item['path'] === 'string' && item['path'].trim()) {
@@ -20459,23 +21517,39 @@ class TransformRuntimeService {
20459
21517
  }
20460
21518
  return hash.toString(36);
20461
21519
  }
21520
+ toJsonLogicValue(value) {
21521
+ if (value === null
21522
+ || typeof value === 'string'
21523
+ || typeof value === 'number'
21524
+ || typeof value === 'boolean') {
21525
+ return value;
21526
+ }
21527
+ if (Array.isArray(value) || (value && typeof value === 'object')) {
21528
+ return value;
21529
+ }
21530
+ return value == null ? null : String(value);
21531
+ }
20462
21532
  }
20463
21533
 
20464
21534
  const DEFAULT_PATH_ACCESSOR = new ConnectionManagerService();
20465
21535
  const DEFAULT_TRANSFORM_RUNNER = new TransformRuntimeService();
21536
+ const DEFAULT_JSON_LOGIC = new PraxisJsonLogicService(null);
21537
+ const COMPOSITION_RULE_ROOTS = ['source', 'event', 'payload', 'state', 'context', 'meta'];
20466
21538
  class LinkExecutorService {
20467
21539
  transforms;
20468
21540
  pathAccessor;
20469
- constructor(transforms = DEFAULT_TRANSFORM_RUNNER, pathAccessor = DEFAULT_PATH_ACCESSOR) {
21541
+ jsonLogic;
21542
+ constructor(transforms = DEFAULT_TRANSFORM_RUNNER, pathAccessor = DEFAULT_PATH_ACCESSOR, jsonLogic = DEFAULT_JSON_LOGIC) {
20470
21543
  this.transforms = transforms;
20471
21544
  this.pathAccessor = pathAccessor;
21545
+ this.jsonLogic = jsonLogic;
20472
21546
  }
20473
21547
  executeLink(link, context) {
20474
21548
  const diagnostics = [];
20475
21549
  const snapshot = this.cloneSnapshot(context.snapshot.state);
20476
21550
  const now = context.now || new Date().toISOString();
20477
21551
  const sourceValue = this.resolveSourceValue(link.from, context, snapshot);
20478
- if (!this.matchesConditions(link, sourceValue, context, snapshot, diagnostics)) {
21552
+ if (!this.matchesCondition(link, sourceValue, context, snapshot, diagnostics)) {
20479
21553
  return {
20480
21554
  status: diagnostics.some((item) => item.severity === 'error' || item.severity === 'fatal')
20481
21555
  ? 'failed'
@@ -20483,7 +21557,7 @@ class LinkExecutorService {
20483
21557
  value: sourceValue,
20484
21558
  diagnostics,
20485
21559
  snapshot,
20486
- skippedReason: diagnostics.length ? 'condition-error' : 'conditions-not-matched',
21560
+ skippedReason: diagnostics.length ? 'condition-error' : 'condition-not-matched',
20487
21561
  };
20488
21562
  }
20489
21563
  const transformResult = link.transform
@@ -20576,113 +21650,52 @@ class LinkExecutorService {
20576
21650
  context: context.context || {},
20577
21651
  }, normalized);
20578
21652
  }
20579
- matchesConditions(link, sourceValue, context, snapshot, diagnostics) {
20580
- for (const condition of link.conditions || []) {
20581
- const result = this.evaluateCondition(condition, sourceValue, context, snapshot, link, diagnostics);
20582
- if (!result) {
20583
- return false;
20584
- }
20585
- }
20586
- return true;
20587
- }
20588
- evaluateCondition(condition, sourceValue, context, snapshot, link, diagnostics) {
20589
- let matched = true;
20590
- switch (condition.kind) {
20591
- case 'always':
20592
- matched = true;
20593
- break;
20594
- case 'present':
20595
- matched = !this.isEmptyValue(this.readConditionValue(condition.path, sourceValue, context, snapshot));
20596
- break;
20597
- case 'equals':
20598
- matched = this.readConditionValue(condition.path, sourceValue, context, snapshot) === condition.value;
20599
- break;
20600
- case 'changed':
20601
- matched = !this.isEqual(sourceValue, context.lastDeliveredValue);
20602
- break;
20603
- case 'matches-port-kind':
20604
- matched = this.matchesPortKind(link, condition.value);
20605
- break;
20606
- case 'expression':
20607
- matched = this.evaluateExpression(condition.expression, sourceValue, context, snapshot, link, diagnostics);
20608
- break;
20609
- default:
20610
- matched = true;
20611
- break;
20612
- }
20613
- return condition.negate ? !matched : matched;
20614
- }
20615
- evaluateExpression(expression, sourceValue, context, snapshot, link, diagnostics) {
20616
- const normalized = String(expression || '').trim();
20617
- if (!normalized) {
21653
+ matchesCondition(link, sourceValue, context, snapshot, diagnostics) {
21654
+ if (!link.condition) {
20618
21655
  return true;
20619
21656
  }
20620
- const binary = normalized.match(/^(.*?)\s*(===|!==|==|!=)\s*(.*?)$/);
20621
- if (binary) {
20622
- const left = this.resolveExpressionOperand(binary[1], sourceValue, context, snapshot);
20623
- const right = this.resolveExpressionOperand(binary[3], sourceValue, context, snapshot);
20624
- switch (binary[2]) {
20625
- case '===':
20626
- case '==':
20627
- return left === right;
20628
- case '!==':
20629
- case '!=':
20630
- return left !== right;
20631
- }
20632
- }
20633
- if (/^(payload|event|state|context|source)\b/.test(normalized)) {
20634
- return Boolean(this.resolveExpressionOperand(normalized, sourceValue, context, snapshot));
20635
- }
20636
- diagnostics.push(this.createDiagnostic(link, 'RUNTIME_CONDITION_EXPRESSION_UNSUPPORTED', 'warning', `A expressao '${normalized}' nao e suportada pelo runtime inicial e foi tratada como falsa.`, {
20637
- expression: normalized,
20638
- }));
20639
- return false;
20640
- }
20641
- resolveExpressionOperand(token, sourceValue, context, snapshot) {
20642
- const normalized = token.trim();
20643
- if (normalized === 'null') {
20644
- return null;
20645
- }
20646
- if (normalized === 'undefined') {
20647
- return undefined;
20648
- }
20649
- if (normalized === 'true') {
20650
- return true;
21657
+ try {
21658
+ return this.jsonLogic.matches({
21659
+ condition: link.condition,
21660
+ data: this.buildConditionData(sourceValue, context, snapshot),
21661
+ options: {
21662
+ availableRoots: [...COMPOSITION_RULE_ROOTS],
21663
+ defaultRoot: null,
21664
+ allowImplicitRoot: false,
21665
+ nowUtc: context.now || new Date().toISOString(),
21666
+ },
21667
+ });
20651
21668
  }
20652
- if (normalized === 'false') {
21669
+ catch (error) {
21670
+ const details = {
21671
+ condition: link.condition,
21672
+ };
21673
+ if (error instanceof PraxisJsonLogicError) {
21674
+ details['jsonLogicCode'] = error.code;
21675
+ }
21676
+ else if (error instanceof Error) {
21677
+ details['error'] = error.message;
21678
+ }
21679
+ diagnostics.push(this.createDiagnostic(link, 'RUNTIME_LINK_CONDITION_INVALID', 'warning', 'A condicao Json Logic do link falhou em runtime e foi tratada como falsa.', details));
20653
21680
  return false;
20654
21681
  }
20655
- if (/^-?\d+(\.\d+)?$/.test(normalized)) {
20656
- return Number(normalized);
20657
- }
20658
- if ((normalized.startsWith('"') && normalized.endsWith('"'))
20659
- || (normalized.startsWith('\'') && normalized.endsWith('\''))) {
20660
- return normalized.slice(1, -1);
20661
- }
20662
- return this.pathAccessor.extractByPath({
20663
- source: sourceValue,
20664
- event: context.event,
20665
- payload: context.payload !== undefined
21682
+ }
21683
+ buildConditionData(sourceValue, context, snapshot) {
21684
+ return {
21685
+ source: this.clone(sourceValue),
21686
+ event: this.clone(context.event),
21687
+ payload: this.clone(context.payload !== undefined
20666
21688
  ? context.payload
20667
21689
  : (context.event && typeof context.event === 'object'
20668
21690
  ? context.event['payload']
20669
- : undefined),
20670
- state: this.buildEffectiveState(snapshot),
20671
- context: context.context || {},
20672
- }, normalized);
20673
- }
20674
- readConditionValue(path, sourceValue, context, snapshot) {
20675
- if (!path) {
20676
- return sourceValue;
20677
- }
20678
- return this.resolveExpressionOperand(path, sourceValue, context, snapshot);
20679
- }
20680
- matchesPortKind(link, value) {
20681
- const expected = String(value || '').trim();
20682
- if (!expected || link.to.kind !== 'component-port') {
20683
- return false;
20684
- }
20685
- return link.to.ref.port === expected;
21691
+ : undefined)),
21692
+ state: this.clone(this.buildEffectiveState(snapshot)),
21693
+ context: this.clone(context.context || {}),
21694
+ meta: {
21695
+ now: context.now || new Date().toISOString(),
21696
+ lastDeliveredAt: context.lastDeliveredAt ?? null,
21697
+ },
21698
+ };
20686
21699
  }
20687
21700
  buildTransformContext(context, snapshot, sourceValue) {
20688
21701
  const eventPayload = context.payload !== undefined
@@ -21606,6 +22619,202 @@ class CompositionRuntimeFacade {
21606
22619
  }
21607
22620
  }
21608
22621
 
22622
+ function migrateLegacyCompositionLinks(links) {
22623
+ return (links || []).map((link) => migrateLegacyCompositionLink(link));
22624
+ }
22625
+ function migrateLegacyCompositionLink(link) {
22626
+ const cloned = clone(link);
22627
+ const { condition: rawCondition, conditions, meta, policy: currentPolicy, metadata: currentMetadata, ...rest } = cloned;
22628
+ const resolvedCondition = resolveCanonicalCondition(rest.id, rawCondition, conditions, meta?.filterExpr);
22629
+ const resolvedPolicy = mergeLegacyPolicy(currentPolicy, meta);
22630
+ const usedLegacyCondition = rawCondition === undefined && (!!conditions?.length || !!meta?.filterExpr)
22631
+ || typeof rawCondition === 'string';
22632
+ const usedLegacyPolicy = !!meta && (meta.debounceMs !== undefined
22633
+ || meta.distinct !== undefined
22634
+ || meta.distinctBy !== undefined);
22635
+ return {
22636
+ ...rest,
22637
+ ...(resolvedCondition !== undefined ? { condition: resolvedCondition } : {}),
22638
+ ...(resolvedPolicy ? { policy: resolvedPolicy } : {}),
22639
+ ...(mergeLegacyMetadata(currentMetadata, usedLegacyCondition || usedLegacyPolicy)
22640
+ ? { metadata: mergeLegacyMetadata(currentMetadata, usedLegacyCondition || usedLegacyPolicy) }
22641
+ : {}),
22642
+ };
22643
+ }
22644
+ function resolveCanonicalCondition(linkId, rawCondition, legacyConditions, legacyFilterExpr) {
22645
+ if (rawCondition === null) {
22646
+ return null;
22647
+ }
22648
+ if (rawCondition !== undefined && typeof rawCondition !== 'string') {
22649
+ return rawCondition;
22650
+ }
22651
+ const migrated = [];
22652
+ if (typeof rawCondition === 'string' && rawCondition.trim()) {
22653
+ migrated.push(parseLegacyExpression(rawCondition.trim(), linkId, 'condition'));
22654
+ }
22655
+ for (const condition of legacyConditions || []) {
22656
+ const migratedCondition = migrateLegacyCondition(linkId, condition);
22657
+ if (migratedCondition === true) {
22658
+ continue;
22659
+ }
22660
+ migrated.push(migratedCondition);
22661
+ }
22662
+ if (legacyFilterExpr?.trim()) {
22663
+ migrated.push(parseLegacyExpression(legacyFilterExpr.trim(), linkId, 'meta.filterExpr'));
22664
+ }
22665
+ if (!migrated.length) {
22666
+ return undefined;
22667
+ }
22668
+ if (migrated.length === 1) {
22669
+ return migrated[0];
22670
+ }
22671
+ return { and: migrated };
22672
+ }
22673
+ function migrateLegacyCondition(linkId, condition) {
22674
+ let migrated;
22675
+ switch (condition.kind) {
22676
+ case 'always':
22677
+ migrated = true;
22678
+ break;
22679
+ case 'expression':
22680
+ migrated = parseLegacyExpression(requireString(condition.expression, linkId, 'condition.expression'), linkId, 'conditions[].expression');
22681
+ break;
22682
+ case 'equals':
22683
+ migrated = {
22684
+ '===': [
22685
+ buildVarReference(condition.path || 'source'),
22686
+ toJsonLogicValue(condition.value),
22687
+ ],
22688
+ };
22689
+ break;
22690
+ case 'present':
22691
+ migrated = {
22692
+ '!': [
22693
+ {
22694
+ isBlank: [buildVarReference(condition.path || 'source')],
22695
+ },
22696
+ ],
22697
+ };
22698
+ break;
22699
+ case 'changed':
22700
+ case 'matches-port-kind':
22701
+ throw new Error(`CompositionLink '${linkId}' uses legacy condition kind '${condition.kind}', which no longer has a canonical condition equivalent. Move this behavior into policy or endpoint modeling before loading the page.`);
22702
+ default:
22703
+ throw new Error(`CompositionLink '${linkId}' contains unsupported legacy condition kind.`);
22704
+ }
22705
+ if (!condition.negate || migrated === true) {
22706
+ return condition.negate && migrated === true ? false : migrated;
22707
+ }
22708
+ return { '!': [migrated] };
22709
+ }
22710
+ function parseLegacyExpression(expression, linkId, sourceField) {
22711
+ const trimmed = expression.trim();
22712
+ const comparisonMatch = trimmed.match(/^(.*?)\s*(===|!==|==|!=|>=|<=|>|<)\s*(.*?)$/);
22713
+ if (comparisonMatch) {
22714
+ const [, leftRaw, operator, rightRaw] = comparisonMatch;
22715
+ const left = parseLegacyOperand(leftRaw.trim(), linkId, sourceField);
22716
+ const right = parseLegacyOperand(rightRaw.trim(), linkId, sourceField);
22717
+ return { [operator]: [left, right] };
22718
+ }
22719
+ const negatedPath = trimmed.match(/^!\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[\d+\])*)$/);
22720
+ if (negatedPath) {
22721
+ return { '!': [buildVarReference(negatedPath[1])] };
22722
+ }
22723
+ if (looksLikePath(trimmed)) {
22724
+ return { '!!': [buildVarReference(trimmed)] };
22725
+ }
22726
+ throw new Error(`CompositionLink '${linkId}' contains unsupported legacy expression in ${sourceField}: "${expression}". Only simple comparisons and root-qualified truthiness checks can be migrated automatically.`);
22727
+ }
22728
+ function parseLegacyOperand(raw, linkId, sourceField) {
22729
+ if (raw === 'null') {
22730
+ return null;
22731
+ }
22732
+ if (raw === 'true') {
22733
+ return true;
22734
+ }
22735
+ if (raw === 'false') {
22736
+ return false;
22737
+ }
22738
+ if (/^-?\d+(?:\.\d+)?$/.test(raw)) {
22739
+ return Number(raw);
22740
+ }
22741
+ if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith('\'') && raw.endsWith('\''))) {
22742
+ return parseQuotedLiteral(raw, linkId, sourceField);
22743
+ }
22744
+ if (raw.startsWith('{') || raw.startsWith('[')) {
22745
+ try {
22746
+ return JSON.parse(raw);
22747
+ }
22748
+ catch {
22749
+ throw new Error(`CompositionLink '${linkId}' contains invalid JSON literal in ${sourceField}: "${raw}".`);
22750
+ }
22751
+ }
22752
+ if (looksLikePath(raw)) {
22753
+ return buildVarReference(raw);
22754
+ }
22755
+ throw new Error(`CompositionLink '${linkId}' contains unsupported legacy operand "${raw}" in ${sourceField}.`);
22756
+ }
22757
+ function parseQuotedLiteral(raw, linkId, sourceField) {
22758
+ try {
22759
+ if (raw.startsWith('\'')) {
22760
+ return JSON.parse(`"${raw.slice(1, -1).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`);
22761
+ }
22762
+ return JSON.parse(raw);
22763
+ }
22764
+ catch {
22765
+ throw new Error(`CompositionLink '${linkId}' contains invalid quoted literal in ${sourceField}: "${raw}".`);
22766
+ }
22767
+ }
22768
+ function buildVarReference(path) {
22769
+ return { var: path };
22770
+ }
22771
+ function mergeLegacyPolicy(policy, legacyMeta) {
22772
+ const merged = {
22773
+ ...(policy || {}),
22774
+ ...(legacyMeta?.debounceMs !== undefined ? { debounceMs: legacyMeta.debounceMs } : {}),
22775
+ ...(legacyMeta?.distinct !== undefined ? { distinct: legacyMeta.distinct } : {}),
22776
+ ...(legacyMeta?.distinctBy ? { distinctBy: legacyMeta.distinctBy } : {}),
22777
+ };
22778
+ return Object.keys(merged).length ? merged : undefined;
22779
+ }
22780
+ function mergeLegacyMetadata(metadata, markLegacySource) {
22781
+ if (!metadata && !markLegacySource) {
22782
+ return undefined;
22783
+ }
22784
+ const merged = {
22785
+ ...(metadata || {}),
22786
+ ...(!metadata?.source && markLegacySource ? { source: 'legacy-widget-connection' } : {}),
22787
+ };
22788
+ return Object.keys(merged).length ? merged : undefined;
22789
+ }
22790
+ function requireString(value, linkId, field) {
22791
+ if (!value?.trim()) {
22792
+ throw new Error(`CompositionLink '${linkId}' is missing legacy field '${field}' required for migration.`);
22793
+ }
22794
+ return value.trim();
22795
+ }
22796
+ function looksLikePath(value) {
22797
+ return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\[\d+\])*$/.test(value);
22798
+ }
22799
+ function toJsonLogicValue(value) {
22800
+ if (value === null
22801
+ || typeof value === 'string'
22802
+ || typeof value === 'number'
22803
+ || typeof value === 'boolean') {
22804
+ return value;
22805
+ }
22806
+ if (Array.isArray(value) || (value && typeof value === 'object')) {
22807
+ return clone(value);
22808
+ }
22809
+ return value == null ? null : String(value);
22810
+ }
22811
+ function clone(value) {
22812
+ if (value == null || typeof value !== 'object') {
22813
+ return value;
22814
+ }
22815
+ return JSON.parse(JSON.stringify(value));
22816
+ }
22817
+
21609
22818
  const DYNAMIC_WIDGET_PAGE_I18N_NAMESPACE = 'dynamicWidgetPage';
21610
22819
  const DYNAMIC_WIDGET_PAGE_I18N_CONFIG = {
21611
22820
  namespaces: {
@@ -21671,7 +22880,38 @@ class WidgetPageCompositionFactory {
21671
22880
  };
21672
22881
  }
21673
22882
  readCanonicalLinks(page) {
21674
- return this.clone(page.composition?.links) || [];
22883
+ const links = this.clone(page.composition?.links) || [];
22884
+ this.assertCanonicalLinks(links);
22885
+ return links;
22886
+ }
22887
+ assertCanonicalLinks(links) {
22888
+ for (const link of links) {
22889
+ const rawLink = link;
22890
+ const id = String(rawLink['id'] ?? 'unknown-link');
22891
+ const condition = rawLink['condition'];
22892
+ const conditions = rawLink['conditions'];
22893
+ const meta = rawLink['meta'];
22894
+ if (typeof condition === 'string') {
22895
+ throw new Error(`WidgetPageCompositionFactory no longer accepts string condition in composition.links for '${id}'. Use canonical Json Logic in condition.`);
22896
+ }
22897
+ if (conditions !== undefined) {
22898
+ throw new Error(`WidgetPageCompositionFactory no longer accepts legacy conditions[] in composition.links for '${id}'. Use canonical Json Logic in condition.`);
22899
+ }
22900
+ if (meta && typeof meta === 'object' && !Array.isArray(meta) && 'filterExpr' in meta) {
22901
+ throw new Error(`WidgetPageCompositionFactory no longer accepts meta.filterExpr in composition.links for '${id}'. Use canonical Json Logic in condition.`);
22902
+ }
22903
+ if (condition !== undefined
22904
+ && condition !== null
22905
+ && (typeof condition !== 'object' || Array.isArray(condition))) {
22906
+ throw new Error(`WidgetPageCompositionFactory requires composition.links[].condition to be canonical Json Logic or null for '${id}'.`);
22907
+ }
22908
+ if (condition && !this.isJsonLogicLike(condition)) {
22909
+ throw new Error(`WidgetPageCompositionFactory requires composition.links[].condition to be a canonical Json Logic expression for '${id}'.`);
22910
+ }
22911
+ }
22912
+ }
22913
+ isJsonLogicLike(condition) {
22914
+ return Object.keys(condition).length > 0;
21675
22915
  }
21676
22916
  indexWidgets(widgets) {
21677
22917
  const widgetsByKey = {};
@@ -25335,4 +26575,4 @@ function provideHookWhitelist(allowed) {
25335
26575
  * Generated bundle index. Do not edit.
25336
26576
  */
25337
26577
 
25338
- export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, AnalyticsPresentationResolver, AnalyticsSchemaContractService, AnalyticsStatsRequestBuilderService, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentKeyService, ComponentMetadataRegistry, CompositionRuntimeFacade, ConsoleLoggerSink, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_TABLE_CONFIG, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DynamicFormService, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG$1 as GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_CATALOG as GLOBAL_ACTION_SPEC_CATALOG, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_LAYER_SCALE_DEFAULTS, PRAXIS_LAYER_SCALE_VARS, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisI18nService, PraxisIconDirective, PraxisIconPickerComponent, PraxisLayerScaleStyleService, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRichTextBlockComponent, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, RULE_PROPERTY_SCHEMA, RemoteConfigStorage, ResourceActionOpenAdapterService, ResourceDiscoveryService, ResourceQuickConnectComponent, ResourceSurfaceOpenAdapterService, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_DRAWER_BRIDGE, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceOpenActionEditorComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildPraxisLayerScaleCss, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, clampRange, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getGlobalActionCatalog, getGlobalActionUiSchema, getReferencedFieldMetadata, getTextTransformer, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isEditorialComponentMeta, isInlineFilterControlType, isRangeValidForFilter, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, minWordsValidator, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, normalizePraxisDataQueryContext, normalizeResourceAvailabilityReasonCode, normalizeStart, normalizeUnknownError, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisDynamicPageMetadata, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRichTextBlockMetadata, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveBuiltinPresets, resolveControlTypeAlias, resolveDefaultValuePresentationFormat, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisFilterCriteria, resolveResourceAvailabilityReasonKey, resolveSpan, resolveValuePresentation, resolveValuePresentationLocale, slugify, stripMasksHook, supportsImplicitValuePresentation, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, translateResourceAvailabilityReason, translateResourceDiscoveryText, translateUnavailableWorkflowMessage, trim, uniqueAsyncValidator, urlValidator, withMessage, withPraxisHttpLoading };
26578
+ export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, AnalyticsPresentationResolver, AnalyticsSchemaContractService, AnalyticsStatsRequestBuilderService, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentKeyService, ComponentMetadataRegistry, CompositionRuntimeFacade, ConsoleLoggerSink, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_JSON_LOGIC_OPERATORS, DEFAULT_TABLE_CONFIG, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DynamicFormService, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG$1 as GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_CATALOG as GLOBAL_ACTION_SPEC_CATALOG, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_JSON_LOGIC_OPERATORS, PRAXIS_LAYER_SCALE_DEFAULTS, PRAXIS_LAYER_SCALE_VARS, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisI18nService, PraxisIconDirective, PraxisIconPickerComponent, PraxisJsonLogicError, PraxisJsonLogicService, PraxisLayerScaleStyleService, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRichTextBlockComponent, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, RULE_PROPERTY_SCHEMA, RemoteConfigStorage, ResourceActionOpenAdapterService, ResourceDiscoveryService, ResourceQuickConnectComponent, ResourceSurfaceOpenAdapterService, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_DRAWER_BRIDGE, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceOpenActionEditorComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildPraxisLayerScaleCss, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, clampRange, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getGlobalActionCatalog, getGlobalActionUiSchema, getReferencedFieldMetadata, getTextTransformer, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isEditorialComponentMeta, isInlineFilterControlType, isRangeValidForFilter, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, migrateLegacyCompositionLink, migrateLegacyCompositionLinks, minWordsValidator, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, normalizePraxisDataQueryContext, normalizeResourceAvailabilityReasonCode, normalizeStart, normalizeUnknownError, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisDynamicPageMetadata, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisJsonLogicOperator, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRichTextBlockMetadata, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveBuiltinPresets, resolveControlTypeAlias, resolveDefaultValuePresentationFormat, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisFilterCriteria, resolveResourceAvailabilityReasonKey, resolveSpan, resolveValuePresentation, resolveValuePresentationLocale, slugify, stripMasksHook, supportsImplicitValuePresentation, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, translateResourceAvailabilityReason, translateResourceDiscoveryText, translateUnavailableWorkflowMessage, trim, uniqueAsyncValidator, urlValidator, withMessage, withPraxisHttpLoading };