[Plugin examples]

Worked AXIS example plugins: CoinGecko source, Tiny Pine engine, Cloudflare DO stream — load paths and contracts.

Plugin examples

Abstract

AXIS ships three loadable example plugins as ES modules under frontend/public/plugins/ (also mirrored for reference under frontend/src/plugins/example-*.js). They are not built-ins; install via Manager → Plugins → Load from URL.

Full narrative: frontend/src/plugins/README.md.

Conceptual model

Loading

  1. Serve the PWA (Vite dev, preview, or axis_pwa_server.py on dist).
  2. Open Manager → Plugins.
  3. Paste a same-origin URL, for example:
http://127.0.0.1:8081/plugins/example-coingecko-source.js
http://127.0.0.1:3000/plugins/example-tiny-pine-engine.js
http://127.0.0.1:8081/plugins/example-cf-do-stream.js
  1. Load — registry updates; pickers show the new plugin.
  2. Persistence: localStorage pynescript.axis.plugins.v1 restores on next visit.

You may host modules on any origin that allows module CORS (same-origin is simplest).

Example catalog

FileidkindPurpose
example-coingecko-source.jscoingeckosourcePublic CoinGecko market_chart → synthetic OHLC
example-tiny-pine-engine.jstiny-pineengineIn-browser JS DSL: sma/ema/rsi/plot/strategy
example-cf-do-stream.jscf-dostreamWS to Worker /api/stream SessionDO

Paths:

  • Served: frontend/public/plugins/
  • Source copies: frontend/src/plugins/example-*.js

CoinGecko source

Contract: kind: 'source', fetchHistorical.

Config: baseUrl (default CoinGecko v3), vsCurrency (default usd).

Behavior:

  • Maps common symbols (BTCbitcoin, …).
  • Requests market_chart for a day window derived from interval × bar budget.
  • Synthesizes OHLC from consecutive price points (API is not full candles).

Limits: public rate limits (~10–30 calls/min); CORS depends on CoinGecko policy.

Use when: demo custom source without a key.


Tiny Pine engine

Contract: kind: 'engine', isReady, run.

Behavior:

  • Tokenizes a tiny expression language.
  • Built-ins: close, open, high, low, volume, sma, ema, rsi, plot, strategy.entry / strategy.close.
  • Returns RunResult with plots/events — not full PYNE fidelity.

Use when: offline demos without loading ~14MB Pyodide or hitting Flask.


Cloudflare DO stream

Contract: kind: 'stream', start → stop.

Config: endpoint — Worker base URL (https://… or http://127.0.0.1:8787). Empty → error.

URL built:

{endpoint as wss}/api/stream?session={symbol}@{interval}&symbol=…&interval=…

Requires: Worker with SESSIONS Durable Object bound and deployed (durable objects).

Use when: many tabs share one upstream Binance kline subscription.


Minimal authoring templates

Source

export default {
  id: 'my-source',
  name: 'My Source',
  kind: 'source',
  description: 'Shown in Manager',
  configSchema: {
    baseUrl: { type: 'string', default: 'https://example.com', label: 'Base URL' },
  },
  async fetchHistorical({ symbol, interval, config }) {
    const res = await fetch(`${config.baseUrl}/bars?symbol=${symbol}&interval=${interval}`);
    return res.json(); // Bar[]
  },
};

Stream

export default {
  id: 'my-stream',
  name: 'My Stream',
  kind: 'stream',
  start({ symbol, interval, onBar, onError, onStatus }) {
    const ws = new WebSocket('wss://example.com/stream');
    ws.onopen = () => onStatus({ state: 'open' });
    ws.onmessage = (ev) => {
      const b = JSON.parse(ev.data);
      onBar({ time: b.t, open: b.o, high: b.h, low: b.l, close: b.c, volume: b.v });
    };
    ws.onerror = () => onError(new Error('ws error'));
    return () => ws.close();
  },
};

Engine

export default {
  id: 'my-engine',
  name: 'My Engine',
  kind: 'engine',
  async isReady() { return true; },
  async run({ script, bars }) {
    return { status: 'success', plots: bars.map((b) => b.close), events: [], series: {}, meta: {} };
  },
};

Storage plugins cannot be loaded from URL yet.

Invariants & edge cases

  1. Treat plugin URLs as executable code.
  2. normalizePluginUrl rewrites /src/plugins//plugins/ for production.
  3. Dangerous schemes rejected (javascript:, vbscript:, data:text/html).
  4. After load, window may emit product-specific events (see README) for UI extensions.

Failure modes

IssueFix
Failed to restore URL404 after deploy path change
CORS on CoinGeckoOffline mock or proxy
DO stream errorsBind SessionDO; set endpoint
Tiny engine parse errorsUnsupported DSL constructs

See also