GitFOSS
refactor(routes): make the Routes Paths + Params typesafe
+ 1423
- 996
@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1665676834248,
+  "_generatedAtUnix": 1665685521848,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "islands": {

...
@@ -22,13 +22,13 @@
       "pathSourceMap": "./public/.islands/PullRequestSourceSelect.bundle.js.map"
     },
     "RepositoriesList": {
-      "hash": "123782350476918ef2f540ddaca91ef9c82bcc8f",
+      "hash": "4cf17af8826b391263e248be55ce4116eb7414d6",
       "pathSource": "./app/islands/RepositoriesList.tsx",
       "pathBundle": "./public/.islands/RepositoriesList.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoriesList.bundle.js.map"
     },
     "RepositoryCommitSummaryLine": {
-      "hash": "7c1eee05085d4ec9ef48e6ecfe8f90408a814a16",
+      "hash": "e012ace9c8913084e366212f13bb7ae0156b31ab",
       "pathSource": "./app/islands/RepositoryCommitSummaryLine.tsx",
       "pathBundle": "./public/.islands/RepositoryCommitSummaryLine.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryCommitSummaryLine.bundle.js.map"

...
@@ -40,7 +40,7 @@
       "pathSourceMap": "./public/.islands/RepositoryCreateForm.bundle.js.map"
     },
     "RepositoryFilesDiffsList": {
-      "hash": "3343a34118a771923801fa256372e534d8599f2c",
+      "hash": "3f25148b18ecf4be33cde9ba288d93f5f7f3fc53",
       "pathSource": "./app/islands/RepositoryFilesDiffsList.tsx",
       "pathBundle": "./public/.islands/RepositoryFilesDiffsList.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryFilesDiffsList.bundle.js.map"

...
@@ -52,7 +52,7 @@
       "pathSourceMap": "./public/.islands/RepositoryForkForm.bundle.js.map"
     },
     "RepositoryHero": {
-      "hash": "8e3ab7bf7a0453764210ea7f352fafc688e8b905",
+      "hash": "81540baa6a726aa5046fbe1c4d09d1d926a7adb3",
       "pathSource": "./app/islands/RepositoryHero.tsx",
       "pathBundle": "./public/.islands/RepositoryHero.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryHero.bundle.js.map"

...
@@ -64,13 +64,13 @@
       "pathSourceMap": "./public/.islands/RepositoryInitialSetup.bundle.js.map"
     },
     "RepositoryPullRequestCreateForm": {
-      "hash": "6ac90711334641e84db75f910431e086fc0d1e00",
+      "hash": "e0ca07f9a904abb40e812678f1a1efde5e3ae994",
       "pathSource": "./app/islands/RepositoryPullRequestCreateForm.tsx",
       "pathBundle": "./public/.islands/RepositoryPullRequestCreateForm.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryPullRequestCreateForm.bundle.js.map"
     },
     "RepositoryTreeView": {
-      "hash": "846a1f7c4654c973bd106763dee544eb2a485ab2",
+      "hash": "730ce2e7a8f5b705deb6fac8e506ebc0d4cc1458",
       "pathSource": "./app/islands/RepositoryTreeView.tsx",
       "pathBundle": "./public/.islands/RepositoryTreeView.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryTreeView.bundle.js.map"

...
@@ -78,19 +78,19 @@
   },
   "views": {
     "HomeView": {
-      "hash": "98b1f0f9fe6b3f5a61b9ca5707459a8a32de0e6c",
+      "hash": "c1530f6ff4b764c99bd296ebdad53d6fd8f95fa2",
       "pathSource": "./app/views/HomeView.tsx"
     },
     "InternalErrorView": {
-      "hash": "63c8e65201adc83014dd9434569fb857d3888635",
+      "hash": "a155f3581f6ccadc5e7db619152490c65b7c4de5",
       "pathSource": "./app/views/InternalErrorView.tsx"
     },
     "LoginView": {
-      "hash": "86965d3c632c5b2b6f3ef5bbcab2ad73adea2de1",
+      "hash": "44c082512925027813f1f7d046ec43e5ac8d3f0d",
       "pathSource": "./app/views/auth/LoginView.tsx"
     },
     "RegisterView": {
-      "hash": "9ef9cd8f4c0e8402994277cf3d7191e3db765e6d",
+      "hash": "d703ac5d1bec1c1167c60b08fc4eb98f02cef139",
       "pathSource": "./app/views/auth/RegisterView.tsx"
     },
     "OrganizationDetailsView": {

...
@@ -110,11 +110,11 @@
       "pathSource": "./app/views/repository/RepositoryCompareView.tsx"
     },
     "RepositoryCreateView": {
-      "hash": "79c276144349b6d0fbe3aeeb658fb1ae30daa9d5",
+      "hash": "83da3d43e76d51e0615ac1d3e22a27df9c43c584",
       "pathSource": "./app/views/repository/RepositoryCreateView.tsx"
     },
     "RepositoryDetailsView": {
-      "hash": "29d660c09e9ae686b6e7c4c16d171e27fbd9b13c",
+      "hash": "486e106dcc7ef2a7499b9702b9dd81ce3cf90b6d",
       "pathSource": "./app/views/repository/RepositoryDetailsView.tsx"
     },
     "RepositoryExploreView": {

...
@@ -122,7 +122,7 @@
       "pathSource": "./app/views/repository/RepositoryExploreView.tsx"
     },
     "RepositoryForkView": {
-      "hash": "1a9509ad1a1a6f287f5ca7fd69ccbe5344d9f520",
+      "hash": "03cf6068a1eac864d44ca06a5bd729427ed73e6f",
       "pathSource": "./app/views/repository/RepositoryForkView.tsx"
     },
     "RepositoryShowObjectView": {

...
@@ -134,11 +134,11 @@
       "pathSource": "./app/views/repositoryPullRequests/RepositoryPullRequestCreateView.tsx"
     },
     "RepositoryPullRequestDetailsView": {
-      "hash": "6c311e13f7cbbc00b502e0d4c1af30493433eb65",
+      "hash": "3be2cc6227dd14e4345c8ab2b38500a00c90df6d",
       "pathSource": "./app/views/repositoryPullRequests/RepositoryPullRequestDetailsView.tsx"
     },
     "RepositoryPullRequestsView": {
-      "hash": "1ad2fff2166e8417a428852f7f4579d4f2ece30d",
+      "hash": "15afac8b1953b882780b60c5b5abb75354ca6a88",
       "pathSource": "./app/views/repositoryPullRequests/RepositoryPullRequestsView.tsx"
     },
     "UserDashboardView": {

app/components/PageHeader.tsx
@@ -3,8 +3,10 @@ import React, { useMemo, VFC } from "react";
 import styled, { css } from "styled-components";
 // app
 import type { CommonProps, WithThemeSchemeProp } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Const } from "../const";
 import { NamedColors } from "../utils/style";
+import { buildRouteLink } from "../utils/shared";
 
 interface PageHeaderProps extends CommonProps {}
 

...
@@ -18,10 +20,17 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
     if (commonProps.authenticated) {
       return (
         <>
-          <a href={`/@${commonProps.currentUserUsername || "ghost"}`}>
+          <a
+            href={buildRouteLink(AppRoute.USER_DETAILS, {
+              username: commonProps.currentUserUsername || "ghost",
+            })}
+          >
             {commonProps.currentUserUsername || "ghost"}
           </a>
-          <a aria-label={"Log off your account"} href={"/auth/logout"}>
+          <a
+            aria-label={"Log off your account"}
+            href={buildRouteLink(AppRoute.AUTH_LOGOUT_ACTION, null)}
+          >
             Logout
           </a>
         </>

...
@@ -30,10 +39,16 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
 
     return (
       <>
-        <a aria-label={"Register a new account"} href={"/auth/register"}>
+        <a
+          aria-label={"Register a new account"}
+          href={buildRouteLink(AppRoute.AUTH_REGISTER, null)}
+        >
           Register
         </a>
-        <a aria-label={"Login to your account"} href={"/auth/login"}>
+        <a
+          aria-label={"Login to your account"}
+          href={buildRouteLink(AppRoute.AUTH_LOGIN, null)}
+        >
           Login
         </a>
       </>

...
@@ -48,19 +63,27 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
         </a>
       </StyledLogoArea>
       <StyledPageHeaderNav>
-        <a aria-label={"Explore Repositories"} href={"/repo/explore"}>
+        <a
+          aria-label={"Explore Repositories"}
+          href={buildRouteLink(AppRoute.REPOSITORY_EXPLORE, null)}
+        >
           Explore Repositories
         </a>
       </StyledPageHeaderNav>
       <StyledActionsArea>
         {commonProps.authenticated && (
-          <a aria-label={"Create a new Repository"} href={"/repo/new"}>
+          <a
+            aria-label={"Create a new Repository"}
+            href={buildRouteLink(AppRoute.REPOSITORY_CREATE, null)}
+          >
             New Repository
           </a>
         )}
         <a
           data-smooth-scroll={"disabled"}
-          href={`/theme/${invertThemeScheme}`}
+          href={buildRouteLink(AppRoute.THEME_SET_SCHEME_ACTION, {
+            themeScheme: invertThemeScheme,
+          })}
           style={{ color: NamedColors.TEXT_MUTED[themeScheme] }}
           title={`Click to enable ${
             themeScheme === "light" ? "dark" : "light"

app/controllers/auth/getLogoutAction.ts
@@ -1,7 +1,7 @@
 // 3rd-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute } from "../../routes";
+import { AppRoute } from "../../routes.defs";
 
 const getLogoutAction: ReqHandler = async (request, reply) => {
   await request.session.destroy();

app/controllers/auth/postLoginAction.ts
@@ -1,7 +1,7 @@
 // 3rd-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import LoginView, { LoginViewProps } from "../../views/auth/LoginView";
 import { makeAuthService } from "../../services/auth";
 

app/controllers/auth/postRegisterAction.ts
@@ -1,7 +1,7 @@
 // 3rd-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import RegisterView, { RegisterViewProps } from "../../views/auth/RegisterView";
 import { makeAuthService } from "../../services/auth";
 

app/controllers/organization/getOrganizationDetailsView.ts
@@ -8,7 +8,7 @@ import {
   User,
 } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 // app views

app/controllers/repository/getRepositoryBrowserView.ts
@@ -3,7 +3,7 @@ import type { ReqHandler } from "@ethicdevs/react-monolith";
 // generated via script[generate:prisma]
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import { Const } from "../../const";
 // app services
 import { makeOrganizationService } from "../../services/organization";

app/controllers/repository/getRepositoryCommitsLogView.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makeRepositoryService } from "../../services/repository";

app/controllers/repository/getRepositoryCompareView.ts
@@ -2,7 +2,7 @@
 import { ReqHandler } from "@ethicdevs/react-monolith";
 // generated via script[generate:prisma]
 import { ResourceVisibility } from "@prisma/client";
-import { AppRoute, AppRoutesParams } from "app/routes";
+import { AppRoute, AppRoutesParams } from "app/routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makeRepositoryService } from "../../services/repository";

app/controllers/repository/getRepositoryCreateView.ts
@@ -1,7 +1,7 @@
 // 1st-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute } from "../../routes";
+import { AppRoute } from "../../routes.defs";
 import { makeUsersService } from "../../services/user";
 // app views
 import RepositoryCreateView, {

app/controllers/repository/getRepositoryDetailsView.ts
@@ -1,7 +1,7 @@
 // 1st-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import { Const } from "../../const";
 // app services
 import { makeOrganizationService } from "../../services/organization";

app/controllers/repository/getRepositoryForkView.ts
@@ -1,7 +1,7 @@
 // 1st-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makeRepositoryService } from "../../services/repository";

app/controllers/repository/getRepositoryShowObjectView.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makeRepositoryService } from "../../services/repository";

app/controllers/repository/postRepositoryCreateAction.ts
@@ -3,7 +3,7 @@ import { join, resolve } from "node:path";
 // 1st-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import { Env } from "../../env";
 // app services
 import { makeRepositoryService } from "../../services/repository";

app/controllers/repository/postRepositoryForkAction.ts
@@ -3,7 +3,7 @@ import { join, resolve } from "node:path";
 // 1st-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import { Env } from "../../env";
 // app services
 import { makeOrganizationService } from "../../services/organization";

app/controllers/repositoryPullRequests/getRepositoryPullRequestCloseAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestCommentCreateAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestCommentDeleteAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestCommentUpdateAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestCreateView.ts
@@ -3,7 +3,7 @@ import { ReqHandler } from "@ethicdevs/react-monolith";
 // app
 import type { RepositoryWithParentAndForkedFromRepos } from "../../types";
 import { Const } from "../../const";
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makeRepositoryService } from "../../services/repository";

app/controllers/repositoryPullRequests/getRepositoryPullRequestDeleteAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestDetailsView.ts
@@ -3,7 +3,7 @@ import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
 import type { RepositoryFileDiff } from "../../types";
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

...
@@ -124,6 +124,7 @@ const getRepositoryPullRequestDetailsView: ReqHandler = async (
       sourceRepo,
       targetParentOrg,
       targetRepo,
+      isCurrentUserAllowedToMerge: true,
     }
   );
 };

app/controllers/repositoryPullRequests/getRepositoryPullRequestMergeAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestUpdateAction.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/getRepositoryPullRequestsView.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { ResourceVisibility } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app services
 import { makeOrganizationService } from "../../services/organization";
 import { makePullRequestService } from "../../services/pullRequest";

app/controllers/repositoryPullRequests/postRepositoryPullRequestCreateAction.ts
@@ -1,7 +1,7 @@
 // 1st-party
 import { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 // app islands
 import {
   PullRequestFormState,

app/controllers/syntaxHighlight/highlightCodeAction.ts
@@ -5,7 +5,7 @@ import Prism from "prismjs";
 import { parse as parseHtmlToJson, TextNode, RealNode } from "himalaya";
 // app
 import type { AppThemeScheme } from "../../types";
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import { escapeHtmlCode } from "../../utils/shared/escapeHtmlCode";
 
 Prism.languages.prisma = Prism.languages.extend("javascript", {

app/controllers/theme/setThemeSchemeAction.ts
@@ -1,7 +1,7 @@
 // 1st-party
 import { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 
 const setThemeSchemeAction: ReqHandler = async (request, reply) => {
   const { referer } = request.headers;

app/controllers/user/getUserDashboardView.ts
@@ -1,7 +1,7 @@
 // 3rd-party
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 // app
-import { AppRoute } from "../../routes";
+import { AppRoute } from "../../routes.defs";
 import { makeUsersService } from "../../services/user";
 // app views
 import UserDashboardView, {

app/controllers/user/getUserDetailsView.ts
@@ -2,7 +2,7 @@
 import type { ReqHandler } from "@ethicdevs/react-monolith";
 import { User } from "@prisma/client";
 // app
-import { AppRoute, AppRoutesParams } from "../../routes";
+import { AppRoute, AppRoutesParams } from "../../routes.defs";
 import { makeUsersService } from "../../services/user";
 // app views
 import UserDetailsView, {

app/islands/RepositoriesList.tsx
@@ -6,8 +6,10 @@ import React from "react";
 import { Organization, Repository } from "@prisma/client";
 // app
 import type { WithThemeSchemeProp } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Card } from "../components/Card.styled";
 import { Grid } from "../components/Grid";
+import { buildRouteLink } from "../utils/shared";
 
 export interface RepositoriesListProps {
   repositories: Array<Repository & { parentOrg: Organization }>;

...
@@ -26,7 +28,12 @@ const RepositoriesList: ReactIsland<
         >
           <Grid.Row fluid nowrap>
             <h1 style={{ margin: 0, flex: 1 }}>
-              <a href={`/${repo.parentOrg.slug}/${repo.slug}`}>
+              <a
+                href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
+                  orgSlug: repo.parentOrg.slug,
+                  repoSlug: repo.slug,
+                })}
+              >
                 {repo.parentOrg.displayName || repo.parentOrg.slug}
                 {" / "}
                 {repo.displayName || repo.slug}

app/islands/RepositoryCommitSummaryLine.tsx
@@ -4,7 +4,9 @@ import type { ReactIsland } from "@ethicdevs/react-monolith";
 import React, { useCallback, useMemo, useState } from "react";
 // app
 import type { RepositoryObject } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Grid } from "../components/Grid";
+import { buildRouteLink } from "../utils/shared";
 
 export interface RepositoryCommitSummaryLineProps {
   commit: RepositoryObject;

...
@@ -56,7 +58,13 @@ const RepositoryCommitSummaryLine: ReactIsland<RepositoryCommitSummaryLineProps>
           <Grid.Col flex={"1 0 calc(100% - 220px)"} style={{ minWidth: 360 }}>
             <strong>{commit.author.name}</strong>
             <span style={{ marginTop: 8 }}>
-              <a href={`/${orgSlug}/${repoSlug}/show/${commit.commit}`}>
+              <a
+                href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
+                  orgSlug,
+                  repoSlug,
+                  objectId: commit.commit,
+                })}
+              >
                 {subject}
               </a>
               {isSubjectTooLongForDisplay ? (

...
@@ -76,11 +84,23 @@ const RepositoryCommitSummaryLine: ReactIsland<RepositoryCommitSummaryLineProps>
             }}
           >
             <span>
-              <a href={`/${orgSlug}/${repoSlug}/show/${commit.commit}`}>
+              <a
+                href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
+                  orgSlug,
+                  repoSlug,
+                  objectId: commit.commit,
+                })}
+              >
                 {commit.abbreviated_commit}
               </a>
               {commit.abbreviated_parent.trim() != "" ? (
-                <a href={`/${orgSlug}/${repoSlug}/show/${commit.parent}`}>
+                <a
+                  href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
+                    orgSlug,
+                    repoSlug,
+                    objectId: commit.parent,
+                  })}
+                >
                   {` (parent ${commit.abbreviated_parent})`}
                 </a>
               ) : null}

app/islands/RepositoryFilesDiffsList.tsx
@@ -8,9 +8,11 @@ import type {
   RepositoryFileDiffChunk,
   WithThemeSchemeProp,
 } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Const } from "../const";
 import { Card } from "../components/Card.styled";
 import { Grid } from "../components/Grid";
+import { buildRouteLink } from "../utils/shared";
 // app islands
 import Code, { getThemedCodeCss } from "../islands/Code";
 

...
@@ -62,12 +64,28 @@ const RepositoryFilesDiffsList: ReactIsland<
                 </div>
                 <div style={{ marginLeft: 16 }}>
                   <a
-                    href={`/${orgSlug}/${repoSlug}/${commitHash}/tree/${diff.to}`}
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_BROWSER_WITH_PATH,
+                      {
+                        orgSlug,
+                        repoSlug,
+                        currentRef: commitHash,
+                        "*": diff.to,
+                      }
+                    )}
                   >
                     View file (current ref)
                   </a>
                   <a
-                    href={`/${orgSlug}/${repoSlug}/${Const.PRIMARY_BRANCH_REF}/tree/${diff.to}`}
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_BROWSER_WITH_PATH,
+                      {
+                        orgSlug,
+                        repoSlug,
+                        currentRef: Const.PRIMARY_BRANCH_REF,
+                        "*": diff.to,
+                      }
+                    )}
                     style={{ marginLeft: 16 }}
                   >
                     View file (${Const.PRIMARY_BRANCH_REF})

app/islands/RepositoryHero.tsx
@@ -6,7 +6,9 @@ import React from "react";
 import type { Organization, Repository } from "@prisma/client";
 // app
 import type { RepositoryForkedFromRepoMeta } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Grid } from "../components/Grid";
+import { buildRouteLink } from "../utils/shared";
 
 export interface RepositoryHeroProps {
   parentOrg: Organization;

...
@@ -34,11 +36,20 @@ const RepositoryHero: ReactIsland<RepositoryHeroProps> = ({
           style={{ marginTop: 8, minWidth: 468 }}
         >
           <h1 style={{ margin: 0 }}>
-            <a href={`/${parentOrg.slug}`}>
+            <a
+              href={buildRouteLink(AppRoute.ORGANIZATION_DETAILS, {
+                orgSlug: parentOrg.slug,
+              })}
+            >
               {parentOrg.displayName || parentOrg.slug}
             </a>
             {" / "}
-            <a href={`/${parentOrg.slug}/${repo.slug}`}>
+            <a
+              href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
+                orgSlug: parentOrg.slug,
+                repoSlug: repo.slug,
+              })}
+            >
               {repo.displayName || repo.slug}
             </a>
             {` ${separator} `}

...
@@ -55,13 +66,20 @@ const RepositoryHero: ReactIsland<RepositoryHeroProps> = ({
               <h5 style={{ margin: 0, marginTop: 8 }}>
                 <span>Forked From</span>
                 {" ∙ "}
-                <a href={`/${forkedFromRepo.organization.slug}`}>
+                <a
+                  href={buildRouteLink(AppRoute.ORGANIZATION_DETAILS, {
+                    orgSlug: forkedFromRepo.organization.slug,
+                  })}
+                >
                   {forkedFromRepo.organization.displayName ||
                     forkedFromRepo.organization.slug}
                 </a>
                 {" / "}
                 <a
-                  href={`/${forkedFromRepo.organization.slug}/${forkedFromRepo.slug}`}
+                  href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
+                    orgSlug: forkedFromRepo.organization.slug,
+                    repoSlug: forkedFromRepo.slug,
+                  })}
                 >
                   {forkedFromRepo.displayName || forkedFromRepo.slug}
                 </a>

...
@@ -70,7 +88,12 @@ const RepositoryHero: ReactIsland<RepositoryHeroProps> = ({
           </div>
           <div style={{ marginTop: 16 }}>
             <div>
-              <a href={`/${parentOrg.slug}/${repo.slug}/pulls`}>
+              <a
+                href={buildRouteLink(AppRoute.REPOSITORY_PULL_REQUESTS, {
+                  orgSlug: parentOrg.slug,
+                  repoSlug: repo.slug,
+                })}
+              >
                 Pull Requests
               </a>
             </div>

...
@@ -79,7 +102,10 @@ const RepositoryHero: ReactIsland<RepositoryHeroProps> = ({
         <Grid.Row nowrap style={{ minWidth: 90, marginTop: 8 }}>
           <span>{forksCount}</span>
           <a
-            href={`/${parentOrg.slug}/${repo.slug}/fork`}
+            href={buildRouteLink(AppRoute.REPOSITORY_FORK, {
+              orgSlug: parentOrg.slug,
+              repoSlug: repo.slug,
+            })}
             style={{ marginLeft: 8 }}
           >
             Fork it!

app/islands/RepositoryPullRequestCreateForm.tsx
@@ -10,9 +10,11 @@ import type {
   RepositoryFileDiff,
   RepositoryWithParentAndForkedFromRepos,
 } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Card } from "../components/Card.styled";
 import { Grid } from "../components/Grid";
 import { IslandWrapper } from "../components/IslandWrapper.styled";
+import { buildRouteLink } from "../utils/shared";
 // app islands
 import RepositoryFilesDiffsList from "./RepositoryFilesDiffsList";
 import PullRequestSourceSelect, {

...
@@ -183,7 +185,10 @@ const RepositoryPullRequestCreateForm: ReactIsland<RepositoryPullRequestCreateFo
         <Grid.Col fluid nowrap>
           <form
             method={"POST"}
-            action={`/${parentOrgSlug}/${repoSlug}/pulls/new`}
+            action={buildRouteLink(
+              AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION,
+              { orgSlug: parentOrgSlug, repoSlug }
+            )}
             style={{ width: "100%" }}
           >
             <Card themeScheme={themeScheme}>

...
@@ -263,7 +268,14 @@ const RepositoryPullRequestCreateForm: ReactIsland<RepositoryPullRequestCreateFo
               Try comparing two branches that have some differences before you
               can create a pull request from the produced diff.
             </p>
-            <a href={`/${parentOrgSlug}/${repoSlug}/pulls/new`}>Try again</a>
+            <a
+              href={buildRouteLink(AppRoute.REPOSITORY_PULL_REQUEST_CREATE, {
+                orgSlug: parentOrgSlug,
+                repoSlug,
+              })}
+            >
+              Try again
+            </a>
           </Grid.Col>
         );
       }

...
@@ -272,7 +284,10 @@ const RepositoryPullRequestCreateForm: ReactIsland<RepositoryPullRequestCreateFo
         <Grid.Col fluid nowrap>
           <form
             method={"POST"}
-            action={`/${parentOrgSlug}/${repoSlug}/pulls/new`}
+            action={buildRouteLink(
+              AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION,
+              { orgSlug: parentOrgSlug, repoSlug }
+            )}
             style={{ width: "100%" }}
           >
             <input type={"hidden"} name={"state_from"} value={state} />

...
@@ -371,7 +386,10 @@ const RepositoryPullRequestCreateForm: ReactIsland<RepositoryPullRequestCreateFo
         <Grid.Col fluid nowrap>
           <form
             method={"POST"}
-            // action={`/${data.source.parentOrg.slug}/${data.source.repo.slug}/pulls/new`}
+            action={buildRouteLink(
+              AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION,
+              { orgSlug: parentOrgSlug, repoSlug }
+            )}
           >
             <input type={"hidden"} name={"state_from"} value={state} />
             <button type={"submit"}>Submit the Pull Request</button>

app/islands/RepositoryTreeView.tsx
@@ -9,8 +9,11 @@ import type {
   RepositoryLog,
   WithThemeSchemeProp,
 } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Grid, TextEllipsis } from "../components";
 import { NamedColors } from "../utils/style";
+import { buildRouteLink } from "../utils/shared";
+// app islands
 // import RepositoryCommitSummaryLine from "./RepositoryCommitSummaryLine";
 
 export interface RepositoryTreeViewProps {

...
@@ -40,14 +43,22 @@ const RepositoryTreeView: ReactIsland<
         text: fileName,
         href:
           currentPath === "/"
-            ? `/${orgSlug}/${repoSlug}/${encodeURIComponent(
-                currentRef
-              )}/tree/${fileName}`
-            : `/${orgSlug}/${repoSlug}/${encodeURIComponent(currentRef)}/tree/${
-                currentPath.endsWith("/") || currentPath === ""
-                  ? currentPath
-                  : `${currentPath}/`
-              }${fileName}`,
+            ? buildRouteLink(AppRoute.REPOSITORY_BROWSER_WITH_PATH, {
+                orgSlug,
+                repoSlug,
+                currentRef: encodeURIComponent(currentRef),
+                "*": fileName,
+              })
+            : buildRouteLink(AppRoute.REPOSITORY_BROWSER_WITH_PATH, {
+                orgSlug,
+                repoSlug,
+                currentRef: encodeURIComponent(currentRef),
+                "*": `${
+                  currentPath.endsWith("/") || currentPath === ""
+                    ? currentPath
+                    : `${currentPath}/`
+                }${fileName}`,
+              }),
       };
     },
     [orgSlug, repoSlug, currentPath]

...
@@ -64,10 +75,13 @@ const RepositoryTreeView: ReactIsland<
 
   const prevPathLink =
     prevPath === "/"
-      ? `/${orgSlug}/${repoSlug}`
-      : `/${orgSlug}/${repoSlug}/${encodeURIComponent(currentRef)}/tree/${
-          prevPath.endsWith("/") ? prevPath : `${prevPath}/`
-        }`;
+      ? buildRouteLink(AppRoute.REPOSITORY_DETAILS, { orgSlug, repoSlug })
+      : buildRouteLink(AppRoute.REPOSITORY_BROWSER_WITH_PATH, {
+          orgSlug,
+          repoSlug,
+          currentRef: encodeURIComponent(currentRef),
+          "*": prevPath.endsWith("/") ? prevPath : `${prevPath}/`,
+        });
 
   return (
     <StyledRepositoryTreeViewContainer>

...
@@ -94,9 +108,15 @@ const RepositoryTreeView: ReactIsland<
                     title={`Go to "${currPathParts
                       .slice(0, idx + 1)
                       .join("/")}/" folder`}
-                    href={`/${orgSlug}/${repoSlug}/${encodeURIComponent(
-                      currentRef
-                    )}/tree/${currPathParts.slice(0, idx + 1).join("/")}/`}
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_BROWSER_WITH_PATH,
+                      {
+                        orgSlug,
+                        repoSlug,
+                        currentRef: encodeURIComponent(currentRef),
+                        "*": `${currPathParts.slice(0, idx + 1).join("/")}/`,
+                      }
+                    )}
                   >
                     <TextEllipsis>{pathPart}/</TextEllipsis>
                   </a>

...
@@ -106,9 +126,11 @@ const RepositoryTreeView: ReactIsland<
           <Grid.Row nowrap alignItems={"center"}>
             <a
               style={{ minWidth: "max-content" }}
-              href={`/${orgSlug}/${repoSlug}/${encodeURIComponent(
-                currentRef
-              )}/commits`}
+              href={buildRouteLink(AppRoute.REPOSITORY_COMMITS_LOG, {
+                orgSlug,
+                repoSlug,
+                currentRef: encodeURIComponent(currentRef),
+              })}
             >
               Commits History
             </a>

...
@@ -156,7 +178,11 @@ const RepositoryTreeView: ReactIsland<
                   {file.lastCommit != null && (
                     <StyledTreeViewListItemAnchor
                       style={{ flex: 1, marginLeft: 16 }}
-                      href={`/${orgSlug}/${repoSlug}/show/${file.lastCommit.commit}`}
+                      href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
+                        orgSlug,
+                        repoSlug,
+                        objectId: file.lastCommit.commit,
+                      })}
                       title={file.lastCommit.subject}
                     >
                       <span

new file
app/routes.defs.tsx
@@ -0,0 +1,937 @@
+// 1st-party
+import type { IRouteParams } from "@ethicdevs/react-monolith";
+// 3rd-party
+import type { FastifySchema } from "fastify";
+// generated via script[generate:prisma]
+import { ResourceVisibility } from "@prisma/client";
+// app
+import type { AppThemeScheme } from "./types";
+// app islands DTO's
+import { PullRequestFormState } from "./islands/RepositoryPullRequestCreateForm";
+
+export enum AppRoute {
+  HOME = "home",
+  THEME_SET_SCHEME_ACTION = "theme.set_scheme.action",
+  AUTH_REGISTER = "auth.register",
+  AUTH_REGISTER_ACTION = "auth.register.action",
+  AUTH_LOGIN = "auth.login",
+  AUTH_LOGIN_ACTION = "auth.login.action",
+  AUTH_LOGOUT_ACTION = "auth.logout.action",
+  ORGANIZATION_DETAILS = "organization.details",
+  REPOSITORY_BROWSER = "repository.browser",
+  REPOSITORY_BROWSER_WITH_PATH = "repository.browser.with_path",
+  REPOSITORY_COMMITS_LOG = "repository.commits_log",
+  REPOSITORY_COMPARE = "repository.compare",
+  REPOSITORY_CREATE = "repository.create",
+  REPOSITORY_CREATE_ACTION = "repository.create.action",
+  REPOSITORY_DETAILS = "repository.details",
+  REPOSITORY_DETAILS_WITH_TRAILING_SLASH = "repository.details.with_trailing_slash",
+  REPOSITORY_EXPLORE = "repository.explore",
+  REPOSITORY_FORK = "repository.fork",
+  REPOSITORY_FORK_ACTION = "repository.fork.action",
+  REPOSITORY_PULL_REQUEST_CLOSE_ACTION = "repository.pull_request.close.action",
+  REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION = "repository.pull_request.comment.create.action",
+  REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION = "repository.pull_request.comment.delete.action",
+  REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION = "repository.pull_request.comment.update.action",
+  REPOSITORY_PULL_REQUEST_CREATE = "repository.pull_request.create",
+  REPOSITORY_PULL_REQUEST_CREATE_ACTION = "repository.pull_request.create.action",
+  REPOSITORY_PULL_REQUEST_DELETE_ACTION = "repository.pull_request.delete.action",
+  REPOSITORY_PULL_REQUEST_DETAILS = "repository.pull_request.details",
+  REPOSITORY_PULL_REQUEST_MERGE_ACTION = "/:orgSlug/:repoSlug/pulls/:pullUid/merge",
+  REPOSITORY_PULL_REQUEST_UPDATE_ACTION = "repository.pull_request.update.action",
+  REPOSITORY_PULL_REQUESTS = "repository.pull_requests",
+  REPOSITORY_SHOW_OBJECT = "repository.show_object",
+  SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION = "syntax_highlight.highlight_code.action",
+  USER_DASHBOARD = "user.dashboard",
+  USER_DETAILS = "user.details",
+}
+
+export const AppRoutePaths: Record<AppRoute, string> = {
+  [AppRoute.HOME]: "/",
+  [AppRoute.THEME_SET_SCHEME_ACTION]: "/theme/:themeScheme",
+  [AppRoute.AUTH_REGISTER]: "/auth/register",
+  [AppRoute.AUTH_REGISTER_ACTION]: "/auth/register",
+  [AppRoute.AUTH_LOGIN]: "/auth/login",
+  [AppRoute.AUTH_LOGIN_ACTION]: "/auth/login",
+  [AppRoute.AUTH_LOGOUT_ACTION]: "/auth/logout",
+  [AppRoute.ORGANIZATION_DETAILS]: "/:orgSlug",
+  [AppRoute.REPOSITORY_BROWSER]: "/:orgSlug/:repoSlug/:currentRef/tree",
+  [AppRoute.REPOSITORY_BROWSER_WITH_PATH]:
+    "/:orgSlug/:repoSlug/:currentRef/tree/*",
+  [AppRoute.REPOSITORY_COMMITS_LOG]: "/:orgSlug/:repoSlug/:currentRef/commits",
+  [AppRoute.REPOSITORY_COMPARE]: "/:orgSlug/:repoSlug/compare/:refA..:refB",
+  [AppRoute.REPOSITORY_CREATE]: "/repo/new",
+  [AppRoute.REPOSITORY_CREATE_ACTION]: "/repo/new",
+  [AppRoute.REPOSITORY_DETAILS]: "/:orgSlug/:repoSlug",
+  [AppRoute.REPOSITORY_DETAILS_WITH_TRAILING_SLASH]: "/:orgSlug/:repoSlug/",
+  [AppRoute.REPOSITORY_EXPLORE]: "/repo/explore",
+  [AppRoute.REPOSITORY_FORK]: "/:orgSlug/:repoSlug/fork",
+  [AppRoute.REPOSITORY_FORK_ACTION]: "/:orgSlug/:repoSlug/fork",
+  [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/close",
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/comment",
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/comment/delete",
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/comment/edit",
+  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE]: "/:orgSlug/:repoSlug/pulls/new",
+  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/new",
+  [AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/delete",
+  [AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid",
+  [AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/merge",
+  [AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]:
+    "/:orgSlug/:repoSlug/pulls/:pullUid/edit",
+  [AppRoute.REPOSITORY_PULL_REQUESTS]: "/:orgSlug/:repoSlug/pulls",
+  [AppRoute.REPOSITORY_SHOW_OBJECT]: "/:orgSlug/:repoSlug/show/:objectId",
+  [AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]:
+    "/api/syntax/highlight/:outputFormat",
+  [AppRoute.USER_DASHBOARD]: "/dashboard",
+  [AppRoute.USER_DETAILS]: "/@:username",
+};
+
+export interface AppRoutesParams extends IRouteParams {
+  [AppRoute.HOME]: undefined;
+  [AppRoute.THEME_SET_SCHEME_ACTION]: {
+    params: {
+      themeScheme: AppThemeScheme;
+    };
+  };
+  [AppRoute.AUTH_REGISTER]: undefined;
+  [AppRoute.AUTH_REGISTER_ACTION]: {
+    body: {
+      email_address: string;
+      username: string;
+      password: string;
+    };
+  };
+  [AppRoute.AUTH_LOGIN]: undefined;
+  [AppRoute.AUTH_LOGIN_ACTION]: {
+    body: {
+      email_address: string;
+      password: string;
+    };
+  };
+  [AppRoute.AUTH_LOGOUT_ACTION]: undefined;
+  [AppRoute.ORGANIZATION_DETAILS]: {
+    params: {
+      orgSlug: string;
+    };
+  };
+  [AppRoute.REPOSITORY_BROWSER]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      currentRef: string;
+      "*": string;
+    };
+  };
+  [AppRoute.REPOSITORY_BROWSER_WITH_PATH]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      currentRef: string;
+      "*": string;
+    };
+  };
+  [AppRoute.REPOSITORY_COMMITS_LOG]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      currentRef: string;
+    };
+  };
+  [AppRoute.REPOSITORY_COMPARE]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      refA: string;
+      refB: string;
+    };
+  };
+  [AppRoute.REPOSITORY_CREATE]: undefined;
+  [AppRoute.REPOSITORY_CREATE_ACTION]: {
+    body: {
+      parent_org_slug: string;
+      repo_display_name: string;
+      repo_init_license_file: "on" | "off";
+      repo_init_license_kind: string;
+      repo_init_readme_file: "on" | "off";
+      repo_keywords: string;
+      repo_keywords_add: string;
+      repo_short_description: string;
+      repo_slug: string;
+      repo_visibility: ResourceVisibility;
+      repo_website_url: string;
+    };
+  };
+  [AppRoute.REPOSITORY_DETAILS]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+  };
+  [AppRoute.REPOSITORY_DETAILS_WITH_TRAILING_SLASH]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+  };
+  [AppRoute.REPOSITORY_EXPLORE]: undefined;
+  [AppRoute.REPOSITORY_FORK]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+  };
+  [AppRoute.REPOSITORY_FORK_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+    body: {
+      target_org_slug: string;
+      target_repo_display_name: string;
+      target_repo_slug: string;
+      target_repo_visibility: ResourceVisibility;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+    };
+    body: {
+      reason_message: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+    };
+    body: {}; // TODO: define this object shape
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+      commentId: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+      commentId: string;
+    };
+    body: {}; // TODO: define this object shape
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+    querystring: {
+      from_branch?: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+    body: {
+      state_from: PullRequestFormState;
+      state_dest: PullRequestFormState;
+      summary: string;
+      description: string;
+      source_parent_org_slug: string;
+      source_repository_slug: string;
+      source_repository_from_branch: string;
+      target_parent_org_slug: string;
+      target_repository_slug: string;
+      target_repository_dest_branch: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+    };
+    body: {
+      merge_summary: string;
+      merge_message: string;
+    };
+  };
+  [AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      pullUid: number;
+    };
+    body: {}; // TODO: define this object shape
+  };
+  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+  };
+  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+      objectId: string;
+    };
+  };
+  [AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]: {
+    params: {
+      outputFormat?: "html" | "json";
+    };
+    body: {
+      code: string;
+      language: string;
+      theme_scheme: AppThemeScheme;
+    };
+  };
+  [AppRoute.USER_DASHBOARD]: undefined;
+  [AppRoute.USER_DETAILS]: {
+    params: {
+      username: string;
+    };
+  };
+}
+
+export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
+  [AppRoute.HOME]: undefined,
+  [AppRoute.THEME_SET_SCHEME_ACTION]: {
+    params: {
+      type: "object",
+      required: ["themeScheme"],
+      additionalProperties: false,
+      properties: {
+        themeScheme: {
+          type: "string",
+          enum: ["light", "dark"],
+        },
+      },
+    },
+  },
+  [AppRoute.AUTH_REGISTER]: undefined,
+  [AppRoute.AUTH_REGISTER_ACTION]: {
+    body: {
+      type: "object",
+      required: ["email_address", "username", "password"],
+      additionalProperties: false,
+      properties: {
+        email_address: { type: "string" },
+        username: { type: "string" },
+        password: { type: "string" },
+      },
+    },
+  },
+  [AppRoute.AUTH_LOGIN]: undefined,
+  [AppRoute.AUTH_LOGIN_ACTION]: {
+    body: {
+      type: "object",
+      required: ["email_address", "password"],
+      additionalProperties: false,
+      properties: {
+        email_address: { type: "string" },
+        password: { type: "string" },
+      },
+    },
+  },
+  [AppRoute.AUTH_LOGOUT_ACTION]: undefined,
+  [AppRoute.ORGANIZATION_DETAILS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_BROWSER]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "currentRef", "*"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        currentRef: {
+          type: "string",
+        },
+        "*": {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_BROWSER_WITH_PATH]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "currentRef", "*"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        currentRef: {
+          type: "string",
+        },
+        "*": {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_COMMITS_LOG]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "currentRef"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        currentRef: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_COMPARE]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "refA", "refB"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        refA: {
+          type: "string",
+        },
+        refB: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_CREATE]: undefined,
+  [AppRoute.REPOSITORY_CREATE_ACTION]: {
+    body: {
+      type: "object",
+      required: [
+        "parent_org_slug",
+        "repo_display_name",
+        "repo_init_license_file",
+        "repo_init_license_kind",
+        "repo_init_readme_file",
+        "repo_keywords",
+        "repo_keywords_add",
+        "repo_short_description",
+        "repo_slug",
+        "repo_visibility",
+        "repo_website_url",
+      ],
+      additionalProperties: false,
+      properties: {
+        parent_org_slug: {
+          type: "string",
+        },
+        repo_display_name: {
+          type: "string",
+          minLength: 3,
+          maxLength: 64,
+        },
+        repo_init_license_file: {
+          type: "string",
+          enum: ["on", "off"],
+        },
+        repo_init_license_kind: {
+          type: "string",
+        },
+        repo_init_readme_file: {
+          type: "string",
+          enum: ["on", "off"],
+        },
+        repo_keywords: {
+          type: "string",
+        },
+        repo_keywords_add: {
+          type: "string",
+        },
+        repo_short_description: {
+          type: "string",
+          minLength: 10,
+          maxLength: 140,
+        },
+        repo_slug: {
+          type: "string",
+          minLength: 3,
+          maxLength: 64,
+        },
+        repo_visibility: {
+          type: "string",
+          enum: Object.values(ResourceVisibility),
+        },
+        repo_website_url: {
+          type: "string",
+          format: "uri",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_DETAILS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_DETAILS_WITH_TRAILING_SLASH]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_EXPLORE]: undefined,
+  [AppRoute.REPOSITORY_FORK]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+    body: {
+      type: "object",
+      required: [
+        "target_org_slug",
+        "target_repo_display_name",
+        "target_repo_slug",
+        "target_repo_visibility",
+      ],
+      additionalProperties: false,
+      properties: {
+        target_org_slug: {
+          type: "string",
+        },
+        target_repo_display_name: {
+          type: "string",
+        },
+        target_repo_slug: {
+          type: "string",
+        },
+        target_repo_visibility: {
+          type: "string",
+          enum: Object.values(ResourceVisibility),
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_FORK_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+    // body: {}, // TODO: define this object shape
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+    // body: {}, // TODO: define this object shape
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+    querystring: {
+      type: "object",
+      required: [],
+      additionalProperties: false,
+      properties: {
+        from_branch: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+    body: {
+      type: "object",
+      required: [
+        "state_from",
+        "state_dest",
+        // --
+        "summary",
+        "source_parent_org_slug",
+        "source_repository_slug",
+        "source_repository_from_branch",
+        "target_parent_org_slug",
+        "target_repository_slug",
+        "target_repository_dest_branch",
+      ],
+      additionalProperties: false,
+      properties: {
+        state_from: {
+          type: "string",
+        },
+        state_dest: {
+          type: "string",
+        },
+        summary: {
+          type: "string",
+        },
+        description: {
+          type: "string",
+        },
+        source_parent_org_slug: {
+          type: "string",
+        },
+        source_repository_slug: {
+          type: "string",
+        },
+        source_repository_from_branch: {
+          type: "string",
+        },
+        target_parent_org_slug: {
+          type: "string",
+        },
+        target_repository_slug: {
+          type: "string",
+        },
+        target_repository_dest_branch: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+    body: {
+      type: "object",
+      required: ["merge_summary"],
+      additionalProperties: false,
+      properties: {
+        merge_summary: {
+          type: "string",
+        },
+        merge_message: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "pullUid"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        pullUid: {
+          type: "number",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug", "objectId"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+        objectId: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]: {
+    params: {
+      type: "object",
+      required: [],
+      additionalProperties: false,
+      properties: {
+        outputFormat: {
+          type: "string",
+          enum: ["html", "json"],
+        },
+      },
+    },
+    body: {
+      type: "object",
+      required: ["code", "language", "theme_scheme"],
+      additionalProperties: false,
+      properties: {
+        code: {
+          type: "string",
+        },
+        language: {
+          type: "string",
+        },
+        theme_scheme: {
+          type: "string",
+          enum: ["light", "dark"],
+        },
+      },
+    },
+  },
+  [AppRoute.USER_DASHBOARD]: undefined,
+  [AppRoute.USER_DETAILS]: {
+    params: {
+      type: "object",
+      required: ["username"],
+      additionalProperties: false,
+      properties: {
+        username: {
+          type: "string",
+        },
+      },
+    },
+  },
+};

@@ -1,16 +1,10 @@
 // 1st-party
-import type { IRouteParams } from "@ethicdevs/react-monolith";
 import { AppRouter, AppRouterGroup, Router } from "@ethicdevs/react-monolith";
 // 3rd-party
-import type { FastifySchema } from "fastify";
 import React from "react";
-// generated via script[generate:prisma]
-import { ResourceVisibility } from "@prisma/client";
 // app
-import type { AppThemeScheme } from "./types";
+import { AppRoute, AppRoutePaths, AppRoutesSchemas } from "./routes.defs";
 import { authenticatedOrLogin, guestOrRedirect } from "./utils/server";
-// app islands
-import type { PullRequestFormState } from "./islands/RepositoryPullRequestCreateForm";
 // app controllers
 import {
   AuthController,

...
@@ -23,836 +17,12 @@ import {
   UserController,
 } from "./controllers";
 
-export enum AppRoute {
-  HOME = "home",
-  THEME_SET_SCHEME_ACTION = "theme.set_scheme.action",
-  AUTH_REGISTER = "auth.register",
-  AUTH_REGISTER_ACTION = "auth.register.action",
-  AUTH_LOGIN = "auth.login",
-  AUTH_LOGIN_ACTION = "auth.login.action",
-  AUTH_LOGOUT_ACTION = "auth.logout.action",
-  USER_DASHBOARD = "user.dashboard",
-  USER_DETAILS = "user.details",
-  ORGANIZATION_DETAILS = "organization.details",
-  REPOSITORY_BROWSER = "repository.browser",
-  REPOSITORY_COMMITS_LOG = "repository.commits_log",
-  REPOSITORY_COMPARE = "repository.compare",
-  REPOSITORY_CREATE = "repository.create",
-  REPOSITORY_CREATE_ACTION = "repository.create.action",
-  REPOSITORY_DETAILS = "repository.details",
-  REPOSITORY_EXPLORE = "repository.explore",
-  REPOSITORY_FORK = "repository.fork",
-  REPOSITORY_FORK_ACTION = "repository.fork.action",
-  REPOSITORY_PULL_REQUEST_CLOSE_ACTION = "repository.pull_request.close.action",
-  REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION = "repository.pull_request.comment.create.action",
-  REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION = "repository.pull_request.comment.delete.action",
-  REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION = "repository.pull_request.comment.update.action",
-  REPOSITORY_PULL_REQUEST_CREATE = "repository.pull_request.create",
-  REPOSITORY_PULL_REQUEST_CREATE_ACTION = "repository.pull_request.create.action",
-  REPOSITORY_PULL_REQUEST_DELETE_ACTION = "repository.pull_request.delete.action",
-  REPOSITORY_PULL_REQUEST_DETAILS = "repository.pull_request.details",
-  REPOSITORY_PULL_REQUEST_MERGE_ACTION = "repository.pull_request.merge.action",
-  REPOSITORY_PULL_REQUEST_UPDATE_ACTION = "repository.pull_request.update.action",
-  REPOSITORY_PULL_REQUESTS = "repository.pull_requests",
-  REPOSITORY_SHOW_OBJECT = "repository.show_object",
-  SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION = "syntax_highlight.highlight_code.action",
-}
-
-export interface AppRoutesParams extends IRouteParams {
-  [AppRoute.HOME]: undefined;
-  [AppRoute.THEME_SET_SCHEME_ACTION]: {
-    params: {
-      themeScheme: string;
-    };
-  };
-  [AppRoute.AUTH_REGISTER]: undefined;
-  [AppRoute.AUTH_REGISTER_ACTION]: {
-    body: {
-      email_address: string;
-      username: string;
-      password: string;
-    };
-  };
-  [AppRoute.AUTH_LOGIN]: undefined;
-  [AppRoute.AUTH_LOGIN_ACTION]: {
-    body: {
-      email_address: string;
-      password: string;
-    };
-  };
-  [AppRoute.AUTH_LOGOUT_ACTION]: undefined;
-  [AppRoute.USER_DASHBOARD]: undefined;
-  [AppRoute.USER_DETAILS]: {
-    params: {
-      username: string;
-    };
-  };
-  [AppRoute.ORGANIZATION_DETAILS]: {
-    params: {
-      orgSlug: string;
-    };
-  };
-  [AppRoute.REPOSITORY_BROWSER]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      currentRef: string;
-      "*": string;
-    };
-  };
-  [AppRoute.REPOSITORY_COMMITS_LOG]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      currentRef: string;
-    };
-  };
-  [AppRoute.REPOSITORY_COMPARE]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      refA: string;
-      refB: string;
-    };
-  };
-  [AppRoute.REPOSITORY_CREATE]: undefined;
-  [AppRoute.REPOSITORY_CREATE_ACTION]: {
-    body: {
-      parent_org_slug: string;
-      repo_display_name: string;
-      repo_init_license_file: "on" | "off";
-      repo_init_license_kind: string;
-      repo_init_readme_file: "on" | "off";
-      repo_keywords: string;
-      repo_keywords_add: string;
-      repo_short_description: string;
-      repo_slug: string;
-      repo_visibility: ResourceVisibility;
-      repo_website_url: string;
-    };
-  };
-  [AppRoute.REPOSITORY_DETAILS]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-    };
-  };
-  [AppRoute.REPOSITORY_EXPLORE]: undefined;
-  [AppRoute.REPOSITORY_FORK]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-    };
-  };
-  [AppRoute.REPOSITORY_FORK_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-    };
-    body: {
-      target_org_slug: string;
-      target_repo_display_name: string;
-      target_repo_slug: string;
-      target_repo_visibility: ResourceVisibility;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-    };
-    body: {
-      reason_message: string;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-    };
-    body: {}; // TODO: define this object shape
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-      commentId: string;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-      commentId: string;
-    };
-    body: {}; // TODO: define this object shape
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-    };
-    querystring: {
-      from_branch?: string;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-    };
-    body: {
-      state_from: PullRequestFormState;
-      state_dest: PullRequestFormState;
-      summary: string;
-      description: string;
-      source_parent_org_slug: string;
-      source_repository_slug: string;
-      source_repository_from_branch: string;
-      target_parent_org_slug: string;
-      target_repository_slug: string;
-      target_repository_dest_branch: string;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-    };
-    body: {
-      merge_summary: string;
-      merge_message: string;
-    };
-  };
-  [AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      pullUid: number;
-    };
-    body: {}; // TODO: define this object shape
-  };
-  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-    };
-  };
-  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
-    params: {
-      orgSlug: string;
-      repoSlug: string;
-      objectId: string;
-    };
-  };
-  [AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]: {
-    params: {
-      outputFormat?: "html" | "json";
-    };
-    body: {
-      code: string;
-      language: string;
-      theme_scheme: AppThemeScheme;
-    };
-  };
-}
-
-export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
-  [AppRoute.HOME]: undefined,
-  [AppRoute.THEME_SET_SCHEME_ACTION]: {
-    params: {
-      type: "object",
-      required: ["themeScheme"],
-      additionalProperties: false,
-      properties: {
-        themeScheme: {
-          type: "string",
-          enum: ["light", "dark"],
-        },
-      },
-    },
-  },
-  [AppRoute.AUTH_REGISTER]: undefined,
-  [AppRoute.AUTH_REGISTER_ACTION]: {
-    body: {
-      type: "object",
-      required: ["email_address", "username", "password"],
-      additionalProperties: false,
-      properties: {
-        email_address: { type: "string" },
-        username: { type: "string" },
-        password: { type: "string" },
-      },
-    },
-  },
-  [AppRoute.AUTH_LOGIN]: undefined,
-  [AppRoute.AUTH_LOGIN_ACTION]: {
-    body: {
-      type: "object",
-      required: ["email_address", "password"],
-      additionalProperties: false,
-      properties: {
-        email_address: { type: "string" },
-        password: { type: "string" },
-      },
-    },
-  },
-  [AppRoute.AUTH_LOGOUT_ACTION]: undefined,
-  [AppRoute.USER_DASHBOARD]: undefined,
-  [AppRoute.USER_DETAILS]: {
-    params: {
-      type: "object",
-      required: ["username"],
-      additionalProperties: false,
-      properties: {
-        username: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.ORGANIZATION_DETAILS]: {
-    params: {
-      type: "object",
-      required: ["orgSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_BROWSER]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "currentRef", "*"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        currentRef: {
-          type: "string",
-        },
-        "*": {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_COMMITS_LOG]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "currentRef"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        currentRef: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_COMPARE]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "refA", "refB"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        refA: {
-          type: "string",
-        },
-        refB: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_CREATE]: undefined,
-  [AppRoute.REPOSITORY_CREATE_ACTION]: {
-    body: {
-      type: "object",
-      required: [
-        "parent_org_slug",
-        "repo_display_name",
-        "repo_init_license_file",
-        "repo_init_license_kind",
-        "repo_init_readme_file",
-        "repo_keywords",
-        "repo_keywords_add",
-        "repo_short_description",
-        "repo_slug",
-        "repo_visibility",
-        "repo_website_url",
-      ],
-      additionalProperties: false,
-      properties: {
-        parent_org_slug: {
-          type: "string",
-        },
-        repo_display_name: {
-          type: "string",
-          minLength: 3,
-          maxLength: 64,
-        },
-        repo_init_license_file: {
-          type: "string",
-          enum: ["on", "off"],
-        },
-        repo_init_license_kind: {
-          type: "string",
-        },
-        repo_init_readme_file: {
-          type: "string",
-          enum: ["on", "off"],
-        },
-        repo_keywords: {
-          type: "string",
-        },
-        repo_keywords_add: {
-          type: "string",
-        },
-        repo_short_description: {
-          type: "string",
-          minLength: 10,
-          maxLength: 140,
-        },
-        repo_slug: {
-          type: "string",
-          minLength: 3,
-          maxLength: 64,
-        },
-        repo_visibility: {
-          type: "string",
-          enum: Object.values(ResourceVisibility),
-        },
-        repo_website_url: {
-          type: "string",
-          format: "uri",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_DETAILS]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_EXPLORE]: undefined,
-  [AppRoute.REPOSITORY_FORK]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-      },
-    },
-    body: {
-      type: "object",
-      required: [
-        "target_org_slug",
-        "target_repo_display_name",
-        "target_repo_slug",
-        "target_repo_visibility",
-      ],
-      additionalProperties: false,
-      properties: {
-        target_org_slug: {
-          type: "string",
-        },
-        target_repo_display_name: {
-          type: "string",
-        },
-        target_repo_slug: {
-          type: "string",
-        },
-        target_repo_visibility: {
-          type: "string",
-          enum: Object.values(ResourceVisibility),
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_FORK_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-    // body: {}, // TODO: define this object shape
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-    // body: {}, // TODO: define this object shape
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-      },
-    },
-    querystring: {
-      type: "object",
-      required: [],
-      additionalProperties: false,
-      properties: {
-        from_branch: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-      },
-    },
-    body: {
-      type: "object",
-      required: [
-        "state_from",
-        "state_dest",
-        // --
-        "summary",
-        "source_parent_org_slug",
-        "source_repository_slug",
-        "source_repository_from_branch",
-        "target_parent_org_slug",
-        "target_repository_slug",
-        "target_repository_dest_branch",
-      ],
-      additionalProperties: false,
-      properties: {
-        state_from: {
-          type: "string",
-        },
-        state_dest: {
-          type: "string",
-        },
-        summary: {
-          type: "string",
-        },
-        description: {
-          type: "string",
-        },
-        source_parent_org_slug: {
-          type: "string",
-        },
-        source_repository_slug: {
-          type: "string",
-        },
-        source_repository_from_branch: {
-          type: "string",
-        },
-        target_parent_org_slug: {
-          type: "string",
-        },
-        target_repository_slug: {
-          type: "string",
-        },
-        target_repository_dest_branch: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-    body: {
-      type: "object",
-      required: ["merge_summary"],
-      additionalProperties: false,
-      properties: {
-        merge_summary: {
-          type: "string",
-        },
-        merge_message: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "pullUid"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        pullUid: {
-          type: "number",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_PULL_REQUESTS]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.REPOSITORY_SHOW_OBJECT]: {
-    params: {
-      type: "object",
-      required: ["orgSlug", "repoSlug", "objectId"],
-      additionalProperties: false,
-      properties: {
-        orgSlug: {
-          type: "string",
-        },
-        repoSlug: {
-          type: "string",
-        },
-        objectId: {
-          type: "string",
-        },
-      },
-    },
-  },
-  [AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]: {
-    params: {
-      type: "object",
-      required: [],
-      additionalProperties: false,
-      properties: {
-        outputFormat: {
-          type: "string",
-          enum: ["html", "json"],
-        },
-      },
-    },
-    body: {
-      type: "object",
-      required: ["code", "language", "theme_scheme"],
-      additionalProperties: false,
-      properties: {
-        code: {
-          type: "string",
-        },
-        language: {
-          type: "string",
-        },
-        theme_scheme: {
-          type: "string",
-          enum: ["light", "dark"],
-        },
-      },
-    },
-  },
-};
-
 const RootAppRouter: AppRouter = () => {
-  const guestOrDashboardRedirect = guestOrRedirect("/dashboard");
+  const guestOrDashboardRedirect = guestOrRedirect(
+    AppRoutePaths[AppRoute.USER_DASHBOARD]
+  );
   const loggedOrLoginRedirect = authenticatedOrLogin();
+
   return (
     <Router.Root>
       <></>

...
@@ -860,7 +30,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.THEME_SET_SCHEME_ACTION}
           method={"GET"}
-          path={"/theme/:themeScheme"}
+          path={AppRoutePaths[AppRoute.THEME_SET_SCHEME_ACTION]}
           schema={AppRoutesSchemas[AppRoute.THEME_SET_SCHEME_ACTION]}
           handler={ThemeController.setThemeSchemeAction}
         />

...
@@ -868,7 +38,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.HOME}
           method={"GET"}
-          path={"/"}
+          path={AppRoutePaths[AppRoute.HOME]}
           preHandler={guestOrDashboardRedirect}
           handler={HomeController.getHomeView}
         />

...
@@ -876,14 +46,14 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.AUTH_REGISTER}
           method={"GET"}
-          path={"/auth/register"}
+          path={AppRoutePaths[AppRoute.AUTH_REGISTER]}
           preHandler={guestOrDashboardRedirect}
           handler={AuthController.getRegisterView}
         />
         <Router.Route
           name={AppRoute.AUTH_REGISTER_ACTION}
           method={"POST"}
-          path={"/auth/register"}
+          path={AppRoutePaths[AppRoute.AUTH_REGISTER_ACTION]}
           preHandler={guestOrDashboardRedirect}
           schema={AppRoutesSchemas[AppRoute.AUTH_REGISTER_ACTION]}
           handler={AuthController.postRegisterAction}

...
@@ -892,14 +62,14 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.AUTH_LOGIN}
           method={"GET"}
-          path={"/auth/login"}
+          path={AppRoutePaths[AppRoute.AUTH_LOGIN]}
           preHandler={guestOrDashboardRedirect}
           handler={AuthController.getLoginView}
         />
         <Router.Route
           name={AppRoute.AUTH_LOGIN_ACTION}
           method={"POST"}
-          path={"/auth/login"}
+          path={AppRoutePaths[AppRoute.AUTH_LOGIN_ACTION]}
           preHandler={guestOrDashboardRedirect}
           schema={AppRoutesSchemas[AppRoute.AUTH_LOGIN_ACTION]}
           handler={AuthController.postLoginAction}

...
@@ -907,7 +77,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.AUTH_LOGOUT_ACTION}
           method={"GET"}
-          path={"/auth/logout"}
+          path={AppRoutePaths[AppRoute.AUTH_LOGOUT_ACTION]}
           preHandler={loggedOrLoginRedirect}
           schema={AppRoutesSchemas[AppRoute.AUTH_LOGOUT_ACTION]}
           handler={AuthController.getLogoutAction}

...
@@ -916,21 +86,21 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.USER_DASHBOARD}
           method={"GET"}
-          path={"/dashboard"}
+          path={AppRoutePaths[AppRoute.USER_DASHBOARD]}
           preHandler={loggedOrLoginRedirect}
           handler={UserController.getUserDashboardView}
         />
         <Router.Route
           name={AppRoute.USER_DETAILS}
           method={"GET"}
-          path={"/@:username"}
+          path={AppRoutePaths[AppRoute.USER_DETAILS]}
           handler={UserController.getUserDetailsView}
         />
         {/* --- */}
         <Router.Route
           name={AppRoute.ORGANIZATION_DETAILS}
           method={"GET"}
-          path={"/:orgSlug"}
+          path={AppRoutePaths[AppRoute.ORGANIZATION_DETAILS]}
           schema={AppRoutesSchemas[AppRoute.ORGANIZATION_DETAILS]}
           handler={OrganizationController.getOrganizationDetailsView}
         />

...
@@ -938,42 +108,42 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_BROWSER}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/:currentRef/tree"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_BROWSER]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_BROWSER]}
           handler={RepositoryController.getRepositoryBrowserView}
         />
         <Router.Route
-          name={AppRoute.REPOSITORY_BROWSER + ".with_path"}
+          name={AppRoute.REPOSITORY_BROWSER_WITH_PATH}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/:currentRef/tree/*"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_BROWSER_WITH_PATH]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_BROWSER]}
           handler={RepositoryController.getRepositoryBrowserView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_COMMITS_LOG}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/:currentRef/commits"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_COMMITS_LOG]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_COMMITS_LOG]}
           handler={RepositoryController.getRepositoryCommitsLogView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_COMPARE}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/compare/:refA..:refB"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_COMPARE]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_COMPARE]}
           handler={RepositoryController.getRepositoryCompareView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_CREATE}
           method={"GET"}
-          path={"/repo/new"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_CREATE]}
           preHandler={loggedOrLoginRedirect}
           handler={RepositoryController.getRepositoryCreateView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_CREATE_ACTION}
           method={"POST"}
-          path={"/repo/new"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_CREATE_ACTION]}
           preHandler={loggedOrLoginRedirect}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_CREATE_ACTION]}
           handler={RepositoryController.postRepositoryCreateAction}

...
@@ -981,41 +151,41 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_DETAILS}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_DETAILS]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_DETAILS]}
           handler={RepositoryController.getRepositoryDetailsView}
         />
         <Router.Route
-          name={AppRoute.REPOSITORY_DETAILS}
+          name={AppRoute.REPOSITORY_DETAILS_WITH_TRAILING_SLASH}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_DETAILS_WITH_TRAILING_SLASH]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_DETAILS]}
           handler={RepositoryController.getRepositoryDetailsView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_EXPLORE}
           method={"GET"}
-          path={"/repo/explore"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_EXPLORE]}
           handler={RepositoryController.getRepositoryExploreView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_FORK}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/fork"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_FORK]}
           preHandler={loggedOrLoginRedirect}
           handler={RepositoryController.getRepositoryForkView}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_FORK_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/fork"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_FORK_ACTION]}
           preHandler={loggedOrLoginRedirect}
           handler={RepositoryController.postRepositoryForkAction}
         />
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/close"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]}
           schema={
             AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_CLOSE_ACTION]
           }

...
@@ -1025,11 +195,17 @@ const RootAppRouter: AppRouter = () => {
           }
         />
         <Router.Route
-          name={AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION}
+          name={AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/comment"}
+          path={
+            AppRoutePaths[
+              AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION
+            ]
+          }
           schema={
-            AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]
+            AppRoutesSchemas[
+              AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_CREATE_ACTION
+            ]
           }
           preHandler={loggedOrLoginRedirect}
           handler={

...
@@ -1037,33 +213,45 @@ const RootAppRouter: AppRouter = () => {
           }
         />
         <Router.Route
-          name={AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION}
+          name={AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/comment/edit"}
+          path={
+            AppRoutePaths[
+              AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION
+            ]
+          }
           schema={
-            AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]
+            AppRoutesSchemas[
+              AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_DELETE_ACTION
+            ]
           }
           preHandler={loggedOrLoginRedirect}
           handler={
-            RepositoryPullRequestsController.getRepositoryPullRequestCommentUpdateAction
+            RepositoryPullRequestsController.getRepositoryPullRequestCommentDeleteAction
           }
         />
         <Router.Route
-          name={AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION}
+          name={AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/comment/delete"}
+          path={
+            AppRoutePaths[
+              AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION
+            ]
+          }
           schema={
-            AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]
+            AppRoutesSchemas[
+              AppRoute.REPOSITORY_PULL_REQUEST_COMMENT_UPDATE_ACTION
+            ]
           }
           preHandler={loggedOrLoginRedirect}
           handler={
-            RepositoryPullRequestsController.getRepositoryPullRequestCommentDeleteAction
+            RepositoryPullRequestsController.getRepositoryPullRequestCommentUpdateAction
           }
         />
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_CREATE}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/pulls/new"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_CREATE]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_CREATE]}
           preHandler={loggedOrLoginRedirect}
           handler={

...
@@ -1073,7 +261,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/new"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]}
           schema={
             AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_CREATE_ACTION]
           }

...
@@ -1085,7 +273,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/delete"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]}
           schema={
             AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION]
           }

...
@@ -1097,7 +285,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_DETAILS}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]}
           preHandler={loggedOrLoginRedirect}
           handler={

...
@@ -1107,7 +295,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/merge"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]}
           schema={
             AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]
           }

...
@@ -1119,7 +307,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION}
           method={"POST"}
-          path={"/:orgSlug/:repoSlug/pulls/:pullUid/edit"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]}
           schema={
             AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION]
           }

...
@@ -1131,7 +319,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_PULL_REQUESTS}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/pulls"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_PULL_REQUESTS]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_PULL_REQUESTS]}
           handler={
             RepositoryPullRequestsController.getRepositoryPullRequestsView

...
@@ -1140,7 +328,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.REPOSITORY_SHOW_OBJECT}
           method={"GET"}
-          path={"/:orgSlug/:repoSlug/show/:objectId"}
+          path={AppRoutePaths[AppRoute.REPOSITORY_SHOW_OBJECT]}
           schema={AppRoutesSchemas[AppRoute.REPOSITORY_SHOW_OBJECT]}
           handler={RepositoryController.getRepositoryShowObjectView}
         />

...
@@ -1148,7 +336,7 @@ const RootAppRouter: AppRouter = () => {
         <Router.Route
           name={AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION}
           method={"POST"}
-          path={"/api/syntax/highlight/:outputFormat"}
+          path={AppRoutePaths[AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]}
           schema={
             AppRoutesSchemas[AppRoute.SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION]
           }

app/utils/server/authenticatedOrLogin.ts
@@ -1,6 +1,6 @@
 import type { preHandlerHookHandler } from "fastify";
 // app
-import { AppRoute } from "../../routes";
+import { AppRoute } from "../../routes.defs";
 
 export const authenticatedOrLogin =
   (): preHandlerHookHandler => async (request, reply) => {

new file
app/utils/shared/buildRouteLink.ts
@@ -0,0 +1,68 @@
+import { AppRoute, AppRoutePaths, AppRoutesParams } from "../../routes.defs";
+
+export default function buildRouteLink<P extends AppRoute>(
+  route: P,
+  routeParams: "params" extends keyof AppRoutesParams[P]
+    ? AppRoutesParams[P]["params"]
+    : {} | null
+): typeof AppRoutePaths[P] {
+  const path = AppRoutePaths[route];
+
+  if (
+    routeParams == null ||
+    (routeParams != null &&
+      typeof routeParams === "object" &&
+      Object.keys(routeParams as any).length <= 0)
+  ) {
+    return path;
+  }
+
+  const paramsEntries =
+    typeof routeParams === "object" ? Object.entries(routeParams as never) : [];
+
+  let pathParts = path.split("/");
+  let linkBuilder = [] as string[];
+
+  paramsEntries.forEach(([k, v]) => {
+    const keyRegExp = new RegExp(`:${k}`, "g");
+    pathParts = pathParts.map((part) => {
+      if (Array.isArray(part.match(keyRegExp))) {
+        return part.replace(keyRegExp, `${String(v)}`);
+      } else if (k === "*" && part === "*") {
+        return String(v);
+      }
+      return part;
+    });
+  });
+
+  pathParts.map((part) => linkBuilder.push(part));
+  return linkBuilder.join("/");
+}
+
+export function buildPathLink<P extends AppRoute>(
+  path: string,
+  routeParams: "params" extends keyof AppRoutesParams[P]
+    ? AppRoutesParams[P]["params"]
+    : undefined
+): string {
+  const paramsEntries =
+    typeof routeParams === "object" ? Object.entries(routeParams as never) : [];
+
+  let pathParts = path.split("/");
+  let linkBuilder = [] as string[];
+
+  paramsEntries.forEach(([k, v]) => {
+    const keyRegExp = new RegExp(`:${k}`, "g");
+    pathParts = pathParts.map((part) => {
+      if (Array.isArray(part.match(keyRegExp))) {
+        return part.replace(keyRegExp, `${String(v)}`);
+      } else if (k === "*" && part === "*") {
+        return String(v);
+      }
+      return part;
+    });
+  });
+
+  pathParts.map((part) => linkBuilder.push(part));
+  return linkBuilder.join("/");
+}

app/utils/shared/index.ts
@@ -1,4 +1,5 @@
+export { escapeHtmlCode } from "./escapeHtmlCode";
+export { default as buildRouteLink } from "./buildRouteLink";
 export { default as getFormEntries } from "./getFormEntries";
-export { default as slugify } from "./slugify";
 export { default as getGitdiffLineStart } from "./getGitdiffLineStart";
-export { escapeHtmlCode } from "./escapeHtmlCode";
+export { default as slugify } from "./slugify";

app/views/HomeView.tsx
@@ -5,7 +5,9 @@ import styled from "styled-components";
 
 // app
 import type { CommonProps } from "../types";
+import { AppRoute } from "../routes.defs";
 import { ButtonAnchor, Layout, PageWrapper } from "../components";
+import { buildRouteLink } from "../utils/shared";
 
 export interface HomeViewProps extends CommonProps {
   foo?: boolean;

...
@@ -17,9 +19,13 @@ const HomeView: ReactView<HomeViewProps> = (props) => {
     <Layout {...commonProps}>
       <PageWrapper>
         <StyledButtonsRow>
-          <ButtonAnchor href={"/auth/register"}>Register</ButtonAnchor>
-          <ButtonAnchor href={"/auth/login"}>Login</ButtonAnchor>
-          <ButtonAnchor href={"/repo/explore"}>
+          <ButtonAnchor href={buildRouteLink(AppRoute.AUTH_REGISTER, {})}>
+            Register
+          </ButtonAnchor>
+          <ButtonAnchor href={buildRouteLink(AppRoute.AUTH_LOGIN, {})}>
+            Login
+          </ButtonAnchor>
+          <ButtonAnchor href={buildRouteLink(AppRoute.REPOSITORY_EXPLORE, {})}>
             Explore Repositories
           </ButtonAnchor>
         </StyledButtonsRow>

app/views/InternalErrorView.tsx
@@ -5,7 +5,9 @@ import React from "react";
 
 // app
 import type { CommonProps } from "../types";
+import { AppRoute } from "../routes.defs";
 import { Card, Layout, PageWrapper } from "../components";
+import { buildRouteLink } from "../utils/shared";
 // app islands
 import Code, { getThemedCodeCss } from "../islands/Code";
 

...
@@ -60,7 +62,7 @@ const InternalErrorView: ReactView<InternalErrorViewProps> = ({
         <div style={{ marginTop: 8 }}>
           {!isNotFoundError &&
             (isRecoverableError ? (
-              <a href="/" role={"button"}>
+              <a href={buildRouteLink(AppRoute.HOME, null)} role={"button"}>
                 Try again 🔄
               </a>
             ) : (

app/views/auth/LoginView.tsx
@@ -2,7 +2,9 @@ import type { ReactView } from "@ethicdevs/react-monolith";
 import React from "react";
 // app
 import type { CommonProps } from "../../types";
+import { AppRoute } from "../../routes.defs";
 import { Button, Card, Layout, PageWrapper, TextInput } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
 
 export interface LoginViewProps extends CommonProps {
   errorMessage?: null | string;

...
@@ -42,7 +44,10 @@ const LoginView: ReactView<LoginViewProps> = ({
           themeScheme={commonProps.themeScheme}
           style={{ marginTop: errorMessage != null ? 16 : 0, padding: 16 }}
         >
-          <form action={`/auth/login`} method={"POST"}>
+          <form
+            method={"POST"}
+            action={buildRouteLink(AppRoute.AUTH_LOGIN_ACTION, {})}
+          >
             {/* Email Address */}
             <div style={{ marginTop: 0 }}>
               <label htmlFor={"username"} style={{ fontWeight: "bold" }}>

app/views/auth/RegisterView.tsx
@@ -2,7 +2,9 @@ import type { ReactView } from "@ethicdevs/react-monolith";
 import React from "react";
 // app
 import type { CommonProps } from "../../types";
+import { AppRoute } from "../../routes.defs";
 import { Button, Card, Layout, PageWrapper, TextInput } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
 
 export interface RegisterViewProps extends CommonProps {
   errorMessage?: null | string;

...
@@ -43,7 +45,10 @@ const RegisterView: ReactView<RegisterViewProps> = ({
           themeScheme={commonProps.themeScheme}
           style={{ marginTop: errorMessage != null ? 16 : 0, padding: 16 }}
         >
-          <form action={`/auth/register`} method={"POST"}>
+          <form
+            method={"POST"}
+            action={buildRouteLink(AppRoute.AUTH_REGISTER_ACTION, {})}
+          >
             {/* Email Address */}
             <div>
               <label htmlFor={"username"} style={{ fontWeight: "bold" }}>

app/views/repository/RepositoryCreateView.tsx
@@ -6,7 +6,9 @@ import React from "react";
 import { Organization } from "@prisma/client";
 // app
 import type { CommonProps } from "../../types";
+import { AppRoute } from "../../routes.defs";
 import { Layout, PageWrapper } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
 // app islands
 import RepositoryCreateForm from "../../islands/RepositoryCreateForm";
 

...
@@ -39,7 +41,10 @@ const RepositoryCreateView: ReactView<RepositoryCreateViewProps> = ({
             <p>{errorMessage}</p>
           </div>
         )}
-        <form action={`/repo/new`} method={"POST"}>
+        <form
+          action={buildRouteLink(AppRoute.REPOSITORY_CREATE_ACTION, {})}
+          method={"POST"}
+        >
           <div data-islandid={`${RepositoryCreateForm.name}$$0`}>
             <RepositoryCreateForm
               availableParentOrgs={availableParentOrgs}

app/views/repository/RepositoryDetailsView.tsx
@@ -13,6 +13,7 @@ import type {
   RepositoryLog,
   RepositoryWithForkedFromRepo,
 } from "../../types";
+import { AppRoute } from "../../routes.defs";
 import { NamedColors } from "../../utils/style";
 import {
   Card,

...
@@ -23,6 +24,7 @@ import {
   MarkdownToJsx,
   PageWrapper,
 } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
 // app islands
 import RepositoryCommitSummaryLine from "../../islands/RepositoryCommitSummaryLine";
 import RepositoryHero from "../../islands/RepositoryHero";

...
@@ -235,13 +237,15 @@ const RepositoryDetailsView: ReactView<RepositoryDetailsViewProps> = ({
                     branch.trim() != "" && (
                       <React.Fragment key={branch}>
                         <a
-                          href={`/${parentOrg.slug}/${
-                            repo.slug
-                          }/${encodeURIComponent(branch)}/tree/${
-                            path != null && path.trim() !== "" && path !== "/"
-                              ? path
-                              : ""
-                          }`}
+                          href={buildRouteLink(AppRoute.REPOSITORY_BROWSER, {
+                            orgSlug: parentOrg.slug,
+                            repoSlug: repo.slug,
+                            currentRef: encodeURIComponent(branch),
+                            "*":
+                              path != null && path.trim() !== "" && path !== "/"
+                                ? path
+                                : "",
+                          })}
                         >
                           {branch}
                         </a>

app/views/repository/RepositoryForkView.tsx
@@ -6,7 +6,9 @@ import React from "react";
 import { Organization, ResourceVisibility } from "@prisma/client";
 // app
 import type { CommonProps, RepositoryWithForkedFromRepo } from "../../types";
+import { AppRoute } from "../../routes.defs";
 import { IslandWrapper, Layout, PageWrapper } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
 // app islands
 import RepositoryForkForm from "../../islands/RepositoryForkForm";
 import RepositoryHero from "../../islands/RepositoryHero";

...
@@ -51,8 +53,11 @@ const RepositoryForkView: ReactView<RepositoryForkViewProps> = ({
           </div>
         )}
         <form
-          action={`/${sourceParentOrg.slug}/${sourceRepo.slug}/fork`}
           method={"POST"}
+          action={buildRouteLink(AppRoute.REPOSITORY_FORK_ACTION, {
+            orgSlug: sourceParentOrg.slug,
+            repoSlug: sourceRepo.slug,
+          })}
         >
           <div data-islandid={`${RepositoryForkForm.name}$$0`}>
             <RepositoryForkForm

app/views/repositoryPullRequests/RepositoryPullRequestDetailsView.tsx
@@ -11,6 +11,8 @@ import type {
   RepositoryObject,
   RepositoryWithForkedFromRepo,
 } from "../../types";
+import { AppRoute } from "../../routes.defs";
+import { buildRouteLink } from "../../utils/shared";
 import {
   Card,
   Grid,

...
@@ -32,6 +34,7 @@ export interface RepositoryPullRequestDetailsViewProps extends CommonProps {
   sourceRepo: RepositoryWithForkedFromRepo;
   targetParentOrg: Organization;
   targetRepo: RepositoryWithForkedFromRepo;
+  isCurrentUserAllowedToMerge?: boolean;
 }
 
 const RepositoryPullRequestDetailsView: ReactView<RepositoryPullRequestDetailsViewProps> =

...
@@ -44,6 +47,7 @@ const RepositoryPullRequestDetailsView: ReactView<RepositoryPullRequestDetailsVi
     sourceRepo,
     targetParentOrg: parentOrg,
     targetRepo: repo,
+    isCurrentUserAllowedToMerge = false,
   }) => {
     return (
       <Layout {...commonProps}>

...
@@ -107,16 +111,50 @@ const RepositoryPullRequestDetailsView: ReactView<RepositoryPullRequestDetailsVi
               </Grid.Row>
             </Grid.Col>
             <Grid.Col fluid style={{ marginTop: 32 }}>
-              <a
-                href={`/${parentOrg.slug}/${repo.slug}/pulls/${pr.uid}?action=edit`}
-              >
-                Edit PR
-              </a>
-              <a
-                href={`/${parentOrg.slug}/${repo.slug}/pulls/${pr.uid}?action=delete`}
-              >
-                Delete PR
-              </a>
+              <Grid.Row fluid>
+                <Grid.Col fluid>
+                  <a
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_PULL_REQUEST_UPDATE_ACTION,
+                      {
+                        orgSlug: parentOrg.slug,
+                        repoSlug: repo.slug,
+                        pullUid: pr.uid,
+                      }
+                    )}
+                  >
+                    Edit PR
+                  </a>
+                  <a
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_PULL_REQUEST_DELETE_ACTION,
+                      {
+                        orgSlug: parentOrg.slug,
+                        repoSlug: repo.slug,
+                        pullUid: pr.uid,
+                      }
+                    )}
+                  >
+                    Delete PR
+                  </a>
+                  <a href={buildRouteLink(AppRoute.HOME, {})}>Home</a>
+                </Grid.Col>
+                {isCurrentUserAllowedToMerge && (
+                  <Grid.Col>
+                    <form
+                      method={"POST"}
+                      action={buildRouteLink(
+                        AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION,
+                        {
+                          orgSlug: parentOrg.slug,
+                          repoSlug: repo.slug,
+                          pullUid: pr.uid,
+                        }
+                      )}
+                    ></form>
+                  </Grid.Col>
+                )}
+              </Grid.Row>
             </Grid.Col>
             <Grid.Col fluid style={{ marginTop: 24 }}>
               <Card

app/views/repositoryPullRequests/RepositoryPullRequestsView.tsx
@@ -6,7 +6,9 @@ import React from "react";
 import type { Organization, PullRequest } from "@prisma/client";
 // app
 import type { CommonProps, RepositoryWithForkedFromRepo } from "../../types";
+import { AppRoute } from "../../routes.defs";
 import { Grid, IslandWrapper, Layout, PageWrapper } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
 // app islands
 import RepositoryHero from "../../islands/RepositoryHero";
 

...
@@ -40,7 +42,12 @@ const RepositoryPullRequestsView: ReactView<RepositoryPullRequestsViewProps> =
             />
           </IslandWrapper>
           <Grid.Col fluid style={{ marginTop: 32 }}>
-            <a href={`/${parentOrg.slug}/${repo.slug}/pulls/new`}>
+            <a
+              href={buildRouteLink(AppRoute.REPOSITORY_PULL_REQUEST_CREATE, {
+                orgSlug: parentOrg.slug,
+                repoSlug: repo.slug,
+              })}
+            >
               New Pull Request
             </a>
           </Grid.Col>

...
@@ -100,7 +107,16 @@ const RepositoryPullRequestsView: ReactView<RepositoryPullRequestsViewProps> =
                   fluid
                   style={{ marginTop: idx === 0 ? 0 : 16 }}
                 >
-                  <a href={`/${parentOrg.slug}/${repo.slug}/pulls/${pr.uid}`}>
+                  <a
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_PULL_REQUEST_DETAILS,
+                      {
+                        orgSlug: parentOrg.slug,
+                        repoSlug: repo.slug,
+                        pullUid: pr.uid,
+                      }
+                    )}
+                  >
                     #{pr.uid} - {pr.summary} [{pr.state}]
                   </a>
                   <span style={{ opacity: 0.67 }}>

...
@@ -142,7 +158,15 @@ const RepositoryPullRequestsView: ReactView<RepositoryPullRequestsViewProps> =
                 <h1>No Pull Request Yet</h1>
                 <p>
                   <span>Be the change you want to see, </span>
-                  <a href={`/${parentOrg.slug}/${repo.slug}/pulls/new`}>
+                  <a
+                    href={buildRouteLink(
+                      AppRoute.REPOSITORY_PULL_REQUEST_CREATE,
+                      {
+                        orgSlug: parentOrg.slug,
+                        repoSlug: repo.slug,
+                      }
+                    )}
+                  >
                     open the first Pull Request
                   </a>
                   <span> to this repository 🚀.</span>