@siliconst/metaui 1.4.37 → 1.4.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siliconst/metaui",
3
- "version": "1.4.37",
3
+ "version": "1.4.44",
4
4
  "description": "Dynamic Metadata UI5 Engine",
5
5
  "author": "SiliconStreetDev1",
6
6
  "license": "MIT",
@@ -8,8 +8,6 @@ sap.ui.define(["./PluginRegistry", "../utils/Logger"], function (___PluginRegist
8
8
  * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost
9
9
  * then applies to the individual active plugins.
10
10
  *
11
- * It ensures the orchestration of conditional rendering, requiring, and invalidating of fields without the plugins
12
- * needing to be aware of each other.
13
11
  *
14
12
  * @namespace nz.co.siliconst.ui5.metaui.core
15
13
  */
@@ -23,59 +21,6 @@ sap.ui.define(["./PluginRegistry", "../utils/Logger"], function (___PluginRegist
23
21
  constructor(policies) {
24
22
  this.policies = policies || [];
25
23
  this.lastStateMatrix = new Map();
26
- this.detectCycles();
27
- }
28
-
29
- /**
30
- * Builds a directed dependency graph and detects cycles to prevent infinite event loops.
31
- */
32
- detectCycles() {
33
- const graph = new Map();
34
- for (const policy of this.policies) {
35
- const sources = new Set();
36
- if (policy.condition) {
37
- const addPaths = obj => {
38
- if (obj) Object.keys(obj).forEach(k => sources.add(k.split("/$row")[0]));
39
- };
40
- addPaths(policy.condition.NumericGreaterThan);
41
- addPaths(policy.condition.NumericLessThan);
42
- addPaths(policy.condition.StringEquals);
43
- if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => sources.add(p.split("/$row")[0]));
44
- if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => sources.add(p.split("/$row")[0]));
45
- }
46
- for (const target of policy.targets) {
47
- const cleanTarget = target.split("/$row")[0];
48
- if (!graph.has(cleanTarget)) {
49
- graph.set(cleanTarget, new Set());
50
- }
51
- sources.forEach(s => graph.get(cleanTarget).add(s));
52
- }
53
- }
54
- const visited = new Set();
55
- const recursionStack = new Set();
56
- const isCyclic = node => {
57
- if (!visited.has(node)) {
58
- visited.add(node);
59
- recursionStack.add(node);
60
- const neighbors = graph.get(node);
61
- if (neighbors) {
62
- for (const neighbor of neighbors) {
63
- if (!visited.has(neighbor) && isCyclic(neighbor)) {
64
- return true;
65
- } else if (recursionStack.has(neighbor)) {
66
- return true;
67
- }
68
- }
69
- }
70
- }
71
- recursionStack.delete(node);
72
- return false;
73
- };
74
- for (const node of graph.keys()) {
75
- if (isCyclic(node)) {
76
- throw new Error(`[MetaUI PolicyEngine] FATAL: Cyclic dependency detected involving policy target '${node}'. This will cause an infinite rendering loop.`);
77
- }
78
- }
79
24
  }
80
25
 
81
26
  /**
@@ -126,53 +71,27 @@ sap.ui.define(["./PluginRegistry", "../utils/Logger"], function (___PluginRegist
126
71
 
127
72
  /**
128
73
  * Expands relative $row policies into absolute index policies for each physical row.
74
+ * Uses generic stringification to decouple from specific condition operator plugins.
129
75
  */
130
76
  expandRowPolicies(data, policies) {
131
77
  const expanded = [];
132
78
  for (const policy of policies) {
133
- let hasRow = false;
134
- let arrayPath = "";
135
- for (const target of policy.targets) {
136
- if (target.includes("$row")) {
137
- hasRow = true;
138
- arrayPath = target.split("/$row")[0];
139
- break;
140
- }
141
- }
142
- if (!hasRow && policy.condition) {
143
- const checkPaths = obj => {
144
- if (!obj) return;
145
- for (const key of Object.keys(obj)) {
146
- if (key.includes("$row")) {
147
- hasRow = true;
148
- arrayPath = key.split("/$row")[0];
149
- break;
79
+ const policyStr = JSON.stringify(policy);
80
+ if (policyStr.includes("$row")) {
81
+ // Extract the array path (everything before "/$row" inside a JSON string value)
82
+ const match = policyStr.match(/"([^"]+)\/\$row/);
83
+ if (match) {
84
+ const arrayPath = match[1];
85
+ const arrayData = this.resolvePointer(data, arrayPath);
86
+ if (Array.isArray(arrayData)) {
87
+ for (let i = 0; i < arrayData.length; i++) {
88
+ const cloneStr = policyStr.replace(/\$row/g, String(i));
89
+ expanded.push(JSON.parse(cloneStr));
150
90
  }
151
91
  }
152
- };
153
- checkPaths(policy.condition.NumericGreaterThan);
154
- checkPaths(policy.condition.NumericLessThan);
155
- checkPaths(policy.condition.StringEquals);
156
- if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => {
157
- if (p.includes("$row")) {
158
- hasRow = true;
159
- arrayPath = p.split("/$row")[0];
160
- }
161
- });
162
- if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => {
163
- if (p.includes("$row")) {
164
- hasRow = true;
165
- arrayPath = p.split("/$row")[0];
166
- }
167
- });
168
- }
169
- if (hasRow && arrayPath) {
170
- const arrayData = this.resolvePointer(data, arrayPath);
171
- if (Array.isArray(arrayData)) {
172
- for (let i = 0; i < arrayData.length; i++) {
173
- const cloneStr = JSON.stringify(policy).replace(/\$row/g, String(i));
174
- expanded.push(JSON.parse(cloneStr));
175
- }
92
+ } else {
93
+ // Fallback if path couldn't be parsed
94
+ expanded.push(policy);
176
95
  }
177
96
  } else {
178
97
  expanded.push(policy);
@@ -1 +1 @@
1
- {"version":3,"file":"PolicyEngine-dbg.js","names":["PluginRegistry","___PluginRegistry","Logger","___utils_Logger","PolicyEngine","constructor","policies","lastStateMatrix","Map","detectCycles","graph","policy","sources","Set","condition","addPaths","obj","Object","keys","forEach","k","add","split","NumericGreaterThan","NumericLessThan","StringEquals","IsNull","p","IsNotNull","target","targets","cleanTarget","has","set","s","get","visited","recursionStack","isCyclic","node","neighbors","neighbor","delete","Error","resolvePointer","data","pointer","undefined","cleanPointer","replace","parts","current","part","evaluateCondition","result","registry","getInstance","key","payload","entries","plugin","getPolicyConditionPlugin","evaluate","bind","warn","expandRowPolicies","expanded","hasRow","arrayPath","includes","checkPaths","arrayData","Array","isArray","i","length","cloneStr","JSON","stringify","String","push","parse","expandedPolicies","ledger","initTargetInLedger","visibility","forcedFalse","forcedTrue","validity","required","editable","isConditionMet","currentLedger","effectPlugin","getPolicyEffectPlugin","effect","error","mapped","resolveState","value","property","message","lastMessage","newStateMatrix","stateMap","errorMessage","deltas","newState","lastState","__exports","__esModule"],"sources":["PolicyEngine.ts"],"sourcesContent":["import { IPolicy, IPolicyCondition, PolicyEffectType } from \"../interfaces/ISchema\";\nimport { PluginRegistry } from \"./PluginRegistry\";\nimport { Logger } from \"../utils/Logger\";\n\nexport type PropertyName = \"visibility\" | \"validity\" | \"required\" | \"editable\";\n\nexport interface IDeltaState {\n /** The absolute JSON pointer path of the target property (e.g., \"/VisitorInfo/PassportNumber\") */\n target: string;\n /** The UI5 visual or structural state to mutate */\n property: PropertyName;\n /** The boolean value to apply to the state */\n value: boolean;\n /** An optional custom error message to paint onto the field if validity is false */\n errorMessage?: string;\n}\n\ninterface ITargetState {\n visibility?: boolean;\n validity?: boolean;\n required?: boolean;\n editable?: boolean;\n errorMessage?: string;\n}\n\n/**\n * The PolicyEngine is responsible for evaluating complex, cross-field conditional logic defined in the schema's `uiPolicies` array.\n * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost\n * then applies to the individual active plugins.\n * \n * It ensures the orchestration of conditional rendering, requiring, and invalidating of fields without the plugins\n * needing to be aware of each other.\n * \n * @namespace nz.co.siliconst.ui5.metaui.core\n */\nexport class PolicyEngine {\n private policies: IPolicy[];\n \n /** Caches the last computed state matrix for differential delta emission */\n private lastStateMatrix: Map<string, ITargetState>;\n\n /**\n * Instantiates the PolicyEngine with an array of policies.\n * @param policies Array of uiPolicies defined in the root schema.\n */\n constructor(policies: IPolicy[]) {\n this.policies = policies || [];\n this.lastStateMatrix = new Map();\n this.detectCycles();\n }\n\n /**\n * Builds a directed dependency graph and detects cycles to prevent infinite event loops.\n */\n private detectCycles(): void {\n const graph = new Map<string, Set<string>>();\n\n for (const policy of this.policies) {\n const sources = new Set<string>();\n if (policy.condition) {\n const addPaths = (obj: Record<string, unknown> | undefined) => {\n if (obj) Object.keys(obj).forEach(k => sources.add(k.split(\"/$row\")[0]));\n };\n addPaths(policy.condition.NumericGreaterThan);\n addPaths(policy.condition.NumericLessThan);\n addPaths(policy.condition.StringEquals);\n if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => sources.add(p.split(\"/$row\")[0]));\n if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => sources.add(p.split(\"/$row\")[0]));\n }\n\n for (const target of policy.targets) {\n const cleanTarget = target.split(\"/$row\")[0];\n if (!graph.has(cleanTarget)) {\n graph.set(cleanTarget, new Set());\n }\n sources.forEach(s => graph.get(cleanTarget)!.add(s));\n }\n }\n\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const isCyclic = (node: string): boolean => {\n if (!visited.has(node)) {\n visited.add(node);\n recursionStack.add(node);\n\n const neighbors = graph.get(node);\n if (neighbors) {\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor) && isCyclic(neighbor)) {\n return true;\n } else if (recursionStack.has(neighbor)) {\n return true;\n }\n }\n }\n }\n recursionStack.delete(node);\n return false;\n };\n\n for (const node of graph.keys()) {\n if (isCyclic(node)) {\n throw new Error(`[MetaUI PolicyEngine] FATAL: Cyclic dependency detected involving policy target '${node}'. This will cause an infinite rendering loop.`);\n }\n }\n }\n\n /**\n * Safely traverses a JSON payload using a JSON pointer.\n * Handles UI5 model prefixes (like metaui>) seamlessly.\n * \n * @param data The root JSON object representing the form payload.\n * @param pointer The JSON pointer to traverse (e.g., \"/Customer/Age\").\n * @returns The resolved value, or undefined if the path does not exist.\n */\n private resolvePointer(data: unknown, pointer: string): unknown {\n if (!data || !pointer) return undefined;\n // Strip leading slash and any UI5 prefixes\n const cleanPointer = pointer.replace(/^\\//, \"\").replace(/^metaui>\\/?/, \"\");\n if (!cleanPointer) return data;\n \n const parts = cleanPointer.split(\"/\");\n let current = data as Record<string, unknown>;\n for (const part of parts) {\n if (current === undefined || current === null) return undefined;\n current = current[part] as Record<string, unknown>;\n }\n return current;\n }\n\n /**\n * Evaluates a complex condition tree against the live JSON payload.\n * \n * @param condition The condition block to evaluate.\n * @param data The live JSON payload from the StateManager.\n * @returns True if all condition parameters are met, otherwise false.\n */\n private evaluateCondition(condition: IPolicyCondition, data: unknown): boolean {\n // If there are multiple operators, we treat them as AND for now\n let result = true;\n const registry = PluginRegistry.getInstance();\n\n for (const [key, payload] of Object.entries(condition)) {\n const plugin = registry.getPolicyConditionPlugin(key);\n if (plugin) {\n if (!plugin.evaluate(payload, data, this.resolvePointer.bind(this))) {\n result = false;\n }\n } else {\n Logger.warn(`[MetaUI PolicyEngine] Unrecognized condition operator: ${key}`);\n }\n }\n\n return result;\n }\n\n /**\n * Expands relative $row policies into absolute index policies for each physical row.\n */\n private expandRowPolicies(data: unknown, policies: IPolicy[]): IPolicy[] {\n const expanded: IPolicy[] = [];\n for (const policy of policies) {\n let hasRow = false;\n let arrayPath = \"\";\n \n for (const target of policy.targets) {\n if (target.includes(\"$row\")) {\n hasRow = true;\n arrayPath = target.split(\"/$row\")[0];\n break;\n }\n }\n if (!hasRow && policy.condition) {\n const checkPaths = (obj: any) => {\n if (!obj) return;\n for (const key of Object.keys(obj)) {\n if (key.includes(\"$row\")) {\n hasRow = true;\n arrayPath = key.split(\"/$row\")[0];\n break;\n }\n }\n };\n checkPaths(policy.condition.NumericGreaterThan);\n checkPaths(policy.condition.NumericLessThan);\n checkPaths(policy.condition.StringEquals);\n \n if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => { if (p.includes(\"$row\")) { hasRow = true; arrayPath = p.split(\"/$row\")[0]; } });\n if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => { if (p.includes(\"$row\")) { hasRow = true; arrayPath = p.split(\"/$row\")[0]; } });\n }\n\n if (hasRow && arrayPath) {\n const arrayData = this.resolvePointer(data, arrayPath);\n if (Array.isArray(arrayData)) {\n for (let i = 0; i < arrayData.length; i++) {\n const cloneStr = JSON.stringify(policy).replace(/\\$row/g, String(i));\n expanded.push(JSON.parse(cloneStr));\n }\n }\n } else {\n expanded.push(policy);\n }\n }\n return expanded;\n }\n\n /**\n * Evaluates policies against the given data payload using a State Resolution Matrix.\n * @param data The JSON data payload.\n * @returns Array of delta states to apply to the UI.\n */\n public evaluate(data: unknown): IDeltaState[] {\n if (!this.policies || this.policies.length === 0) {\n return [];\n }\n\n const expandedPolicies = this.expandRowPolicies(data, this.policies);\n\n // Ledger: target -> property -> { forcedFalse: number, forcedTrue: number, lastMessage?: string }\n const ledger = new Map<string, Record<PropertyName, { forcedFalse: number, forcedTrue: number, lastMessage?: string }>>();\n\n const initTargetInLedger = (target: string) => {\n if (!ledger.has(target)) {\n ledger.set(target, {\n visibility: { forcedFalse: 0, forcedTrue: 0 },\n validity: { forcedFalse: 0, forcedTrue: 0 },\n required: { forcedFalse: 0, forcedTrue: 0 },\n editable: { forcedFalse: 0, forcedTrue: 0 }\n });\n }\n };\n\n // 1. Build the Composite State Ledger Matrix\n for (const policy of expandedPolicies) {\n const isConditionMet = this.evaluateCondition(policy.condition, data);\n \n for (const target of policy.targets) {\n initTargetInLedger(target);\n const currentLedger = ledger.get(target)!;\n \n const effectPlugin = PluginRegistry.getInstance().getPolicyEffectPlugin(policy.effect);\n if (!effectPlugin) {\n Logger.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${policy.effect}`);\n continue;\n }\n const mapped = effectPlugin.resolveState(isConditionMet, policy.effect);\n \n // Track forces. For restrictable properties (visibility, validity, editable), forcedFalse acts as a lock.\n // For required, forcedTrue acts as a lock.\n if (mapped.value) {\n currentLedger[mapped.property].forcedTrue++;\n } else {\n currentLedger[mapped.property].forcedFalse++;\n if (mapped.property === \"validity\" && isConditionMet && policy.message) {\n currentLedger.validity.lastMessage = policy.message;\n }\n }\n }\n }\n\n const newStateMatrix = new Map<string, ITargetState>();\n\n // 2. Resolve the Ledger into a concrete State Matrix\n for (const [target, stateMap] of ledger.entries()) {\n newStateMatrix.set(target, {\n // False (Hidden) wins if ANY policy forced it false\n visibility: stateMap.visibility.forcedFalse > 0 ? false : true,\n // False (Invalid) wins if ANY policy forced it false\n validity: stateMap.validity.forcedFalse > 0 ? false : true,\n // True (Required) wins if ANY policy forced it true\n required: stateMap.required.forcedTrue > 0 ? true : false,\n // False (Disabled) wins if ANY policy forced it false\n editable: stateMap.editable.forcedFalse > 0 ? false : true,\n errorMessage: stateMap.validity.lastMessage\n });\n }\n\n const deltas: IDeltaState[] = [];\n\n // 3. Differential Emission\n for (const [target, newState] of newStateMatrix.entries()) {\n const lastState = this.lastStateMatrix.get(target) || {};\n\n if (newState.visibility !== undefined && newState.visibility !== lastState.visibility) {\n deltas.push({ target, property: \"visibility\", value: newState.visibility });\n }\n if (newState.validity !== undefined && newState.validity !== lastState.validity) {\n if (newState.validity !== lastState.validity || newState.errorMessage !== lastState.errorMessage) {\n deltas.push({ target, property: \"validity\", value: newState.validity, errorMessage: newState.errorMessage });\n }\n }\n if (newState.required !== undefined && newState.required !== lastState.required) {\n deltas.push({ target, property: \"required\", value: newState.required });\n }\n if (newState.editable !== undefined && newState.editable !== lastState.editable) {\n deltas.push({ target, property: \"editable\", value: newState.editable });\n }\n }\n\n // Cache for next evaluation sweep\n this.lastStateMatrix = newStateMatrix;\n\n return deltas;\n }\n}\n"],"mappings":";;;QACSA,cAAc,GAAAC,iBAAA;EAAA,MACdC,MAAM,GAAAC,eAAA;EAuBf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACO,MAAMC,YAAY,CAAC;IAGtB;;IAGA;AACJ;AACA;AACA;IACIC,WAAWA,CAACC,QAAmB,EAAE;MAC7B,IAAI,CAACA,QAAQ,GAAGA,QAAQ,IAAI,EAAE;MAC9B,IAAI,CAACC,eAAe,GAAG,IAAIC,GAAG,CAAC,CAAC;MAChC,IAAI,CAACC,YAAY,CAAC,CAAC;IACvB;;IAEA;AACJ;AACA;IACYA,YAAYA,CAAA,EAAS;MACzB,MAAMC,KAAK,GAAG,IAAIF,GAAG,CAAsB,CAAC;MAE5C,KAAK,MAAMG,MAAM,IAAI,IAAI,CAACL,QAAQ,EAAE;QAChC,MAAMM,OAAO,GAAG,IAAIC,GAAG,CAAS,CAAC;QACjC,IAAIF,MAAM,CAACG,SAAS,EAAE;UAClB,MAAMC,QAAQ,GAAIC,GAAwC,IAAK;YAC3D,IAAIA,GAAG,EAAEC,MAAM,CAACC,IAAI,CAACF,GAAG,CAAC,CAACG,OAAO,CAACC,CAAC,IAAIR,OAAO,CAACS,GAAG,CAACD,CAAC,CAACE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;UAC5E,CAAC;UACDP,QAAQ,CAACJ,MAAM,CAACG,SAAS,CAACS,kBAAkB,CAAC;UAC7CR,QAAQ,CAACJ,MAAM,CAACG,SAAS,CAACU,eAAe,CAAC;UAC1CT,QAAQ,CAACJ,MAAM,CAACG,SAAS,CAACW,YAAY,CAAC;UACvC,IAAId,MAAM,CAACG,SAAS,CAACY,MAAM,EAAEf,MAAM,CAACG,SAAS,CAACY,MAAM,CAACP,OAAO,CAACQ,CAAC,IAAIf,OAAO,CAACS,GAAG,CAACM,CAAC,CAACL,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;UACnG,IAAIX,MAAM,CAACG,SAAS,CAACc,SAAS,EAAEjB,MAAM,CAACG,SAAS,CAACc,SAAS,CAACT,OAAO,CAACQ,CAAC,IAAIf,OAAO,CAACS,GAAG,CAACM,CAAC,CAACL,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7G;QAEA,KAAK,MAAMO,MAAM,IAAIlB,MAAM,CAACmB,OAAO,EAAE;UACjC,MAAMC,WAAW,GAAGF,MAAM,CAACP,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;UAC5C,IAAI,CAACZ,KAAK,CAACsB,GAAG,CAACD,WAAW,CAAC,EAAE;YACzBrB,KAAK,CAACuB,GAAG,CAACF,WAAW,EAAE,IAAIlB,GAAG,CAAC,CAAC,CAAC;UACrC;UACAD,OAAO,CAACO,OAAO,CAACe,CAAC,IAAIxB,KAAK,CAACyB,GAAG,CAACJ,WAAW,CAAC,CAAEV,GAAG,CAACa,CAAC,CAAC,CAAC;QACxD;MACJ;MAEA,MAAME,OAAO,GAAG,IAAIvB,GAAG,CAAS,CAAC;MACjC,MAAMwB,cAAc,GAAG,IAAIxB,GAAG,CAAS,CAAC;MAExC,MAAMyB,QAAQ,GAAIC,IAAY,IAAc;QACxC,IAAI,CAACH,OAAO,CAACJ,GAAG,CAACO,IAAI,CAAC,EAAE;UACpBH,OAAO,CAACf,GAAG,CAACkB,IAAI,CAAC;UACjBF,cAAc,CAAChB,GAAG,CAACkB,IAAI,CAAC;UAExB,MAAMC,SAAS,GAAG9B,KAAK,CAACyB,GAAG,CAACI,IAAI,CAAC;UACjC,IAAIC,SAAS,EAAE;YACX,KAAK,MAAMC,QAAQ,IAAID,SAAS,EAAE;cAC9B,IAAI,CAACJ,OAAO,CAACJ,GAAG,CAACS,QAAQ,CAAC,IAAIH,QAAQ,CAACG,QAAQ,CAAC,EAAE;gBAC9C,OAAO,IAAI;cACf,CAAC,MAAM,IAAIJ,cAAc,CAACL,GAAG,CAACS,QAAQ,CAAC,EAAE;gBACrC,OAAO,IAAI;cACf;YACJ;UACJ;QACJ;QACAJ,cAAc,CAACK,MAAM,CAACH,IAAI,CAAC;QAC3B,OAAO,KAAK;MAChB,CAAC;MAED,KAAK,MAAMA,IAAI,IAAI7B,KAAK,CAACQ,IAAI,CAAC,CAAC,EAAE;QAC7B,IAAIoB,QAAQ,CAACC,IAAI,CAAC,EAAE;UAChB,MAAM,IAAII,KAAK,CAAC,oFAAoFJ,IAAI,gDAAgD,CAAC;QAC7J;MACJ;IACJ;;IAEA;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;IACYK,cAAcA,CAACC,IAAa,EAAEC,OAAe,EAAW;MAC5D,IAAI,CAACD,IAAI,IAAI,CAACC,OAAO,EAAE,OAAOC,SAAS;MACvC;MACA,MAAMC,YAAY,GAAGF,OAAO,CAACG,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAACA,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;MAC1E,IAAI,CAACD,YAAY,EAAE,OAAOH,IAAI;MAE9B,MAAMK,KAAK,GAAGF,YAAY,CAAC1B,KAAK,CAAC,GAAG,CAAC;MACrC,IAAI6B,OAAO,GAAGN,IAA+B;MAC7C,KAAK,MAAMO,IAAI,IAAIF,KAAK,EAAE;QACtB,IAAIC,OAAO,KAAKJ,SAAS,IAAII,OAAO,KAAK,IAAI,EAAE,OAAOJ,SAAS;QAC/DI,OAAO,GAAGA,OAAO,CAACC,IAAI,CAA4B;MACtD;MACA,OAAOD,OAAO;IAClB;;IAEA;AACJ;AACA;AACA;AACA;AACA;AACA;IACYE,iBAAiBA,CAACvC,SAA2B,EAAE+B,IAAa,EAAW;MAC3E;MACA,IAAIS,MAAM,GAAG,IAAI;MACjB,MAAMC,QAAQ,GAAGvD,cAAc,CAACwD,WAAW,CAAC,CAAC;MAE7C,KAAK,MAAM,CAACC,GAAG,EAAEC,OAAO,CAAC,IAAIzC,MAAM,CAAC0C,OAAO,CAAC7C,SAAS,CAAC,EAAE;QACpD,MAAM8C,MAAM,GAAGL,QAAQ,CAACM,wBAAwB,CAACJ,GAAG,CAAC;QACrD,IAAIG,MAAM,EAAE;UACR,IAAI,CAACA,MAAM,CAACE,QAAQ,CAACJ,OAAO,EAAEb,IAAI,EAAE,IAAI,CAACD,cAAc,CAACmB,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;YACjET,MAAM,GAAG,KAAK;UAClB;QACJ,CAAC,MAAM;UACHpD,MAAM,CAAC8D,IAAI,CAAC,0DAA0DP,GAAG,EAAE,CAAC;QAChF;MACJ;MAEA,OAAOH,MAAM;IACjB;;IAEA;AACJ;AACA;IACYW,iBAAiBA,CAACpB,IAAa,EAAEvC,QAAmB,EAAa;MACrE,MAAM4D,QAAmB,GAAG,EAAE;MAC9B,KAAK,MAAMvD,MAAM,IAAIL,QAAQ,EAAE;QAC3B,IAAI6D,MAAM,GAAG,KAAK;QAClB,IAAIC,SAAS,GAAG,EAAE;QAElB,KAAK,MAAMvC,MAAM,IAAIlB,MAAM,CAACmB,OAAO,EAAE;UACjC,IAAID,MAAM,CAACwC,QAAQ,CAAC,MAAM,CAAC,EAAE;YACzBF,MAAM,GAAG,IAAI;YACbC,SAAS,GAAGvC,MAAM,CAACP,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACpC;UACJ;QACJ;QACA,IAAI,CAAC6C,MAAM,IAAIxD,MAAM,CAACG,SAAS,EAAE;UAC7B,MAAMwD,UAAU,GAAItD,GAAQ,IAAK;YAC7B,IAAI,CAACA,GAAG,EAAE;YACV,KAAK,MAAMyC,GAAG,IAAIxC,MAAM,CAACC,IAAI,CAACF,GAAG,CAAC,EAAE;cAChC,IAAIyC,GAAG,CAACY,QAAQ,CAAC,MAAM,CAAC,EAAE;gBACtBF,MAAM,GAAG,IAAI;gBACbC,SAAS,GAAGX,GAAG,CAACnC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACjC;cACJ;YACJ;UACJ,CAAC;UACDgD,UAAU,CAAC3D,MAAM,CAACG,SAAS,CAACS,kBAAkB,CAAC;UAC/C+C,UAAU,CAAC3D,MAAM,CAACG,SAAS,CAACU,eAAe,CAAC;UAC5C8C,UAAU,CAAC3D,MAAM,CAACG,SAAS,CAACW,YAAY,CAAC;UAEzC,IAAId,MAAM,CAACG,SAAS,CAACY,MAAM,EAAEf,MAAM,CAACG,SAAS,CAACY,MAAM,CAACP,OAAO,CAACQ,CAAC,IAAI;YAAE,IAAIA,CAAC,CAAC0C,QAAQ,CAAC,MAAM,CAAC,EAAE;cAAEF,MAAM,GAAG,IAAI;cAAEC,SAAS,GAAGzC,CAAC,CAACL,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAAE;UAAE,CAAC,CAAC;UAClJ,IAAIX,MAAM,CAACG,SAAS,CAACc,SAAS,EAAEjB,MAAM,CAACG,SAAS,CAACc,SAAS,CAACT,OAAO,CAACQ,CAAC,IAAI;YAAE,IAAIA,CAAC,CAAC0C,QAAQ,CAAC,MAAM,CAAC,EAAE;cAAEF,MAAM,GAAG,IAAI;cAAEC,SAAS,GAAGzC,CAAC,CAACL,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAAE;UAAE,CAAC,CAAC;QAC5J;QAEA,IAAI6C,MAAM,IAAIC,SAAS,EAAE;UACrB,MAAMG,SAAS,GAAG,IAAI,CAAC3B,cAAc,CAACC,IAAI,EAAEuB,SAAS,CAAC;UACtD,IAAII,KAAK,CAACC,OAAO,CAACF,SAAS,CAAC,EAAE;YAC1B,KAAK,IAAIG,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGH,SAAS,CAACI,MAAM,EAAED,CAAC,EAAE,EAAE;cACvC,MAAME,QAAQ,GAAGC,IAAI,CAACC,SAAS,CAACnE,MAAM,CAAC,CAACsC,OAAO,CAAC,QAAQ,EAAE8B,MAAM,CAACL,CAAC,CAAC,CAAC;cACpER,QAAQ,CAACc,IAAI,CAACH,IAAI,CAACI,KAAK,CAACL,QAAQ,CAAC,CAAC;YACvC;UACJ;QACJ,CAAC,MAAM;UACHV,QAAQ,CAACc,IAAI,CAACrE,MAAM,CAAC;QACzB;MACJ;MACA,OAAOuD,QAAQ;IACnB;;IAEA;AACJ;AACA;AACA;AACA;IACWJ,QAAQA,CAACjB,IAAa,EAAiB;MAC1C,IAAI,CAAC,IAAI,CAACvC,QAAQ,IAAI,IAAI,CAACA,QAAQ,CAACqE,MAAM,KAAK,CAAC,EAAE;QAC9C,OAAO,EAAE;MACb;MAEA,MAAMO,gBAAgB,GAAG,IAAI,CAACjB,iBAAiB,CAACpB,IAAI,EAAE,IAAI,CAACvC,QAAQ,CAAC;;MAEpE;MACA,MAAM6E,MAAM,GAAG,IAAI3E,GAAG,CAAkG,CAAC;MAEzH,MAAM4E,kBAAkB,GAAIvD,MAAc,IAAK;QAC3C,IAAI,CAACsD,MAAM,CAACnD,GAAG,CAACH,MAAM,CAAC,EAAE;UACrBsD,MAAM,CAAClD,GAAG,CAACJ,MAAM,EAAE;YACfwD,UAAU,EAAE;cAAEC,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE,CAAC;YAC7CC,QAAQ,EAAE;cAAEF,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE,CAAC;YAC3CE,QAAQ,EAAE;cAAEH,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE,CAAC;YAC3CG,QAAQ,EAAE;cAAEJ,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE;UAC9C,CAAC,CAAC;QACN;MACJ,CAAC;;MAED;MACA,KAAK,MAAM5E,MAAM,IAAIuE,gBAAgB,EAAE;QACnC,MAAMS,cAAc,GAAG,IAAI,CAACtC,iBAAiB,CAAC1C,MAAM,CAACG,SAAS,EAAE+B,IAAI,CAAC;QAErE,KAAK,MAAMhB,MAAM,IAAIlB,MAAM,CAACmB,OAAO,EAAE;UACjCsD,kBAAkB,CAACvD,MAAM,CAAC;UAC1B,MAAM+D,aAAa,GAAGT,MAAM,CAAChD,GAAG,CAACN,MAAM,CAAE;UAEzC,MAAMgE,YAAY,GAAG7F,cAAc,CAACwD,WAAW,CAAC,CAAC,CAACsC,qBAAqB,CAACnF,MAAM,CAACoF,MAAM,CAAC;UACtF,IAAI,CAACF,YAAY,EAAE;YACf3F,MAAM,CAAC8F,KAAK,CAAC,qDAAqDrF,MAAM,CAACoF,MAAM,EAAE,CAAC;YAClF;UACJ;UACA,MAAME,MAAM,GAAGJ,YAAY,CAACK,YAAY,CAACP,cAAc,EAAEhF,MAAM,CAACoF,MAAM,CAAC;;UAEvE;UACA;UACA,IAAIE,MAAM,CAACE,KAAK,EAAE;YACdP,aAAa,CAACK,MAAM,CAACG,QAAQ,CAAC,CAACb,UAAU,EAAE;UAC/C,CAAC,MAAM;YACHK,aAAa,CAACK,MAAM,CAACG,QAAQ,CAAC,CAACd,WAAW,EAAE;YAC5C,IAAIW,MAAM,CAACG,QAAQ,KAAK,UAAU,IAAIT,cAAc,IAAIhF,MAAM,CAAC0F,OAAO,EAAE;cACpET,aAAa,CAACJ,QAAQ,CAACc,WAAW,GAAG3F,MAAM,CAAC0F,OAAO;YACvD;UACJ;QACJ;MACJ;MAEA,MAAME,cAAc,GAAG,IAAI/F,GAAG,CAAuB,CAAC;;MAEtD;MACA,KAAK,MAAM,CAACqB,MAAM,EAAE2E,QAAQ,CAAC,IAAIrB,MAAM,CAACxB,OAAO,CAAC,CAAC,EAAE;QAC/C4C,cAAc,CAACtE,GAAG,CAACJ,MAAM,EAAE;UACvB;UACAwD,UAAU,EAAEmB,QAAQ,CAACnB,UAAU,CAACC,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;UAC9D;UACAE,QAAQ,EAAEgB,QAAQ,CAAChB,QAAQ,CAACF,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;UAC1D;UACAG,QAAQ,EAAEe,QAAQ,CAACf,QAAQ,CAACF,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,KAAK;UACzD;UACAG,QAAQ,EAAEc,QAAQ,CAACd,QAAQ,CAACJ,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;UAC1DmB,YAAY,EAAED,QAAQ,CAAChB,QAAQ,CAACc;QACpC,CAAC,CAAC;MACN;MAEA,MAAMI,MAAqB,GAAG,EAAE;;MAEhC;MACA,KAAK,MAAM,CAAC7E,MAAM,EAAE8E,QAAQ,CAAC,IAAIJ,cAAc,CAAC5C,OAAO,CAAC,CAAC,EAAE;QACvD,MAAMiD,SAAS,GAAG,IAAI,CAACrG,eAAe,CAAC4B,GAAG,CAACN,MAAM,CAAC,IAAI,CAAC,CAAC;QAExD,IAAI8E,QAAQ,CAACtB,UAAU,KAAKtC,SAAS,IAAI4D,QAAQ,CAACtB,UAAU,KAAKuB,SAAS,CAACvB,UAAU,EAAE;UACnFqB,MAAM,CAAC1B,IAAI,CAAC;YAAEnD,MAAM;YAAEuE,QAAQ,EAAE,YAAY;YAAED,KAAK,EAAEQ,QAAQ,CAACtB;UAAW,CAAC,CAAC;QAC/E;QACA,IAAIsB,QAAQ,CAACnB,QAAQ,KAAKzC,SAAS,IAAI4D,QAAQ,CAACnB,QAAQ,KAAKoB,SAAS,CAACpB,QAAQ,EAAE;UAC7E,IAAImB,QAAQ,CAACnB,QAAQ,KAAKoB,SAAS,CAACpB,QAAQ,IAAImB,QAAQ,CAACF,YAAY,KAAKG,SAAS,CAACH,YAAY,EAAE;YAC9FC,MAAM,CAAC1B,IAAI,CAAC;cAAEnD,MAAM;cAAEuE,QAAQ,EAAE,UAAU;cAAED,KAAK,EAAEQ,QAAQ,CAACnB,QAAQ;cAAEiB,YAAY,EAAEE,QAAQ,CAACF;YAAa,CAAC,CAAC;UAChH;QACJ;QACA,IAAIE,QAAQ,CAAClB,QAAQ,KAAK1C,SAAS,IAAI4D,QAAQ,CAAClB,QAAQ,KAAKmB,SAAS,CAACnB,QAAQ,EAAE;UAC7EiB,MAAM,CAAC1B,IAAI,CAAC;YAAEnD,MAAM;YAAEuE,QAAQ,EAAE,UAAU;YAAED,KAAK,EAAEQ,QAAQ,CAAClB;UAAS,CAAC,CAAC;QAC3E;QACA,IAAIkB,QAAQ,CAACjB,QAAQ,KAAK3C,SAAS,IAAI4D,QAAQ,CAACjB,QAAQ,KAAKkB,SAAS,CAAClB,QAAQ,EAAE;UAC7EgB,MAAM,CAAC1B,IAAI,CAAC;YAAEnD,MAAM;YAAEuE,QAAQ,EAAE,UAAU;YAAED,KAAK,EAAEQ,QAAQ,CAACjB;UAAS,CAAC,CAAC;QAC3E;MACJ;;MAEA;MACA,IAAI,CAACnF,eAAe,GAAGgG,cAAc;MAErC,OAAOG,MAAM;IACjB;EACJ;EAAC,IAAAG,SAAA;IAAAC,UAAA;EAAA;EAAAD,SAAA,CAAAzG,YAAA,GAAAA,YAAA;EAAA,OAAAyG,SAAA;AAAA","ignoreList":[]}
1
+ {"version":3,"file":"PolicyEngine-dbg.js","names":["PluginRegistry","___PluginRegistry","Logger","___utils_Logger","PolicyEngine","constructor","policies","lastStateMatrix","Map","resolvePointer","data","pointer","undefined","cleanPointer","replace","parts","split","current","part","evaluateCondition","condition","result","registry","getInstance","key","payload","Object","entries","plugin","getPolicyConditionPlugin","evaluate","bind","warn","expandRowPolicies","expanded","policy","policyStr","JSON","stringify","includes","match","arrayPath","arrayData","Array","isArray","i","length","cloneStr","String","push","parse","expandedPolicies","ledger","initTargetInLedger","target","has","set","visibility","forcedFalse","forcedTrue","validity","required","editable","isConditionMet","targets","currentLedger","get","effectPlugin","getPolicyEffectPlugin","effect","error","mapped","resolveState","value","property","message","lastMessage","newStateMatrix","stateMap","errorMessage","deltas","newState","lastState","__exports","__esModule"],"sources":["PolicyEngine.ts"],"sourcesContent":["import { IPolicy, IPolicyCondition, PolicyEffectType } from \"../interfaces/ISchema\";\nimport { PluginRegistry } from \"./PluginRegistry\";\nimport { Logger } from \"../utils/Logger\";\n\nexport type PropertyName = \"visibility\" | \"validity\" | \"required\" | \"editable\";\n\nexport interface IDeltaState {\n /** The absolute JSON pointer path of the target property (e.g., \"/VisitorInfo/PassportNumber\") */\n target: string;\n /** The UI5 visual or structural state to mutate */\n property: PropertyName;\n /** The boolean value to apply to the state */\n value: boolean;\n /** An optional custom error message to paint onto the field if validity is false */\n errorMessage?: string;\n}\n\ninterface ITargetState {\n visibility?: boolean;\n validity?: boolean;\n required?: boolean;\n editable?: boolean;\n errorMessage?: string;\n}\n\n/**\n * The PolicyEngine is responsible for evaluating complex, cross-field conditional logic defined in the schema's `uiPolicies` array.\n * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost\n * then applies to the individual active plugins.\n * \n * \n * @namespace nz.co.siliconst.ui5.metaui.core\n */\nexport class PolicyEngine {\n private policies: IPolicy[];\n\n /** Caches the last computed state matrix for differential delta emission */\n private lastStateMatrix: Map<string, ITargetState>;\n\n /**\n * Instantiates the PolicyEngine with an array of policies.\n * @param policies Array of uiPolicies defined in the root schema.\n */\n constructor(policies: IPolicy[]) {\n this.policies = policies || [];\n this.lastStateMatrix = new Map();\n }\n\n /**\n * Safely traverses a JSON payload using a JSON pointer.\n * Handles UI5 model prefixes (like metaui>) seamlessly.\n * \n * @param data The root JSON object representing the form payload.\n * @param pointer The JSON pointer to traverse (e.g., \"/Customer/Age\").\n * @returns The resolved value, or undefined if the path does not exist.\n */\n private resolvePointer(data: unknown, pointer: string): unknown {\n if (!data || !pointer) return undefined;\n // Strip leading slash and any UI5 prefixes\n const cleanPointer = pointer.replace(/^\\//, \"\").replace(/^metaui>\\/?/, \"\");\n if (!cleanPointer) return data;\n\n const parts = cleanPointer.split(\"/\");\n let current = data as Record<string, unknown>;\n for (const part of parts) {\n if (current === undefined || current === null) return undefined;\n current = current[part] as Record<string, unknown>;\n }\n return current;\n }\n\n /**\n * Evaluates a complex condition tree against the live JSON payload.\n * \n * @param condition The condition block to evaluate.\n * @param data The live JSON payload from the StateManager.\n * @returns True if all condition parameters are met, otherwise false.\n */\n private evaluateCondition(condition: IPolicyCondition, data: unknown): boolean {\n // If there are multiple operators, we treat them as AND for now\n let result = true;\n const registry = PluginRegistry.getInstance();\n\n for (const [key, payload] of Object.entries(condition)) {\n const plugin = registry.getPolicyConditionPlugin(key);\n if (plugin) {\n if (!plugin.evaluate(payload, data, this.resolvePointer.bind(this))) {\n result = false;\n }\n } else {\n Logger.warn(`[MetaUI PolicyEngine] Unrecognized condition operator: ${key}`);\n }\n }\n\n return result;\n }\n\n /**\n * Expands relative $row policies into absolute index policies for each physical row.\n * Uses generic stringification to decouple from specific condition operator plugins.\n */\n private expandRowPolicies(data: unknown, policies: IPolicy[]): IPolicy[] {\n const expanded: IPolicy[] = [];\n for (const policy of policies) {\n const policyStr = JSON.stringify(policy);\n if (policyStr.includes(\"$row\")) {\n // Extract the array path (everything before \"/$row\" inside a JSON string value)\n const match = policyStr.match(/\"([^\"]+)\\/\\$row/);\n if (match) {\n const arrayPath = match[1];\n const arrayData = this.resolvePointer(data, arrayPath);\n if (Array.isArray(arrayData)) {\n for (let i = 0; i < arrayData.length; i++) {\n const cloneStr = policyStr.replace(/\\$row/g, String(i));\n expanded.push(JSON.parse(cloneStr));\n }\n }\n } else {\n // Fallback if path couldn't be parsed\n expanded.push(policy);\n }\n } else {\n expanded.push(policy);\n }\n }\n return expanded;\n }\n\n /**\n * Evaluates policies against the given data payload using a State Resolution Matrix.\n * @param data The JSON data payload.\n * @returns Array of delta states to apply to the UI.\n */\n public evaluate(data: unknown): IDeltaState[] {\n if (!this.policies || this.policies.length === 0) {\n return [];\n }\n\n const expandedPolicies = this.expandRowPolicies(data, this.policies);\n\n // Ledger: target -> property -> { forcedFalse: number, forcedTrue: number, lastMessage?: string }\n const ledger = new Map<string, Record<PropertyName, { forcedFalse: number, forcedTrue: number, lastMessage?: string }>>();\n\n const initTargetInLedger = (target: string) => {\n if (!ledger.has(target)) {\n ledger.set(target, {\n visibility: { forcedFalse: 0, forcedTrue: 0 },\n validity: { forcedFalse: 0, forcedTrue: 0 },\n required: { forcedFalse: 0, forcedTrue: 0 },\n editable: { forcedFalse: 0, forcedTrue: 0 }\n });\n }\n };\n\n // 1. Build the Composite State Ledger Matrix\n for (const policy of expandedPolicies) {\n const isConditionMet = this.evaluateCondition(policy.condition, data);\n\n for (const target of policy.targets) {\n initTargetInLedger(target);\n const currentLedger = ledger.get(target)!;\n\n const effectPlugin = PluginRegistry.getInstance().getPolicyEffectPlugin(policy.effect);\n if (!effectPlugin) {\n Logger.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${policy.effect}`);\n continue;\n }\n const mapped = effectPlugin.resolveState(isConditionMet, policy.effect);\n\n // Track forces. For restrictable properties (visibility, validity, editable), forcedFalse acts as a lock.\n // For required, forcedTrue acts as a lock.\n if (mapped.value) {\n currentLedger[mapped.property].forcedTrue++;\n } else {\n currentLedger[mapped.property].forcedFalse++;\n if (mapped.property === \"validity\" && isConditionMet && policy.message) {\n currentLedger.validity.lastMessage = policy.message;\n }\n }\n }\n }\n\n const newStateMatrix = new Map<string, ITargetState>();\n\n // 2. Resolve the Ledger into a concrete State Matrix\n for (const [target, stateMap] of ledger.entries()) {\n newStateMatrix.set(target, {\n // False (Hidden) wins if ANY policy forced it false\n visibility: stateMap.visibility.forcedFalse > 0 ? false : true,\n // False (Invalid) wins if ANY policy forced it false\n validity: stateMap.validity.forcedFalse > 0 ? false : true,\n // True (Required) wins if ANY policy forced it true\n required: stateMap.required.forcedTrue > 0 ? true : false,\n // False (Disabled) wins if ANY policy forced it false\n editable: stateMap.editable.forcedFalse > 0 ? false : true,\n errorMessage: stateMap.validity.lastMessage\n });\n }\n\n const deltas: IDeltaState[] = [];\n\n // 3. Differential Emission\n for (const [target, newState] of newStateMatrix.entries()) {\n const lastState = this.lastStateMatrix.get(target) || {};\n\n if (newState.visibility !== undefined && newState.visibility !== lastState.visibility) {\n deltas.push({ target, property: \"visibility\", value: newState.visibility });\n }\n if (newState.validity !== undefined && newState.validity !== lastState.validity) {\n if (newState.validity !== lastState.validity || newState.errorMessage !== lastState.errorMessage) {\n deltas.push({ target, property: \"validity\", value: newState.validity, errorMessage: newState.errorMessage });\n }\n }\n if (newState.required !== undefined && newState.required !== lastState.required) {\n deltas.push({ target, property: \"required\", value: newState.required });\n }\n if (newState.editable !== undefined && newState.editable !== lastState.editable) {\n deltas.push({ target, property: \"editable\", value: newState.editable });\n }\n }\n\n // Cache for next evaluation sweep\n this.lastStateMatrix = newStateMatrix;\n\n return deltas;\n }\n}\n"],"mappings":";;;QACSA,cAAc,GAAAC,iBAAA;EAAA,MACdC,MAAM,GAAAC,eAAA;EAuBf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACO,MAAMC,YAAY,CAAC;IAGtB;;IAGA;AACJ;AACA;AACA;IACIC,WAAWA,CAACC,QAAmB,EAAE;MAC7B,IAAI,CAACA,QAAQ,GAAGA,QAAQ,IAAI,EAAE;MAC9B,IAAI,CAACC,eAAe,GAAG,IAAIC,GAAG,CAAC,CAAC;IACpC;;IAEA;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;IACYC,cAAcA,CAACC,IAAa,EAAEC,OAAe,EAAW;MAC5D,IAAI,CAACD,IAAI,IAAI,CAACC,OAAO,EAAE,OAAOC,SAAS;MACvC;MACA,MAAMC,YAAY,GAAGF,OAAO,CAACG,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAACA,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;MAC1E,IAAI,CAACD,YAAY,EAAE,OAAOH,IAAI;MAE9B,MAAMK,KAAK,GAAGF,YAAY,CAACG,KAAK,CAAC,GAAG,CAAC;MACrC,IAAIC,OAAO,GAAGP,IAA+B;MAC7C,KAAK,MAAMQ,IAAI,IAAIH,KAAK,EAAE;QACtB,IAAIE,OAAO,KAAKL,SAAS,IAAIK,OAAO,KAAK,IAAI,EAAE,OAAOL,SAAS;QAC/DK,OAAO,GAAGA,OAAO,CAACC,IAAI,CAA4B;MACtD;MACA,OAAOD,OAAO;IAClB;;IAEA;AACJ;AACA;AACA;AACA;AACA;AACA;IACYE,iBAAiBA,CAACC,SAA2B,EAAEV,IAAa,EAAW;MAC3E;MACA,IAAIW,MAAM,GAAG,IAAI;MACjB,MAAMC,QAAQ,GAAGtB,cAAc,CAACuB,WAAW,CAAC,CAAC;MAE7C,KAAK,MAAM,CAACC,GAAG,EAAEC,OAAO,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACP,SAAS,CAAC,EAAE;QACpD,MAAMQ,MAAM,GAAGN,QAAQ,CAACO,wBAAwB,CAACL,GAAG,CAAC;QACrD,IAAII,MAAM,EAAE;UACR,IAAI,CAACA,MAAM,CAACE,QAAQ,CAACL,OAAO,EAAEf,IAAI,EAAE,IAAI,CAACD,cAAc,CAACsB,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;YACjEV,MAAM,GAAG,KAAK;UAClB;QACJ,CAAC,MAAM;UACHnB,MAAM,CAAC8B,IAAI,CAAC,0DAA0DR,GAAG,EAAE,CAAC;QAChF;MACJ;MAEA,OAAOH,MAAM;IACjB;;IAEA;AACJ;AACA;AACA;IACYY,iBAAiBA,CAACvB,IAAa,EAAEJ,QAAmB,EAAa;MACrE,MAAM4B,QAAmB,GAAG,EAAE;MAC9B,KAAK,MAAMC,MAAM,IAAI7B,QAAQ,EAAE;QAC3B,MAAM8B,SAAS,GAAGC,IAAI,CAACC,SAAS,CAACH,MAAM,CAAC;QACxC,IAAIC,SAAS,CAACG,QAAQ,CAAC,MAAM,CAAC,EAAE;UAC5B;UACA,MAAMC,KAAK,GAAGJ,SAAS,CAACI,KAAK,CAAC,iBAAiB,CAAC;UAChD,IAAIA,KAAK,EAAE;YACP,MAAMC,SAAS,GAAGD,KAAK,CAAC,CAAC,CAAC;YAC1B,MAAME,SAAS,GAAG,IAAI,CAACjC,cAAc,CAACC,IAAI,EAAE+B,SAAS,CAAC;YACtD,IAAIE,KAAK,CAACC,OAAO,CAACF,SAAS,CAAC,EAAE;cAC1B,KAAK,IAAIG,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGH,SAAS,CAACI,MAAM,EAAED,CAAC,EAAE,EAAE;gBACvC,MAAME,QAAQ,GAAGX,SAAS,CAACtB,OAAO,CAAC,QAAQ,EAAEkC,MAAM,CAACH,CAAC,CAAC,CAAC;gBACvDX,QAAQ,CAACe,IAAI,CAACZ,IAAI,CAACa,KAAK,CAACH,QAAQ,CAAC,CAAC;cACvC;YACJ;UACJ,CAAC,MAAM;YACH;YACAb,QAAQ,CAACe,IAAI,CAACd,MAAM,CAAC;UACzB;QACJ,CAAC,MAAM;UACHD,QAAQ,CAACe,IAAI,CAACd,MAAM,CAAC;QACzB;MACJ;MACA,OAAOD,QAAQ;IACnB;;IAEA;AACJ;AACA;AACA;AACA;IACWJ,QAAQA,CAACpB,IAAa,EAAiB;MAC1C,IAAI,CAAC,IAAI,CAACJ,QAAQ,IAAI,IAAI,CAACA,QAAQ,CAACwC,MAAM,KAAK,CAAC,EAAE;QAC9C,OAAO,EAAE;MACb;MAEA,MAAMK,gBAAgB,GAAG,IAAI,CAAClB,iBAAiB,CAACvB,IAAI,EAAE,IAAI,CAACJ,QAAQ,CAAC;;MAEpE;MACA,MAAM8C,MAAM,GAAG,IAAI5C,GAAG,CAAkG,CAAC;MAEzH,MAAM6C,kBAAkB,GAAIC,MAAc,IAAK;QAC3C,IAAI,CAACF,MAAM,CAACG,GAAG,CAACD,MAAM,CAAC,EAAE;UACrBF,MAAM,CAACI,GAAG,CAACF,MAAM,EAAE;YACfG,UAAU,EAAE;cAAEC,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE,CAAC;YAC7CC,QAAQ,EAAE;cAAEF,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE,CAAC;YAC3CE,QAAQ,EAAE;cAAEH,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE,CAAC;YAC3CG,QAAQ,EAAE;cAAEJ,WAAW,EAAE,CAAC;cAAEC,UAAU,EAAE;YAAE;UAC9C,CAAC,CAAC;QACN;MACJ,CAAC;;MAED;MACA,KAAK,MAAMxB,MAAM,IAAIgB,gBAAgB,EAAE;QACnC,MAAMY,cAAc,GAAG,IAAI,CAAC5C,iBAAiB,CAACgB,MAAM,CAACf,SAAS,EAAEV,IAAI,CAAC;QAErE,KAAK,MAAM4C,MAAM,IAAInB,MAAM,CAAC6B,OAAO,EAAE;UACjCX,kBAAkB,CAACC,MAAM,CAAC;UAC1B,MAAMW,aAAa,GAAGb,MAAM,CAACc,GAAG,CAACZ,MAAM,CAAE;UAEzC,MAAMa,YAAY,GAAGnE,cAAc,CAACuB,WAAW,CAAC,CAAC,CAAC6C,qBAAqB,CAACjC,MAAM,CAACkC,MAAM,CAAC;UACtF,IAAI,CAACF,YAAY,EAAE;YACfjE,MAAM,CAACoE,KAAK,CAAC,qDAAqDnC,MAAM,CAACkC,MAAM,EAAE,CAAC;YAClF;UACJ;UACA,MAAME,MAAM,GAAGJ,YAAY,CAACK,YAAY,CAACT,cAAc,EAAE5B,MAAM,CAACkC,MAAM,CAAC;;UAEvE;UACA;UACA,IAAIE,MAAM,CAACE,KAAK,EAAE;YACdR,aAAa,CAACM,MAAM,CAACG,QAAQ,CAAC,CAACf,UAAU,EAAE;UAC/C,CAAC,MAAM;YACHM,aAAa,CAACM,MAAM,CAACG,QAAQ,CAAC,CAAChB,WAAW,EAAE;YAC5C,IAAIa,MAAM,CAACG,QAAQ,KAAK,UAAU,IAAIX,cAAc,IAAI5B,MAAM,CAACwC,OAAO,EAAE;cACpEV,aAAa,CAACL,QAAQ,CAACgB,WAAW,GAAGzC,MAAM,CAACwC,OAAO;YACvD;UACJ;QACJ;MACJ;MAEA,MAAME,cAAc,GAAG,IAAIrE,GAAG,CAAuB,CAAC;;MAEtD;MACA,KAAK,MAAM,CAAC8C,MAAM,EAAEwB,QAAQ,CAAC,IAAI1B,MAAM,CAACzB,OAAO,CAAC,CAAC,EAAE;QAC/CkD,cAAc,CAACrB,GAAG,CAACF,MAAM,EAAE;UACvB;UACAG,UAAU,EAAEqB,QAAQ,CAACrB,UAAU,CAACC,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;UAC9D;UACAE,QAAQ,EAAEkB,QAAQ,CAAClB,QAAQ,CAACF,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;UAC1D;UACAG,QAAQ,EAAEiB,QAAQ,CAACjB,QAAQ,CAACF,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,KAAK;UACzD;UACAG,QAAQ,EAAEgB,QAAQ,CAAChB,QAAQ,CAACJ,WAAW,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI;UAC1DqB,YAAY,EAAED,QAAQ,CAAClB,QAAQ,CAACgB;QACpC,CAAC,CAAC;MACN;MAEA,MAAMI,MAAqB,GAAG,EAAE;;MAEhC;MACA,KAAK,MAAM,CAAC1B,MAAM,EAAE2B,QAAQ,CAAC,IAAIJ,cAAc,CAAClD,OAAO,CAAC,CAAC,EAAE;QACvD,MAAMuD,SAAS,GAAG,IAAI,CAAC3E,eAAe,CAAC2D,GAAG,CAACZ,MAAM,CAAC,IAAI,CAAC,CAAC;QAExD,IAAI2B,QAAQ,CAACxB,UAAU,KAAK7C,SAAS,IAAIqE,QAAQ,CAACxB,UAAU,KAAKyB,SAAS,CAACzB,UAAU,EAAE;UACnFuB,MAAM,CAAC/B,IAAI,CAAC;YAAEK,MAAM;YAAEoB,QAAQ,EAAE,YAAY;YAAED,KAAK,EAAEQ,QAAQ,CAACxB;UAAW,CAAC,CAAC;QAC/E;QACA,IAAIwB,QAAQ,CAACrB,QAAQ,KAAKhD,SAAS,IAAIqE,QAAQ,CAACrB,QAAQ,KAAKsB,SAAS,CAACtB,QAAQ,EAAE;UAC7E,IAAIqB,QAAQ,CAACrB,QAAQ,KAAKsB,SAAS,CAACtB,QAAQ,IAAIqB,QAAQ,CAACF,YAAY,KAAKG,SAAS,CAACH,YAAY,EAAE;YAC9FC,MAAM,CAAC/B,IAAI,CAAC;cAAEK,MAAM;cAAEoB,QAAQ,EAAE,UAAU;cAAED,KAAK,EAAEQ,QAAQ,CAACrB,QAAQ;cAAEmB,YAAY,EAAEE,QAAQ,CAACF;YAAa,CAAC,CAAC;UAChH;QACJ;QACA,IAAIE,QAAQ,CAACpB,QAAQ,KAAKjD,SAAS,IAAIqE,QAAQ,CAACpB,QAAQ,KAAKqB,SAAS,CAACrB,QAAQ,EAAE;UAC7EmB,MAAM,CAAC/B,IAAI,CAAC;YAAEK,MAAM;YAAEoB,QAAQ,EAAE,UAAU;YAAED,KAAK,EAAEQ,QAAQ,CAACpB;UAAS,CAAC,CAAC;QAC3E;QACA,IAAIoB,QAAQ,CAACnB,QAAQ,KAAKlD,SAAS,IAAIqE,QAAQ,CAACnB,QAAQ,KAAKoB,SAAS,CAACpB,QAAQ,EAAE;UAC7EkB,MAAM,CAAC/B,IAAI,CAAC;YAAEK,MAAM;YAAEoB,QAAQ,EAAE,UAAU;YAAED,KAAK,EAAEQ,QAAQ,CAACnB;UAAS,CAAC,CAAC;QAC3E;MACJ;;MAEA;MACA,IAAI,CAACvD,eAAe,GAAGsE,cAAc;MAErC,OAAOG,MAAM;IACjB;EACJ;EAAC,IAAAG,SAAA;IAAAC,UAAA;EAAA;EAAAD,SAAA,CAAA/E,YAAA,GAAAA,YAAA;EAAA,OAAA+E,SAAA;AAAA","ignoreList":[]}
@@ -23,8 +23,6 @@ declare module "nz/co/siliconst/ui5/metaui/core/PolicyEngine" {
23
23
  * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost
24
24
  * then applies to the individual active plugins.
25
25
  *
26
- * It ensures the orchestration of conditional rendering, requiring, and invalidating of fields without the plugins
27
- * needing to be aware of each other.
28
26
  *
29
27
  * @namespace nz.co.siliconst.ui5.metaui.core
30
28
  */
@@ -37,10 +35,6 @@ declare module "nz/co/siliconst/ui5/metaui/core/PolicyEngine" {
37
35
  * @param policies Array of uiPolicies defined in the root schema.
38
36
  */
39
37
  constructor(policies: IPolicy[]);
40
- /**
41
- * Builds a directed dependency graph and detects cycles to prevent infinite event loops.
42
- */
43
- private detectCycles;
44
38
  /**
45
39
  * Safely traverses a JSON payload using a JSON pointer.
46
40
  * Handles UI5 model prefixes (like metaui>) seamlessly.
@@ -60,6 +54,7 @@ declare module "nz/co/siliconst/ui5/metaui/core/PolicyEngine" {
60
54
  private evaluateCondition;
61
55
  /**
62
56
  * Expands relative $row policies into absolute index policies for each physical row.
57
+ * Uses generic stringification to decouple from specific condition operator plugins.
63
58
  */
64
59
  private expandRowPolicies;
65
60
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"PolicyEngine.d.ts","sourceRoot":"../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/core/PolicyEngine.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,8CAA8C,CAAC;IAC9D,OAAO,EAAE,OAAO,EAAsC,MAAM,+CAA+C,CAAC;IAI5G,KAAY,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;IAE/E,UAAiB,WAAW;QACxB,kGAAkG;QAClG,MAAM,EAAE,MAAM,CAAC;QACf,mDAAmD;QACnD,QAAQ,EAAE,YAAY,CAAC;QACvB,8CAA8C;QAC9C,KAAK,EAAE,OAAO,CAAC;QACf,oFAAoF;QACpF,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB;IAED,UAAU,YAAY;QAClB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB;IAED;;;;;;;;;OASG;IACH,MAAa,YAAY;QACrB,OAAO,CAAC,QAAQ,CAAY;QAE5B,4EAA4E;QAC5E,OAAO,CAAC,eAAe,CAA4B;QAEnD;;;WAGG;oBACS,QAAQ,EAAE,OAAO,EAAE;QAM/B;;WAEG;QACH,OAAO,CAAC,YAAY;QAuDpB;;;;;;;WAOG;QACH,OAAO,CAAC,cAAc;QAetB;;;;;;WAMG;QACH,OAAO,CAAC,iBAAiB;QAmBzB;;WAEG;QACH,OAAO,CAAC,iBAAiB;QA+CzB;;;;WAIG;QACI,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,EAAE;KA6FhD;CAEA"}
1
+ {"version":3,"file":"PolicyEngine.d.ts","sourceRoot":"../../../../../../../..","sources":["src/nz/co/siliconst/ui5/metaui/core/PolicyEngine.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,8CAA8C,CAAC;IAC9D,OAAO,EAAE,OAAO,EAAsC,MAAM,+CAA+C,CAAC;IAI5G,KAAY,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;IAE/E,UAAiB,WAAW;QACxB,kGAAkG;QAClG,MAAM,EAAE,MAAM,CAAC;QACf,mDAAmD;QACnD,QAAQ,EAAE,YAAY,CAAC;QACvB,8CAA8C;QAC9C,KAAK,EAAE,OAAO,CAAC;QACf,oFAAoF;QACpF,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB;IAED,UAAU,YAAY;QAClB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB;IAED;;;;;;;OAOG;IACH,MAAa,YAAY;QACrB,OAAO,CAAC,QAAQ,CAAY;QAE5B,4EAA4E;QAC5E,OAAO,CAAC,eAAe,CAA4B;QAEnD;;;WAGG;oBACS,QAAQ,EAAE,OAAO,EAAE;QAK/B;;;;;;;WAOG;QACH,OAAO,CAAC,cAAc;QAetB;;;;;;WAMG;QACH,OAAO,CAAC,iBAAiB;QAmBzB;;;WAGG;QACH,OAAO,CAAC,iBAAiB;QA2BzB;;;;WAIG;QACI,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,EAAE;KA6FhD;CAEA"}
@@ -1,2 +1,2 @@
1
- sap.ui.define(["./PluginRegistry","../utils/Logger"],function(e,i){"use strict";const t=e["PluginRegistry"];const r=i["Logger"];class o{constructor(e){this.policies=e||[];this.lastStateMatrix=new Map;this.detectCycles()}detectCycles(){const e=new Map;for(const i of this.policies){const t=new Set;if(i.condition){const e=e=>{if(e)Object.keys(e).forEach(e=>t.add(e.split("/$row")[0]))};e(i.condition.NumericGreaterThan);e(i.condition.NumericLessThan);e(i.condition.StringEquals);if(i.condition.IsNull)i.condition.IsNull.forEach(e=>t.add(e.split("/$row")[0]));if(i.condition.IsNotNull)i.condition.IsNotNull.forEach(e=>t.add(e.split("/$row")[0]))}for(const r of i.targets){const i=r.split("/$row")[0];if(!e.has(i)){e.set(i,new Set)}t.forEach(t=>e.get(i).add(t))}}const i=new Set;const t=new Set;const r=o=>{if(!i.has(o)){i.add(o);t.add(o);const s=e.get(o);if(s){for(const e of s){if(!i.has(e)&&r(e)){return true}else if(t.has(e)){return true}}}}t.delete(o);return false};for(const i of e.keys()){if(r(i)){throw new Error(`[MetaUI PolicyEngine] FATAL: Cyclic dependency detected involving policy target '${i}'. This will cause an infinite rendering loop.`)}}}resolvePointer(e,i){if(!e||!i)return undefined;const t=i.replace(/^\//,"").replace(/^metaui>\/?/,"");if(!t)return e;const r=t.split("/");let o=e;for(const e of r){if(o===undefined||o===null)return undefined;o=o[e]}return o}evaluateCondition(e,i){let o=true;const s=t.getInstance();for(const[t,n]of Object.entries(e)){const e=s.getPolicyConditionPlugin(t);if(e){if(!e.evaluate(n,i,this.resolvePointer.bind(this))){o=false}}else{r.warn(`[MetaUI PolicyEngine] Unrecognized condition operator: ${t}`)}}return o}expandRowPolicies(e,i){const t=[];for(const r of i){let i=false;let o="";for(const e of r.targets){if(e.includes("$row")){i=true;o=e.split("/$row")[0];break}}if(!i&&r.condition){const e=e=>{if(!e)return;for(const t of Object.keys(e)){if(t.includes("$row")){i=true;o=t.split("/$row")[0];break}}};e(r.condition.NumericGreaterThan);e(r.condition.NumericLessThan);e(r.condition.StringEquals);if(r.condition.IsNull)r.condition.IsNull.forEach(e=>{if(e.includes("$row")){i=true;o=e.split("/$row")[0]}});if(r.condition.IsNotNull)r.condition.IsNotNull.forEach(e=>{if(e.includes("$row")){i=true;o=e.split("/$row")[0]}})}if(i&&o){const i=this.resolvePointer(e,o);if(Array.isArray(i)){for(let e=0;e<i.length;e++){const i=JSON.stringify(r).replace(/\$row/g,String(e));t.push(JSON.parse(i))}}}else{t.push(r)}}return t}evaluate(e){if(!this.policies||this.policies.length===0){return[]}const i=this.expandRowPolicies(e,this.policies);const o=new Map;const s=e=>{if(!o.has(e)){o.set(e,{visibility:{forcedFalse:0,forcedTrue:0},validity:{forcedFalse:0,forcedTrue:0},required:{forcedFalse:0,forcedTrue:0},editable:{forcedFalse:0,forcedTrue:0}})}};for(const n of i){const i=this.evaluateCondition(n.condition,e);for(const e of n.targets){s(e);const l=o.get(e);const c=t.getInstance().getPolicyEffectPlugin(n.effect);if(!c){r.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${n.effect}`);continue}const a=c.resolveState(i,n.effect);if(a.value){l[a.property].forcedTrue++}else{l[a.property].forcedFalse++;if(a.property==="validity"&&i&&n.message){l.validity.lastMessage=n.message}}}}const n=new Map;for(const[e,i]of o.entries()){n.set(e,{visibility:i.visibility.forcedFalse>0?false:true,validity:i.validity.forcedFalse>0?false:true,required:i.required.forcedTrue>0?true:false,editable:i.editable.forcedFalse>0?false:true,errorMessage:i.validity.lastMessage})}const l=[];for(const[e,i]of n.entries()){const t=this.lastStateMatrix.get(e)||{};if(i.visibility!==undefined&&i.visibility!==t.visibility){l.push({target:e,property:"visibility",value:i.visibility})}if(i.validity!==undefined&&i.validity!==t.validity){if(i.validity!==t.validity||i.errorMessage!==t.errorMessage){l.push({target:e,property:"validity",value:i.validity,errorMessage:i.errorMessage})}}if(i.required!==undefined&&i.required!==t.required){l.push({target:e,property:"required",value:i.required})}if(i.editable!==undefined&&i.editable!==t.editable){l.push({target:e,property:"editable",value:i.editable})}}this.lastStateMatrix=n;return l}}var s={__esModule:true};s.PolicyEngine=o;return s});
1
+ sap.ui.define(["./PluginRegistry","../utils/Logger"],function(e,i){"use strict";const t=e["PluginRegistry"];const r=i["Logger"];class s{constructor(e){this.policies=e||[];this.lastStateMatrix=new Map}resolvePointer(e,i){if(!e||!i)return undefined;const t=i.replace(/^\//,"").replace(/^metaui>\/?/,"");if(!t)return e;const r=t.split("/");let s=e;for(const e of r){if(s===undefined||s===null)return undefined;s=s[e]}return s}evaluateCondition(e,i){let s=true;const o=t.getInstance();for(const[t,n]of Object.entries(e)){const e=o.getPolicyConditionPlugin(t);if(e){if(!e.evaluate(n,i,this.resolvePointer.bind(this))){s=false}}else{r.warn(`[MetaUI PolicyEngine] Unrecognized condition operator: ${t}`)}}return s}expandRowPolicies(e,i){const t=[];for(const r of i){const i=JSON.stringify(r);if(i.includes("$row")){const s=i.match(/"([^"]+)\/\$row/);if(s){const r=s[1];const o=this.resolvePointer(e,r);if(Array.isArray(o)){for(let e=0;e<o.length;e++){const r=i.replace(/\$row/g,String(e));t.push(JSON.parse(r))}}}else{t.push(r)}}else{t.push(r)}}return t}evaluate(e){if(!this.policies||this.policies.length===0){return[]}const i=this.expandRowPolicies(e,this.policies);const s=new Map;const o=e=>{if(!s.has(e)){s.set(e,{visibility:{forcedFalse:0,forcedTrue:0},validity:{forcedFalse:0,forcedTrue:0},required:{forcedFalse:0,forcedTrue:0},editable:{forcedFalse:0,forcedTrue:0}})}};for(const n of i){const i=this.evaluateCondition(n.condition,e);for(const e of n.targets){o(e);const a=s.get(e);const l=t.getInstance().getPolicyEffectPlugin(n.effect);if(!l){r.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${n.effect}`);continue}const c=l.resolveState(i,n.effect);if(c.value){a[c.property].forcedTrue++}else{a[c.property].forcedFalse++;if(c.property==="validity"&&i&&n.message){a.validity.lastMessage=n.message}}}}const n=new Map;for(const[e,i]of s.entries()){n.set(e,{visibility:i.visibility.forcedFalse>0?false:true,validity:i.validity.forcedFalse>0?false:true,required:i.required.forcedTrue>0?true:false,editable:i.editable.forcedFalse>0?false:true,errorMessage:i.validity.lastMessage})}const a=[];for(const[e,i]of n.entries()){const t=this.lastStateMatrix.get(e)||{};if(i.visibility!==undefined&&i.visibility!==t.visibility){a.push({target:e,property:"visibility",value:i.visibility})}if(i.validity!==undefined&&i.validity!==t.validity){if(i.validity!==t.validity||i.errorMessage!==t.errorMessage){a.push({target:e,property:"validity",value:i.validity,errorMessage:i.errorMessage})}}if(i.required!==undefined&&i.required!==t.required){a.push({target:e,property:"required",value:i.required})}if(i.editable!==undefined&&i.editable!==t.editable){a.push({target:e,property:"editable",value:i.editable})}}this.lastStateMatrix=n;return a}}var o={__esModule:true};o.PolicyEngine=s;return o});
2
2
  //# sourceMappingURL=PolicyEngine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PolicyEngine.js","names":["PluginRegistry","___PluginRegistry","Logger","___utils_Logger","PolicyEngine","constructor","policies","this","lastStateMatrix","Map","detectCycles","graph","policy","sources","Set","condition","addPaths","obj","Object","keys","forEach","k","add","split","NumericGreaterThan","NumericLessThan","StringEquals","IsNull","p","IsNotNull","target","targets","cleanTarget","has","set","s","get","visited","recursionStack","isCyclic","node","neighbors","neighbor","delete","Error","resolvePointer","data","pointer","undefined","cleanPointer","replace","parts","current","part","evaluateCondition","result","registry","getInstance","key","payload","entries","plugin","getPolicyConditionPlugin","evaluate","bind","warn","expandRowPolicies","expanded","hasRow","arrayPath","includes","checkPaths","arrayData","Array","isArray","i","length","cloneStr","JSON","stringify","String","push","parse","expandedPolicies","ledger","initTargetInLedger","visibility","forcedFalse","forcedTrue","validity","required","editable","isConditionMet","currentLedger","effectPlugin","getPolicyEffectPlugin","effect","error","mapped","resolveState","value","property","message","lastMessage","newStateMatrix","stateMap","errorMessage","deltas","newState","lastState","__exports","__esModule"],"sources":["PolicyEngine.ts"],"sourcesContent":["import { IPolicy, IPolicyCondition, PolicyEffectType } from \"../interfaces/ISchema\";\nimport { PluginRegistry } from \"./PluginRegistry\";\nimport { Logger } from \"../utils/Logger\";\n\nexport type PropertyName = \"visibility\" | \"validity\" | \"required\" | \"editable\";\n\nexport interface IDeltaState {\n /** The absolute JSON pointer path of the target property (e.g., \"/VisitorInfo/PassportNumber\") */\n target: string;\n /** The UI5 visual or structural state to mutate */\n property: PropertyName;\n /** The boolean value to apply to the state */\n value: boolean;\n /** An optional custom error message to paint onto the field if validity is false */\n errorMessage?: string;\n}\n\ninterface ITargetState {\n visibility?: boolean;\n validity?: boolean;\n required?: boolean;\n editable?: boolean;\n errorMessage?: string;\n}\n\n/**\n * The PolicyEngine is responsible for evaluating complex, cross-field conditional logic defined in the schema's `uiPolicies` array.\n * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost\n * then applies to the individual active plugins.\n * \n * It ensures the orchestration of conditional rendering, requiring, and invalidating of fields without the plugins\n * needing to be aware of each other.\n * \n * @namespace nz.co.siliconst.ui5.metaui.core\n */\nexport class PolicyEngine {\n private policies: IPolicy[];\n \n /** Caches the last computed state matrix for differential delta emission */\n private lastStateMatrix: Map<string, ITargetState>;\n\n /**\n * Instantiates the PolicyEngine with an array of policies.\n * @param policies Array of uiPolicies defined in the root schema.\n */\n constructor(policies: IPolicy[]) {\n this.policies = policies || [];\n this.lastStateMatrix = new Map();\n this.detectCycles();\n }\n\n /**\n * Builds a directed dependency graph and detects cycles to prevent infinite event loops.\n */\n private detectCycles(): void {\n const graph = new Map<string, Set<string>>();\n\n for (const policy of this.policies) {\n const sources = new Set<string>();\n if (policy.condition) {\n const addPaths = (obj: Record<string, unknown> | undefined) => {\n if (obj) Object.keys(obj).forEach(k => sources.add(k.split(\"/$row\")[0]));\n };\n addPaths(policy.condition.NumericGreaterThan);\n addPaths(policy.condition.NumericLessThan);\n addPaths(policy.condition.StringEquals);\n if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => sources.add(p.split(\"/$row\")[0]));\n if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => sources.add(p.split(\"/$row\")[0]));\n }\n\n for (const target of policy.targets) {\n const cleanTarget = target.split(\"/$row\")[0];\n if (!graph.has(cleanTarget)) {\n graph.set(cleanTarget, new Set());\n }\n sources.forEach(s => graph.get(cleanTarget)!.add(s));\n }\n }\n\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const isCyclic = (node: string): boolean => {\n if (!visited.has(node)) {\n visited.add(node);\n recursionStack.add(node);\n\n const neighbors = graph.get(node);\n if (neighbors) {\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor) && isCyclic(neighbor)) {\n return true;\n } else if (recursionStack.has(neighbor)) {\n return true;\n }\n }\n }\n }\n recursionStack.delete(node);\n return false;\n };\n\n for (const node of graph.keys()) {\n if (isCyclic(node)) {\n throw new Error(`[MetaUI PolicyEngine] FATAL: Cyclic dependency detected involving policy target '${node}'. This will cause an infinite rendering loop.`);\n }\n }\n }\n\n /**\n * Safely traverses a JSON payload using a JSON pointer.\n * Handles UI5 model prefixes (like metaui>) seamlessly.\n * \n * @param data The root JSON object representing the form payload.\n * @param pointer The JSON pointer to traverse (e.g., \"/Customer/Age\").\n * @returns The resolved value, or undefined if the path does not exist.\n */\n private resolvePointer(data: unknown, pointer: string): unknown {\n if (!data || !pointer) return undefined;\n // Strip leading slash and any UI5 prefixes\n const cleanPointer = pointer.replace(/^\\//, \"\").replace(/^metaui>\\/?/, \"\");\n if (!cleanPointer) return data;\n \n const parts = cleanPointer.split(\"/\");\n let current = data as Record<string, unknown>;\n for (const part of parts) {\n if (current === undefined || current === null) return undefined;\n current = current[part] as Record<string, unknown>;\n }\n return current;\n }\n\n /**\n * Evaluates a complex condition tree against the live JSON payload.\n * \n * @param condition The condition block to evaluate.\n * @param data The live JSON payload from the StateManager.\n * @returns True if all condition parameters are met, otherwise false.\n */\n private evaluateCondition(condition: IPolicyCondition, data: unknown): boolean {\n // If there are multiple operators, we treat them as AND for now\n let result = true;\n const registry = PluginRegistry.getInstance();\n\n for (const [key, payload] of Object.entries(condition)) {\n const plugin = registry.getPolicyConditionPlugin(key);\n if (plugin) {\n if (!plugin.evaluate(payload, data, this.resolvePointer.bind(this))) {\n result = false;\n }\n } else {\n Logger.warn(`[MetaUI PolicyEngine] Unrecognized condition operator: ${key}`);\n }\n }\n\n return result;\n }\n\n /**\n * Expands relative $row policies into absolute index policies for each physical row.\n */\n private expandRowPolicies(data: unknown, policies: IPolicy[]): IPolicy[] {\n const expanded: IPolicy[] = [];\n for (const policy of policies) {\n let hasRow = false;\n let arrayPath = \"\";\n \n for (const target of policy.targets) {\n if (target.includes(\"$row\")) {\n hasRow = true;\n arrayPath = target.split(\"/$row\")[0];\n break;\n }\n }\n if (!hasRow && policy.condition) {\n const checkPaths = (obj: any) => {\n if (!obj) return;\n for (const key of Object.keys(obj)) {\n if (key.includes(\"$row\")) {\n hasRow = true;\n arrayPath = key.split(\"/$row\")[0];\n break;\n }\n }\n };\n checkPaths(policy.condition.NumericGreaterThan);\n checkPaths(policy.condition.NumericLessThan);\n checkPaths(policy.condition.StringEquals);\n \n if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => { if (p.includes(\"$row\")) { hasRow = true; arrayPath = p.split(\"/$row\")[0]; } });\n if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => { if (p.includes(\"$row\")) { hasRow = true; arrayPath = p.split(\"/$row\")[0]; } });\n }\n\n if (hasRow && arrayPath) {\n const arrayData = this.resolvePointer(data, arrayPath);\n if (Array.isArray(arrayData)) {\n for (let i = 0; i < arrayData.length; i++) {\n const cloneStr = JSON.stringify(policy).replace(/\\$row/g, String(i));\n expanded.push(JSON.parse(cloneStr));\n }\n }\n } else {\n expanded.push(policy);\n }\n }\n return expanded;\n }\n\n /**\n * Evaluates policies against the given data payload using a State Resolution Matrix.\n * @param data The JSON data payload.\n * @returns Array of delta states to apply to the UI.\n */\n public evaluate(data: unknown): IDeltaState[] {\n if (!this.policies || this.policies.length === 0) {\n return [];\n }\n\n const expandedPolicies = this.expandRowPolicies(data, this.policies);\n\n // Ledger: target -> property -> { forcedFalse: number, forcedTrue: number, lastMessage?: string }\n const ledger = new Map<string, Record<PropertyName, { forcedFalse: number, forcedTrue: number, lastMessage?: string }>>();\n\n const initTargetInLedger = (target: string) => {\n if (!ledger.has(target)) {\n ledger.set(target, {\n visibility: { forcedFalse: 0, forcedTrue: 0 },\n validity: { forcedFalse: 0, forcedTrue: 0 },\n required: { forcedFalse: 0, forcedTrue: 0 },\n editable: { forcedFalse: 0, forcedTrue: 0 }\n });\n }\n };\n\n // 1. Build the Composite State Ledger Matrix\n for (const policy of expandedPolicies) {\n const isConditionMet = this.evaluateCondition(policy.condition, data);\n \n for (const target of policy.targets) {\n initTargetInLedger(target);\n const currentLedger = ledger.get(target)!;\n \n const effectPlugin = PluginRegistry.getInstance().getPolicyEffectPlugin(policy.effect);\n if (!effectPlugin) {\n Logger.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${policy.effect}`);\n continue;\n }\n const mapped = effectPlugin.resolveState(isConditionMet, policy.effect);\n \n // Track forces. For restrictable properties (visibility, validity, editable), forcedFalse acts as a lock.\n // For required, forcedTrue acts as a lock.\n if (mapped.value) {\n currentLedger[mapped.property].forcedTrue++;\n } else {\n currentLedger[mapped.property].forcedFalse++;\n if (mapped.property === \"validity\" && isConditionMet && policy.message) {\n currentLedger.validity.lastMessage = policy.message;\n }\n }\n }\n }\n\n const newStateMatrix = new Map<string, ITargetState>();\n\n // 2. Resolve the Ledger into a concrete State Matrix\n for (const [target, stateMap] of ledger.entries()) {\n newStateMatrix.set(target, {\n // False (Hidden) wins if ANY policy forced it false\n visibility: stateMap.visibility.forcedFalse > 0 ? false : true,\n // False (Invalid) wins if ANY policy forced it false\n validity: stateMap.validity.forcedFalse > 0 ? false : true,\n // True (Required) wins if ANY policy forced it true\n required: stateMap.required.forcedTrue > 0 ? true : false,\n // False (Disabled) wins if ANY policy forced it false\n editable: stateMap.editable.forcedFalse > 0 ? false : true,\n errorMessage: stateMap.validity.lastMessage\n });\n }\n\n const deltas: IDeltaState[] = [];\n\n // 3. Differential Emission\n for (const [target, newState] of newStateMatrix.entries()) {\n const lastState = this.lastStateMatrix.get(target) || {};\n\n if (newState.visibility !== undefined && newState.visibility !== lastState.visibility) {\n deltas.push({ target, property: \"visibility\", value: newState.visibility });\n }\n if (newState.validity !== undefined && newState.validity !== lastState.validity) {\n if (newState.validity !== lastState.validity || newState.errorMessage !== lastState.errorMessage) {\n deltas.push({ target, property: \"validity\", value: newState.validity, errorMessage: newState.errorMessage });\n }\n }\n if (newState.required !== undefined && newState.required !== lastState.required) {\n deltas.push({ target, property: \"required\", value: newState.required });\n }\n if (newState.editable !== undefined && newState.editable !== lastState.editable) {\n deltas.push({ target, property: \"editable\", value: newState.editable });\n }\n }\n\n // Cache for next evaluation sweep\n this.lastStateMatrix = newStateMatrix;\n\n return deltas;\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"PolicyEngine.js","names":["PluginRegistry","___PluginRegistry","Logger","___utils_Logger","PolicyEngine","constructor","policies","this","lastStateMatrix","Map","resolvePointer","data","pointer","undefined","cleanPointer","replace","parts","split","current","part","evaluateCondition","condition","result","registry","getInstance","key","payload","Object","entries","plugin","getPolicyConditionPlugin","evaluate","bind","warn","expandRowPolicies","expanded","policy","policyStr","JSON","stringify","includes","match","arrayPath","arrayData","Array","isArray","i","length","cloneStr","String","push","parse","expandedPolicies","ledger","initTargetInLedger","target","has","set","visibility","forcedFalse","forcedTrue","validity","required","editable","isConditionMet","targets","currentLedger","get","effectPlugin","getPolicyEffectPlugin","effect","error","mapped","resolveState","value","property","message","lastMessage","newStateMatrix","stateMap","errorMessage","deltas","newState","lastState","__exports","__esModule"],"sources":["PolicyEngine.ts"],"sourcesContent":["import { IPolicy, IPolicyCondition, PolicyEffectType } from \"../interfaces/ISchema\";\nimport { PluginRegistry } from \"./PluginRegistry\";\nimport { Logger } from \"../utils/Logger\";\n\nexport type PropertyName = \"visibility\" | \"validity\" | \"required\" | \"editable\";\n\nexport interface IDeltaState {\n /** The absolute JSON pointer path of the target property (e.g., \"/VisitorInfo/PassportNumber\") */\n target: string;\n /** The UI5 visual or structural state to mutate */\n property: PropertyName;\n /** The boolean value to apply to the state */\n value: boolean;\n /** An optional custom error message to paint onto the field if validity is false */\n errorMessage?: string;\n}\n\ninterface ITargetState {\n visibility?: boolean;\n validity?: boolean;\n required?: boolean;\n editable?: boolean;\n errorMessage?: string;\n}\n\n/**\n * The PolicyEngine is responsible for evaluating complex, cross-field conditional logic defined in the schema's `uiPolicies` array.\n * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost\n * then applies to the individual active plugins.\n * \n * \n * @namespace nz.co.siliconst.ui5.metaui.core\n */\nexport class PolicyEngine {\n private policies: IPolicy[];\n\n /** Caches the last computed state matrix for differential delta emission */\n private lastStateMatrix: Map<string, ITargetState>;\n\n /**\n * Instantiates the PolicyEngine with an array of policies.\n * @param policies Array of uiPolicies defined in the root schema.\n */\n constructor(policies: IPolicy[]) {\n this.policies = policies || [];\n this.lastStateMatrix = new Map();\n }\n\n /**\n * Safely traverses a JSON payload using a JSON pointer.\n * Handles UI5 model prefixes (like metaui>) seamlessly.\n * \n * @param data The root JSON object representing the form payload.\n * @param pointer The JSON pointer to traverse (e.g., \"/Customer/Age\").\n * @returns The resolved value, or undefined if the path does not exist.\n */\n private resolvePointer(data: unknown, pointer: string): unknown {\n if (!data || !pointer) return undefined;\n // Strip leading slash and any UI5 prefixes\n const cleanPointer = pointer.replace(/^\\//, \"\").replace(/^metaui>\\/?/, \"\");\n if (!cleanPointer) return data;\n\n const parts = cleanPointer.split(\"/\");\n let current = data as Record<string, unknown>;\n for (const part of parts) {\n if (current === undefined || current === null) return undefined;\n current = current[part] as Record<string, unknown>;\n }\n return current;\n }\n\n /**\n * Evaluates a complex condition tree against the live JSON payload.\n * \n * @param condition The condition block to evaluate.\n * @param data The live JSON payload from the StateManager.\n * @returns True if all condition parameters are met, otherwise false.\n */\n private evaluateCondition(condition: IPolicyCondition, data: unknown): boolean {\n // If there are multiple operators, we treat them as AND for now\n let result = true;\n const registry = PluginRegistry.getInstance();\n\n for (const [key, payload] of Object.entries(condition)) {\n const plugin = registry.getPolicyConditionPlugin(key);\n if (plugin) {\n if (!plugin.evaluate(payload, data, this.resolvePointer.bind(this))) {\n result = false;\n }\n } else {\n Logger.warn(`[MetaUI PolicyEngine] Unrecognized condition operator: ${key}`);\n }\n }\n\n return result;\n }\n\n /**\n * Expands relative $row policies into absolute index policies for each physical row.\n * Uses generic stringification to decouple from specific condition operator plugins.\n */\n private expandRowPolicies(data: unknown, policies: IPolicy[]): IPolicy[] {\n const expanded: IPolicy[] = [];\n for (const policy of policies) {\n const policyStr = JSON.stringify(policy);\n if (policyStr.includes(\"$row\")) {\n // Extract the array path (everything before \"/$row\" inside a JSON string value)\n const match = policyStr.match(/\"([^\"]+)\\/\\$row/);\n if (match) {\n const arrayPath = match[1];\n const arrayData = this.resolvePointer(data, arrayPath);\n if (Array.isArray(arrayData)) {\n for (let i = 0; i < arrayData.length; i++) {\n const cloneStr = policyStr.replace(/\\$row/g, String(i));\n expanded.push(JSON.parse(cloneStr));\n }\n }\n } else {\n // Fallback if path couldn't be parsed\n expanded.push(policy);\n }\n } else {\n expanded.push(policy);\n }\n }\n return expanded;\n }\n\n /**\n * Evaluates policies against the given data payload using a State Resolution Matrix.\n * @param data The JSON data payload.\n * @returns Array of delta states to apply to the UI.\n */\n public evaluate(data: unknown): IDeltaState[] {\n if (!this.policies || this.policies.length === 0) {\n return [];\n }\n\n const expandedPolicies = this.expandRowPolicies(data, this.policies);\n\n // Ledger: target -> property -> { forcedFalse: number, forcedTrue: number, lastMessage?: string }\n const ledger = new Map<string, Record<PropertyName, { forcedFalse: number, forcedTrue: number, lastMessage?: string }>>();\n\n const initTargetInLedger = (target: string) => {\n if (!ledger.has(target)) {\n ledger.set(target, {\n visibility: { forcedFalse: 0, forcedTrue: 0 },\n validity: { forcedFalse: 0, forcedTrue: 0 },\n required: { forcedFalse: 0, forcedTrue: 0 },\n editable: { forcedFalse: 0, forcedTrue: 0 }\n });\n }\n };\n\n // 1. Build the Composite State Ledger Matrix\n for (const policy of expandedPolicies) {\n const isConditionMet = this.evaluateCondition(policy.condition, data);\n\n for (const target of policy.targets) {\n initTargetInLedger(target);\n const currentLedger = ledger.get(target)!;\n\n const effectPlugin = PluginRegistry.getInstance().getPolicyEffectPlugin(policy.effect);\n if (!effectPlugin) {\n Logger.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${policy.effect}`);\n continue;\n }\n const mapped = effectPlugin.resolveState(isConditionMet, policy.effect);\n\n // Track forces. For restrictable properties (visibility, validity, editable), forcedFalse acts as a lock.\n // For required, forcedTrue acts as a lock.\n if (mapped.value) {\n currentLedger[mapped.property].forcedTrue++;\n } else {\n currentLedger[mapped.property].forcedFalse++;\n if (mapped.property === \"validity\" && isConditionMet && policy.message) {\n currentLedger.validity.lastMessage = policy.message;\n }\n }\n }\n }\n\n const newStateMatrix = new Map<string, ITargetState>();\n\n // 2. Resolve the Ledger into a concrete State Matrix\n for (const [target, stateMap] of ledger.entries()) {\n newStateMatrix.set(target, {\n // False (Hidden) wins if ANY policy forced it false\n visibility: stateMap.visibility.forcedFalse > 0 ? false : true,\n // False (Invalid) wins if ANY policy forced it false\n validity: stateMap.validity.forcedFalse > 0 ? false : true,\n // True (Required) wins if ANY policy forced it true\n required: stateMap.required.forcedTrue > 0 ? true : false,\n // False (Disabled) wins if ANY policy forced it false\n editable: stateMap.editable.forcedFalse > 0 ? false : true,\n errorMessage: stateMap.validity.lastMessage\n });\n }\n\n const deltas: IDeltaState[] = [];\n\n // 3. Differential Emission\n for (const [target, newState] of newStateMatrix.entries()) {\n const lastState = this.lastStateMatrix.get(target) || {};\n\n if (newState.visibility !== undefined && newState.visibility !== lastState.visibility) {\n deltas.push({ target, property: \"visibility\", value: newState.visibility });\n }\n if (newState.validity !== undefined && newState.validity !== lastState.validity) {\n if (newState.validity !== lastState.validity || newState.errorMessage !== lastState.errorMessage) {\n deltas.push({ target, property: \"validity\", value: newState.validity, errorMessage: newState.errorMessage });\n }\n }\n if (newState.required !== undefined && newState.required !== lastState.required) {\n deltas.push({ target, property: \"required\", value: newState.required });\n }\n if (newState.editable !== undefined && newState.editable !== lastState.editable) {\n deltas.push({ target, property: \"editable\", value: newState.editable });\n }\n }\n\n // Cache for next evaluation sweep\n this.lastStateMatrix = newStateMatrix;\n\n return deltas;\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -28,14 +28,12 @@ interface ITargetState {
28
28
  * It operates strictly on the JSON payload structure and returns an array of `IDeltaState` mutations which the GeneratorHost
29
29
  * then applies to the individual active plugins.
30
30
  *
31
- * It ensures the orchestration of conditional rendering, requiring, and invalidating of fields without the plugins
32
- * needing to be aware of each other.
33
31
  *
34
32
  * @namespace nz.co.siliconst.ui5.metaui.core
35
33
  */
36
34
  export class PolicyEngine {
37
35
  private policies: IPolicy[];
38
-
36
+
39
37
  /** Caches the last computed state matrix for differential delta emission */
40
38
  private lastStateMatrix: Map<string, ITargetState>;
41
39
 
@@ -46,65 +44,6 @@ export class PolicyEngine {
46
44
  constructor(policies: IPolicy[]) {
47
45
  this.policies = policies || [];
48
46
  this.lastStateMatrix = new Map();
49
- this.detectCycles();
50
- }
51
-
52
- /**
53
- * Builds a directed dependency graph and detects cycles to prevent infinite event loops.
54
- */
55
- private detectCycles(): void {
56
- const graph = new Map<string, Set<string>>();
57
-
58
- for (const policy of this.policies) {
59
- const sources = new Set<string>();
60
- if (policy.condition) {
61
- const addPaths = (obj: Record<string, unknown> | undefined) => {
62
- if (obj) Object.keys(obj).forEach(k => sources.add(k.split("/$row")[0]));
63
- };
64
- addPaths(policy.condition.NumericGreaterThan);
65
- addPaths(policy.condition.NumericLessThan);
66
- addPaths(policy.condition.StringEquals);
67
- if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => sources.add(p.split("/$row")[0]));
68
- if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => sources.add(p.split("/$row")[0]));
69
- }
70
-
71
- for (const target of policy.targets) {
72
- const cleanTarget = target.split("/$row")[0];
73
- if (!graph.has(cleanTarget)) {
74
- graph.set(cleanTarget, new Set());
75
- }
76
- sources.forEach(s => graph.get(cleanTarget)!.add(s));
77
- }
78
- }
79
-
80
- const visited = new Set<string>();
81
- const recursionStack = new Set<string>();
82
-
83
- const isCyclic = (node: string): boolean => {
84
- if (!visited.has(node)) {
85
- visited.add(node);
86
- recursionStack.add(node);
87
-
88
- const neighbors = graph.get(node);
89
- if (neighbors) {
90
- for (const neighbor of neighbors) {
91
- if (!visited.has(neighbor) && isCyclic(neighbor)) {
92
- return true;
93
- } else if (recursionStack.has(neighbor)) {
94
- return true;
95
- }
96
- }
97
- }
98
- }
99
- recursionStack.delete(node);
100
- return false;
101
- };
102
-
103
- for (const node of graph.keys()) {
104
- if (isCyclic(node)) {
105
- throw new Error(`[MetaUI PolicyEngine] FATAL: Cyclic dependency detected involving policy target '${node}'. This will cause an infinite rendering loop.`);
106
- }
107
- }
108
47
  }
109
48
 
110
49
  /**
@@ -120,7 +59,7 @@ export class PolicyEngine {
120
59
  // Strip leading slash and any UI5 prefixes
121
60
  const cleanPointer = pointer.replace(/^\//, "").replace(/^metaui>\/?/, "");
122
61
  if (!cleanPointer) return data;
123
-
62
+
124
63
  const parts = cleanPointer.split("/");
125
64
  let current = data as Record<string, unknown>;
126
65
  for (const part of parts) {
@@ -158,46 +97,27 @@ export class PolicyEngine {
158
97
 
159
98
  /**
160
99
  * Expands relative $row policies into absolute index policies for each physical row.
100
+ * Uses generic stringification to decouple from specific condition operator plugins.
161
101
  */
162
102
  private expandRowPolicies(data: unknown, policies: IPolicy[]): IPolicy[] {
163
103
  const expanded: IPolicy[] = [];
164
104
  for (const policy of policies) {
165
- let hasRow = false;
166
- let arrayPath = "";
167
-
168
- for (const target of policy.targets) {
169
- if (target.includes("$row")) {
170
- hasRow = true;
171
- arrayPath = target.split("/$row")[0];
172
- break;
173
- }
174
- }
175
- if (!hasRow && policy.condition) {
176
- const checkPaths = (obj: any) => {
177
- if (!obj) return;
178
- for (const key of Object.keys(obj)) {
179
- if (key.includes("$row")) {
180
- hasRow = true;
181
- arrayPath = key.split("/$row")[0];
182
- break;
105
+ const policyStr = JSON.stringify(policy);
106
+ if (policyStr.includes("$row")) {
107
+ // Extract the array path (everything before "/$row" inside a JSON string value)
108
+ const match = policyStr.match(/"([^"]+)\/\$row/);
109
+ if (match) {
110
+ const arrayPath = match[1];
111
+ const arrayData = this.resolvePointer(data, arrayPath);
112
+ if (Array.isArray(arrayData)) {
113
+ for (let i = 0; i < arrayData.length; i++) {
114
+ const cloneStr = policyStr.replace(/\$row/g, String(i));
115
+ expanded.push(JSON.parse(cloneStr));
183
116
  }
184
117
  }
185
- };
186
- checkPaths(policy.condition.NumericGreaterThan);
187
- checkPaths(policy.condition.NumericLessThan);
188
- checkPaths(policy.condition.StringEquals);
189
-
190
- if (policy.condition.IsNull) policy.condition.IsNull.forEach(p => { if (p.includes("$row")) { hasRow = true; arrayPath = p.split("/$row")[0]; } });
191
- if (policy.condition.IsNotNull) policy.condition.IsNotNull.forEach(p => { if (p.includes("$row")) { hasRow = true; arrayPath = p.split("/$row")[0]; } });
192
- }
193
-
194
- if (hasRow && arrayPath) {
195
- const arrayData = this.resolvePointer(data, arrayPath);
196
- if (Array.isArray(arrayData)) {
197
- for (let i = 0; i < arrayData.length; i++) {
198
- const cloneStr = JSON.stringify(policy).replace(/\$row/g, String(i));
199
- expanded.push(JSON.parse(cloneStr));
200
- }
118
+ } else {
119
+ // Fallback if path couldn't be parsed
120
+ expanded.push(policy);
201
121
  }
202
122
  } else {
203
123
  expanded.push(policy);
@@ -235,18 +155,18 @@ export class PolicyEngine {
235
155
  // 1. Build the Composite State Ledger Matrix
236
156
  for (const policy of expandedPolicies) {
237
157
  const isConditionMet = this.evaluateCondition(policy.condition, data);
238
-
158
+
239
159
  for (const target of policy.targets) {
240
160
  initTargetInLedger(target);
241
161
  const currentLedger = ledger.get(target)!;
242
-
162
+
243
163
  const effectPlugin = PluginRegistry.getInstance().getPolicyEffectPlugin(policy.effect);
244
164
  if (!effectPlugin) {
245
165
  Logger.error(`[MetaUI PolicyEngine] Unrecognized policy effect: ${policy.effect}`);
246
166
  continue;
247
167
  }
248
168
  const mapped = effectPlugin.resolveState(isConditionMet, policy.effect);
249
-
169
+
250
170
  // Track forces. For restrictable properties (visibility, validity, editable), forcedFalse acts as a lock.
251
171
  // For required, forcedTrue acts as a lock.
252
172
  if (mapped.value) {
@@ -1 +1 @@
1
- {"version":3,"file":"ISchema-dbg.js","names":[],"sources":["ISchema.ts"],"sourcesContent":["/**\n * @file ISchema.ts\n * @description MetaUI v2 Core Schema Contract.\n * Aligned with standard JSON-Schema syntax (properties, items) for native AI generation,\n * with a proprietary 'ui' directive block for Fiori visual orchestration.\n */\n\nimport { RenderModeType, SchemaTypeType } from \"../constants/MetaUIConstants\";\n\nexport type FieldType = SchemaTypeType | \"date\";\n\n/**\n * Value help structure mapping SAP Key/Text pairs.\n */\nexport interface IValueHelp {\n key: string;\n text: string;\n}\n\nexport interface IValidationRule {\n name: string;\n args?: unknown;\n}\n\n/**\n * An element in the visual layout tree.\n */\nexport interface ILayoutElement {\n type: \"Group\" | \"Control\" | \"HorizontalLayout\" | \"VerticalLayout\" | \"WizardStep\";\n label?: string;\n scope?: string; // JSON Pointer to property, e.g. \"#/properties/FirstName\"\n elements?: ILayoutElement[];\n widget?: string; // Optional override for the widget\n}\n\n/**\n * Visual orchestration directives to separate layout from data modeling.\n */\nexport interface IUIDirective {\n label?: string;\n isKey?: boolean;\n readOnly?: boolean;\n widget?: string;\n visibleOn?: string;\n enabledOn?: string;\n format?: string;\n rows?: number;\n expandable?: boolean;\n fullWidth?: boolean;\n controlProps?: Record<string, unknown>;\n validators?: (string | IValidationRule)[];\n formatter?: string;\n args?: unknown;\n dialogButtonText?: string;\n layoutBudget?: number;\n renderMode?: RenderModeType;\n}\n\nexport interface IRemoteValueHelpConfig {\n url: string;\n keyPath: string;\n textPath: string;\n}\n\nexport type PolicyEffectType = \"Require\" | \"Hide\" | \"Disable\" | \"Invalidate\" | \"Show\" | \"Enable\" | \"Validate\" | \"Optional\";\n\nexport interface IPolicyCondition {\n NumericGreaterThan?: Record<string, number>;\n NumericLessThan?: Record<string, number>;\n StringEquals?: Record<string, string>;\n DateLessThan?: Record<string, string>;\n IsNull?: string[];\n IsNotNull?: string[];\n [operator: string]: unknown; // Extensible for other operators\n}\n\nexport interface IPolicy {\n effect: PolicyEffectType;\n targets: string[];\n condition: IPolicyCondition;\n message?: string;\n}\n\n/**\n * Standard JSON-Schema property definition.\n */\nexport interface IPropertyMetadata {\n type: FieldType;\n $ref?: string;\n \n // MetaUI specific orchestration\n ui?: IUIDirective;\n \n // Standard validations\n required?: boolean;\n maxLength?: number;\n minLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n \n // Numeric specifics\n precision?: number;\n scale?: number;\n multipleOf?: number;\n \n // Value constraints\n valueHelp?: IValueHelp[] | IRemoteValueHelpConfig;\n enum?: string[] | number[];\n \n // OpenAPI Advanced features\n default?: unknown;\n nullable?: boolean;\n writeOnly?: boolean;\n readOnly?: boolean;\n example?: unknown;\n deprecated?: boolean;\n exclusiveMinimum?: boolean | number;\n exclusiveMaximum?: boolean | number;\n \n // Array Validation\n maxItems?: number;\n minItems?: number;\n uniqueItems?: boolean;\n\n // Object Validation\n maxProperties?: number;\n minProperties?: number;\n\n // Polymorphism and Composition\n oneOf?: IPropertyMetadata[];\n anyOf?: IPropertyMetadata[];\n allOf?: IPropertyMetadata[];\n not?: IPropertyMetadata;\n discriminator?: { propertyName: string, mapping?: Record<string, string> };\n \n // Nested recursion for objects and arrays\n properties?: Record<string, IPropertyMetadata>;\n items?: IPropertyMetadata;\n uiLayout?: ILayoutElement[];\n additionalProperties?: boolean | IPropertyMetadata;\n}\n\n/**\n * The master schema contract ingested by the Engine.\n */\nexport interface ISchema {\n title?: string;\n uiPolicies?: IPolicy[];\n layoutStrategy?: string;\n type?: \"object\" | \"array\"; // Implicit layout hinting\n properties?: Record<string, IPropertyMetadata>;\n items?: IPropertyMetadata;\n uiLayout?: ILayoutElement[];\n additionalProperties?: boolean;\n definitions?: Record<string, ISchema>;\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"ISchema-dbg.js","names":[],"sources":["ISchema.ts"],"sourcesContent":["/**\n * @file ISchema.ts\n * @description MetaUI v2 Core Schema Contract.\n * Aligned with standard JSON-Schema syntax (properties, items) for native AI generation,\n * with a proprietary 'ui' directive block for Fiori visual orchestration.\n */\n\nimport { RenderModeType, SchemaTypeType } from \"../constants/MetaUIConstants\";\n\nexport type FieldType = SchemaTypeType | \"date\";\n\n/**\n * Value help structure mapping SAP Key/Text pairs.\n */\nexport interface IValueHelp {\n key: string;\n text: string;\n}\n\nexport interface IValidationRule {\n name: string;\n args?: unknown;\n}\n\n/**\n * An element in the visual layout tree.\n */\nexport interface ILayoutElement {\n type: \"Group\" | \"Control\" | \"HorizontalLayout\" | \"VerticalLayout\" | \"WizardStep\";\n label?: string;\n scope?: string; // JSON Pointer to property, e.g. \"#/properties/FirstName\"\n elements?: ILayoutElement[];\n widget?: string; // Optional override for the widget\n}\n\n/**\n * Visual orchestration directives to separate layout from data modeling.\n */\nexport interface IUIDirective {\n label?: string;\n isKey?: boolean;\n readOnly?: boolean;\n widget?: string;\n visibleOn?: string;\n enabledOn?: string;\n format?: string;\n rows?: number;\n expandable?: boolean;\n fullWidth?: boolean;\n controlProps?: Record<string, unknown>;\n validators?: (string | IValidationRule)[];\n formatter?: string;\n args?: unknown;\n dialogButtonText?: string;\n layoutBudget?: number;\n renderMode?: RenderModeType;\n}\n\nexport interface IRemoteValueHelpConfig {\n url: string;\n keyPath: string;\n textPath: string;\n}\n\nexport type PolicyEffectType = \"Require\" | \"Hide\" | \"Disable\" | \"Invalidate\" | \"Show\" | \"Enable\" | \"Validate\" | \"Optional\";\n\nexport interface IPolicyCondition {\n NumericGreaterThan?: Record<string, number>;\n NumericLessThan?: Record<string, number>;\n StringEquals?: Record<string, string>;\n DateLessThan?: Record<string, string>;\n IsNull?: string[];\n IsNotNull?: string[];\n [operator: string]: unknown; // Extensible for other operators\n}\n\nexport interface IPolicy {\n effect: PolicyEffectType;\n targets: string[];\n condition: IPolicyCondition;\n message?: string;\n}\n\n/**\n * Standard JSON-Schema property definition.\n */\nexport interface IPropertyMetadata {\n type: FieldType;\n $ref?: string;\n \n // MetaUI specific orchestration\n ui?: IUIDirective;\n \n // Standard validations\n required?: boolean | string[];\n maxLength?: number;\n minLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n \n // Numeric specifics\n precision?: number;\n scale?: number;\n multipleOf?: number;\n \n // Value constraints\n valueHelp?: IValueHelp[] | IRemoteValueHelpConfig;\n enum?: string[] | number[];\n \n // OpenAPI Advanced features\n default?: unknown;\n nullable?: boolean;\n writeOnly?: boolean;\n readOnly?: boolean;\n example?: unknown;\n deprecated?: boolean;\n exclusiveMinimum?: boolean | number;\n exclusiveMaximum?: boolean | number;\n \n // Array Validation\n maxItems?: number;\n minItems?: number;\n uniqueItems?: boolean;\n\n // Object Validation\n maxProperties?: number;\n minProperties?: number;\n\n // Polymorphism and Composition\n oneOf?: IPropertyMetadata[];\n anyOf?: IPropertyMetadata[];\n allOf?: IPropertyMetadata[];\n not?: IPropertyMetadata;\n discriminator?: { propertyName: string, mapping?: Record<string, string> };\n \n // Nested recursion for objects and arrays\n properties?: Record<string, IPropertyMetadata>;\n items?: IPropertyMetadata;\n uiLayout?: ILayoutElement[];\n additionalProperties?: boolean | IPropertyMetadata;\n}\n\n/**\n * The master schema contract ingested by the Engine.\n */\nexport interface ISchema {\n title?: string;\n uiPolicies?: IPolicy[];\n layoutStrategy?: string;\n type?: \"object\" | \"array\"; // Implicit layout hinting\n properties?: Record<string, IPropertyMetadata>;\n items?: IPropertyMetadata;\n uiLayout?: ILayoutElement[];\n additionalProperties?: boolean;\n required?: string[];\n definitions?: Record<string, ISchema>;\n}\n"],"mappings":"","ignoreList":[]}
@@ -78,7 +78,7 @@ declare module "nz/co/siliconst/ui5/metaui/interfaces/ISchema" {
78
78
  type: FieldType;
79
79
  $ref?: string;
80
80
  ui?: IUIDirective;
81
- required?: boolean;
81
+ required?: boolean | string[];
82
82
  maxLength?: number;
83
83
  minLength?: number;
84
84
  minimum?: number;
@@ -127,6 +127,7 @@ declare module "nz/co/siliconst/ui5/metaui/interfaces/ISchema" {
127
127
  items?: IPropertyMetadata;
128
128
  uiLayout?: ILayoutElement[];
129
129
  additionalProperties?: boolean;
130
+ required?: string[];
130
131
  definitions?: Record<string, ISchema>;
131
132
  }
132
133
  }