Skip to content

# Lens Protocol Handlers

Lens has a file association with the `lens://` protocol. This means that Lens can be opened by external programs by providing a link that has `lens` as its protocol. Lens provides a routing mechanism that extensions can use to register custom handlers.

## Registering A Protocol Handler

The field `protocolHandlers` exists both on `LensMainExtension` and on `LensRendererExtension`. This field will be iterated through every time a `lens://` request gets sent to the application. The `pathSchema` argument must comply with the path-to-regexp package's `compileToRegex` function.

Once you have registered a handler it will be called when a user opens a link on their computer. Handlers will be run in both `main` and `renderer` in parallel with no synchronization between the two processes. Furthermore, both `main` and `renderer` are routed separately. In other words, which handler is selected in either process is independent from the list of possible handlers in the other.

Example of registering a handler:

    import { Main, Common } from "@k8slens/extensions";

    function rootHandler(params: Common.Types.ProtocolRouteParams) {
      console.log("routed to ExampleExtension", params);
    }

    export default class ExampleExtensionMain extends Main.LensExtension {
      protocolHandlers = [
        pathSchema: "/",
        handler: rootHandler,
      ]
    }

For testing the routing of URIs the `open` (on macOS) or `xdg-open` (on most linux) CLI utilities can be used. For the above handler, the following URI would be always routed to it:

    open lens://extension/example-extension/

## Deregistering A Protocol Handler

All that is needed to deregister a handler is to remove it from the array of handlers.

## Routing Algorithm

The routing mechanism for extensions is quite straight forward. For example consider an extension `example-extension` which is published by the `@mirantis` org. If it were to register a handler with `"/display/:type"` as its corresponding link then we would match the following URI like this:

Once matched, the handler would be called with the following argument (note both `"search"` and `"pathname"` will always be defined):

    {
      "search": {
        "text": "Hello"
      },
      "pathname": {
        "type": "notification"
      }
    }

As the diagram above shows, the search (or query) params are not considered as part of the handler resolution. If the URI had instead been `lens://extension/@mirantis/example-extension/display/notification/green` then a third (and optional) field will have the rest of the path. The `tail` field would be filled with `"/green"`. If multiple `pathSchema`'s match a given URI then the most specific handler will be called.

For example consider the following `pathSchema`'s:

1. `"/"`
2. `"/display"`
3. `"/display/:type"`
4. `"/show/:id"`

The URI sub-path `"/display"` would be routed to #2 since it is an exact match. On the other hand, the subpath `"/display/notification"` would be routed to #3.

The URI is routed to the most specific matching `pathSchema`. This way the `"/"` (root) `pathSchema` acts as a sort of catch all or default route if no other route matches.