@praxisui/core 4.0.0-beta.0 → 5.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.
- package/README.md +1 -0
- package/fesm2022/praxisui-core.mjs +1589 -349
- package/index.d.ts +2247 -2112
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
907
|
+
// Conditional validation rules
|
|
897
908
|
if (ui.conditionalValidation !== undefined) {
|
|
898
|
-
const cond = this.
|
|
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
|
-
|
|
1090
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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$
|
|
1843
|
+
return clone$2(left);
|
|
1826
1844
|
if (Array.isArray(left)) {
|
|
1827
|
-
return (Array.isArray(right) ? right : clone$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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 (
|
|
5594
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
6032
|
-
if (
|
|
6033
|
-
|
|
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
|
|
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
|
|
12847
|
-
if (
|
|
12848
|
-
normalized.
|
|
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
|
|
12911
|
-
if (
|
|
13979
|
+
function normalizeCondition(condition) {
|
|
13980
|
+
if (condition === undefined) {
|
|
12912
13981
|
return undefined;
|
|
12913
13982
|
}
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
|
|
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:
|
|
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
|
-
'
|
|
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: '
|
|
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: '
|
|
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
|
|
16153
|
-
safetyNotes: '
|
|
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
|
|
16161
|
-
safetyNotes: '
|
|
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[].
|
|
16371
|
-
{ path: 'page.composition.links[].policy', category: 'connections', valueKind: 'object', description: 'Politicas opcionais
|
|
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 === '
|
|
19328
|
-
return this.
|
|
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
|
-
|
|
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
|
-
|
|
19560
|
-
|
|
19561
|
-
return
|
|
19562
|
-
|
|
19563
|
-
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
|
|
19568
|
-
|
|
19569
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
20306
|
-
if (!condition
|
|
20307
|
-
return
|
|
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
|
-
|
|
20323
|
-
|
|
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
|
-
|
|
20326
|
-
|
|
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
|
-
|
|
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.
|
|
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' : '
|
|
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
|
-
|
|
20580
|
-
|
|
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
|
-
|
|
20621
|
-
|
|
20622
|
-
|
|
20623
|
-
|
|
20624
|
-
|
|
20625
|
-
|
|
20626
|
-
|
|
20627
|
-
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
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
|
-
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
|
|
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
|
-
|
|
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 };
|