@golem-sdk/golem-js
Version:
NodeJS and WebBrowser SDK for building apps running on Golem Network
237 lines (236 loc) • 12.4 kB
TypeScript
import { EventEmitter } from "eventemitter3";
import { Agreement } from "./agreement";
import { Demand, DraftOfferProposalPool, IMarketApi, MarketEvents, MarketProposalEvent, OfferProposalSelector } from "./index";
import { Logger, YagnaApi } from "../shared/utils";
import { Allocation, IPaymentApi } from "../payment";
import { Observable } from "rxjs";
import { OfferCounterProposal, OfferProposal, OfferProposalFilter } from "./proposal";
import { DemandBodyBuilder, DemandSpecification, OrderDemandOptions } from "./demand";
import { IActivityApi, IFileServer } from "../activity";
import { StorageProvider } from "../shared/storage";
import { MarketOrderSpec } from "../golem-network";
import { INetworkApi, NetworkModule } from "../network";
import { AgreementOptions } from "./agreement/agreement";
import { ScanOptions, ScanSpecification, ScannedOffer } from "./scan";
export type DemandEngine = "vm" | "vm-nvidia" | "wasmtime";
export type PricingOptions = {
model: "linear";
maxStartPrice: number;
maxCpuPerHourPrice: number;
maxEnvPerHourPrice: number;
} | {
model: "burn-rate";
avgGlmPerHour: number;
};
export interface OrderMarketOptions {
/** How long you want to rent the resources in hours */
rentHours: number;
/** Pricing strategy that will be used to filter the offers from the market */
pricing: PricingOptions;
/** A user-defined filter function which will determine if the offer proposal is valid for use. */
offerProposalFilter?: OfferProposalFilter;
/** A user-defined function that will be used to pick the best fitting offer proposal from available ones */
offerProposalSelector?: OfferProposalSelector;
}
export interface MarketModuleOptions {
/**
* Number of seconds after which the demand will be un-subscribed and subscribed again to get fresh
* offers from the market
*
* @default 30 minutes
*/
demandRefreshIntervalSec: number;
}
export interface MarketModule {
events: EventEmitter<MarketEvents>;
/**
* Build a DemandSpecification based on the given options and allocation.
* You can obtain an allocation using the payment module.
* The method returns a DemandSpecification that can be used to publish the demand to the market,
* for example using the `publishDemand` method.
*/
buildDemandDetails(demandOptions: OrderDemandOptions, orderOptions: OrderMarketOptions, allocation: Allocation): Promise<DemandSpecification>;
/**
* Build a ScanSpecification that can be used to scan the market for offers.
* The difference between this method and `buildDemandDetails` is that this method does not require an
* allocation, doesn't inherit payment properties from `GolemNetwork` settings and doesn't provide any defaults.
* If you wish to set the payment platform, you need to specify it in the ScanOptions.
*/
buildScanSpecification(options: ScanOptions): ScanSpecification;
/**
* Publishes the demand to the market and handles refreshing it when needed.
* Each time the demand is refreshed, a new demand is emitted by the observable.
* Keep in mind that since this method returns an observable, nothing will happen until you subscribe to it.
* Unsubscribing will remove the demand from the market.
*/
publishAndRefreshDemand(demandSpec: DemandSpecification): Observable<Demand>;
/**
* Return an observable that will emit values representing various events related to this demand
*/
collectMarketProposalEvents(demand: Demand): Observable<MarketProposalEvent>;
/**
* Subscribes to the proposals for the given demand.
* If an error occurs, the observable will emit an error and complete.
* Keep in mind that since this method returns an observable, nothing will happen until you subscribe to it.
*
* This method will just yield all the proposals that will be found for that demand without any additional logic.
*
* The {@link collectDraftOfferProposals} is a more specialized variant of offer collection, which includes negotiations
* and demand re-subscription logic
*/
collectAllOfferProposals(demand: Demand): Observable<OfferProposal>;
/**
* Sends a counter-offer to the provider. Note that to get the provider's response to your
* counter you should listen to events returned by `collectDemandOfferEvents`.
*
* @returns The counter-proposal that the requestor made to the Provider
*/
negotiateProposal(receivedProposal: OfferProposal, counterDemandSpec: DemandSpecification): Promise<OfferCounterProposal>;
/**
* Internally
*
* - ya-ts-client createAgreement
* - ya-ts-client approveAgreement
* - ya-ts-client "wait for approval"
*
* @param proposal
* @param signalOrTimeout - The timeout in milliseconds or an AbortSignal that will be used to cancel the operation
*
* @return Returns when the provider accepts the agreement, rejects otherwise. The resulting agreement is ready to create activities from.
*/
proposeAgreement(proposal: OfferProposal, agreementOptions?: AgreementOptions, signalOrTimeout?: AbortSignal | number): Promise<Agreement>;
/**
* @return The Agreement that has been terminated via Yagna
* @param signalOrTimeout - The timeout in milliseconds or an AbortSignal that will be used to cancel the operation
*/
terminateAgreement(agreement: Agreement, reason?: string, signalOrTimeout?: AbortSignal | number): Promise<Agreement>;
/**
* Acquire a proposal from the pool and sign an agreement with the provider. If signing the agreement fails,
* destroy the proposal and try again with another one. The method returns an agreement that's ready to be used.
* Optionally, you can provide a timeout in milliseconds or an AbortSignal that can be used to cancel the operation
* early. If the operation is cancelled, the method will throw an error.
* Note that this method will respect the acquire timeout set in the pool and will throw an error if no proposal
* is available within the specified time.
*
* @example
* ```ts
* const agreement = await marketModule.signAgreementFromPool(draftProposalPool, 10_000); // throws TimeoutError if the operation takes longer than 10 seconds
* ```
* @example
* ```ts
* const signal = AbortSignal.timeout(10_000);
* const agreement = await marketModule.signAgreementFromPool(draftProposalPool, signal); // throws TimeoutError if the operation takes longer than 10 seconds
* ```
* @param draftProposalPool - The pool of draft proposals to acquire from
* @param agreementOptions - options used to sign the agreement such as expiration or waitingForApprovalTimeout
* @param signalOrTimeout - The timeout in milliseconds or an AbortSignal that will be used to cancel the operation
*/
signAgreementFromPool(draftProposalPool: DraftOfferProposalPool, agreementOptions?: AgreementOptions, signalOrTimeout?: number | AbortSignal): Promise<Agreement>;
/**
* Creates a demand for the given package and allocation and starts collecting, filtering and negotiating proposals.
* The method returns an observable that emits a batch of draft proposals every time the buffer is full.
* The method will automatically negotiate the proposals until they are moved to the `Draft` state.
* Keep in mind that since this method returns an observable, nothing will happen until you subscribe to it.
* Unsubscribing from the observable will stop the process and remove the demand from the market.
*/
collectDraftOfferProposals(options: {
demandSpecification: DemandSpecification;
pricing: PricingOptions;
filter?: OfferProposalFilter;
minProposalsBatchSize?: number;
proposalsBatchReleaseTimeoutMs?: number;
}): Observable<OfferProposal>;
/**
* Estimate the budget for the given order and maximum numbers of agreemnets.
* Keep in mind that this is just an estimate and the actual cost may vary.
* The method returns the estimated budget in GLM.
* @param params
*/
estimateBudget({ maxAgreements, order }: {
maxAgreements: number;
order: MarketOrderSpec;
}): number;
/**
* Fetch the most up-to-date agreement details from the yagna
*/
fetchAgreement(agreementId: string, signalOrTimeout?: AbortSignal | number): Promise<Agreement>;
/**
* Scan the market for offers that match the given demand specification.
*/
scan(scanSpecification: ScanSpecification): Observable<ScannedOffer>;
}
/**
* Represents a director that can instruct DemandDetailsBuilder
*
* Demand is a complex concept in Golem. Requestors can place arbitrary properties and constraints on such
* market entity. While the demand request on the Golem Protocol level is a flat list of properties (key, value) and constraints,
* from the Requestor side they form logical groups that make sense together.
*
* The idea behind Directors is that you can encapsulate this grouping knowledge along with validation logic etc to prepare
* all the final demand request body properties in a more controlled and organized manner.
*/
export interface IDemandDirector {
apply(builder: DemandBodyBuilder): Promise<void> | void;
}
export declare class MarketModuleImpl implements MarketModule {
private readonly deps;
events: EventEmitter<MarketEvents, any>;
private readonly logger;
private readonly marketApi;
private fileServer;
private options;
constructor(deps: {
logger: Logger;
yagna: YagnaApi;
paymentApi: IPaymentApi;
activityApi: IActivityApi;
marketApi: IMarketApi;
networkApi: INetworkApi;
networkModule: NetworkModule;
fileServer: IFileServer;
storageProvider: StorageProvider;
}, options?: Partial<MarketModuleOptions>);
buildDemandDetails(demandOptions: OrderDemandOptions, orderOptions: OrderMarketOptions, allocation: Allocation): Promise<DemandSpecification>;
buildScanSpecification(options: ScanOptions): ScanSpecification;
/**
* Augments the user-provided options with additional logic
*
* Use Case: serve the GVMI from the requestor and avoid registry
*/
private applyLocalGVMIServeSupport;
/**
* Publishes the specified demand and re-publishes it based on demandSpecification.expirationSec interval
*/
publishAndRefreshDemand(demandSpecification: DemandSpecification): Observable<Demand>;
collectMarketProposalEvents(demand: Demand): Observable<MarketProposalEvent>;
collectAllOfferProposals(demand: Demand): Observable<OfferProposal>;
negotiateProposal(offerProposal: OfferProposal, counterDemand: DemandSpecification): Promise<OfferCounterProposal>;
proposeAgreement(proposal: OfferProposal, options?: AgreementOptions, signalOrTimeout?: AbortSignal | number): Promise<Agreement>;
terminateAgreement(agreement: Agreement, reason?: string, signalOrTimeout?: AbortSignal | number): Promise<Agreement>;
collectDraftOfferProposals(options: {
demandSpecification: DemandSpecification;
pricing: PricingOptions;
filter?: OfferProposalFilter;
minProposalsBatchSize?: number;
proposalsBatchReleaseTimeoutMs?: number;
}): Observable<OfferProposal>;
private emitMarketProposalEvents;
signAgreementFromPool(draftProposalPool: DraftOfferProposalPool, agreementOptions?: AgreementOptions, signalOrTimeout?: number | AbortSignal): Promise<Agreement>;
/**
* Reduce initial proposals to a set grouped by the provider's key to avoid duplicate offers
*/
private reduceInitialProposalsByProviderKey;
estimateBudget({ order, maxAgreements }: {
order: MarketOrderSpec;
maxAgreements: number;
}): number;
fetchAgreement(agreementId: string, signalOrTimeout?: AbortSignal | number): Promise<Agreement>;
/**
* Subscribes to an observable that maps yagna events into our domain events
* and emits these domain events via EventEmitter
*/
private collectAndEmitAgreementEvents;
private filterProposalsByUserFilter;
private filterProposalsByPricingOptions;
scan(scanSpecification: ScanSpecification): Observable<ScannedOffer>;
}