@solana/plugin-core 7.0.0 → 7.1.0-canary-20260812160611

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,7 +67,7 @@ function addCleanupToClientWithExistingStack(client, cleanup) {
67
67
  return client;
68
68
  }
69
69
  function addCleanupToClientWithoutExistingStack(client, cleanup) {
70
- const stack = new DisposableStack();
70
+ const stack = createCleanupStack();
71
71
  if (Symbol.dispose in client) {
72
72
  const existingDispose = client[Symbol.dispose];
73
73
  stack.defer(() => existingDispose.call(client));
@@ -76,11 +76,60 @@ function addCleanupToClientWithoutExistingStack(client, cleanup) {
76
76
  const additions = {
77
77
  [DISPOSABLE_STACK_PROPERTY]: stack,
78
78
  [Symbol.dispose]() {
79
- stack[Symbol.dispose]();
79
+ stack.dispose();
80
80
  }
81
81
  };
82
82
  return extendClient(client, additions);
83
83
  }
84
+ function createCleanupStack() {
85
+ return typeof globalThis.DisposableStack === "function" ? new globalThis.DisposableStack() : createFallbackCleanupStack();
86
+ }
87
+ function createFallbackCleanupStack() {
88
+ const cleanups = [];
89
+ let disposed = false;
90
+ return {
91
+ defer(cleanup) {
92
+ if (disposed) {
93
+ throw new ReferenceError("Cannot add values to a disposed stack");
94
+ }
95
+ if (typeof cleanup !== "function") {
96
+ throw new TypeError(`${String(cleanup)} is not a function`);
97
+ }
98
+ cleanups.push(cleanup);
99
+ },
100
+ dispose() {
101
+ if (disposed) {
102
+ return;
103
+ }
104
+ disposed = true;
105
+ let error;
106
+ let hasError = false;
107
+ for (let ii = cleanups.length - 1; ii >= 0; ii--) {
108
+ try {
109
+ cleanups[ii]();
110
+ } catch (e) {
111
+ error = hasError ? createSuppressedError(e, error) : e;
112
+ hasError = true;
113
+ }
114
+ }
115
+ cleanups.length = 0;
116
+ if (hasError) {
117
+ throw error;
118
+ }
119
+ }
120
+ };
121
+ }
122
+ function createSuppressedError(error, suppressed) {
123
+ const message = "An error was suppressed during disposal";
124
+ if (typeof globalThis.SuppressedError === "function") {
125
+ return new globalThis.SuppressedError(error, suppressed, message);
126
+ }
127
+ return Object.defineProperties(new Error(message), {
128
+ error: { configurable: true, value: error, writable: true },
129
+ name: { configurable: true, value: "SuppressedError", writable: true },
130
+ suppressed: { configurable: true, value: suppressed, writable: true }
131
+ });
132
+ }
84
133
 
85
134
  exports.createClient = createClient;
86
135
  exports.extendClient = extendClient;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.browser.cjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Later, when the client is no longer needed:\n * using client = createClient().use(myPlugin();\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = new DisposableStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack[Symbol.dispose]();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":";;;AA2KO,SAAS,aAA4C,KAAA,EAA8B;AACtF,EAAA,OAAO,MAAA,CAAO,KAAA,IAAU,EAAY,CAAA;AACxC;AAEA,SAAS,OAA6B,KAAA,EAA6B;AAC/D,EAAA,OAAO,MAAA,CAAO,MAAA;AAAA,IACV,MAAA,CAAO,gBAAA;AAAA,MACH,EAAC;AAAA,MACD;AAAA,QACI,GAAG,MAAA,CAAO,yBAAA,CAA0B,KAAK,CAAA;AAAA,QACzC,GAAA,EAAK;AAAA,UACD,YAAA,EAAc,KAAA;AAAA,UACd,UAAA,EAAY,IAAA;AAAA,UACZ,KAAA,EAAO,SAAoD,MAAA,EAAsC;AAC7F,YAAA,MAAM,MAAA,GAAS,OAAO,KAAK,CAAA;AAC3B,YAAA,OAAO,kBAAkB,OAAA,GAAU,iBAAA,CAAkB,MAAM,CAAA,GAAI,OAAO,MAAM,CAAA;AAAA,UAChF,CAAA;AAAA,UACA,QAAA,EAAU;AAAA;AACd;AACJ;AACJ,GACJ;AACJ;AAEA,SAAS,kBAAwC,OAAA,EAA6C;AAC1F,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACjB,MAAM,UAAA,EAAY;AACd,MAAA,OAAO,OAAA,CAAQ,KAAK,CAAA,CAAA,KAAK,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,MAAM,UAAU,CAAA;AAAA,IACxD,CAAA;AAAA,IACA,QAAQ,SAAA,EAAW;AACf,MAAA,OAAO,OAAA,CAAQ,KAAK,CAAA,CAAA,KAAK,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,QAAQ,SAAS,CAAA;AAAA,IACzD,CAAA;AAAA,IACA,IAAA,CAAK,aAAa,UAAA,EAAY;AAC1B,MAAA,OAAO,OAAA,CAAQ,KAAK,CAAA,CAAA,KAAK,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,IAAA,CAAK,WAAA,EAAa,UAAU,CAAA;AAAA,IACpE,CAAA;AAAA,IACA,IAA8C,MAAA,EAAsC;AAChF,MAAA,OAAO,iBAAA,CAAkB,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,IACjD;AAAA,GACmB,CAAA;AAC3B;AA2DO,SAAS,YAAA,CACZ,QACA,SAAA,EACmC;AACnC,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,gBAAA,CAAiB,EAAC,EAAG,0BAA0B,MAAA,CAAO,yBAAA,CAA0B,MAAM,CAAC,CAAC,CAAA;AAC9G,EAAA,MAAA,CAAO,gBAAA,CAAiB,MAAA,EAAQ,MAAA,CAAO,yBAAA,CAA0B,SAAS,CAAC,CAAA;AAC3E,EAAA,OAAO,MAAA,CAAO,OAAO,MAAM,CAAA;AAC/B;AAEA,SAAS,0BAA2D,WAAA,EAAmB;AACnF,EAAA,MAAM,SAAS,EAAC;AAChB,EAAA,KAAA,MAAW,GAAA,IAAO,OAAA,CAAQ,OAAA,CAAQ,WAAW,CAAA,EAAG;AAC5C,IAAA,MAAA,CAAO,GAAG,IAAI,EAAE,GAAG,YAAY,GAAc,CAAA,EAAG,cAAc,IAAA,EAAK;AAAA,EACvE;AACA,EAAA,OAAO,MAAA;AACX;AA4DO,SAAS,WAAA,CACZ,QACA,OAAA,EACmC;AACnC,EAAA,IAAI,6BAA6B,MAAA,EAAQ;AACrC,IAAA,OAAO,mCAAA;AAAA,MACH,MAAA;AAAA,MACA;AAAA,KACJ;AAAA,EACJ,CAAA,MAAO;AACH,IAAA,OAAO,sCAAA,CAAuC,QAAQ,OAAO,CAAA;AAAA,EACjE;AACJ;AAEA,IAAM,yBAAA,GAA4B,6BAAA;AAElC,SAAS,mCAAA,CACL,QACA,OAAA,EACmC;AAEnC,EAAA,MAAA,CAAO,yBAAyB,CAAA,CAAE,KAAA,CAAM,OAAO,CAAA;AAE/C,EAAA,OAAO,MAAA;AACX;AAEA,SAAS,sCAAA,CACL,QACA,OAAA,EACmC;AACnC,EAAA,MAAM,QAAQ,kBAAA,EAAmB;AAGjC,EAAA,IAAI,MAAA,CAAO,WAAW,MAAA,EAAQ;AAC1B,IAAA,MAAM,eAAA,GAAmB,MAAA,CAAsB,MAAA,CAAO,OAAO,CAAA;AAC7D,IAAA,KAAA,CAAM,KAAA,CAAM,MAAM,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EAClD;AAGA,EAAA,KAAA,CAAM,MAAM,OAAO,CAAA;AAGnB,EAAA,MAAM,SAAA,GAAY;AAAA,IACd,CAAC,yBAAyB,GAAG,KAAA;AAAA,IAC7B,CAAC,MAAA,CAAO,OAAO,CAAA,GAAI;AACf,MAAA,KAAA,CAAM,OAAA,EAAQ;AAAA,IAClB;AAAA,GACJ;AAEA,EAAA,OAAO,YAAA,CAAa,QAAQ,SAAS,CAAA;AACzC;AAeA,SAAS,kBAAA,GAAmC;AAIxC,EAAA,OAAO,OAAO,WAAW,eAAA,KAAoB,UAAA,GACvC,IAAI,UAAA,CAAW,eAAA,KACf,0BAAA,EAA2B;AACrC;AAEA,SAAS,0BAAA,GAA2C;AAChD,EAAA,MAAM,WAA2B,EAAC;AAClC,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,OAAO;AAAA,IACH,MAAM,OAAA,EAAS;AACX,MAAA,IAAI,QAAA,EAAU;AAIV,QAAA,MAAM,IAAI,eAAe,uCAAuC,CAAA;AAAA,MACpE;AACA,MAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AAI/B,QAAA,MAAM,IAAI,SAAA,CAAU,CAAA,EAAG,MAAA,CAAO,OAAO,CAAC,CAAA,kBAAA,CAAoB,CAAA;AAAA,MAC9D;AACA,MAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,OAAA,GAAU;AACN,MAAA,IAAI,QAAA,EAAU;AACV,QAAA;AAAA,MACJ;AACA,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,IAAI,KAAA;AACJ,MAAA,IAAI,QAAA,GAAW,KAAA;AAGf,MAAA,KAAA,IAAS,KAAK,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG,EAAA,IAAM,GAAG,EAAA,EAAA,EAAM;AAC9C,QAAA,IAAI;AACA,UAAA,QAAA,CAAS,EAAE,CAAA,EAAE;AAAA,QACjB,SAAS,CAAA,EAAG;AACR,UAAA,KAAA,GAAQ,QAAA,GAAW,qBAAA,CAAsB,CAAA,EAAG,KAAK,CAAA,GAAI,CAAA;AACrD,UAAA,QAAA,GAAW,IAAA;AAAA,QACf;AAAA,MACJ;AACA,MAAA,QAAA,CAAS,MAAA,GAAS,CAAA;AAClB,MAAA,IAAI,QAAA,EAAU;AACV,QAAA,MAAM,KAAA;AAAA,MACV;AAAA,IACJ;AAAA,GACJ;AACJ;AAEA,SAAS,qBAAA,CAAsB,OAAgB,UAAA,EAA4B;AAIvE,EAAA,MAAM,OAAA,GAAU,yCAAA;AAChB,EAAA,IAAI,OAAO,UAAA,CAAW,eAAA,KAAoB,UAAA,EAAY;AAClD,IAAA,OAAO,IAAI,UAAA,CAAW,eAAA,CAAgB,KAAA,EAAO,YAAY,OAAO,CAAA;AAAA,EACpE;AAIA,EAAA,OAAO,MAAA,CAAO,gBAAA,CAAiB,IAAI,KAAA,CAAM,OAAO,CAAA,EAAG;AAAA,IAC/C,OAAO,EAAE,YAAA,EAAc,MAAM,KAAA,EAAO,KAAA,EAAO,UAAU,IAAA,EAAK;AAAA,IAC1D,MAAM,EAAE,YAAA,EAAc,MAAM,KAAA,EAAO,iBAAA,EAAmB,UAAU,IAAA,EAAK;AAAA,IACrE,YAAY,EAAE,YAAA,EAAc,MAAM,KAAA,EAAO,UAAA,EAAY,UAAU,IAAA;AAAK,GACvE,CAAA;AACL","file":"index.browser.cjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one\n * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have\n * not shipped explicit resource management are supported too, though a `using` declaration needs a\n * `Symbol.dispose` polyfill there.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Build the client in the scope that should own it:\n * using client = createClient().use(myPlugin());\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @example Disposing without a using declaration\n * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.\n * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the\n * polyfill before any client is created, since a client registers its dispose method under\n * whatever `Symbol.dispose` was at the time.\n * ```ts\n * const client = createClient().use(myPlugin());\n *\n * // Later, when the client is no longer needed:\n * client[Symbol.dispose]();\n * // `socket.close()` has now been called.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform\n * availability of `DisposableStack`.\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = createCleanupStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack.dispose();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n\n/**\n * The slice of `DisposableStack` that {@link withCleanup} relies on.\n *\n * Deliberately narrow — `defer()` and the plain `dispose()` method only — so that it can be\n * satisfied both by the platform's `DisposableStack` and by {@link createFallbackCleanupStack} on\n * runtimes that lack one. Note that `dispose()` is used in preference to `[Symbol.dispose]()`\n * because runtimes missing `DisposableStack` are missing `Symbol.dispose` as well.\n */\ntype CleanupStack = {\n defer(cleanup: () => void): void;\n dispose(): void;\n};\n\nfunction createCleanupStack(): CleanupStack {\n // Always prefer the runtime's own implementation. The fallback exists only for runtimes that\n // have not shipped explicit resource management — most notably Safari, which as of Safari 27\n // provides neither `DisposableStack` nor `Symbol.dispose`.\n return typeof globalThis.DisposableStack === 'function'\n ? new globalThis.DisposableStack()\n : createFallbackCleanupStack();\n}\n\nfunction createFallbackCleanupStack(): CleanupStack {\n const cleanups: (() => void)[] = [];\n let disposed = false;\n return {\n defer(cleanup) {\n if (disposed) {\n // Mirrors the `ReferenceError` thrown by `DisposableStack.prototype.defer` so that\n // both stacks refuse a late cleanup the same way, rather than silently accepting\n // one that will never run.\n throw new ReferenceError('Cannot add values to a disposed stack');\n }\n if (typeof cleanup !== 'function') {\n // `DisposableStack.prototype.defer` rejects a non-callable up front. Deferring that\n // failure to disposal would surface it far from its cause, and would tangle it up in\n // the error aggregation of unrelated cleanups.\n throw new TypeError(`${String(cleanup)} is not a function`);\n }\n cleanups.push(cleanup);\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n let error: unknown;\n let hasError = false;\n // Cleanups run in reverse order of registration, and one that throws must not stop the\n // rest from running; their errors are aggregated into a `SuppressedError` chain instead.\n for (let ii = cleanups.length - 1; ii >= 0; ii--) {\n try {\n cleanups[ii]();\n } catch (e) {\n error = hasError ? createSuppressedError(e, error) : e;\n hasError = true;\n }\n }\n cleanups.length = 0;\n if (hasError) {\n throw error;\n }\n },\n };\n}\n\nfunction createSuppressedError(error: unknown, suppressed: unknown): Error {\n // The message `DisposableStack` disposal produces. Passing it explicitly matters: the two-argument\n // form of `SuppressedError` leaves the message empty, which would make an aggregated error read\n // differently depending on which stack produced it.\n const message = 'An error was suppressed during disposal';\n if (typeof globalThis.SuppressedError === 'function') {\n return new globalThis.SuppressedError(error, suppressed, message);\n }\n // A runtime without `DisposableStack` has no `SuppressedError` constructor either, so reproduce\n // its shape on a plain error — including the non-enumerability of every property, so that\n // serializing or spreading the error does not depend on which runtime built it.\n return Object.defineProperties(new Error(message), {\n error: { configurable: true, value: error, writable: true },\n name: { configurable: true, value: 'SuppressedError', writable: true },\n suppressed: { configurable: true, value: suppressed, writable: true },\n });\n}\n"]}
@@ -65,7 +65,7 @@ function addCleanupToClientWithExistingStack(client, cleanup) {
65
65
  return client;
66
66
  }
67
67
  function addCleanupToClientWithoutExistingStack(client, cleanup) {
68
- const stack = new DisposableStack();
68
+ const stack = createCleanupStack();
69
69
  if (Symbol.dispose in client) {
70
70
  const existingDispose = client[Symbol.dispose];
71
71
  stack.defer(() => existingDispose.call(client));
@@ -74,11 +74,60 @@ function addCleanupToClientWithoutExistingStack(client, cleanup) {
74
74
  const additions = {
75
75
  [DISPOSABLE_STACK_PROPERTY]: stack,
76
76
  [Symbol.dispose]() {
77
- stack[Symbol.dispose]();
77
+ stack.dispose();
78
78
  }
79
79
  };
80
80
  return extendClient(client, additions);
81
81
  }
82
+ function createCleanupStack() {
83
+ return typeof globalThis.DisposableStack === "function" ? new globalThis.DisposableStack() : createFallbackCleanupStack();
84
+ }
85
+ function createFallbackCleanupStack() {
86
+ const cleanups = [];
87
+ let disposed = false;
88
+ return {
89
+ defer(cleanup) {
90
+ if (disposed) {
91
+ throw new ReferenceError("Cannot add values to a disposed stack");
92
+ }
93
+ if (typeof cleanup !== "function") {
94
+ throw new TypeError(`${String(cleanup)} is not a function`);
95
+ }
96
+ cleanups.push(cleanup);
97
+ },
98
+ dispose() {
99
+ if (disposed) {
100
+ return;
101
+ }
102
+ disposed = true;
103
+ let error;
104
+ let hasError = false;
105
+ for (let ii = cleanups.length - 1; ii >= 0; ii--) {
106
+ try {
107
+ cleanups[ii]();
108
+ } catch (e) {
109
+ error = hasError ? createSuppressedError(e, error) : e;
110
+ hasError = true;
111
+ }
112
+ }
113
+ cleanups.length = 0;
114
+ if (hasError) {
115
+ throw error;
116
+ }
117
+ }
118
+ };
119
+ }
120
+ function createSuppressedError(error, suppressed) {
121
+ const message = "An error was suppressed during disposal";
122
+ if (typeof globalThis.SuppressedError === "function") {
123
+ return new globalThis.SuppressedError(error, suppressed, message);
124
+ }
125
+ return Object.defineProperties(new Error(message), {
126
+ error: { configurable: true, value: error, writable: true },
127
+ name: { configurable: true, value: "SuppressedError", writable: true },
128
+ suppressed: { configurable: true, value: suppressed, writable: true }
129
+ });
130
+ }
82
131
 
83
132
  export { createClient, extendClient, withCleanup };
84
133
  //# sourceMappingURL=index.browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.browser.mjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Later, when the client is no longer needed:\n * using client = createClient().use(myPlugin();\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = new DisposableStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack[Symbol.dispose]();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.browser.mjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one\n * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have\n * not shipped explicit resource management are supported too, though a `using` declaration needs a\n * `Symbol.dispose` polyfill there.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Build the client in the scope that should own it:\n * using client = createClient().use(myPlugin());\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @example Disposing without a using declaration\n * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.\n * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the\n * polyfill before any client is created, since a client registers its dispose method under\n * whatever `Symbol.dispose` was at the time.\n * ```ts\n * const client = createClient().use(myPlugin());\n *\n * // Later, when the client is no longer needed:\n * client[Symbol.dispose]();\n * // `socket.close()` has now been called.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform\n * availability of `DisposableStack`.\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = createCleanupStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack.dispose();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n\n/**\n * The slice of `DisposableStack` that {@link withCleanup} relies on.\n *\n * Deliberately narrow — `defer()` and the plain `dispose()` method only — so that it can be\n * satisfied both by the platform's `DisposableStack` and by {@link createFallbackCleanupStack} on\n * runtimes that lack one. Note that `dispose()` is used in preference to `[Symbol.dispose]()`\n * because runtimes missing `DisposableStack` are missing `Symbol.dispose` as well.\n */\ntype CleanupStack = {\n defer(cleanup: () => void): void;\n dispose(): void;\n};\n\nfunction createCleanupStack(): CleanupStack {\n // Always prefer the runtime's own implementation. The fallback exists only for runtimes that\n // have not shipped explicit resource management — most notably Safari, which as of Safari 27\n // provides neither `DisposableStack` nor `Symbol.dispose`.\n return typeof globalThis.DisposableStack === 'function'\n ? new globalThis.DisposableStack()\n : createFallbackCleanupStack();\n}\n\nfunction createFallbackCleanupStack(): CleanupStack {\n const cleanups: (() => void)[] = [];\n let disposed = false;\n return {\n defer(cleanup) {\n if (disposed) {\n // Mirrors the `ReferenceError` thrown by `DisposableStack.prototype.defer` so that\n // both stacks refuse a late cleanup the same way, rather than silently accepting\n // one that will never run.\n throw new ReferenceError('Cannot add values to a disposed stack');\n }\n if (typeof cleanup !== 'function') {\n // `DisposableStack.prototype.defer` rejects a non-callable up front. Deferring that\n // failure to disposal would surface it far from its cause, and would tangle it up in\n // the error aggregation of unrelated cleanups.\n throw new TypeError(`${String(cleanup)} is not a function`);\n }\n cleanups.push(cleanup);\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n let error: unknown;\n let hasError = false;\n // Cleanups run in reverse order of registration, and one that throws must not stop the\n // rest from running; their errors are aggregated into a `SuppressedError` chain instead.\n for (let ii = cleanups.length - 1; ii >= 0; ii--) {\n try {\n cleanups[ii]();\n } catch (e) {\n error = hasError ? createSuppressedError(e, error) : e;\n hasError = true;\n }\n }\n cleanups.length = 0;\n if (hasError) {\n throw error;\n }\n },\n };\n}\n\nfunction createSuppressedError(error: unknown, suppressed: unknown): Error {\n // The message `DisposableStack` disposal produces. Passing it explicitly matters: the two-argument\n // form of `SuppressedError` leaves the message empty, which would make an aggregated error read\n // differently depending on which stack produced it.\n const message = 'An error was suppressed during disposal';\n if (typeof globalThis.SuppressedError === 'function') {\n return new globalThis.SuppressedError(error, suppressed, message);\n }\n // A runtime without `DisposableStack` has no `SuppressedError` constructor either, so reproduce\n // its shape on a plain error — including the non-enumerability of every property, so that\n // serializing or spreading the error does not depend on which runtime built it.\n return Object.defineProperties(new Error(message), {\n error: { configurable: true, value: error, writable: true },\n name: { configurable: true, value: 'SuppressedError', writable: true },\n suppressed: { configurable: true, value: suppressed, writable: true },\n });\n}\n"]}
@@ -65,7 +65,7 @@ function addCleanupToClientWithExistingStack(client, cleanup) {
65
65
  return client;
66
66
  }
67
67
  function addCleanupToClientWithoutExistingStack(client, cleanup) {
68
- const stack = new DisposableStack();
68
+ const stack = createCleanupStack();
69
69
  if (Symbol.dispose in client) {
70
70
  const existingDispose = client[Symbol.dispose];
71
71
  stack.defer(() => existingDispose.call(client));
@@ -74,11 +74,60 @@ function addCleanupToClientWithoutExistingStack(client, cleanup) {
74
74
  const additions = {
75
75
  [DISPOSABLE_STACK_PROPERTY]: stack,
76
76
  [Symbol.dispose]() {
77
- stack[Symbol.dispose]();
77
+ stack.dispose();
78
78
  }
79
79
  };
80
80
  return extendClient(client, additions);
81
81
  }
82
+ function createCleanupStack() {
83
+ return typeof globalThis.DisposableStack === "function" ? new globalThis.DisposableStack() : createFallbackCleanupStack();
84
+ }
85
+ function createFallbackCleanupStack() {
86
+ const cleanups = [];
87
+ let disposed = false;
88
+ return {
89
+ defer(cleanup) {
90
+ if (disposed) {
91
+ throw new ReferenceError("Cannot add values to a disposed stack");
92
+ }
93
+ if (typeof cleanup !== "function") {
94
+ throw new TypeError(`${String(cleanup)} is not a function`);
95
+ }
96
+ cleanups.push(cleanup);
97
+ },
98
+ dispose() {
99
+ if (disposed) {
100
+ return;
101
+ }
102
+ disposed = true;
103
+ let error;
104
+ let hasError = false;
105
+ for (let ii = cleanups.length - 1; ii >= 0; ii--) {
106
+ try {
107
+ cleanups[ii]();
108
+ } catch (e) {
109
+ error = hasError ? createSuppressedError(e, error) : e;
110
+ hasError = true;
111
+ }
112
+ }
113
+ cleanups.length = 0;
114
+ if (hasError) {
115
+ throw error;
116
+ }
117
+ }
118
+ };
119
+ }
120
+ function createSuppressedError(error, suppressed) {
121
+ const message = "An error was suppressed during disposal";
122
+ if (typeof globalThis.SuppressedError === "function") {
123
+ return new globalThis.SuppressedError(error, suppressed, message);
124
+ }
125
+ return Object.defineProperties(new Error(message), {
126
+ error: { configurable: true, value: error, writable: true },
127
+ name: { configurable: true, value: "SuppressedError", writable: true },
128
+ suppressed: { configurable: true, value: suppressed, writable: true }
129
+ });
130
+ }
82
131
 
83
132
  export { createClient, extendClient, withCleanup };
84
133
  //# sourceMappingURL=index.native.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.native.mjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Later, when the client is no longer needed:\n * using client = createClient().use(myPlugin();\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = new DisposableStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack[Symbol.dispose]();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.native.mjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one\n * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have\n * not shipped explicit resource management are supported too, though a `using` declaration needs a\n * `Symbol.dispose` polyfill there.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Build the client in the scope that should own it:\n * using client = createClient().use(myPlugin());\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @example Disposing without a using declaration\n * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.\n * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the\n * polyfill before any client is created, since a client registers its dispose method under\n * whatever `Symbol.dispose` was at the time.\n * ```ts\n * const client = createClient().use(myPlugin());\n *\n * // Later, when the client is no longer needed:\n * client[Symbol.dispose]();\n * // `socket.close()` has now been called.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform\n * availability of `DisposableStack`.\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = createCleanupStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack.dispose();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n\n/**\n * The slice of `DisposableStack` that {@link withCleanup} relies on.\n *\n * Deliberately narrow — `defer()` and the plain `dispose()` method only — so that it can be\n * satisfied both by the platform's `DisposableStack` and by {@link createFallbackCleanupStack} on\n * runtimes that lack one. Note that `dispose()` is used in preference to `[Symbol.dispose]()`\n * because runtimes missing `DisposableStack` are missing `Symbol.dispose` as well.\n */\ntype CleanupStack = {\n defer(cleanup: () => void): void;\n dispose(): void;\n};\n\nfunction createCleanupStack(): CleanupStack {\n // Always prefer the runtime's own implementation. The fallback exists only for runtimes that\n // have not shipped explicit resource management — most notably Safari, which as of Safari 27\n // provides neither `DisposableStack` nor `Symbol.dispose`.\n return typeof globalThis.DisposableStack === 'function'\n ? new globalThis.DisposableStack()\n : createFallbackCleanupStack();\n}\n\nfunction createFallbackCleanupStack(): CleanupStack {\n const cleanups: (() => void)[] = [];\n let disposed = false;\n return {\n defer(cleanup) {\n if (disposed) {\n // Mirrors the `ReferenceError` thrown by `DisposableStack.prototype.defer` so that\n // both stacks refuse a late cleanup the same way, rather than silently accepting\n // one that will never run.\n throw new ReferenceError('Cannot add values to a disposed stack');\n }\n if (typeof cleanup !== 'function') {\n // `DisposableStack.prototype.defer` rejects a non-callable up front. Deferring that\n // failure to disposal would surface it far from its cause, and would tangle it up in\n // the error aggregation of unrelated cleanups.\n throw new TypeError(`${String(cleanup)} is not a function`);\n }\n cleanups.push(cleanup);\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n let error: unknown;\n let hasError = false;\n // Cleanups run in reverse order of registration, and one that throws must not stop the\n // rest from running; their errors are aggregated into a `SuppressedError` chain instead.\n for (let ii = cleanups.length - 1; ii >= 0; ii--) {\n try {\n cleanups[ii]();\n } catch (e) {\n error = hasError ? createSuppressedError(e, error) : e;\n hasError = true;\n }\n }\n cleanups.length = 0;\n if (hasError) {\n throw error;\n }\n },\n };\n}\n\nfunction createSuppressedError(error: unknown, suppressed: unknown): Error {\n // The message `DisposableStack` disposal produces. Passing it explicitly matters: the two-argument\n // form of `SuppressedError` leaves the message empty, which would make an aggregated error read\n // differently depending on which stack produced it.\n const message = 'An error was suppressed during disposal';\n if (typeof globalThis.SuppressedError === 'function') {\n return new globalThis.SuppressedError(error, suppressed, message);\n }\n // A runtime without `DisposableStack` has no `SuppressedError` constructor either, so reproduce\n // its shape on a plain error — including the non-enumerability of every property, so that\n // serializing or spreading the error does not depend on which runtime built it.\n return Object.defineProperties(new Error(message), {\n error: { configurable: true, value: error, writable: true },\n name: { configurable: true, value: 'SuppressedError', writable: true },\n suppressed: { configurable: true, value: suppressed, writable: true },\n });\n}\n"]}
@@ -67,7 +67,7 @@ function addCleanupToClientWithExistingStack(client, cleanup) {
67
67
  return client;
68
68
  }
69
69
  function addCleanupToClientWithoutExistingStack(client, cleanup) {
70
- const stack = new DisposableStack();
70
+ const stack = createCleanupStack();
71
71
  if (Symbol.dispose in client) {
72
72
  const existingDispose = client[Symbol.dispose];
73
73
  stack.defer(() => existingDispose.call(client));
@@ -76,11 +76,60 @@ function addCleanupToClientWithoutExistingStack(client, cleanup) {
76
76
  const additions = {
77
77
  [DISPOSABLE_STACK_PROPERTY]: stack,
78
78
  [Symbol.dispose]() {
79
- stack[Symbol.dispose]();
79
+ stack.dispose();
80
80
  }
81
81
  };
82
82
  return extendClient(client, additions);
83
83
  }
84
+ function createCleanupStack() {
85
+ return typeof globalThis.DisposableStack === "function" ? new globalThis.DisposableStack() : createFallbackCleanupStack();
86
+ }
87
+ function createFallbackCleanupStack() {
88
+ const cleanups = [];
89
+ let disposed = false;
90
+ return {
91
+ defer(cleanup) {
92
+ if (disposed) {
93
+ throw new ReferenceError("Cannot add values to a disposed stack");
94
+ }
95
+ if (typeof cleanup !== "function") {
96
+ throw new TypeError(`${String(cleanup)} is not a function`);
97
+ }
98
+ cleanups.push(cleanup);
99
+ },
100
+ dispose() {
101
+ if (disposed) {
102
+ return;
103
+ }
104
+ disposed = true;
105
+ let error;
106
+ let hasError = false;
107
+ for (let ii = cleanups.length - 1; ii >= 0; ii--) {
108
+ try {
109
+ cleanups[ii]();
110
+ } catch (e) {
111
+ error = hasError ? createSuppressedError(e, error) : e;
112
+ hasError = true;
113
+ }
114
+ }
115
+ cleanups.length = 0;
116
+ if (hasError) {
117
+ throw error;
118
+ }
119
+ }
120
+ };
121
+ }
122
+ function createSuppressedError(error, suppressed) {
123
+ const message = "An error was suppressed during disposal";
124
+ if (typeof globalThis.SuppressedError === "function") {
125
+ return new globalThis.SuppressedError(error, suppressed, message);
126
+ }
127
+ return Object.defineProperties(new Error(message), {
128
+ error: { configurable: true, value: error, writable: true },
129
+ name: { configurable: true, value: "SuppressedError", writable: true },
130
+ suppressed: { configurable: true, value: suppressed, writable: true }
131
+ });
132
+ }
84
133
 
85
134
  exports.createClient = createClient;
86
135
  exports.extendClient = extendClient;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.node.cjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Later, when the client is no longer needed:\n * using client = createClient().use(myPlugin();\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = new DisposableStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack[Symbol.dispose]();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.node.cjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one\n * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have\n * not shipped explicit resource management are supported too, though a `using` declaration needs a\n * `Symbol.dispose` polyfill there.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Build the client in the scope that should own it:\n * using client = createClient().use(myPlugin());\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @example Disposing without a using declaration\n * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.\n * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the\n * polyfill before any client is created, since a client registers its dispose method under\n * whatever `Symbol.dispose` was at the time.\n * ```ts\n * const client = createClient().use(myPlugin());\n *\n * // Later, when the client is no longer needed:\n * client[Symbol.dispose]();\n * // `socket.close()` has now been called.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform\n * availability of `DisposableStack`.\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = createCleanupStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack.dispose();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n\n/**\n * The slice of `DisposableStack` that {@link withCleanup} relies on.\n *\n * Deliberately narrow — `defer()` and the plain `dispose()` method only — so that it can be\n * satisfied both by the platform's `DisposableStack` and by {@link createFallbackCleanupStack} on\n * runtimes that lack one. Note that `dispose()` is used in preference to `[Symbol.dispose]()`\n * because runtimes missing `DisposableStack` are missing `Symbol.dispose` as well.\n */\ntype CleanupStack = {\n defer(cleanup: () => void): void;\n dispose(): void;\n};\n\nfunction createCleanupStack(): CleanupStack {\n // Always prefer the runtime's own implementation. The fallback exists only for runtimes that\n // have not shipped explicit resource management — most notably Safari, which as of Safari 27\n // provides neither `DisposableStack` nor `Symbol.dispose`.\n return typeof globalThis.DisposableStack === 'function'\n ? new globalThis.DisposableStack()\n : createFallbackCleanupStack();\n}\n\nfunction createFallbackCleanupStack(): CleanupStack {\n const cleanups: (() => void)[] = [];\n let disposed = false;\n return {\n defer(cleanup) {\n if (disposed) {\n // Mirrors the `ReferenceError` thrown by `DisposableStack.prototype.defer` so that\n // both stacks refuse a late cleanup the same way, rather than silently accepting\n // one that will never run.\n throw new ReferenceError('Cannot add values to a disposed stack');\n }\n if (typeof cleanup !== 'function') {\n // `DisposableStack.prototype.defer` rejects a non-callable up front. Deferring that\n // failure to disposal would surface it far from its cause, and would tangle it up in\n // the error aggregation of unrelated cleanups.\n throw new TypeError(`${String(cleanup)} is not a function`);\n }\n cleanups.push(cleanup);\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n let error: unknown;\n let hasError = false;\n // Cleanups run in reverse order of registration, and one that throws must not stop the\n // rest from running; their errors are aggregated into a `SuppressedError` chain instead.\n for (let ii = cleanups.length - 1; ii >= 0; ii--) {\n try {\n cleanups[ii]();\n } catch (e) {\n error = hasError ? createSuppressedError(e, error) : e;\n hasError = true;\n }\n }\n cleanups.length = 0;\n if (hasError) {\n throw error;\n }\n },\n };\n}\n\nfunction createSuppressedError(error: unknown, suppressed: unknown): Error {\n // The message `DisposableStack` disposal produces. Passing it explicitly matters: the two-argument\n // form of `SuppressedError` leaves the message empty, which would make an aggregated error read\n // differently depending on which stack produced it.\n const message = 'An error was suppressed during disposal';\n if (typeof globalThis.SuppressedError === 'function') {\n return new globalThis.SuppressedError(error, suppressed, message);\n }\n // A runtime without `DisposableStack` has no `SuppressedError` constructor either, so reproduce\n // its shape on a plain error — including the non-enumerability of every property, so that\n // serializing or spreading the error does not depend on which runtime built it.\n return Object.defineProperties(new Error(message), {\n error: { configurable: true, value: error, writable: true },\n name: { configurable: true, value: 'SuppressedError', writable: true },\n suppressed: { configurable: true, value: suppressed, writable: true },\n });\n}\n"]}
@@ -65,7 +65,7 @@ function addCleanupToClientWithExistingStack(client, cleanup) {
65
65
  return client;
66
66
  }
67
67
  function addCleanupToClientWithoutExistingStack(client, cleanup) {
68
- const stack = new DisposableStack();
68
+ const stack = createCleanupStack();
69
69
  if (Symbol.dispose in client) {
70
70
  const existingDispose = client[Symbol.dispose];
71
71
  stack.defer(() => existingDispose.call(client));
@@ -74,11 +74,60 @@ function addCleanupToClientWithoutExistingStack(client, cleanup) {
74
74
  const additions = {
75
75
  [DISPOSABLE_STACK_PROPERTY]: stack,
76
76
  [Symbol.dispose]() {
77
- stack[Symbol.dispose]();
77
+ stack.dispose();
78
78
  }
79
79
  };
80
80
  return extendClient(client, additions);
81
81
  }
82
+ function createCleanupStack() {
83
+ return typeof globalThis.DisposableStack === "function" ? new globalThis.DisposableStack() : createFallbackCleanupStack();
84
+ }
85
+ function createFallbackCleanupStack() {
86
+ const cleanups = [];
87
+ let disposed = false;
88
+ return {
89
+ defer(cleanup) {
90
+ if (disposed) {
91
+ throw new ReferenceError("Cannot add values to a disposed stack");
92
+ }
93
+ if (typeof cleanup !== "function") {
94
+ throw new TypeError(`${String(cleanup)} is not a function`);
95
+ }
96
+ cleanups.push(cleanup);
97
+ },
98
+ dispose() {
99
+ if (disposed) {
100
+ return;
101
+ }
102
+ disposed = true;
103
+ let error;
104
+ let hasError = false;
105
+ for (let ii = cleanups.length - 1; ii >= 0; ii--) {
106
+ try {
107
+ cleanups[ii]();
108
+ } catch (e) {
109
+ error = hasError ? createSuppressedError(e, error) : e;
110
+ hasError = true;
111
+ }
112
+ }
113
+ cleanups.length = 0;
114
+ if (hasError) {
115
+ throw error;
116
+ }
117
+ }
118
+ };
119
+ }
120
+ function createSuppressedError(error, suppressed) {
121
+ const message = "An error was suppressed during disposal";
122
+ if (typeof globalThis.SuppressedError === "function") {
123
+ return new globalThis.SuppressedError(error, suppressed, message);
124
+ }
125
+ return Object.defineProperties(new Error(message), {
126
+ error: { configurable: true, value: error, writable: true },
127
+ name: { configurable: true, value: "SuppressedError", writable: true },
128
+ suppressed: { configurable: true, value: suppressed, writable: true }
129
+ });
130
+ }
82
131
 
83
132
  export { createClient, extendClient, withCleanup };
84
133
  //# sourceMappingURL=index.node.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.node.mjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Later, when the client is no longer needed:\n * using client = createClient().use(myPlugin();\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = new DisposableStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack[Symbol.dispose]();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.node.mjs","sourcesContent":["/**\n * Defines a plugin that transforms or extends a client with additional functionality.\n *\n * For instance, plugins may add RPC capabilities, wallet integration, transaction building,\n * or other features necessary for interacting with the Solana blockchain.\n *\n * Plugins are functions that take a client object as input and return a new client object\n * or a promise that resolves to a new client object. This allows for both synchronous\n * and asynchronous transformations and extensions of the client.\n *\n * Plugins are usually applied using the `use` method on a {@link Client} or {@link AsyncClient}\n * instance, which {@link createClient} provides as a starting point.\n *\n * @typeParam TInput - The input client object type that this plugin accepts.\n * @typeParam TOutput - The output type. Either a new client object or a promise resolving to one.\n *\n * @example Basic RPC plugin\n * Given an RPC endpoint, this plugin adds an `rpc` property to the client.\n *\n * ```ts\n * import { createClient, createSolanaRpc } from '@solana/kit';\n *\n * // Define a simple RPC plugin.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n *\n * // Use the plugin.\n * const client = createClient().use(rpcPlugin('https://api.mainnet-beta.solana.com'));\n * await client.rpc.getLatestBlockhash().send();\n * ```\n *\n * @example Async plugin that generates a payer wallet\n * The following plugin shows how to create an asynchronous plugin that generates a new keypair signer.\n *\n * ```ts\n * import { createClient, generateKeypairSigner } from '@solana/kit';\n *\n * // Define a plugin that generates a new keypair signer.\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n *\n * // Use the plugin.\n * const client = await createClient().use(generatedPayerPlugin());\n * console.log(client.payer.address);\n * ```\n *\n * @example Plugins with input requirements\n * A plugin can specify required properties on the input client. The example below requires the\n * client to already have a `payer` signer attached to the client in order to perform an airdrop.\n *\n * ```ts\n * import { createClient, TransactionSigner, Lamports, lamports } from '@solana/kit';\n *\n * // Define a plugin that airdrops lamports to the payer set on the client.\n * function airdropPayerPlugin(lamports: Lamports) {\n * return async <T extends { payer: TransactionSigner }>(client: T) => {\n * await myAirdropFunction(client.payer, lamports);\n * return client;\n * };\n * }\n *\n * // Use the plugins.\n * const client = await createClient()\n * .use(generatedPayerPlugin()) // This is required before using the airdrop plugin.\n * .use(airdropPayerPlugin(lamports(1_000_000_000n)));\n * ```\n *\n * @example Chaining plugins\n * Multiple plugins — asynchronous or not — can be chained together to build up complex clients.\n * The example below demonstrates how to gradually build a client with multiple plugins.\n * Notice how, despite having multiple asynchronous plugins, we only need to `await` the final result.\n * This is because the `use` method on `AsyncClient` returns another `AsyncClient`, allowing for seamless chaining.\n *\n * ```ts\n * import { createClient, createSolanaRpc, createSolanaRpcSubscriptions, generateKeypairSigner } from '@solana/kit';\n *\n * // Define multiple plugins.\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpc(endpoint) });\n * }\n * function rpcSubscriptionsPlugin(endpoint: string) {\n * return <T extends object>(client: T) => ({...client, rpc: createSolanaRpcSubscriptions(endpoint) });\n * }\n * function generatedPayerPlugin() {\n * return async <T extends object>(client: T) => ({...client, payer: await generateKeypairSigner() });\n * }\n * function generatedAuthorityPlugin() {\n * return async <T extends object>(client: T) => ({...client, authority: await generateKeypairSigner() });\n * }\n *\n * // Chain plugins together.\n * const client = await createClient()\n * .use(rpcPlugin('https://api.mainnet-beta.solana.com'))\n * .use(rpcSubscriptionsPlugin('wss://api.mainnet-beta.solana.com'))\n * .use(generatedPayerPlugin())\n * .use(generatedAuthorityPlugin());\n * ```\n */\nexport type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;\n\n/**\n * A client that can be extended with plugins.\n *\n * The `Client` type represents a client object that can be built up through\n * the application of one or more plugins. It provides a `use` method to\n * apply plugins, either synchronously (returning a new `Client`) or\n * asynchronously (returning an {@link AsyncClient}).\n *\n * @typeParam TSelf - The current shape of the client object including all applied plugins.\n */\nexport type Client<TSelf extends object> = TSelf & {\n /**\n * Applies a plugin to extend or transform the client.\n *\n * @param plugin The plugin function to apply to this client.\n * @returns Either a new `Client` (for sync plugins) or {@link AsyncClient} (for async plugins).\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => TOutput extends Promise<infer U> ? AsyncClient<U extends object ? U : never> : Client<TOutput>;\n};\n\n/**\n * An asynchronous wrapper that represents a promise of a client.\n *\n * The `AsyncClient` type is returned when an async plugin is applied to a client.\n * It behaves like a `Promise<Client<TSelf>>` but with an additional `use` method\n * that allows chaining more plugins before the promise resolves.\n *\n * This enables fluent chaining of both synchronous and asynchronous plugins\n * without having to await intermediate promises.\n *\n * @typeParam TSelf - The shape of the client object that this async client will resolve to.\n */\nexport type AsyncClient<TSelf extends object> = Promise<Client<TSelf>> & {\n /**\n * Applies a plugin to the client once it resolves.\n *\n * @param plugin The plugin function to apply to the resolved client.\n * @returns A new `AsyncClient` representing the result of applying the plugin.\n */\n readonly use: <TOutput extends Promise<object> | object>(\n plugin: ClientPlugin<TSelf, TOutput>,\n ) => AsyncClient<TOutput extends Promise<infer U> ? (U extends object ? U : never) : TOutput>;\n};\n\n/**\n * Creates a new empty client that can be extended with plugins.\n *\n * This serves as an entry point for building Solana clients.\n * Start with an empty client and chain the `.use()` method\n * to apply plugins that add various functionalities such as RPC\n * connectivity, wallet integration, transaction building, and more.\n *\n * See {@link ClientPlugin} for detailed examples on creating and using plugins.\n *\n * @returns An empty client object with only the `use` method available.\n *\n * @example Basic client setup\n * ```ts\n * import { createClient } from '@solana/client';\n * import { generatedPayer } from '@solana/kit-plugin-payer';\n * import { rpc } from '@solana/kit-plugin-rpc';\n *\n * const client = await createClient()\n * .use(generatedPayer())\n * .use(rpc('https://api.mainnet-beta.solana.com'));\n * ```\n */\nexport function createClient<TSelf extends object = object>(value?: TSelf): Client<TSelf> {\n return addUse(value ?? ({} as TSelf));\n}\n\nfunction addUse<TSelf extends object>(value: TSelf): Client<TSelf> {\n return Object.freeze(\n Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(value),\n use: {\n configurable: false,\n enumerable: true,\n value: function <TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n const result = plugin(value);\n return result instanceof Promise ? createAsyncClient(result) : addUse(result);\n },\n writable: false,\n },\n },\n ),\n ) as Client<TSelf>;\n}\n\nfunction createAsyncClient<TSelf extends object>(promise: Promise<TSelf>): AsyncClient<TSelf> {\n return Object.freeze({\n catch(onrejected) {\n return promise.then(v => addUse(v)).catch(onrejected);\n },\n finally(onfinally) {\n return promise.then(v => addUse(v)).finally(onfinally);\n },\n then(onfulfilled, onrejected) {\n return promise.then(v => addUse(v)).then(onfulfilled, onrejected);\n },\n use<TOutput extends Promise<object> | object>(plugin: ClientPlugin<TSelf, TOutput>) {\n return createAsyncClient(promise.then(plugin));\n },\n } as AsyncClient<TSelf>);\n}\n\n/**\n * The result of extending a client of type `TClient` with additional properties of type `TAdditions`.\n *\n * Structurally equivalent to `Omit<TClient, keyof TAdditions> & TAdditions` — keys present\n * on both `TClient` and `TAdditions` are replaced by the `TAdditions` version — but expressed\n * as a single homomorphic mapped type so the inferred type displays as a flat object literal\n * rather than a deeply nested chain of `Omit<...>` intersections. Optional (`?`) and `readonly`\n * modifiers from both sides are preserved.\n *\n * Plugin authors who write their own merging helpers can reuse this type to keep the\n * inferred shape of their plugin's output legible in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the client being extended.\n * @typeParam TAdditions - The type of the properties being merged in.\n *\n * @example\n * ```ts\n * function withRpc<TClient extends object>(client: TClient, endpoint: string): ExtendedClient<TClient, { rpc: Rpc }> {\n * return extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link extendClient}\n */\nexport type ExtendedClient<TClient extends object, TAdditions extends object> = {\n [K in keyof (Omit<TClient, keyof TAdditions> & TAdditions)]: (Omit<TClient, keyof TAdditions> & TAdditions)[K];\n} & {};\n\n/**\n * Extends a client object with additional properties, preserving property descriptors\n * (getters, symbol-keyed properties, and non-enumerable properties) from both objects.\n *\n * Use this inside plugins instead of plain object spread (`{...client, ...additions}`)\n * when the client may carry getters or symbol-keyed properties that spread would flatten or lose.\n * When the same key exists on both, `additions` wins.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a single\n * object literal so chained `extendClient` calls do not accumulate nested `Omit<...>` wrappers\n * in editor tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @typeParam TAdditions - The type of the properties being added.\n * @param client - The original client object to extend.\n * @param additions - The properties to add or override on the client.\n * @returns A new object combining both, with `additions` taking precedence on conflicts.\n *\n * @example\n * ```ts\n * function rpcPlugin(endpoint: string) {\n * return <T extends object>(client: T) =>\n * extendClient(client, { rpc: createSolanaRpc(endpoint) });\n * }\n * ```\n *\n * @see {@link ClientPlugin}\n * @see {@link ExtendedClient}\n */\nexport function extendClient<TClient extends object, TAdditions extends object>(\n client: TClient,\n additions: TAdditions,\n): ExtendedClient<TClient, TAdditions> {\n const result = Object.defineProperties({}, toConfigurableDescriptors(Object.getOwnPropertyDescriptors(client)));\n Object.defineProperties(result, Object.getOwnPropertyDescriptors(additions));\n return Object.freeze(result) as ExtendedClient<TClient, TAdditions>;\n}\n\nfunction toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors: T): T {\n const result = {} as Record<string | symbol, PropertyDescriptor>;\n for (const key of Reflect.ownKeys(descriptors)) {\n result[key] = { ...descriptors[key as keyof T], configurable: true };\n }\n return result as T;\n}\n\n/**\n * Wraps a client with a cleanup function, making it {@link Disposable}.\n *\n * Plugin authors can use this to register teardown logic (e.g. closing\n * connections or clearing timers) that runs when the client is disposed.\n * If the client already implements `Symbol.dispose`, the existing dispose\n * logic is chained so that it runs after the new `cleanup` function.\n *\n * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one\n * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have\n * not shipped explicit resource management are supported too, though a `using` declaration needs a\n * `Symbol.dispose` polyfill there.\n *\n * The return type is an {@link ExtendedClient}, which flattens the merged shape into a\n * single object literal so chained calls do not accumulate nested intersections in editor\n * tooltips and error messages.\n *\n * @typeParam TClient - The type of the original client.\n * @param client - The client to wrap.\n * @param cleanup - The cleanup function to run when the client is disposed.\n * @return A new client that extends `TClient` and implements `Disposable`.\n *\n * @example\n * Register a cleanup function in a plugin that opens a WebSocket connection.\n * ```ts\n * function myPlugin() {\n * return <T extends object>(client: T) => {\n * const socket = new WebSocket('wss://api.example.com');\n * return withCleanup(\n * extendClient(client, { socket }),\n * () => socket.close(),\n * );\n * };\n * }\n *\n * // Build the client in the scope that should own it:\n * using client = createClient().use(myPlugin());\n * // `socket.close()` is called automatically when `client` goes out of scope.\n * ```\n *\n * @example Disposing without a using declaration\n * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.\n * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the\n * polyfill before any client is created, since a client registers its dispose method under\n * whatever `Symbol.dispose` was at the time.\n * ```ts\n * const client = createClient().use(myPlugin());\n *\n * // Later, when the client is no longer needed:\n * client[Symbol.dispose]();\n * // `socket.close()` has now been called.\n * ```\n *\n * @see {@link extendClient}\n * @see {@link ExtendedClient}\n * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform\n * availability of `DisposableStack`.\n */\nexport function withCleanup<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n if (DISPOSABLE_STACK_PROPERTY in client) {\n return addCleanupToClientWithExistingStack(\n client as Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack> & TClient,\n cleanup,\n );\n } else {\n return addCleanupToClientWithoutExistingStack(client, cleanup);\n }\n}\n\nconst DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;\n\nfunction addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack>>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n // If we already have the stack, add the new cleanup to it\n client[DISPOSABLE_STACK_PROPERTY].defer(cleanup);\n // We assume we already added a dispose method when we added the stack\n return client as unknown as ExtendedClient<TClient, Disposable>;\n}\n\nfunction addCleanupToClientWithoutExistingStack<TClient extends object>(\n client: TClient,\n cleanup: () => void,\n): ExtendedClient<TClient, Disposable> {\n const stack = createCleanupStack();\n\n // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack\n if (Symbol.dispose in client) {\n const existingDispose = (client as Disposable)[Symbol.dispose];\n stack.defer(() => existingDispose.call(client));\n }\n\n // Add the new cleanup to the stack\n stack.defer(cleanup);\n\n // We add our stack to the client, and replace any existing dispose method with our stack dispose\n const additions = {\n [DISPOSABLE_STACK_PROPERTY]: stack,\n [Symbol.dispose]() {\n stack.dispose();\n },\n };\n\n return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;\n}\n\n/**\n * The slice of `DisposableStack` that {@link withCleanup} relies on.\n *\n * Deliberately narrow — `defer()` and the plain `dispose()` method only — so that it can be\n * satisfied both by the platform's `DisposableStack` and by {@link createFallbackCleanupStack} on\n * runtimes that lack one. Note that `dispose()` is used in preference to `[Symbol.dispose]()`\n * because runtimes missing `DisposableStack` are missing `Symbol.dispose` as well.\n */\ntype CleanupStack = {\n defer(cleanup: () => void): void;\n dispose(): void;\n};\n\nfunction createCleanupStack(): CleanupStack {\n // Always prefer the runtime's own implementation. The fallback exists only for runtimes that\n // have not shipped explicit resource management — most notably Safari, which as of Safari 27\n // provides neither `DisposableStack` nor `Symbol.dispose`.\n return typeof globalThis.DisposableStack === 'function'\n ? new globalThis.DisposableStack()\n : createFallbackCleanupStack();\n}\n\nfunction createFallbackCleanupStack(): CleanupStack {\n const cleanups: (() => void)[] = [];\n let disposed = false;\n return {\n defer(cleanup) {\n if (disposed) {\n // Mirrors the `ReferenceError` thrown by `DisposableStack.prototype.defer` so that\n // both stacks refuse a late cleanup the same way, rather than silently accepting\n // one that will never run.\n throw new ReferenceError('Cannot add values to a disposed stack');\n }\n if (typeof cleanup !== 'function') {\n // `DisposableStack.prototype.defer` rejects a non-callable up front. Deferring that\n // failure to disposal would surface it far from its cause, and would tangle it up in\n // the error aggregation of unrelated cleanups.\n throw new TypeError(`${String(cleanup)} is not a function`);\n }\n cleanups.push(cleanup);\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n let error: unknown;\n let hasError = false;\n // Cleanups run in reverse order of registration, and one that throws must not stop the\n // rest from running; their errors are aggregated into a `SuppressedError` chain instead.\n for (let ii = cleanups.length - 1; ii >= 0; ii--) {\n try {\n cleanups[ii]();\n } catch (e) {\n error = hasError ? createSuppressedError(e, error) : e;\n hasError = true;\n }\n }\n cleanups.length = 0;\n if (hasError) {\n throw error;\n }\n },\n };\n}\n\nfunction createSuppressedError(error: unknown, suppressed: unknown): Error {\n // The message `DisposableStack` disposal produces. Passing it explicitly matters: the two-argument\n // form of `SuppressedError` leaves the message empty, which would make an aggregated error read\n // differently depending on which stack produced it.\n const message = 'An error was suppressed during disposal';\n if (typeof globalThis.SuppressedError === 'function') {\n return new globalThis.SuppressedError(error, suppressed, message);\n }\n // A runtime without `DisposableStack` has no `SuppressedError` constructor either, so reproduce\n // its shape on a plain error — including the non-enumerability of every property, so that\n // serializing or spreading the error does not depend on which runtime built it.\n return Object.defineProperties(new Error(message), {\n error: { configurable: true, value: error, writable: true },\n name: { configurable: true, value: 'SuppressedError', writable: true },\n suppressed: { configurable: true, value: suppressed, writable: true },\n });\n}\n"]}
@@ -228,6 +228,11 @@ export declare function extendClient<TClient extends object, TAdditions extends
228
228
  * If the client already implements `Symbol.dispose`, the existing dispose
229
229
  * logic is chained so that it runs after the new `cleanup` function.
230
230
  *
231
+ * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one
232
+ * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have
233
+ * not shipped explicit resource management are supported too, though a `using` declaration needs a
234
+ * `Symbol.dispose` polyfill there.
235
+ *
231
236
  * The return type is an {@link ExtendedClient}, which flattens the merged shape into a
232
237
  * single object literal so chained calls do not accumulate nested intersections in editor
233
238
  * tooltips and error messages.
@@ -250,13 +255,28 @@ export declare function extendClient<TClient extends object, TAdditions extends
250
255
  * };
251
256
  * }
252
257
  *
253
- * // Later, when the client is no longer needed:
254
- * using client = createClient().use(myPlugin();
258
+ * // Build the client in the scope that should own it:
259
+ * using client = createClient().use(myPlugin());
255
260
  * // `socket.close()` is called automatically when `client` goes out of scope.
256
261
  * ```
257
262
  *
263
+ * @example Disposing without a using declaration
264
+ * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.
265
+ * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the
266
+ * polyfill before any client is created, since a client registers its dispose method under
267
+ * whatever `Symbol.dispose` was at the time.
268
+ * ```ts
269
+ * const client = createClient().use(myPlugin());
270
+ *
271
+ * // Later, when the client is no longer needed:
272
+ * client[Symbol.dispose]();
273
+ * // `socket.close()` has now been called.
274
+ * ```
275
+ *
258
276
  * @see {@link extendClient}
259
277
  * @see {@link ExtendedClient}
278
+ * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform
279
+ * availability of `DisposableStack`.
260
280
  */
261
281
  export declare function withCleanup<TClient extends object>(client: TClient, cleanup: () => void): ExtendedClient<TClient, Disposable>;
262
282
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,MAAM,MAAM,YAAY,CAAC,MAAM,SAAS,MAAM,EAAE,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvH;;;;;;;;;GASG;AACH,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,IAAI,KAAK,GAAG;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACnD,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,KACnC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;CACvG,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG;IACrE;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACnD,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,KACnC,WAAW,CAAC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;CACjG,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAExF;AAuCD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,cAAc,CAAC,OAAO,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,IAAI;KAC3E,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;CACjH,GAAG,EAAE,CAAC;AAEP;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,EAC1E,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,UAAU,GACtB,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAIrC;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAC9C,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,IAAI,GACpB,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CASrC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,MAAM,MAAM,YAAY,CAAC,MAAM,SAAS,MAAM,EAAE,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvH;;;;;;;;;GASG;AACH,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,IAAI,KAAK,GAAG;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACnD,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,KACnC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;CACvG,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG;IACrE;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACnD,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,KACnC,WAAW,CAAC,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;CACjG,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAExF;AAuCD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,cAAc,CAAC,OAAO,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,IAAI;KAC3E,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;CACjH,GAAG,EAAE,CAAC;AAEP;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,EAC1E,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,UAAU,GACtB,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAIrC;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAC9C,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,IAAI,GACpB,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CASrC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/plugin-core",
3
- "version": "7.0.0",
3
+ "version": "7.1.0-canary-20260812160611",
4
4
  "description": "Core helpers for creating and extending Kit clients with plugins",
5
5
  "homepage": "https://www.solanakit.com/api#solanaplugin-core",
6
6
  "exports": {
package/src/client.ts CHANGED
@@ -292,6 +292,11 @@ function toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors:
292
292
  * If the client already implements `Symbol.dispose`, the existing dispose
293
293
  * logic is chained so that it runs after the new `cleanup` function.
294
294
  *
295
+ * Cleanups run in reverse order of registration, disposal is idempotent, and if more than one
296
+ * cleanup throws then the errors are aggregated into a `SuppressedError` chain. Runtimes that have
297
+ * not shipped explicit resource management are supported too, though a `using` declaration needs a
298
+ * `Symbol.dispose` polyfill there.
299
+ *
295
300
  * The return type is an {@link ExtendedClient}, which flattens the merged shape into a
296
301
  * single object literal so chained calls do not accumulate nested intersections in editor
297
302
  * tooltips and error messages.
@@ -314,13 +319,28 @@ function toConfigurableDescriptors<T extends PropertyDescriptorMap>(descriptors:
314
319
  * };
315
320
  * }
316
321
  *
317
- * // Later, when the client is no longer needed:
318
- * using client = createClient().use(myPlugin();
322
+ * // Build the client in the scope that should own it:
323
+ * using client = createClient().use(myPlugin());
319
324
  * // `socket.close()` is called automatically when `client` goes out of scope.
320
325
  * ```
321
326
  *
327
+ * @example Disposing without a using declaration
328
+ * `using` requires explicit resource management, which Safari has not shipped as of Safari 27.
329
+ * Either dispose the client yourself, as below, or polyfill `Symbol.dispose` — installing the
330
+ * polyfill before any client is created, since a client registers its dispose method under
331
+ * whatever `Symbol.dispose` was at the time.
332
+ * ```ts
333
+ * const client = createClient().use(myPlugin());
334
+ *
335
+ * // Later, when the client is no longer needed:
336
+ * client[Symbol.dispose]();
337
+ * // `socket.close()` has now been called.
338
+ * ```
339
+ *
322
340
  * @see {@link extendClient}
323
341
  * @see {@link ExtendedClient}
342
+ * @remarks See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform
343
+ * availability of `DisposableStack`.
324
344
  */
325
345
  export function withCleanup<TClient extends object>(
326
346
  client: TClient,
@@ -328,7 +348,7 @@ export function withCleanup<TClient extends object>(
328
348
  ): ExtendedClient<TClient, Disposable> {
329
349
  if (DISPOSABLE_STACK_PROPERTY in client) {
330
350
  return addCleanupToClientWithExistingStack(
331
- client as Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack> & TClient,
351
+ client as Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack> & TClient,
332
352
  cleanup,
333
353
  );
334
354
  } else {
@@ -338,7 +358,7 @@ export function withCleanup<TClient extends object>(
338
358
 
339
359
  const DISPOSABLE_STACK_PROPERTY = '__PRIVATE__DISPOSABLE_STACK' as const;
340
360
 
341
- function addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, DisposableStack>>(
361
+ function addCleanupToClientWithExistingStack<TClient extends Record<typeof DISPOSABLE_STACK_PROPERTY, CleanupStack>>(
342
362
  client: TClient,
343
363
  cleanup: () => void,
344
364
  ): ExtendedClient<TClient, Disposable> {
@@ -352,7 +372,7 @@ function addCleanupToClientWithoutExistingStack<TClient extends object>(
352
372
  client: TClient,
353
373
  cleanup: () => void,
354
374
  ): ExtendedClient<TClient, Disposable> {
355
- const stack = new DisposableStack();
375
+ const stack = createCleanupStack();
356
376
 
357
377
  // If the client has an existing dispose method but not our stack, we maintain this existing cleanup by deferring it to the new stack
358
378
  if (Symbol.dispose in client) {
@@ -367,9 +387,93 @@ function addCleanupToClientWithoutExistingStack<TClient extends object>(
367
387
  const additions = {
368
388
  [DISPOSABLE_STACK_PROPERTY]: stack,
369
389
  [Symbol.dispose]() {
370
- stack[Symbol.dispose]();
390
+ stack.dispose();
371
391
  },
372
392
  };
373
393
 
374
394
  return extendClient(client, additions) as unknown as ExtendedClient<TClient, Disposable>;
375
395
  }
396
+
397
+ /**
398
+ * The slice of `DisposableStack` that {@link withCleanup} relies on.
399
+ *
400
+ * Deliberately narrow — `defer()` and the plain `dispose()` method only — so that it can be
401
+ * satisfied both by the platform's `DisposableStack` and by {@link createFallbackCleanupStack} on
402
+ * runtimes that lack one. Note that `dispose()` is used in preference to `[Symbol.dispose]()`
403
+ * because runtimes missing `DisposableStack` are missing `Symbol.dispose` as well.
404
+ */
405
+ type CleanupStack = {
406
+ defer(cleanup: () => void): void;
407
+ dispose(): void;
408
+ };
409
+
410
+ function createCleanupStack(): CleanupStack {
411
+ // Always prefer the runtime's own implementation. The fallback exists only for runtimes that
412
+ // have not shipped explicit resource management — most notably Safari, which as of Safari 27
413
+ // provides neither `DisposableStack` nor `Symbol.dispose`.
414
+ return typeof globalThis.DisposableStack === 'function'
415
+ ? new globalThis.DisposableStack()
416
+ : createFallbackCleanupStack();
417
+ }
418
+
419
+ function createFallbackCleanupStack(): CleanupStack {
420
+ const cleanups: (() => void)[] = [];
421
+ let disposed = false;
422
+ return {
423
+ defer(cleanup) {
424
+ if (disposed) {
425
+ // Mirrors the `ReferenceError` thrown by `DisposableStack.prototype.defer` so that
426
+ // both stacks refuse a late cleanup the same way, rather than silently accepting
427
+ // one that will never run.
428
+ throw new ReferenceError('Cannot add values to a disposed stack');
429
+ }
430
+ if (typeof cleanup !== 'function') {
431
+ // `DisposableStack.prototype.defer` rejects a non-callable up front. Deferring that
432
+ // failure to disposal would surface it far from its cause, and would tangle it up in
433
+ // the error aggregation of unrelated cleanups.
434
+ throw new TypeError(`${String(cleanup)} is not a function`);
435
+ }
436
+ cleanups.push(cleanup);
437
+ },
438
+ dispose() {
439
+ if (disposed) {
440
+ return;
441
+ }
442
+ disposed = true;
443
+ let error: unknown;
444
+ let hasError = false;
445
+ // Cleanups run in reverse order of registration, and one that throws must not stop the
446
+ // rest from running; their errors are aggregated into a `SuppressedError` chain instead.
447
+ for (let ii = cleanups.length - 1; ii >= 0; ii--) {
448
+ try {
449
+ cleanups[ii]();
450
+ } catch (e) {
451
+ error = hasError ? createSuppressedError(e, error) : e;
452
+ hasError = true;
453
+ }
454
+ }
455
+ cleanups.length = 0;
456
+ if (hasError) {
457
+ throw error;
458
+ }
459
+ },
460
+ };
461
+ }
462
+
463
+ function createSuppressedError(error: unknown, suppressed: unknown): Error {
464
+ // The message `DisposableStack` disposal produces. Passing it explicitly matters: the two-argument
465
+ // form of `SuppressedError` leaves the message empty, which would make an aggregated error read
466
+ // differently depending on which stack produced it.
467
+ const message = 'An error was suppressed during disposal';
468
+ if (typeof globalThis.SuppressedError === 'function') {
469
+ return new globalThis.SuppressedError(error, suppressed, message);
470
+ }
471
+ // A runtime without `DisposableStack` has no `SuppressedError` constructor either, so reproduce
472
+ // its shape on a plain error — including the non-enumerability of every property, so that
473
+ // serializing or spreading the error does not depend on which runtime built it.
474
+ return Object.defineProperties(new Error(message), {
475
+ error: { configurable: true, value: error, writable: true },
476
+ name: { configurable: true, value: 'SuppressedError', writable: true },
477
+ suppressed: { configurable: true, value: suppressed, writable: true },
478
+ });
479
+ }