@weavix/tracker-plugin-sdk
v0.1.7
Published
Core package for Tracker plugin SDK
Readme
@weavix/tracker-plugin-sdk
Core package for Tracker plugins (Weavix / npm).
Provides low-level host API, typed Tracker Public API proxy, UI helpers, shared storage, and event system.
For React applications, use
@weavix/tracker-plugin-sdk-react— it providesTrackerPluginProvider,useTrackerPluginContext, hooks, and all exports from this package.
Installation
npm install @weavix/tracker-plugin-sdkPeer dependencies: @weavix/tracker-api-plugin, @weavix/tracker-core.
Quick start
import { hostApi, trackerApi } from '@weavix/tracker-plugin-sdk';
// Initialise the plugin bridge (call once at startup)
hostApi.init({ autoResize: true });
// Get current theme
const theme = await hostApi.getTheme(); // 'light' | 'light-hc' | 'dark' | 'dark-hc' | 'system' | undefined
// Get current language
const language = await hostApi.getLanguage(); // 'ru' | 'en' | undefined
// Get slot context (e.g. current issue for slot 'issue.action')
const context = await hostApi.getContext();
// Notify the host that the plugin is ready
await hostApi.notifyReady();
// Resize the plugin container
await hostApi.updateContentSize({ height: 500 });Tracker API
All Tracker Public API calls go through trackerApi.v3 — a typed proxy over HTTP methods. Keys are OpenAPI endpoint paths from @weavix/tracker-api-types; the IDE provides autocomplete and JSDoc.
Full endpoint reference: Tracker API Reference.
import { trackerApi } from '@weavix/tracker-plugin-sdk';
// GET — returns { data, headers }
const { data: issue } = await trackerApi.v3.get['/issues/{id}']({
pathParams: { id: 'QUEUE-123' },
queryParams: { expand: ['COMMENTS'] },
});
// POST
await trackerApi.v3.post['/v2/issues']({
bodyParams: { queue: { key: 'TASK' }, summary: 'New issue' },
});
// PATCH
await trackerApi.v3.patch['/v2/issues/{id}']({
pathParams: { id: 'QUEUE-123' },
bodyParams: { summary: 'Updated title' },
});
// DELETE
await trackerApi.v3.delete['/v2/issues/{id}']({
pathParams: { id: 'QUEUE-123' },
});Queues
// List queues
const { data: queues } = await trackerApi.v3.get['/v2/queues']({
queryParams: { page: 1, perPage: 100 },
});
// Get a specific queue
const { data: queue } = await trackerApi.v3.get['/v2/queues/{id}']({
pathParams: { id: 'MYQUEUE' },
});Issues
// Get issue by key
const { data: issue } = await trackerApi.v3.get['/issues/{id}']({
pathParams: { id: 'QUEUE-123' },
});
// Create issue
const { data: newIssue } = await trackerApi.v3.post['/v2/issues']({
bodyParams: {
queue: { key: 'QUEUE' },
summary: 'Issue title',
},
});
// Search issues
const { data: issues } = await trackerApi.v3.post['/v2/issues/_search']({
bodyParams: { filter: { queue: 'QUEUE' } },
});hostApi
Singleton for communicating with the host. Call hostApi.init() before using any other method.
hostApi.init(options)
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| autoResize | boolean | true | Automatically resize the iframe height when content changes |
Reading context
const theme = await hostApi.getTheme(); // 'light' | 'light-hc' | 'dark' | 'dark-hc' | 'system' | undefined
const lang = await hostApi.getLanguage(); // 'ru' | 'en' | ...
const userId = await hostApi.getUserId(); // string | undefined
const orgId = await hostApi.getOrgId(); // string | undefined
const isYateam = await hostApi.getIsYateam(); // boolean
const ctx = await hostApi.getContext(); // SlotContextMap[slot]Synchronous getters (available after init())
const slot = hostApi.getSlot(); // string — current slot name
const service = hostApi.getService(); // string — service identifier
const queryParams = hostApi.getQueryParams(); // Record<string, string>
const entityId = hostApi.getEntityId(); // string | null
const entityMeta = hostApi.getEntityMeta(); // Record<string, string> | undefined
const ctxLevel = hostApi.getContextLevel(); // 'basic' | 'full'Plugin lifecycle
await hostApi.notifyReady();
await hostApi.updateContentSize({ height: 500 });
hostApi.disableAutoResize();
await hostApi.close({ reason: 'done' });
await hostApi.preventClose({ prevent: true });Toast notifications
Show toast notifications in the host application via uiApi.toaster.
Permission: Requires
"toaster"inpermissions.uiof the plugin manifest.
import { uiApi } from '@weavix/tracker-plugin-sdk';
// Simple toast
uiApi.toaster.add({
title: 'Saved',
theme: 'success',
});
// Toast with body text and custom duration
uiApi.toaster.add({
title: 'Error',
theme: 'danger',
content: 'Failed to load data',
autoHiding: 10000,
});
// Toast with action button
uiApi.toaster.add({
title: 'Item deleted',
theme: 'info',
content: 'QUEUE-123',
actions: [
{
label: 'Undo',
onClick: () => {
// handle click
},
},
],
});uiApi.toaster.add(options)
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| title | string | — | Toast title (required, max 200 chars) |
| name | string | auto | Unique key for deduplication |
| theme | 'success' \| 'danger' \| 'warning' \| 'info' | 'info' | Theme |
| content | string | — | Body text (max 500 chars) |
| autoHiding | number | 5000 | Display time in ms (1000–30000) |
| isClosable | boolean | true | Show close button |
| actions | ToastAction[] | — | Action buttons (max 2) |
ToastAction:
| Field | Type | Description |
|-------|------|-------------|
| label | string | Button label (max 50 chars) |
| onClick | () => void | Click callback |
Returns Promise<{ name: string }>.
Limits:
title— max 200 charscontent— max 500 charsactions— max 2 buttons- Rate limit — 5 toasts per 10 seconds per plugin
Navigation
Open a path in the host application or an external URL via uiApi.navigate:
import { uiApi } from '@weavix/tracker-plugin-sdk';
await uiApi.navigate({
path: '/queues/MYQUEUE',
params: { tab: 'settings' },
options: { newTab: true },
});| Field | Type | Description |
|-------|------|-------------|
| path | string | Path or URL (required) |
| params | Record<string, string \| number \| boolean \| null \| undefined \| (string \| number \| boolean \| null \| undefined)[]> | Query parameters |
| options.newTab | boolean | Open in a new tab |
TrackerPluginProvider (React) intercepts external link clicks in the iframe and calls uiApi.navigate.
Storage API
storageApi — organisation-level JSON storage mediated by the host. Data is shared across all plugin users in the organisation; write permission is determined by the host via the canWrite field.
Currently the only available context is storageApi.orgShared.
import { storageApi } from '@weavix/tracker-plugin-sdk';
// Read
const record = await storageApi.orgShared.get('settings');
// record: { data: { theme: 'dark' }, version: 5, canWrite: true, ... } | null
// Create a new record
await storageApi.orgShared.patch({
bucket: 'settings',
data: { theme: 'dark', notifications: true },
version: 0,
});
// Update without knowing the current version — SDK reads it and retries on conflict
await storageApi.orgShared.patch({
bucket: 'settings',
data: { count: 42 },
});
// Delete a field — pass null
await storageApi.orgShared.patch({
bucket: 'settings',
data: { theme: null },
});storageApi.orgShared.get(bucket?)
Returns Promise<StorageRecord | null>.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| bucket | string \| undefined | 'default' (set by host) | Record key |
storageApi.orgShared.patch(options)
Merge-patch: fields from data are merged onto the current record. A null value deletes the key. Returns the full merged StorageRecord with the new version.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| bucket | string | 'default' (set by host) | Record key |
| data | Record<string, unknown> | — | Merge-doc; null deletes a field |
| version | number \| undefined | auto-resolve | Expected current version |
Versioning:
- With explicit
version— single request;VERSION_CONFLICTis thrown to the caller. Passversion: 0to create a new record. - Without
version— SDK reads the current version, then patches. Retries up to 2 times onVERSION_CONFLICT(writesconsole.warnon each retry). Worst case: 6 round-trips.
Storage errors
| Code | Constant | When |
|------|----------|------|
| 1010 | VERSION_CONFLICT | Provided version does not match current |
| 1011 | DATA_TOO_LARGE | Record size exceeds 256 KiB after the operation |
| 1012 | BAD_KEY | bucket failed format or length validation |
import { PluginActionError, VERSION_CONFLICT, storageApi } from '@weavix/tracker-plugin-sdk';
try {
await storageApi.orgShared.patch({ bucket: 'settings', data: { x: 1 }, version: 0 });
} catch (e) {
if (e instanceof PluginActionError && e.code === VERSION_CONFLICT) {
// notify the user that the version is stale
}
}Storage types
import type {
StorageContextType, // 'orgShared'
StorageRecord, // { key, version, data, canWrite, createdAt, updatedAt }
StorageGetPayload,
StoragePatchPayload,
} from '@weavix/tracker-plugin-sdk';External APIs
hostApi methods for calling external APIs through the host proxy. Domains and auth schemes are configured in permissions.external in the plugin manifest.
Direct fetch calls from the plugin are blocked by CSP — use the proxy.
import { hostApi } from '@weavix/tracker-plugin-sdk';
// Check if the user has credentials; show dialog if not
const { success } = await hostApi.externalApiAuthCheckAndRequest({
domains: ['api.example.com'],
});
if (!success) {
// user dismissed the dialog or timeout elapsed
return;
}
// Proxy HTTP request — host injects auth headers
const { status, body } = await hostApi.externalApiCall({
url: 'https://api.example.com/v1/items',
method: 'GET',
});Typical flow: externalApiAuthCheckAndRequest (or externalApiAuthGetStatus + externalApiAuthRequest) → externalApiCall. To revoke saved credentials: externalApiAuthRevoke.
hostApi.externalApiAuthGetStatus(payload)
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| domains | string[] \| undefined | all plugin domains | Domains to check |
| contextType | 'user' \| 'organization' \| undefined | — | Credential context type |
Returns Promise<{ domains: ExternalApiAuthGetStatusDomain[] }>.
hostApi.externalApiAuthRequest(payload)
| Parameter | Type | Description |
|-----------|------|-------------|
| domains | ExternalApiDomainInfo[] | Domains (required, at least one) |
ExternalApiDomainInfo:
| Field | Type | Description |
|-------|------|-------------|
| domain | string | Domain from the manifest |
| instructions | string \| { en?: string; ru?: string } | Optional dialog hint |
Returns Promise<{ success: boolean }>. success: false means the user dismissed the dialog. Timeout: ~5 minutes.
hostApi.externalApiAuthRevoke(payload)
| Parameter | Type | Description |
|-----------|------|-------------|
| domains | string[] | Domains to revoke (at least one) |
| contextType | 'user' \| 'organization' \| undefined | Credential context type |
Returns Promise<{ success: boolean }>.
hostApi.externalApiAuthCheckAndRequest(payload)
Combines getStatus + request for unauthenticated domains only. Parameters are identical to externalApiAuthGetStatus.
Returns Promise<{ success: boolean }>. Returns { success: true } immediately if all domains are already authenticated.
hostApi.externalApiCall(payload)
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| url | string | — | Full request URL (required) |
| method | 'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE' | — | HTTP method (required) |
| headers | Record<string, string> | — | Additional headers |
| body | Record<string, unknown> | — | Request body |
| timeoutMs | number | — | Timeout in milliseconds |
| contextType | 'user' \| 'organization' \| undefined | — | Credential context |
Returns Promise<{ status: number; headers?: Record<string, string>; body?: Record<string, unknown> }>.
On proxy error, throws PluginActionError with code EXTERNAL_API_CALL_ERROR (1013); details in errorData (status, message, code, title).
External API errors
| Code | Constant | When |
|------|----------|------|
| 1013 | EXTERNAL_API_CALL_ERROR | externalApiCall proxy call failed |
import { EXTERNAL_API_CALL_ERROR, PluginActionError, hostApi } from '@weavix/tracker-plugin-sdk';
try {
await hostApi.externalApiCall({ url: 'https://api.example.com/x', method: 'GET' });
} catch (e) {
if (e instanceof PluginActionError && e.code === EXTERNAL_API_CALL_ERROR) {
console.log(e.errorData);
}
}Error handling
import { trackerApi, PluginActionError } from '@weavix/tracker-plugin-sdk';
try {
await trackerApi.v3.get['/issues/{id}']({ pathParams: { id: 'BAD' } });
} catch (e) {
if (e instanceof PluginActionError) {
console.log(e.code, e.message, e.errorData);
}
}Error codes
| Code | Constant | When |
|------|----------|------|
| 1002 | VALIDATION_ERROR | Request data failed validation |
| 1003 | METHOD_NOT_SUPPORTED | Method not supported in this configuration |
| 1004 | MISSING_REQUIRED_SCOPE | Plugin lacks the required permission scope |
| 1005 | CONTEXT_ERROR | Failed to fetch or parse slot context |
| 1007 | RATE_LIMIT_EXCEEDED | Request rate limit exceeded |
| 1008 | CONFIRM_ALREADY_OPEN | A confirmation dialog is already open |
| 1010 | VERSION_CONFLICT | Storage record version mismatch |
| 1011 | DATA_TOO_LARGE | Data exceeds 256 KiB |
| 1012 | BAD_KEY | Bucket key failed validation |
| 1013 | EXTERNAL_API_CALL_ERROR | External API proxy call failed |
API summary
hostApi
init(options?)— initialise the plugin bridge. Call once before all other methods.getTheme()— current theme ('light' | 'light-hc' | 'dark' | 'dark-hc' | 'system')getLanguage()— current language code ('ru','en', etc.)getContext()— slot context (type depends on the slot; seeSlotContextMap)getSlot()— current slot name (call afterinit())updateContentSize(request)— update the plugin container heightnotifyReady()— notify the host that the plugin is readydisableAutoResize()— disable automatic resizingexternalApiAuthGetStatus(payload)— credential status for domainsexternalApiAuthRequest(payload)— show credential input dialogexternalApiAuthRevoke(payload)— revoke saved credentialsexternalApiAuthCheckAndRequest(payload)— check and request credentials for unauthenticated domainsexternalApiCall(payload)— proxy HTTP request to an external API
uiApi
uiApi.navigate(request)— navigate in the host applicationuiApi.toaster.add(options)— show a toast notificationuiApi.confirm.show(options)— show a confirmation dialog (permissionconfirmin manifest)
trackerApi.v3
Typed proxy over Tracker Public API v3 endpoints:
trackerApi.v3.get[path](payload)— GET;payload:pathParams, optionalqueryParamstrackerApi.v3.post[path](payload)— POST;payload:bodyParams, optionalpathParams,queryParamstrackerApi.v3.put[path](payload)— PUTtrackerApi.v3.patch[path](payload)— PATCHtrackerApi.v3.delete[path](payload)— DELETE;payload:pathParams, optionalqueryParams
All methods return Promise<{ data, headers }> where data is typed according to the endpoint response.
Types
import type {
Theme,
ApiCallResult,
ApiCallPayload,
TrackerApiCallOptions,
ContentSizeUpdateRequest,
SlotContextMap,
BasicContext,
ContextLevel,
LocalizedString,
Issue,
Attachment,
StorageContextType,
StorageRecord,
StorageGetPayload,
StoragePatchPayload,
ToastAction,
ToastOptions,
ConfirmOptions,
ConfirmResult,
} from '@weavix/tracker-plugin-sdk';Key types:
SlotContextMap, slot contexts,LocalizedString, and related types (Issue,Attachment, etc.) — from@weavix/tracker-core, re-exported from this package.- HTTP request/response types for
trackerApi.v3endpoints — from@weavix/tracker-api-plugin.
React integration
For React applications, use @weavix/tracker-plugin-sdk-react — TrackerPluginProvider, useTrackerPluginContext, useLocalizedString, and all exports from this package.
Related packages
| Package | Purpose |
|---------|---------|
| @weavix/tracker-plugin-sdk-react | React integration: TrackerPluginProvider, hooks |
| @weavix/tracker-api-types | Tracker Public API v3 types |
| @weavix/sdk-core | Base runtime (hostApi, uiApi, storageApi, events) |
| @weavix/sdk-react | Base React wrapper |
License
UNLICENSED
