The runtime
A plugin’s create returns a runtime: the live connection to one configured source. The app creates one runtime per source and
calls these methods. Every failure must reject with an IntegrationError; never let a raw exception out.
import type { IntegrationConnectionStatus, IntegrationInstance, InventoryContainer, InventoryItem, InventorySearchQuery, InventorySearchResult, RemoteId} from './domain'import type { IntegrationContext } from './services'import type { IntegrationManifest } from './manifest'
export interface IntegrationConnectionResult { status: IntegrationConnectionStatus serverVersion?: string apiVersion?: string}
export interface GetContainersOptions { parentRemoteId?: RemoteId signal?: AbortSignal}
export interface GetContainerContentsOptions { limit?: number offset?: number signal?: AbortSignal}
export interface SyncOptions { since?: string signal?: AbortSignal}
export interface SyncResult { containers: InventoryContainer[] items: InventoryItem[] syncedAt: string}
/** Active connection and provider behavior. All failures must reject with an IntegrationError. */export interface InventoryIntegrationRuntime<TCredentials = unknown> { connect(credentials: TCredentials): Promise<IntegrationConnectionResult> disconnect(): Promise<void> testConnection(): Promise<IntegrationConnectionStatus> getContainers(options?: GetContainersOptions): Promise<InventoryContainer[]> getContainer(remoteId: RemoteId): Promise<InventoryContainer> getContainerContents(remoteContainerId: RemoteId, options?: GetContainerContentsOptions): Promise<InventoryItem[]> getItem(remoteId: RemoteId): Promise<InventoryItem> search?(query: InventorySearchQuery): Promise<InventorySearchResult[]> getDeepLink?(resource: InventoryContainer | InventoryItem): string | null sync?(options?: SyncOptions): Promise<SyncResult>}
export interface InventoryIntegrationPlugin<TConfig = unknown, TCredentials = unknown> { readonly manifest: IntegrationManifest create( context: IntegrationContext, instance: IntegrationInstance, config: TConfig ): InventoryIntegrationRuntime<TCredentials>}Required methods
Section titled “Required methods”| Method | Rules |
|---|---|
connect(credentials) |
Sign in. Wrong credentials reject with authentication-failed; an unreachable server with network-unreachable. Resolve with a connected status and, if known, the server version. |
disconnect() |
Forget the session. Idempotent: calling it twice must not fail. |
testConnection() |
Report, don’t throw: resolve with { state: 'connected' } or { state: 'error', error }. Used for “is this source still working?”. |
getContainers() |
Every container, each with a root-to-leaf path whose last element is its own name. Use parentRemoteId to list one level if the source supports it. |
getContainer(remoteId) |
One container. An unknown id rejects with not-found. |
getContainerContents(remoteContainerId) |
The items directly in that container, each pointing back at it through containerId. An unknown container rejects with not-found; it must not return an empty list. Honour limit and offset. |
getItem(remoteId) |
One item. An unknown id rejects with not-found. |
Calling anything before connect must also reject with a normalized error (for example authentication-expired).
Optional methods
Section titled “Optional methods”| Method | When to implement it |
|---|---|
search(query) |
Declare the search capability and implement it. Honour limit, containerIds, includeItems and includeContainers: if the remote API cannot filter, filter in the plugin. Each result says whether it canLocatePhysically: true when you know which container the item is in. |
getDeepLink(resource) |
Return the URL of that container or item in the source’s own web interface, or null. |
sync(options) |
Only if the source can return changes since a time (incrementalSync). The app falls back to listing containers and contents otherwise. |
Things to keep in mind
Section titled “Things to keep in mind”- Use the context for I/O. HTTP, storage, cache, logging and the clock all come from the context, which is also how tests replace the network.
- One request path. Funnel every call through one method that maps transport failures and HTTP statuses to errors.
- Cancel when asked. Methods that take a
signalshould pass it to the request. - Don’t cache in the plugin what the app already caches. The app mirrors containers and items locally; the plugin’s job is to answer fresh questions.