Extending with plugins
Extend the client with plugins that observe searches through typed events.
OpenPlaces extends Node's EventEmitter, so every client is also an event source. As a search runs, it emits two typed events: "query" once when a stream begins, and "place" for every place the stream yields, after deduplication and where filtering. Anything that wants to watch a search subscribes to these two events.
Subscribe with the standard EventEmitter API — listeners are typed, so place needs no casting:
import { OpenPlaces } from "openplaces";
const client = new OpenPlaces();
client.on("query", (query) => console.log(`query: ${query}`));
client.on("place", (place) => console.log(`name: ${place.name}`));
await client.places.search("coffee in amsterdam", { limit: 20 });Because places.search drains places.stream, the same events fire regardless of which method you call, and if you break out of a stream early, no further events fire — see Streaming search results.
Writing a plugin
A plugin is a function that receives the client:
type Plugin = (client: OpenPlaces) => void;The constructor invokes every function value of its options object as a plugin, so listeners are attached before any search can begin and never miss an event. A minimal logging plugin looks like this:
const logger: OpenPlaces.Plugin = (client) => {
client.on("query", (query) => console.error(`[logger] ${query}`));
client.on("place", (place) => console.error(`[logger] ${place.name}`));
};
const client = new OpenPlaces({ logger });The core OpenPlaces.Options interface declares no properties of its own — it exists to be extended. Published plugins augment it with declare module "openplaces" so their option key is typed for consumers, and once any such augmentation is in your program (@openplaces/atlas below does exactly that), TypeScript rejects undeclared keys like logger. Declaring a local key the same way keeps it compiling either way:
declare module "openplaces" {
namespace OpenPlaces {
interface Options {
logger?: OpenPlaces.Plugin;
}
}
}Atlas, the shipped plugin
Atlas, the visual inspector, is exactly such a plugin. It subscribes to query and place, forwards each event over a local bridge to the inspector UI, and opens it in your browser — all from the same two events shown above.
import { atlas } from "@openplaces/atlas";
const client = new OpenPlaces({ atlas });The @openplaces/atlas package augments OpenPlaces.Options with an atlas key, so the option typechecks once the package is imported. See Inspecting with Atlas for setup.