@mochabug/adapt-plugin-toolkit
Version:
The API toolkit to facilitate mochabug adapt plugin development
1,241 lines (1,240 loc) • 65.9 kB
TypeScript
import { JsonValue } from '@bufbuild/protobuf';
import { ValueJson } from '@bufbuild/protobuf/wkt';
import { Client, Code, ConnectError } from '@connectrpc/connect';
import { SignalData } from './genproto/mochabugapis/adapt/graph/signal_data_pb';
import { SignalFormatJson } from './genproto/mochabugapis/adapt/graph/signal_format_pb';
import { VertexMetadataJson } from './genproto/mochabugapis/adapt/graph/vertex_metadata_pb';
import { ConfiguratorService, ExchangeOperation, ExecutorService, ListIncomingSignalsResponseJson, NamespaceJson, PluginService, Token } from './genproto/mochabugapis/adapt/runtime/v1/runtime_pb';
import { GetValue, SelectOpJson, WriteOperation } from './genproto/mochabugapis/adapt/runtime/v1/store_pb';
import { ConfiguratorEnvironment, Environment, ExecutorEnvironment } from './types';
export * from './genproto/mochabugapis/adapt/automations/v1/automations_pb';
export * from './genproto/mochabugapis/adapt/graph/exchange_pb';
export * from './genproto/mochabugapis/adapt/graph/jtd_schema_pb';
export * from './genproto/mochabugapis/adapt/graph/receiver_pb';
export * from './genproto/mochabugapis/adapt/graph/signal_binding_pb';
export * from './genproto/mochabugapis/adapt/graph/signal_data_pb';
export * from './genproto/mochabugapis/adapt/graph/signal_descriptor_pb';
export * from './genproto/mochabugapis/adapt/graph/signal_format_pb';
export * from './genproto/mochabugapis/adapt/graph/transceiver_pb';
export * from './genproto/mochabugapis/adapt/graph/transmitter_pb';
export * from './genproto/mochabugapis/adapt/graph/vertex_metadata_pb';
export * from './genproto/mochabugapis/adapt/runtime/v1/incoming_pb';
export * from './genproto/mochabugapis/adapt/runtime/v1/runtime_pb';
export * from './genproto/mochabugapis/adapt/runtime/v1/store_pb';
export * from './types';
export { Code, ConnectError };
export type { ValueJson };
/**
* Array representing asset directories.
*/
export type AssetDirectory = {
/**
* Name of the asset directory.
*/
name: string;
/**
* Type of the asset (file or directory).
*/
type: 'file' | 'directory';
}[];
/**
* Represents the content of an asset file.
*/
export type AssetFile = {
/**
* Content of the asset file as a readable stream.
*/
content: ReadableStream;
/**
* MIME type of the asset file.
*/
mime: string;
};
/**
* Base class for API interactions. Extended by specific API implementations.
* Not intended for direct use.
*/
export declare class ApiBase {
protected env: Environment;
protected pluginService: Client<typeof PluginService>;
protected pluginToken: string;
/**
* Initializes the ApiBase instance with environment settings and a plugin token.
*
* @param env - Environment configuration for API communication.
* @param pluginToken - Token for plugin authentication.
*/
constructor(env: Environment, pluginToken: string);
/**
* Retrieves a single plugin-scoped variable by its name.
* Supports dot notation for nested variable names (e.g., 'api.credentials.key').
*
* @param name - The name of the variable (1-100 chars, pattern: ^[_$a-zA-Z][_$a-zA-Z0-9]*(?:\.[_$a-zA-Z][_$a-zA-Z0-9]*)*$).
* @returns A promise that resolves with the variable value (undefined if not found).
*
* @example
* ```typescript
* const apiUrl = await api.getSystemVariable<string>('api.url');
* const nested = await api.getSystemVariable<string>('config.database.host');
* ```
*/
getSystemVariable<T = ValueJson>(name: string): Promise<T>;
/**
* Retrieves multiple plugin-scoped variables by their names with full type safety.
* Supports dot notation for nested variable names.
*
* @template T - Record type mapping variable names to their expected types.
* @param keys - Names of the variables to retrieve (1-100 unique names, each 1-100 chars).
* @returns A promise that resolves with an object mapping variable names to their typed values.
* @throws {ConnectError} Code.InvalidArgument if keys array is empty or exceeds 100 items.
*
* @example
* ```typescript
* const vars = await api.getSystemVariables<{
* 'api.url': string;
* 'api.timeout': number;
* 'api.enabled': boolean;
* }>('api.url', 'api.timeout', 'api.enabled');
*
* // Fully typed!
* vars['api.url']; // string
* vars['api.timeout']; // number
* vars['api.enabled']; // boolean
* ```
*/
getSystemVariables<T extends Record<string, any>>(...keys: Array<keyof T & string>): Promise<T>;
/**
* Retrieves a single user-scoped variable by its name.
* Supports dot notation for nested variable names (e.g., 'user.preferences.theme').
*
* @param name - The name of the variable (1-100 chars, pattern: ^[_$a-zA-Z][_$a-zA-Z0-9]*(?:\.[_$a-zA-Z][_$a-zA-Z0-9]*)*$).
* @returns A promise that resolves with the user variable value (undefined if not found).
*
* @example
* ```typescript
* const theme = await api.getUserVariable<string>('user.preferences.theme');
* const email = await api.getUserVariable<string>('user.email');
* ```
*/
getUserVariable<T = ValueJson>(name: string): Promise<T>;
/**
* Retrieves multiple user-scoped variables by their names with full type safety.
* Supports dot notation for nested variable names.
*
* @template T - Record type mapping variable names to their expected types.
* @param keys - Names of the user variables to retrieve (1-100 unique names, each 1-100 chars).
* @returns A promise that resolves with an object mapping variable names to their typed values.
* @throws {ConnectError} Code.InvalidArgument if keys array is empty or exceeds 100 items.
*
* @example
* ```typescript
* const vars = await api.getUserVariables<{
* 'user.email': string;
* 'user.age': number;
* 'user.verified': boolean;
* }>('user.email', 'user.age', 'user.verified');
*
* // Fully typed!
* vars['user.email']; // string
* vars['user.age']; // number
* vars['user.verified']; // boolean
* ```
*/
getUserVariables<T extends Record<string, any>>(...keys: Array<keyof T & string>): Promise<T>;
/**
* Retrieves a single user OAuth2 bearer token by its name.
* Returns a Token object containing the access token, type (typically 'Bearer'), and expiration timestamp.
*
* @param name - The name of the user token (1-100 chars, pattern: ^[_$a-zA-Z][_$a-zA-Z0-9]*(?:\.[_$a-zA-Z][_$a-zA-Z0-9]*)*$).
* @returns A promise that resolves with the Token object (undefined if not found).
* Token contains: { name, token, type, expires }
*
* @example
* ```typescript
* const githubToken = await api.getUserToken('github');
* if (githubToken) {
* console.log(githubToken.token, githubToken.type, githubToken.expires);
* }
* ```
*/
getUserToken(name: string): Promise<Token>;
/**
* Retrieves multiple user OAuth2 bearer tokens by their names.
* Each Token contains the access token, type (typically 'Bearer'), and expiration timestamp.
*
* @param names - Names of the user tokens to retrieve (1-100 unique names, each 1-100 chars).
* @returns A promise that resolves with an object mapping token names to their Token objects.
* Each Token contains: { name, token, type, expires }
* @throws {ConnectError} Code.InvalidArgument if names array is empty or exceeds 100 items.
*
* @example
* ```typescript
* const tokens = await api.getUserTokens('github', 'google');
*
* tokens['github']; // Token | undefined
* tokens['google']; // Token | undefined
* ```
*/
getUserTokens(...names: string[]): Promise<Record<string, Token>>;
/**
* Retrieves a single plugin OAuth2 bearer token from a service account by its name.
* Returns a Token object containing the access token, type (typically 'Bearer'), and expiration timestamp.
*
* @param name - The name of the plugin token (1-100 chars, pattern: ^[_$a-zA-Z][_$a-zA-Z0-9]*(?:\.[_$a-zA-Z][_$a-zA-Z0-9]*)*$).
* @returns A promise that resolves with the Token object (undefined if not found).
* Token contains: { name, token, type, expires }
*
* @example
* ```typescript
* const serviceToken = await api.getSystemToken('service_account');
* if (serviceToken) {
* console.log(serviceToken.token, serviceToken.expires);
* }
* ```
*/
getSystemToken(name: string): Promise<Token>;
/**
* Retrieves multiple plugin OAuth2 bearer tokens from service accounts by their names.
* Each Token contains the access token, type (typically 'Bearer'), and expiration timestamp.
*
* @param names - Names of the plugin tokens to retrieve (1-100 unique names, each 1-100 chars).
* @returns A promise that resolves with an object mapping token names to their Token objects.
* Each Token contains: { name, token, type, expires }
* @throws {ConnectError} Code.InvalidArgument if names array is empty or exceeds 100 items.
*
* @example
* ```typescript
* const tokens = await api.getSystemTokens('service_account', 'backup_service');
*
* tokens['service_account']; // Token | undefined
* tokens['backup_service']; // Token | undefined
* ```
*/
getSystemTokens(...names: string[]): Promise<Record<string, Token>>;
/**
* Creates a fetch function for user-level HTTP proxy requests.
*
* User services are vertex-level configurations where each user can provide
* their own certificates, API keys, and credentials.
*
* @param name - The service name defined in vertex user_services
* @returns A fetch function configured with HTTP proxy headers
*
* @example
* ```typescript
* const fetch = api.userHttpProxy("stripe_api");
* const res = await fetch("https://api.stripe.com/v1/charges");
* ```
*/
userHttpProxy(name: string): typeof fetch;
/**
* Creates a fetch function for system-level HTTP proxy requests.
*
* System services are plugin-wide configurations shared across all vertices.
* Configured once by plugin administrators.
*
* @param name - The service name defined in manifest system_services
* @returns A fetch function configured with HTTP proxy headers
*
* @example
* ```typescript
* const fetch = api.systemHttpProxy("internal_api");
* const res = await fetch("https://internal.example.com/data");
* ```
*/
systemHttpProxy(name: string): typeof fetch;
/**
* Validates an access token to ensure it's authorized for the current plugin and execution context.
* The authorization check verifies the token has the required scopes for plugin operations.
*
* @param token - The access token to validate (max 10000 chars).
* @returns A promise that resolves when the token is successfully validated.
* @throws {ConnectError} Code.Unauthenticated if the token is invalid or Code.PermissionDenied if unauthorized.
*
* @example
* ```typescript
* try {
* await api.authorize(userProvidedToken);
* // Token is valid, proceed with operations
* } catch (error) {
* // Token is invalid or lacks permissions
* }
* ```
*/
authorize(token: string): Promise<void>;
/**
* Get information about configured system services.
* System services are plugin-wide configurations shared across all vertices.
*
* @returns A promise that resolves with a map of configured services where keys are service paths
* (using dot notation for nested services) and values indicate if the service is configured.
*
* @example
* ```typescript
* const services = await api.getSystemServices();
* // Returns { "http_service": true, "oneof_service.option_a": true, "grouped.nested": true }
* ```
*/
getSystemServices(): Promise<{
[key: string]: boolean;
}>;
/**
* Reads a file from the plugin's assets directory.
*
* @param path - The file path relative to the assets directory.
* @returns A promise that resolves with the file content and MIME type.
* @throws {ConnectError} If the file is not found or is actually a directory.
*/
readFile(path: string): Promise<AssetFile>;
/**
* Reads a directory listing from the plugin's assets directory.
*
* @param path - The directory path relative to the assets directory.
* @returns A promise that resolves with the directory content as an array of assets.
* @throws {ConnectError} If the directory is not found or is actually a file.
*/
readDirectory(path: string): Promise<AssetDirectory>;
}
/**
* Represents the result of a batch read operation from the store.
* Keys that were not found in the store are not present in the result.
*/
export declare class BatchReadResult {
readonly result: Record<string, GetValue>;
/**
* Constructs a BatchReadResult from the store's batch read response.
* @param result - Map of keys to their retrieved values (keys not found are excluded).
* @internal
*/
constructor(result: Record<string, GetValue>);
/**
* Retrieves the result of a specific read operation by its key as JSON.
*
* @param key - The key to retrieve from the batch result.
* @returns The value and metadata, or undefined if the key was not found in the store.
* - value: The parsed JSON value
* - ttl: Remaining TTL in seconds (0 = no expiration)
* - lastModified: When the value was last modified
* - etag: Entity tag for concurrency control
*
* @example
* ```typescript
* const result = await store.readBatch(['user:1', 'user:2', 'user:3']);
* const user1 = result.get<User>('user:1');
* if (user1) {
* console.log(user1.value.name, 'TTL:', user1.ttl);
* }
* ```
*/
get<T = any>(key: string): {
value: T;
ttl: number;
lastModified: Date;
etag: string;
} | undefined;
/**
* Retrieves the result of a specific read operation by its key as raw binary.
*
* @param key - The key to retrieve from the batch result.
* @returns The raw binary value and metadata, or undefined if the key was not found in the store.
* - value: The raw binary data as Uint8Array
* - ttl: Remaining TTL in seconds (0 = no expiration)
* - lastModified: When the value was last modified
* - etag: Entity tag for concurrency control
*
* @example
* ```typescript
* const result = await store.readBatch(['asset:1', 'asset:2']);
* const asset1 = result.getBinary('asset:1');
* if (asset1) {
* console.log('Size:', asset1.value.length, 'bytes');
* }
* ```
*/
getBinary(key: string): {
ttl: number;
value: Uint8Array;
lastModified: Date;
etag: string;
} | undefined;
}
/**
* Utility class for constructing batch write operations for the KV store.
* Provides a fluent interface for building write batches.
*
* Note: Batch operations do not support preconditions. Use Store.insert() and Store.delete()
* methods directly for conditional writes.
*/
export declare class WriteBatchBuilder {
private ops;
/**
* Adds an insert operation to the batch for JSON data or raw binary data.
*
* @param key - The key to insert (max 4096 characters).
* @param value - The value to store (will be JSON-serialized unless it's Uint8Array).
* @param ttl - Optional time-to-live in seconds (0 = no expiration).
* @returns The current instance of the builder for chaining.
*
* @example
* ```typescript
* const batch = new WriteBatchBuilder()
* .insert('user:1', { name: 'Alice' }, 3600)
* .insert('user:2', { name: 'Bob' })
* .build();
* ```
*/
insert<T = any>(key: string, value: T, ttl?: number): this;
/**
* Adds a delete operation to the batch.
*
* @param key - The key to delete (max 4096 characters).
* @returns The current instance of the builder for chaining.
*
* @example
* ```typescript
* const batch = new WriteBatchBuilder()
* .delete('temp:cache:1')
* .delete('temp:cache:2')
* .build();
* ```
*/
delete(key: string): this;
/**
* Adds a range delete operation to the batch (lexicographic comparison).
*
* Range delete works like SelectOp for defining the range. Not setting any start or end will
* delete the entire store when this batch is executed.
*
* @param start - The start key of the range. If omitted, deletes from the empty key.
* @param end - The end key of the range. If omitted, deletes to the end.
* @param startInclusive - Include start (>= vs >). Defaults to `false`.
* @param endInclusive - Include end (<= vs <). Defaults to `false`.
* @returns The current instance for method chaining.
*
* @example
* ```typescript
* // Delete all keys with prefix "user/"
* builder.rangeDelete('user/', 'user/~');
*
* // Delete logs for a specific month
* builder.rangeDelete('logs/2024-01/', 'logs/2024-02/');
* ```
*/
rangeDelete(start?: string, end?: string, startInclusive?: boolean, endInclusive?: boolean): this;
/**
* Finalizes the batch and returns the array of write operations.
*
* @returns The array of WriteOperation objects ready to be executed.
*/
build(): WriteOperation[];
}
/**
* Options for lock operations.
*/
export interface LockOptions {
/**
* Maximum number of retries if the lock is not acquired immediately. Defaults to 5.
*/
maxRetries?: number;
/**
* Initial delay before retrying in milliseconds. Defaults to 50ms.
*/
initialDelay?: number;
/**
* Maximum delay between retries in milliseconds. Defaults to 2000ms.
*/
maxDelay?: number;
}
/**
* Preconditions for conditional insert operations.
* Used for implementing locks and optimistic concurrency control.
*
* IMPORTANT CONSTRAINTS (from proto validation):
* - failIfExists CANNOT be combined with etagEquals, timestampEquals, or timestampRange
* - When failIfExists is true, all other fields must be undefined
* - etagEquals and timestamp conditions can be used together
*/
export interface InsertCondition {
/**
* Fail the insert if the key already exists.
* When true, this cannot be combined with any other conditions.
* Useful for acquiring locks atomically.
*/
failIfExists?: boolean;
/**
* Only insert if the current etag matches this value.
* Cannot be used with failIfExists.
*/
etagEquals?: string;
/**
* Only insert if last_modified exactly matches this timestamp.
* Cannot be used with failIfExists or timestampRange.
*/
timestampEquals?: Date;
/**
* Only insert if last_modified falls within this time range.
* Cannot be used with failIfExists or timestampEquals.
*/
timestampRange: {
/**
* Start
*
* @generated from field: google.protobuf.Timestamp start = 1;
*/
start?: Date;
/**
* End
*
* @generated from field: google.protobuf.Timestamp end = 2;
*/
end?: Date;
/**
* Include start (>= vs >)
*
* @generated from field: bool start_inclusive = 3;
*/
startInclusive: boolean;
/**
* Include end (<= vs <)
*
* @generated from field: bool end_inclusive = 4;
*/
endInclusive: boolean;
};
}
/**
* Preconditions for conditional delete operations.
* Used for safe deletion with verification.
*
* IMPORTANT CONSTRAINTS (from proto validation):
* - mustExists CANNOT be combined with etagEquals, timestampEquals, or timestampRange
* - When mustExists is true, all other fields must be undefined
* - etagEquals and timestamp conditions can be used together
*/
export interface DeleteCondition {
/**
* Fail the delete if the key doesn't exist.
* When true, this cannot be combined with any other conditions.
*/
mustExists?: boolean;
/**
* Only delete if the current etag matches this value.
* Cannot be used with mustExists.
*/
etagEquals?: string;
/**
* Only delete if last_modified exactly matches this timestamp.
* Cannot be used with mustExists or timestampRange.
*/
timestampEquals?: Date;
/**
* Only delete if last_modified falls within this time range.
* Cannot be used with mustExists or timestampEquals.
*/
timestampRange: {
/**
* Start
*
* @generated from field: google.protobuf.Timestamp start = 1;
*/
start?: Date;
/**
* End
*
* @generated from field: google.protobuf.Timestamp end = 2;
*/
end?: Date;
/**
* Include start (>= vs >)
*
* @generated from field: bool start_inclusive = 3;
*/
startInclusive: boolean;
/**
* Include end (<= vs <)
*
* @generated from field: bool end_inclusive = 4;
*/
endInclusive: boolean;
};
}
/**
* Metadata returned from store write operations.
*/
export interface StoreMetadata {
/**
* Remaining time-to-live in seconds.
* 0 means no expiration (the value will not expire).
*/
ttl: number;
/**
* Timestamp when the value was last modified.
* All operations in a write batch share the same timestamp.
*/
lastModified: Date;
/**
* Entity tag for optimistic concurrency control.
* Changes with each modification of the value.
*/
etag: string;
}
/**
* Internal type for store service clients
* @internal
*/
type StoreServiceClient = Client<typeof ExecutorService> | Client<typeof ConfiguratorService>;
/**
* Represents a store in a specific namespace, providing key-value storage operations.
* Stores can be scoped to different namespaces with different lifetimes:
* - NAMESPACE_VERTEX: Lives as long as the plugin in the project (vertex scope)
* - NAMESPACE_PLUGIN: Lives as long as the plugin in the project (plugin-wide scope)
* - NAMESPACE_VERTEX_INSTANCE: Lives as long as the instance (vertex scope, session storage)
* - NAMESPACE_PLUGIN_INSTANCE: Lives as long as the instance (plugin-wide scope, session storage)
*/
export declare class Store {
private service;
private namespace;
/**
* Constructs a Store instance for a specific namespace.
* @param service - The gRPC service client (ExecutorService or ConfiguratorService).
* @param namespace - The namespace determining the store's scope and lifetime.
* @internal
*/
constructor(service: StoreServiceClient, namespace: NamespaceJson);
/**
* Inserts or updates a key-value pair in the store with JSON data.
* Supports conditional writes for implementing locks and optimistic concurrency control.
*
* @param key - The key to insert (max 4096 characters).
* @param value - The JSON value to store (will be JSON-serialized).
* @param ttl - Optional time-to-live in seconds (0 = no expiration).
* @param condition - Optional preconditions for the write:
* - failIfExists: Fail if key already exists (cannot be combined with other conditions)
* - etagEquals: Only write if current etag matches
* - timestampEquals: Only write if last_modified exactly matches
* - timestampRange: Only write if last_modified falls within range
* @returns A promise that resolves with metadata (ttl, lastModified, etag) when the operation completes.
* @throws {ConnectError} Code.FailedPrecondition if precondition fails, Code.InvalidArgument for invalid inputs.
*
* @example
* ```typescript
* // Simple insert
* await store.insert('user:123', { name: 'Alice' }, 3600);
*
* // Conditional insert - fail if exists (for locks)
* await store.insert('lock:resource', { owner: 'process-1' }, 30, { failIfExists: true });
*
* // Optimistic concurrency with etag
* const data = await store.get('counter');
* await store.insert('counter', data.data + 1, undefined, { etagEquals: data.etag });
* ```
*/
insert<T = any>(key: string, value: T, ttl?: number, condition?: InsertCondition): Promise<StoreMetadata>;
/**
* Deletes a key-value pair from the store.
* Supports conditional deletes for safe removal operations.
*
* @param key - The key to delete (max 4096 characters).
* @param condition - Optional preconditions for the delete:
* - mustExists: Fail if key doesn't exist (cannot be combined with other conditions)
* - etagEquals: Only delete if current etag matches
* - timestampEquals: Only delete if last_modified exactly matches
* - timestampRange: Only delete if last_modified falls within range
* @returns A promise that resolves when the operation completes.
* @throws {ConnectError} Code.FailedPrecondition if precondition fails.
*
* @example
* ```typescript
* // Simple delete
* await store.delete('temp:data');
*
* // Conditional delete - only if exists
* await store.delete('lock:resource', { mustExists: true });
*
* // Delete with etag verification
* const data = await store.get('config');
* await store.delete('config', { etagEquals: data.etag });
* ```
*/
delete(key: string, condition?: DeleteCondition): Promise<void>;
/**
* Deletes all items within a specified key range from the store (lexicographic comparison).
*
* Range delete works like SelectOp for defining the range. Not setting any start or end will
* delete the entire store.
*
* Note: Range delete operations do not support preconditions. For conditional deletes,
* use the {@link delete} method.
*
* @param start - The start key of the range. If omitted, deletes from the empty key.
* @param end - The end key of the range. If omitted, deletes to the end.
* @param startInclusive - Include start (>= vs >). Defaults to `false`.
* @param endInclusive - Include end (<= vs <). Defaults to `false`.
* @returns A promise that resolves when the range delete operation completes.
*
* @example
* ```typescript
* // Delete all keys with prefix "user/"
* await store.rangeDelete('user/', 'user/~');
*
* // Delete logs for a specific month
* await store.rangeDelete('logs/2024-01/', 'logs/2024-02/');
*
* // Delete all items in the store
* await store.rangeDelete();
* ```
*/
rangeDelete(start?: string, end?: string, startInclusive?: boolean, endInclusive?: boolean): Promise<void>;
/**
* Retrieves a value by its key from the store as JSON.
*
* @param key - The key to retrieve (max 4096 characters).
* @returns A promise that resolves with the retrieved JSON value and metadata, or undefined if key not found.
* - data: The parsed JSON value
* - ttl: Remaining TTL in seconds (0 = no expiration)
* - etag: The ETag for optimistic concurrency control
* - lastModified: Timestamp of last modification
*
* @example
* ```typescript
* const user = await store.get<User>('user:123');
* if (user) {
* console.log(user.data.name, 'expires in', user.ttl, 'seconds');
* }
* ```
*/
get<T = any>(key: string): Promise<{
data: T;
ttl: number;
etag: string;
lastModified: Date;
} | undefined>;
/**
* Retrieves a value by its key from the store as raw binary.
*
* @param key - The key to retrieve (max 4096 characters).
* @returns A promise that resolves with the retrieved binary value and metadata, or undefined if key not found.
* - data: The raw binary value as Uint8Array
* - ttl: Remaining TTL in seconds (0 = no expiration)
* - etag: The ETag for optimistic concurrency control
* - lastModified: Timestamp of last modification
*
* @example
* ```typescript
* const asset = await store.getBinary('asset:image.png');
* if (asset) {
* console.log('Image size:', asset.data.length, 'bytes');
* }
* ```
*/
getBinary(key: string): Promise<{
data: Uint8Array<ArrayBufferLike>;
ttl: number;
etag: string;
lastModified: Date;
} | undefined>;
/**
* Performs a range query on keys using lexicographic comparison, returning parsed JSON data.
*
* Range queries allow scanning keys within a specific range. For example:
* - "user/" to "user/~" - all keys starting with "user/"
* - "logs/2024-01/" to "logs/2024-02/" - all logs from January 2024
*
* Not setting start or end will scan the entire store.
* Results are always returned in ascending order by key.
*
* @param select - The select operation specifying:
* - start: Optional start key (max 4096 chars). Omit to start from empty key
* - end: Optional end key (max 4096 chars)
* - startInclusive: Whether to include start key (>= vs >)
* - endInclusive: Whether to include end key (<= vs <)
* - limit: Max results (1-1000)
* - pageToken: Optional token to continue from previous query
* @returns A promise that resolves to an object containing:
* - items: Array of values with metadata (value as JSON type T, TTL, etag, lastModified)
* - nextToken: Optional pagination token to continue the query
*
* @example
* ```typescript
* // Get all user keys
* const result = await store.selectRange<User>({
* start: 'user/',
* end: 'user/~',
* startInclusive: true,
* endInclusive: false,
* limit: 100
* });
*
* // Continue pagination
* if (result.nextToken) {
* const next = await store.selectRange({
* start: 'user/',
* end: 'user/~',
* startInclusive: true,
* endInclusive: false,
* limit: 100,
* pageToken: result.nextToken
* });
* }
* ```
*/
selectRange<T = JsonValue>(select: SelectOpJson): Promise<{
nextToken?: string;
items: {
key: string;
value: T;
ttl: number;
etag: string;
lastModified: Date;
}[];
}>;
/**
* Performs a range query on keys using lexicographic comparison, returning raw binary data.
*
* Range queries allow scanning keys within a specific range. For example:
* - "user/" to "user/~" - all keys starting with "user/"
* - "logs/2024-01/" to "logs/2024-02/" - all logs from January 2024
*
* Not setting start or end will scan the entire store.
* Results are always returned in ascending order by key.
*
* @param select - The select operation specifying:
* - start: Optional start key (max 4096 chars). Omit to start from empty key
* - end: Optional end key (max 4096 chars)
* - startInclusive: Whether to include start key (>= vs >)
* - endInclusive: Whether to include end key (<= vs <)
* - limit: Max results (1-1000)
* - pageToken: Optional token to continue from previous query
* @returns A promise that resolves to an object containing:
* - items: Array of values with metadata (value as Uint8Array, TTL, etag, lastModified)
* - nextToken: Optional pagination token to continue the query
*
* @example
* ```typescript
* // Get all binary assets
* const result = await store.selectRangeBinary({
* start: 'assets/',
* end: 'assets/~',
* startInclusive: true,
* endInclusive: false,
* limit: 50
* });
*
* // Continue pagination
* if (result.nextToken) {
* const next = await store.selectRangeBinary({
* start: 'assets/',
* end: 'assets/~',
* startInclusive: true,
* endInclusive: false,
* limit: 50,
* pageToken: result.nextToken
* });
* }
* ```
*/
selectRangeBinary(select: SelectOpJson): Promise<{
nextToken?: string;
items: {
value: Uint8Array<ArrayBufferLike>;
ttl: number;
etag: string;
lastModified: Date;
}[];
}>;
/**
* Executes a batch of write operations on the store.
* All operations are atomic - either all succeed or all fail.
* All operations in a batch share the same timestamp.
* Interfering operations on the same key have undefined order.
*
* Note: Batch operations do not support preconditions. Use the single insert/delete
* methods for conditional writes.
*
* @param ops - The write operations to execute (1-500 operations). Use WriteBatchBuilder to construct.
* @returns A promise that resolves with metadata for each inserted key (deletes don't return metadata).
* @throws {ConnectError} Code.InvalidArgument if invalid operations or limits exceeded.
*
* @example
* ```typescript
* const ops = new WriteBatchBuilder()
* .insert('user:1', { name: 'Alice' }, 3600)
* .insert('user:2', { name: 'Bob' }, 3600)
* .delete('temp:old')
* .build();
*
* const metadata = await store.writeBatch(ops);
* console.log('Insert metadata:', metadata);
* ```
*/
writeBatch(ops: WriteOperation[]): Promise<{
[key: string]: StoreMetadata;
}>;
/**
* Executes a batch of read operations on the store.
* Keys not found are ignored (not present in the result).
*
* @param keys - The keys to read (1-10000 keys, each max 4096 characters).
* @returns A promise that resolves with a BatchReadResult containing found items.
*
* @example
* ```typescript
* const result = await store.readBatch(['user:1', 'user:2', 'user:3']);
* const user1 = result.get<User>('user:1');
* if (user1) {
* console.log(user1.value.name);
* }
* ```
*/
readBatch(keys: string[]): Promise<BatchReadResult>;
/**
* Attempts to acquire a lock for a specified duration.
* Uses an atomic insert operation with failIfExists precondition that fails if the key already exists.
*
* @param locker - The unique identifier for the lock (max 4096 chars).
* @param ttl - Time-to-live in seconds (defaults to 5 seconds if not specified).
* @returns A promise that resolves with true if the lock was acquired, false if already held.
*
* @example
* ```typescript
* // Try to acquire lock with default 5 second TTL
* const acquired = await store.acquire('resource_lock');
* if (acquired) {
* // Lock acquired, perform work
* }
*
* // Acquire lock with custom TTL
* const acquired2 = await store.acquire('long_operation', 30);
* ```
*/
acquire(locker: string, ttl?: number): Promise<boolean>;
/**
* Releases a previously acquired lock by deleting the lock key.
*
* @param locker - The unique identifier for the lock (max 4096 chars).
* @returns A promise that resolves when the lock is released.
*
* @example
* ```typescript
* await store.release('resource_lock');
* ```
*/
release(locker: string): Promise<void>;
/**
* Acquires a lock and executes a critical section of code with exponential backoff retry logic.
* Automatically releases the lock after the section completes or fails.
*
* The retry mechanism uses exponential backoff:
* - Delay = min(initialDelay * 2^attempt, maxDelay)
* - Default: 50ms, 100ms, 200ms, 400ms, 800ms (capped at maxDelay)
*
* @template T - The return type of the critical section function.
* @param key - The unique identifier for the lock (max 4096 chars).
* @param section - The critical section to execute once the lock is acquired.
* @param options - Optional lock options:
* - maxRetries: Maximum retry attempts (default: 5)
* - initialDelay: Initial delay in ms before first retry (default: 50ms)
* - maxDelay: Maximum delay in ms between retries (default: 2000ms)
* @returns A promise that resolves with the result of the critical section.
* @throws {Error} If the lock cannot be acquired after maxRetries attempts.
*
* @example
* ```typescript
* const result = await store.lock('counter_lock', async () => {
* const counter = await store.get<number>('counter');
* await store.insert('counter', (counter?.data ?? 0) + 1);
* return counter?.data ?? 0;
* });
*
* // With custom options
* const result2 = await store.lock('resource', async () => {
* // critical work
* }, { maxRetries: 10, initialDelay: 100, maxDelay: 5000 });
* ```
*/
lock<T>(key: string, section: () => Promise<T>, options?: LockOptions): Promise<T>;
}
/**
* Provides access to the least privileged Executor API, without session capabilities.
* This API allows plugins to interact with vertex configuration and local storage.
*/
export declare class ExecutorApi extends ApiBase {
protected executor: Client<typeof ExecutorService>;
/**
* Initializes an instance of ExecutorApi.
*
* @param env - The executor environment configuration.
* @param pluginToken - The plugin token for authorization.
*/
constructor(env: ExecutorEnvironment, pluginToken: string);
/**
* Get vertex configuration with JSON-parsed config field.
* Optionally specify which fields to include in the response.
*
* @template T - Type of the config object (defaults to any)
* @param fields - Optional field names to include. If not specified, returns all fields.
* @returns Object containing the requested fields with config as parsed JSON (type T).
*
* @example
* ```typescript
* // Get all fields (all fields are guaranteed to be present)
* const {config, metadata, services} = await api.getConfig<Settings>();
*
* // Get only config with type - result type is { config: Settings }
* const {config} = await api.getConfig<Settings>('config');
*
* // Get config and metadata - result type is { config: Settings; metadata: VertexMetadataJson }
* const {config, metadata} = await api.getConfig<Settings>('config', 'metadata');
* ```
*/
getConfig<T = any>(): Promise<{
config: T;
metadata: VertexMetadataJson;
configuredServices: {
[key: string]: boolean;
};
}>;
getConfig<T = any, K extends 'config' | 'metadata' | 'configuredServices' = 'config' | 'metadata' | 'configuredServices'>(...fields: K[]): Promise<Pick<{
config: T;
metadata: VertexMetadataJson;
configuredServices: {
[key: string]: boolean;
};
}, K>>;
/**
* Get vertex configuration with binary config field (Uint8Array).
* Optionally specify which fields to include in the response.
*
* @param fields - Optional field names to include. If not specified, returns all fields.
* @returns Object containing the requested fields with config as raw Uint8Array.
*
* @example
* ```typescript
* // Get all fields with binary config (all guaranteed present)
* const {config, metadata, services} = await api.getConfigBinary();
*
* // Get only binary config - result type is { config: Uint8Array }
* const {config} = await api.getConfigBinary('config');
* ```
*/
getConfigBinary(): Promise<{
config: Uint8Array;
metadata: VertexMetadataJson;
configuredServices: {
[key: string]: boolean;
};
}>;
getConfigBinary<K extends 'config' | 'metadata' | 'configuredServices' = 'config' | 'metadata' | 'configuredServices'>(...fields: K[]): Promise<Pick<{
config: Uint8Array;
metadata: VertexMetadataJson;
configuredServices: {
[key: string]: boolean;
};
}, K>>;
/**
* Provides access to a local store instance, scoped by namespace.
*
* Namespace lifetimes:
* - 'vertex' (NAMESPACE_VERTEX): Vertex-scoped, lives as long as the plugin in the project
* - 'plugin' (NAMESPACE_PLUGIN): Plugin-wide scope, lives as long as the plugin in the project
*
* Access scopes:
* - NAMESPACE_VERTEX: runtimeapi/executor.store.vertex.app, runtimeapi/executor.store.vertex.app:read
* - NAMESPACE_PLUGIN: runtimeapi/executor.store.plugin.app, runtimeapi/executor.store.plugin.app:read
*
* @param namespace - The namespace for the store, either 'vertex' or 'plugin'.
* @returns An instance of Store configured for the specified namespace.
*/
getLocalStore(namespace: 'vertex' | 'plugin'): Store;
/**
* Retrieves a session-scoped API with the provided authorization header.
* Session APIs have additional capabilities like sending signals and managing exchanges.
*
* @param authHeader - The authorization header to use for the session API (e.g., 'Bearer <access-token>').
* @returns An instance of SessionExecutorApi configured with the provided auth header.
*/
getSessionApi(authHeader: string): SessionExecutorApi;
}
/**
* Provides an API with session storage capabilities, specifically for use when a session is stopping.
* This API allows access to session-scoped stores that persist for the lifetime of the instance.
*/
export declare class StopExecutorApi extends ExecutorApi {
/**
* The session authorization header for session-based operations.
*/
protected sessionAuthHeader: string;
/**
* Initializes an instance of StopExecutorApi.
*
* @param env - The executor environment configuration.
* @param pluginToken - The plugin token for authorization.
* @param sessionAuthHeader - The authorization header for session-based operations.
*/
constructor(env: ExecutorEnvironment, pluginToken: string, sessionAuthHeader: string);
/**
* Retrieves the session authorization header used for this API instance.
* This header contains the session access token and can be used to create
* additional session-scoped API instances or for custom authorization needs.
*
* @returns The session authorization header (format: 'Bearer <token>').
*
* @example
* ```typescript
* const authHeader = api.getAuthHeader();
* // Use for creating another session API or custom requests
* ```
*/
getAuthHeader(): string;
/**
* Retrieves a session-specific store instance based on the provided namespace.
* Session stores persist for the lifetime of the instance (the running session).
*
* Namespace lifetimes:
* - 'vertex' (NAMESPACE_VERTEX_INSTANCE): Vertex-scoped, lives as long as the instance
* - 'plugin' (NAMESPACE_PLUGIN_INSTANCE): Plugin-wide scope, lives as long as the instance
*
* Access scopes:
* - NAMESPACE_VERTEX_INSTANCE: runtimeapi/executor.store.vertex.instance, runtimeapi/executor.store.vertex.instance:read
* - NAMESPACE_PLUGIN_INSTANCE: runtimeapi/executor.store.plugin.instance, runtimeapi/executor.store.plugin.instance:read
*
* @param namespace - The namespace for the store, either 'vertex' or 'plugin'.
* @returns An instance of Store configured for the session's namespace.
*/
getSessionStore(namespace: 'vertex' | 'plugin'): Store;
}
/**
* Options for listing bound receiver signals.
*/
export interface ListSignalsOptions {
/**
* Number of signals per page (max 500, defaults to 50 if not specified).
*/
pageSize?: number;
/**
* Whether to order signals in descending order.
*/
orderDescending?: boolean;
/**
* Cursor for pagination (max 500 chars).
* Used internally by continueListSignals - typically you don't set this directly.
* @internal
*/
cursor?: string;
}
/**
* Provides access to the Privileged Executor API, allowing session-based operations.
* This API enables sending signals, managing exchanges, and accessing receiver signals.
*/
export declare class SessionExecutorApi extends StopExecutorApi {
/**
* Initializes an instance of SessionExecutorApi.
*
* @param env - The executor environment configuration.
* @param pluginToken - The plugin token for authorization.
* @param sessionAuthHeader - The authorization header for session access.
*/
constructor(env: ExecutorEnvironment, pluginToken: string, sessionAuthHeader: string);
/**
* Lists signals bound to the receiver that activated this vertex, returning parsed JSON data.
*
* Signal retrieval is always scoped to the specific receiver that triggered
* the vertex execution, providing access to the contextual data that initiated
* this workflow.
*
* IMPORTANT: The response payload is limited to a maximum of 16MB.
* If the total size of all signals would exceed this limit, the response
* will be truncated and a cursor will be provided to continue fetching
* the remaining signals in subsequent requests.
*
* @param options - Optional listing options:
* - pageSize: Number of signals per page (max 500, defaults to 50)
* - orderDescending: Whether to order signals in descending order
* @returns A promise that resolves to the list of signals, receiver name, and a cursor for pagination.
*
* @example
* ```typescript
* const result = await api.listSignals({ pageSize: 10 });
* result.signals // Record<string, ValueJson>
* ```
*/
listSignals(options?: ListSignalsOptions): Promise<{
signals: Record<string, ValueJson>;
receiver: string;
cursor?: string;
}>;
/**
* Lists signals bound to the receiver that activated this vertex, returning raw binary data.
*
* Signal retrieval is always scoped to the specific receiver that triggered
* the vertex execution, providing access to the contextual data that initiated
* this workflow.
*
* IMPORTANT: The response payload is limited to a maximum of 16MB.
* If the total size of all signals would exceed this limit, the response
* will be truncated and a cursor will be provided to continue fetching
* the remaining signals in subsequent requests.
*
* @param options - Optional listing options:
* - pageSize: Number of signals per page (max 500, defaults to 50)
* - orderDescending: Whether to order signals in descending order
* @returns A promise that resolves to the list of binary signals, receiver name, and a cursor for pagination.
*
* @example
* ```typescript
* const result = await api.listSignalsBinary({ pageSize: 10 });
* result.signals // Record<string, SignalData>
* ```
*/
listSignalsBinary(options?: ListSignalsOptions): Promise<{
signals: Record<string, SignalData>;
receiver: string;
cursor?: string;
}>;
/**
* Continues listing signals bound to the receiver that activated this vertex from a given cursor, returning parsed JSON data.
*
* Signal retrieval is always scoped to the specific receiver that triggered
* the vertex execution, providing access to the contextual data that initiated
* this workflow.
*
* @param cursor - The cursor to continue from.
* @returns A promise that resolves to the next set of signals, receiver name, and a new cursor for further pagination.
*
* @example
* ```typescript
* const result = await api.continueListSignals(cursor);
* result.signals // Record<string, ValueJson>
* ```
*/
continueListSignals(cursor: string): Promise<{
signals: Record<string, ValueJson>;
receiver: string;
cursor?: string;
}>;
/**
* Continues listing signals bound to the receiver that activated this vertex from a given cursor, returning raw binary data.
*
* Signal retrieval is always scoped to the specific receiver that triggered
* the vertex execution, providing access to the contextual data that initiated
* this workflow.
*
* @param cursor - The cursor to continue from.
* @returns A promise that resolves to the next set of binary signals, receiver name, and a new cursor for further pagination.
*
* @example
* ```typescript
* const result = await api.continueListSignalsBinary(cursor);
* result.signals // Record<string, Uint8Array<ArrayBufferLike>>
* ```
*/
continueListSignalsBinary(cursor: string): Promise<{
signals: Record<string, SignalData>;
receiver: string;
cursor?: string;
}>;
/**
* Retrieves a signal bound to the receiver that activated this vertex, returning parsed JSON data.
*
* Signal retrieval is always scoped to the specific receiver that triggered
* the vertex execution, providing access to the contextual data that initiated