{"id":47632946,"url":"https://github.com/aouledissa/deep-match","last_synced_at":"2026-04-01T23:52:18.943Z","repository":{"id":317455732,"uuid":"1012455973","full_name":"aouledissa/deep-match","owner":"aouledissa","description":"Android deeplink toolkit: a Gradle plugin and runtime library that generates manifest filters, type-safe routing, and validation from YAML specs","archived":false,"fork":false,"pushed_at":"2026-03-24T13:23:32.000Z","size":1798,"stargazers_count":20,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-24T22:39:04.513Z","etag":null,"topics":["android","android-development","android-library","app-links","code-generation","deep-linking","deeplink","gradle","gradle-plugin","intent-filter","kotlin","kotlin-library","mobile","navigation","routing","toolkit","uri","uri-routing","yaml"],"latest_commit_sha":null,"homepage":"https://aouledissa.com/deep-match/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aouledissa.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":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-02T11:07:57.000Z","updated_at":"2026-03-24T12:32:54.000Z","dependencies_parsed_at":"2025-10-01T02:24:09.250Z","dependency_job_id":"8843efe8-9974-418b-a343-c7e9b043fa77","html_url":"https://github.com/aouledissa/deep-match","commit_stats":null,"previous_names":["aouledissa/deep-match"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/aouledissa/deep-match","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aouledissa%2Fdeep-match","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aouledissa%2Fdeep-match/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aouledissa%2Fdeep-match/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aouledissa%2Fdeep-match/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aouledissa","download_url":"https://codeload.github.com/aouledissa/deep-match/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aouledissa%2Fdeep-match/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31293126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["android","android-development","android-library","app-links","code-generation","deep-linking","deeplink","gradle","gradle-plugin","intent-filter","kotlin","kotlin-library","mobile","navigation","routing","toolkit","uri","uri-routing","yaml"],"created_at":"2026-04-01T23:52:18.356Z","updated_at":"2026-04-01T23:52:18.929Z","avatar_url":"https://github.com/aouledissa.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e [!IMPORTANT]\n\u003e **Security notice:** The primary artifact signing key has been compromised and revoked\n\u003e (`5283 67B0 1C0B 54E0 55A6  96E0 4D0B DAAD C6F8 86DB`). **It must no longer be trusted.**.\n\u003e \n\u003e All releases from **1.1.0 onwards** are signed with the new key\n\u003e (`80D0 DA79 427D A034 593F  2F35 0F14 8D47 0842 C013`).\n\u003e \n\u003e See [CHANGELOG](CHANGELOG.md#110---2026-03-29) for details.\n\n![Build](https://img.shields.io/github/actions/workflow/status/aouledissa/deep-match/test.yml?branch=main)\n[![Docs](https://img.shields.io/github/actions/workflow/status/aouledissa/deep-match/docs.yml?branch=main\u0026label=docs)](https://aouledissa.com/deep-match/)\n[![Maven Central](https://img.shields.io/maven-central/v/com.aouledissa.deepmatch/deepmatch-processor)](https://github.com/aouledissa/deep-match/releases)\n[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/com.aouledissa.deepmatch.gradle?logo=gradle)](https://plugins.gradle.org/plugin/com.aouledissa.deepmatch.gradle)\n![API](https://img.shields.io/badge/API-24+-brightgreen?logo=android)\n![AGP](https://img.shields.io/badge/AGP-9.0+-blue?logo=gradle)\n![Kotlin](https://img.shields.io/badge/Kotlin-2.3+-7F52FF?logo=kotlin\u0026logoColor=white)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue)](https://www.apache.org/licenses/LICENSE-2.0)\n[![AndroidWeekly](https://androidweekly.net/issues/issue-717/badge)](https://androidweekly.net/issues/issue-717)\n\n# DeepMatch — Android Deeplink Toolkit\n\nDeepMatch is a complete toolkit for Android deeplink handling: a Gradle plugin and runtime library that automates everything. Describe your links once in YAML, and it generates the manifest intent filters, type-safe parameter classes, and runtime routing logic for you.\n\n![DeepMatch Toolkit](images/social-preview.png)\n\n## Why DeepMatch?\n\nAndroid deep linking has a fundamental synchronization problem. Every deep link must be declared in at least two places: the `AndroidManifest.xml` as an `\u003cintent-filter\u003e`, and your Kotlin code as string-based URI parsing logic. When one changes without the other, links break silently at runtime.\n\n**DeepMatch eliminates this entirely.** You declare your deep links once in a YAML spec file. The Gradle plugin reads that file and automatically generates:\n\n- `\u003cintent-filter\u003e` entries in your `AndroidManifest.xml`\n- Strongly-typed Kotlin parameter classes (one per deep link)\n- A ready-to-use runtime processor for URI matching\n\nThe YAML file becomes the single source of truth for everything deep link-related.\n\n## Key Features\n\n**Automatic manifest generation** — The plugin generates all `\u003cintent-filter\u003e` entries for you. It picks the right path attribute (`path`, `pathPrefix`, `pathPattern`), correctly handles mixed schemes for App Links verification, generates separate intent filters for each host and scheme combination for granular routing control, and suppresses AGP 9 lint warnings for hostless URIs. You never touch the manifest XML for deep links again.\n\n**Type-safe routing** — Instead of extracting strings from a `Uri` and casting manually, you get generated data classes with typed fields. A `numeric` path param becomes an `Int`. A missing optional query param becomes `null`. A successful match is never ambiguous with no-match.\n\n**Build-time validation** — The plugin catches broken configurations before they ship: missing schemes, duplicate spec names, and URI-shape collisions across composed modules all fail the build with a clear error.\n\n**Multi-module composition** — Library modules declare their own `.deeplinks.yml`. App modules automatically discover and compose all dependency processors into a single unified `CompositeDeeplinkProcessor`. Collision detection runs at build time.\n\n**URI validation task** — Run `./gradlew validateDeeplinks --uri \"...\"` to test any URI against your specs locally and get a per-spec `[MATCH]` / `[MISS]` diagnostic with extracted params.\n\n**HTML report** — Optionally generate a self-contained HTML deeplink catalog with a live in-browser URI validator, auto-generated example URIs, and near-miss diagnostics.\n\n## Setup\n\n### 1. Apply the plugin\n\n```kotlin\n// app/build.gradle.kts\nplugins {\n    id(\"com.android.application\")\n    // AGP 9+: Kotlin is built into AGP, do not apply org.jetbrains.kotlin.android\n    id(\"com.aouledissa.deepmatch.gradle\") version \"\u003cversion\u003e\"\n}\n```\n\n### 2. Add the runtime dependency\n\n```kotlin\ndependencies {\n    implementation(\"com.aouledissa.deepmatch:deepmatch-processor:\u003cversion\u003e\")\n}\n```\n\n### 3. Create a spec file\n\nCreate `.deeplinks.yml` in your module root:\n\n```yaml\ndeeplinkSpecs:\n  - name: \"open profile\"\n    activity: com.example.app.ProfileActivity\n    categories: [DEFAULT, BROWSABLE]\n    scheme: [https, app]\n    host: [\"example.com\"]\n    pathParams:\n      - name: profile\n      - name: userId\n        type: alphanumeric\n    queryParams:\n      - name: ref\n        type: string\n        required: true\n    fragment: \"details\"\n\n  - name: \"open series\"\n    activity: com.example.app.MainActivity\n    categories: [DEFAULT, BROWSABLE]\n    scheme: [app]\n    host: [\"example.com\"]\n    pathParams:\n      - name: series\n      - name: seriesId\n        type: numeric\n    queryParams:\n      - name: ref\n        type: string\n```\n\n**Path params** are matched positionally and in order. **Query params** are matched by name, order-independent. Untyped params act as literal path segments. Host is optional — omit it for hostless URIs like `app:///home`.\n\n### 4. Build\n\n```bash\n./gradlew build\n```\n\nThe plugin generates intent filters in your manifest, a typed `AppDeeplinkParams` sealed interface, one `*DeeplinkParams` class per spec, and an `AppDeeplinkProcessor` ready for use at runtime.\n\n### 5. Handle deep links\n\n```kotlin\nclass MainActivity : AppCompatActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        when (val params = AppDeeplinkProcessor.match(intent.data) as? AppDeeplinkParams) {\n            is OpenProfileDeeplinkParams -\u003e openProfile(params.userId, params.ref)\n            is OpenSeriesDeeplinkParams  -\u003e openSeries(params.seriesId)\n            null -\u003e showHome()\n        }\n    }\n}\n```\n\nNo string extraction. No manual casting. No runtime surprises.\n\n## Configuration\n\n```kotlin\ndeepMatch {\n    generateManifestFiles = true                          // default: false\n    manifestSyncViolation = ManifestSyncViolation.WARN    // default: WARN — use FAIL to break the build\n    verbose = true                                        // default: false — set to true to enable build logs\n    report {\n        enabled = true             // default: false\n        // output = layout.buildDirectory.file(\"reports/deeplinks.html\")\n    }\n}\n```\n\n## Deeplink Report\n\nEnable the report to generate a self-contained HTML dashboard for viewing, testing, and sharing your deeplinks:\n\n### Deeplink Catalogue\n\nBrowse all your deep links in a searchable, organized catalogue with full spec details:\n\n![Deeplink Catalogue](images/catalogue.png)\n\nThe catalogue displays:\n- Spec name, description, and module/source\n- Schemes, hosts, ports, and path templates\n- Path and query parameters with types and requirements\n- Example URIs with sample values\n- **ADB commands for terminal testing** — Copy-to-clipboard commands to quickly test deeplinks using `adb shell am start`\n- Generated Kotlin class names and regex patterns\n\n### Interactive URI Validator\n\nTest any URI against your specs in the browser with live validation and near-miss diagnostics:\n\n![URL Validator](images/url-validator.png)\n\nThe validator shows:\n- Matching spec with extracted parameters\n- Near-miss diagnostics (e.g., missing required query params)\n- Quick-test buttons for each spec's example URI\n- Full URI parsing without leaving the browser\n\nThe report is a single self-contained HTML file — no external dependencies, no server required. Share it via email, upload as a CI artifact, or host as a static page.\n\n## Gradle Tasks\n\n| Task | Description |\n|------|-------------|\n| `generate\u003cVariant\u003eDeeplinkSpecs` | Generate Kotlin sources from YAML specs |\n| `generate\u003cVariant\u003eDeeplinkManifest` | Generate manifest intent filter entries |\n| `generate\u003cVariant\u003eDeeplinkReport` | Generate the HTML deeplink report (if enabled) |\n| `validateDeeplinks --uri \"...\"` | Validate a URI against all declared specs |\n| `validate\u003cVariant\u003eCompositeSpecsCollisions` | Detect URI-shape collisions across composed modules |\n\nRequired query params generate non-null properties. Optional query params generate nullable ones.\n\n## Multi-Module Projects\n\nEach module declares its own `.deeplinks.yml`. The plugin auto-discovers any Gradle dependency that also applies DeepMatch and composes all their processors into a `CompositeDeeplinkProcessor` in the consuming app. The first processor in dependency order that matches a URI wins.\n\n```kotlin\n// No manual wiring needed. This is generated automatically.\nval result = AppDeeplinkProcessor.match(uri)  // Searches all composed modules\n```\n\nCollision detection across composed modules runs as part of the build.\n\n## Modules\n\n| Module | Description |\n|--------|-------------|\n| `deepmatch-plugin` | Gradle plugin that parses YAML specs, generates Kotlin sources, and produces manifest entries |\n| `deepmatch-processor` | Android library with `DeeplinkProcessor` and `CompositeDeeplinkProcessor` for runtime URI matching |\n| `deepmatch-api` | Shared model classes: `DeeplinkSpec`, `Param`, `ParamType`, `DeeplinkParams` |\n| `deepmatch-testing` | Reusable test fixtures: fake processors and spec builders |\n| `samples/android-app` | End-to-end sample with Compose UI, generated manifest, and ADB test URIs |\n\n## Documentation\n\nFull documentation is available at **[aouledissa.com/deep-match](https://aouledissa.com/deep-match/)**.\n\n| Page                                                         | Description                                                |\n|--------------------------------------------------------------|------------------------------------------------------------|\n| [Plugin](docs/plugin.md)                                     | Plugin setup, configuration, and build integration         |\n| [Deeplink Specs](docs/deeplink-specs.md)                     | Full YAML spec reference with examples                     |\n| [Composite Specs](docs/composite-specs.md)                   | Multi-module processor composition and match precedence    |\n| [Tasks](docs/tasks.md)                                       | All generated Gradle tasks and the `validateDeeplinks` CLI |\n| [Report](docs/report.md)                                     | HTML catalog and live URI validator                        |\n| [Migration 0.3.0-beta](docs/migrations/migration-guide-0.3.0-beta.md)   | Migration guide for 0.3.0-beta                             |\n| [Migration 0.2.0-alpha](docs/migrations/migration-guide-0.2.0-alpha.md) | Migration guide for 0.2.0-alpha                            |\n\n## Testing\n\n```bash\n./gradlew test                  # Run all unit and Robolectric tests\n./gradlew publishToMavenLocal   # Publish locally for downstream testing\n```\n\nShared test fixtures live in `deepmatch-testing` and can be used in downstream projects.\n\n## Official Distribution\n\nOfficial DeepMatch releases are published **exclusively** to [Sonatype Central Portal (Maven Central)](https://central.sonatype.com/namespace/com.aouledissa.deepmatch) for library artifacts and the [Gradle Plugin Portal](https://plugins.gradle.org/plugin/com.aouledissa.deepmatch.gradle) for the plugin. **The authors bear no responsibility for artifacts obtained from any other source.** See [SECURITY.md](SECURITY.md) for the full policy.\n\n## Contributing\n\nIssues and pull requests are welcome. Please ensure `./gradlew test` passes before opening a PR.\n\n## License\n\n```\nCopyright 2026 DeepMatch Contributors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faouledissa%2Fdeep-match","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faouledissa%2Fdeep-match","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faouledissa%2Fdeep-match/lists"}