@olane/o-node 0.7.6 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAIV,MAAM,EAEP,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,qBAAa,eAAgB,SAAQ,WAAW;IAGlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAFrD,aAAa,EAAE,UAAU,CAAC;gBAEF,MAAM,EAAE,qBAAqB;IAKtD,IAAI,CAAC,MAAM,EAAE,MAAM;IAWzB,QAAQ;IAOF,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAuC/C,KAAK;CAKZ"}
1
+ {"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAIV,MAAM,EAEP,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,qBAAa,eAAgB,SAAQ,WAAW;IAGlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAFrD,aAAa,EAAE,UAAU,CAAC;gBAEF,MAAM,EAAE,qBAAqB;IAKtD,IAAI,CAAC,MAAM,EAAE,MAAM;IAWzB,QAAQ;IAOF,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAyC/C,KAAK;CAKZ"}
@@ -25,6 +25,7 @@ export class oNodeConnection extends oConnection {
25
25
  try {
26
26
  const stream = await this.p2pConnection.newStream(this.nextHopAddress.protocol, {
27
27
  maxOutboundStreams: Infinity,
28
+ runOnLimitedConnection: true, // TODO: should this be configurable?
28
29
  });
29
30
  if (!stream || (stream.status !== 'open' && stream.status !== 'reset')) {
30
31
  throw new oError(oErrorCodes.FAILED_TO_DIAL_TARGET, 'Failed to dial target');
@@ -35,6 +36,7 @@ export class oNodeConnection extends oConnection {
35
36
  // Send the data
36
37
  await stream.send(new TextEncoder().encode(request.toString()));
37
38
  const res = await this.read(stream);
39
+ await stream.close();
38
40
  // process the response
39
41
  const response = new oResponse({
40
42
  ...res.result,
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,EACN,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAKL,QAAQ,EAET,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAGnF,OAAO,EAAmB,SAAS,EAAE,MAAM,eAAe,CAAC;AAI3D,qBAAa,KAAM,SAAQ,SAAS;IAC3B,MAAM,EAAG,MAAM,CAAC;IAChB,OAAO,EAAG,MAAM,CAAC;IACjB,OAAO,EAAG,YAAY,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAG,sBAAsB,CAAC;IAC3C,gBAAgB,EAAG,qBAAqB,CAAC;IAChD,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;gBAE3B,MAAM,EAAE,WAAW;IAK/B,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAEhC;IAED,IAAI,aAAa,IAAI,YAAY,CAKhC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAOhC;IAED,mBAAmB,IAAI,GAAG,EAAE;IAItB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC,IAAI,aAAa,IAAI,YAAY,CAEhC;IAED,IAAI,gBAAgB,IAAI,cAAc,EAAE,CAEvC;IAED,IAAI,UAAU,IAAI,cAAc,EAAE,CAIjC;IAEK,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC/B,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;IAItC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAG1D;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;cA0FxB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAMvC,OAAO,CACX,cAAc,EAAE,YAAY,EAC5B,aAAa,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC;IA0BrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAOhC"}
1
+ {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,EACN,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAKL,QAAQ,EAET,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAGnF,OAAO,EAAmB,SAAS,EAAE,MAAM,eAAe,CAAC;AAI3D,qBAAa,KAAM,SAAQ,SAAS;IAC3B,MAAM,EAAG,MAAM,CAAC;IAChB,OAAO,EAAG,MAAM,CAAC;IACjB,OAAO,EAAG,YAAY,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAG,sBAAsB,CAAC;IAC3C,gBAAgB,EAAG,qBAAqB,CAAC;IAChD,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;gBAE3B,MAAM,EAAE,WAAW;IAK/B,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAEhC;IAED,IAAI,aAAa,IAAI,YAAY,CAKhC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAOhC;IAED,mBAAmB,IAAI,GAAG,EAAE;IAItB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC,IAAI,aAAa,IAAI,YAAY,CAEhC;IAED,IAAI,gBAAgB,IAAI,cAAc,EAAE,CAEvC;IAED,IAAI,UAAU,IAAI,cAAc,EAAE,CAIjC;IAEK,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC/B,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;IAItC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAG1D;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;cA0FxB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAMvC,OAAO,CACX,cAAc,EAAE,YAAY,EAC5B,aAAa,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC;IA0BrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAOhC"}
@@ -80,6 +80,7 @@ export class oNode extends oToolBase {
80
80
  return;
81
81
  }
82
82
  // if no parent transports, register with the parent to get them
83
+ // TODO: should we remove the transports check to make this more consistent?
83
84
  if (this.config.parent && this.config.parent.transports.length === 0) {
84
85
  this.logger.debug('Registering node with parent...', this.config.parent);
85
86
  const parentRegistration = await this.use(this.config.parent, {
@@ -3,10 +3,43 @@ import { oRouterRequest, RouteResponse } from '@olane/o-core';
3
3
  import type { oNode } from '../o-node.js';
4
4
  import { oToolRouter } from '@olane/o-tool';
5
5
  export declare class oNodeRouter extends oToolRouter {
6
+ private routingPolicy;
6
7
  constructor();
8
+ /**
9
+ * Forwards a request to the specified address via libp2p transport.
10
+ * Handles self-routing (local execution) and destination detection.
11
+ *
12
+ * @param address The next hop address to forward to
13
+ * @param request The router request to forward
14
+ * @param node The current node context
15
+ * @returns The response from the forwarded request
16
+ */
7
17
  protected forward(address: oNodeAddress, request: oRouterRequest, node: oNode): Promise<any>;
8
- private handleExternalAddress;
18
+ /**
19
+ * Executes a request locally when routing to self.
20
+ */
21
+ private executeSelfRouting;
22
+ /**
23
+ * Checks if the next hop is the final destination address.
24
+ */
25
+ private isDestinationAddress;
26
+ /**
27
+ * Unwraps the routing envelope when we've reached the destination.
28
+ */
29
+ private unwrapDestinationRequest;
30
+ /**
31
+ * Dials a remote node and transmits the request via libp2p.
32
+ */
33
+ private dialAndTransmit;
34
+ /**
35
+ * Translates an address to determine the next hop and target addresses.
36
+ * First checks routing policy for external routing, then applies resolver chain.
37
+ */
9
38
  translate(address: oNodeAddress, node: oNode): Promise<RouteResponse>;
39
+ /**
40
+ * Determines if an address is internal to this node's hierarchy.
41
+ * Delegates to the routing policy for the decision.
42
+ */
10
43
  isInternal(addressWithTransports: oNodeAddress, node: oNode): boolean;
11
44
  }
12
45
  //# sourceMappingURL=o-node.router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.router.d.ts","sourceRoot":"","sources":["../../../src/router/o-node.router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAML,cAAc,EACd,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,qBAAa,WAAY,SAAQ,WAAW;;cAK1B,OAAO,CACrB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,GAAG,CAAC;IA0Ef,OAAO,CAAC,qBAAqB;IAsBvB,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC;IAqB3E,UAAU,CAAC,qBAAqB,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;CAetE"}
1
+ {"version":3,"file":"o-node.router.d.ts","sourceRoot":"","sources":["../../../src/router/o-node.router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAML,cAAc,EACd,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK5C,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,aAAa,CAAqB;;IAO1C;;;;;;;;OAQG;cACa,OAAO,CACrB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,GAAG,CAAC;IA0Bf;;OAEG;YACW,kBAAkB;IAgBhC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;OAEG;YACW,eAAe;IA2B7B;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC;IAyB3E;;;OAGG;IACH,UAAU,CAAC,qBAAqB,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;CAGtE"}
@@ -1,62 +1,89 @@
1
- import { oNodeAddress } from './o-node.address.js';
2
- import { CoreUtils, oAddress, oError, oErrorCodes, oRequest, } from '@olane/o-core';
1
+ import { oAddress, oError, oErrorCodes, oRequest, } from '@olane/o-core';
3
2
  import { oToolRouter } from '@olane/o-tool';
4
3
  import { oNodeConnection } from '../connection/o-node-connection.js';
4
+ import { oNodeRoutingPolicy } from './o-node.routing-policy.js';
5
5
  export class oNodeRouter extends oToolRouter {
6
6
  constructor() {
7
7
  super();
8
+ this.routingPolicy = new oNodeRoutingPolicy();
8
9
  }
10
+ /**
11
+ * Forwards a request to the specified address via libp2p transport.
12
+ * Handles self-routing (local execution) and destination detection.
13
+ *
14
+ * @param address The next hop address to forward to
15
+ * @param request The router request to forward
16
+ * @param node The current node context
17
+ * @returns The response from the forwarded request
18
+ */
9
19
  async forward(address, request, node) {
10
20
  if (!request.stream) {
11
21
  throw new oError(oErrorCodes.INVALID_REQUEST, 'Stream is required');
12
22
  }
13
- const stream = request.stream;
14
23
  let nextHopRequest = new oRequest({
15
24
  method: request.method,
16
25
  params: request.params,
17
26
  id: request.id,
18
27
  });
19
- // are we dialing self?
20
- if (node.address.toString() === address.toString()) {
21
- const { payload } = request.params;
22
- const params = payload.params;
23
- nextHopRequest = new oRequest({
24
- method: payload.method,
25
- params: {
26
- ...params,
27
- },
28
- id: request.id,
29
- });
30
- const result = await node.execute(nextHopRequest);
31
- const response = CoreUtils.buildResponse(nextHopRequest, result, result?.error);
32
- // add the request method to the response
33
- return response;
28
+ // Handle self-routing: execute locally instead of dialing
29
+ if (this.routingPolicy.isSelfAddress(address, node)) {
30
+ return this.executeSelfRouting(request, node);
34
31
  }
35
- // next hop is the destination address
36
- if (address
37
- ?.toStaticAddress()
38
- .equals(new oAddress(request?.params?.address).toStaticAddress())) {
39
- // reached destination, so change from route to actual request
40
- const { payload } = request.params;
41
- const params = payload.params;
42
- nextHopRequest = new oRequest({
43
- method: payload.method,
44
- params: {
45
- ...params,
46
- },
47
- id: request.id,
48
- });
32
+ // Check if we've reached the final destination
33
+ const isDestination = this.isDestinationAddress(address, request);
34
+ if (isDestination) {
35
+ nextHopRequest = this.unwrapDestinationRequest(request);
49
36
  }
50
- // dial the target
37
+ // Dial and transmit to remote node
38
+ return this.dialAndTransmit(address, nextHopRequest, node);
39
+ }
40
+ /**
41
+ * Executes a request locally when routing to self.
42
+ */
43
+ async executeSelfRouting(request, node) {
44
+ const { payload } = request.params;
45
+ const params = payload.params;
46
+ const localRequest = new oRequest({
47
+ method: payload.method,
48
+ params: { ...params },
49
+ id: request.id,
50
+ });
51
+ const result = await node.execute(localRequest);
52
+ return result;
53
+ }
54
+ /**
55
+ * Checks if the next hop is the final destination address.
56
+ */
57
+ isDestinationAddress(address, request) {
58
+ return address
59
+ ?.toStaticAddress()
60
+ .equals(new oAddress(request?.params?.address).toStaticAddress());
61
+ }
62
+ /**
63
+ * Unwraps the routing envelope when we've reached the destination.
64
+ */
65
+ unwrapDestinationRequest(request) {
66
+ const { payload } = request.params;
67
+ const params = payload.params;
68
+ return new oRequest({
69
+ method: payload.method,
70
+ params: { ...params },
71
+ id: request.id,
72
+ });
73
+ }
74
+ /**
75
+ * Dials a remote node and transmits the request via libp2p.
76
+ */
77
+ async dialAndTransmit(address, request, node) {
51
78
  try {
52
- const connection = await node?.p2pNode.dial(address.libp2pTransports.map((t) => t.toMultiaddr()));
79
+ const connection = await node.p2pNode.dial(address.libp2pTransports.map((t) => t.toMultiaddr()));
53
80
  const nodeConnection = new oNodeConnection({
54
81
  p2pConnection: connection,
55
82
  nextHopAddress: address,
56
83
  address: node.address,
57
84
  callerAddress: node.address,
58
85
  });
59
- const response = await nodeConnection.transmit(nextHopRequest);
86
+ const response = await nodeConnection.transmit(request);
60
87
  return response.result.data;
61
88
  }
62
89
  catch (error) {
@@ -66,26 +93,17 @@ export class oNodeRouter extends oToolRouter {
66
93
  throw error;
67
94
  }
68
95
  }
69
- handleExternalAddress(address, node) {
70
- // determine if this is external
71
- const isInternal = this.isInternal(address, node);
72
- if (!isInternal) {
73
- // external address, so we need to route
74
- this.logger.debug('Address is external, routing...', address);
75
- // route to leader of external OS
76
- return {
77
- nextHopAddress: new oNodeAddress(oAddress.leader().toString(), address.libp2pTransports),
78
- targetAddress: address,
79
- };
80
- }
81
- return null;
82
- }
96
+ /**
97
+ * Translates an address to determine the next hop and target addresses.
98
+ * First checks routing policy for external routing, then applies resolver chain.
99
+ */
83
100
  async translate(address, node) {
84
- const externalRoute = this.handleExternalAddress(address, node);
101
+ // Check if external routing is needed
102
+ const externalRoute = this.routingPolicy.getExternalRoutingStrategy(address, node);
85
103
  if (externalRoute) {
86
- this.logger.debug('External route found', externalRoute);
87
104
  return externalRoute;
88
105
  }
106
+ // Apply resolver chain for internal routing
89
107
  const { nextHopAddress, targetAddress, requestOverride } = await this.addressResolution.resolve({
90
108
  address,
91
109
  node,
@@ -97,14 +115,11 @@ export class oNodeRouter extends oToolRouter {
97
115
  requestOverride,
98
116
  };
99
117
  }
118
+ /**
119
+ * Determines if an address is internal to this node's hierarchy.
120
+ * Delegates to the routing policy for the decision.
121
+ */
100
122
  isInternal(addressWithTransports, node) {
101
- if (addressWithTransports.paths.indexOf(oAddress.leader().paths) !== -1 && // if the address has a leader
102
- addressWithTransports.libp2pTransports?.length > 0) {
103
- // transports are provided, let's see if they match our known leaders
104
- const isLeaderRef = addressWithTransports.toString() === oAddress.leader().toString();
105
- const isOurLeaderRef = node.hierarchyManager.leaders.some((l) => l.equals(addressWithTransports));
106
- return isLeaderRef || isOurLeaderRef;
107
- }
108
- return true;
123
+ return this.routingPolicy.isInternalAddress(addressWithTransports, node);
109
124
  }
110
125
  }
@@ -0,0 +1,30 @@
1
+ import { oAddress, oRoutingPolicy, RouteResponse } from '@olane/o-core';
2
+ import type { oNode } from '../o-node.js';
3
+ /**
4
+ * Routing policy implementation for oNode that handles internal/external routing decisions
5
+ * and leader-based routing strategies.
6
+ */
7
+ export declare class oNodeRoutingPolicy extends oRoutingPolicy {
8
+ /**
9
+ * Determines if an address is internal to the current node's hierarchy.
10
+ * An address is considered internal if:
11
+ * 1. It doesn't have leader references, OR
12
+ * 2. Its leader references match our known leaders
13
+ *
14
+ * @param address The address to check
15
+ * @param node The current node context
16
+ * @returns True if the address is internal to this hierarchy
17
+ */
18
+ isInternalAddress(address: oAddress, node: oNode): boolean;
19
+ /**
20
+ * Determines the routing strategy for external addresses.
21
+ * External addresses are routed to the leader of the external OS using
22
+ * the address's libp2p transports.
23
+ *
24
+ * @param address The address to evaluate
25
+ * @param node The current node context
26
+ * @returns RouteResponse if external routing is needed, null if internal
27
+ */
28
+ getExternalRoutingStrategy(address: oAddress, node: oNode): RouteResponse | null;
29
+ }
30
+ //# sourceMappingURL=o-node.routing-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"o-node.routing-policy.d.ts","sourceRoot":"","sources":["../../../src/router/o-node.routing-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAG1C;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,cAAc;IACpD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;IAkB1D;;;;;;;;OAQG;IACH,0BAA0B,CACxB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,KAAK,GACV,aAAa,GAAG,IAAI;CAuBxB"}
@@ -0,0 +1,52 @@
1
+ import { oAddress, oRoutingPolicy } from '@olane/o-core';
2
+ import { oNodeAddress } from './o-node.address.js';
3
+ /**
4
+ * Routing policy implementation for oNode that handles internal/external routing decisions
5
+ * and leader-based routing strategies.
6
+ */
7
+ export class oNodeRoutingPolicy extends oRoutingPolicy {
8
+ /**
9
+ * Determines if an address is internal to the current node's hierarchy.
10
+ * An address is considered internal if:
11
+ * 1. It doesn't have leader references, OR
12
+ * 2. Its leader references match our known leaders
13
+ *
14
+ * @param address The address to check
15
+ * @param node The current node context
16
+ * @returns True if the address is internal to this hierarchy
17
+ */
18
+ isInternalAddress(address, node) {
19
+ const nodeAddress = address;
20
+ if (nodeAddress.paths.indexOf(oAddress.leader().paths) !== -1 && // if the address has a leader
21
+ nodeAddress.libp2pTransports?.length > 0) {
22
+ // transports are provided, let's see if they match our known leaders
23
+ const isLeaderRef = nodeAddress.toString() === oAddress.leader().toString();
24
+ const isOurLeaderRef = node.hierarchyManager.leaders.some((l) => l.equals(nodeAddress));
25
+ return isLeaderRef || isOurLeaderRef;
26
+ }
27
+ return true;
28
+ }
29
+ /**
30
+ * Determines the routing strategy for external addresses.
31
+ * External addresses are routed to the leader of the external OS using
32
+ * the address's libp2p transports.
33
+ *
34
+ * @param address The address to evaluate
35
+ * @param node The current node context
36
+ * @returns RouteResponse if external routing is needed, null if internal
37
+ */
38
+ getExternalRoutingStrategy(address, node) {
39
+ const nodeAddress = address;
40
+ const isInternal = this.isInternalAddress(address, node);
41
+ if (!isInternal) {
42
+ // external address, so we need to route
43
+ this.logger.debug('Address is external, routing...', nodeAddress.toString(), nodeAddress.libp2pTransports.map((t) => t.toString()));
44
+ // route to leader of external OS
45
+ return {
46
+ nextHopAddress: new oNodeAddress(oAddress.leader().toString(), nodeAddress.libp2pTransports),
47
+ targetAddress: nodeAddress,
48
+ };
49
+ }
50
+ return null;
51
+ }
52
+ }
@@ -1,8 +1,170 @@
1
- import { oAddress, oAddressResolver, oTransport, ResolveRequest, RouteResponse } from '@olane/o-core';
1
+ import { oAddress, oAddressResolver, oCore, oTransport, ResolveRequest, RouteResponse } from '@olane/o-core';
2
+ import { oNodeTransport } from '../o-node.transport.js';
3
+ /**
4
+ * Address resolver that searches a registry to find transports for addresses.
5
+ *
6
+ * This resolver queries a registry service to find the transport information
7
+ * for addresses that don't already have transports configured. It's designed
8
+ * to be extensible through subclassing via protected template methods.
9
+ *
10
+ * @example Basic usage
11
+ * ```typescript
12
+ * const resolver = new oSearchResolver(nodeAddress);
13
+ * router.addResolver(resolver);
14
+ * ```
15
+ *
16
+ * @example Creating a custom search resolver
17
+ * ```typescript
18
+ * class CustomSearchResolver extends oSearchResolver {
19
+ * // Search a different registry
20
+ * protected getRegistryAddress(): oAddress {
21
+ * return new oAddress('o://my-custom-registry');
22
+ * }
23
+ *
24
+ * // Use round-robin selection instead of first result
25
+ * private currentIndex = 0;
26
+ * protected selectResult(results: any[]): any | null {
27
+ * if (results.length === 0) return null;
28
+ * const result = results[this.currentIndex % results.length];
29
+ * this.currentIndex++;
30
+ * return result;
31
+ * }
32
+ *
33
+ * // Add custom filtering logic
34
+ * protected filterSearchResults(results: any[], node: oCore): any[] {
35
+ * return super.filterSearchResults(results, node).filter(
36
+ * result => result.status === 'active'
37
+ * );
38
+ * }
39
+ *
40
+ * // Implement custom routing logic with transport setup
41
+ * protected determineNextHop(
42
+ * node: oCore,
43
+ * resolvedTargetAddress: oAddress,
44
+ * searchResult: any
45
+ * ): oAddress {
46
+ * // Always route directly to target, bypassing hierarchy
47
+ * const targetTransports = this.mapTransports(searchResult);
48
+ * resolvedTargetAddress.setTransports(targetTransports);
49
+ * return resolvedTargetAddress;
50
+ * }
51
+ * }
52
+ * ```
53
+ *
54
+ * ## Extension Points
55
+ *
56
+ * The following protected methods can be overridden to customize behavior:
57
+ *
58
+ * - `getRegistryAddress()` - Change which registry to search
59
+ * - `getSearchMethod()` - Change the registry method to call
60
+ * - `buildSearchParams()` - Customize search parameters
61
+ * - `filterSearchResults()` - Add custom filtering logic
62
+ * - `selectResult()` - Implement custom result selection (e.g., load balancing)
63
+ * - `mapTransports()` - Customize how transports are mapped from results
64
+ * - `determineNextHop()` - Implement custom routing logic to determine the next hop address
65
+ * - `resolveNextHopTransports()` - Customize transport resolution for the next hop
66
+ */
2
67
  export declare class oSearchResolver extends oAddressResolver {
3
68
  protected readonly address: oAddress;
4
69
  constructor(address: oAddress);
5
70
  get customTransports(): oTransport[];
71
+ /**
72
+ * Returns the address of the registry to search.
73
+ * Override this method to search a different registry.
74
+ * @returns The registry address to query
75
+ */
76
+ protected getRegistryAddress(): oAddress;
77
+ /**
78
+ * Returns the method name to call on the registry.
79
+ * Override this method to use a different search method.
80
+ * @returns The method name to call
81
+ */
82
+ protected getSearchMethod(): string;
83
+ /**
84
+ * Builds the search parameters for the registry query.
85
+ * Override this method to customize search parameters.
86
+ * @param address - The address being resolved
87
+ * @returns Parameters to pass to the registry search method
88
+ */
89
+ protected buildSearchParams(address: oAddress): any;
90
+ /**
91
+ * Filters the search results from the registry.
92
+ * Override this method to apply custom filtering logic.
93
+ * @param results - Raw results from the registry
94
+ * @param node - The current node context
95
+ * @returns Filtered array of results
96
+ */
97
+ protected filterSearchResults(results: any[], node: oCore): any[];
98
+ /**
99
+ * Selects which result to use from the filtered results.
100
+ * Override this method to implement custom selection logic (e.g., load balancing).
101
+ * @param results - Filtered search results
102
+ * @returns The selected result, or null if no suitable result
103
+ */
104
+ protected selectResult(results: any[]): any | null;
105
+ /**
106
+ * Maps the transport data from the search result to oNodeTransport instances.
107
+ * Override this method to customize transport mapping.
108
+ * @param result - The selected search result
109
+ * @returns Array of oNodeTransport instances
110
+ */
111
+ protected mapTransports(result: any): oNodeTransport[];
112
+ /**
113
+ * Resolves the transports for the next hop address.
114
+ * This handles the logic of determining which transports to use based on
115
+ * whether the next hop is the leader, a known child, or a new target.
116
+ *
117
+ * Override this method to customize transport resolution logic.
118
+ *
119
+ * @param nextHop - The next hop address
120
+ * @param targetTransports - The transports from the registry search result
121
+ * @param node - The current node context
122
+ * @returns Array of transports to use for the next hop
123
+ */
124
+ protected resolveNextHopTransports(nextHop: oAddress, targetTransports: oNodeTransport[], node: oCore): oNodeTransport[];
125
+ /**
126
+ * Determines the next hop address for routing to the target and sets up its transports.
127
+ *
128
+ * This method implements the complete routing logic including:
129
+ * 1. Determining the next address in the path (using `oAddress.next()`)
130
+ * 2. Mapping transports from the search result
131
+ * 3. Resolving and setting transports on the next hop address
132
+ * 4. Setting transports on the target address
133
+ *
134
+ * The default implementation:
135
+ * - Uses `oAddress.next()` for standard hierarchy-based routing
136
+ * - Maps transports from the registry search result
137
+ * - Resolves next hop transports based on leader/hierarchy
138
+ * - Configures both next hop and target with appropriate transports
139
+ *
140
+ * Override this method to implement custom routing logic, such as:
141
+ * - Direct peer-to-peer routing
142
+ * - Custom hierarchy traversal
143
+ * - Alternative transport selection strategies
144
+ * - Bypass leader for certain routes
145
+ *
146
+ * @param node - The current node context
147
+ * @param resolvedTargetAddress - The resolved target address to route to
148
+ * @param searchResult - The raw search result from the registry containing transport data
149
+ * @returns The next hop address with transports configured
150
+ *
151
+ * @example Custom direct routing
152
+ * ```typescript
153
+ * class DirectSearchResolver extends oSearchResolver {
154
+ * protected determineNextHop(
155
+ * node: oCore,
156
+ * resolvedTargetAddress: oAddress,
157
+ * searchResult: any
158
+ * ): oAddress {
159
+ * // Always route directly to the target, bypassing hierarchy
160
+ * const targetTransports = this.mapTransports(searchResult);
161
+ * resolvedTargetAddress.setTransports(targetTransports);
162
+ * return resolvedTargetAddress;
163
+ * }
164
+ * }
165
+ * ```
166
+ */
167
+ protected determineNextHop(node: oCore, resolvedTargetAddress: oAddress, searchResult: any): oAddress;
6
168
  resolve(request: ResolveRequest): Promise<RouteResponse>;
7
169
  }
8
170
  //# sourceMappingURL=o-node.search-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.search-resolver.d.ts","sourceRoot":"","sources":["../../../../src/router/resolvers/o-node.search-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,gBAAgB,EAEhB,UAAU,EACV,cAAc,EAEd,aAAa,EAEd,MAAM,eAAe,CAAC;AAGvB,qBAAa,eAAgB,SAAQ,gBAAgB;IACvC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ;gBAAjB,OAAO,EAAE,QAAQ;IAIhD,IAAI,gBAAgB,IAAI,UAAU,EAAE,CAEnC;IAEK,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAiE/D"}
1
+ {"version":3,"file":"o-node.search-resolver.d.ts","sourceRoot":"","sources":["../../../../src/router/resolvers/o-node.search-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,KAAK,EAEL,UAAU,EACV,cAAc,EAEd,aAAa,EAEd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,qBAAa,eAAgB,SAAQ,gBAAgB;IACvC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ;gBAAjB,OAAO,EAAE,QAAQ;IAIhD,IAAI,gBAAgB,IAAI,UAAU,EAAE,CAEnC;IAED;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,IAAI,QAAQ;IAIxC;;;;OAIG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;;;;OAKG;IACH,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,QAAQ,GAAG,GAAG;IAOnD;;;;;;OAMG;IACH,SAAS,CAAC,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,GAAG,GAAG,EAAE;IASjE;;;;;OAKG;IACH,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI;IAIlD;;;;;OAKG;IACH,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,GAAG,GAAG,cAAc,EAAE;IAOtD;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,wBAAwB,CAChC,OAAO,EAAE,QAAQ,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,IAAI,EAAE,KAAK,GACV,cAAc,EAAE;IAgBnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;IACH,SAAS,CAAC,gBAAgB,CACxB,IAAI,EAAE,KAAK,EACX,qBAAqB,EAAE,QAAQ,EAC/B,YAAY,EAAE,GAAG,GAChB,QAAQ;IAeL,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6D/D"}
@@ -1,5 +1,69 @@
1
1
  import { oAddress, oAddressResolver, oCustomTransport, RestrictedAddresses, } from '@olane/o-core';
2
2
  import { oNodeTransport } from '../o-node.transport.js';
3
+ /**
4
+ * Address resolver that searches a registry to find transports for addresses.
5
+ *
6
+ * This resolver queries a registry service to find the transport information
7
+ * for addresses that don't already have transports configured. It's designed
8
+ * to be extensible through subclassing via protected template methods.
9
+ *
10
+ * @example Basic usage
11
+ * ```typescript
12
+ * const resolver = new oSearchResolver(nodeAddress);
13
+ * router.addResolver(resolver);
14
+ * ```
15
+ *
16
+ * @example Creating a custom search resolver
17
+ * ```typescript
18
+ * class CustomSearchResolver extends oSearchResolver {
19
+ * // Search a different registry
20
+ * protected getRegistryAddress(): oAddress {
21
+ * return new oAddress('o://my-custom-registry');
22
+ * }
23
+ *
24
+ * // Use round-robin selection instead of first result
25
+ * private currentIndex = 0;
26
+ * protected selectResult(results: any[]): any | null {
27
+ * if (results.length === 0) return null;
28
+ * const result = results[this.currentIndex % results.length];
29
+ * this.currentIndex++;
30
+ * return result;
31
+ * }
32
+ *
33
+ * // Add custom filtering logic
34
+ * protected filterSearchResults(results: any[], node: oCore): any[] {
35
+ * return super.filterSearchResults(results, node).filter(
36
+ * result => result.status === 'active'
37
+ * );
38
+ * }
39
+ *
40
+ * // Implement custom routing logic with transport setup
41
+ * protected determineNextHop(
42
+ * node: oCore,
43
+ * resolvedTargetAddress: oAddress,
44
+ * searchResult: any
45
+ * ): oAddress {
46
+ * // Always route directly to target, bypassing hierarchy
47
+ * const targetTransports = this.mapTransports(searchResult);
48
+ * resolvedTargetAddress.setTransports(targetTransports);
49
+ * return resolvedTargetAddress;
50
+ * }
51
+ * }
52
+ * ```
53
+ *
54
+ * ## Extension Points
55
+ *
56
+ * The following protected methods can be overridden to customize behavior:
57
+ *
58
+ * - `getRegistryAddress()` - Change which registry to search
59
+ * - `getSearchMethod()` - Change the registry method to call
60
+ * - `buildSearchParams()` - Customize search parameters
61
+ * - `filterSearchResults()` - Add custom filtering logic
62
+ * - `selectResult()` - Implement custom result selection (e.g., load balancing)
63
+ * - `mapTransports()` - Customize how transports are mapped from results
64
+ * - `determineNextHop()` - Implement custom routing logic to determine the next hop address
65
+ * - `resolveNextHopTransports()` - Customize transport resolution for the next hop
66
+ */
3
67
  export class oSearchResolver extends oAddressResolver {
4
68
  constructor(address) {
5
69
  super(address);
@@ -8,8 +72,144 @@ export class oSearchResolver extends oAddressResolver {
8
72
  get customTransports() {
9
73
  return [new oCustomTransport('/search')];
10
74
  }
75
+ /**
76
+ * Returns the address of the registry to search.
77
+ * Override this method to search a different registry.
78
+ * @returns The registry address to query
79
+ */
80
+ getRegistryAddress() {
81
+ return new oAddress(RestrictedAddresses.REGISTRY);
82
+ }
83
+ /**
84
+ * Returns the method name to call on the registry.
85
+ * Override this method to use a different search method.
86
+ * @returns The method name to call
87
+ */
88
+ getSearchMethod() {
89
+ return 'search';
90
+ }
91
+ /**
92
+ * Builds the search parameters for the registry query.
93
+ * Override this method to customize search parameters.
94
+ * @param address - The address being resolved
95
+ * @returns Parameters to pass to the registry search method
96
+ */
97
+ buildSearchParams(address) {
98
+ return {
99
+ staticAddress: address.toRootAddress().toString(),
100
+ address: address.toString(),
101
+ };
102
+ }
103
+ /**
104
+ * Filters the search results from the registry.
105
+ * Override this method to apply custom filtering logic.
106
+ * @param results - Raw results from the registry
107
+ * @param node - The current node context
108
+ * @returns Filtered array of results
109
+ */
110
+ filterSearchResults(results, node) {
111
+ return results.filter(
112
+ // filter out the items that may cause infinite looping
113
+ (result) => result.staticAddress !== RestrictedAddresses.REGISTRY &&
114
+ result.address !== node.address);
115
+ }
116
+ /**
117
+ * Selects which result to use from the filtered results.
118
+ * Override this method to implement custom selection logic (e.g., load balancing).
119
+ * @param results - Filtered search results
120
+ * @returns The selected result, or null if no suitable result
121
+ */
122
+ selectResult(results) {
123
+ return results.length > 0 ? results[0] : null;
124
+ }
125
+ /**
126
+ * Maps the transport data from the search result to oNodeTransport instances.
127
+ * Override this method to customize transport mapping.
128
+ * @param result - The selected search result
129
+ * @returns Array of oNodeTransport instances
130
+ */
131
+ mapTransports(result) {
132
+ return result.transports.map((t) => new oNodeTransport(t.value));
133
+ }
134
+ /**
135
+ * Resolves the transports for the next hop address.
136
+ * This handles the logic of determining which transports to use based on
137
+ * whether the next hop is the leader, a known child, or a new target.
138
+ *
139
+ * Override this method to customize transport resolution logic.
140
+ *
141
+ * @param nextHop - The next hop address
142
+ * @param targetTransports - The transports from the registry search result
143
+ * @param node - The current node context
144
+ * @returns Array of transports to use for the next hop
145
+ */
146
+ resolveNextHopTransports(nextHop, targetTransports, node) {
147
+ // If next hop is the leader, use leader transports
148
+ if (nextHop.value === RestrictedAddresses.LEADER) {
149
+ return (node.leader?.transports || []);
150
+ }
151
+ // Check if next hop is a known child in the hierarchy
152
+ const childAddress = node?.hierarchyManager.getChild(nextHop);
153
+ if (childAddress?.transports) {
154
+ return childAddress.transports;
155
+ }
156
+ // Fall back to target transports from registry
157
+ return targetTransports;
158
+ }
159
+ /**
160
+ * Determines the next hop address for routing to the target and sets up its transports.
161
+ *
162
+ * This method implements the complete routing logic including:
163
+ * 1. Determining the next address in the path (using `oAddress.next()`)
164
+ * 2. Mapping transports from the search result
165
+ * 3. Resolving and setting transports on the next hop address
166
+ * 4. Setting transports on the target address
167
+ *
168
+ * The default implementation:
169
+ * - Uses `oAddress.next()` for standard hierarchy-based routing
170
+ * - Maps transports from the registry search result
171
+ * - Resolves next hop transports based on leader/hierarchy
172
+ * - Configures both next hop and target with appropriate transports
173
+ *
174
+ * Override this method to implement custom routing logic, such as:
175
+ * - Direct peer-to-peer routing
176
+ * - Custom hierarchy traversal
177
+ * - Alternative transport selection strategies
178
+ * - Bypass leader for certain routes
179
+ *
180
+ * @param node - The current node context
181
+ * @param resolvedTargetAddress - The resolved target address to route to
182
+ * @param searchResult - The raw search result from the registry containing transport data
183
+ * @returns The next hop address with transports configured
184
+ *
185
+ * @example Custom direct routing
186
+ * ```typescript
187
+ * class DirectSearchResolver extends oSearchResolver {
188
+ * protected determineNextHop(
189
+ * node: oCore,
190
+ * resolvedTargetAddress: oAddress,
191
+ * searchResult: any
192
+ * ): oAddress {
193
+ * // Always route directly to the target, bypassing hierarchy
194
+ * const targetTransports = this.mapTransports(searchResult);
195
+ * resolvedTargetAddress.setTransports(targetTransports);
196
+ * return resolvedTargetAddress;
197
+ * }
198
+ * }
199
+ * ```
200
+ */
201
+ determineNextHop(node, resolvedTargetAddress, searchResult) {
202
+ // Determine next hop using standard hierarchy logic
203
+ const nextHopAddress = oAddress.next(node.address, resolvedTargetAddress);
204
+ // Map transports from search result
205
+ const targetTransports = this.mapTransports(searchResult);
206
+ // Set transports on the next hop based on routing logic
207
+ nextHopAddress.setTransports(this.resolveNextHopTransports(nextHopAddress, targetTransports, node));
208
+ return nextHopAddress;
209
+ }
11
210
  async resolve(request) {
12
211
  const { address, node, request: resolveRequest, targetAddress } = request;
212
+ // Early return: if address already has transports, no search needed
13
213
  if (address.transports.length > 0) {
14
214
  return {
15
215
  nextHopAddress: address,
@@ -17,43 +217,36 @@ export class oSearchResolver extends oAddressResolver {
17
217
  requestOverride: resolveRequest,
18
218
  };
19
219
  }
20
- // search the leader registry for the address
21
- const extraParams = address
22
- .toString()
23
- .replace(address.toRootAddress().toString(), '');
24
- const params = {
25
- staticAddress: address.toRootAddress().toString(),
26
- address: address.toString(),
27
- };
28
- const registrySearchResults = await node.use(new oAddress(RestrictedAddresses.REGISTRY), {
29
- method: 'search',
30
- params: params,
220
+ // Perform registry search
221
+ const searchParams = this.buildSearchParams(address);
222
+ const registryAddress = this.getRegistryAddress();
223
+ const searchResponse = await node.use(registryAddress, {
224
+ method: this.getSearchMethod(),
225
+ params: searchParams,
31
226
  });
32
- // if there are results, return the first one
33
- const registrySearchResultsArray = registrySearchResults.result.data.filter(
34
- // filter out the items that may cause infinite looping
35
- (result) => result.staticAddress !== RestrictedAddresses.REGISTRY &&
36
- result.address !== node.address);
37
- if (registrySearchResultsArray.length > 0) {
38
- const registrySearchResult = registrySearchResultsArray[0];
39
- // we know the final destination, so let's return it + the next hop
40
- const targetAddress = new oAddress(registrySearchResult.address + extraParams);
41
- const nextHopAddress = oAddress.next(node.address, targetAddress);
42
- const targetTransports = registrySearchResult.transports.map((t) => new oNodeTransport(t.value));
43
- const childAddress = node?.hierarchyManager.getChild(nextHopAddress);
44
- nextHopAddress.setTransports(nextHopAddress.value === RestrictedAddresses.LEADER
45
- ? node.leader?.transports
46
- : childAddress?.transports || targetTransports);
47
- targetAddress.setTransports(targetTransports);
227
+ // Filter and select result
228
+ const filteredResults = this.filterSearchResults(searchResponse.result.data, node);
229
+ const selectedResult = this.selectResult(filteredResults);
230
+ // Early return: if no result found, return original address
231
+ if (!selectedResult) {
48
232
  return {
49
- nextHopAddress: nextHopAddress,
233
+ nextHopAddress: address,
50
234
  targetAddress: targetAddress,
51
235
  requestOverride: resolveRequest,
52
236
  };
53
237
  }
238
+ // Build route from search result
239
+ const extraParams = address
240
+ .toString()
241
+ .replace(address.toRootAddress().toString(), '');
242
+ const resolvedTargetAddress = new oAddress(selectedResult.address + extraParams);
243
+ // Set transports on the target address
244
+ resolvedTargetAddress.setTransports(this.mapTransports(selectedResult));
245
+ // Determine next hop and configure transports
246
+ const nextHopAddress = this.determineNextHop(node, resolvedTargetAddress, selectedResult);
54
247
  return {
55
- nextHopAddress: address,
56
- targetAddress: targetAddress,
248
+ nextHopAddress: nextHopAddress,
249
+ targetAddress: resolvedTargetAddress,
57
250
  requestOverride: resolveRequest,
58
251
  };
59
252
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/o-node",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -56,10 +56,10 @@
56
56
  "typescript": "5.4.5"
57
57
  },
58
58
  "peerDependencies": {
59
- "@olane/o-config": "^0.7.5",
60
- "@olane/o-core": "^0.7.5",
61
- "@olane/o-protocol": "^0.7.5",
62
- "@olane/o-tool": "^0.7.5"
59
+ "@olane/o-config": "^0.7.7",
60
+ "@olane/o-core": "^0.7.7",
61
+ "@olane/o-protocol": "^0.7.7",
62
+ "@olane/o-tool": "^0.7.7"
63
63
  },
64
64
  "dependencies": {
65
65
  "debug": "^4.4.1",