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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@openfin/search-api

v1.5.9

Published

A search API framework for OpenFin.

Downloads

4

Readme

Workspace Search

The Search Extension is a framework for searching, fetching, aggregating and actioning data through application interopability.

Getting Started

There are two options for using this package.

Import functions from NPM package and bundle:

import { create, subscribe } from '@openfin/search-api';

Or import the script fron our CDN and use the fin.Search global namespace.

<script src="https://cdn.openfin.co/search-api/index.js" />
<script>
    const create = fin.Search.create;
    const subscribe = fin.Search.subscribe;
</script>

API Reference

More in depth API documentation can be found here.

Examples

Searching for Data

// It is recommended to bundle and import search API functions.
// However, the global `fin.Search` namespace can be used as an alternative.
import { subscribe } from '@openfin/search-api';

// Subscribe to a search topic.
const searchTopic = await subscribe({ topic: 'foo' });

/* Search for data with query `bar`.
   This will return a generator which can be called multiple times
   while there are pending responses from search data providers. */
const generator = searchTopic.search({ query: 'bar' });
while (true) {
    const { value, done } = await generator.next();

    // Implementation specific rendering logic.
    render(results);

    // If done is true, all search providers have responded with their data.
    if (done) {
        break;
    }
}

// When done with search request, call close.
generator.close();

Registering a Search Provider

// It is recommended to bundle and import search API functions.
// However, the global `fin.Search` namespace can be used as an alternative.
import { subscribe } from '@openfin/search-api';

const openWindowAction = 'Open Window';

// Example search handler that returns results from a backend query.
async function getSearchResults({ query }) {
    const res = await fetch('/search?q=' + encodeURIComponent(query));
    if (!res.ok) {
        throw new Error('uh oh something went wrong');
    }
    const json = await res.json();

    /* Return initial search results.

       All results must have the `name` and `description`
       field at the very least for rendering purposes. */
    return json.map((myResult) => ({
        name: myResult.nameAttribute,
        shortDescription: myResult.shortDescriptionAttribute,
        description: myResult.descriptionAttribute,
        actions: [openWindowAction], // Dispatchable actions for this search result.
        data: myResult.customMetadata
    }));
}

// Example function for actioning a result from `getSearchResults`.
function openWindowForSearchResult(result) {
    const dispatchedAction = result.action; // `result.action` is set to the action that was dispatched.
    if (dispatchedAction !== openWindowAction) return;
    window.open(result.data.url);
}

// Subscribe to a search topic.
const searchTopic = await subscribe({ topic: 'foo' });

/* The `name` and `onSearch` attributes are required for a data provider.

   The `onSearch` function will be called back when the topic is searched
   on. (ex. `searchTopic.search("my query")`)

   The `onResultDispatch` function will be called back when a search result
   is dispatched. (ex. `searchTopic.dispatch("Provider Name", searchResult, "Open Window")`) */
const provider = {
    name: 'bar',
    onSearch: getSearchResults,
    onResultDispatch: openWindowForSearchResult
};

// Register the search data provider.
await searchTopic.register(provider);

Actioning Search Results

// It is recommended to bundle and import search API functions.
// However, the global `fin.Search` namespace can be used as an alternative.
import { subscribe } from '@openfin/search-api';

// Subscribe to a search topic.
const searchTopic = await subscribe({ topic: 'foo' });

// Get search results.
const generator = searchTopic.search({ query: 'bar' });
const { value } = await generator.next();

/* Dispatches the first search result in the first response back to the
   respective provider, such that the provider can action the result. */
const firstSearchProviderResponse = value[0];
const firstSearchProviderName = firstSearchProviderResponse.provider.name;
const firstSearchResult = firstSearchProviderResponse.results[0];
const firstSearchAction = firstSearchResult.actions[0];
await searchTopic.dispatch(firstSearchProviderName, firstSearchResult, firstSearchAction); // Omitting will default to first action in `result.actions`.

Control Search Topic Subscriptions

// It is recommended to bundle and import search API functions.
// However, the global `fin.Search` namespace can be used as an alternative.
import { create } from "@openfin/search-api";

const searchTopic = await create({ topic: "foo" });

// Only hosts in the list can subscribe to the search topic.
const allowedHosts = ["www.vendor.com"];
searchTopic.onSubscription(identity => {
    // Get the URL of the subscribing identity.
    const info = await fin.View.wrapSync(identity).getInfo();
    const url = new URL(info.url);

    return allowedHosts.includes(url.host);
});

List Search Providers

// It is recommended to bundle and import search API functions.
// However, the global `fin.Search` namespace can be used as an alternative.
import { create } from '@openfin/search-api';

// Subscribe or create a search topic.
const searchTopic = await create({ topic: 'foo' });

// Returns a list of provider info objects.
const info = await searchTopic.getAllProviderInfo();

for (let provider of info) {
    console.log(`Provider Name: ${provider.name}, Openfin Identity: ${provider.identity}`);
}

Searching Specific Providers

// It is recommended to bundle and import search API functions.
// However, the global `fin.Search` namespace can be used as an alternative.
import { create } from '@openfin/search-api';

// Subscribe or create a search topic.
const searchTopic = await create({ topic: 'foo' });

// Only searches against providers in the provider name list.
searchTopic.searchProviders(['Provider Name 1', 'Provider Name 2'], 'bar');

Pushing Search Results

For simple use cases, the Search Extension allows search providers to register an async handler function that returns a set of search results. Internally the Search Extension uses a pull architecture, allowing the search requester to pull in an initial set of results from all search providers listening in on a search topic.

// A simple search handler that returns an initial set of search results.
async function getSearchResults({ query }) {
    const res = await fetch('/search?q=' + encodeURIComponent(query));
    if (!res.ok) {
        throw new Error('uh oh something went wrong');
    }
    const json = await res.json();

    // These results will be pulled in by the search requester.
    return json.map((myResult) => ({
        name: myResult.nameAttribute,
        shortDescription: myResult.shortDescriptionAttribute,
        description: myResult.descriptionAttribute,
        actions: [openWindowAction],
        data: myResult.customMetadata
    }));
}

For a more complex use case, like a long running query, it might be desired to push new or updated search results to the search requester after a long period of time. For said use case, you can use the search listener response object as highlighted in the example below.

/* An advanced search handler that pushes new or updated search results
   as long as the search request has not been closed. */
async function getSearchResults(request, response) {
    /* ID of the search request.
       Can be used to tie related search providers together. */
    const id = request.id;

    // The search query.
    const query = request.query;

    /* ▼ PUSH ARCHITECTURE ▼ */

    /* Open the response stream, notifying the search requester that
       there are new or updated search results that have yet to be pushed
       by the current provider. */
    response.open();

    const myLongRunningQuery = makeMyLongRunningQuery(query);

    // On new or updated search results push them to the search requester.
    const onNewResults = (myResults) => {
        // Map the new results.
        const newResults = myResults.map((myResult) => ({
            key: myResult.keyAttribute,
            name: myResult.nameAttribute,
            shortDescription: myResult.shortDescriptionAttribute,
            description: myResult.descriptionAttribute,
            actions: [openWindowAction],
            data: myResult.customMetadata
        }));

        /* Push the new results to the search requester.

         If the `key` attribute matches a previously pushed search result,
         the old result will be updated with the new result's content. */
        response.respond(newResults);
    };
    myLongRunningQuery.onNewResults(onNewResults);

    /* Remove the listener and close the long running query if
       the request has been closed by the search requester. */
    request.onClose(() => {
        myLongRunningQuery.close();
    });

    /**
     * Upon query completion, close the response. This notifies
     * the requester that the current search provider is done sending results.
     */
    myLongRunningQuery.onQueryDone(() => {
        response.close();
    });
}