import { ScriptingEngine, EngineCapabilities, InputValue, PreparedScript, ExecutionRequest, ExecutionHandlers, ExecutionSession } from '@luxalgo/vela/plugin'; /** * The host-facing order-flow seam behind Pine's `request.footprint()`. * * Vela owns bars, never order flow — so a host that HAS per-bar volume footprints * (a footprint-capable data provider) hands the engine a {@link FootprintSource}, * and the engine exposes it to PineTS as the optional `getFootprintData` surface of * its virtual market-data provider. Without a source the surface is simply absent * and every `request.footprint()` call answers `na`, exactly as PineTS specifies. * * The shapes below are structurally identical to PineTS's `FootprintBar` / * `FootprintLevel` (declared here rather than imported so this package builds and * types against any pinets version — the surface only becomes REACHABLE once the * bundled pinets implements `request.footprint()`). */ /** Executed volume at one price level of a bar, split by aggressor side. */ interface FootprintLevel { /** Level price — the LOW edge of the price bucket this level covers. */ price: number; /** Volume executed by buy-aggressors (ask lifts) at this level. */ buyVolume: number; /** Volume executed by sell-aggressors (bid hits) at this level. */ sellVolume: number; } /** * The volume footprint of ONE chart bar, keyed by the bar's open time. Levels may * sit on any price grid: PineTS re-bins them into `ticks_per_row × mintick` rows * itself, so a source serves its finest granularity and never needs the row size. */ interface FootprintBar { /** Bar open time, epoch ms — equals the matching bar's `time`. */ openTime: number; /** Price step the levels were bucketed on. Informational only. */ tick?: number; /** Price levels, any order; levels without volume may be omitted. */ levels: FootprintLevel[]; } /** The window PineTS asks for — mirrors its `getMarketData(…, limit, sDate, eDate)` slots. */ interface FootprintRange { /** First bar open time (epoch ms), inclusive. Absent on a bare tail poll. */ from?: number; /** Exclusive end (epoch ms). Absent = "up to now". */ to?: number; /** Bar count of the loaded history on the initial load; absent on tail polls. */ limit?: number; } /** * Per-bar footprints of `(symbol, timeframe)` over `range`, plain symbol (chart-type * modifiers never reach it: order flow is never derived). Usually the chart series, * but a `request.footprint()` evaluated inside `request.security()` asks for THAT * context's symbol and timeframe, which may differ from the chart's. * * Call pattern (PineTS's), per series: one call over the whole loaded history, then * — whenever the market data changes (a live tick, new bars) — a call from the * forming bar's open time onward. Returned bars REPLACE what PineTS holds for those * open times, so a live source just answers with its current state for the tail. * Bars the source cannot serve are omitted; the script reads `na` for them. */ type FootprintSource = (symbol: string, timeframe: string, range: FootprintRange) => Promise; /** A library import as the resolver receives it (the shape PineTS passes). */ interface PineLibraryImport { /** `user/library/version`, as written in the import statement. */ path: string; user: string; /** The library's name: the title of its `library()` declaration. */ name: string; version: number; /** Path of the library holding the import; undefined for the script itself. */ importer?: string; } /** * Where the libraries of `import //` statements come from: a * function returning a library's Pine source, or null for a library it does not know. * Any PineTS import resolver (an object with `resolve(path, info)`) works as well. */ type PineLibraryResolver = ((path: string, info: PineLibraryImport) => string | null | undefined | Promise) | { resolve(path: string, info: PineLibraryImport): Promise; }; /** * Which scripts publish a declaration-props schema (the settings dialog's * "Properties" tab): every script, only `strategy()` scripts, or none. * Presentation-only — hidden props keep their source/spec values at run time, * and programmatic `setProps` overrides still apply. */ type PropsVisibility = 'all' | 'strategy' | 'none'; /** * What the engines' `props` option accepts: a visibility mode, or an explicit * WHITELIST of prop keys — only those entries are published, in the LIST's * order, so the host controls both the subset and the layout of the Properties * tab. A script owning none of the whitelisted keys (e.g. an `indicator()` * under a strategy-only list) publishes no schema and gets no tab at all. */ type PropsFilter = PropsVisibility | readonly string[]; /** Options for {@link PineEngine}. */ interface PineEngineOptions { /** * Host-level defaults for declaration props (`initial_capital`, `precision`, …), * applied BENEATH source-declared values: a script that declares the prop keeps * its own value; a script that omits it gets the host's default instead of the * Pine spec one. Folded into the props schema's `defval`s at prepare, so the * settings dialog opens on them and "Reset defaults" restores them. */ defaultProps?: Record; /** * Which scripts publish the declaration-props schema (drives whether the * settings dialog shows a "Properties" tab): `'all'` (default) every script, * `'strategy'` only `strategy()` scripts, `'none'` no script — or an explicit * WHITELIST of prop keys, published in the list's order (a script owning none * of the listed keys gets no tab). Presentation-only: hidden props keep their * source/spec values, and `setProps` still applies. */ props?: PropsFilter; /** * Per-bar volume footprints for Pine's `request.footprint()`. Vela owns bars, not * order flow, so a host with a footprint-capable data source supplies this; the * engine exposes it to PineTS as its provider's optional `getFootprintData` * surface. Absent ≡ `request.footprint()` answers `na` on every bar. */ footprints?: FootprintSource; /** * Where the libraries of `import //` statements come from. * Asked at prepare for each library a script imports (and theirs); a script importing * a library it does not know fails to prepare, naming the import. Absent ≡ a script * with an `import` fails to run. */ importResolver?: PineLibraryResolver; } /** * The in-process PineTS implementation of `ScriptingEngine`. Both the static run * and the live stream go through the shared `runtime` — the same code the * worker-backed `PineWorkerEngine` runs — so this class is just the in-process * wiring to the port. Vela owns market data and passes bars into `execute`; * this engine never fetches. */ declare class PineEngine implements ScriptingEngine { readonly language = "pine"; readonly capabilities: EngineCapabilities; private readonly defaultProps; private readonly propsVisibility; private readonly footprints; private readonly importResolver; constructor(opts?: PineEngineOptions); prepare(source: string, instanceId: string): Promise; execute(req: ExecutionRequest, handlers: ExecutionHandlers): ExecutionSession; } /** Minimal Worker surface the proxy needs — the real `Worker` satisfies it; tests inject a fake. */ interface WorkerLike { postMessage(message: unknown): void; addEventListener(type: 'message', listener: (event: { data: unknown; }) => void): void; terminate(): void; } interface PineWorkerOptions { /** * Override the worker source. By default the worker is inlined into the bundle * and spawned from a Blob URL (no separate file, no URL to configure); set this * to load a hosted worker file instead — e.g. under a CSP that blocks `blob:`. */ workerUrl?: string; /** Worker factory — tests inject a fake; defaults to the inlined Blob worker. */ createWorker?: () => WorkerLike; /** * Host-level defaults for declaration props (`initial_capital`, `precision`, …), * applied BENEATH source-declared values: a script that declares the prop keeps * its own value; a script that omits it gets the host's default instead of the * Pine spec one. Folded into the props schema's `defval`s at prepare, so the * settings dialog opens on them and "Reset defaults" restores them. */ defaultProps?: Record; /** * Which scripts publish the declaration-props schema (drives whether the * settings dialog shows a "Properties" tab): `'all'` (default) every script, * `'strategy'` only `strategy()` scripts, `'none'` no script — or an explicit * WHITELIST of prop keys, published in the list's order (a script owning none * of the listed keys gets no tab). Presentation-only: hidden props keep their * source/spec values, and `setProps` still applies. */ props?: PropsFilter; /** * Per-bar volume footprints for Pine's `request.footprint()`. Vela owns bars, not * order flow, so a host with a footprint-capable data source supplies this; the * worker exposes it to PineTS as its provider's optional `getFootprintData` * surface and round-trips each call here (the source runs on the main thread, * like `fetchSeries`). Absent ≡ `request.footprint()` answers `na` on every bar. */ footprints?: FootprintSource; /** * Where the libraries of `import //` statements come from. * Runs on the main thread: the worker asks it at prepare for each library a script * imports (and theirs); a script importing a library it does not know fails to * prepare, naming the import. Absent ≡ a script with an `import` fails to run. */ importResolver?: PineLibraryResolver; } /** * A worker-backed PineTS engine: identical Pine semantics to `PineEngine`, but the * transpile + execution run on a Web Worker, keeping the main thread responsive. * Implements the same `ScriptingEngine` port, so the orchestrator never learns a * worker exists. Secondary fetches (request.security) round-trip back to the main * thread, where the cache + network live. * * Live charts stream: the worker holds ONE persistent PineTS streaming context per * live session, so a tick ships only the forming bar (a tiny `bars` delta) and the * script re-executes incrementally — never a full snapshot + full re-run per tick. * Static sessions (non-live charts, viewport-dependent scripts) run per poke with * the pending-run coalescing below; live messages NEVER enter that bookkeeping — * a live session acks nothing, so counting it would jam the coalescing forever. */ declare class PineWorkerEngine implements ScriptingEngine { readonly language = "pine"; readonly capabilities: EngineCapabilities; private worker; private readonly spawn; private readonly defaultProps; private readonly propsVisibility; private readonly footprints; private readonly importResolver; private readonly prepares; private readonly sessions; private reqId; private readonly contextWaits; private sessionId; constructor(opts?: PineWorkerOptions); prepare(source: string, instanceId: string): Promise; execute(req: ExecutionRequest, handlers: ExecutionHandlers): ExecutionSession; /** * Coalesced bar-change notification: shipping a full bars snapshot + a complete re-run per * tick is wasteful when ticks burst (a gap heal, a fast market) — while ANY run is in flight * for the session, just mark it dirty and re-run ONCE (with a fresh snapshot) when the run * lands. The worker can't read the live bars array, hence the snapshot per posted run. * * `'backfill'` notifications never cross to the worker at all: every posted run ships a * FRESH full snapshot anyway, so intermediate partial-history snapshots buy nothing — the * worker sees the deepened history on the `'complete'` (or next tick) run. This also keeps * the pending-run bookkeeping honest (a posted-but-skipped run would jam the coalescing). */ private notifyBars; /** Post a message that triggers exactly one worker run (acked by one `done`/`error`). */ private postRun; /** A worker run finished (`done` or `error`): flush a coalesced bar notification, if any. */ private runFinished; /** Terminate the worker (no port hook calls this yet; exposed for host cleanup). */ terminate(): void; private barsOf; private workerInstance; private post; private onMessage; /** * Answer a worker's secondary-fetch request via the orchestrator's gateway. Any * live session's `fetchSeries` works — they all route to the same chart-level * cache-backed gateway (same provider, neutral by (symbol, timeframe)). */ private serveFetch; /** * Answer a worker's footprint request from the engine's `footprints` option. The * worker only asks when `execute` flagged a source, so a missing one here is a * host bug — answer empty rather than hang the script's await. */ private serveFootprints; /** Answer a worker's library request from the engine's `importResolver` (null: unknown library). */ private serveImport; } export { type FootprintBar, type FootprintLevel, type FootprintRange, type FootprintSource, PineEngine, type PineEngineOptions, type PineLibraryImport, type PineLibraryResolver, PineWorkerEngine, type PineWorkerOptions, type PropsFilter, type PropsVisibility };