@parity/product-sdk-chain-client 0.2.1 → 0.4.0

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.
package/dist/index.d.ts CHANGED
@@ -2,8 +2,11 @@ import { ChainDefinition, TypedApi, PolkadotClient } from 'polkadot-api';
2
2
  import { polkadot_asset_hub } from '@parity/product-sdk-descriptors/polkadot-asset-hub';
3
3
  import { kusama_asset_hub } from '@parity/product-sdk-descriptors/kusama-asset-hub';
4
4
  import { paseo_asset_hub } from '@parity/product-sdk-descriptors/paseo-asset-hub';
5
- import { bulletin } from '@parity/product-sdk-descriptors/bulletin';
6
- import { individuality } from '@parity/product-sdk-descriptors/individuality';
5
+ import { previewnet_asset_hub } from '@parity/product-sdk-descriptors/previewnet-asset-hub';
6
+ import { paseo_bulletin } from '@parity/product-sdk-descriptors/paseo-bulletin';
7
+ import { previewnet_bulletin } from '@parity/product-sdk-descriptors/previewnet-bulletin';
8
+ import { paseo_individuality } from '@parity/product-sdk-descriptors/paseo-individuality';
9
+ import { previewnet_individuality } from '@parity/product-sdk-descriptors/previewnet-individuality';
7
10
  export { isInsideContainer, isInsideContainerSync } from '@parity/product-sdk-host';
8
11
 
9
12
  /**
@@ -20,14 +23,14 @@ export { isInsideContainer, isInsideContainerSync } from '@parity/product-sdk-ho
20
23
  * @example
21
24
  * ```ts
22
25
  * import { createChainClient } from "@parity/product-sdk-chain-client";
23
- * import { paseo_asset_hub } from "./descriptors/paseo-asset-hub";
24
- * import { bulletin } from "./descriptors/bulletin";
26
+ * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
27
+ * import { paseo_bulletin } from "@parity/product-sdk-descriptors/paseo-bulletin";
25
28
  *
26
29
  * const client = await createChainClient({
27
- * chains: { assetHub: paseo_asset_hub, bulletin },
30
+ * chains: { assetHub: paseo_asset_hub, bulletin: paseo_bulletin },
28
31
  * rpcs: {
29
- * assetHub: ["wss://sys.ibp.network/asset-hub-paseo"],
30
- * bulletin: ["wss://paseo-bulletin-rpc.polkadot.io"],
32
+ * assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"],
33
+ * bulletin: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
31
34
  * },
32
35
  * });
33
36
  * ```
@@ -45,7 +48,7 @@ interface ChainClientConfig<TChains extends Record<string, ChainDefinition> = Re
45
48
  *
46
49
  * Each key from your config maps to a fully-typed PAPI {@link TypedApi}.
47
50
  * Access raw `PolkadotClient` instances via `.raw` for advanced use cases
48
- * like creating an `InkSdk` for contract interactions.
51
+ * like creating a `ContractRuntime` for pallet-revive contract interactions.
49
52
  *
50
53
  * @typeParam TChains - The chain descriptor record used to create this client.
51
54
  *
@@ -54,15 +57,15 @@ interface ChainClientConfig<TChains extends Record<string, ChainDefinition> = Re
54
57
  * // Typed API access — fully typed from your descriptors
55
58
  * const account = await client.assetHub.query.System.Account.getValue(addr);
56
59
  *
57
- * // Raw client for advanced use (e.g., InkSdk for contracts)
58
- * import { createInkSdk } from "@polkadot-api/sdk-ink";
59
- * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
60
+ * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)
61
+ * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
62
+ * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
60
63
  * ```
61
64
  */
62
65
  type ChainClient<TChains extends Record<string, ChainDefinition>> = {
63
66
  [K in string & keyof TChains]: TypedApi<TChains[K]>;
64
67
  } & {
65
- /** Raw `PolkadotClient` instances, keyed by chain name. Use for advanced APIs like `createInkSdk`. */
68
+ /** Raw `PolkadotClient` instances, keyed by chain name. Use for advanced APIs like `createContractRuntime`. */
66
69
  raw: {
67
70
  [K in string & keyof TChains]: PolkadotClient;
68
71
  };
@@ -79,8 +82,12 @@ interface ChainEntry {
79
82
  * Create a multi-chain client with user-provided descriptors and RPC endpoints.
80
83
  *
81
84
  * Returns fully-typed APIs for each chain plus raw `PolkadotClient` access via `.raw`.
82
- * Connections use host routing (via `@parity/product-sdk-host`) when inside a container,
83
- * falling back to direct WebSocket RPC.
85
+ * Connections route through the host provider (`@parity/product-sdk-host`) the SDK
86
+ * is designed to run exclusively inside a host container (Polkadot Browser / Desktop).
87
+ * Throws if no host provider is available; there is no direct-WebSocket fallback.
88
+ *
89
+ * The `config.rpcs` field is currently unused at runtime (kept for API compatibility
90
+ * and BYOD documentation), since the host owns the chain connection.
84
91
  *
85
92
  * Results are cached by genesis-hash fingerprint — calling with the same descriptors
86
93
  * returns the same instance.
@@ -88,14 +95,14 @@ interface ChainEntry {
88
95
  * @example
89
96
  * ```ts
90
97
  * import { createChainClient } from "@parity/product-sdk-chain-client";
91
- * import { paseo_asset_hub } from "./descriptors/paseo-asset-hub";
92
- * import { bulletin } from "./descriptors/bulletin";
98
+ * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
99
+ * import { paseo_bulletin } from "@parity/product-sdk-descriptors/paseo-bulletin";
93
100
  *
94
101
  * const client = await createChainClient({
95
- * chains: { assetHub: paseo_asset_hub, bulletin },
102
+ * chains: { assetHub: paseo_asset_hub, bulletin: paseo_bulletin },
96
103
  * rpcs: {
97
- * assetHub: ["wss://sys.ibp.network/asset-hub-paseo"],
98
- * bulletin: ["wss://paseo-bulletin-rpc.polkadot.io"],
104
+ * assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"],
105
+ * bulletin: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
99
106
  * },
100
107
  * });
101
108
  *
@@ -103,9 +110,9 @@ interface ChainEntry {
103
110
  * const account = await client.assetHub.query.System.Account.getValue(addr);
104
111
  * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();
105
112
  *
106
- * // Raw client for advanced use (e.g., InkSdk for contracts)
107
- * import { createInkSdk } from "@polkadot-api/sdk-ink";
108
- * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
113
+ * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)
114
+ * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
115
+ * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
109
116
  *
110
117
  * // Cleanup
111
118
  * client.destroy();
@@ -135,19 +142,32 @@ declare function getClient(descriptor: ChainDefinition): PolkadotClient;
135
142
  declare function isConnected(descriptor: ChainDefinition): boolean;
136
143
 
137
144
  /** Known network environment with built-in descriptors and RPC endpoints. */
138
- type Environment = "polkadot" | "kusama" | "paseo";
139
- /** Maps each environment to its asset hub descriptor type. */
140
- type AssetHubDescriptors = {
141
- polkadot: typeof polkadot_asset_hub;
142
- kusama: typeof kusama_asset_hub;
143
- paseo: typeof paseo_asset_hub;
145
+ type Environment = "polkadot" | "kusama" | "paseo" | "previewnet";
146
+ /** Per-environment descriptor types for each chain in the preset. */
147
+ type PresetDescriptors = {
148
+ polkadot: {
149
+ assetHub: typeof polkadot_asset_hub;
150
+ bulletin: typeof paseo_bulletin;
151
+ individuality: typeof paseo_individuality;
152
+ };
153
+ kusama: {
154
+ assetHub: typeof kusama_asset_hub;
155
+ bulletin: typeof paseo_bulletin;
156
+ individuality: typeof paseo_individuality;
157
+ };
158
+ paseo: {
159
+ assetHub: typeof paseo_asset_hub;
160
+ bulletin: typeof paseo_bulletin;
161
+ individuality: typeof paseo_individuality;
162
+ };
163
+ previewnet: {
164
+ assetHub: typeof previewnet_asset_hub;
165
+ bulletin: typeof previewnet_bulletin;
166
+ individuality: typeof previewnet_individuality;
167
+ };
144
168
  };
145
169
  /** The chain shape returned by {@link getChainAPI} for a given environment. */
146
- type PresetChains<E extends Environment> = {
147
- assetHub: AssetHubDescriptors[E];
148
- bulletin: typeof bulletin;
149
- individuality: typeof individuality;
150
- };
170
+ type PresetChains<E extends Environment> = PresetDescriptors[E];
151
171
  /**
152
172
  * Get a chain client for a known environment with built-in descriptors and RPCs.
153
173
  *
@@ -168,9 +188,10 @@ type PresetChains<E extends Environment> = {
168
188
  * const account = await client.assetHub.query.System.Account.getValue(addr);
169
189
  * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();
170
190
  *
171
- * // Raw client for advanced use (e.g., InkSdk for contracts)
172
- * import { createInkSdk } from "@polkadot-api/sdk-ink";
173
- * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
191
+ * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)
192
+ * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
193
+ * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
194
+ * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
174
195
  *
175
196
  * client.destroy();
176
197
  * ```
package/dist/index.js CHANGED
@@ -128,7 +128,7 @@ function isConnected(descriptor) {
128
128
  if (!genesis) return false;
129
129
  return findEntryByGenesis(genesis) !== void 0;
130
130
  }
131
- var AVAILABLE_ENVIRONMENTS = /* @__PURE__ */ new Set(["paseo"]);
131
+ var AVAILABLE_ENVIRONMENTS = /* @__PURE__ */ new Set(["paseo", "previewnet"]);
132
132
  var rpcs = {
133
133
  polkadot: {
134
134
  assetHub: [
@@ -147,26 +147,45 @@ var rpcs = {
147
147
  individuality: []
148
148
  },
149
149
  paseo: {
150
- assetHub: [
151
- "wss://asset-hub-paseo-rpc.n.dwellir.com",
152
- "wss://sys.ibp.network/asset-hub-paseo"
153
- ],
150
+ assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"],
154
151
  bulletin: [...BULLETIN_RPCS.paseo],
155
- individuality: ["wss://paseo-people-next-rpc.polkadot.io"]
152
+ individuality: ["wss://paseo-people-next-system-rpc.polkadot.io"]
153
+ },
154
+ previewnet: {
155
+ assetHub: ["wss://previewnet.substrate.dev/asset-hub"],
156
+ bulletin: [...BULLETIN_RPCS.previewnet],
157
+ individuality: ["wss://previewnet.substrate.dev/people"]
156
158
  }
157
159
  };
158
160
  async function loadDescriptors(env) {
159
- const assetHubImport = {
160
- polkadot: () => import('@parity/product-sdk-descriptors/polkadot-asset-hub'),
161
- kusama: () => import('@parity/product-sdk-descriptors/kusama-asset-hub'),
162
- paseo: () => import('@parity/product-sdk-descriptors/paseo-asset-hub')
163
- }[env]();
164
- const [ahMod, { bulletin }, { individuality }] = await Promise.all([
165
- assetHubImport,
166
- import('@parity/product-sdk-descriptors/bulletin'),
167
- import('@parity/product-sdk-descriptors/individuality')
168
- ]);
169
- const assetHub = "polkadot_asset_hub" in ahMod ? ahMod.polkadot_asset_hub : "kusama_asset_hub" in ahMod ? ahMod.kusama_asset_hub : ahMod.paseo_asset_hub;
161
+ const loaders = {
162
+ polkadot: () => Promise.all([
163
+ import('@parity/product-sdk-descriptors/polkadot-asset-hub'),
164
+ // Polkadot bulletin/individuality are not yet live; gated by
165
+ // AVAILABLE_ENVIRONMENTS so this branch is unreachable today.
166
+ Promise.reject(new Error("polkadot bulletin descriptor not yet available")),
167
+ Promise.reject(new Error("polkadot individuality descriptor not yet available"))
168
+ ]),
169
+ kusama: () => Promise.all([
170
+ import('@parity/product-sdk-descriptors/kusama-asset-hub'),
171
+ Promise.reject(new Error("kusama bulletin descriptor not yet available")),
172
+ Promise.reject(new Error("kusama individuality descriptor not yet available"))
173
+ ]),
174
+ paseo: () => Promise.all([
175
+ import('@parity/product-sdk-descriptors/paseo-asset-hub'),
176
+ import('@parity/product-sdk-descriptors/paseo-bulletin'),
177
+ import('@parity/product-sdk-descriptors/paseo-individuality')
178
+ ]),
179
+ previewnet: () => Promise.all([
180
+ import('@parity/product-sdk-descriptors/previewnet-asset-hub'),
181
+ import('@parity/product-sdk-descriptors/previewnet-bulletin'),
182
+ import('@parity/product-sdk-descriptors/previewnet-individuality')
183
+ ])
184
+ };
185
+ const [ahMod, bulletinMod, individualityMod] = await loaders[env]();
186
+ const assetHub = "polkadot_asset_hub" in ahMod ? ahMod.polkadot_asset_hub : "kusama_asset_hub" in ahMod ? ahMod.kusama_asset_hub : "paseo_asset_hub" in ahMod ? ahMod.paseo_asset_hub : ahMod.previewnet_asset_hub;
187
+ const bulletin = "paseo_bulletin" in bulletinMod ? bulletinMod.paseo_bulletin : bulletinMod.previewnet_bulletin;
188
+ const individuality = "paseo_individuality" in individualityMod ? individualityMod.paseo_individuality : individualityMod.previewnet_individuality;
170
189
  return { assetHub, bulletin, individuality };
171
190
  }
172
191
  async function getChainAPI(env) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/providers.ts","../src/hmr.ts","../src/clients.ts","../src/presets.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { getHostProvider } from \"@parity/product-sdk-host\";\nimport type { JsonRpcProvider } from \"polkadot-api\";\n\n/**\n * Create a PAPI-compatible JSON-RPC provider for a chain.\n *\n * Routes connections through the host provider (`@parity/product-sdk-host`).\n * The SDK is designed to run exclusively inside a host container.\n *\n * @throws {Error} If the host provider is unavailable (not inside a container).\n */\nexport async function createProvider(genesisHash: string): Promise<JsonRpcProvider> {\n const hostProvider = await getHostProvider(genesisHash as `0x${string}`);\n if (!hostProvider) {\n throw new Error(\n `Host provider unavailable for chain ${genesisHash}. Ensure you are running inside a host container (Polkadot Browser / Desktop).`,\n );\n }\n return hostProvider;\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi, beforeEach } = import.meta.vitest;\n\n // Shared state between hoisted mocks and tests\n const state = vi.hoisted(() => ({\n fakeProvider: (() => {}) as unknown as JsonRpcProvider,\n hostProviderCalls: [] as unknown[][],\n hostProviderAvailable: true,\n }));\n\n vi.mock(\"@parity/product-sdk-host\", async (importOriginal) => ({\n ...(await importOriginal<typeof import(\"@parity/product-sdk-host\")>()),\n getHostProvider: async (...args: unknown[]) => {\n state.hostProviderCalls.push(args);\n if (!state.hostProviderAvailable) return null;\n return state.fakeProvider;\n },\n }));\n\n beforeEach(() => {\n state.hostProviderCalls = [];\n state.hostProviderAvailable = true;\n });\n\n test(\"returns host provider when available\", async () => {\n const result = await createProvider(\"0xabc\");\n expect(result).toBe(state.fakeProvider);\n expect(state.hostProviderCalls.length).toBe(1);\n expect(state.hostProviderCalls[0][0]).toBe(\"0xabc\");\n });\n\n test(\"throws when host provider unavailable\", async () => {\n state.hostProviderAvailable = false;\n await expect(createProvider(\"0xabc\")).rejects.toThrow(/Host provider unavailable/);\n });\n}\n","import type { ChainEntry } from \"./types.js\";\n\ndeclare global {\n var __chainClientCache: Map<string, ChainEntry> | undefined;\n}\n\n/** Get the HMR-safe client cache, keyed by genesis hash. */\nexport function getClientCache(): Map<string, ChainEntry> {\n globalThis.__chainClientCache ??= new Map();\n return globalThis.__chainClientCache;\n}\n\n/** Clear all entries from the client cache. Destroys active clients. */\nexport function clearClientCache(): void {\n const cache = getClientCache();\n for (const entry of cache.values()) {\n try {\n entry.client.destroy();\n } catch {\n // client may already be destroyed\n }\n }\n cache.clear();\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getClientCache returns a Map\", () => {\n const cache = getClientCache();\n expect(cache).toBeInstanceOf(Map);\n });\n\n test(\"getClientCache returns the same instance on repeated calls\", () => {\n const a = getClientCache();\n const b = getClientCache();\n expect(a).toBe(b);\n });\n}\n","import type { ChainDefinition, PolkadotClient } from \"polkadot-api\";\nimport { createClient } from \"polkadot-api\";\nimport { createProvider } from \"./providers.js\";\nimport { getClientCache, clearClientCache } from \"./hmr.js\";\nimport type { ChainEntry, ChainClientConfig, ChainClient } from \"./types.js\";\n\n// Cache keys are scoped by a fingerprint of the config so that two\n// `createChainClient` calls with different chain sets don't collide.\nconst cacheKey = (fingerprint: string, genesis: string) => `${fingerprint}:${genesis}`;\n\nfunction findEntryByGenesis(genesis: string): ChainEntry | undefined {\n for (const [key, entry] of getClientCache()) {\n if (key.endsWith(`:${genesis}`)) return entry;\n }\n}\n\nconst clientInstances = new Map<string, Promise<ChainClient<any>>>();\n\n/** Build a stable fingerprint from sorted chain names + genesis hashes. */\nfunction configFingerprint(chains: Record<string, ChainDefinition>): string {\n return Object.entries(chains)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, desc]) => `${name}:${desc.genesis ?? \"unknown\"}`)\n .join(\"|\");\n}\n\n/**\n * Create a multi-chain client with user-provided descriptors and RPC endpoints.\n *\n * Returns fully-typed APIs for each chain plus raw `PolkadotClient` access via `.raw`.\n * Connections use host routing (via `@parity/product-sdk-host`) when inside a container,\n * falling back to direct WebSocket RPC.\n *\n * Results are cached by genesis-hash fingerprint — calling with the same descriptors\n * returns the same instance.\n *\n * @example\n * ```ts\n * import { createChainClient } from \"@parity/product-sdk-chain-client\";\n * import { paseo_asset_hub } from \"./descriptors/paseo-asset-hub\";\n * import { bulletin } from \"./descriptors/bulletin\";\n *\n * const client = await createChainClient({\n * chains: { assetHub: paseo_asset_hub, bulletin },\n * rpcs: {\n * assetHub: [\"wss://sys.ibp.network/asset-hub-paseo\"],\n * bulletin: [\"wss://paseo-bulletin-rpc.polkadot.io\"],\n * },\n * });\n *\n * // Fully typed from your descriptors\n * const account = await client.assetHub.query.System.Account.getValue(addr);\n * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();\n *\n * // Raw client for advanced use (e.g., InkSdk for contracts)\n * import { createInkSdk } from \"@polkadot-api/sdk-ink\";\n * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });\n *\n * // Cleanup\n * client.destroy();\n * ```\n */\nexport async function createChainClient<const TChains extends Record<string, ChainDefinition>>(\n config: ChainClientConfig<TChains>,\n): Promise<ChainClient<TChains>> {\n const fingerprint = configFingerprint(config.chains);\n\n const existing = clientInstances.get(fingerprint);\n if (existing) return existing as Promise<ChainClient<TChains>>;\n\n const promise = initChainClient(config, fingerprint).catch((err) => {\n // Clean up any clients created before the failure to avoid leaking\n // WebSocket connections that are unreachable except via destroyAll().\n const cache = getClientCache();\n for (const [key, entry] of cache) {\n if (key.startsWith(`${fingerprint}:`)) {\n try {\n entry.client.destroy();\n } catch {\n /* already destroyed */\n }\n cache.delete(key);\n }\n }\n clientInstances.delete(fingerprint);\n throw err;\n });\n clientInstances.set(fingerprint, promise);\n return promise;\n}\n\n/* @integration */\nasync function initChainClient<const TChains extends Record<string, ChainDefinition>>(\n config: ChainClientConfig<TChains>,\n fingerprint: string,\n): Promise<ChainClient<TChains>> {\n const names = Object.keys(config.chains) as (string & keyof TChains)[];\n const clientCache = getClientCache();\n\n // Create providers and clients in parallel\n const entries = await Promise.all(\n names.map(async (name) => {\n const descriptor = config.chains[name] as ChainDefinition;\n const genesis = descriptor.genesis;\n if (!genesis) {\n throw new Error(`Descriptor for chain \"${name}\" has no genesis hash.`);\n }\n const provider = await createProvider(genesis);\n const client = createClient(provider);\n\n // Populate HMR cache so getClient() and isConnected() work\n const key = cacheKey(fingerprint, genesis);\n if (!clientCache.has(key)) {\n clientCache.set(key, {\n client,\n api: new Map(),\n } satisfies ChainEntry);\n }\n\n return { name, descriptor, client, genesis };\n }),\n );\n\n // Build typed APIs and raw client map\n const apis = {} as Record<string, unknown>;\n const raw = {} as Record<string, PolkadotClient>;\n\n for (const { name, descriptor, client } of entries) {\n apis[name] = client.getTypedApi(descriptor);\n raw[name] = client;\n }\n\n return {\n ...apis,\n raw,\n destroy() {\n for (const { genesis } of entries) {\n const key = cacheKey(fingerprint, genesis);\n const entry = clientCache.get(key);\n if (entry) {\n try {\n entry.client.destroy();\n } catch {\n /* already destroyed */\n }\n clientCache.delete(key);\n }\n }\n clientInstances.delete(fingerprint);\n },\n } as ChainClient<TChains>;\n}\n\n/**\n * Destroy all chain client instances and reset internal caches.\n *\n * Tears down every connection created by {@link createChainClient}.\n */\nexport function destroyAll(): void {\n clearClientCache();\n clientInstances.clear();\n}\n\n/**\n * Get the raw `PolkadotClient` for a connected chain by its descriptor.\n *\n * The chain must have been initialized via {@link createChainClient} first.\n * Alternatively, use `client.raw.<name>` on the returned {@link ChainClient}.\n *\n * @throws If the chain has not been connected yet.\n */\nexport function getClient(descriptor: ChainDefinition): PolkadotClient {\n const genesis = descriptor.genesis;\n if (!genesis) throw new Error(\"Descriptor has no genesis hash.\");\n const entry = findEntryByGenesis(genesis);\n if (!entry?.client) {\n throw new Error(\n `Chain not connected (genesis: ${genesis}). Call createChainClient() first to establish connections.`,\n );\n }\n return entry.client;\n}\n\n/**\n * Check if a chain is currently connected.\n *\n * Synchronous — no side effects, no initialization.\n */\nexport function isConnected(descriptor: ChainDefinition): boolean {\n const genesis = descriptor.genesis;\n if (!genesis) return false;\n return findEntryByGenesis(genesis) !== undefined;\n}\n\nif (import.meta.vitest) {\n const { test, expect, beforeEach } = import.meta.vitest;\n\n const fakeDescriptor = { genesis: \"0xtest\" } as ChainDefinition;\n const fakeClient = {\n destroy: () => {},\n getTypedApi: () => ({}),\n } as unknown as PolkadotClient;\n\n function seedCache(genesis: string, client: PolkadotClient, fp = \"test\") {\n getClientCache().set(cacheKey(fp, genesis), {\n client,\n api: new Map(),\n });\n }\n\n beforeEach(() => {\n clearClientCache();\n clientInstances.clear();\n });\n\n // --- isConnected ---\n\n test(\"isConnected returns false for unknown chain\", () => {\n expect(isConnected(fakeDescriptor)).toBe(false);\n });\n\n test(\"isConnected returns true after cache is populated\", () => {\n seedCache(\"0xtest\", fakeClient);\n expect(isConnected(fakeDescriptor)).toBe(true);\n });\n\n test(\"isConnected returns false for descriptor without genesis\", () => {\n expect(isConnected({} as ChainDefinition)).toBe(false);\n });\n\n // --- getClient ---\n\n test(\"getClient returns client from cache\", () => {\n seedCache(\"0xtest\", fakeClient);\n expect(getClient(fakeDescriptor)).toBe(fakeClient);\n });\n\n test(\"getClient throws for unconnected chain\", () => {\n expect(() => getClient(fakeDescriptor)).toThrow(/Chain not connected/);\n });\n\n test(\"getClient throws for descriptor without genesis\", () => {\n expect(() => getClient({} as ChainDefinition)).toThrow(/no genesis hash/);\n });\n\n // --- destroyAll ---\n\n test(\"destroyAll calls client.destroy() and clears caches\", () => {\n let destroyed = false;\n const trackableClient = {\n destroy: () => {\n destroyed = true;\n },\n getTypedApi: () => ({}),\n } as unknown as PolkadotClient;\n seedCache(\"0xtest\", trackableClient);\n clientInstances.set(\"test\", Promise.resolve({} as ChainClient<any>));\n destroyAll();\n expect(destroyed).toBe(true);\n expect(isConnected(fakeDescriptor)).toBe(false);\n expect(clientInstances.size).toBe(0);\n });\n\n // --- createChainClient ---\n\n test(\"createChainClient returns same promise for identical config\", async () => {\n const fakeResult = {} as ChainClient<any>;\n const fp = configFingerprint({ a: fakeDescriptor });\n clientInstances.set(fp, Promise.resolve(fakeResult));\n const result = await createChainClient({\n chains: { a: fakeDescriptor },\n rpcs: { a: [] },\n });\n expect(result).toBe(fakeResult);\n });\n\n test(\"createChainClient deduplicates concurrent calls\", async () => {\n const fakeResult = {} as ChainClient<any>;\n const fp = configFingerprint({ x: fakeDescriptor });\n clientInstances.set(fp, Promise.resolve(fakeResult));\n const [a, b] = await Promise.all([\n createChainClient({ chains: { x: fakeDescriptor }, rpcs: { x: [] } }),\n createChainClient({ chains: { x: fakeDescriptor }, rpcs: { x: [] } }),\n ]);\n expect(a).toBe(b);\n });\n\n test(\"createChainClient returns different results for different configs\", async () => {\n const descA = { genesis: \"0xaaa\" } as ChainDefinition;\n const descB = { genesis: \"0xbbb\" } as ChainDefinition;\n const resultA = {} as ChainClient<any>;\n const resultB = {} as ChainClient<any>;\n clientInstances.set(configFingerprint({ a: descA }), Promise.resolve(resultA));\n clientInstances.set(configFingerprint({ b: descB }), Promise.resolve(resultB));\n const a = await createChainClient({ chains: { a: descA }, rpcs: { a: [] } });\n const b = await createChainClient({ chains: { b: descB }, rpcs: { b: [] } });\n expect(a).not.toBe(b);\n });\n\n // --- configFingerprint ---\n\n test(\"configFingerprint is stable regardless of key order\", () => {\n const d1 = { genesis: \"0x1\" } as ChainDefinition;\n const d2 = { genesis: \"0x2\" } as ChainDefinition;\n expect(configFingerprint({ a: d1, b: d2 })).toBe(configFingerprint({ b: d2, a: d1 }));\n });\n\n // --- findEntryByGenesis ---\n\n test(\"findEntryByGenesis returns undefined for missing genesis\", () => {\n expect(findEntryByGenesis(\"0xnonexistent\")).toBeUndefined();\n });\n\n // --- full lifecycle ---\n\n test(\"full lifecycle: seed, verify connected, destroy, verify disconnected\", () => {\n seedCache(\"0xtest\", fakeClient);\n expect(isConnected(fakeDescriptor)).toBe(true);\n expect(getClient(fakeDescriptor)).toBe(fakeClient);\n destroyAll();\n expect(isConnected(fakeDescriptor)).toBe(false);\n expect(() => getClient(fakeDescriptor)).toThrow(/Chain not connected/);\n });\n\n test(\"two fingerprints cached independently, destroy one leaves other intact\", () => {\n const sharedGenesis = \"0xshared\";\n const clientA = { destroy: () => {} } as PolkadotClient;\n const clientB = { destroy: () => {} } as PolkadotClient;\n const descriptorShared = { genesis: sharedGenesis } as ChainDefinition;\n\n seedCache(sharedGenesis, clientA, \"fpA\");\n seedCache(sharedGenesis, clientB, \"fpB\");\n\n expect(isConnected(descriptorShared)).toBe(true);\n\n // Destroy only fpA's entry\n const cache = getClientCache();\n const keyA = cacheKey(\"fpA\", sharedGenesis);\n cache.get(keyA)?.client.destroy();\n cache.delete(keyA);\n\n // fpB's entry still alive\n expect(isConnected(descriptorShared)).toBe(true);\n expect(getClient(descriptorShared)).toBe(clientB);\n });\n}\n","import type { ChainDefinition } from \"polkadot-api\";\nimport { BULLETIN_RPCS } from \"@parity/product-sdk-host\";\nimport { createChainClient } from \"./clients.js\";\nimport type { ChainClient } from \"./types.js\";\n\n// Type-only imports — erased at compile time, zero bundle cost.\n// These give us per-chain TypedApi types without importing runtime descriptor data.\nimport type { polkadot_asset_hub as PolkadotAssetHubDef } from \"@parity/product-sdk-descriptors/polkadot-asset-hub\";\nimport type { kusama_asset_hub as KusamaAssetHubDef } from \"@parity/product-sdk-descriptors/kusama-asset-hub\";\nimport type { paseo_asset_hub as PaseoAssetHubDef } from \"@parity/product-sdk-descriptors/paseo-asset-hub\";\nimport type { bulletin as BulletinDef } from \"@parity/product-sdk-descriptors/bulletin\";\nimport type { individuality as IndividualityDef } from \"@parity/product-sdk-descriptors/individuality\";\n\n/** Known network environment with built-in descriptors and RPC endpoints. */\nexport type Environment = \"polkadot\" | \"kusama\" | \"paseo\";\n\n/** Environments where all chains (asset hub, bulletin, individuality) are live. */\nconst AVAILABLE_ENVIRONMENTS: Set<Environment> = new Set([\"paseo\"]);\n\nconst rpcs = {\n polkadot: {\n assetHub: [\n \"wss://polkadot-asset-hub-rpc.polkadot.io\",\n \"wss://sys.ibp.network/asset-hub-polkadot\",\n ],\n bulletin: [...BULLETIN_RPCS.polkadot],\n individuality: [] as string[],\n },\n kusama: {\n assetHub: [\n \"wss://kusama-asset-hub-rpc.polkadot.io\",\n \"wss://sys.ibp.network/asset-hub-kusama\",\n ],\n bulletin: [...BULLETIN_RPCS.kusama],\n individuality: [] as string[],\n },\n paseo: {\n assetHub: [\n \"wss://asset-hub-paseo-rpc.n.dwellir.com\",\n \"wss://sys.ibp.network/asset-hub-paseo\",\n ],\n bulletin: [...BULLETIN_RPCS.paseo],\n individuality: [\"wss://paseo-people-next-rpc.polkadot.io\"],\n },\n} as const;\n\n/**\n * Lazy-load descriptors for a specific environment.\n * Only imports the chains needed — avoids bundling all 5 chains when\n * a consumer only uses one environment.\n */\nasync function loadDescriptors(env: Environment) {\n const assetHubImport = {\n polkadot: () => import(\"@parity/product-sdk-descriptors/polkadot-asset-hub\"),\n kusama: () => import(\"@parity/product-sdk-descriptors/kusama-asset-hub\"),\n paseo: () => import(\"@parity/product-sdk-descriptors/paseo-asset-hub\"),\n }[env]();\n\n const [ahMod, { bulletin }, { individuality }] = await Promise.all([\n assetHubImport,\n import(\"@parity/product-sdk-descriptors/bulletin\"),\n import(\"@parity/product-sdk-descriptors/individuality\"),\n ]);\n\n // Extract the asset hub descriptor (the named export varies per environment)\n const assetHub =\n \"polkadot_asset_hub\" in ahMod\n ? ahMod.polkadot_asset_hub\n : \"kusama_asset_hub\" in ahMod\n ? ahMod.kusama_asset_hub\n : (ahMod as typeof import(\"@parity/product-sdk-descriptors/paseo-asset-hub\"))\n .paseo_asset_hub;\n\n return { assetHub, bulletin, individuality };\n}\n\n/** Maps each environment to its asset hub descriptor type. */\ntype AssetHubDescriptors = {\n polkadot: typeof PolkadotAssetHubDef;\n kusama: typeof KusamaAssetHubDef;\n paseo: typeof PaseoAssetHubDef;\n};\n\n/** The chain shape returned by {@link getChainAPI} for a given environment. */\nexport type PresetChains<E extends Environment> = {\n assetHub: AssetHubDescriptors[E];\n bulletin: typeof BulletinDef;\n individuality: typeof IndividualityDef;\n};\n\n/**\n * Get a chain client for a known environment with built-in descriptors and RPCs.\n *\n * This is the **zero-config** path — no need to import descriptors or specify\n * endpoints. For custom chains or BYOD descriptors, use\n * {@link createChainClient} instead.\n *\n * Returns the same {@link ChainClient} type as `createChainClient`, with\n * `assetHub`, `bulletin`, and `individuality` chain keys.\n *\n * @example\n * ```ts\n * import { getChainAPI } from \"@parity/product-sdk-chain-client\";\n *\n * const client = await getChainAPI(\"paseo\");\n *\n * // Fully typed — no descriptor imports needed\n * const account = await client.assetHub.query.System.Account.getValue(addr);\n * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();\n *\n * // Raw client for advanced use (e.g., InkSdk for contracts)\n * import { createInkSdk } from \"@polkadot-api/sdk-ink\";\n * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });\n *\n * client.destroy();\n * ```\n */\nexport async function getChainAPI<E extends Environment>(\n env: E,\n): Promise<ChainClient<PresetChains<E>>> {\n if (!AVAILABLE_ENVIRONMENTS.has(env)) {\n throw new Error(`Chain API for \"${env}\" is not yet available`);\n }\n\n const descriptors = await loadDescriptors(env);\n const envRpcs = rpcs[env];\n\n return createChainClient({\n chains: {\n assetHub: descriptors.assetHub,\n bulletin: descriptors.bulletin,\n individuality: descriptors.individuality,\n },\n rpcs: {\n assetHub: [...envRpcs.assetHub],\n bulletin: [...envRpcs.bulletin],\n individuality: [...envRpcs.individuality],\n },\n }) as Promise<ChainClient<PresetChains<E>>>;\n}\n\nif (import.meta.vitest) {\n const { test, expect, beforeEach } = import.meta.vitest;\n const { destroyAll } = await import(\"./clients.js\");\n\n // Test-only genesis hashes for assertion — not used in production code.\n const GENESIS = {\n polkadot_asset_hub: \"0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f\",\n kusama_asset_hub: \"0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a\",\n paseo_asset_hub: \"0xd6eec26135305a8ad257a20d003357284c8aa03d0bdb2b357ab0a22371e11ef2\",\n bulletin: \"0x744960c32e3a3df5440e1ecd4d34096f1ce2230d7016a5ada8a765d5a622b4ea\",\n individuality: \"0xd01475fde5d0592989b7715ae1d2e89fdb4f8c7688c09c850d75e1d4bdb47d64\",\n } as const;\n\n beforeEach(() => {\n destroyAll();\n });\n\n // --- GENESIS constants ---\n\n test(\"genesis constants are valid hex hashes\", () => {\n for (const hash of Object.values(GENESIS)) {\n expect(hash).toMatch(/^0x[a-f0-9]{64}$/);\n }\n });\n\n // --- RPC config ---\n\n test(\"rpcs defined for all environments\", () => {\n for (const env of [\"polkadot\", \"kusama\", \"paseo\"] as const) {\n const envRpcs = rpcs[env];\n expect(envRpcs.assetHub.length).toBeGreaterThan(0);\n }\n });\n\n test(\"paseo has RPCs for all chains\", () => {\n const envRpcs = rpcs.paseo;\n expect(envRpcs.bulletin.length).toBeGreaterThan(0);\n expect(envRpcs.individuality.length).toBeGreaterThan(0);\n });\n\n // --- getChainAPI ---\n\n test(\"polkadot and kusama throw as not yet available\", async () => {\n await expect(getChainAPI(\"polkadot\")).rejects.toThrow(\"not yet available\");\n await expect(getChainAPI(\"kusama\")).rejects.toThrow(\"not yet available\");\n });\n\n // --- loadDescriptors ---\n\n test(\"loadDescriptors returns descriptors with genesis hashes for paseo\", async () => {\n const descriptors = await loadDescriptors(\"paseo\");\n expect(descriptors).toBeDefined();\n expect(descriptors.assetHub).toBeDefined();\n expect(descriptors.bulletin).toBeDefined();\n expect(descriptors.individuality).toBeDefined();\n expect(descriptors.assetHub.genesis).toBe(GENESIS.paseo_asset_hub);\n expect(descriptors.bulletin.genesis).toBe(GENESIS.bulletin);\n expect(descriptors.individuality.genesis).toBe(GENESIS.individuality);\n });\n\n test(\"loadDescriptors returns correct asset hub per environment\", async () => {\n const polkadot = await loadDescriptors(\"polkadot\");\n const kusama = await loadDescriptors(\"kusama\");\n const paseo = await loadDescriptors(\"paseo\");\n expect(polkadot.assetHub.genesis).toBe(GENESIS.polkadot_asset_hub);\n expect(kusama.assetHub.genesis).toBe(GENESIS.kusama_asset_hub);\n expect(paseo.assetHub.genesis).toBe(GENESIS.paseo_asset_hub);\n // bulletin and individuality are the same across environments\n expect(polkadot.bulletin.genesis).toBe(paseo.bulletin.genesis);\n expect(polkadot.individuality.genesis).toBe(paseo.individuality.genesis);\n });\n\n // --- AVAILABLE_ENVIRONMENTS ---\n\n test(\"only paseo is currently available\", () => {\n expect(AVAILABLE_ENVIRONMENTS.has(\"paseo\")).toBe(true);\n expect(AVAILABLE_ENVIRONMENTS.has(\"polkadot\")).toBe(false);\n expect(AVAILABLE_ENVIRONMENTS.has(\"kusama\")).toBe(false);\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/providers.ts","../src/hmr.ts","../src/clients.ts","../src/presets.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { getHostProvider } from \"@parity/product-sdk-host\";\nimport type { JsonRpcProvider } from \"polkadot-api\";\n\n/**\n * Create a PAPI-compatible JSON-RPC provider for a chain.\n *\n * Routes connections through the host provider (`@parity/product-sdk-host`).\n * The SDK is designed to run exclusively inside a host container.\n *\n * @throws {Error} If the host provider is unavailable (not inside a container).\n */\nexport async function createProvider(genesisHash: string): Promise<JsonRpcProvider> {\n const hostProvider = await getHostProvider(genesisHash as `0x${string}`);\n if (!hostProvider) {\n throw new Error(\n `Host provider unavailable for chain ${genesisHash}. Ensure you are running inside a host container (Polkadot Browser / Desktop).`,\n );\n }\n return hostProvider;\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi, beforeEach } = import.meta.vitest;\n\n // Shared state between hoisted mocks and tests\n const state = vi.hoisted(() => ({\n fakeProvider: (() => {}) as unknown as JsonRpcProvider,\n hostProviderCalls: [] as unknown[][],\n hostProviderAvailable: true,\n }));\n\n vi.mock(\"@parity/product-sdk-host\", async (importOriginal) => ({\n ...(await importOriginal<typeof import(\"@parity/product-sdk-host\")>()),\n getHostProvider: async (...args: unknown[]) => {\n state.hostProviderCalls.push(args);\n if (!state.hostProviderAvailable) return null;\n return state.fakeProvider;\n },\n }));\n\n beforeEach(() => {\n state.hostProviderCalls = [];\n state.hostProviderAvailable = true;\n });\n\n test(\"returns host provider when available\", async () => {\n const result = await createProvider(\"0xabc\");\n expect(result).toBe(state.fakeProvider);\n expect(state.hostProviderCalls.length).toBe(1);\n expect(state.hostProviderCalls[0][0]).toBe(\"0xabc\");\n });\n\n test(\"throws when host provider unavailable\", async () => {\n state.hostProviderAvailable = false;\n await expect(createProvider(\"0xabc\")).rejects.toThrow(/Host provider unavailable/);\n });\n}\n","import type { ChainEntry } from \"./types.js\";\n\ndeclare global {\n var __chainClientCache: Map<string, ChainEntry> | undefined;\n}\n\n/** Get the HMR-safe client cache, keyed by genesis hash. */\nexport function getClientCache(): Map<string, ChainEntry> {\n globalThis.__chainClientCache ??= new Map();\n return globalThis.__chainClientCache;\n}\n\n/** Clear all entries from the client cache. Destroys active clients. */\nexport function clearClientCache(): void {\n const cache = getClientCache();\n for (const entry of cache.values()) {\n try {\n entry.client.destroy();\n } catch {\n // client may already be destroyed\n }\n }\n cache.clear();\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getClientCache returns a Map\", () => {\n const cache = getClientCache();\n expect(cache).toBeInstanceOf(Map);\n });\n\n test(\"getClientCache returns the same instance on repeated calls\", () => {\n const a = getClientCache();\n const b = getClientCache();\n expect(a).toBe(b);\n });\n}\n","import type { ChainDefinition, PolkadotClient } from \"polkadot-api\";\nimport { createClient } from \"polkadot-api\";\nimport { createProvider } from \"./providers.js\";\nimport { getClientCache, clearClientCache } from \"./hmr.js\";\nimport type { ChainEntry, ChainClientConfig, ChainClient } from \"./types.js\";\n\n// Cache keys are scoped by a fingerprint of the config so that two\n// `createChainClient` calls with different chain sets don't collide.\nconst cacheKey = (fingerprint: string, genesis: string) => `${fingerprint}:${genesis}`;\n\nfunction findEntryByGenesis(genesis: string): ChainEntry | undefined {\n for (const [key, entry] of getClientCache()) {\n if (key.endsWith(`:${genesis}`)) return entry;\n }\n}\n\nconst clientInstances = new Map<string, Promise<ChainClient<any>>>();\n\n/** Build a stable fingerprint from sorted chain names + genesis hashes. */\nfunction configFingerprint(chains: Record<string, ChainDefinition>): string {\n return Object.entries(chains)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, desc]) => `${name}:${desc.genesis ?? \"unknown\"}`)\n .join(\"|\");\n}\n\n/**\n * Create a multi-chain client with user-provided descriptors and RPC endpoints.\n *\n * Returns fully-typed APIs for each chain plus raw `PolkadotClient` access via `.raw`.\n * Connections route through the host provider (`@parity/product-sdk-host`) — the SDK\n * is designed to run exclusively inside a host container (Polkadot Browser / Desktop).\n * Throws if no host provider is available; there is no direct-WebSocket fallback.\n *\n * The `config.rpcs` field is currently unused at runtime (kept for API compatibility\n * and BYOD documentation), since the host owns the chain connection.\n *\n * Results are cached by genesis-hash fingerprint — calling with the same descriptors\n * returns the same instance.\n *\n * @example\n * ```ts\n * import { createChainClient } from \"@parity/product-sdk-chain-client\";\n * import { paseo_asset_hub } from \"@parity/product-sdk-descriptors/paseo-asset-hub\";\n * import { paseo_bulletin } from \"@parity/product-sdk-descriptors/paseo-bulletin\";\n *\n * const client = await createChainClient({\n * chains: { assetHub: paseo_asset_hub, bulletin: paseo_bulletin },\n * rpcs: {\n * assetHub: [\"wss://paseo-asset-hub-next-rpc.polkadot.io\"],\n * bulletin: [\"wss://paseo-bulletin-next-rpc.polkadot.io\"],\n * },\n * });\n *\n * // Fully typed from your descriptors\n * const account = await client.assetHub.query.System.Account.getValue(addr);\n * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();\n *\n * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)\n * import { createContractRuntimeFromClient } from \"@parity/product-sdk-contracts\";\n * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);\n *\n * // Cleanup\n * client.destroy();\n * ```\n */\nexport async function createChainClient<const TChains extends Record<string, ChainDefinition>>(\n config: ChainClientConfig<TChains>,\n): Promise<ChainClient<TChains>> {\n const fingerprint = configFingerprint(config.chains);\n\n const existing = clientInstances.get(fingerprint);\n if (existing) return existing as Promise<ChainClient<TChains>>;\n\n const promise = initChainClient(config, fingerprint).catch((err) => {\n // Clean up any clients created before the failure to avoid leaking\n // WebSocket connections that are unreachable except via destroyAll().\n const cache = getClientCache();\n for (const [key, entry] of cache) {\n if (key.startsWith(`${fingerprint}:`)) {\n try {\n entry.client.destroy();\n } catch {\n /* already destroyed */\n }\n cache.delete(key);\n }\n }\n clientInstances.delete(fingerprint);\n throw err;\n });\n clientInstances.set(fingerprint, promise);\n return promise;\n}\n\n/* @integration */\nasync function initChainClient<const TChains extends Record<string, ChainDefinition>>(\n config: ChainClientConfig<TChains>,\n fingerprint: string,\n): Promise<ChainClient<TChains>> {\n const names = Object.keys(config.chains) as (string & keyof TChains)[];\n const clientCache = getClientCache();\n\n // Create providers and clients in parallel\n const entries = await Promise.all(\n names.map(async (name) => {\n const descriptor = config.chains[name] as ChainDefinition;\n const genesis = descriptor.genesis;\n if (!genesis) {\n throw new Error(`Descriptor for chain \"${name}\" has no genesis hash.`);\n }\n const provider = await createProvider(genesis);\n const client = createClient(provider);\n\n // Populate HMR cache so getClient() and isConnected() work\n const key = cacheKey(fingerprint, genesis);\n if (!clientCache.has(key)) {\n clientCache.set(key, {\n client,\n api: new Map(),\n } satisfies ChainEntry);\n }\n\n return { name, descriptor, client, genesis };\n }),\n );\n\n // Build typed APIs and raw client map\n const apis = {} as Record<string, unknown>;\n const raw = {} as Record<string, PolkadotClient>;\n\n for (const { name, descriptor, client } of entries) {\n apis[name] = client.getTypedApi(descriptor);\n raw[name] = client;\n }\n\n return {\n ...apis,\n raw,\n destroy() {\n for (const { genesis } of entries) {\n const key = cacheKey(fingerprint, genesis);\n const entry = clientCache.get(key);\n if (entry) {\n try {\n entry.client.destroy();\n } catch {\n /* already destroyed */\n }\n clientCache.delete(key);\n }\n }\n clientInstances.delete(fingerprint);\n },\n } as ChainClient<TChains>;\n}\n\n/**\n * Destroy all chain client instances and reset internal caches.\n *\n * Tears down every connection created by {@link createChainClient}.\n */\nexport function destroyAll(): void {\n clearClientCache();\n clientInstances.clear();\n}\n\n/**\n * Get the raw `PolkadotClient` for a connected chain by its descriptor.\n *\n * The chain must have been initialized via {@link createChainClient} first.\n * Alternatively, use `client.raw.<name>` on the returned {@link ChainClient}.\n *\n * @throws If the chain has not been connected yet.\n */\nexport function getClient(descriptor: ChainDefinition): PolkadotClient {\n const genesis = descriptor.genesis;\n if (!genesis) throw new Error(\"Descriptor has no genesis hash.\");\n const entry = findEntryByGenesis(genesis);\n if (!entry?.client) {\n throw new Error(\n `Chain not connected (genesis: ${genesis}). Call createChainClient() first to establish connections.`,\n );\n }\n return entry.client;\n}\n\n/**\n * Check if a chain is currently connected.\n *\n * Synchronous — no side effects, no initialization.\n */\nexport function isConnected(descriptor: ChainDefinition): boolean {\n const genesis = descriptor.genesis;\n if (!genesis) return false;\n return findEntryByGenesis(genesis) !== undefined;\n}\n\nif (import.meta.vitest) {\n const { test, expect, beforeEach } = import.meta.vitest;\n\n const fakeDescriptor = { genesis: \"0xtest\" } as ChainDefinition;\n const fakeClient = {\n destroy: () => {},\n getTypedApi: () => ({}),\n } as unknown as PolkadotClient;\n\n function seedCache(genesis: string, client: PolkadotClient, fp = \"test\") {\n getClientCache().set(cacheKey(fp, genesis), {\n client,\n api: new Map(),\n });\n }\n\n beforeEach(() => {\n clearClientCache();\n clientInstances.clear();\n });\n\n // --- isConnected ---\n\n test(\"isConnected returns false for unknown chain\", () => {\n expect(isConnected(fakeDescriptor)).toBe(false);\n });\n\n test(\"isConnected returns true after cache is populated\", () => {\n seedCache(\"0xtest\", fakeClient);\n expect(isConnected(fakeDescriptor)).toBe(true);\n });\n\n test(\"isConnected returns false for descriptor without genesis\", () => {\n expect(isConnected({} as ChainDefinition)).toBe(false);\n });\n\n // --- getClient ---\n\n test(\"getClient returns client from cache\", () => {\n seedCache(\"0xtest\", fakeClient);\n expect(getClient(fakeDescriptor)).toBe(fakeClient);\n });\n\n test(\"getClient throws for unconnected chain\", () => {\n expect(() => getClient(fakeDescriptor)).toThrow(/Chain not connected/);\n });\n\n test(\"getClient throws for descriptor without genesis\", () => {\n expect(() => getClient({} as ChainDefinition)).toThrow(/no genesis hash/);\n });\n\n // --- destroyAll ---\n\n test(\"destroyAll calls client.destroy() and clears caches\", () => {\n let destroyed = false;\n const trackableClient = {\n destroy: () => {\n destroyed = true;\n },\n getTypedApi: () => ({}),\n } as unknown as PolkadotClient;\n seedCache(\"0xtest\", trackableClient);\n clientInstances.set(\"test\", Promise.resolve({} as ChainClient<any>));\n destroyAll();\n expect(destroyed).toBe(true);\n expect(isConnected(fakeDescriptor)).toBe(false);\n expect(clientInstances.size).toBe(0);\n });\n\n // --- createChainClient ---\n\n test(\"createChainClient returns same promise for identical config\", async () => {\n const fakeResult = {} as ChainClient<any>;\n const fp = configFingerprint({ a: fakeDescriptor });\n clientInstances.set(fp, Promise.resolve(fakeResult));\n const result = await createChainClient({\n chains: { a: fakeDescriptor },\n rpcs: { a: [] },\n });\n expect(result).toBe(fakeResult);\n });\n\n test(\"createChainClient deduplicates concurrent calls\", async () => {\n const fakeResult = {} as ChainClient<any>;\n const fp = configFingerprint({ x: fakeDescriptor });\n clientInstances.set(fp, Promise.resolve(fakeResult));\n const [a, b] = await Promise.all([\n createChainClient({ chains: { x: fakeDescriptor }, rpcs: { x: [] } }),\n createChainClient({ chains: { x: fakeDescriptor }, rpcs: { x: [] } }),\n ]);\n expect(a).toBe(b);\n });\n\n test(\"createChainClient returns different results for different configs\", async () => {\n const descA = { genesis: \"0xaaa\" } as ChainDefinition;\n const descB = { genesis: \"0xbbb\" } as ChainDefinition;\n const resultA = {} as ChainClient<any>;\n const resultB = {} as ChainClient<any>;\n clientInstances.set(configFingerprint({ a: descA }), Promise.resolve(resultA));\n clientInstances.set(configFingerprint({ b: descB }), Promise.resolve(resultB));\n const a = await createChainClient({ chains: { a: descA }, rpcs: { a: [] } });\n const b = await createChainClient({ chains: { b: descB }, rpcs: { b: [] } });\n expect(a).not.toBe(b);\n });\n\n // --- configFingerprint ---\n\n test(\"configFingerprint is stable regardless of key order\", () => {\n const d1 = { genesis: \"0x1\" } as ChainDefinition;\n const d2 = { genesis: \"0x2\" } as ChainDefinition;\n expect(configFingerprint({ a: d1, b: d2 })).toBe(configFingerprint({ b: d2, a: d1 }));\n });\n\n // --- findEntryByGenesis ---\n\n test(\"findEntryByGenesis returns undefined for missing genesis\", () => {\n expect(findEntryByGenesis(\"0xnonexistent\")).toBeUndefined();\n });\n\n // --- full lifecycle ---\n\n test(\"full lifecycle: seed, verify connected, destroy, verify disconnected\", () => {\n seedCache(\"0xtest\", fakeClient);\n expect(isConnected(fakeDescriptor)).toBe(true);\n expect(getClient(fakeDescriptor)).toBe(fakeClient);\n destroyAll();\n expect(isConnected(fakeDescriptor)).toBe(false);\n expect(() => getClient(fakeDescriptor)).toThrow(/Chain not connected/);\n });\n\n test(\"two fingerprints cached independently, destroy one leaves other intact\", () => {\n const sharedGenesis = \"0xshared\";\n const clientA = { destroy: () => {} } as PolkadotClient;\n const clientB = { destroy: () => {} } as PolkadotClient;\n const descriptorShared = { genesis: sharedGenesis } as ChainDefinition;\n\n seedCache(sharedGenesis, clientA, \"fpA\");\n seedCache(sharedGenesis, clientB, \"fpB\");\n\n expect(isConnected(descriptorShared)).toBe(true);\n\n // Destroy only fpA's entry\n const cache = getClientCache();\n const keyA = cacheKey(\"fpA\", sharedGenesis);\n cache.get(keyA)?.client.destroy();\n cache.delete(keyA);\n\n // fpB's entry still alive\n expect(isConnected(descriptorShared)).toBe(true);\n expect(getClient(descriptorShared)).toBe(clientB);\n });\n}\n","import type { ChainDefinition } from \"polkadot-api\";\nimport { BULLETIN_RPCS } from \"@parity/product-sdk-host\";\nimport { createChainClient } from \"./clients.js\";\nimport type { ChainClient } from \"./types.js\";\n\n// Type-only imports — erased at compile time, zero bundle cost.\n// These give us per-chain TypedApi types without importing runtime descriptor data.\n// Every environment ships its own descriptor for each chain (asset hub, bulletin,\n// individuality) so that genesis hashes and metadata reflect the live chain\n// instance the consumer connects to.\nimport type { polkadot_asset_hub as PolkadotAssetHubDef } from \"@parity/product-sdk-descriptors/polkadot-asset-hub\";\nimport type { kusama_asset_hub as KusamaAssetHubDef } from \"@parity/product-sdk-descriptors/kusama-asset-hub\";\nimport type { paseo_asset_hub as PaseoAssetHubDef } from \"@parity/product-sdk-descriptors/paseo-asset-hub\";\nimport type { previewnet_asset_hub as PreviewnetAssetHubDef } from \"@parity/product-sdk-descriptors/previewnet-asset-hub\";\nimport type { paseo_bulletin as PaseoBulletinDef } from \"@parity/product-sdk-descriptors/paseo-bulletin\";\nimport type { previewnet_bulletin as PreviewnetBulletinDef } from \"@parity/product-sdk-descriptors/previewnet-bulletin\";\nimport type { paseo_individuality as PaseoIndividualityDef } from \"@parity/product-sdk-descriptors/paseo-individuality\";\nimport type { previewnet_individuality as PreviewnetIndividualityDef } from \"@parity/product-sdk-descriptors/previewnet-individuality\";\n\n/** Known network environment with built-in descriptors and RPC endpoints. */\nexport type Environment = \"polkadot\" | \"kusama\" | \"paseo\" | \"previewnet\";\n\n/** Environments where all chains (asset hub, bulletin, individuality) are live. */\nconst AVAILABLE_ENVIRONMENTS: Set<Environment> = new Set([\"paseo\", \"previewnet\"]);\n\nconst rpcs = {\n polkadot: {\n assetHub: [\n \"wss://polkadot-asset-hub-rpc.polkadot.io\",\n \"wss://sys.ibp.network/asset-hub-polkadot\",\n ],\n bulletin: [...BULLETIN_RPCS.polkadot],\n individuality: [] as string[],\n },\n kusama: {\n assetHub: [\n \"wss://kusama-asset-hub-rpc.polkadot.io\",\n \"wss://sys.ibp.network/asset-hub-kusama\",\n ],\n bulletin: [...BULLETIN_RPCS.kusama],\n individuality: [] as string[],\n },\n paseo: {\n assetHub: [\"wss://paseo-asset-hub-next-rpc.polkadot.io\"],\n bulletin: [...BULLETIN_RPCS.paseo],\n individuality: [\"wss://paseo-people-next-system-rpc.polkadot.io\"],\n },\n previewnet: {\n assetHub: [\"wss://previewnet.substrate.dev/asset-hub\"],\n bulletin: [...BULLETIN_RPCS.previewnet],\n individuality: [\"wss://previewnet.substrate.dev/people\"],\n },\n} as const;\n\n/**\n * Lazy-load descriptors for a specific environment.\n *\n * Every chain (asset hub, bulletin, individuality) ships a per-environment\n * descriptor so that genesis hashes and metadata reflect the live chain\n * instance the consumer connects to. Dynamic imports are code-split per\n * environment, so a consumer using one environment doesn't bundle the others.\n */\nasync function loadDescriptors(env: Environment) {\n const loaders = {\n polkadot: () =>\n Promise.all([\n import(\"@parity/product-sdk-descriptors/polkadot-asset-hub\"),\n // Polkadot bulletin/individuality are not yet live; gated by\n // AVAILABLE_ENVIRONMENTS so this branch is unreachable today.\n Promise.reject(new Error(\"polkadot bulletin descriptor not yet available\")),\n Promise.reject(new Error(\"polkadot individuality descriptor not yet available\")),\n ]),\n kusama: () =>\n Promise.all([\n import(\"@parity/product-sdk-descriptors/kusama-asset-hub\"),\n Promise.reject(new Error(\"kusama bulletin descriptor not yet available\")),\n Promise.reject(new Error(\"kusama individuality descriptor not yet available\")),\n ]),\n paseo: () =>\n Promise.all([\n import(\"@parity/product-sdk-descriptors/paseo-asset-hub\"),\n import(\"@parity/product-sdk-descriptors/paseo-bulletin\"),\n import(\"@parity/product-sdk-descriptors/paseo-individuality\"),\n ]),\n previewnet: () =>\n Promise.all([\n import(\"@parity/product-sdk-descriptors/previewnet-asset-hub\"),\n import(\"@parity/product-sdk-descriptors/previewnet-bulletin\"),\n import(\"@parity/product-sdk-descriptors/previewnet-individuality\"),\n ]),\n };\n\n const [ahMod, bulletinMod, individualityMod] = await loaders[env]();\n\n // Extract the named exports — varies per environment. The fallback casts use\n // narrow shape types (not the full `typeof import(...)`) to keep formatter\n // wrap behavior compatible with esbuild's parser.\n const assetHub =\n \"polkadot_asset_hub\" in ahMod\n ? ahMod.polkadot_asset_hub\n : \"kusama_asset_hub\" in ahMod\n ? ahMod.kusama_asset_hub\n : \"paseo_asset_hub\" in ahMod\n ? ahMod.paseo_asset_hub\n : (ahMod as { previewnet_asset_hub: typeof PreviewnetAssetHubDef })\n .previewnet_asset_hub;\n\n const bulletin =\n \"paseo_bulletin\" in bulletinMod\n ? bulletinMod.paseo_bulletin\n : (bulletinMod as { previewnet_bulletin: typeof PreviewnetBulletinDef })\n .previewnet_bulletin;\n\n const individuality =\n \"paseo_individuality\" in individualityMod\n ? individualityMod.paseo_individuality\n : (individualityMod as { previewnet_individuality: typeof PreviewnetIndividualityDef })\n .previewnet_individuality;\n\n return { assetHub, bulletin, individuality };\n}\n\n/** Per-environment descriptor types for each chain in the preset. */\ntype PresetDescriptors = {\n polkadot: {\n assetHub: typeof PolkadotAssetHubDef;\n // Bulletin/individuality not yet live on polkadot — types reuse paseo\n // shape so the API surface stays consistent; runtime path is gated.\n bulletin: typeof PaseoBulletinDef;\n individuality: typeof PaseoIndividualityDef;\n };\n kusama: {\n assetHub: typeof KusamaAssetHubDef;\n bulletin: typeof PaseoBulletinDef;\n individuality: typeof PaseoIndividualityDef;\n };\n paseo: {\n assetHub: typeof PaseoAssetHubDef;\n bulletin: typeof PaseoBulletinDef;\n individuality: typeof PaseoIndividualityDef;\n };\n previewnet: {\n assetHub: typeof PreviewnetAssetHubDef;\n bulletin: typeof PreviewnetBulletinDef;\n individuality: typeof PreviewnetIndividualityDef;\n };\n};\n\n/** The chain shape returned by {@link getChainAPI} for a given environment. */\nexport type PresetChains<E extends Environment> = PresetDescriptors[E];\n\n/**\n * Get a chain client for a known environment with built-in descriptors and RPCs.\n *\n * This is the **zero-config** path — no need to import descriptors or specify\n * endpoints. For custom chains or BYOD descriptors, use\n * {@link createChainClient} instead.\n *\n * Returns the same {@link ChainClient} type as `createChainClient`, with\n * `assetHub`, `bulletin`, and `individuality` chain keys.\n *\n * @example\n * ```ts\n * import { getChainAPI } from \"@parity/product-sdk-chain-client\";\n *\n * const client = await getChainAPI(\"paseo\");\n *\n * // Fully typed — no descriptor imports needed\n * const account = await client.assetHub.query.System.Account.getValue(addr);\n * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();\n *\n * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)\n * import { createContractRuntimeFromClient } from \"@parity/product-sdk-contracts\";\n * import { paseo_asset_hub } from \"@parity/product-sdk-descriptors/paseo-asset-hub\";\n * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);\n *\n * client.destroy();\n * ```\n */\nexport async function getChainAPI<E extends Environment>(\n env: E,\n): Promise<ChainClient<PresetChains<E>>> {\n if (!AVAILABLE_ENVIRONMENTS.has(env)) {\n throw new Error(`Chain API for \"${env}\" is not yet available`);\n }\n\n const descriptors = await loadDescriptors(env);\n const envRpcs = rpcs[env];\n\n return createChainClient({\n chains: {\n assetHub: descriptors.assetHub,\n bulletin: descriptors.bulletin,\n individuality: descriptors.individuality,\n },\n rpcs: {\n assetHub: [...envRpcs.assetHub],\n bulletin: [...envRpcs.bulletin],\n individuality: [...envRpcs.individuality],\n },\n }) as Promise<ChainClient<PresetChains<E>>>;\n}\n\nif (import.meta.vitest) {\n const { test, expect, beforeEach } = import.meta.vitest;\n const { destroyAll } = await import(\"./clients.js\");\n\n // Test-only genesis hashes for assertion — not used in production code.\n // Each chain has a per-environment genesis: bulletin and individuality\n // are distinct chain instances across paseo and previewnet (same runtime,\n // separate deployments).\n const GENESIS = {\n polkadot_asset_hub: \"0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f\",\n kusama_asset_hub: \"0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a\",\n paseo_asset_hub: \"0x173cea9df45656cf612c8b8ece56e04e9a693c69cfaac47d3628dae735067af8\",\n previewnet_asset_hub: \"0x860d75a890388e2ad02c54aa451264d04af89765773a51cd56868b4293c7867c\",\n paseo_bulletin: \"0x8cfe6717dc4becfda2e13c488a1e2061ff2dfee96e7d031157f72d36716c0a22\",\n previewnet_bulletin: \"0xf37fa1f1450ea120edbf64c3fc447f671a00e1f1095a698f42eeec073c7ee487\",\n paseo_individuality: \"0x053e1a785bb0990b98768124d9609e963d9ca3558f5ac6e90a4297aaa0a0bd4b\",\n previewnet_individuality:\n \"0xbf3a38ecba96d2f647bc12198011b9e4f0ba3a7e2a190597205cbe238f5c125d\",\n } as const;\n\n beforeEach(() => {\n destroyAll();\n });\n\n // --- GENESIS constants ---\n\n test(\"genesis constants are valid hex hashes\", () => {\n for (const hash of Object.values(GENESIS)) {\n expect(hash).toMatch(/^0x[a-f0-9]{64}$/);\n }\n });\n\n // --- RPC config ---\n\n test(\"rpcs defined for all environments\", () => {\n for (const env of [\"polkadot\", \"kusama\", \"paseo\", \"previewnet\"] as const) {\n const envRpcs = rpcs[env];\n expect(envRpcs.assetHub.length).toBeGreaterThan(0);\n }\n });\n\n test(\"paseo has RPCs for all chains\", () => {\n const envRpcs = rpcs.paseo;\n expect(envRpcs.bulletin.length).toBeGreaterThan(0);\n expect(envRpcs.individuality.length).toBeGreaterThan(0);\n });\n\n test(\"previewnet has RPCs for all chains\", () => {\n const envRpcs = rpcs.previewnet;\n expect(envRpcs.bulletin.length).toBeGreaterThan(0);\n expect(envRpcs.individuality.length).toBeGreaterThan(0);\n });\n\n // --- getChainAPI ---\n\n test(\"polkadot and kusama throw as not yet available\", async () => {\n await expect(getChainAPI(\"polkadot\")).rejects.toThrow(\"not yet available\");\n await expect(getChainAPI(\"kusama\")).rejects.toThrow(\"not yet available\");\n });\n\n // --- loadDescriptors ---\n\n test(\"loadDescriptors returns descriptors with genesis hashes for paseo\", async () => {\n const descriptors = await loadDescriptors(\"paseo\");\n expect(descriptors).toBeDefined();\n expect(descriptors.assetHub).toBeDefined();\n expect(descriptors.bulletin).toBeDefined();\n expect(descriptors.individuality).toBeDefined();\n expect(descriptors.assetHub.genesis).toBe(GENESIS.paseo_asset_hub);\n expect(descriptors.bulletin.genesis).toBe(GENESIS.paseo_bulletin);\n expect(descriptors.individuality.genesis).toBe(GENESIS.paseo_individuality);\n });\n\n test(\"loadDescriptors returns descriptors with genesis hashes for previewnet\", async () => {\n const descriptors = await loadDescriptors(\"previewnet\");\n expect(descriptors).toBeDefined();\n expect(descriptors.assetHub).toBeDefined();\n expect(descriptors.bulletin).toBeDefined();\n expect(descriptors.individuality).toBeDefined();\n expect(descriptors.assetHub.genesis).toBe(GENESIS.previewnet_asset_hub);\n expect(descriptors.bulletin.genesis).toBe(GENESIS.previewnet_bulletin);\n expect(descriptors.individuality.genesis).toBe(GENESIS.previewnet_individuality);\n });\n\n test(\"loadDescriptors returns environment-specific descriptors for every chain\", async () => {\n const paseo = await loadDescriptors(\"paseo\");\n const previewnet = await loadDescriptors(\"previewnet\");\n // asset-hub: paseo and previewnet are different runtimes (different chains)\n expect(paseo.assetHub.genesis).toBe(GENESIS.paseo_asset_hub);\n expect(previewnet.assetHub.genesis).toBe(GENESIS.previewnet_asset_hub);\n // bulletin: same runtime, different chain instances → distinct genesis\n expect(paseo.bulletin.genesis).toBe(GENESIS.paseo_bulletin);\n expect(previewnet.bulletin.genesis).toBe(GENESIS.previewnet_bulletin);\n expect(paseo.bulletin.genesis).not.toBe(previewnet.bulletin.genesis);\n // individuality: same runtime, different chain instances → distinct genesis\n expect(paseo.individuality.genesis).toBe(GENESIS.paseo_individuality);\n expect(previewnet.individuality.genesis).toBe(GENESIS.previewnet_individuality);\n expect(paseo.individuality.genesis).not.toBe(previewnet.individuality.genesis);\n });\n\n // --- AVAILABLE_ENVIRONMENTS ---\n\n test(\"paseo and previewnet are currently available\", () => {\n expect(AVAILABLE_ENVIRONMENTS.has(\"paseo\")).toBe(true);\n expect(AVAILABLE_ENVIRONMENTS.has(\"previewnet\")).toBe(true);\n expect(AVAILABLE_ENVIRONMENTS.has(\"polkadot\")).toBe(false);\n expect(AVAILABLE_ENVIRONMENTS.has(\"kusama\")).toBe(false);\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parity/product-sdk-chain-client",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Multi-chain Polkadot API client with typed access to Asset Hub, Bulletin, and other chains",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -19,9 +19,9 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "polkadot-api": "^2.1.2",
22
- "@parity/product-sdk-descriptors": "0.2.1",
23
22
  "@parity/product-sdk-logger": "0.1.1",
24
- "@parity/product-sdk-host": "0.2.0"
23
+ "@parity/product-sdk-descriptors": "0.4.0",
24
+ "@parity/product-sdk-host": "0.2.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsup": "^8.4.0",
package/src/clients.ts CHANGED
@@ -28,8 +28,12 @@ function configFingerprint(chains: Record<string, ChainDefinition>): string {
28
28
  * Create a multi-chain client with user-provided descriptors and RPC endpoints.
29
29
  *
30
30
  * Returns fully-typed APIs for each chain plus raw `PolkadotClient` access via `.raw`.
31
- * Connections use host routing (via `@parity/product-sdk-host`) when inside a container,
32
- * falling back to direct WebSocket RPC.
31
+ * Connections route through the host provider (`@parity/product-sdk-host`) the SDK
32
+ * is designed to run exclusively inside a host container (Polkadot Browser / Desktop).
33
+ * Throws if no host provider is available; there is no direct-WebSocket fallback.
34
+ *
35
+ * The `config.rpcs` field is currently unused at runtime (kept for API compatibility
36
+ * and BYOD documentation), since the host owns the chain connection.
33
37
  *
34
38
  * Results are cached by genesis-hash fingerprint — calling with the same descriptors
35
39
  * returns the same instance.
@@ -37,14 +41,14 @@ function configFingerprint(chains: Record<string, ChainDefinition>): string {
37
41
  * @example
38
42
  * ```ts
39
43
  * import { createChainClient } from "@parity/product-sdk-chain-client";
40
- * import { paseo_asset_hub } from "./descriptors/paseo-asset-hub";
41
- * import { bulletin } from "./descriptors/bulletin";
44
+ * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
45
+ * import { paseo_bulletin } from "@parity/product-sdk-descriptors/paseo-bulletin";
42
46
  *
43
47
  * const client = await createChainClient({
44
- * chains: { assetHub: paseo_asset_hub, bulletin },
48
+ * chains: { assetHub: paseo_asset_hub, bulletin: paseo_bulletin },
45
49
  * rpcs: {
46
- * assetHub: ["wss://sys.ibp.network/asset-hub-paseo"],
47
- * bulletin: ["wss://paseo-bulletin-rpc.polkadot.io"],
50
+ * assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"],
51
+ * bulletin: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
48
52
  * },
49
53
  * });
50
54
  *
@@ -52,9 +56,9 @@ function configFingerprint(chains: Record<string, ChainDefinition>): string {
52
56
  * const account = await client.assetHub.query.System.Account.getValue(addr);
53
57
  * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();
54
58
  *
55
- * // Raw client for advanced use (e.g., InkSdk for contracts)
56
- * import { createInkSdk } from "@polkadot-api/sdk-ink";
57
- * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
59
+ * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)
60
+ * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
61
+ * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
58
62
  *
59
63
  * // Cleanup
60
64
  * client.destroy();
package/src/presets.ts CHANGED
@@ -5,17 +5,23 @@ import type { ChainClient } from "./types.js";
5
5
 
6
6
  // Type-only imports — erased at compile time, zero bundle cost.
7
7
  // These give us per-chain TypedApi types without importing runtime descriptor data.
8
+ // Every environment ships its own descriptor for each chain (asset hub, bulletin,
9
+ // individuality) so that genesis hashes and metadata reflect the live chain
10
+ // instance the consumer connects to.
8
11
  import type { polkadot_asset_hub as PolkadotAssetHubDef } from "@parity/product-sdk-descriptors/polkadot-asset-hub";
9
12
  import type { kusama_asset_hub as KusamaAssetHubDef } from "@parity/product-sdk-descriptors/kusama-asset-hub";
10
13
  import type { paseo_asset_hub as PaseoAssetHubDef } from "@parity/product-sdk-descriptors/paseo-asset-hub";
11
- import type { bulletin as BulletinDef } from "@parity/product-sdk-descriptors/bulletin";
12
- import type { individuality as IndividualityDef } from "@parity/product-sdk-descriptors/individuality";
14
+ import type { previewnet_asset_hub as PreviewnetAssetHubDef } from "@parity/product-sdk-descriptors/previewnet-asset-hub";
15
+ import type { paseo_bulletin as PaseoBulletinDef } from "@parity/product-sdk-descriptors/paseo-bulletin";
16
+ import type { previewnet_bulletin as PreviewnetBulletinDef } from "@parity/product-sdk-descriptors/previewnet-bulletin";
17
+ import type { paseo_individuality as PaseoIndividualityDef } from "@parity/product-sdk-descriptors/paseo-individuality";
18
+ import type { previewnet_individuality as PreviewnetIndividualityDef } from "@parity/product-sdk-descriptors/previewnet-individuality";
13
19
 
14
20
  /** Known network environment with built-in descriptors and RPC endpoints. */
15
- export type Environment = "polkadot" | "kusama" | "paseo";
21
+ export type Environment = "polkadot" | "kusama" | "paseo" | "previewnet";
16
22
 
17
23
  /** Environments where all chains (asset hub, bulletin, individuality) are live. */
18
- const AVAILABLE_ENVIRONMENTS: Set<Environment> = new Set(["paseo"]);
24
+ const AVAILABLE_ENVIRONMENTS: Set<Environment> = new Set(["paseo", "previewnet"]);
19
25
 
20
26
  const rpcs = {
21
27
  polkadot: {
@@ -35,58 +41,113 @@ const rpcs = {
35
41
  individuality: [] as string[],
36
42
  },
37
43
  paseo: {
38
- assetHub: [
39
- "wss://asset-hub-paseo-rpc.n.dwellir.com",
40
- "wss://sys.ibp.network/asset-hub-paseo",
41
- ],
44
+ assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"],
42
45
  bulletin: [...BULLETIN_RPCS.paseo],
43
- individuality: ["wss://paseo-people-next-rpc.polkadot.io"],
46
+ individuality: ["wss://paseo-people-next-system-rpc.polkadot.io"],
47
+ },
48
+ previewnet: {
49
+ assetHub: ["wss://previewnet.substrate.dev/asset-hub"],
50
+ bulletin: [...BULLETIN_RPCS.previewnet],
51
+ individuality: ["wss://previewnet.substrate.dev/people"],
44
52
  },
45
53
  } as const;
46
54
 
47
55
  /**
48
56
  * Lazy-load descriptors for a specific environment.
49
- * Only imports the chains needed — avoids bundling all 5 chains when
50
- * a consumer only uses one environment.
57
+ *
58
+ * Every chain (asset hub, bulletin, individuality) ships a per-environment
59
+ * descriptor so that genesis hashes and metadata reflect the live chain
60
+ * instance the consumer connects to. Dynamic imports are code-split per
61
+ * environment, so a consumer using one environment doesn't bundle the others.
51
62
  */
52
63
  async function loadDescriptors(env: Environment) {
53
- const assetHubImport = {
54
- polkadot: () => import("@parity/product-sdk-descriptors/polkadot-asset-hub"),
55
- kusama: () => import("@parity/product-sdk-descriptors/kusama-asset-hub"),
56
- paseo: () => import("@parity/product-sdk-descriptors/paseo-asset-hub"),
57
- }[env]();
58
-
59
- const [ahMod, { bulletin }, { individuality }] = await Promise.all([
60
- assetHubImport,
61
- import("@parity/product-sdk-descriptors/bulletin"),
62
- import("@parity/product-sdk-descriptors/individuality"),
63
- ]);
64
-
65
- // Extract the asset hub descriptor (the named export varies per environment)
64
+ const loaders = {
65
+ polkadot: () =>
66
+ Promise.all([
67
+ import("@parity/product-sdk-descriptors/polkadot-asset-hub"),
68
+ // Polkadot bulletin/individuality are not yet live; gated by
69
+ // AVAILABLE_ENVIRONMENTS so this branch is unreachable today.
70
+ Promise.reject(new Error("polkadot bulletin descriptor not yet available")),
71
+ Promise.reject(new Error("polkadot individuality descriptor not yet available")),
72
+ ]),
73
+ kusama: () =>
74
+ Promise.all([
75
+ import("@parity/product-sdk-descriptors/kusama-asset-hub"),
76
+ Promise.reject(new Error("kusama bulletin descriptor not yet available")),
77
+ Promise.reject(new Error("kusama individuality descriptor not yet available")),
78
+ ]),
79
+ paseo: () =>
80
+ Promise.all([
81
+ import("@parity/product-sdk-descriptors/paseo-asset-hub"),
82
+ import("@parity/product-sdk-descriptors/paseo-bulletin"),
83
+ import("@parity/product-sdk-descriptors/paseo-individuality"),
84
+ ]),
85
+ previewnet: () =>
86
+ Promise.all([
87
+ import("@parity/product-sdk-descriptors/previewnet-asset-hub"),
88
+ import("@parity/product-sdk-descriptors/previewnet-bulletin"),
89
+ import("@parity/product-sdk-descriptors/previewnet-individuality"),
90
+ ]),
91
+ };
92
+
93
+ const [ahMod, bulletinMod, individualityMod] = await loaders[env]();
94
+
95
+ // Extract the named exports — varies per environment. The fallback casts use
96
+ // narrow shape types (not the full `typeof import(...)`) to keep formatter
97
+ // wrap behavior compatible with esbuild's parser.
66
98
  const assetHub =
67
99
  "polkadot_asset_hub" in ahMod
68
100
  ? ahMod.polkadot_asset_hub
69
101
  : "kusama_asset_hub" in ahMod
70
102
  ? ahMod.kusama_asset_hub
71
- : (ahMod as typeof import("@parity/product-sdk-descriptors/paseo-asset-hub"))
72
- .paseo_asset_hub;
103
+ : "paseo_asset_hub" in ahMod
104
+ ? ahMod.paseo_asset_hub
105
+ : (ahMod as { previewnet_asset_hub: typeof PreviewnetAssetHubDef })
106
+ .previewnet_asset_hub;
107
+
108
+ const bulletin =
109
+ "paseo_bulletin" in bulletinMod
110
+ ? bulletinMod.paseo_bulletin
111
+ : (bulletinMod as { previewnet_bulletin: typeof PreviewnetBulletinDef })
112
+ .previewnet_bulletin;
113
+
114
+ const individuality =
115
+ "paseo_individuality" in individualityMod
116
+ ? individualityMod.paseo_individuality
117
+ : (individualityMod as { previewnet_individuality: typeof PreviewnetIndividualityDef })
118
+ .previewnet_individuality;
73
119
 
74
120
  return { assetHub, bulletin, individuality };
75
121
  }
76
122
 
77
- /** Maps each environment to its asset hub descriptor type. */
78
- type AssetHubDescriptors = {
79
- polkadot: typeof PolkadotAssetHubDef;
80
- kusama: typeof KusamaAssetHubDef;
81
- paseo: typeof PaseoAssetHubDef;
123
+ /** Per-environment descriptor types for each chain in the preset. */
124
+ type PresetDescriptors = {
125
+ polkadot: {
126
+ assetHub: typeof PolkadotAssetHubDef;
127
+ // Bulletin/individuality not yet live on polkadot — types reuse paseo
128
+ // shape so the API surface stays consistent; runtime path is gated.
129
+ bulletin: typeof PaseoBulletinDef;
130
+ individuality: typeof PaseoIndividualityDef;
131
+ };
132
+ kusama: {
133
+ assetHub: typeof KusamaAssetHubDef;
134
+ bulletin: typeof PaseoBulletinDef;
135
+ individuality: typeof PaseoIndividualityDef;
136
+ };
137
+ paseo: {
138
+ assetHub: typeof PaseoAssetHubDef;
139
+ bulletin: typeof PaseoBulletinDef;
140
+ individuality: typeof PaseoIndividualityDef;
141
+ };
142
+ previewnet: {
143
+ assetHub: typeof PreviewnetAssetHubDef;
144
+ bulletin: typeof PreviewnetBulletinDef;
145
+ individuality: typeof PreviewnetIndividualityDef;
146
+ };
82
147
  };
83
148
 
84
149
  /** The chain shape returned by {@link getChainAPI} for a given environment. */
85
- export type PresetChains<E extends Environment> = {
86
- assetHub: AssetHubDescriptors[E];
87
- bulletin: typeof BulletinDef;
88
- individuality: typeof IndividualityDef;
89
- };
150
+ export type PresetChains<E extends Environment> = PresetDescriptors[E];
90
151
 
91
152
  /**
92
153
  * Get a chain client for a known environment with built-in descriptors and RPCs.
@@ -108,9 +169,10 @@ export type PresetChains<E extends Environment> = {
108
169
  * const account = await client.assetHub.query.System.Account.getValue(addr);
109
170
  * const fee = await client.bulletin.query.TransactionStorage.ByteFee.getValue();
110
171
  *
111
- * // Raw client for advanced use (e.g., InkSdk for contracts)
112
- * import { createInkSdk } from "@polkadot-api/sdk-ink";
113
- * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
172
+ * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)
173
+ * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
174
+ * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
175
+ * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
114
176
  *
115
177
  * client.destroy();
116
178
  * ```
@@ -144,12 +206,19 @@ if (import.meta.vitest) {
144
206
  const { destroyAll } = await import("./clients.js");
145
207
 
146
208
  // Test-only genesis hashes for assertion — not used in production code.
209
+ // Each chain has a per-environment genesis: bulletin and individuality
210
+ // are distinct chain instances across paseo and previewnet (same runtime,
211
+ // separate deployments).
147
212
  const GENESIS = {
148
213
  polkadot_asset_hub: "0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f",
149
214
  kusama_asset_hub: "0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a",
150
- paseo_asset_hub: "0xd6eec26135305a8ad257a20d003357284c8aa03d0bdb2b357ab0a22371e11ef2",
151
- bulletin: "0x744960c32e3a3df5440e1ecd4d34096f1ce2230d7016a5ada8a765d5a622b4ea",
152
- individuality: "0xd01475fde5d0592989b7715ae1d2e89fdb4f8c7688c09c850d75e1d4bdb47d64",
215
+ paseo_asset_hub: "0x173cea9df45656cf612c8b8ece56e04e9a693c69cfaac47d3628dae735067af8",
216
+ previewnet_asset_hub: "0x860d75a890388e2ad02c54aa451264d04af89765773a51cd56868b4293c7867c",
217
+ paseo_bulletin: "0x8cfe6717dc4becfda2e13c488a1e2061ff2dfee96e7d031157f72d36716c0a22",
218
+ previewnet_bulletin: "0xf37fa1f1450ea120edbf64c3fc447f671a00e1f1095a698f42eeec073c7ee487",
219
+ paseo_individuality: "0x053e1a785bb0990b98768124d9609e963d9ca3558f5ac6e90a4297aaa0a0bd4b",
220
+ previewnet_individuality:
221
+ "0xbf3a38ecba96d2f647bc12198011b9e4f0ba3a7e2a190597205cbe238f5c125d",
153
222
  } as const;
154
223
 
155
224
  beforeEach(() => {
@@ -167,7 +236,7 @@ if (import.meta.vitest) {
167
236
  // --- RPC config ---
168
237
 
169
238
  test("rpcs defined for all environments", () => {
170
- for (const env of ["polkadot", "kusama", "paseo"] as const) {
239
+ for (const env of ["polkadot", "kusama", "paseo", "previewnet"] as const) {
171
240
  const envRpcs = rpcs[env];
172
241
  expect(envRpcs.assetHub.length).toBeGreaterThan(0);
173
242
  }
@@ -179,6 +248,12 @@ if (import.meta.vitest) {
179
248
  expect(envRpcs.individuality.length).toBeGreaterThan(0);
180
249
  });
181
250
 
251
+ test("previewnet has RPCs for all chains", () => {
252
+ const envRpcs = rpcs.previewnet;
253
+ expect(envRpcs.bulletin.length).toBeGreaterThan(0);
254
+ expect(envRpcs.individuality.length).toBeGreaterThan(0);
255
+ });
256
+
182
257
  // --- getChainAPI ---
183
258
 
184
259
  test("polkadot and kusama throw as not yet available", async () => {
@@ -195,26 +270,42 @@ if (import.meta.vitest) {
195
270
  expect(descriptors.bulletin).toBeDefined();
196
271
  expect(descriptors.individuality).toBeDefined();
197
272
  expect(descriptors.assetHub.genesis).toBe(GENESIS.paseo_asset_hub);
198
- expect(descriptors.bulletin.genesis).toBe(GENESIS.bulletin);
199
- expect(descriptors.individuality.genesis).toBe(GENESIS.individuality);
273
+ expect(descriptors.bulletin.genesis).toBe(GENESIS.paseo_bulletin);
274
+ expect(descriptors.individuality.genesis).toBe(GENESIS.paseo_individuality);
275
+ });
276
+
277
+ test("loadDescriptors returns descriptors with genesis hashes for previewnet", async () => {
278
+ const descriptors = await loadDescriptors("previewnet");
279
+ expect(descriptors).toBeDefined();
280
+ expect(descriptors.assetHub).toBeDefined();
281
+ expect(descriptors.bulletin).toBeDefined();
282
+ expect(descriptors.individuality).toBeDefined();
283
+ expect(descriptors.assetHub.genesis).toBe(GENESIS.previewnet_asset_hub);
284
+ expect(descriptors.bulletin.genesis).toBe(GENESIS.previewnet_bulletin);
285
+ expect(descriptors.individuality.genesis).toBe(GENESIS.previewnet_individuality);
200
286
  });
201
287
 
202
- test("loadDescriptors returns correct asset hub per environment", async () => {
203
- const polkadot = await loadDescriptors("polkadot");
204
- const kusama = await loadDescriptors("kusama");
288
+ test("loadDescriptors returns environment-specific descriptors for every chain", async () => {
205
289
  const paseo = await loadDescriptors("paseo");
206
- expect(polkadot.assetHub.genesis).toBe(GENESIS.polkadot_asset_hub);
207
- expect(kusama.assetHub.genesis).toBe(GENESIS.kusama_asset_hub);
290
+ const previewnet = await loadDescriptors("previewnet");
291
+ // asset-hub: paseo and previewnet are different runtimes (different chains)
208
292
  expect(paseo.assetHub.genesis).toBe(GENESIS.paseo_asset_hub);
209
- // bulletin and individuality are the same across environments
210
- expect(polkadot.bulletin.genesis).toBe(paseo.bulletin.genesis);
211
- expect(polkadot.individuality.genesis).toBe(paseo.individuality.genesis);
293
+ expect(previewnet.assetHub.genesis).toBe(GENESIS.previewnet_asset_hub);
294
+ // bulletin: same runtime, different chain instances → distinct genesis
295
+ expect(paseo.bulletin.genesis).toBe(GENESIS.paseo_bulletin);
296
+ expect(previewnet.bulletin.genesis).toBe(GENESIS.previewnet_bulletin);
297
+ expect(paseo.bulletin.genesis).not.toBe(previewnet.bulletin.genesis);
298
+ // individuality: same runtime, different chain instances → distinct genesis
299
+ expect(paseo.individuality.genesis).toBe(GENESIS.paseo_individuality);
300
+ expect(previewnet.individuality.genesis).toBe(GENESIS.previewnet_individuality);
301
+ expect(paseo.individuality.genesis).not.toBe(previewnet.individuality.genesis);
212
302
  });
213
303
 
214
304
  // --- AVAILABLE_ENVIRONMENTS ---
215
305
 
216
- test("only paseo is currently available", () => {
306
+ test("paseo and previewnet are currently available", () => {
217
307
  expect(AVAILABLE_ENVIRONMENTS.has("paseo")).toBe(true);
308
+ expect(AVAILABLE_ENVIRONMENTS.has("previewnet")).toBe(true);
218
309
  expect(AVAILABLE_ENVIRONMENTS.has("polkadot")).toBe(false);
219
310
  expect(AVAILABLE_ENVIRONMENTS.has("kusama")).toBe(false);
220
311
  });
package/src/types.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ChainDefinition, PolkadotClient, TypedApi } from "polkadot-api";
2
2
 
3
3
  /** Supported chain environments for the Polkadot ecosystem. */
4
- export type Environment = "polkadot" | "kusama" | "paseo" | "local" | "westend";
4
+ export type Environment = "polkadot" | "kusama" | "paseo" | "previewnet" | "local" | "westend";
5
5
 
6
6
  /**
7
7
  * Configuration for {@link createChainClient}.
@@ -17,14 +17,14 @@ export type Environment = "polkadot" | "kusama" | "paseo" | "local" | "westend";
17
17
  * @example
18
18
  * ```ts
19
19
  * import { createChainClient } from "@parity/product-sdk-chain-client";
20
- * import { paseo_asset_hub } from "./descriptors/paseo-asset-hub";
21
- * import { bulletin } from "./descriptors/bulletin";
20
+ * import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
21
+ * import { paseo_bulletin } from "@parity/product-sdk-descriptors/paseo-bulletin";
22
22
  *
23
23
  * const client = await createChainClient({
24
- * chains: { assetHub: paseo_asset_hub, bulletin },
24
+ * chains: { assetHub: paseo_asset_hub, bulletin: paseo_bulletin },
25
25
  * rpcs: {
26
- * assetHub: ["wss://sys.ibp.network/asset-hub-paseo"],
27
- * bulletin: ["wss://paseo-bulletin-rpc.polkadot.io"],
26
+ * assetHub: ["wss://paseo-asset-hub-next-rpc.polkadot.io"],
27
+ * bulletin: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
28
28
  * },
29
29
  * });
30
30
  * ```
@@ -43,7 +43,7 @@ export interface ChainClientConfig<
43
43
  *
44
44
  * Each key from your config maps to a fully-typed PAPI {@link TypedApi}.
45
45
  * Access raw `PolkadotClient` instances via `.raw` for advanced use cases
46
- * like creating an `InkSdk` for contract interactions.
46
+ * like creating a `ContractRuntime` for pallet-revive contract interactions.
47
47
  *
48
48
  * @typeParam TChains - The chain descriptor record used to create this client.
49
49
  *
@@ -52,15 +52,15 @@ export interface ChainClientConfig<
52
52
  * // Typed API access — fully typed from your descriptors
53
53
  * const account = await client.assetHub.query.System.Account.getValue(addr);
54
54
  *
55
- * // Raw client for advanced use (e.g., InkSdk for contracts)
56
- * import { createInkSdk } from "@polkadot-api/sdk-ink";
57
- * const inkSdk = createInkSdk(client.raw.assetHub, { atBest: true });
55
+ * // Raw client for advanced use (e.g., a ContractRuntime for pallet-revive contracts)
56
+ * import { createContractRuntimeFromClient } from "@parity/product-sdk-contracts";
57
+ * const runtime = createContractRuntimeFromClient(client.raw.assetHub, paseo_asset_hub);
58
58
  * ```
59
59
  */
60
60
  export type ChainClient<TChains extends Record<string, ChainDefinition>> = {
61
61
  [K in string & keyof TChains]: TypedApi<TChains[K]>;
62
62
  } & {
63
- /** Raw `PolkadotClient` instances, keyed by chain name. Use for advanced APIs like `createInkSdk`. */
63
+ /** Raw `PolkadotClient` instances, keyed by chain name. Use for advanced APIs like `createContractRuntime`. */
64
64
  raw: { [K in string & keyof TChains]: PolkadotClient };
65
65
  /** Destroy all connections managed by this client. */
66
66
  destroy: () => void;