{"id":27145258,"url":"https://github.com/e-mre/version_comparator","last_synced_at":"2025-04-08T09:31:07.180Z","repository":{"id":65931767,"uuid":"595843731","full_name":"E-MRE/version_comparator","owner":"E-MRE","description":"Platform specific (Android, iOS or Huawei) app version comparator between store and local.","archived":false,"fork":false,"pushed_at":"2025-02-04T07:59:11.000Z","size":489,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-04T08:29:12.064Z","etag":null,"topics":["dart","flutter","version-compare"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/E-MRE.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-31T23:18:58.000Z","updated_at":"2025-02-04T07:58:32.000Z","dependencies_parsed_at":"2024-06-07T10:21:43.898Z","dependency_job_id":null,"html_url":"https://github.com/E-MRE/version_comparator","commit_stats":{"total_commits":56,"total_committers":2,"mean_commits":28.0,"dds":0.1071428571428571,"last_synced_commit":"5613e26275ab39d03ce6035f4d4780edd2f87fd8"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/E-MRE%2Fversion_comparator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/E-MRE%2Fversion_comparator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/E-MRE%2Fversion_comparator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/E-MRE%2Fversion_comparator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/E-MRE","download_url":"https://codeload.github.com/E-MRE/version_comparator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247813149,"owners_count":21000438,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","version-compare"],"created_at":"2025-04-08T09:31:06.563Z","updated_at":"2025-04-08T09:31:07.163Z","avatar_url":"https://github.com/E-MRE.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eVersionComparator\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/34197392/221657155-c98d1de3-ac8d-4c51-b1cd-b39cfdd3e739.png\" width=\"250px\"\u003e\n\u003c/p\u003e\n\u003ch2 align=\"center\"\u003eSimple, Enjoyable \u0026 Customizable Comparator\u003c/h2\u003e\u003c/br\u003e\nA Flutter package for compare app version between store and local version.\n\n- 🚀 Compare app version with all stores. (PlayStore, AppStore, AppGallery and Custom)\n- ⚡ Customizable \u0026 Manageable\n- ❤️ Simple, powerful, \u0026 manageable structure\n- 🔒 Changeable error \u0026 info messages with yours.\n- 🎈 Display AlertDialog or custom widgets when compare versions.\n\n\n## App Version Compare Platforms\n\n| Android | iOS | Huawei  | Custom |\n|---------|-----|---------|--------|\n|    ✔    |  ✔  |    ✔    |    ✔   |\n\n\n\u003c!-- TABLE OF CONTENTS --\u003e\n\u003cdetails open=\"open\"\u003e\n  \u003csummary\u003eContents\u003c/summary\u003e\n  \u003col\u003e\n    \u003cli\u003e\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#other-features\"\u003eOther Features\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#set-custom-messages\"\u003eSet Custom Messages\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#customization\"\u003eCustomization\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#parameter-details\"\u003eParameter Details\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n\u003c/details\u003e\n\n## Usage\n\nImport `package:version_comparator/version_comparator.dart`, and use the widgets or features. That's all your need. You can compare with future operation or widget. Here is some simple comparator examples:\n\nCompare with future operation:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\nfinal response = await VersionComparator.instance.versionCompare();\n\nprint(response?.data?.toJson() ?? '-');\n\n////////PRINT////////\n//{\n//  \"localVersion\":\"1.0.0\",\n//  \"storeVersion\":\"1.0.1\",\n//  \"updateLink:\"https://your_store_link.com,\n//  \"isAppVersionOld:\"true\n//}\n```\n\nCompare with widget:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(widget.title)),\n      body: AppVersionComparatorView.widget(\n        errorPageBuilder: (context, error) =\u003e Center(child: Text(error)),\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        outOfDateVersionPageBuilder: (context, error, data) =\u003e _buildOutOfDateWidget(error, data),\n        child: _buildBody(),\n      ),\n    );\n  }\n```\n\n\u003ctable width=100% text-align=\"center\"\u003e\n\t\u003cthead\u003e\n\t\t\u003ctd\u003e\n\t\t\t\u003cb\u003eVersion Compare with AlertDialog\u003c/b\u003e\n\t\t\u003c/td\u003e\n\t\t\u003ctd\u003e\n\t\t\t\u003cb\u003eVersion Compare with Widget\u003c/b\u003e\n\t\t\u003c/td\u003e\n\t\u003c/thead\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003e\n\t\t\t\u003cimg width=\"200\" alt=\"code-one\" align=\"center\" src=\"https://user-images.githubusercontent.com/34197392/221685005-94318ff0-63c2-4e03-89a2-2150a44da75a.gif\"\u003e\n\t\t\u003c/td\u003e\n\t\t\u003ctd\u003e\n\t\t\t\u003cimg src=\"https://user-images.githubusercontent.com/34197392/221682663-17a904dc-32d4-4dbd-bf5b-d90ced277b6c.gif\" width=\"200\" alt=\"Example Version Comparator Widget\"\u003e\n\t\t\u003c/td\u003e\n\t\u003c/tr\u003e\n\u003c/table\u003e\n\n## Features\n\nThere are many features for version compare. (VersionComparator widgets, future operations, alertDialog manager, custom version comparator).\n\n### Platform Specific Compare:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\n  Future\u003cvoid\u003e compareVersion() async {\n    //set loading state\n    _setLoading(true); \n    \n    // get store and local version result\n    final response = await VersionComparator.instance.versionCompare();\n\n    //set loading state\n    _setLoading(false);\n    \n    //display response\n    print(result?.data?.toJson() ?? '-');\n  }\n```\n\n### Custom Compare:\n\n```dart\n    await VersionComparator.instance.customCompare(\n      localVersion: 'YOUR_APP_VERSION', //example: 1.0.0\n      store: MyStoreModel(appId: 'YOUR_APP_BUNDLE_ID'), //example: com.example.app\n      customUpdateLink: (body) =\u003e 'YOU CAN ADD CUSTOM UPDATE LINK', //example: https://pub.dev/\n      \n      //Convert store version and return it from response.body\n      onConvertVersion: (responseBody) {\n        final result = MyVersionConvertManager().convert(MyVersionResponseModel.fromResponse(responseBody));\n        return result.data;\n      },\n    );\n```\n\n### Show Version Alert Dialog:\n\n```dart\n  await VersionComparator.instance.showSimpleVersionDialog(\n      context,\n      versionResponseModel,\n      isUpdateRequired: isRequired,\n  );\n```\n\u003cp\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/34197392/221686629-66ee8072-7908-45a7-abd6-ad75c367769c.png\" width=\"200px\"\u003e\n\u003c/p\u003e\n\n\n### Platform Specific Compare Widget:\n\n```dart\n      AppVersionComparatorView.widget(\n        errorPageBuilder: (context, error) =\u003e Center(child: Text(error)),\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        outOfDateVersionPageBuilder: (context, error, data) =\u003e _buildOutOfDateWidget(error, data),\n        child: _buildBody(),\n      )\n```\n\u003cp\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/34197392/221682663-17a904dc-32d4-4dbd-bf5b-d90ced277b6c.gif\" width=\"200\"\u003e\n\u003c/p\u003e\n      \n\n### Platform Specific Compare AlertDialog:\n\n```dart\n      AppVersionComparatorView.alertDialog(\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        onOutOfDateVersionError: (message, data) =\u003e debugPrint('Error: $message'),\n        child: _buildBody(),\n      )\n```\n\t\t\t\n\u003cp\u003e\n  \u003cimg width=\"200\" alt=\"code-one\" align=\"center\" src=\"https://user-images.githubusercontent.com/34197392/221685005-94318ff0-63c2-4e03-89a2-2150a44da75a.gif\"\u003e\n\u003c/p\u003e\n\n\n## Other Features\n\nYou can also use these features if you need:\n\n```dart\n//Gets bundle id\nfinal bundleResult = await VersionComparator.instance.getBundleId();\n\n//Gets current platform (Android, iOS, Huawei or Undefined)\nfinal platformResult = await VersionComparator.instance.getPlatform();\n\n//Gets current app version\nfinal versionResult = await VersionComparator.instance.getCurrentAppVersion();\n```\n\n## Set Custom Messages\n\nYou can also customize package error and info messages with yours. It's so simple. If you want to change messages with yours just create a class and extend it from ```ComparatorErrorMessage```or ```ComparatorInfoMessage```. After that override messages with yours. And before using comparator set your custom message classes.\n\nFor set Comparator Error Message:\n\n```dart\nVersionComparator.instance.setErrorMessages(MyCustomErrorMessage());\n```\n\nFor set Comparator Info Message:\n\n```dart\nVersionComparator.instance.setInfoMessages(MyCustomInfoMessage());\n```\n\nDon't forget. If you want to set a few messages use extends, for set all messages than use implements. Here is a simple example:\n\n\n```dart\n//set specific messages extend base class.\nclass MyCustomErrorMessage extends ComparatorErrorMessage{\n  @override\n  String get appNotFound =\u003e 'App not found in the store.';\n\n  @override\n  String get appVersionFetchError =\u003e 'app version info couldn\\'t fetch.';\n}\n```\n\n```dart\n//For set all messages just implement base class like this\nclass ClassName implements ComparatorInfoMessage {\n  @override\n  String get checkVersionErrorDialogTitle =\u003e 'custom message;\n\n  @override\n  String get checkVersionLoadingMessage =\u003e 'custom message;\n  \n  //..//\n}\n```\n\n## Customization\n\nIf you want to compare app version from another store, it's possible with custom compare methods. Just follow these steps.\n\n### Create StoreModel\n\nCustom compare method needs a store model. Don't forget your model must be extend from ```BaseStoreModel```.\n\n```dart\nclass MyStoreModel extends BaseStoreModel{\n\n  @override\n  String get appId =\u003e 'YOUR_BUNDLE_ID_OR_APP_ID'; //for example: 'com.example.app'\n\n  @override\n  String get storeUrl =\u003e 'YOUR_CUSTOM_STORE_URL_WITH_ENDPOINT'; //for example: 'https://your-store.com/apps/details'\n\n  @override\n  String get versionQuery =\u003e 'QUERY_OF_STORE_URL_FOR_COMPARE'; //for example: 'appId=com.example.app\u0026lang=en'\n}\n```\n\n### Create An App Version Converter Function\n\nCustom comparator sends request to store and gets json response. You must create an app version converter function. It's a simple android version converter example:\n\n```dart\nclass MyVersionConvertManager extends VersionConvertService\u003cMyVersionResponseModel\u003e {\n  @override\n  DataResult\u003cString\u003e convert(EntityModel\u003cMyVersionResponseModel\u003e entity) {\n    final firstMatch = RegExpHelper.androidVersionRegExp.firstMatch(entity.responseBody);\n\n    return _checkRegExpVersion(firstMatch);\n  }\n\n  DataResult\u003cString\u003e _checkRegExpVersion(RegExpMatch? regExpMatch) {\n    if (regExpMatch == null || regExpMatch.groupCount \u003c 1) {\n      return DataResult.error(message: 'Version not matched');\n    }\n\n    final version = regExpMatch.group(1);\n    return version == null || version.isEmpty\n        ? DataResult.error(message: 'Version not matched')\n        : DataResult.success(data: version);\n  }\n}\n```\n\n### Set CustomComparator Parameters\n\nAfter all just set parameters to custom comparator. For example:\n\n```dart\n  Future\u003cDataResult\u003cVersionResponseModel\u003e\u003e customCompareVersion() async {\n    return await VersionComparator.instance.customCompare(\n      localVersion: 'YOUR_APP_VERSION', \n      store: MyStoreModel(appId: 'YOUR_APP_BUNDLE_ID'),\n      customUpdateLink: (body) =\u003e 'YOU CAN ADD CUSTOM UPDATE LINK (OPTIONAL)',\n      onConvertVersion: (responseBody) {\n        //Converter function. It returns app version from store.\n        final result = MyVersionConvertManager().convert(MyVersionResponseModel.fromResponse(responseBody));\n        return result.data;\n      },\n    );\n  }\n```\n\n## Parameter Details\n\nImport `package:version_comparator/version_comparator.dart`, and use the widgets or features.\n\nIf you don't want to manage Future operations and states just call `AppVersionComparatorView` or `CustomVersionComparatorView` widgets. These widgets manage getting store version and local version, states (loading, error and success) and comparing versions. Also these widgets has two modes (AlertDialog and Widget).\n\nAlertDialog mode displays all state changes in the alert dialog, other mode displays on the widget tree.\n\nExample of app version compare with Widget mode:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(widget.title)),\n      //All changes will be display on the screen instead of child.\n      body: AppVersionComparatorView.widget(\n        //If your app id is different to current app than fill this parameter.\n        //For example AppGallery (Huawei store) has different app id from app bundle id.\n        customAppId: 'CUSTOM_APP_OR_BUNDLE_ID',\n        //If any error happens (like socket exception) this builder calls. If this builder is null than it returns child widget.\n        errorPageBuilder: (_, error) =\u003e Center(child: Text(error)),\n        //When fetching data from store this text will be override. If you don't want to any text just pass empty or null.\n        loadingText: 'Loading',\n        isLoadingTextVisible: true,\n        //If you want to required app version upgrade set true. \n        isUpdateRequired: true,\n        //You can set loading widget size\n        loadingWidgetSize: 36,\n        //Custom loading widget\n        loadingWidget: const CircularProgressIndicator(),\n        //Its calling when version comparing (like null data)\n        onCompareError: (message) =\u003e debugPrint(message),\n        //Its calling when app is up to date.\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        //Its calling when app is out of date.\n        onOutOfDateVersionError: (message, data) =\u003e debugPrint(message),\n        //When state changes this method calls\n        onStateChanged: (state) =\u003e debugPrint('State is: ${state.runtimeType}'),\n        //Your page or view.\n        child: _buildBody(),\n        //When app version is out of date you can set widget with this builder.\n        outOfDateVersionPageBuilder: (_, error, data) {\n          return Column(\n            mainAxisSize: MainAxisSize.max,\n            crossAxisAlignment: CrossAxisAlignment.center,\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              Text(error, textAlign: TextAlign.center),\n              const SizedBox(height: 16),\n              Text('Out of date version: ${data.localVersion}'),\n              Text('New version: ${data.storeVersion}'),\n            ],\n          );\n        },\n      ),\n    );\n  }\n```\n\nExample of app version compare with AlertDialog mode:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(widget.title)),\n      //All changes will be display on the screen instead of child.\n      body: AppVersionComparatorView.widget(\n        //If your app id is different to current app than fill this parameter.\n        //For example AppGallery (Huawei store) has different app id from app bundle id.\n        huaweiAppId: 'HUAWEI_APP_STORE_ID',\n        //If any error happens (like socket exception) this builder calls.\n        invalidVersionDialogContentBuilder: (context, message) =\u003e Center(child: Text(message)),\n        //When fetching data from store this text will be override. If you don't want to any text just pass empty or null.\n        loadingText: 'Loading',\n        isLoadingTextVisible: true,\n        //If you want to required app version upgrade set true. \n        isUpdateRequired: true,\n        //You can set loading widget size\n        loadingWidgetSize: 36,\n        //Custom loading widget\n        loadingWidget: const CircularProgressIndicator(),\n        //Sets alert cancel button visibility.\n        isCancelActionVisible: true,\n        //Sets dismissible of alert dialog.\n        isDismissibleWhenGetDataError: true,\n        //After pop alert dialog this method runs.\n        onAfterPopDialog: (result) =\u003e debugPrint('alert pop'),\n        //Its calling when version comparing (like null data)\n        onCompareError: (message) =\u003e debugPrint(message),\n        //Its calling when app is up to date.\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        //Its calling when app is out of date.\n        onOutOfDateVersionError: (message, data) =\u003e debugPrint(message),\n        //When state changes this method calls\n        onStateChanged: (state) =\u003e debugPrint('State is: ${state.runtimeType}'),\n        //Your page or view.\n        child: _buildBody(),\n      ),\n    );\n  }\n```\n\nExample custom version compare with Widget mode:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(widget.title)),\n      //All changes will be display on the screen instead of child.\n      body: CustomVersionComparatorView.widget(\n        //It calls Future method for custom version comparing.\n        //You can set your custom Future method but it should be return DataResult\u003cVersionResponseModel\u003e.\n        onVersionCompareCallback: () async =\u003e await VersionComparator.instance.versionCompare(),\n        //If your app id is different to current app than fill this parameter.\n        //For example AppGallery (Huawei store) has different app id from app bundle id.\n        huaweiAppId: 'HUAWEI_APP_STORE_ID',\n        //If any error happens (like socket exception) this builder calls. If this builder is null than it returns child widget.\n        errorPageBuilder: (_, error) =\u003e Center(child: Text(error)),\n        //When fetching data from store this text will be override. If you don't want to any text just pass empty or null.\n        loadingText: 'Loading',\n        isLoadingTextVisible: true,\n        //If you want to required app version upgrade set true. \n        isUpdateRequired: true,\n        //You can set loading widget size\n        loadingWidgetSize: 36,\n        //Custom loading widget\n        loadingWidget: const CircularProgressIndicator(),\n        //Its calling when version comparing (like null data)\n        onCompareError: (message) =\u003e debugPrint(message),\n        //Its calling when app is up to date.\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        //Its calling when app is out of date.\n        onOutOfDateVersionError: (message, data) =\u003e debugPrint(message),\n        //When state changes this method calls\n        onStateChanged: (state) =\u003e debugPrint('State is: ${state.runtimeType}'),\n        //Your page or view.\n        child: _buildBody(),\n        //When app version is out of date you can set widget with this builder.\n        outOfDateVersionPageBuilder: (_, error, data) {\n          return Column(\n            mainAxisSize: MainAxisSize.max,\n            crossAxisAlignment: CrossAxisAlignment.center,\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              Text(error, textAlign: TextAlign.center),\n              const SizedBox(height: 16),\n              Text('Out of date version: ${data.localVersion}'),\n              Text('New version: ${data.storeVersion}'),\n            ],\n          );\n        },\n      ),\n    );\n  }\n```\n\nExample custom version compare with AlertDialog mode:\n\n```dart\nimport 'package:version_comparator/version_comparator.dart';\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: Text(widget.title)),\n      //All changes will be display on the screen instead of child.\n      body: CustomVersionComparatorView.widget(\n        //It calls Future method for custom version comparing.\n        //You can set your custom Future method but it should be return DataResult\u003cVersionResponseModel\u003e.\n        onVersionCompareCallback: () async =\u003e await VersionComparator.instance.versionCompare(),\n        //If your app id is different to current app than fill this parameter.\n        //For example AppGallery (Huawei store) has different app id from app bundle id.\n        huaweiAppId: 'HUAWEI_APP_STORE_ID',\n        //If any error happens (like socket exception) this builder calls.\n        invalidVersionDialogContentBuilder: (context, message) =\u003e Center(child: Text(message)),\n        //When fetching data from store this text will be override. If you don't want to any text just pass empty or null.\n        loadingText: 'Loading',\n        isLoadingTextVisible: true,\n        //If you want to required app version upgrade set true. \n        isUpdateRequired: true,\n        //You can set loading widget size\n        loadingWidgetSize: 36,\n        //Custom loading widget\n        loadingWidget: const CircularProgressIndicator(),\n        //Sets alert cancel button visibility.\n        isCancelActionVisible: true,\n        //Sets dismissible of alert dialog.\n        isDismissibleWhenGetDataError: true,\n        //After pop alert dialog this method runs.\n        onAfterPopDialog: (result) =\u003e debugPrint('alert pop'),\n        //Its calling when version comparing (like null data)\n        onCompareError: (message) =\u003e debugPrint(message),\n        //Its calling when app is up to date.\n        onCompareSuccess: (data) =\u003e debugPrint('Success: ${data.storeVersion}'),\n        //Its calling when app is out of date.\n        onOutOfDateVersionError: (message, data) =\u003e debugPrint(message),\n        //When state changes this method calls\n        onStateChanged: (state) =\u003e debugPrint('State is: ${state.runtimeType}'),\n        //Your page or view.\n        child: _buildBody(),\n      ),\n    );\n  }\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fe-mre%2Fversion_comparator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fe-mre%2Fversion_comparator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fe-mre%2Fversion_comparator/lists"}