class AcpEndpointPaths { const AcpEndpointPaths._({ required this.basePath, required this.webSocketPath, required this.httpRpcPath, }); final String basePath; final String webSocketPath; final String httpRpcPath; static AcpEndpointPaths fromBaseEndpoint(Uri endpoint) { final basePath = _normalizeBasePath(endpoint.path); final prefixedBasePath = basePath.isEmpty ? '' : basePath; return AcpEndpointPaths._( basePath: prefixedBasePath, webSocketPath: prefixedBasePath.isEmpty ? '/acp' : '$prefixedBasePath/acp', httpRpcPath: prefixedBasePath.isEmpty ? '/acp/rpc' : '$prefixedBasePath/acp/rpc', ); } static bool isProviderMappingPath(String rawPath) { var path = rawPath.trim(); if (path.isEmpty || path == '/') { return false; } if (!path.startsWith('/')) { path = '/$path'; } path = path.replaceFirst(RegExp(r'/+$'), ''); if (path.endsWith('/acp/rpc')) { path = path.substring(0, path.length - '/acp/rpc'.length); } else if (path.endsWith('/acp')) { path = path.substring(0, path.length - '/acp'.length); } path = path.replaceFirst(RegExp(r'/+$'), ''); return path == '/acp-server' || path.startsWith('/acp-server/') || path == '/gateway' || path.startsWith('/gateway/'); } static String _normalizeBasePath(String rawPath) { var path = rawPath.trim(); if (path.isEmpty || path == '/') { return ''; } if (!path.startsWith('/')) { path = '/$path'; } path = path.replaceFirst(RegExp(r'/+$'), ''); if (path.isEmpty || path == '/') { return ''; } if (path.endsWith('/acp/rpc')) { path = path.substring(0, path.length - '/acp/rpc'.length); } else if (path.endsWith('/acp')) { path = path.substring(0, path.length - '/acp'.length); } path = path.replaceFirst(RegExp(r'/+$'), ''); return path == '/' ? '' : path; } } Uri? resolveAcpWebSocketEndpoint(Uri? endpoint) { if (endpoint == null || endpoint.host.trim().isEmpty) { return null; } if (AcpEndpointPaths.isProviderMappingPath(endpoint.path)) { return null; } final scheme = endpoint.scheme.trim().toLowerCase(); final wsScheme = switch (scheme) { 'https' || 'wss' => 'wss', _ => 'ws', }; final paths = AcpEndpointPaths.fromBaseEndpoint(endpoint); return endpoint.replace( scheme: wsScheme, path: paths.webSocketPath, query: null, fragment: null, ); } Uri? resolveAcpHttpRpcEndpoint(Uri? endpoint) { if (endpoint == null || endpoint.host.trim().isEmpty) { return null; } if (AcpEndpointPaths.isProviderMappingPath(endpoint.path)) { return null; } final scheme = endpoint.scheme.trim().toLowerCase(); if (scheme != 'http' && scheme != 'https') { return null; } final paths = AcpEndpointPaths.fromBaseEndpoint(endpoint); return endpoint.replace(path: paths.httpRpcPath, query: null, fragment: null); }