PAZATOR DOCS
PLUGINS
PLUGIN SYSTEM; v0.May1526

How to
Build Plugins.

Architecture

Every plugin is a plain JS IIFE (immediately-invoked function expression) that calls window.pazatorPlugins.register() with a plugin definition object. There are no dependencies, no build step, no package manager. You can install a plugin by dropping a URL or pasting code directly into the Plugins tab.

The plugin system lives in app/js/core/plugins.js (~485 lines). It also houses Tastur, the app-wide event bus. The system manages lifecycle (register, init, enable, disable, unload), persistence of enabled/disabled states, and a UI manager with search/filter cards.

Plugin Definition

The object you pass to register() has these fields:

{
  id:          string,       // unique, e.g. "my-awesome-plugin"
  name:        string,       // display name
  version:     string,       // semver, e.g. "1.0.0"
  description: string,       // short blurb
  icon:        string,       // Font Awesome class, e.g. "fas fa-rocket"
  author:      string,       // your name/handle

  // Lifecycle hooks
  onLoad:      function(api),   // called when enabled
  onUnload:    function(api),   // called when disabled

  // UI extensions
  tabs:        [TabDef],        // register new tab(s)
  menuItems:   [MenuItemDef],   // add menu entries

  // Event handlers
  handlers:    { eventName: function(data) }
}

Minimal Example

(function () {
  'use strict';
  window.pazatorPlugins.register({
    id: 'hello-pazator',
    name: 'Hello Pazator',
    version: '0.1.0',
    description: 'Shows a greeting when enabled',
    icon: 'fas fa-wave',
    author: 'You',
    onLoad: function () {
      alert('Hello from Pazator!');
    }
  });
})();

Paste that into the Plugins tab code area and hit install. That's it.

Plugin API

Your onLoad and onUnload hooks receive an api object with these capabilities:

api.store — Data Store

Direct reference to pazatorStore. Full CRUD access to all entities:

api.store.getHumans()           // array of Person entities
api.store.getOthers()           // array of Organization/entity records
api.store.getTags()             // array of tags
api.store.getCases()            // array of cases
api.store.getRelatedObjects()   // graph edges
api.store.searchObjects(query)  // search across entities

api.events — Tastur Event Bus

Subscribe to app events or emit your own:

var unsub = api.events.on('entity_changed', function (data) {
  console.log('Entity changed:', data.id, data.action);
});
// call unsub() to clean up (or let deinitPlugin do it via handlers)

Built-in events you can hook into:

  • entity_changed{id, type, action}
  • humans_changed / others_changed
  • data_changed — any data mutation
  • data_saved / data_loaded
  • threat_detected — TIDE scan result
  • search_performed{query, results}

api.settings — KV Store

Persist plugin settings (stored in IndexedDB KV store):

api.settings.get('myKey').then(function (val) { ... });
api.settings.set('myKey', someValue);

api.modals — Modal Helpers

api.modals.alert('Message');           // simple alert
api.modals.confirm('Proceed?');         // returns boolean
api.modals.show({title, body, actions});// custom modal

api.tab — Register Tabs

Add an entirely new tab to the app with api.tab.register(cfg):

api.tab.register({
  id: 'my-tab',
  label: 'My Tab',
  render: function (container) {
    container.innerHTML = '<h2>Hello from plugin!</h2>';
  }
});

This automatically adds a tab button in the sidebar and a content panel. Remove with api.tab.unregister('my-tab').

api.menu — Menu Items

Add items to the app's menu bar:

api.menu.register({
  menu: 'tools',     // 'view' | 'data' | 'tools' | 'system' | 'help'
  label: 'My Action',
  icon: 'fas fa-bolt',
  action: function () { /* handle click */ }
});
api.menu.unregister('my-action-id');

api.log(msg) — Logging

Logs with your plugin's ID as prefix: [Plugin:my-plugin] msg.

Event Handlers (handlers)

Instead of manually subscribing in onLoad, you can list handlers in the plugin definition. The system subscribes/unsubscribes them automatically on enable/disable:

window.pazatorPlugins.register({
  id: 'example',
  name: 'Example',
  ...
  handlers: {
    entity_changed: function (data) {
      if (data.action === 'add' && data.type === 'human') {
        api.modals.alert('New person added!');
      }
    },
    data_saved: function () {
      console.log('Data saved');
    }
  }
});

Installation Methods

1. From URL

Host your plugin JS anywhere (a gist, your own server, a CDN), paste the URL into the Plugins tab's URL input, and click Install. The app loads it via a dynamic <script> tag.

2. From Code

Paste the full plugin IIFE source directly into the code textarea on the Plugins tab. The system wraps it in a new Function('api', code) call, so you can also write code that uses the api parameter directly.

3. Built-in (bundled in app/js/plugins/)

Place a .js file in app/js/plugins/ and load it in app/index.html. See server-search.js as a reference — it's a bundled plugin that's disabled by default.

Real Example: Server Search

The built-in Server Search plugin (app/js/plugins/server-search.js) shows the full pattern:

  • Wraps everything in an IIFE
  • Adds a menu item under the Data menu via menuItems
  • Opens a modal on click (full custom DOM, no helper)
  • Fetches from the PZLS search API
  • Registers with window.pazatorPlugins.register()

Read the source for a complete real-world example of DOM creation, API calls, async flows, and cleanup.

Disable / Uninstall

Users can toggle your plugin on/off from the Plugins tab cards. When toggled off, the system calls onUnload, unsubscribes all handlers, removes registered tabs and menu items. State is persisted across page reloads.

Best Practices

  • Wrap in an IIFE to avoid leaking globals
  • Clean up in onUnload (remove DOM, intervals, etc.)
  • Use api.settings for persistence, not localStorage directly
  • Prefix any custom CSS classes with plugin- to avoid conflicts
  • Catch errors in async code so a broken plugin doesn't break the app

Future

The plugin system is minimal by design. Upcoming additions may include a plugin marketplace, sandboxed iframe isolation, and a richer API surface for hooks into the analysis pipeline.