npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@atlassianintegrations/polaris-forge-object-resolver

v0.1.7

Published

a set of small helper functions for building Forge-powered Polaris Object Providers ✨

Readme

Polaris Forge Object Resolver

The foundation of each Forge function is the Polaris Forge Object Resolver. This package handles the core logic regarding the process of resolving third party data into Polaris JSON schema. It is made up of two parts, Client and Provider.


Client

The Client is responsible for making calls out to third parties. This includes interacting with the Forge infrastructure to make requests with the correct OAuth2 tokens, as well as serializing and deserializing the response body to the correct format. It also handles error responses and returning the necessary responses to complete the OAuth2 flow when required. It uses the Forge API under the hood to make requests.

Creating your Client

The client is used in the foundation of a Forge Object Provider. It accepts the following:

  • baseUrl: A base URL for your API that will be suffixed to every request. (e.g. https://www.googleapis.com)
  • outboundAuthorization: An object containing the following used to set up authentication attributes:
    • authMethod: This is set before the Authorization header. Default set to 'Bearer'.
    • authKey: This references the externalAuth.key in your manifest.yml
    • authHeaders: Any additional headers you may want to pass into the requeset.
  • authHeaderName: Auth header name. Defaults to Authorization (Optional)

Making Requests with the Client

Requests are made with the client with the following methods:

  • GET
  • POST
  • PUT
  • DELETE

They may be invoked both as methods (e.g. client.get('www.google.com', <funtion opts>) or as a function param (e.g. client.request('www.google.com', {method: 'GET', ...})). Headers and query params may be passed as the second function argument.

Client Examples

Here's us instantiating a client:

new Client({
  baseUrl: 'https://www.googleapis.com',
  outboundAuthorization: { authKey: 'my-auth-key', authMethod: 'Token', authHeaders: { myHeader1: 'myHeader' } },
  authHeaderName: 'Authorization', // optional
});

Here's a simple example where we make a get request with some headers:

export const getFile = async (client: Client, { fileId: any }) => {
  return client.get(`api.fishsticks.com/v1/${fileId}`, {
    query: { queryParam1: 'someParam' },
    headers: { header1: 'someHeader' },
    body: { bodyAttribute1: 'someData' },
  });
};

Provider

The provider orchestrates the flow in which third party data should be resolved into Polaris JSON schema. The provider can take one of two forms depending on the type of data the developer wishes to resolve.

ObjectProvider

The object provider is responsible for resolver data related to links. Links corresponds to URLs pasted in the editor. linkResolvers required in the constructor of the ObjectProvider.

Creating a ObjectResolver

The ObjectResolver accepts the following:

  • Client: This is the client mentioned above, it is used to create and send requests to third parties.
  • linkResolver: A Forge Object Provider linkResolver, as seen below.
  • errorHandlers: Maps error codes / errors to error handlers.

Executing a ObjectResolver

A ObjectResolver is run simply by using the execute method. It accepts an EventPayload, which is composed of a ResolvePayload which looks like the following:

  • type: determines whether it is a 'resolve' payload
  • payload: Contains data pertaining to the ResolvePayload
    • ResolvePayload:
      • resourceUrl: Contains a URL to resolve

e.g. return await objectResolver.execute({type: 'resolve', {payload: resourceUrl: 'test.com'}})

linkResolver Concepts

To write a Forge Object Provider linkResolver, you need to understand the three-step process involved:

  1. Patterns: first, an incoming URL or request is matched against a set of regex patterns with named groups. E.g. https://drive.google.com/file/123 will extract 123 as a fileId;
  2. Resolvers: second, the results of the match are provided to a resolver function. This function fetches raw JSON data from the third-party API;
  3. Formatters: third, the result of the third-party API calls are transformed to Polaris JSON schema.

The final result, the Polaris JSON schema response

linkResolvers: {
  file: {
    pattern: [/^https:\/\/dogs\.com\.\/(<?dogName>.*?)$/],
    resolver: async (client, url, matches) => await client.get(`/api/get/${matches.dogName}`);
    formatter: (data) => ({
        "type": "card",
        "group": {
          "name": "New Dogs",
          "id": "dogs_1"
        },
        "context": {
          "url": "https://dog-house.com",
          "icon":  "https://dog-house.com/icon.png",
          "title": "House of dogs"
        },
        "content": {
          "description": "'In the Doghouse' is a science fiction short story by American writers Orson Scott Card and Jay A Parry."
        }
    }),
  },
}
ObjectResolver Example

Here's a full example, with an instantiated Client passed into the ObjectProvider. Taking each of the three steps above into account, a set of utilities have been provided for your use. To write a Polaris JSON schema object provider for, say, Dogs API, we may choose to do the following:

import {
  ObjectProvider,
  Client,
  EventPayload,
  ResolverResponse,
  ClientError,
  defaults,
} from '@atlassian/polaris-forge-object-resolver';

export async function run(event: EventPayload): Promise<ResolverResponse> {
  const provider = new ObjectProvider({
    client: new Client({
      baseUrl: 'https://api.dogs.com',
      outboundAuthorization: { authKey: 'woofer' },
      authHeaderName: 'Authorization', // optional
    }),
    linkResolvers: {
      file: {
        pattern: [/^https:\/\/dogs\.com\.\/(<?dogName>.*?)$/],
        resolver: async (client, url, matches) => await client.get(`/api/get/${matches.dogName}`),
        formatter: data => ({
          type: 'card',
          group: {
            name: 'New Dogs',
            id: 'dogs_1',
          },
          context: {
            url: 'https://dog-house.com',
            icon: 'https://dog-house.com/icon.png',
            title: 'House of dogs',
          },
          content: {
            description:
              "'In the Doghouse' is a science fiction short story by American writers Orson Scott Card and Jay A Parry.",
          },
          properties: {
            hotdog_price: {
              name: 'Hotdog Price',
              value: 100,
            },
          },
        }),
      },
    },
    errorHandlers: {
      401: () => ({ meta: defaults.meta.unauthorized, data: undefined }),
      403: () => ({ meta: defaults.meta.permissionDenied, data: undefined }),
      404: () => ({ meta: defaults.meta.notFound, data: undefined }),
    },
  });
  return await provider.execute(event);
}

The key detail here is to ensure your authKey matches up with the externalAuth.key in your manifest.yml. If everything else is setup correctly, you are good to go!


ObjectResolver Example for "api_key" auth type
export async function runPendo(event: EventPayload): Promise<ResolverResponse> {
  const provider = new ObjectProvider({
    client: new Client({
      baseUrl: 'https://app.pendo.io',
      authHeaderName: 'x-pendo-integration-key', // optional, by default is "Authorization"
    }),
    linkResolvers: {
      message: {
        pattern: [/^https:\/\/app\.pendo\.io\/pages\/(?<pageId>.+)$/],
        resolver: async (client, url, matches) => {
          const response = await client.get(`/api/v1/page/?=${matches.pageId}`);
          return { page: response[0], url };
        },
        formatter: ({ page, url }) => {
          return {
            type: 'card',
            group: {
              name: 'New Dogs',
              id: 'dogs_1',
            },
            context: {
              icon: 'https://pendo.io/icon.png',
              url: url,
              title: `${page.kind} ${page.id}`,
            },
            properties: {
              createdByUser: {
                name: 'Created by user',
                value: `${page.createdByUser.first} ${page.createdByUser.last}`,
              },
            },
          };
        },
      },
    },
    errorHandlers: {
      401: () => ({ meta: defaults.meta.unauthorized, data: undefined }),
      403: () => ({ meta: defaults.meta.unauthorized, data: undefined }),
    },
  });

  /* Setting custom auth token decorator is optional, default implementation is:
   * const defaultAuthTokenDecorator = (authToken: string) => `Basic ${Buffer.from(authToken).toString('base64')}`;
   */
  provider.setAuthTokenDecorator(authToken => authToken);

  return await provider.execute(event);
}