@twin.org/rights-management-plugins 0.0.3-next.29 → 0.0.3-next.30

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.
Files changed (25) hide show
  1. package/dist/es/index.js +5 -2
  2. package/dist/es/index.js.map +1 -1
  3. package/dist/es/models/IAutomationPolicyExecutionActionConfig.js +4 -0
  4. package/dist/es/models/IAutomationPolicyExecutionActionConfig.js.map +1 -0
  5. package/dist/es/models/IAutomationPolicyExecutionActionConstructorOptions.js +2 -0
  6. package/dist/es/models/IAutomationPolicyExecutionActionConstructorOptions.js.map +1 -0
  7. package/dist/es/policyArbiters/defaultPolicyArbiter.js +232 -86
  8. package/dist/es/policyArbiters/defaultPolicyArbiter.js.map +1 -1
  9. package/dist/es/policyExecutionActions/automationPolicyExecutionAction.js +76 -0
  10. package/dist/es/policyExecutionActions/automationPolicyExecutionAction.js.map +1 -0
  11. package/dist/es/policyExecutionActions/loggingPolicyExecutionAction.js.map +1 -1
  12. package/dist/types/index.d.ts +5 -2
  13. package/dist/types/models/IAutomationPolicyExecutionActionConfig.d.ts +9 -0
  14. package/dist/types/models/IAutomationPolicyExecutionActionConstructorOptions.d.ts +15 -0
  15. package/dist/types/policyExecutionActions/automationPolicyExecutionAction.d.ts +49 -0
  16. package/dist/types/policyExecutionActions/loggingPolicyExecutionAction.d.ts +1 -1
  17. package/docs/changelog.md +77 -48
  18. package/docs/examples.md +0 -29
  19. package/docs/reference/classes/AutomationPolicyExecutionAction.md +143 -0
  20. package/docs/reference/classes/LoggingPolicyExecutionAction.md +1 -1
  21. package/docs/reference/index.md +3 -0
  22. package/docs/reference/interfaces/IAutomationPolicyExecutionActionConfig.md +11 -0
  23. package/docs/reference/interfaces/IAutomationPolicyExecutionActionConstructorOptions.md +25 -0
  24. package/locales/en.json +1 -0
  25. package/package.json +3 -2
package/dist/es/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // Copyright 2025 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- export * from "./models/IDefaultPolicyArbiterConstructorOptions.js";
3
+ export * from "./models/IAutomationPolicyExecutionActionConfig.js";
4
+ export * from "./models/IAutomationPolicyExecutionActionConstructorOptions.js";
4
5
  export * from "./models/IDefaultPolicyArbiterConfig.js";
6
+ export * from "./models/IDefaultPolicyArbiterConstructorOptions.js";
5
7
  export * from "./models/IDefaultPolicyEnforcementProcessorConstructorOptions.js";
6
8
  export * from "./models/IIdentityPolicyInformationSourceConstructorOptions.js";
7
9
  export * from "./models/ILoggingPolicyExecutionActionConfig.js";
@@ -18,10 +20,11 @@ export * from "./policyArbiters/defaultPolicyArbiter.js";
18
20
  export * from "./policyArbiters/passThroughPolicyArbiter.js";
19
21
  export * from "./policyEnforcementProcessor/defaultPolicyEnforcementProcessor.js";
20
22
  export * from "./policyEnforcementProcessor/passThroughPolicyEnforcementProcessor.js";
23
+ export * from "./policyExecutionActions/automationPolicyExecutionAction.js";
21
24
  export * from "./policyExecutionActions/loggingPolicyExecutionAction.js";
22
- export * from "./policyObligationEnforcers/passThroughPolicyObligationEnforcer.js";
23
25
  export * from "./policyInformationSources/identityPolicyInformationSource.js";
24
26
  export * from "./policyInformationSources/staticPolicyInformationSource.js";
25
27
  export * from "./policyNegotiators/passThroughPolicyNegotiator.js";
28
+ export * from "./policyObligationEnforcers/passThroughPolicyObligationEnforcer.js";
26
29
  export * from "./policyRequesters/passThroughPolicyRequester.js";
27
30
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,qDAAqD,CAAC;AACpE,cAAc,yCAAyC,CAAC;AACxD,cAAc,kEAAkE,CAAC;AACjF,cAAc,gEAAgE,CAAC;AAC/E,cAAc,iDAAiD,CAAC;AAChE,cAAc,6DAA6D,CAAC;AAC5E,cAAc,yDAAyD,CAAC;AACxE,cAAc,sEAAsE,CAAC;AACrF,cAAc,4DAA4D,CAAC;AAC3E,cAAc,oEAAoE,CAAC;AACnF,cAAc,2DAA2D,CAAC;AAC1E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,kDAAkD,CAAC;AACjE,cAAc,8DAA8D,CAAC;AAC7E,cAAc,0CAA0C,CAAC;AACzD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,mEAAmE,CAAC;AAClF,cAAc,uEAAuE,CAAC;AACtF,cAAc,0DAA0D,CAAC;AACzE,cAAc,oEAAoE,CAAC;AACnF,cAAc,+DAA+D,CAAC;AAC9E,cAAc,6DAA6D,CAAC;AAC5E,cAAc,oDAAoD,CAAC;AACnE,cAAc,kDAAkD,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/IDefaultPolicyArbiterConstructorOptions.js\";\nexport * from \"./models/IDefaultPolicyArbiterConfig.js\";\nexport * from \"./models/IDefaultPolicyEnforcementProcessorConstructorOptions.js\";\nexport * from \"./models/IIdentityPolicyInformationSourceConstructorOptions.js\";\nexport * from \"./models/ILoggingPolicyExecutionActionConfig.js\";\nexport * from \"./models/ILoggingPolicyExecutionActionConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyArbiterConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyEnforcementProcessorConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyNegotiatorConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyObligationEnforcerConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyRequesterConstructorOptions.js\";\nexport * from \"./models/IStaticPolicyInformationSource.js\";\nexport * from \"./models/IStaticPolicyInformationSourceConfig.js\";\nexport * from \"./models/IStaticPolicyInformationSourceConstructorOptions.js\";\nexport * from \"./policyArbiters/defaultPolicyArbiter.js\";\nexport * from \"./policyArbiters/passThroughPolicyArbiter.js\";\nexport * from \"./policyEnforcementProcessor/defaultPolicyEnforcementProcessor.js\";\nexport * from \"./policyEnforcementProcessor/passThroughPolicyEnforcementProcessor.js\";\nexport * from \"./policyExecutionActions/loggingPolicyExecutionAction.js\";\nexport * from \"./policyObligationEnforcers/passThroughPolicyObligationEnforcer.js\";\nexport * from \"./policyInformationSources/identityPolicyInformationSource.js\";\nexport * from \"./policyInformationSources/staticPolicyInformationSource.js\";\nexport * from \"./policyNegotiators/passThroughPolicyNegotiator.js\";\nexport * from \"./policyRequesters/passThroughPolicyRequester.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,oDAAoD,CAAC;AACnE,cAAc,gEAAgE,CAAC;AAC/E,cAAc,yCAAyC,CAAC;AACxD,cAAc,qDAAqD,CAAC;AACpE,cAAc,kEAAkE,CAAC;AACjF,cAAc,gEAAgE,CAAC;AAC/E,cAAc,iDAAiD,CAAC;AAChE,cAAc,6DAA6D,CAAC;AAC5E,cAAc,yDAAyD,CAAC;AACxE,cAAc,sEAAsE,CAAC;AACrF,cAAc,4DAA4D,CAAC;AAC3E,cAAc,oEAAoE,CAAC;AACnF,cAAc,2DAA2D,CAAC;AAC1E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,kDAAkD,CAAC;AACjE,cAAc,8DAA8D,CAAC;AAC7E,cAAc,0CAA0C,CAAC;AACzD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,mEAAmE,CAAC;AAClF,cAAc,uEAAuE,CAAC;AACtF,cAAc,6DAA6D,CAAC;AAC5E,cAAc,0DAA0D,CAAC;AACzE,cAAc,+DAA+D,CAAC;AAC9E,cAAc,6DAA6D,CAAC;AAC5E,cAAc,oDAAoD,CAAC;AACnE,cAAc,oEAAoE,CAAC;AACnF,cAAc,kDAAkD,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/IAutomationPolicyExecutionActionConfig.js\";\nexport * from \"./models/IAutomationPolicyExecutionActionConstructorOptions.js\";\nexport * from \"./models/IDefaultPolicyArbiterConfig.js\";\nexport * from \"./models/IDefaultPolicyArbiterConstructorOptions.js\";\nexport * from \"./models/IDefaultPolicyEnforcementProcessorConstructorOptions.js\";\nexport * from \"./models/IIdentityPolicyInformationSourceConstructorOptions.js\";\nexport * from \"./models/ILoggingPolicyExecutionActionConfig.js\";\nexport * from \"./models/ILoggingPolicyExecutionActionConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyArbiterConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyEnforcementProcessorConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyNegotiatorConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyObligationEnforcerConstructorOptions.js\";\nexport * from \"./models/IPassThroughPolicyRequesterConstructorOptions.js\";\nexport * from \"./models/IStaticPolicyInformationSource.js\";\nexport * from \"./models/IStaticPolicyInformationSourceConfig.js\";\nexport * from \"./models/IStaticPolicyInformationSourceConstructorOptions.js\";\nexport * from \"./policyArbiters/defaultPolicyArbiter.js\";\nexport * from \"./policyArbiters/passThroughPolicyArbiter.js\";\nexport * from \"./policyEnforcementProcessor/defaultPolicyEnforcementProcessor.js\";\nexport * from \"./policyEnforcementProcessor/passThroughPolicyEnforcementProcessor.js\";\nexport * from \"./policyExecutionActions/automationPolicyExecutionAction.js\";\nexport * from \"./policyExecutionActions/loggingPolicyExecutionAction.js\";\nexport * from \"./policyInformationSources/identityPolicyInformationSource.js\";\nexport * from \"./policyInformationSources/staticPolicyInformationSource.js\";\nexport * from \"./policyNegotiators/passThroughPolicyNegotiator.js\";\nexport * from \"./policyObligationEnforcers/passThroughPolicyObligationEnforcer.js\";\nexport * from \"./policyRequesters/passThroughPolicyRequester.js\";\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IAutomationPolicyExecutionActionConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IAutomationPolicyExecutionActionConfig.js","sourceRoot":"","sources":["../../../src/models/IAutomationPolicyExecutionActionConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Options for the Automation Policy Execution Action Component.\n */\nexport interface IAutomationPolicyExecutionActionConfig {\n\t/**\n\t * The policy decision stages to trigger the automation actions, if undefined defaults to \"inform\".\n\t */\n\ttriggerActions?: string[];\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IAutomationPolicyExecutionActionConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IAutomationPolicyExecutionActionConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IAutomationPolicyExecutionActionConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAutomationPolicyExecutionActionConfig } from \"./IAutomationPolicyExecutionActionConfig.js\";\n\n/**\n * Options for the Automation Policy Execution Action.\n */\nexport interface IAutomationPolicyExecutionActionConstructorOptions {\n\t/**\n\t * The automation component for executing automation policies.\n\t * @default automation\n\t */\n\tautomationComponentType?: string;\n\n\t/**\n\t * The configuration for the automation policy execution.\n\t */\n\tconfig?: IAutomationPolicyExecutionActionConfig;\n}\n"]}
@@ -24,20 +24,31 @@ export class DefaultPolicyArbiter {
24
24
  */
25
25
  static _DEFAULT_MAX_INHERITANCE_DEPTH = 10;
26
26
  /**
27
- * TWIN prefix operations.
27
+ * Datasource key for the primary JSON path data source.
28
28
  * @internal
29
29
  */
30
- static _TWIN_PREFIX_OPERATIONS = "twin:";
30
+ static _DATA_SOURCE_KEY = "data";
31
31
  /**
32
- * TWIN prefix JSONPath.
32
+ * Datasource key for the information source used in JSON path expressions.
33
33
  * @internal
34
34
  */
35
- static _TWIN_PREFIX_JSONPATH = "jsonpath";
35
+ static _INFORMATION_SOURCE_KEY = "information";
36
36
  /**
37
- * TWIN prefix information.
37
+ * TWIN prefix JSONPath canonical alias.
38
38
  * @internal
39
39
  */
40
- static _TWIN_PREFIX_INFORMATION = "information";
40
+ static _TWIN_JSONPATH = "twin:jsonPath";
41
+ /**
42
+ * Canonical jsonPath expression property.
43
+ * @internal
44
+ */
45
+ static _TWIN_JSONPATH_EXPRESSION = "twin:jsonPathExpression";
46
+ /**
47
+ * Optional data source key property for canonical twin:jsonPath targets.
48
+ * When absent, defaults to the primary data source.
49
+ * @internal
50
+ */
51
+ static _TWIN_JSONPATH_DATA_SOURCE = "twin:jsonPathDataSource";
41
52
  /**
42
53
  * The logging component.
43
54
  * @internal
@@ -141,8 +152,8 @@ export class DefaultPolicyArbiter {
141
152
  const mergedPolicy = await this.mergeInheritedPolicies(agreement);
142
153
  const expandedPolicy = this.expandCompactPolicyRules(mergedPolicy);
143
154
  const dataSources = {
144
- [`${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_JSONPATH}`]: data,
145
- [`${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_INFORMATION}`]: information
155
+ [DefaultPolicyArbiter._DATA_SOURCE_KEY]: data,
156
+ [DefaultPolicyArbiter._INFORMATION_SOURCE_KEY]: information
146
157
  };
147
158
  // Extract agreement parties once for use in rule evaluation
148
159
  const agreementAssigner = OdrlPolicyHelper.getPartyIds(agreement.assigner);
@@ -797,7 +808,7 @@ export class DefaultPolicyArbiter {
797
808
  */
798
809
  async enforceDuty(policy, duty, dataSources, ruleDataContext) {
799
810
  const enforcerNames = PolicyObligationEnforcerFactory.names();
800
- const information = dataSources[`${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_INFORMATION}`];
811
+ const information = dataSources[DefaultPolicyArbiter._INFORMATION_SOURCE_KEY];
801
812
  if (enforcerNames.length === 0) {
802
813
  throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "noObligationEnforcersRegistered");
803
814
  }
@@ -827,51 +838,70 @@ export class DefaultPolicyArbiter {
827
838
  * @internal
828
839
  */
829
840
  tryResolveTargetDataSource(targetId, dataSources, resolveValue = false) {
830
- // If there is no target id, default to the entire "twin:jsonpath" datasource
841
+ // If there is no target id, default to the data datasource
831
842
  if (Is.empty(targetId)) {
832
843
  return {
833
- prefix: `${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_JSONPATH}`,
834
- source: dataSources[`${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_JSONPATH}`],
844
+ source: dataSources[DefaultPolicyArbiter._DATA_SOURCE_KEY],
835
845
  target: "$",
836
- value: dataSources[`${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_JSONPATH}`]
846
+ value: dataSources[DefaultPolicyArbiter._DATA_SOURCE_KEY]
837
847
  };
838
848
  }
839
- // Otherwise lookup the target id prefix in the datasources and return the remaining suffix as the target path/key
840
- const prefixes = Object.keys(dataSources).sort((a, b) => b.length - a.length);
841
- for (const prefix of prefixes) {
842
- if (targetId.startsWith(`${prefix}:`)) {
843
- const source = dataSources[prefix];
844
- const target = targetId.slice(prefix.length + 1);
845
- if (!target.startsWith("$")) {
846
- throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "ruleTargetNotSupported", {
847
- target: targetId
848
- });
849
- }
850
- if (!resolveValue) {
851
- return {
852
- prefix,
853
- source,
854
- target
855
- };
856
- }
857
- const matches = JsonPathHelper.query(target, source);
858
- if (matches.length === 0) {
859
- throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "ruleTargetNotSupported", {
860
- target: targetId
861
- });
862
- }
863
- return {
864
- prefix,
865
- source,
866
- target,
867
- value: matches.length === 1 ? matches[0].value : matches.map(m => m.value)
868
- };
869
- }
849
+ // Handle twin:jsonPath:<datasource>:<expression> format.
850
+ // The datasource segment is optional; when absent the expression starts with "$"
851
+ // and falls back to the primary data source.
852
+ if (targetId.startsWith(`${DefaultPolicyArbiter._TWIN_JSONPATH}:`)) {
853
+ const rest = targetId.slice(DefaultPolicyArbiter._TWIN_JSONPATH.length + 1);
854
+ const matchingKey = Object.keys(dataSources).find(k => rest.startsWith(`${k}:`));
855
+ const sourceKey = matchingKey ?? DefaultPolicyArbiter._DATA_SOURCE_KEY;
856
+ const expression = matchingKey ? rest.slice(matchingKey.length + 1) : rest;
857
+ return this.resolveDataSourceByKey(sourceKey, expression, dataSources, resolveValue);
858
+ }
859
+ // Handle <datasource>:<expression> format
860
+ const matchingKey = Object.keys(dataSources).find(k => targetId.startsWith(`${k}:`));
861
+ if (matchingKey) {
862
+ const expression = targetId.slice(matchingKey.length + 1);
863
+ return this.resolveDataSourceByKey(matchingKey, expression, dataSources, resolveValue);
870
864
  }
871
865
  throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "ruleTargetNotSupported", {
872
866
  target: targetId
873
867
  });
874
868
  }
869
+ /**
870
+ * Resolve a datasource by key and evaluate a JSONPath expression against it.
871
+ * @param sourceKey The datasource key.
872
+ * @param expression The JSONPath expression.
873
+ * @param dataSources The available datasources.
874
+ * @param resolveValue Whether to evaluate the expression and return the matched value.
875
+ * @returns The resolved source, target expression, and optionally the matched value.
876
+ * @internal
877
+ */
878
+ resolveDataSourceByKey(sourceKey, expression, dataSources, resolveValue = false) {
879
+ if (!Is.stringValue(expression)) {
880
+ throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "jsonPathExpressionMissing", {
881
+ operand: "target"
882
+ });
883
+ }
884
+ if (!expression.startsWith("$")) {
885
+ throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "ruleTargetNotSupported", {
886
+ target: `${sourceKey}:${expression}`
887
+ });
888
+ }
889
+ const source = dataSources[sourceKey];
890
+ if (!resolveValue) {
891
+ return { source, target: expression };
892
+ }
893
+ const matches = JsonPathHelper.query(expression, source);
894
+ if (matches.length === 0) {
895
+ throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "ruleTargetNotSupported", {
896
+ target: `${sourceKey}:${expression}`
897
+ });
898
+ }
899
+ return {
900
+ source,
901
+ target: expression,
902
+ value: matches.length === 1 ? matches[0].value : matches.map(m => m.value)
903
+ };
904
+ }
875
905
  /**
876
906
  * Extract the target id from the permission.
877
907
  * @param target The permission target.
@@ -889,7 +919,23 @@ export class DefaultPolicyArbiter {
889
919
  if (arr.length === 0) {
890
920
  return undefined;
891
921
  }
892
- return Is.string(arr[0]) ? arr[0] : OdrlPolicyHelper.getUid(arr[0]);
922
+ const first = arr[0];
923
+ if (Is.string(first)) {
924
+ return first;
925
+ }
926
+ if (this.isTwinJsonPathTarget(first)) {
927
+ const t = first;
928
+ const expression = t[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION];
929
+ if (Is.stringValue(expression)) {
930
+ const dataSource = t[DefaultPolicyArbiter._TWIN_JSONPATH_DATA_SOURCE];
931
+ const sourceKey = Is.stringValue(dataSource)
932
+ ? dataSource
933
+ : DefaultPolicyArbiter._DATA_SOURCE_KEY;
934
+ return `${sourceKey}:${expression}`;
935
+ }
936
+ return undefined;
937
+ }
938
+ return OdrlPolicyHelper.getUid(first);
893
939
  }
894
940
  /**
895
941
  * Resolve the target identifier used for rule data context lookup.
@@ -952,6 +998,14 @@ export class DefaultPolicyArbiter {
952
998
  throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "multipleTargetsNotSupported");
953
999
  }
954
1000
  const firstTarget = arr[0];
1001
+ if (this.isTwinJsonPathTarget(firstTarget)) {
1002
+ const t = firstTarget;
1003
+ const resolved = this.resolveDataSourceByKey(t[DefaultPolicyArbiter._TWIN_JSONPATH_DATA_SOURCE] ?? DefaultPolicyArbiter._DATA_SOURCE_KEY, t[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION], dataSources);
1004
+ return {
1005
+ target: resolved.target,
1006
+ refinements: []
1007
+ };
1008
+ }
955
1009
  if (Is.object(firstTarget)) {
956
1010
  // Guard against unsupported ODRL asset properties
957
1011
  if (Is.notEmpty(firstTarget.hasPolicy)) {
@@ -962,7 +1016,8 @@ export class DefaultPolicyArbiter {
962
1016
  }
963
1017
  if (Is.object(firstTarget) &&
964
1018
  OdrlPolicyHelper.getType(firstTarget) === OdrlTypes.AssetCollection) {
965
- if (!Is.stringValue(firstTarget.source)) {
1019
+ if (!Is.stringValue(firstTarget.source) ||
1020
+ !firstTarget.source.startsWith(`${DefaultPolicyArbiter._TWIN_JSONPATH}:`)) {
966
1021
  throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "assetCollectionSourceNotSupported", {
967
1022
  source: firstTarget.source ?? ""
968
1023
  });
@@ -1054,11 +1109,18 @@ export class DefaultPolicyArbiter {
1054
1109
  };
1055
1110
  }
1056
1111
  const regularConstraint = refinement;
1057
- return {
1112
+ const constraintWithExtensions = regularConstraint;
1113
+ const canonicalExpression = constraintWithExtensions[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION];
1114
+ const rewrittenConstraint = {
1058
1115
  ...regularConstraint,
1059
1116
  leftOperand: this.rewriteOperandForDecisionTarget(regularConstraint.leftOperand, sourceTarget, itemTarget),
1060
1117
  rightOperand: this.rewriteOperandForDecisionTarget(regularConstraint.rightOperand, sourceTarget, itemTarget)
1061
1118
  };
1119
+ if (Is.stringValue(canonicalExpression)) {
1120
+ rewrittenConstraint[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION] =
1121
+ this.rewriteWildcardPath(canonicalExpression, sourceTarget, itemTarget);
1122
+ }
1123
+ return rewrittenConstraint;
1062
1124
  }
1063
1125
  /**
1064
1126
  * Rewrite JSONPath-based operands from wildcard source to concrete item target.
@@ -1069,20 +1131,18 @@ export class DefaultPolicyArbiter {
1069
1131
  * @internal
1070
1132
  */
1071
1133
  rewriteOperandForDecisionTarget(operand, sourceTarget, itemTarget) {
1072
- if (Is.stringValue(operand)) {
1073
- const prefix = `${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_JSONPATH}:`;
1074
- if (operand.startsWith(prefix)) {
1075
- const valuePath = operand.slice(prefix.length);
1076
- return `${prefix}${this.rewriteWildcardPath(valuePath, sourceTarget, itemTarget)}`;
1077
- }
1078
- return operand;
1079
- }
1080
1134
  if (Is.object(operand)) {
1081
- const typedOperand = { ...operand };
1082
- if (typedOperand["@type"] ===
1083
- `${DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS}${DefaultPolicyArbiter._TWIN_PREFIX_JSONPATH}` &&
1084
- Is.stringValue(typedOperand["@value"])) {
1085
- typedOperand["@value"] = this.rewriteWildcardPath(typedOperand["@value"], sourceTarget, itemTarget);
1135
+ const typedOperand = {
1136
+ ...operand
1137
+ };
1138
+ if (this.isTwinJsonPathOperandType(typedOperand["@type"])) {
1139
+ if (Is.stringValue(typedOperand["@value"])) {
1140
+ typedOperand["@value"] = this.rewriteWildcardPath(typedOperand["@value"], sourceTarget, itemTarget);
1141
+ }
1142
+ const canonicalExpression = typedOperand[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION];
1143
+ if (Is.stringValue(canonicalExpression)) {
1144
+ typedOperand[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION] = this.rewriteWildcardPath(canonicalExpression, sourceTarget, itemTarget);
1145
+ }
1086
1146
  }
1087
1147
  return typedOperand;
1088
1148
  }
@@ -1150,8 +1210,8 @@ export class DefaultPolicyArbiter {
1150
1210
  throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "constraintStatusNotSupported");
1151
1211
  }
1152
1212
  // Evaluate the main constraint condition
1153
- const leftValue = this.calculateOperandValue(regularConstraint.leftOperand, dataSources);
1154
- const rightValue = this.calculateOperandValue(regularConstraint.rightOperand, dataSources);
1213
+ const leftValue = this.calculateOperandValue(regularConstraint.leftOperand, dataSources, regularConstraint);
1214
+ const rightValue = this.calculateOperandValue(regularConstraint.rightOperand, dataSources, regularConstraint);
1155
1215
  const mainSatisfied = this.evaluateOperator(regularConstraint.operator, leftValue, rightValue);
1156
1216
  // If main constraint is not satisfied, the overall constraint fails
1157
1217
  return mainSatisfied;
@@ -1276,27 +1336,12 @@ export class DefaultPolicyArbiter {
1276
1336
  * @internal
1277
1337
  */
1278
1338
  tryResolveOperandLookup(operandTypeOrValue, operandValue, dataSources) {
1279
- let lookupTargetId;
1280
- if (dataSources[operandTypeOrValue]) {
1281
- if (!Is.stringValue(operandValue)) {
1282
- return undefined;
1283
- }
1284
- lookupTargetId = `${operandTypeOrValue}:${operandValue}`;
1285
- }
1286
- else if (operandTypeOrValue.includes(":")) {
1287
- lookupTargetId = operandTypeOrValue;
1288
- }
1289
- else {
1339
+ const sourceKey = this.normalizeTwinJsonPathOperandAlias(operandTypeOrValue);
1340
+ if (!dataSources[sourceKey] || !Is.stringValue(operandValue)) {
1290
1341
  return undefined;
1291
1342
  }
1292
- // Delegate prefixed path matching to shared datasource resolver
1293
- if (operandTypeOrValue.startsWith(DefaultPolicyArbiter._TWIN_PREFIX_OPERATIONS)) {
1294
- const resolved = this.tryResolveTargetDataSource(lookupTargetId, dataSources);
1295
- return {
1296
- source: resolved.source,
1297
- jsonPath: resolved.target
1298
- };
1299
- }
1343
+ const resolved = this.resolveDataSourceByKey(sourceKey, operandValue, dataSources);
1344
+ return { source: resolved.source, jsonPath: resolved.target };
1300
1345
  }
1301
1346
  /**
1302
1347
  * Calculate an operand value.
@@ -1305,24 +1350,42 @@ export class DefaultPolicyArbiter {
1305
1350
  * @returns The resolved operand value.
1306
1351
  * @internal
1307
1352
  */
1308
- calculateOperandValue(operand, dataSources) {
1353
+ calculateOperandValue(operand, dataSources, constraint) {
1309
1354
  // Treat prefixed operands as selectors against a namespaced source dictionary.
1310
1355
  // Examples: twin:jsonpath:$.field, twin:information:$.credentials.level
1311
1356
  let jsonPath;
1312
1357
  let operandRoot;
1313
1358
  if (Is.stringValue(operand)) {
1314
- const lookup = this.tryResolveOperandLookup(operand, operand, dataSources);
1359
+ const expression = this.extractJsonPathExpressionFromConstraint(constraint, operand);
1360
+ let resolvedOperand = operand;
1361
+ if (operand === DefaultPolicyArbiter._TWIN_JSONPATH &&
1362
+ Is.object(constraint)) {
1363
+ const dataSourceOverride = constraint[DefaultPolicyArbiter._TWIN_JSONPATH_DATA_SOURCE];
1364
+ if (Is.stringValue(dataSourceOverride)) {
1365
+ resolvedOperand = dataSourceOverride;
1366
+ }
1367
+ }
1368
+ const lookup = this.tryResolveOperandLookup(resolvedOperand, expression ?? operand, dataSources);
1315
1369
  if (lookup) {
1316
1370
  jsonPath = lookup.jsonPath;
1317
1371
  operandRoot = lookup.source;
1318
1372
  }
1319
1373
  }
1320
1374
  else if (Is.object(operand)) {
1375
+ const typedOperand = operand;
1321
1376
  // Is this an object { "@value": "18", "@type": "xsd:integer" } ?
1322
- const value = operand["@value"];
1323
- const type = operand["@type"];
1377
+ const value = this.extractJsonPathExpressionFromTypedOperand(typedOperand) ??
1378
+ typedOperand["@value"];
1379
+ const type = typedOperand["@type"];
1324
1380
  if (Is.stringValue(type)) {
1325
- const lookup = this.tryResolveOperandLookup(type, value, dataSources);
1381
+ let resolvedType = type;
1382
+ if (this.isTwinJsonPathOperandType(type)) {
1383
+ const dataSourceOverride = typedOperand[DefaultPolicyArbiter._TWIN_JSONPATH_DATA_SOURCE];
1384
+ if (Is.stringValue(dataSourceOverride)) {
1385
+ resolvedType = dataSourceOverride;
1386
+ }
1387
+ }
1388
+ const lookup = this.tryResolveOperandLookup(resolvedType, value, dataSources);
1326
1389
  if (lookup) {
1327
1390
  jsonPath = lookup.jsonPath;
1328
1391
  operandRoot = lookup.source;
@@ -1352,6 +1415,89 @@ export class DefaultPolicyArbiter {
1352
1415
  // Not JSON Path or object value so return as is
1353
1416
  return operand;
1354
1417
  }
1418
+ /**
1419
+ * Determine if a target object uses the canonical twin:jsonPath object format.
1420
+ * @param target The target value.
1421
+ * @returns True if the target is a canonical twin:jsonPath object.
1422
+ * @internal
1423
+ */
1424
+ isTwinJsonPathTarget(target) {
1425
+ return Is.object(target) && this.isTwinJsonPathOperandType(OdrlPolicyHelper.getType(target));
1426
+ }
1427
+ /**
1428
+ * Determine if the operand type is a jsonPath namespace.
1429
+ * @param type The operand type.
1430
+ * @returns True if the type is twin:jsonPath.
1431
+ * @internal
1432
+ */
1433
+ isTwinJsonPathOperandType(type) {
1434
+ return type === DefaultPolicyArbiter._TWIN_JSONPATH;
1435
+ }
1436
+ /**
1437
+ * Normalize canonical jsonPath alias to the legacy jsonpath namespace key.
1438
+ * @param operandTypeOrValue The raw operand type or value.
1439
+ * @returns The normalized operand type/value.
1440
+ * @internal
1441
+ */
1442
+ normalizeTwinJsonPathOperandAlias(operandTypeOrValue) {
1443
+ if (operandTypeOrValue === DefaultPolicyArbiter._TWIN_JSONPATH ||
1444
+ operandTypeOrValue.startsWith(`${DefaultPolicyArbiter._TWIN_JSONPATH}:`)) {
1445
+ const suffix = operandTypeOrValue.slice(DefaultPolicyArbiter._TWIN_JSONPATH.length);
1446
+ return `${DefaultPolicyArbiter._DATA_SOURCE_KEY}${suffix}`;
1447
+ }
1448
+ return operandTypeOrValue;
1449
+ }
1450
+ /**
1451
+ * Extract canonical jsonPath expression from typed operand object.
1452
+ * @param operand The typed operand.
1453
+ * @returns The jsonPath expression if present.
1454
+ * @internal
1455
+ */
1456
+ extractJsonPathExpressionFromTypedOperand(operand) {
1457
+ if (!this.isTwinJsonPathOperandType(operand["@type"])) {
1458
+ return undefined;
1459
+ }
1460
+ const expression = operand[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION];
1461
+ if (Is.stringValue(expression)) {
1462
+ return expression;
1463
+ }
1464
+ const type = operand["@type"];
1465
+ if (type === DefaultPolicyArbiter._TWIN_JSONPATH) {
1466
+ throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "jsonPathExpressionMissing", {
1467
+ operand: "rightOperand"
1468
+ });
1469
+ }
1470
+ }
1471
+ /**
1472
+ * Extract canonical jsonPath expression from constraint for leftOperand aliases.
1473
+ * @param constraint The constraint containing the left operand.
1474
+ * @param leftOperand The left operand.
1475
+ * @returns The expression if canonical form is used.
1476
+ * @internal
1477
+ */
1478
+ extractJsonPathExpressionFromConstraint(constraint, leftOperand) {
1479
+ if (!Is.object(constraint)) {
1480
+ return undefined;
1481
+ }
1482
+ if (constraint.leftOperand !== leftOperand) {
1483
+ return undefined;
1484
+ }
1485
+ if (leftOperand.startsWith(`${DefaultPolicyArbiter._TWIN_JSONPATH}:`)) {
1486
+ throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "jsonPathExpressionMissing", {
1487
+ operand: "leftOperand"
1488
+ });
1489
+ }
1490
+ if (leftOperand !== DefaultPolicyArbiter._TWIN_JSONPATH) {
1491
+ return undefined;
1492
+ }
1493
+ const expression = constraint[DefaultPolicyArbiter._TWIN_JSONPATH_EXPRESSION];
1494
+ if (Is.stringValue(expression)) {
1495
+ return expression;
1496
+ }
1497
+ throw new GeneralError(DefaultPolicyArbiter.CLASS_NAME, "jsonPathExpressionMissing", {
1498
+ operand: "leftOperand"
1499
+ });
1500
+ }
1355
1501
  /**
1356
1502
  * Evaluate an ODRL operator against resolved operands.
1357
1503
  * @param operator The operator.