UNPKG

@dojoengine/sdk

Version:

Dojo SDK: Build onchain and provable apps faster

752 lines (742 loc) 27.6 kB
import * as torii$1 from '@dojoengine/torii-wasm/node'; import { PatternMatching, ComparisonOperator, Clause as Clause$1 } from '@dojoengine/torii-wasm/node'; import { Pagination as Pagination$1 } from '@dojoengine/torii-wasm'; import * as torii from '@dojoengine/torii-wasm/types'; import { Query, PaginationDirection, Clause, OrderBy, Subscription } from '@dojoengine/torii-wasm/types'; import { Result } from 'neverthrow'; import { StarknetDomain, TypedData, Account, BigNumberish } from 'starknet'; /** * A generic pagination class that handles cursor-based pagination for query results. * This class manages the state of paginated items and provides methods to navigate through pages. * * @template T - The schema type that extends SchemaType * @template Inner - The type of items being paginated (must be an array type) */ declare class Pagination<T extends SchemaType, Inner extends any[]> { limit: number; cursor?: string | undefined; direction?: string | undefined; private items; /** * Creates a new Pagination instance * * @param limit - The maximum number of items to return per page * @param cursor - Optional cursor string for pagination * @param direction - Optional direction of pagination (defaults to "Forward") */ constructor(limit: number, cursor?: string | undefined, direction?: string | undefined); /** * Creates a Pagination instance from a ToriiQueryBuilder * * @param query - The query builder to extract pagination parameters from * @returns A new Pagination instance configured with the query's pagination settings */ static fromQuery<T extends SchemaType, Inner extends any[]>(query: ToriiQueryBuilder<T>, nextCursor?: string): Pagination<T, Inner>; /** * Sets the items for the current page * * @param items - The items to set for the current page * @returns The Pagination instance for method chaining */ withItems(items: Inner): this; /** * Gets the current page's items * * @returns The array of items for the current page */ getItems(): Inner; /** * Gets a query builder configured for the next page * * @param query - The base query builder to configure * @returns A new query builder configured for the next page */ getNextQuery(query: ToriiQueryBuilder<T>): ToriiQueryBuilder<T>; /** * Gets a query builder configured for the previous page * * @param query - The base query builder to configure * @returns A new query builder configured for the previous page */ getPreviousQuery(query: ToriiQueryBuilder<T>): ToriiQueryBuilder<T>; } /** * SchemaType represents the structure of your Dojo world models. * Each namespace contains models defined by their field types. * * @example * ```typescript * const schema = { * world: { * Player: { * id: 'felt252', * name: 'string', * score: 'u32' * }, * Item: { * id: 'felt252', * name: 'string', * durability: 'u8' * } * } * } satisfies SchemaType; * ``` */ type SchemaType = { /** * namespace: Your namespace for grouping related models. * This is typically used to organize models by their domain or context. * For example, 'world', 'game', 'inventory', etc. */ [namespace: string]: { /** * model: Your model name, case sensitive. * This represents a specific entity or concept within your namespace. * For example, 'Player', 'Item', 'Quest', etc. */ [model: string]: { /** * Dynamic fields of the model. * These can be of any type, typically representing the properties of your model. */ [field: string]: any; }; }; }; /** * Standardized result of a query - an array of parsed entities. */ type StandardizedQueryResult<T extends SchemaType> = Array<ParsedEntity<T>>; /** * Parsed entity with its ID and models. * Ensures that each model's data adheres to the schema's field types. * * @example * ```typescript * // Given a schema: * const schema = { * world: { * Player: { * id: 'felt252', * name: 'string', * score: 'u32' * } * } * } satisfies SchemaType; * * // A ParsedEntity might look like: * const entity: ParsedEntity<typeof schema> = { * entityId: '0x123', * models: { * world: { * Player: { * id: '0x123', * name: 'Alice', * score: 100 * } * } * } * }; * ``` */ type ParsedEntity<T extends SchemaType> = { entityId: string; models: { [K in keyof T]: { [M in keyof T[K]]?: T[K][M] extends object ? Partial<T[K][M]> : T[K][M]; }; }; }; /** * Utility type to extract all models' data from SchemaType. * This is useful for typing messages and model data. */ type UnionOfModelData<T extends SchemaType> = { [K in keyof T]: { [L in keyof T[K]]: T[K][L]; }[keyof T[K]]; }[keyof T]; /** * Response type for queries with pagination support. */ type ToriiResponse<T extends SchemaType> = Pagination<T, StandardizedQueryResult<T>>; /** * Response type for subscriptions - includes initial data and subscription handle. */ type SubscribeResponse<T extends SchemaType> = [ ToriiResponse<T>, torii.Subscription ]; /** * Request type for getting tokens. */ interface GetTokenRequest { contractAddresses?: string[]; tokenIds?: string[]; } /** * Request type for getting token balances. */ interface GetTokenBalanceRequest extends GetTokenRequest { accountAddresses?: string[]; } /** * Success result for subscription callbacks. */ type Success<T> = { data: T; error: undefined; }; /** * Failure result for subscription callbacks. */ type Failure<E> = { data: undefined; error: E; }; /** * Arguments passed to subscription callbacks. * Either contains data or an error, but not both. */ type SubscriptionCallbackArgs<T, E = Error> = Success<T> | Failure<E>; /** * Callback function type for subscriptions. */ type SubscriptionCallback<T> = (response: SubscriptionCallbackArgs<T>) => void; /** * Request type for subscribing to token balance updates. */ type SubscribeTokenBalanceRequest = GetTokenBalanceRequest & { callback: SubscriptionCallback<torii.TokenBalance>; }; /** * Request type for updating token balance subscriptions. */ type UpdateTokenBalanceSubscriptionRequest = GetTokenBalanceRequest & { subscription: torii.Subscription; }; type SubscribeTokenRequest = GetTokenRequest & { callback: SubscriptionCallback<torii.Token>; }; /** * SDK interface for interacting with the DojoEngine. * Provides methods for querying, subscribing, and managing your Dojo world. * * @template T - The schema type defining your world's models. * * @example * ```typescript * const sdk = await init<typeof schema>({ * client: { worldAddress: "0x...", toriiUrl: "http://localhost:8080" }, * domain: { name: "MyApp", version: "1.0.0", chainId: "SN_MAIN" } * }); * * // Query entities * const { items } = await sdk.getEntities({ * query: new ToriiQueryBuilder().withClause(...) * }); * * // Subscribe to updates * const [initial, subscription] = await sdk.subscribeEntityQuery({ * query: new ToriiQueryBuilder().withClause(...), * callback: ({ data, error }) => { * if (error) console.error(error); * else console.log(data); * } * }); * ``` */ interface SDK<T extends SchemaType> { /** * The underlying Torii client instance. * Use this for advanced operations not covered by the SDK methods. */ client: torii.ToriiClient; /** * Subscribes to entity updates based on the provided query. * Returns initial data and a subscription handle. * * @param {SubscribeParams<T>} params - Query and callback parameters * @returns {Promise<SubscribeResponse<T>>} - Initial data and subscription handle * * @example * ```typescript * const [initial, subscription] = await sdk.subscribeEntityQuery({ * query: new ToriiQueryBuilder() * .withClause(KeysClause([ModelsMapping.Player], [address])), * callback: ({ data, error }) => { * if (data) console.log('Entity updated:', data); * } * }); * ``` */ subscribeEntityQuery: (params: SubscribeParams<T>) => Promise<SubscribeResponse<T>>; /** * Subscribes to event messages based on the provided query. * Returns initial data and a subscription handle. * * @param {SubscribeParams<T>} params - Query and callback parameters * @returns {Promise<SubscribeResponse<T>>} - Initial data and subscription handle */ subscribeEventQuery: (params: SubscribeParams<T>) => Promise<SubscribeResponse<T>>; /** * Subscribes to token balance updates. * Returns initial balances and a subscription handle. * * @param {SubscribeTokenBalanceRequest} request - Filter and callback parameters * @returns {Promise<[torii.TokenBalances, torii.Subscription]>} - Initial balances and subscription */ subscribeTokenBalance: (request: SubscribeTokenBalanceRequest) => Promise<[torii.TokenBalances, torii.Subscription]>; /** * Subscribes to token updates * * # Parameters * @param {SubscribeTokenRequest} request * @returns {Promise<[torii.Tokens, torii.Subscription]>} */ subscribeToken: (request: SubscribeTokenRequest) => Promise<[torii.Tokens, torii.Subscription]>; /** * Fetches entities from the Torii client based on the provided query. * * @param {GetParams<T>} params - Query parameters * @returns {Promise<ToriiResponse<T>>} - Paginated query results * * @example * ```typescript * const result = await sdk.getEntities({ * query: new ToriiQueryBuilder() * .withClause(KeysClause([ModelsMapping.Player], [address])) * .limit(10) * }); * * // Access entities * result.items.forEach(entity => { * console.log(entity.models.world.Player); * }); * * // Load more if available * if (result.hasNextPage()) { * const nextPage = await result.fetchNextPage(); * } * ``` */ getEntities: (params: GetParams<T>) => Promise<ToriiResponse<T>>; /** * Fetches event messages from the Torii client based on the provided query. * * @param {GetParams<T>} params - Query parameters * @returns {Promise<ToriiResponse<T>>} - Paginated query results */ getEventMessages: (params: GetParams<T>) => Promise<ToriiResponse<T>>; /** * Generates typed data for signing messages. * Used for creating off-chain messages that can be verified on-chain. * * @param {string} nsModel - Model name prefixed with namespace (e.g., "world-Player") * @param {M} message - The message data conforming to the model structure * @param {Array} modelMapping - Optional custom type mappings * @param {Record} additionalTypes - Optional additional EIP-712 types * @returns {TypedData} - EIP-712 typed data ready for signing */ generateTypedData: <M extends UnionOfModelData<T>>(nsModel: string, message: M, modelMapping?: Array<{ name: string; type: string; }>, additionalTypes?: Record<string, Array<{ name: string; type: string; }>>) => TypedData; /** * Sends a signed message to the Torii server. * In web environments, requires an Account. In Node.js, uses configured signer. * * @param {TypedData} data - The typed data to sign and send * @param {Account} account - The account to sign with (web only) * @returns {Promise<Result<Uint8Array, string>>} - Success with message ID or error */ sendMessage: (data: TypedData, account?: Account) => Promise<Result<string, string>>; /** * Sends multiple signed messages to the Torii server in a batch. * In web environments, requires an Account. In Node.js, uses configured signer. * * @param {TypedData[]} data - Array of typed data to sign and send * @param {Account} account - The account to sign with (web only) * @returns {Promise<Result<string[], string>>} - Success with array of message IDs or error */ sendMessageBatch: (data: TypedData[], account?: Account) => Promise<Result<string[], string>>; /** * Gets token information. * * @param {GetTokenRequest} request - Filter parameters * @returns {Promise<torii.Tokens>} - Token information */ getTokens(request: GetTokenRequest): Promise<torii.Tokens>; /** * Gets token balances for specified accounts. * * @param {GetTokenBalanceRequest} request - Filter parameters * @returns {Promise<torii.TokenBalances>} - Token balances */ getTokenBalances(request: GetTokenBalanceRequest): Promise<torii.TokenBalances>; /** * Creates a subscription for token balance updates. * Unlike `subscribeTokenBalance`, this only returns the subscription handle. * * @param {SubscribeTokenBalanceRequest} request - Filter and callback parameters * @returns {torii.Subscription} - Subscription handle */ onTokenBalanceUpdated: (request: SubscribeTokenBalanceRequest) => torii.Subscription; /** * Subscribes to token updates * * # Parameters * @param {string[]} contract_addresses - Array of contract addresses to filter (empty for all) * @param {string[]} token_ids - Array of token ids to filter (empty for all) * @param {Function} callback - JavaScript function to call on updates * * # Returns * Result containing subscription handle or error * @returns torii.Subscription */ onTokenUpdated: (request: SubscribeTokenRequest) => torii.Subscription; /** * Updates an existing token balance subscription with new filters. * * @param {UpdateTokenBalanceSubscriptionRequest} request - New filter parameters * @returns {Promise<void>} */ updateTokenBalanceSubscription: (request: UpdateTokenBalanceSubscriptionRequest) => Promise<void>; /** * Updates an existing entity subscription with new clauses. * * @param {torii.Subscription} subscription - Existing subscription to update * @param {torii.Clause} clauses - New filter clauses * @returns {Promise<void>} */ updateEntitySubscription: (subscription: torii.Subscription, clauses: torii.Clause) => Promise<void>; /** * Updates an existing event message subscription with new clauses. * * @param {torii.Subscription} subscription - Existing subscription to update * @param {torii.Clause} clauses - New filter clauses * @returns {Promise<void>} */ updateEventMessageSubscription: (subscription: torii.Subscription, clauses: torii.Clause, historical: boolean) => Promise<void>; /** * Gets controller information for the specified contract addresses. * * @param {string[]} contract_addresses - Contract addresses to query (empty for all) * @returns {Promise<torii.Controllers>} - Controller information */ getControllers: (contract_addresses: string[]) => Promise<torii.Controllers>; } /** * Client configuration type with required world address. */ type SDKClientConfig = Partial<Omit<torii.ClientConfig, "worldAddress">> & { worldAddress: torii.ClientConfig["worldAddress"]; }; /** * Configuration interface for initializing the SDK. * * @example * ```typescript * const config: SDKConfig = { * client: { * worldAddress: "0x...", * toriiUrl: "http://localhost:8080", * relayUrl: "/ip4/127.0.0.1/tcp/9090" * }, * domain: { * name: "MyApp", * version: "1.0.0", * chainId: "SN_MAIN" * }, * // Node.js only: * signer: signingKey, * identity: "0x..." * }; * ``` */ interface SDKConfig { /** * Configuration for the Torii client connection. */ client: SDKClientConfig; /** * The Starknet domain configuration for EIP-712 typed data. */ domain: StarknetDomain; /** * Signing key for off-chain messages (Node.js only). * Required when using `sendMessage` in Node.js environments. */ signer?: torii.SigningKey; /** * Identity address for off-chain messages (Node.js only). * This should match the `identity` field in your Dojo models. */ identity?: string; /** * Enable debug logging for queries and subscriptions. */ withLogger?: boolean; } /** * @deprecated - Use SDKConfig.withLogger instead */ interface SDKFunctionOptions { logging?: boolean; } /** * Parameters for subscription methods. */ interface SubscribeParams<T extends SchemaType> { /** * Query builder instance configured with your filters. */ query: ToriiQueryBuilder<T>; /** * Callback function invoked when data changes. * Receives either data or an error, but not both. */ callback: SubscriptionCallback<StandardizedQueryResult<T>>; /** * @deprecated - Use `query.historical()` instead */ historical?: boolean; } /** * Parameters for get/fetch methods. */ interface GetParams<T extends SchemaType> { /** * Query builder instance configured with your filters. */ query: ToriiQueryBuilder<T>; /** * @deprecated - Use `query.historical()` instead */ historical?: boolean; } type ToriiQueryBuilderOptions = Omit<Partial<Query>, "clause">; declare class ToriiQueryBuilder<T extends SchemaType> { private query; constructor(options?: ToriiQueryBuilderOptions); /** * Set the maximum number of results to return */ withLimit(limit: number): ToriiQueryBuilder<T>; /** * Set the offset for pagination * @deprecated Use `withCursor` instead */ withOffset(): ToriiQueryBuilder<T>; /** * Set the cursor for pagination * undefined is default, fetch from starting point * `next_cursor` is return from queries */ withCursor(cursor: string): ToriiQueryBuilder<T>; /** * Set the maximum number of results to return */ withDirection(direction: PaginationDirection): ToriiQueryBuilder<T>; /** * Add the clause to filter results */ withClause(clause: Clause): ToriiQueryBuilder<T>; /** * Set whether to include hashed keys in the response * HashedKeys represent internal torii entity id. */ includeHashedKeys(): ToriiQueryBuilder<T>; /** * Add a single order by clause */ addOrderBy(model: keyof T & string, member: string, direction: "Asc" | "Desc"): ToriiQueryBuilder<T>; /** * Add multiple order by clauses at once */ withOrderBy(orderBy: OrderBy[]): ToriiQueryBuilder<T>; /** * Add a single entity model to filter */ addEntityModel(model: keyof T & string): ToriiQueryBuilder<T>; /** * Set multiple entity models at once */ withEntityModels(models: (keyof T & string)[]): ToriiQueryBuilder<T>; /** * Build the final query */ build(): Query; /** * Create a new builder instance with pagination settings * */ static withPagination<T extends Record<string, Record<string, any>>>(cursor: string, limit: number, direction: PaginationDirection): ToriiQueryBuilder<T>; /** * Returns inner clause inside a Result wrapper. */ getClause(): Result<Clause, string>; getPagination(): Pagination$1; } type HistoricalToriiQueryBuilderOptions = Omit<Partial<ToriiQueryBuilderOptions>, "historical">; declare class HistoricalToriiQueryBuilder<T extends SchemaType> extends ToriiQueryBuilder<T> { constructor(options?: ToriiQueryBuilderOptions); } type ToUnion<T> = T extends infer U ? U : never; type ExtractPrimitiveKeys<T> = T extends Record<infer K, any> ? K : never; type PrimitiveTypeKeys = ToUnion<ExtractPrimitiveKeys<torii.Primitive>>; type MemberValueParam = { type: PrimitiveTypeKeys; value: any; } | any; type ClauseBuilderInterface = { build(): Clause$1; }; type ModelPath<T, K extends keyof T> = K extends string ? T[K] extends Record<string, any> ? { [SubK in keyof T[K]]: `${K}-${SubK & string}`; }[keyof T[K]] : never : never; type GetModelType<T, Path extends string> = Path extends `${infer Namespace}-${infer Model}` ? Namespace extends keyof T ? Model extends keyof T[Namespace] ? T[Namespace][Model] : never : never : never; /** * Saves some keyboard strokes to get a KeysClause. * * @param models - the models you want to query, has to be in form of ns-Model * @param keys - the keys that has the model. You can use `undefined` as a wildcard to match any key * @param pattern - either VariableLen or FixedLen - to check exact match of key number * @return ClauseBuilder<T> */ declare function KeysClause<T extends SchemaType>(models: ModelPath<T, keyof T>[], keys: (string | undefined)[], pattern?: PatternMatching): ClauseBuilder<T>; /** * Saves some keyboard strokes to get a HashedKeysClause. * * @param keys - the hashed_keys (entityId) that you want to query over * @return ClauseBuilder<T> */ declare function HashedKeysClause<T extends SchemaType>(keys: BigNumberish[]): ClauseBuilder<T>; /** * Saves some keyboard strokes to get a MemberClause. * * @template T - the schema type * @param model - the model you want to query, has to be in form of ns-Model * @param member - the member of the model on which you want to apply operator * @param operator - the operator to apply * @param value - the value to operate on. * @return ClauseBuilder<T> */ declare function MemberClause<T extends SchemaType, Path extends ModelPath<T, keyof T>, M extends keyof GetModelType<T, ModelPath<T, keyof T>>>(model: Path, member: M & string, operator: ComparisonOperator, value: GetModelType<T, Path>[M] | GetModelType<T, Path>[M][] | MemberValueParam): ClauseBuilder<T>; /** * Saves some keyboard strokes to get a Composite "Or" Clause * * @template T - the schema type * @param clauses - the inner clauses that you want to compose * @return CompositeBuilder<T> */ declare function AndComposeClause<T extends SchemaType>(clauses: ClauseBuilderInterface[]): CompositeBuilder<T>; /** * Saves some keyboard strokes to get a Composite "And" Clause * @template T - the schema type * @param clauses - the inner clauses that you want to compose * @return CompositeBuilder<T> */ declare function OrComposeClause<T extends SchemaType>(clauses: ClauseBuilderInterface[]): CompositeBuilder<T>; declare class ClauseBuilder<T extends SchemaType> { private clause; constructor(); /** * Create a clause based on entity keys */ keys(models: ModelPath<T, keyof T>[], keys: (string | undefined)[], pattern?: PatternMatching): ClauseBuilder<T>; /** * Create a hashed keys clause based on entity keys * keys: an array of your keys array (no need to hash it, just pass raw keys) */ hashed_keys(keys: BigNumberish[]): ClauseBuilder<T>; /** * Create a member clause for comparing values */ where<Path extends ModelPath<T, keyof T>, M extends keyof GetModelType<T, Path>>(model: Path, member: M & string, operator: ComparisonOperator, value: GetModelType<T, Path>[M] | GetModelType<T, Path>[M][] | MemberValueParam): ClauseBuilder<T>; /** * Start a composite clause chain */ compose(): CompositeBuilder<T>; /** * Build the final clause */ build(): Clause$1; } declare class CompositeBuilder<T extends Record<string, Record<string, any>>> { private orClauses; private andClauses; or(clauses: ClauseBuilderInterface[]): CompositeBuilder<T>; and(clauses: ClauseBuilderInterface[]): CompositeBuilder<T>; build(): Clause$1; } type DojoWorkerCallback = () => Promise<Subscription[]>; /** * Creates a worker process that manages subscriptions. * * This function executes the provided callback to obtain subscriptions, * and sets up signal handlers to ensure proper cleanup when the process * is terminated. When SIGTERM or SIGINT signals are received, all subscriptions * are freed before the process exits. * * @param callback - A function that returns a Promise resolving to an array of Subscriptions * @returns A Promise that resolves when the worker is set up * * @example * ```ts * await createWorker(async () => { * const client = await createClient(); * return [client.subscribe(...)]; * }); * ``` */ declare function createWorker(callback: DojoWorkerCallback): Promise<void>; /** * Custom hook to retrieve a specific model for a given entityId within a specified namespace. * * @param entityId - The ID of the entity. * @param model - The model to retrieve, specified as a string in the format "namespace-modelName". * @returns The model structure if found, otherwise undefined. */ declare function getModelByEntityId<N extends keyof SchemaType, M extends keyof SchemaType[N] & string, Schema extends SchemaType>(entityId: BigNumberish, model: `${N}-${M}`, value: StandardizedQueryResult<Schema>): SchemaType[N][M] | undefined; /** * Custom hook to retrieve a specific model for a given entityId within a specified namespace. * * @param entityId - The ID of the entity. * @param model - The model to retrieve, specified as a string in the format "namespace-modelName". * @returns The model structure if found, otherwise undefined. */ declare function getModel<N extends keyof SchemaType, M extends keyof SchemaType[N] & string, Schema extends SchemaType>(model: `${N}-${M}`, value: StandardizedQueryResult<Schema>): SchemaType[N][M] | undefined; declare const defaultClientConfig: Partial<torii$1.ClientConfig>; /** * Initializes the SDK for Node.js environment with the provided configuration and schema. * * @template T - The schema type. * @param {SDKConfig} options - The configuration object for the SDK. * @returns {Promise<SDK<T>>} - A promise that resolves to the initialized SDK instance. * * @example * ```typescript * import { init } from "@dojoengine/sdk/node"; * import { schema } from "./models.gen"; * * const sdk = await init<typeof schema>({ * client: { * worldAddress: "0x...", * toriiUrl: "http://localhost:8080", * }, * domain: { * name: "MyApp", * version: "1.0.0", * chainId: "SN_MAIN", * }, * signer: signingKey, * identity: "0x...", * }); * ``` */ declare function init<T extends SchemaType>(options: SDKConfig): Promise<SDK<T>>; export { AndComposeClause, ClauseBuilder, type DojoWorkerCallback, type GetParams, type GetTokenBalanceRequest, type GetTokenRequest, HashedKeysClause, HistoricalToriiQueryBuilder, type HistoricalToriiQueryBuilderOptions, KeysClause, MemberClause, OrComposeClause, type ParsedEntity, type SDK, type SDKClientConfig, type SDKConfig, type SDKFunctionOptions, type SchemaType, type StandardizedQueryResult, type SubscribeParams, type SubscribeResponse, type SubscribeTokenBalanceRequest, type SubscribeTokenRequest, type SubscriptionCallback, type SubscriptionCallbackArgs, ToriiQueryBuilder, type ToriiResponse, type UnionOfModelData, type UpdateTokenBalanceSubscriptionRequest, createWorker, defaultClientConfig, getModel, getModelByEntityId, init };